mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-pcu.git
				synced 2025-11-04 06:03:30 +00:00 
			
		
		
		
	Compare commits
	
		
			1848 Commits
		
	
	
		
			jolly/outd
			...
			next
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					deed90dd00 | ||
| 
						 | 
					4f67a9bf46 | ||
| 
						 | 
					1989a19066 | ||
| 
						 | 
					8c29236d35 | ||
| 
						 | 
					ab178903d4 | ||
| 
						 | 
					b657213773 | ||
| 
						 | 
					94dc6a8b6c | ||
| 
						 | 
					826576287e | ||
| 
						 | 
					86f4c093d1 | ||
| 
						 | 
					c6e911cf22 | ||
| 
						 | 
					9c1db1738f | ||
| 
						 | 
					d65bd9d7b2 | ||
| 
						 | 
					c9880b97cf | ||
| 
						 | 
					4c2387026a | ||
| 
						 | 
					2761e574de | ||
| 
						 | 
					dc2aaac29f | ||
| 
						 | 
					38f80be73b | ||
| 
						 | 
					1a1557a60a | ||
| 
						 | 
					f62b0ec37d | ||
| 
						 | 
					9d2fd018ff | ||
| 
						 | 
					bd54205475 | ||
| 
						 | 
					4c51eaf05b | ||
| 
						 | 
					6e25119c18 | ||
| 
						 | 
					c43570c351 | ||
| 
						 | 
					c48d27b57b | ||
| 
						 | 
					48517620b9 | ||
| 
						 | 
					9b63cd04e4 | ||
| 
						 | 
					58916318ef | ||
| 
						 | 
					f53815f2fc | ||
| 
						 | 
					632542348a | ||
| 
						 | 
					c432e062ea | ||
| 
						 | 
					6bab522e90 | ||
| 
						 | 
					eb13c79cc0 | ||
| 
						 | 
					faf0ccb241 | ||
| 
						 | 
					4b6f0bfe69 | ||
| 
						 | 
					20271c421c | ||
| 
						 | 
					c6571b5581 | ||
| 
						 | 
					50272a4776 | ||
| 
						 | 
					393484a5d0 | ||
| 
						 | 
					0e35aee194 | ||
| 
						 | 
					c6dcfe32f3 | ||
| 
						 | 
					4b7a71f93f | ||
| 
						 | 
					292d04d19d | ||
| 
						 | 
					d9367e34db | ||
| 
						 | 
					4e453b41f3 | ||
| 
						 | 
					fa48b4b720 | ||
| 
						 | 
					f593fc5cbb | ||
| 
						 | 
					039ee8200a | ||
| 
						 | 
					34f61af3d0 | ||
| 
						 | 
					2ab840a1fa | ||
| 
						 | 
					54742f287c | ||
| 
						 | 
					14339f6fac | ||
| 
						 | 
					25ebf3c8f9 | ||
| 
						 | 
					846fd248dc | ||
| 
						 | 
					710e0e9ad8 | ||
| 
						 | 
					434799720c | ||
| 
						 | 
					ffc533b1af | ||
| 
						 | 
					ab3aca65c5 | ||
| 
						 | 
					16e1678bfc | ||
| 
						 | 
					58046e45d1 | ||
| 
						 | 
					2c0931cedc | ||
| 
						 | 
					127e7392f6 | ||
| 
						 | 
					feee2b9b83 | ||
| 
						 | 
					42445ea944 | ||
| 
						 | 
					ed066b5328 | ||
| 
						 | 
					86580e1966 | ||
| 
						 | 
					b5ae0811d1 | ||
| 
						 | 
					50a1ede693 | ||
| 
						 | 
					ce3bd2522a | ||
| 
						 | 
					222f674116 | ||
| 
						 | 
					54e6450293 | ||
| 
						 | 
					0b998b15da | ||
| 
						 | 
					c1f38c7f0b | ||
| 
						 | 
					ade9c2f553 | ||
| 
						 | 
					9a6f0b191a | ||
| 
						 | 
					7bd92a3e1d | ||
| 
						 | 
					4bab867d9f | ||
| 
						 | 
					107e94c9f8 | ||
| 
						 | 
					56f223d8d1 | ||
| 
						 | 
					5d376845bb | ||
| 
						 | 
					82519264ca | ||
| 
						 | 
					d7f0558b5c | ||
| 
						 | 
					755a8d61bb | ||
| 
						 | 
					fecab50066 | ||
| 
						 | 
					c7cc4162e1 | ||
| 
						 | 
					95f8fa1f7c | ||
| 
						 | 
					582a15e413 | ||
| 
						 | 
					5bc9612c02 | ||
| 
						 | 
					3a42d17b14 | ||
| 
						 | 
					fd1fbdb8db | ||
| 
						 | 
					99360a304f | ||
| 
						 | 
					15c58ace75 | ||
| 
						 | 
					68bfdff3f0 | ||
| 
						 | 
					4f2c8cd96a | ||
| 
						 | 
					df58ddf6d8 | ||
| 
						 | 
					c1f31c46ac | ||
| 
						 | 
					91cc780b40 | ||
| 
						 | 
					1e97951582 | ||
| 
						 | 
					702ebee751 | ||
| 
						 | 
					30617115ba | ||
| 
						 | 
					5447f3acf6 | ||
| 
						 | 
					81c549d5be | ||
| 
						 | 
					3cba94d70e | ||
| 
						 | 
					58fdc54a7f | ||
| 
						 | 
					0057bd76fd | ||
| 
						 | 
					2ad15d51fa | ||
| 
						 | 
					3973eb5fe8 | ||
| 
						 | 
					11f01105a0 | ||
| 
						 | 
					4fe090146f | ||
| 
						 | 
					423bf8c408 | ||
| 
						 | 
					cf6c71263f | ||
| 
						 | 
					8afc6bad80 | ||
| 
						 | 
					5b9d0bb8e5 | ||
| 
						 | 
					a89008b724 | ||
| 
						 | 
					a70bf72ce5 | ||
| 
						 | 
					9688dc9aca | ||
| 
						 | 
					c85e093969 | ||
| 
						 | 
					36177c6b58 | ||
| 
						 | 
					95e2266832 | ||
| 
						 | 
					4e1c9adb67 | ||
| 
						 | 
					7c9a4a41bc | ||
| 
						 | 
					ed2afa3bed | ||
| 
						 | 
					50aa492b85 | ||
| 
						 | 
					fe8de457ac | ||
| 
						 | 
					4df2658884 | ||
| 
						 | 
					10475f5832 | ||
| 
						 | 
					dfbf3d2c09 | ||
| 
						 | 
					47a3b780db | ||
| 
						 | 
					1f8e229221 | ||
| 
						 | 
					00f52cc3d6 | ||
| 
						 | 
					b18d2a5fd9 | ||
| 
						 | 
					151bc5b0d3 | ||
| 
						 | 
					9345eb34d3 | ||
| 
						 | 
					cf6b3bc08f | ||
| 
						 | 
					66e8a49734 | ||
| 
						 | 
					94a367f224 | ||
| 
						 | 
					13a12e2e3b | ||
| 
						 | 
					9313da517d | ||
| 
						 | 
					a78f0d5bfe | ||
| 
						 | 
					8e69fc0c3a | ||
| 
						 | 
					069a637be8 | ||
| 
						 | 
					41ff273226 | ||
| 
						 | 
					0c10b3cdc1 | ||
| 
						 | 
					abba102d7b | ||
| 
						 | 
					830ca26034 | ||
| 
						 | 
					55aca83098 | ||
| 
						 | 
					44768f2127 | ||
| 
						 | 
					952cb3d5d7 | ||
| 
						 | 
					a58ec61514 | ||
| 
						 | 
					1aef113bb7 | ||
| 
						 | 
					57dcde4242 | ||
| 
						 | 
					b71aab5646 | ||
| 
						 | 
					4668dc6f07 | ||
| 
						 | 
					79784d0249 | ||
| 
						 | 
					fdbcea3532 | ||
| 
						 | 
					05be90367a | ||
| 
						 | 
					3877848e22 | ||
| 
						 | 
					e0fb465678 | ||
| 
						 | 
					809dc8b046 | ||
| 
						 | 
					f7e1df0da2 | ||
| 
						 | 
					ced5c1f5c8 | ||
| 
						 | 
					7b7fb225ce | ||
| 
						 | 
					a06ac18d22 | ||
| 
						 | 
					41a22a7ab8 | ||
| 
						 | 
					ab7159f6ec | ||
| 
						 | 
					c0805e6389 | ||
| 
						 | 
					202a47886c | ||
| 
						 | 
					c0a250d17d | ||
| 
						 | 
					1e77ca88af | ||
| 
						 | 
					54211b1e1b | ||
| 
						 | 
					f7ec52560f | ||
| 
						 | 
					91e3567a15 | ||
| 
						 | 
					1a5439b739 | ||
| 
						 | 
					fc464935a4 | ||
| 
						 | 
					201da4e5b2 | ||
| 
						 | 
					7bb8cd683c | ||
| 
						 | 
					7963edba09 | ||
| 
						 | 
					2238228e3c | ||
| 
						 | 
					8f1701fe24 | ||
| 
						 | 
					0298c0b6a0 | ||
| 
						 | 
					a100a6bc56 | ||
| 
						 | 
					3a27102e59 | ||
| 
						 | 
					db5e339da4 | ||
| 
						 | 
					2e6b60df45 | ||
| 
						 | 
					d1049dc8cc | ||
| 
						 | 
					e91c4c72b1 | ||
| 
						 | 
					906aafc9e2 | ||
| 
						 | 
					8a35e640a3 | ||
| 
						 | 
					4a5209d8bc | ||
| 
						 | 
					289f90048b | ||
| 
						 | 
					0ece97d718 | ||
| 
						 | 
					a45aafd39c | ||
| 
						 | 
					2182e627cd | ||
| 
						 | 
					793583ea21 | ||
| 
						 | 
					f473ec9d7a | ||
| 
						 | 
					519d071131 | ||
| 
						 | 
					47f15fb6fd | ||
| 
						 | 
					e891222920 | ||
| 
						 | 
					113fb419ec | ||
| 
						 | 
					54b159aab9 | ||
| 
						 | 
					ad79b857cd | ||
| 
						 | 
					e8dcf64881 | ||
| 
						 | 
					97296b299c | ||
| 
						 | 
					05f9f59a67 | ||
| 
						 | 
					a281495008 | ||
| 
						 | 
					03de898d19 | ||
| 
						 | 
					924aaad4bc | ||
| 
						 | 
					ac3fd12026 | ||
| 
						 | 
					695ce77167 | ||
| 
						 | 
					54faf023be | ||
| 
						 | 
					f842e06c88 | ||
| 
						 | 
					eb70a4098f | ||
| 
						 | 
					55022ea1b1 | ||
| 
						 | 
					f5a251bcee | ||
| 
						 | 
					1e00947c29 | ||
| 
						 | 
					bed48cc14f | ||
| 
						 | 
					c2d3625bf6 | ||
| 
						 | 
					1188167a92 | ||
| 
						 | 
					480c8acc8b | ||
| 
						 | 
					d3123ea0f5 | ||
| 
						 | 
					a5b9fb5304 | ||
| 
						 | 
					8b4b19a012 | ||
| 
						 | 
					da971ee502 | ||
| 
						 | 
					86fad1ec4e | ||
| 
						 | 
					d6b913fc39 | ||
| 
						 | 
					b77a2c992e | ||
| 
						 | 
					3db4789be8 | ||
| 
						 | 
					1e6c350a9f | ||
| 
						 | 
					b644fd1f09 | ||
| 
						 | 
					5012e07685 | ||
| 
						 | 
					398f60e11c | ||
| 
						 | 
					b645365546 | ||
| 
						 | 
					95e4a2b3ae | ||
| 
						 | 
					ea2147af90 | ||
| 
						 | 
					8938431fdc | ||
| 
						 | 
					7d0f9a0ec3 | ||
| 
						 | 
					30d9a5989e | ||
| 
						 | 
					022f9e56e5 | ||
| 
						 | 
					7fd9a29eba | ||
| 
						 | 
					58cd1d2f8a | ||
| 
						 | 
					4e8f2c310f | ||
| 
						 | 
					74aa3523f3 | ||
| 
						 | 
					259a694ba7 | ||
| 
						 | 
					9c4f9ffec1 | ||
| 
						 | 
					7d3ee9ed8d | ||
| 
						 | 
					228628860f | ||
| 
						 | 
					daa6c1c645 | ||
| 
						 | 
					4c5a7d315c | ||
| 
						 | 
					49efd9b606 | ||
| 
						 | 
					e309d80dd4 | ||
| 
						 | 
					2bbdf2e3d7 | ||
| 
						 | 
					cb98894eb1 | ||
| 
						 | 
					305763dc6f | ||
| 
						 | 
					9897b26e3e | ||
| 
						 | 
					f1159c559b | ||
| 
						 | 
					b47b137c66 | ||
| 
						 | 
					87eec1fd74 | ||
| 
						 | 
					133fe4a852 | ||
| 
						 | 
					569f0d27c7 | ||
| 
						 | 
					4808d1ceb8 | ||
| 
						 | 
					270c9ea5d9 | ||
| 
						 | 
					343ec9b9fd | ||
| 
						 | 
					8072e354cc | ||
| 
						 | 
					d87722d03c | ||
| 
						 | 
					46fd7a0316 | ||
| 
						 | 
					a2848546d2 | ||
| 
						 | 
					2c1fed20ab | ||
| 
						 | 
					e9bddabc01 | ||
| 
						 | 
					b6450840dc | ||
| 
						 | 
					b1bbcae71c | ||
| 
						 | 
					758ace867b | ||
| 
						 | 
					16705a4db1 | ||
| 
						 | 
					4d1663594b | ||
| 
						 | 
					983bb7eb31 | ||
| 
						 | 
					528820dbe1 | ||
| 
						 | 
					87c6dd3c26 | ||
| 
						 | 
					38de84cdc4 | ||
| 
						 | 
					9f6d1a85e1 | ||
| 
						 | 
					b385969039 | ||
| 
						 | 
					442198cd41 | ||
| 
						 | 
					e9f77d377b | ||
| 
						 | 
					7bde60f260 | ||
| 
						 | 
					b3f239785c | ||
| 
						 | 
					834cbab97d | ||
| 
						 | 
					9767e44fd4 | ||
| 
						 | 
					1136fdb563 | ||
| 
						 | 
					35668a2e06 | ||
| 
						 | 
					a611b4f6a0 | ||
| 
						 | 
					8353d97904 | ||
| 
						 | 
					8347359cb0 | ||
| 
						 | 
					a6c5db954f | ||
| 
						 | 
					1ab35d100b | ||
| 
						 | 
					5bece2a0ed | ||
| 
						 | 
					3839605ec9 | ||
| 
						 | 
					974cc7b324 | ||
| 
						 | 
					d038361e95 | ||
| 
						 | 
					0d0ac1b949 | ||
| 
						 | 
					194b6f06a0 | ||
| 
						 | 
					08c5037d60 | ||
| 
						 | 
					781f04a8f8 | ||
| 
						 | 
					24c643e143 | ||
| 
						 | 
					19622aee5b | ||
| 
						 | 
					c1a726c573 | ||
| 
						 | 
					5d5b50aaf3 | ||
| 
						 | 
					9459ebde32 | ||
| 
						 | 
					290d9030e9 | ||
| 
						 | 
					0a369e560c | ||
| 
						 | 
					84abd2f65e | ||
| 
						 | 
					01aef5ed8b | ||
| 
						 | 
					5d2d2ec466 | ||
| 
						 | 
					a1ac2f017f | ||
| 
						 | 
					5fc95771bb | ||
| 
						 | 
					bd9973a64f | ||
| 
						 | 
					e3ef51ec06 | ||
| 
						 | 
					cd6c466922 | ||
| 
						 | 
					6ce1299567 | ||
| 
						 | 
					9b5c960f55 | ||
| 
						 | 
					8e2bd1e79d | ||
| 
						 | 
					40a9e603b9 | ||
| 
						 | 
					38aaa10ed4 | ||
| 
						 | 
					2597cf1494 | ||
| 
						 | 
					542e388e4f | ||
| 
						 | 
					db56a3563e | ||
| 
						 | 
					962d717f41 | ||
| 
						 | 
					1d68cdff92 | ||
| 
						 | 
					1156776572 | ||
| 
						 | 
					a76cdaceb2 | ||
| 
						 | 
					59d64fb234 | ||
| 
						 | 
					7a1fdfde8a | ||
| 
						 | 
					c0ddaa94e6 | ||
| 
						 | 
					e4bcd2103c | ||
| 
						 | 
					c82c1f5efc | ||
| 
						 | 
					48587d5475 | ||
| 
						 | 
					1d52c1e02e | ||
| 
						 | 
					0f41b710b7 | ||
| 
						 | 
					11b0f00c00 | ||
| 
						 | 
					ce0dae9fda | ||
| 
						 | 
					fad2128d17 | ||
| 
						 | 
					b69928cfaf | ||
| 
						 | 
					59fc0bda6e | ||
| 
						 | 
					0052051c07 | ||
| 
						 | 
					b75c27febd | ||
| 
						 | 
					183b01eb4c | ||
| 
						 | 
					fad557ec0f | ||
| 
						 | 
					f2dad593ae | ||
| 
						 | 
					5f10fbb166 | ||
| 
						 | 
					a2c574ede1 | ||
| 
						 | 
					74b750df81 | ||
| 
						 | 
					811c90c5fd | ||
| 
						 | 
					9d5a115ee2 | ||
| 
						 | 
					8f628ab77f | ||
| 
						 | 
					eb1e0fa859 | ||
| 
						 | 
					5935707489 | ||
| 
						 | 
					9e7361a8ba | ||
| 
						 | 
					bcb226d607 | ||
| 
						 | 
					85faa762c3 | ||
| 
						 | 
					f2c6c83816 | ||
| 
						 | 
					732373d7b4 | ||
| 
						 | 
					26743ac4f9 | ||
| 
						 | 
					f861d312fe | ||
| 
						 | 
					72e395656d | ||
| 
						 | 
					259532fcf9 | ||
| 
						 | 
					d21e961a8b | ||
| 
						 | 
					cbf05f5146 | ||
| 
						 | 
					43f0bce253 | ||
| 
						 | 
					81b610ea2c | ||
| 
						 | 
					07b6487c5a | ||
| 
						 | 
					b90d34b3d1 | ||
| 
						 | 
					a0a0b7fb0e | ||
| 
						 | 
					088dcbc016 | ||
| 
						 | 
					4c593c8c8a | ||
| 
						 | 
					a2d972a38a | ||
| 
						 | 
					3f2022e2cb | ||
| 
						 | 
					93ad3fd9b9 | ||
| 
						 | 
					0614e9333f | ||
| 
						 | 
					fac8332649 | ||
| 
						 | 
					c9915660ff | ||
| 
						 | 
					6fd4733041 | ||
| 
						 | 
					0d7bc876c0 | ||
| 
						 | 
					0b0391ff66 | ||
| 
						 | 
					2ae8337669 | ||
| 
						 | 
					c10cb81593 | ||
| 
						 | 
					d0156fd6a8 | ||
| 
						 | 
					c33a024d4a | ||
| 
						 | 
					5bb87b83d1 | ||
| 
						 | 
					c68e97012c | ||
| 
						 | 
					7b9e56a500 | ||
| 
						 | 
					09afd6f230 | ||
| 
						 | 
					97e88fd35f | ||
| 
						 | 
					a107f8f496 | ||
| 
						 | 
					2338e5318e | ||
| 
						 | 
					5dc6e465cb | ||
| 
						 | 
					ce60011e77 | ||
| 
						 | 
					de0e558baf | ||
| 
						 | 
					322456ed47 | ||
| 
						 | 
					17402a5902 | ||
| 
						 | 
					f094b46d1c | ||
| 
						 | 
					db9ea55c6e | ||
| 
						 | 
					e6bca376aa | ||
| 
						 | 
					98eb03c391 | ||
| 
						 | 
					9ee90d2323 | ||
| 
						 | 
					acd67cbdf1 | ||
| 
						 | 
					20331ae5f6 | ||
| 
						 | 
					3301cc900e | ||
| 
						 | 
					3f27fb56e4 | ||
| 
						 | 
					72ec18ce5a | ||
| 
						 | 
					a4e8c10a38 | ||
| 
						 | 
					2e90a1f015 | ||
| 
						 | 
					b30f28f38d | ||
| 
						 | 
					c374ab00ac | ||
| 
						 | 
					c8280a538a | ||
| 
						 | 
					f5e275aec0 | ||
| 
						 | 
					70a211747b | ||
| 
						 | 
					99eb353337 | ||
| 
						 | 
					f3ac06bbaf | ||
| 
						 | 
					b2653fe619 | ||
| 
						 | 
					799cf8221a | ||
| 
						 | 
					d2e50e7f21 | ||
| 
						 | 
					a0cbde700a | ||
| 
						 | 
					ce160147f4 | ||
| 
						 | 
					d71c566ee6 | ||
| 
						 | 
					0b25f693b3 | ||
| 
						 | 
					f497412f20 | ||
| 
						 | 
					2b6acd8873 | ||
| 
						 | 
					e5e2f747c3 | ||
| 
						 | 
					24fa27453f | ||
| 
						 | 
					980ef1e38b | ||
| 
						 | 
					007056e4ad | ||
| 
						 | 
					754b093d16 | ||
| 
						 | 
					de63548f04 | ||
| 
						 | 
					10ec506cc9 | ||
| 
						 | 
					e50ce6e45c | ||
| 
						 | 
					20848c3ae5 | ||
| 
						 | 
					7faa5da209 | ||
| 
						 | 
					efad80bfbf | ||
| 
						 | 
					81b40cbaf3 | ||
| 
						 | 
					866becefff | ||
| 
						 | 
					2f16924959 | ||
| 
						 | 
					570f9135cd | ||
| 
						 | 
					de7bb75ccf | ||
| 
						 | 
					1de6873810 | ||
| 
						 | 
					29aeb901e4 | ||
| 
						 | 
					773cb74ec8 | ||
| 
						 | 
					40d4e35fb3 | ||
| 
						 | 
					bbafcc1602 | ||
| 
						 | 
					1145fd263c | ||
| 
						 | 
					2679ec0a9f | ||
| 
						 | 
					f22163b1df | ||
| 
						 | 
					6fd8ffb6fe | ||
| 
						 | 
					0daf913e0c | ||
| 
						 | 
					b47e53b5fa | ||
| 
						 | 
					5fc6e010a5 | ||
| 
						 | 
					55f06c3d77 | ||
| 
						 | 
					e60d9c7e9d | ||
| 
						 | 
					1553049226 | ||
| 
						 | 
					4b57b6da54 | ||
| 
						 | 
					d8e5e8bb3b | ||
| 
						 | 
					e87066d01e | ||
| 
						 | 
					584daba8e9 | ||
| 
						 | 
					39a65056da | ||
| 
						 | 
					8a87f913bd | ||
| 
						 | 
					74bc150ab2 | ||
| 
						 | 
					fa5f91c05f | ||
| 
						 | 
					fb65682d34 | ||
| 
						 | 
					2d075be3b8 | ||
| 
						 | 
					9b2f7c420e | ||
| 
						 | 
					5574a58cd6 | ||
| 
						 | 
					fee767f21c | ||
| 
						 | 
					9a530a2430 | ||
| 
						 | 
					9cbfe11ee2 | ||
| 
						 | 
					c74e217799 | ||
| 
						 | 
					ac2b866426 | ||
| 
						 | 
					3ff1a3c6d5 | ||
| 
						 | 
					8832c2e5d4 | ||
| 
						 | 
					d83c8ffb6b | ||
| 
						 | 
					1ec29c7324 | ||
| 
						 | 
					28b4d27209 | ||
| 
						 | 
					ea9de4ae25 | ||
| 
						 | 
					5e300ce565 | ||
| 
						 | 
					47de23266d | ||
| 
						 | 
					d636f74923 | ||
| 
						 | 
					f960d5bcf4 | ||
| 
						 | 
					3568fcfa7d | ||
| 
						 | 
					3898cd6843 | ||
| 
						 | 
					9f62b92258 | ||
| 
						 | 
					f6b83a24a3 | ||
| 
						 | 
					4590b91728 | ||
| 
						 | 
					e525bf94ba | ||
| 
						 | 
					e4a243c02b | ||
| 
						 | 
					463746917f | ||
| 
						 | 
					8ea3cbe6b2 | ||
| 
						 | 
					034e32f0eb | ||
| 
						 | 
					29248d6941 | ||
| 
						 | 
					cc76d412fd | ||
| 
						 | 
					bc4f3930a9 | ||
| 
						 | 
					bcc6408cd1 | ||
| 
						 | 
					e36fb5b802 | ||
| 
						 | 
					900c2e277a | ||
| 
						 | 
					771da85a11 | ||
| 
						 | 
					e26467c4ed | ||
| 
						 | 
					60bf845f25 | ||
| 
						 | 
					c515551625 | ||
| 
						 | 
					5b71697618 | ||
| 
						 | 
					7cce825fa4 | ||
| 
						 | 
					98e4c53cad | ||
| 
						 | 
					c0b4f4a633 | ||
| 
						 | 
					c0190c8a5a | ||
| 
						 | 
					72c102acf8 | ||
| 
						 | 
					cdbc5dbd1c | ||
| 
						 | 
					ea39fade07 | ||
| 
						 | 
					87bfbe4fe2 | ||
| 
						 | 
					5cb002f0ef | ||
| 
						 | 
					99c437b7c7 | ||
| 
						 | 
					54681c3ffe | ||
| 
						 | 
					bd0b0b3242 | ||
| 
						 | 
					771de1f439 | ||
| 
						 | 
					65a0d1d19b | ||
| 
						 | 
					5530f12f71 | ||
| 
						 | 
					d7c3265223 | ||
| 
						 | 
					b507e428e8 | ||
| 
						 | 
					fbfab297ee | ||
| 
						 | 
					db12f254ce | ||
| 
						 | 
					506eb23dc5 | ||
| 
						 | 
					585cfb28b8 | ||
| 
						 | 
					3f064f516d | ||
| 
						 | 
					32499b614b | ||
| 
						 | 
					30f6617c79 | ||
| 
						 | 
					d0fc9e80fe | ||
| 
						 | 
					3a61b920f2 | ||
| 
						 | 
					f6d282822e | ||
| 
						 | 
					c925ccccfc | ||
| 
						 | 
					657a4c0ccd | ||
| 
						 | 
					fc75cc0ecf | ||
| 
						 | 
					1e6eb30f51 | ||
| 
						 | 
					1d8497ba6a | ||
| 
						 | 
					ffebd24456 | ||
| 
						 | 
					17954da56c | ||
| 
						 | 
					78f58618f3 | ||
| 
						 | 
					bd0dac3783 | ||
| 
						 | 
					0bf622e057 | ||
| 
						 | 
					fd734de4d1 | ||
| 
						 | 
					87b6e7dbed | ||
| 
						 | 
					ce27d1e126 | ||
| 
						 | 
					ef444142c8 | ||
| 
						 | 
					05bca3524a | ||
| 
						 | 
					b3b0c49d1c | ||
| 
						 | 
					cef2f843b4 | ||
| 
						 | 
					afbf189ef2 | ||
| 
						 | 
					d752d7cebe | ||
| 
						 | 
					8dc09e73d0 | ||
| 
						 | 
					f681f07cd0 | ||
| 
						 | 
					2ccb6aef89 | ||
| 
						 | 
					58543709e4 | ||
| 
						 | 
					1ec211f57f | ||
| 
						 | 
					1879442443 | ||
| 
						 | 
					f4c77e686a | ||
| 
						 | 
					ad586a9fe4 | ||
| 
						 | 
					bddf1ad7f6 | ||
| 
						 | 
					9d1cdb1f69 | ||
| 
						 | 
					488aa29083 | ||
| 
						 | 
					e13cdc503e | ||
| 
						 | 
					812d466bbd | ||
| 
						 | 
					04797b1e35 | ||
| 
						 | 
					2beb1b85e0 | ||
| 
						 | 
					2b5c629055 | ||
| 
						 | 
					63700ead34 | ||
| 
						 | 
					474dc77894 | ||
| 
						 | 
					38cfa734f4 | ||
| 
						 | 
					5211d9deca | ||
| 
						 | 
					cfb6321b88 | ||
| 
						 | 
					5360ef5447 | ||
| 
						 | 
					e46d8dcaab | ||
| 
						 | 
					28f160e76c | ||
| 
						 | 
					45fdc44d68 | ||
| 
						 | 
					37ae22ab13 | ||
| 
						 | 
					a8892a69b3 | ||
| 
						 | 
					ef1fe58e19 | ||
| 
						 | 
					df0fd8ba27 | ||
| 
						 | 
					9e5ef54594 | ||
| 
						 | 
					c7849c2dfe | ||
| 
						 | 
					aef6bf43e5 | ||
| 
						 | 
					2585451408 | ||
| 
						 | 
					19b15a5b93 | ||
| 
						 | 
					3bcc7ab418 | ||
| 
						 | 
					cd2ac56bd4 | ||
| 
						 | 
					2c076bcb4d | ||
| 
						 | 
					6170ac041f | ||
| 
						 | 
					aad87b66e8 | ||
| 
						 | 
					ab8b01effd | ||
| 
						 | 
					7b7f2048b8 | ||
| 
						 | 
					8bb7904458 | ||
| 
						 | 
					f3038e7b2a | ||
| 
						 | 
					f17dfc062a | ||
| 
						 | 
					210ccf4a1d | ||
| 
						 | 
					5c3a9880ca | ||
| 
						 | 
					e4e70d052e | ||
| 
						 | 
					fba931bab6 | ||
| 
						 | 
					243a204021 | ||
| 
						 | 
					2d24eba903 | ||
| 
						 | 
					3a499f3cb2 | ||
| 
						 | 
					0d482c5c97 | ||
| 
						 | 
					1f0ca54bba | ||
| 
						 | 
					7fe3895b46 | ||
| 
						 | 
					79c6c4db3b | ||
| 
						 | 
					76791d20f5 | ||
| 
						 | 
					6d73205280 | ||
| 
						 | 
					b7439f28a1 | ||
| 
						 | 
					3f640773f3 | ||
| 
						 | 
					5c3783b7e8 | ||
| 
						 | 
					a558ad4269 | ||
| 
						 | 
					eb64d43922 | ||
| 
						 | 
					41d6b35670 | ||
| 
						 | 
					0e6e45a65f | ||
| 
						 | 
					34513feff9 | ||
| 
						 | 
					902e3e58db | ||
| 
						 | 
					12a0987b36 | ||
| 
						 | 
					0e6ac799f7 | ||
| 
						 | 
					8ab35b14e9 | ||
| 
						 | 
					32518cce5a | ||
| 
						 | 
					f57dccbbfb | ||
| 
						 | 
					ba66ea4406 | ||
| 
						 | 
					2ba608415b | ||
| 
						 | 
					4a1796fe76 | ||
| 
						 | 
					c1e44908fe | ||
| 
						 | 
					3fa235fe01 | ||
| 
						 | 
					d4a39291e0 | ||
| 
						 | 
					fb59a93425 | ||
| 
						 | 
					e742cc0997 | ||
| 
						 | 
					3eb47363ad | ||
| 
						 | 
					0367ddd62b | ||
| 
						 | 
					6e96dd4665 | ||
| 
						 | 
					898dddb1d1 | ||
| 
						 | 
					48b1e7a86f | ||
| 
						 | 
					3447c4a8a4 | ||
| 
						 | 
					1473b377c2 | ||
| 
						 | 
					8a8e0fb267 | ||
| 
						 | 
					fb3fd09353 | ||
| 
						 | 
					a4de02db5d | ||
| 
						 | 
					02fbfc15c7 | ||
| 
						 | 
					a0353547b1 | ||
| 
						 | 
					136ebccc5e | ||
| 
						 | 
					51754b6f35 | ||
| 
						 | 
					d5ffeb5e63 | ||
| 
						 | 
					f4d3973688 | ||
| 
						 | 
					0fb91b736c | ||
| 
						 | 
					a3ff316c5f | ||
| 
						 | 
					ed6e4acdf6 | ||
| 
						 | 
					0620656288 | ||
| 
						 | 
					e43ef21d35 | ||
| 
						 | 
					bea2edbc46 | ||
| 
						 | 
					1beed38b54 | ||
| 
						 | 
					fa3085b45e | ||
| 
						 | 
					86e35e4887 | ||
| 
						 | 
					8119ecd2db | ||
| 
						 | 
					d3a0d91a38 | ||
| 
						 | 
					01f9bc7bf1 | ||
| 
						 | 
					9feaddc390 | ||
| 
						 | 
					360e021d0a | ||
| 
						 | 
					807dde070f | ||
| 
						 | 
					57d3515abd | ||
| 
						 | 
					2a47c73217 | ||
| 
						 | 
					b26854c276 | ||
| 
						 | 
					bd5647ee92 | ||
| 
						 | 
					23c0e018e4 | ||
| 
						 | 
					0160a29b6c | ||
| 
						 | 
					fc8afc2f33 | ||
| 
						 | 
					81b58ccd94 | ||
| 
						 | 
					7426c5f957 | ||
| 
						 | 
					0c55bf19a5 | ||
| 
						 | 
					1f2e69fd35 | ||
| 
						 | 
					f0af1b051a | ||
| 
						 | 
					99278b1050 | ||
| 
						 | 
					5b521891ba | ||
| 
						 | 
					64bc9400d4 | ||
| 
						 | 
					61469d1d86 | ||
| 
						 | 
					47aab58ec3 | ||
| 
						 | 
					dfb08d31fe | ||
| 
						 | 
					96481c8d3f | ||
| 
						 | 
					2eeddee132 | ||
| 
						 | 
					57d91ea785 | ||
| 
						 | 
					d2829b89c1 | ||
| 
						 | 
					f605f3d891 | ||
| 
						 | 
					452b533114 | ||
| 
						 | 
					5c8f1ccff0 | ||
| 
						 | 
					6bc5c2aac6 | ||
| 
						 | 
					5828b60af5 | ||
| 
						 | 
					58721eb350 | ||
| 
						 | 
					6fb29639df | ||
| 
						 | 
					032f94b099 | ||
| 
						 | 
					32b58e6418 | ||
| 
						 | 
					a27873f1e2 | ||
| 
						 | 
					3a89f21540 | ||
| 
						 | 
					d930b4d5f4 | ||
| 
						 | 
					8f934f55d7 | ||
| 
						 | 
					b1776b65b3 | ||
| 
						 | 
					4e2a1e3e98 | ||
| 
						 | 
					c182b98998 | ||
| 
						 | 
					b682cd6a51 | ||
| 
						 | 
					4095f31fea | ||
| 
						 | 
					4ff37feb4c | ||
| 
						 | 
					b9ba5ad239 | ||
| 
						 | 
					4214c4ae53 | ||
| 
						 | 
					099f4f2169 | ||
| 
						 | 
					173d7fdbb9 | ||
| 
						 | 
					076122f592 | ||
| 
						 | 
					ac5e3e222a | ||
| 
						 | 
					516697e29a | ||
| 
						 | 
					3e9c071aa6 | ||
| 
						 | 
					e63c72acfb | ||
| 
						 | 
					54af2dba78 | ||
| 
						 | 
					448750e4e2 | ||
| 
						 | 
					218ee98de0 | ||
| 
						 | 
					09269017d3 | ||
| 
						 | 
					3df1532e97 | ||
| 
						 | 
					e176a4d047 | ||
| 
						 | 
					082443d2c7 | ||
| 
						 | 
					78ab624c2a | ||
| 
						 | 
					f0f7df1b87 | ||
| 
						 | 
					143b2da4f8 | ||
| 
						 | 
					7a9c1660cc | ||
| 
						 | 
					0b0748a4be | ||
| 
						 | 
					45143d270c | ||
| 
						 | 
					f1a334be63 | ||
| 
						 | 
					513c9bca17 | ||
| 
						 | 
					33c52b6271 | ||
| 
						 | 
					7e8e3978fe | ||
| 
						 | 
					8ccf704608 | ||
| 
						 | 
					42f2d61ac9 | ||
| 
						 | 
					e6e4898027 | ||
| 
						 | 
					9f06cbff6d | ||
| 
						 | 
					a661bcd086 | ||
| 
						 | 
					39f845848c | ||
| 
						 | 
					782da2cf95 | ||
| 
						 | 
					89b85e078e | ||
| 
						 | 
					f75381498c | ||
| 
						 | 
					8b4bd46b95 | ||
| 
						 | 
					74906224ca | ||
| 
						 | 
					5a5919435e | ||
| 
						 | 
					bdc55fad62 | ||
| 
						 | 
					8343b4adbb | ||
| 
						 | 
					d2a219e644 | ||
| 
						 | 
					731e2bb328 | ||
| 
						 | 
					77988d469d | ||
| 
						 | 
					847ed9f8cd | ||
| 
						 | 
					4da385998a | ||
| 
						 | 
					c5407c775a | ||
| 
						 | 
					6dc90b8c86 | ||
| 
						 | 
					2afec6dba5 | ||
| 
						 | 
					0cc7212cfd | ||
| 
						 | 
					adca67bcbb | ||
| 
						 | 
					f633b8d8b2 | ||
| 
						 | 
					1187a7719c | ||
| 
						 | 
					910a387b0e | ||
| 
						 | 
					4382e4e8fe | ||
| 
						 | 
					735e435e8e | ||
| 
						 | 
					5b22fb7953 | ||
| 
						 | 
					c907b88ecd | ||
| 
						 | 
					4c112dc5a6 | ||
| 
						 | 
					137fd59bf4 | ||
| 
						 | 
					c9ce6f916e | ||
| 
						 | 
					5441e1f2f0 | ||
| 
						 | 
					5d7f757e49 | ||
| 
						 | 
					0fdaa9d383 | ||
| 
						 | 
					7e4921d8e2 | ||
| 
						 | 
					69d585e148 | ||
| 
						 | 
					a76a7d0c6c | ||
| 
						 | 
					d000d80968 | ||
| 
						 | 
					92e9c17aec | ||
| 
						 | 
					92b7a50605 | ||
| 
						 | 
					e9fe0e3d06 | ||
| 
						 | 
					a296118e6d | ||
| 
						 | 
					164b59d757 | ||
| 
						 | 
					d2d51ed109 | ||
| 
						 | 
					01bd0cc42f | ||
| 
						 | 
					9f46071409 | ||
| 
						 | 
					c59ef12e51 | ||
| 
						 | 
					2ecf0fdfc2 | ||
| 
						 | 
					46fbfceac6 | ||
| 
						 | 
					fdd79e9828 | ||
| 
						 | 
					cac6b66638 | ||
| 
						 | 
					088c7df571 | ||
| 
						 | 
					0e5998087e | ||
| 
						 | 
					0524e38d9e | ||
| 
						 | 
					d81b3bf360 | ||
| 
						 | 
					4cb6e04914 | ||
| 
						 | 
					2399b1dbfc | ||
| 
						 | 
					186206cff2 | ||
| 
						 | 
					6298fbb7b2 | ||
| 
						 | 
					93d947f5e8 | ||
| 
						 | 
					896574e92b | ||
| 
						 | 
					529ce88545 | ||
| 
						 | 
					2141962baf | ||
| 
						 | 
					71affcedba | ||
| 
						 | 
					3e659ea9b7 | ||
| 
						 | 
					00fd0df046 | ||
| 
						 | 
					9d7357e4fe | ||
| 
						 | 
					d0532b53eb | ||
| 
						 | 
					ea98b7d784 | ||
| 
						 | 
					7d32f55e4e | ||
| 
						 | 
					2617bf2016 | ||
| 
						 | 
					58818585bc | ||
| 
						 | 
					8dce1de6d2 | ||
| 
						 | 
					5081806f4d | ||
| 
						 | 
					b3a17d6074 | ||
| 
						 | 
					a4f570fe7a | ||
| 
						 | 
					7df82d412e | ||
| 
						 | 
					869c0c2e55 | ||
| 
						 | 
					0bc982e714 | ||
| 
						 | 
					467f633b16 | ||
| 
						 | 
					b2de1f7888 | ||
| 
						 | 
					effdec6e13 | ||
| 
						 | 
					20c7c46bce | ||
| 
						 | 
					406a1f0acf | ||
| 
						 | 
					b2f0b62cd4 | ||
| 
						 | 
					327e121a0f | ||
| 
						 | 
					1714aeaa67 | ||
| 
						 | 
					59e4a4fee1 | ||
| 
						 | 
					ee5be3a009 | ||
| 
						 | 
					c21f007277 | ||
| 
						 | 
					ea9968f685 | ||
| 
						 | 
					ef784e4e9e | ||
| 
						 | 
					912131803b | ||
| 
						 | 
					1a11d1db09 | ||
| 
						 | 
					25a3ca4e59 | ||
| 
						 | 
					ead08aae35 | ||
| 
						 | 
					39eb95f130 | ||
| 
						 | 
					cea806e5b9 | ||
| 
						 | 
					b4d368b576 | ||
| 
						 | 
					da0a194b57 | ||
| 
						 | 
					59f50c2718 | ||
| 
						 | 
					701afa4b3a | ||
| 
						 | 
					a5eb67d91c | ||
| 
						 | 
					842d781b5e | ||
| 
						 | 
					b709144f1b | ||
| 
						 | 
					8d55563523 | ||
| 
						 | 
					1bcfa9aacf | ||
| 
						 | 
					bfc54b551b | ||
| 
						 | 
					49beba49eb | ||
| 
						 | 
					d34ec1b969 | ||
| 
						 | 
					47c682937c | ||
| 
						 | 
					e64917a932 | ||
| 
						 | 
					9dab1baef8 | ||
| 
						 | 
					f826e8ab2b | ||
| 
						 | 
					3e51d3e5bd | ||
| 
						 | 
					ac0490ad2a | ||
| 
						 | 
					4c9ec22546 | ||
| 
						 | 
					a4f4822784 | ||
| 
						 | 
					5b0df1f1c5 | ||
| 
						 | 
					5759a19020 | ||
| 
						 | 
					1962136a33 | ||
| 
						 | 
					5a6bcfb797 | ||
| 
						 | 
					8bfa659087 | ||
| 
						 | 
					84bf0faed9 | ||
| 
						 | 
					b216c6b165 | ||
| 
						 | 
					10e37a5089 | ||
| 
						 | 
					d78adfb577 | ||
| 
						 | 
					1f18909335 | ||
| 
						 | 
					b1be6112bb | ||
| 
						 | 
					f60cf62f4f | ||
| 
						 | 
					a10c39866b | ||
| 
						 | 
					341dccd7e2 | ||
| 
						 | 
					fd13f6c199 | ||
| 
						 | 
					c4178e55ea | ||
| 
						 | 
					0cd8e4eade | ||
| 
						 | 
					bc219d5450 | ||
| 
						 | 
					cd34dd3b2b | ||
| 
						 | 
					f7740aa44b | ||
| 
						 | 
					05d7b5dd59 | ||
| 
						 | 
					18a17aa487 | ||
| 
						 | 
					2813f931dd | ||
| 
						 | 
					717cdf5405 | ||
| 
						 | 
					9530a404ce | ||
| 
						 | 
					8c8027c307 | ||
| 
						 | 
					aae1bfbbe0 | ||
| 
						 | 
					5579595464 | ||
| 
						 | 
					241f5bcb00 | ||
| 
						 | 
					3741f14689 | ||
| 
						 | 
					865436dee0 | ||
| 
						 | 
					a468cfaf2e | ||
| 
						 | 
					062dfa1d0c | ||
| 
						 | 
					49b78229ca | ||
| 
						 | 
					c136be04f7 | ||
| 
						 | 
					0fb294a8dd | ||
| 
						 | 
					9dabfa2c2b | ||
| 
						 | 
					356ac618f1 | ||
| 
						 | 
					e8284a7f92 | ||
| 
						 | 
					a49475b5a8 | ||
| 
						 | 
					ccde5c9557 | ||
| 
						 | 
					3de6d0602f | ||
| 
						 | 
					333d7e6345 | ||
| 
						 | 
					e6d26ec09c | ||
| 
						 | 
					5382e0fc1f | ||
| 
						 | 
					dd1700a397 | ||
| 
						 | 
					7783964bb9 | ||
| 
						 | 
					0a8fae8d14 | ||
| 
						 | 
					db84235a0b | ||
| 
						 | 
					20827374e9 | ||
| 
						 | 
					b86a30dc22 | ||
| 
						 | 
					5dd8d1bbd8 | ||
| 
						 | 
					727295f206 | ||
| 
						 | 
					1275a3f91a | ||
| 
						 | 
					a01e2ee177 | ||
| 
						 | 
					d0a887b28b | ||
| 
						 | 
					4ae5406959 | ||
| 
						 | 
					b609190369 | ||
| 
						 | 
					49b83ec3a3 | ||
| 
						 | 
					78ce59137f | ||
| 
						 | 
					de9da39b33 | ||
| 
						 | 
					d34646a865 | ||
| 
						 | 
					53f0b4deb6 | ||
| 
						 | 
					d935d88a8c | ||
| 
						 | 
					e9a138e111 | ||
| 
						 | 
					1d8744ce96 | ||
| 
						 | 
					8adfcd06a1 | ||
| 
						 | 
					127a1e0750 | ||
| 
						 | 
					b3df58660f | ||
| 
						 | 
					b78a4a6dfe | ||
| 
						 | 
					06bdb3550c | ||
| 
						 | 
					67b89cae08 | ||
| 
						 | 
					e66de5b5ae | ||
| 
						 | 
					ee78bf0882 | ||
| 
						 | 
					da7250ad2c | ||
| 
						 | 
					963cdaffd5 | ||
| 
						 | 
					1f2bb6e93e | ||
| 
						 | 
					5395073fff | ||
| 
						 | 
					cc4214a429 | ||
| 
						 | 
					0e63644d14 | ||
| 
						 | 
					34bfbdaf9e | ||
| 
						 | 
					168911b438 | ||
| 
						 | 
					68fc12775f | ||
| 
						 | 
					5d93f0f4ec | ||
| 
						 | 
					bb47d957a8 | ||
| 
						 | 
					f86307e1e4 | ||
| 
						 | 
					ed3413e397 | ||
| 
						 | 
					c0c3afd079 | ||
| 
						 | 
					ae4838101a | ||
| 
						 | 
					fb41afaaf6 | ||
| 
						 | 
					9434e52af9 | ||
| 
						 | 
					f276138202 | ||
| 
						 | 
					d453eaa788 | ||
| 
						 | 
					f868bdbe76 | ||
| 
						 | 
					d71e8b32e3 | ||
| 
						 | 
					4ea452689d | ||
| 
						 | 
					e4727a3591 | ||
| 
						 | 
					d38b92e972 | ||
| 
						 | 
					0a4a6c1200 | ||
| 
						 | 
					6348aea6a2 | ||
| 
						 | 
					da66f71ffe | ||
| 
						 | 
					da933e0ff8 | ||
| 
						 | 
					fd9e16ce97 | ||
| 
						 | 
					465f5bbb6f | ||
| 
						 | 
					628dcfbc97 | ||
| 
						 | 
					0ee31cfa38 | ||
| 
						 | 
					8e70bb5bb4 | ||
| 
						 | 
					22a901905c | ||
| 
						 | 
					3463bd4adc | ||
| 
						 | 
					e26ee01d56 | ||
| 
						 | 
					0241526836 | ||
| 
						 | 
					9f5f008aed | ||
| 
						 | 
					8d2d9e8985 | ||
| 
						 | 
					9bbe1600cc | ||
| 
						 | 
					16ddc90eab | ||
| 
						 | 
					7c7a86c080 | ||
| 
						 | 
					a35c911a91 | ||
| 
						 | 
					3c2eaebd21 | ||
| 
						 | 
					fd71384104 | ||
| 
						 | 
					b119198992 | ||
| 
						 | 
					01826c13b1 | ||
| 
						 | 
					959d1dee67 | ||
| 
						 | 
					eebcb1e3e8 | ||
| 
						 | 
					02352b487a | ||
| 
						 | 
					50b097003b | ||
| 
						 | 
					e6cadb4e3c | ||
| 
						 | 
					1ec4d80176 | ||
| 
						 | 
					9876f4bb21 | ||
| 
						 | 
					7fd177b91c | ||
| 
						 | 
					2d91260ea4 | ||
| 
						 | 
					6bae2d11f1 | ||
| 
						 | 
					0b4da058ad | ||
| 
						 | 
					79cb245157 | ||
| 
						 | 
					cbf9a721d6 | ||
| 
						 | 
					505a86d396 | ||
| 
						 | 
					36bdc5f7a4 | ||
| 
						 | 
					d572054ca7 | ||
| 
						 | 
					878bd1f296 | ||
| 
						 | 
					1d7644b23a | ||
| 
						 | 
					2ec6b8e758 | ||
| 
						 | 
					df69809b82 | ||
| 
						 | 
					5d94b5455f | ||
| 
						 | 
					1a679127af | ||
| 
						 | 
					cf2152b24c | ||
| 
						 | 
					e8ccafc63d | ||
| 
						 | 
					914955209e | ||
| 
						 | 
					899d36d813 | ||
| 
						 | 
					d32aa03520 | ||
| 
						 | 
					2c9f980163 | ||
| 
						 | 
					99ab0a8fa0 | ||
| 
						 | 
					550a54184b | ||
| 
						 | 
					23617c001d | ||
| 
						 | 
					189742b66c | ||
| 
						 | 
					e04fd0cf0f | ||
| 
						 | 
					6922bcd929 | ||
| 
						 | 
					7fdbf89ef3 | ||
| 
						 | 
					6f0dc96929 | ||
| 
						 | 
					1a5066112f | ||
| 
						 | 
					d302e4fb28 | ||
| 
						 | 
					68e2c6375e | ||
| 
						 | 
					cb846ecbbc | ||
| 
						 | 
					b82bd92e57 | ||
| 
						 | 
					2fcfc29020 | ||
| 
						 | 
					ce936f3cd4 | ||
| 
						 | 
					c1c9d6a9d8 | ||
| 
						 | 
					c8fd4b7c42 | ||
| 
						 | 
					f929e62525 | ||
| 
						 | 
					4acb6b7251 | ||
| 
						 | 
					95e379241a | ||
| 
						 | 
					543756adbe | ||
| 
						 | 
					2cb1547993 | ||
| 
						 | 
					9736d00b12 | ||
| 
						 | 
					660709dc7c | ||
| 
						 | 
					58b6646750 | ||
| 
						 | 
					ed3ae4a392 | ||
| 
						 | 
					f0bb25450c | ||
| 
						 | 
					de810f2005 | ||
| 
						 | 
					cad867ec8d | ||
| 
						 | 
					280448ba7b | ||
| 
						 | 
					1aa7527302 | ||
| 
						 | 
					ca025c02ef | ||
| 
						 | 
					97e48a3252 | ||
| 
						 | 
					63d33ad2d7 | ||
| 
						 | 
					7952282b78 | ||
| 
						 | 
					a859a21800 | ||
| 
						 | 
					7a05b039c8 | ||
| 
						 | 
					e44383baa4 | ||
| 
						 | 
					5a5d2b7a27 | ||
| 
						 | 
					2b09c39c9c | ||
| 
						 | 
					bacb65b48b | ||
| 
						 | 
					656eed5975 | ||
| 
						 | 
					173ef90a53 | ||
| 
						 | 
					fd263b0dfd | ||
| 
						 | 
					99db40ad2d | ||
| 
						 | 
					22d7e75e1f | ||
| 
						 | 
					f3f1bde4fc | ||
| 
						 | 
					528ff3910f | ||
| 
						 | 
					2efdf69734 | ||
| 
						 | 
					9d5580b6dd | ||
| 
						 | 
					4f8438a6cd | ||
| 
						 | 
					7f4841b3ac | ||
| 
						 | 
					7f28c97fcc | ||
| 
						 | 
					be881c028f | ||
| 
						 | 
					64e7b83139 | ||
| 
						 | 
					d6752491e1 | ||
| 
						 | 
					be314d9a54 | ||
| 
						 | 
					14e26cbca3 | ||
| 
						 | 
					2d2efb13e7 | ||
| 
						 | 
					0f5c6956dd | ||
| 
						 | 
					fbd82e4e9f | ||
| 
						 | 
					b55f313735 | ||
| 
						 | 
					215e18c9d4 | ||
| 
						 | 
					2305afd86c | ||
| 
						 | 
					4cc46d3e2a | ||
| 
						 | 
					9e8593917f | ||
| 
						 | 
					f1a7b8fc66 | ||
| 
						 | 
					7c72acaa94 | ||
| 
						 | 
					1ff70c26e3 | ||
| 
						 | 
					7d5157ee17 | ||
| 
						 | 
					be80c3670e | ||
| 
						 | 
					2647a337a8 | ||
| 
						 | 
					18831c3ca9 | ||
| 
						 | 
					7505f1d636 | ||
| 
						 | 
					4e7424d47b | ||
| 
						 | 
					9876a3ba5d | ||
| 
						 | 
					6b356a58d1 | ||
| 
						 | 
					f2694b74c9 | ||
| 
						 | 
					8eb17143f2 | ||
| 
						 | 
					81a04f7d79 | ||
| 
						 | 
					646da1ba8d | ||
| 
						 | 
					5a3c84d0fd | ||
| 
						 | 
					5f93f855a7 | ||
| 
						 | 
					f04a5b33ec | ||
| 
						 | 
					0316dc6e48 | ||
| 
						 | 
					29e3a2f0f3 | ||
| 
						 | 
					56d06f3e1e | ||
| 
						 | 
					2ca86afdec | ||
| 
						 | 
					741d25cb6f | ||
| 
						 | 
					f4bb42459c | ||
| 
						 | 
					4a07a3be11 | ||
| 
						 | 
					0df80be95e | ||
| 
						 | 
					acf66fb456 | ||
| 
						 | 
					a35122c496 | ||
| 
						 | 
					9f6867033f | ||
| 
						 | 
					36df7740dd | ||
| 
						 | 
					08c72fb4a9 | ||
| 
						 | 
					8cba7e9b6d | ||
| 
						 | 
					13965aed74 | ||
| 
						 | 
					db88380b76 | ||
| 
						 | 
					3b1c553773 | ||
| 
						 | 
					9b3d7e0159 | ||
| 
						 | 
					f9940ca8d7 | ||
| 
						 | 
					53bc1861c4 | ||
| 
						 | 
					b4beb545f7 | ||
| 
						 | 
					d7630f2256 | ||
| 
						 | 
					419b034975 | ||
| 
						 | 
					b41262fa0b | ||
| 
						 | 
					eb08f86c02 | ||
| 
						 | 
					f2f24b0959 | ||
| 
						 | 
					192bf33ffb | ||
| 
						 | 
					2bef4dbd43 | ||
| 
						 | 
					3fdcb2b84e | ||
| 
						 | 
					ae9c575d2c | ||
| 
						 | 
					c2141c6064 | ||
| 
						 | 
					70955c765c | ||
| 
						 | 
					a88d065606 | ||
| 
						 | 
					5058bd6e9e | ||
| 
						 | 
					fec94d1c5c | ||
| 
						 | 
					14bb0947b4 | ||
| 
						 | 
					3a3b6a7e86 | ||
| 
						 | 
					53efa3eeb6 | ||
| 
						 | 
					314ec4db40 | ||
| 
						 | 
					f0e403911d | ||
| 
						 | 
					6e9f9c20e9 | ||
| 
						 | 
					cc34a5b43f | ||
| 
						 | 
					22f8087b98 | ||
| 
						 | 
					fc1b3e6c90 | ||
| 
						 | 
					f2ba4cbf51 | ||
| 
						 | 
					7e7a261de0 | ||
| 
						 | 
					a47aaa4e7a | ||
| 
						 | 
					08f631c197 | ||
| 
						 | 
					369c2fb7b4 | ||
| 
						 | 
					166c9fc827 | ||
| 
						 | 
					7b57997874 | ||
| 
						 | 
					cb7289094a | ||
| 
						 | 
					96ccea8436 | ||
| 
						 | 
					4c9e549aa3 | ||
| 
						 | 
					0d05805b76 | ||
| 
						 | 
					ed2dbf6954 | ||
| 
						 | 
					bf49f042d4 | ||
| 
						 | 
					aa9daa1b9d | ||
| 
						 | 
					38f18698b3 | ||
| 
						 | 
					5ffbb2744f | ||
| 
						 | 
					a24e1cd508 | ||
| 
						 | 
					37005a165d | ||
| 
						 | 
					580edbc326 | ||
| 
						 | 
					8e323b39f9 | ||
| 
						 | 
					869449cdd0 | ||
| 
						 | 
					8f8197f3fd | ||
| 
						 | 
					a3a567e765 | ||
| 
						 | 
					e1ca87f057 | ||
| 
						 | 
					93c55d04e5 | ||
| 
						 | 
					2b3121eebf | ||
| 
						 | 
					2e3a81e4b3 | ||
| 
						 | 
					5c75480e90 | ||
| 
						 | 
					d88bb2e825 | ||
| 
						 | 
					c362df25a2 | ||
| 
						 | 
					27dc941475 | ||
| 
						 | 
					ead5e4724c | ||
| 
						 | 
					6e75bc7fe3 | ||
| 
						 | 
					845c01ef3f | ||
| 
						 | 
					554a835e90 | ||
| 
						 | 
					39c6c7f738 | ||
| 
						 | 
					b3100e187b | ||
| 
						 | 
					e8f5fe5255 | ||
| 
						 | 
					ce1beb423c | ||
| 
						 | 
					784a0bd000 | ||
| 
						 | 
					d87e1d6ab7 | ||
| 
						 | 
					6167925147 | ||
| 
						 | 
					4abc686d76 | ||
| 
						 | 
					392a545336 | ||
| 
						 | 
					4aa78a8bea | ||
| 
						 | 
					6c3dc61db5 | ||
| 
						 | 
					3b802e3c4a | ||
| 
						 | 
					690a734ebf | ||
| 
						 | 
					9e862e1e7f | ||
| 
						 | 
					d0222cfe2d | ||
| 
						 | 
					409f980a18 | ||
| 
						 | 
					14e00f8f66 | ||
| 
						 | 
					5265f59525 | ||
| 
						 | 
					86b6f05d19 | ||
| 
						 | 
					5643f35fb4 | ||
| 
						 | 
					76d767cbe8 | ||
| 
						 | 
					953c78987a | ||
| 
						 | 
					c3c58046c7 | ||
| 
						 | 
					111ebe84c2 | ||
| 
						 | 
					eb93f592e5 | ||
| 
						 | 
					f5898a0528 | ||
| 
						 | 
					edfd7e3d94 | ||
| 
						 | 
					acfb883011 | ||
| 
						 | 
					42aba81c2f | ||
| 
						 | 
					08e5d604d3 | ||
| 
						 | 
					19d1e9270d | ||
| 
						 | 
					218482769b | ||
| 
						 | 
					d32cbbb130 | ||
| 
						 | 
					8df447dc77 | ||
| 
						 | 
					b8a5426cf0 | ||
| 
						 | 
					2db0f08e08 | ||
| 
						 | 
					7c8d39a67b | ||
| 
						 | 
					c8cbfc2c98 | ||
| 
						 | 
					ae0a799f44 | ||
| 
						 | 
					91ff7d1864 | ||
| 
						 | 
					9659d59307 | ||
| 
						 | 
					cf6ae9d12f | ||
| 
						 | 
					af75ce8e15 | ||
| 
						 | 
					be4a08b58a | ||
| 
						 | 
					60f77033ad | ||
| 
						 | 
					e77d49f2a2 | ||
| 
						 | 
					ac49d0943a | ||
| 
						 | 
					16d29c7da4 | ||
| 
						 | 
					b6b3c7eb27 | ||
| 
						 | 
					1c95bd383e | ||
| 
						 | 
					159d4de370 | ||
| 
						 | 
					c62216b4fc | ||
| 
						 | 
					6835cead8c | ||
| 
						 | 
					6e013a136a | ||
| 
						 | 
					af387e2199 | ||
| 
						 | 
					444bc82081 | ||
| 
						 | 
					23c4b3f158 | ||
| 
						 | 
					af45473cd5 | ||
| 
						 | 
					cef20ae67a | ||
| 
						 | 
					ee31090b2e | ||
| 
						 | 
					64921d217b | ||
| 
						 | 
					56f99d19c3 | ||
| 
						 | 
					e0b21f41c2 | ||
| 
						 | 
					257b630216 | ||
| 
						 | 
					e91bd3babd | ||
| 
						 | 
					b139598b1c | ||
| 
						 | 
					076f5c794d | ||
| 
						 | 
					537b149828 | ||
| 
						 | 
					4a6fe534ce | ||
| 
						 | 
					2b349b5d33 | ||
| 
						 | 
					ebebad1c92 | ||
| 
						 | 
					56af6d55ed | ||
| 
						 | 
					f76fedeed5 | ||
| 
						 | 
					fea17f8b8c | ||
| 
						 | 
					af9a39d954 | ||
| 
						 | 
					28c40b1757 | ||
| 
						 | 
					3449a61032 | ||
| 
						 | 
					1c3b8998bc | ||
| 
						 | 
					ac28905082 | ||
| 
						 | 
					04e72d34f5 | ||
| 
						 | 
					6eed1911fd | ||
| 
						 | 
					b31f5ef699 | ||
| 
						 | 
					d4ad731bae | ||
| 
						 | 
					4f666bc113 | ||
| 
						 | 
					7f79f0d332 | ||
| 
						 | 
					77da35515c | ||
| 
						 | 
					7b3675bf7a | ||
| 
						 | 
					0f352a6f22 | ||
| 
						 | 
					bf9042203d | ||
| 
						 | 
					7af53e61f0 | ||
| 
						 | 
					88fb6136fb | ||
| 
						 | 
					e21b79cb21 | ||
| 
						 | 
					f16a069fd7 | ||
| 
						 | 
					5979fe9d8a | ||
| 
						 | 
					400ec02e8a | ||
| 
						 | 
					40da3e17e5 | ||
| 
						 | 
					5a2b8be3f5 | ||
| 
						 | 
					2b558857dd | ||
| 
						 | 
					a8c2aaf6f0 | ||
| 
						 | 
					69c9bfa089 | ||
| 
						 | 
					b2439bbb8a | ||
| 
						 | 
					3a10dbd564 | ||
| 
						 | 
					e0853cdf42 | ||
| 
						 | 
					5879c6493f | ||
| 
						 | 
					47a57f6f86 | ||
| 
						 | 
					61205a7e65 | ||
| 
						 | 
					57cf69a18c | ||
| 
						 | 
					dd08ac86e6 | ||
| 
						 | 
					bae33a7001 | ||
| 
						 | 
					5e46a20e03 | ||
| 
						 | 
					c135b878cd | ||
| 
						 | 
					1139ec1d0f | ||
| 
						 | 
					3db617f14a | ||
| 
						 | 
					efe62a7395 | ||
| 
						 | 
					14376a73a5 | ||
| 
						 | 
					506f156f7a | ||
| 
						 | 
					1653f837e3 | ||
| 
						 | 
					20b7ba7501 | ||
| 
						 | 
					cc9358f95a | ||
| 
						 | 
					16c6ecc365 | ||
| 
						 | 
					5f494b8415 | ||
| 
						 | 
					9ae282372c | ||
| 
						 | 
					ed46afda6f | ||
| 
						 | 
					ea65c72d06 | ||
| 
						 | 
					c91c18e6ef | ||
| 
						 | 
					e0c734dcfe | ||
| 
						 | 
					07111668d4 | ||
| 
						 | 
					4944c195d4 | ||
| 
						 | 
					5cd496d208 | ||
| 
						 | 
					83426b20a3 | ||
| 
						 | 
					617c7127f4 | ||
| 
						 | 
					23f93a15ca | ||
| 
						 | 
					ec478756cc | ||
| 
						 | 
					9380f5d218 | ||
| 
						 | 
					ac89a555fa | ||
| 
						 | 
					699b8dca49 | ||
| 
						 | 
					ccc34e4d30 | ||
| 
						 | 
					c6d4ceeda6 | ||
| 
						 | 
					9ec49e2411 | ||
| 
						 | 
					fa464bbce9 | ||
| 
						 | 
					e565564bc9 | ||
| 
						 | 
					cb1b494c89 | ||
| 
						 | 
					6d86628e5b | ||
| 
						 | 
					e2e004e7a9 | ||
| 
						 | 
					ace7b570a0 | ||
| 
						 | 
					f1379346f7 | ||
| 
						 | 
					34cf156b80 | ||
| 
						 | 
					9cc783a87d | ||
| 
						 | 
					a99d95e3af | ||
| 
						 | 
					e500e2e5d1 | ||
| 
						 | 
					b671dbfe94 | ||
| 
						 | 
					1e50a3dade | ||
| 
						 | 
					70b96aa232 | ||
| 
						 | 
					07eb655244 | ||
| 
						 | 
					1eae96ca2f | ||
| 
						 | 
					626369c2fb | ||
| 
						 | 
					409efa1ec8 | ||
| 
						 | 
					411686402b | ||
| 
						 | 
					04a108617a | ||
| 
						 | 
					e1d2b3568a | ||
| 
						 | 
					da1a79ef5b | ||
| 
						 | 
					51b1151044 | ||
| 
						 | 
					94cde130ca | ||
| 
						 | 
					e4bcb62dbf | ||
| 
						 | 
					20f6fd1b63 | ||
| 
						 | 
					b4584ff6c4 | ||
| 
						 | 
					0808f68601 | ||
| 
						 | 
					25db7c6116 | ||
| 
						 | 
					7bf9f49728 | ||
| 
						 | 
					0ae4313800 | ||
| 
						 | 
					d0aee85b29 | ||
| 
						 | 
					09fdf6622a | ||
| 
						 | 
					37e896dff1 | ||
| 
						 | 
					b33e675e5a | ||
| 
						 | 
					8158ea7288 | ||
| 
						 | 
					144a1d0516 | ||
| 
						 | 
					8322d08071 | ||
| 
						 | 
					a17fccbcf4 | ||
| 
						 | 
					1751c62c98 | ||
| 
						 | 
					f47f68a9d8 | ||
| 
						 | 
					62e96a3535 | ||
| 
						 | 
					a700dd9e11 | ||
| 
						 | 
					17214bb06d | ||
| 
						 | 
					befc760f86 | ||
| 
						 | 
					489a2b35d8 | ||
| 
						 | 
					10ed79553a | ||
| 
						 | 
					1d0a52a349 | ||
| 
						 | 
					6dbe822062 | ||
| 
						 | 
					b3f713bd7b | ||
| 
						 | 
					3c91cb881d | ||
| 
						 | 
					a098c19b55 | ||
| 
						 | 
					d9e102472a | ||
| 
						 | 
					1db67e0a35 | ||
| 
						 | 
					9200ce6019 | ||
| 
						 | 
					ddfc0d5763 | ||
| 
						 | 
					d3eac2867a | ||
| 
						 | 
					1c68abaffa | ||
| 
						 | 
					71e55118f5 | ||
| 
						 | 
					7b9f825ae8 | ||
| 
						 | 
					b0e5eaf59a | ||
| 
						 | 
					9a2845d491 | ||
| 
						 | 
					0e50ce6145 | ||
| 
						 | 
					767193e20b | ||
| 
						 | 
					d1cb41bfd0 | ||
| 
						 | 
					5752285bc5 | ||
| 
						 | 
					b75e23143b | ||
| 
						 | 
					4f459799e3 | ||
| 
						 | 
					be0cbc1b7e | ||
| 
						 | 
					87597358cf | ||
| 
						 | 
					9399046729 | ||
| 
						 | 
					e43460b50f | ||
| 
						 | 
					5367086175 | ||
| 
						 | 
					dfef28de88 | ||
| 
						 | 
					67c385046d | ||
| 
						 | 
					fecece0e59 | ||
| 
						 | 
					e04e0b0a20 | ||
| 
						 | 
					6eeb7c7e74 | ||
| 
						 | 
					6e4ccec6c4 | ||
| 
						 | 
					0288cdb0a8 | ||
| 
						 | 
					3d62fc55d8 | ||
| 
						 | 
					87d7341fbe | ||
| 
						 | 
					29d91e9271 | ||
| 
						 | 
					801d6fe40c | ||
| 
						 | 
					0494c75a53 | ||
| 
						 | 
					0d39dc92b5 | ||
| 
						 | 
					1f33294b1c | ||
| 
						 | 
					2acfbebfd3 | ||
| 
						 | 
					c0c580c414 | ||
| 
						 | 
					56e8d71090 | ||
| 
						 | 
					00fc6b13f2 | ||
| 
						 | 
					90de3a7ffe | ||
| 
						 | 
					d58b711eec | ||
| 
						 | 
					cc12f02658 | ||
| 
						 | 
					d0261b72de | ||
| 
						 | 
					0c1c8778df | ||
| 
						 | 
					0a0b5dcb32 | ||
| 
						 | 
					502bd1feea | ||
| 
						 | 
					e25b5b91f6 | ||
| 
						 | 
					2cbe80b53e | ||
| 
						 | 
					3bed5d11d2 | ||
| 
						 | 
					cbb1e70554 | ||
| 
						 | 
					c495209122 | ||
| 
						 | 
					005ee7f862 | ||
| 
						 | 
					2493c660e9 | ||
| 
						 | 
					a3e4509ff9 | ||
| 
						 | 
					eceb910fef | ||
| 
						 | 
					95340242ed | ||
| 
						 | 
					612e93e360 | ||
| 
						 | 
					39645b824a | ||
| 
						 | 
					1e96af6325 | ||
| 
						 | 
					7c44415d78 | ||
| 
						 | 
					0eabffdc35 | ||
| 
						 | 
					1842c921b3 | ||
| 
						 | 
					adcdf150a6 | ||
| 
						 | 
					a41a71e2d4 | ||
| 
						 | 
					297edf754f | ||
| 
						 | 
					08fe76a893 | ||
| 
						 | 
					5e9f40d3d9 | ||
| 
						 | 
					18fef10641 | ||
| 
						 | 
					f5c97476de | ||
| 
						 | 
					49f26bf6e8 | ||
| 
						 | 
					07e39302ec | ||
| 
						 | 
					0f58af6627 | ||
| 
						 | 
					635d47c78c | ||
| 
						 | 
					1c15573012 | ||
| 
						 | 
					efd5dbb168 | ||
| 
						 | 
					510d7d36ed | ||
| 
						 | 
					e481815583 | ||
| 
						 | 
					08e57c8366 | ||
| 
						 | 
					341689d6c5 | ||
| 
						 | 
					ad1fcccf60 | ||
| 
						 | 
					ca102af92b | ||
| 
						 | 
					eb100244b0 | ||
| 
						 | 
					418a4230e0 | ||
| 
						 | 
					cf706b0775 | ||
| 
						 | 
					e2732e2f59 | ||
| 
						 | 
					f72fcfe219 | ||
| 
						 | 
					7e994e392d | ||
| 
						 | 
					6a8a1dcda2 | ||
| 
						 | 
					d1d1633121 | ||
| 
						 | 
					cd44ec41c5 | ||
| 
						 | 
					f55e58f5cf | ||
| 
						 | 
					350f64d9e2 | ||
| 
						 | 
					4f3c420852 | ||
| 
						 | 
					b2886f1a0d | ||
| 
						 | 
					93d1711011 | ||
| 
						 | 
					6a16e0c092 | ||
| 
						 | 
					351a573396 | ||
| 
						 | 
					3016888ee0 | ||
| 
						 | 
					532a4b54f5 | ||
| 
						 | 
					1dac2ebb71 | ||
| 
						 | 
					b8f260176e | ||
| 
						 | 
					6c813fc9bc | ||
| 
						 | 
					538ac5b574 | ||
| 
						 | 
					2354402b7a | ||
| 
						 | 
					057c285cd7 | ||
| 
						 | 
					1b3864fc47 | ||
| 
						 | 
					4bbe3349c2 | ||
| 
						 | 
					48aa0b0d99 | ||
| 
						 | 
					0d12a2fa89 | ||
| 
						 | 
					fe6e2e4a08 | ||
| 
						 | 
					078bb713e1 | ||
| 
						 | 
					2207c5e4ef | ||
| 
						 | 
					febf1a0ac9 | ||
| 
						 | 
					54044b0635 | ||
| 
						 | 
					b59d61b4b4 | ||
| 
						 | 
					e8915b9d9d | ||
| 
						 | 
					2c98f1db78 | ||
| 
						 | 
					c421e8aaf2 | ||
| 
						 | 
					35cc1c0ff3 | ||
| 
						 | 
					f3405e5b03 | ||
| 
						 | 
					6d8884de49 | ||
| 
						 | 
					77e58f602d | ||
| 
						 | 
					5d77f14904 | ||
| 
						 | 
					17a1d5e162 | ||
| 
						 | 
					1e0c61032f | ||
| 
						 | 
					cf1fae7f38 | ||
| 
						 | 
					73191a443f | ||
| 
						 | 
					fc03bbe078 | ||
| 
						 | 
					9167055ed2 | ||
| 
						 | 
					8f3520579a | ||
| 
						 | 
					f81e2f7621 | ||
| 
						 | 
					59fe8f79cc | ||
| 
						 | 
					d8f339592d | ||
| 
						 | 
					a09e33cdeb | ||
| 
						 | 
					e5109ba1f0 | ||
| 
						 | 
					3d0cc2f97d | ||
| 
						 | 
					a004799699 | ||
| 
						 | 
					b293043e2d | ||
| 
						 | 
					80367aae17 | ||
| 
						 | 
					772415fd8a | ||
| 
						 | 
					afa72f5210 | ||
| 
						 | 
					85c1ea5cb6 | ||
| 
						 | 
					3ce011f44f | ||
| 
						 | 
					d54d9f5c75 | ||
| 
						 | 
					146514e180 | ||
| 
						 | 
					55844795be | ||
| 
						 | 
					7c3751b10b | ||
| 
						 | 
					f4a1ec6ce7 | ||
| 
						 | 
					c2fab7a6ff | ||
| 
						 | 
					0a940087b2 | ||
| 
						 | 
					2bf3446f7a | ||
| 
						 | 
					746b390201 | ||
| 
						 | 
					1cd9d886e6 | ||
| 
						 | 
					fe2dcc8aec | ||
| 
						 | 
					f3eec04655 | ||
| 
						 | 
					765736dc77 | ||
| 
						 | 
					7a16d46fdc | ||
| 
						 | 
					ccde4c462d | ||
| 
						 | 
					b03d427b08 | ||
| 
						 | 
					73193110f2 | ||
| 
						 | 
					1fe69323ad | ||
| 
						 | 
					3fd2ddf1a2 | ||
| 
						 | 
					dd4af8045f | ||
| 
						 | 
					e45c19b3e8 | ||
| 
						 | 
					882fc9b174 | ||
| 
						 | 
					f34f34495b | ||
| 
						 | 
					df022f6cb2 | ||
| 
						 | 
					c7b998cc73 | ||
| 
						 | 
					4af30533f0 | ||
| 
						 | 
					f37e514a96 | ||
| 
						 | 
					8f399de135 | ||
| 
						 | 
					ba26368040 | ||
| 
						 | 
					6f791d049a | ||
| 
						 | 
					6f0796a33a | ||
| 
						 | 
					fdcdde2756 | ||
| 
						 | 
					402cdcd02f | ||
| 
						 | 
					2f1974b5ac | ||
| 
						 | 
					1b517342ae | ||
| 
						 | 
					705653b2d7 | ||
| 
						 | 
					aadfc2e121 | ||
| 
						 | 
					9c623892f5 | ||
| 
						 | 
					a42b2ad5ed | ||
| 
						 | 
					550bb88a9e | ||
| 
						 | 
					88553abf4d | ||
| 
						 | 
					c92b964e2d | ||
| 
						 | 
					b3a87ced5a | ||
| 
						 | 
					81e8f0a8a2 | ||
| 
						 | 
					752a3b2baa | ||
| 
						 | 
					5e94cd4fde | ||
| 
						 | 
					cc5a4cbe9b | ||
| 
						 | 
					48df40d2a4 | ||
| 
						 | 
					f1786a375f | ||
| 
						 | 
					6f7cb2cb4f | ||
| 
						 | 
					0f2b5fc749 | ||
| 
						 | 
					5241c1a018 | ||
| 
						 | 
					52ea8a0d87 | ||
| 
						 | 
					e6e605ba86 | ||
| 
						 | 
					c3f4330fa3 | ||
| 
						 | 
					f86fb7a953 | ||
| 
						 | 
					8a31f9e016 | ||
| 
						 | 
					11f2d58dbd | ||
| 
						 | 
					3cbf9e040c | ||
| 
						 | 
					3c95776805 | ||
| 
						 | 
					7a5f3c2153 | ||
| 
						 | 
					7f3e662b34 | ||
| 
						 | 
					cbb00ebd04 | ||
| 
						 | 
					423dd2286b | ||
| 
						 | 
					4c06d9155c | ||
| 
						 | 
					c15d5cc230 | ||
| 
						 | 
					9977e1545a | ||
| 
						 | 
					6ab5b24be4 | ||
| 
						 | 
					7decedcbf8 | ||
| 
						 | 
					faf3ef45b3 | ||
| 
						 | 
					e9b1ebba9d | ||
| 
						 | 
					270f7fce1d | ||
| 
						 | 
					f1593b7c49 | ||
| 
						 | 
					ab6cd921e3 | ||
| 
						 | 
					9a76968ec4 | ||
| 
						 | 
					9c5539d46d | ||
| 
						 | 
					86dc355a33 | ||
| 
						 | 
					bc15570651 | ||
| 
						 | 
					e358ff8fa4 | ||
| 
						 | 
					df6b4f52e0 | ||
| 
						 | 
					9eb8ace260 | ||
| 
						 | 
					12c039cdb2 | ||
| 
						 | 
					a6ba67cb3a | ||
| 
						 | 
					8b16ae30fe | ||
| 
						 | 
					c03e38291a | ||
| 
						 | 
					1577779526 | ||
| 
						 | 
					ef93bdb19b | ||
| 
						 | 
					64b49bc3ce | ||
| 
						 | 
					40fc8f9e46 | ||
| 
						 | 
					b18aedcc50 | ||
| 
						 | 
					9525567d77 | ||
| 
						 | 
					6b5660c19f | ||
| 
						 | 
					321f3c3104 | ||
| 
						 | 
					092478f294 | ||
| 
						 | 
					5a7f636ce4 | ||
| 
						 | 
					c6382ddd6e | ||
| 
						 | 
					6058220d2a | ||
| 
						 | 
					58db60c68e | ||
| 
						 | 
					c3d5325fc8 | ||
| 
						 | 
					c70aae4697 | ||
| 
						 | 
					937e2a6919 | ||
| 
						 | 
					e5dc2a0ac5 | ||
| 
						 | 
					796270bf83 | ||
| 
						 | 
					9241fd0957 | ||
| 
						 | 
					e9429b5d3e | ||
| 
						 | 
					55cf994c29 | ||
| 
						 | 
					b1302b083e | ||
| 
						 | 
					32f9a59ab4 | ||
| 
						 | 
					e23102602c | ||
| 
						 | 
					b3d5ee2934 | ||
| 
						 | 
					51e093bd1c | ||
| 
						 | 
					985fd114f2 | ||
| 
						 | 
					fce431cf3a | ||
| 
						 | 
					aa35ba7584 | ||
| 
						 | 
					857281f7ff | ||
| 
						 | 
					d26318e4a7 | ||
| 
						 | 
					4e8b50cd8d | ||
| 
						 | 
					0e0f2f5faf | ||
| 
						 | 
					a6e47c7f54 | ||
| 
						 | 
					5697b4ccfa | ||
| 
						 | 
					acb5427bda | ||
| 
						 | 
					be57081721 | ||
| 
						 | 
					b7840466ce | ||
| 
						 | 
					a1da251c10 | ||
| 
						 | 
					096f6f9f39 | ||
| 
						 | 
					758dc12c9f | ||
| 
						 | 
					28e5378b55 | ||
| 
						 | 
					9948514086 | ||
| 
						 | 
					77e05971b4 | ||
| 
						 | 
					bc1626e5de | ||
| 
						 | 
					b809866be5 | ||
| 
						 | 
					5464c9baf2 | ||
| 
						 | 
					34f6e5ebe6 | ||
| 
						 | 
					474685e26e | ||
| 
						 | 
					bd449f57a7 | ||
| 
						 | 
					870c601f1d | ||
| 
						 | 
					6b88cddc21 | ||
| 
						 | 
					8021644e9d | ||
| 
						 | 
					ec80f82824 | ||
| 
						 | 
					fc498c9e7b | ||
| 
						 | 
					875fc895a8 | ||
| 
						 | 
					e1a075ab59 | ||
| 
						 | 
					53a336f0e5 | ||
| 
						 | 
					8d0e489484 | ||
| 
						 | 
					15bb1a2a51 | ||
| 
						 | 
					5da2014f13 | ||
| 
						 | 
					7a344716a6 | ||
| 
						 | 
					cb5c49b581 | ||
| 
						 | 
					750ca67ce9 | ||
| 
						 | 
					842808848c | ||
| 
						 | 
					5a9658168a | ||
| 
						 | 
					396f4161cb | ||
| 
						 | 
					c1ae22694c | ||
| 
						 | 
					90b87ea5e6 | ||
| 
						 | 
					180def907b | ||
| 
						 | 
					1997787c52 | ||
| 
						 | 
					b98dd9e240 | ||
| 
						 | 
					93e048fe27 | ||
| 
						 | 
					158776411b | ||
| 
						 | 
					f537298cca | ||
| 
						 | 
					61a0a04d26 | ||
| 
						 | 
					3dc56a3b34 | ||
| 
						 | 
					ae03f22199 | ||
| 
						 | 
					22b31190cb | ||
| 
						 | 
					46bcb8d59d | ||
| 
						 | 
					02beed5e98 | ||
| 
						 | 
					2db7e7e7db | ||
| 
						 | 
					24c1a5ba29 | ||
| 
						 | 
					42de18f347 | ||
| 
						 | 
					d9262b3b55 | ||
| 
						 | 
					40cfaa6837 | ||
| 
						 | 
					7292373f92 | ||
| 
						 | 
					70ddde6929 | ||
| 
						 | 
					f63cabd931 | ||
| 
						 | 
					a54bbbbf02 | ||
| 
						 | 
					dea63b96e0 | ||
| 
						 | 
					05f8efc1a2 | ||
| 
						 | 
					4f753c64d6 | ||
| 
						 | 
					65be4808af | ||
| 
						 | 
					6673005215 | ||
| 
						 | 
					782f6ddf99 | ||
| 
						 | 
					86300bbeea | ||
| 
						 | 
					af8094d799 | ||
| 
						 | 
					aa9c326d7e | ||
| 
						 | 
					fcbc702112 | ||
| 
						 | 
					d11290b90b | ||
| 
						 | 
					6f9f434463 | ||
| 
						 | 
					9ae367f639 | ||
| 
						 | 
					b65e08a7be | ||
| 
						 | 
					545876550f | ||
| 
						 | 
					6796ed23ab | ||
| 
						 | 
					09ef27ae04 | ||
| 
						 | 
					4ed1dae533 | ||
| 
						 | 
					1702f102af | ||
| 
						 | 
					7ce21eb042 | ||
| 
						 | 
					34bd8bdf30 | ||
| 
						 | 
					9f0c1d216a | ||
| 
						 | 
					cedf890928 | ||
| 
						 | 
					621dc2fd01 | ||
| 
						 | 
					111614a994 | ||
| 
						 | 
					d6ef5345e5 | ||
| 
						 | 
					63f29d6f73 | ||
| 
						 | 
					6ca0a9076e | ||
| 
						 | 
					9446485e3d | ||
| 
						 | 
					f0984897a5 | ||
| 
						 | 
					24e98d039d | ||
| 
						 | 
					17b0d83a1f | ||
| 
						 | 
					ba5543fbf3 | ||
| 
						 | 
					b78adcdd11 | ||
| 
						 | 
					9b30c7f46e | ||
| 
						 | 
					b6acfdaa24 | ||
| 
						 | 
					67ed34eedb | ||
| 
						 | 
					1b33361bab | ||
| 
						 | 
					b19d7268c3 | ||
| 
						 | 
					1af73f6c81 | ||
| 
						 | 
					ee7a535608 | ||
| 
						 | 
					485860cc31 | ||
| 
						 | 
					d507ce6b85 | ||
| 
						 | 
					8d7a632eef | ||
| 
						 | 
					4ffc260869 | ||
| 
						 | 
					9f521cd4af | ||
| 
						 | 
					b0250ebeac | ||
| 
						 | 
					698b612188 | ||
| 
						 | 
					ae947fcf67 | ||
| 
						 | 
					344ff48756 | ||
| 
						 | 
					173a240a7e | ||
| 
						 | 
					45396a99d9 | ||
| 
						 | 
					bfdd5f285b | ||
| 
						 | 
					948a3d62b1 | ||
| 
						 | 
					b0a0075845 | ||
| 
						 | 
					11a748935e | ||
| 
						 | 
					8481a0553d | ||
| 
						 | 
					743bafa50c | ||
| 
						 | 
					96efa70a9e | ||
| 
						 | 
					02ab4a8803 | ||
| 
						 | 
					96173aec93 | ||
| 
						 | 
					1c344e2668 | ||
| 
						 | 
					7380babdba | ||
| 
						 | 
					0946f99b08 | ||
| 
						 | 
					455613076a | ||
| 
						 | 
					4f6a4e5d6d | ||
| 
						 | 
					964ddb6aa0 | ||
| 
						 | 
					9e21d84f1e | ||
| 
						 | 
					099535a1a7 | ||
| 
						 | 
					8692128365 | ||
| 
						 | 
					bb20b2c64c | ||
| 
						 | 
					bcafdf8d90 | ||
| 
						 | 
					443c822f77 | ||
| 
						 | 
					d868928888 | ||
| 
						 | 
					d1d114f5b7 | ||
| 
						 | 
					31d0df92ac | ||
| 
						 | 
					17c31ce173 | ||
| 
						 | 
					dcc9c39529 | ||
| 
						 | 
					70862c9409 | ||
| 
						 | 
					26da8361e9 | ||
| 
						 | 
					483f77a275 | ||
| 
						 | 
					985806030d | ||
| 
						 | 
					52c911b3b4 | ||
| 
						 | 
					2023bd18b6 | ||
| 
						 | 
					d6bd91e4e5 | ||
| 
						 | 
					9d938388f6 | ||
| 
						 | 
					bc1e52cfbf | ||
| 
						 | 
					741481d3e0 | ||
| 
						 | 
					4ea940787e | ||
| 
						 | 
					416ce69550 | ||
| 
						 | 
					c0f1644c88 | ||
| 
						 | 
					e8d9a5fa54 | ||
| 
						 | 
					b67a8a348a | ||
| 
						 | 
					90d5df4ae7 | ||
| 
						 | 
					a9744debd9 | ||
| 
						 | 
					dfe17d7f91 | ||
| 
						 | 
					e13298d093 | ||
| 
						 | 
					bb00704871 | ||
| 
						 | 
					421fe79e39 | ||
| 
						 | 
					939bfaefec | ||
| 
						 | 
					f14ddb7830 | ||
| 
						 | 
					997d2ac3fe | ||
| 
						 | 
					cf0265a112 | ||
| 
						 | 
					49be8bce50 | ||
| 
						 | 
					148fb9a3bc | ||
| 
						 | 
					98fe945a0d | ||
| 
						 | 
					249c7e9431 | ||
| 
						 | 
					40bd0c4b57 | ||
| 
						 | 
					4b984b14a3 | ||
| 
						 | 
					ed70cb733c | ||
| 
						 | 
					d8157c07df | ||
| 
						 | 
					90692f93cf | ||
| 
						 | 
					90f08efe58 | ||
| 
						 | 
					a30f47613a | ||
| 
						 | 
					51c57045e5 | ||
| 
						 | 
					a004e6a823 | ||
| 
						 | 
					783aa4bcb8 | ||
| 
						 | 
					b6bb55d88c | ||
| 
						 | 
					7dac4862bc | ||
| 
						 | 
					72075f0e00 | ||
| 
						 | 
					81a12be317 | ||
| 
						 | 
					050ace2fb4 | ||
| 
						 | 
					570b44b29b | ||
| 
						 | 
					0b874b64ef | ||
| 
						 | 
					30a73d8544 | ||
| 
						 | 
					4440845614 | ||
| 
						 | 
					5dc29a51ef | ||
| 
						 | 
					3afe56d7d8 | ||
| 
						 | 
					827ed55c3b | ||
| 
						 | 
					e5a093986b | ||
| 
						 | 
					d1e340f0be | ||
| 
						 | 
					71cce91a76 | ||
| 
						 | 
					0f4541b691 | ||
| 
						 | 
					273a222d7f | ||
| 
						 | 
					7a5a67ab7b | ||
| 
						 | 
					97644ed7f8 | ||
| 
						 | 
					02d7cd2ac2 | ||
| 
						 | 
					138f4e62d2 | ||
| 
						 | 
					9aaf2ae24d | ||
| 
						 | 
					e4050114f5 | ||
| 
						 | 
					850d80b898 | ||
| 
						 | 
					de5253a20f | ||
| 
						 | 
					a23c7eee15 | ||
| 
						 | 
					106ea79337 | ||
| 
						 | 
					d87b4685b8 | ||
| 
						 | 
					5b8a528251 | ||
| 
						 | 
					f01929bcb6 | ||
| 
						 | 
					8c3680dcc9 | ||
| 
						 | 
					00950743d7 | ||
| 
						 | 
					afdd9e1be5 | ||
| 
						 | 
					3a7eb6b80f | ||
| 
						 | 
					0c9b50c498 | ||
| 
						 | 
					aafcd703f3 | ||
| 
						 | 
					b83e2a7d5c | ||
| 
						 | 
					5cae087ae9 | ||
| 
						 | 
					f7adfdebe9 | ||
| 
						 | 
					701d9f83f8 | ||
| 
						 | 
					9eb552b239 | ||
| 
						 | 
					499ff415a9 | ||
| 
						 | 
					3b1332cdb4 | ||
| 
						 | 
					5f14bd9410 | ||
| 
						 | 
					a3c12fb6c5 | ||
| 
						 | 
					a9be1547b1 | ||
| 
						 | 
					99a107dbee | ||
| 
						 | 
					80be275710 | ||
| 
						 | 
					aafcbbb252 | ||
| 
						 | 
					df4d20e95b | ||
| 
						 | 
					9a91346fa1 | ||
| 
						 | 
					514491d726 | ||
| 
						 | 
					cd8a83a42c | ||
| 
						 | 
					4b39dd1c00 | ||
| 
						 | 
					adb2f18538 | ||
| 
						 | 
					07e97cf8a5 | ||
| 
						 | 
					cbcd124588 | ||
| 
						 | 
					309ce74376 | ||
| 
						 | 
					14db19ed11 | ||
| 
						 | 
					802bb6eac8 | ||
| 
						 | 
					a95348c03e | ||
| 
						 | 
					1115f5972c | ||
| 
						 | 
					df72c89e4b | 
							
								
								
									
										50
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										50
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,9 +1,11 @@
 | 
			
		||||
*.o
 | 
			
		||||
*.lo
 | 
			
		||||
*.a
 | 
			
		||||
*.sw?
 | 
			
		||||
Makefile.in
 | 
			
		||||
Makefile
 | 
			
		||||
.deps
 | 
			
		||||
src/cscope*
 | 
			
		||||
 | 
			
		||||
aclocal.m4
 | 
			
		||||
autom4te.cache
 | 
			
		||||
@@ -12,11 +14,57 @@ config.guess
 | 
			
		||||
config.sub
 | 
			
		||||
config.status
 | 
			
		||||
configure
 | 
			
		||||
compile
 | 
			
		||||
depcomp
 | 
			
		||||
install-sh
 | 
			
		||||
missing
 | 
			
		||||
libtool
 | 
			
		||||
*libtool
 | 
			
		||||
ltmain.sh
 | 
			
		||||
*~
 | 
			
		||||
 | 
			
		||||
core
 | 
			
		||||
core.*
 | 
			
		||||
 | 
			
		||||
osmoappdesc.pyc
 | 
			
		||||
 | 
			
		||||
# binaries
 | 
			
		||||
src/osmo-pcu
 | 
			
		||||
src/osmo-pcu-remote
 | 
			
		||||
 | 
			
		||||
# tests
 | 
			
		||||
.dirstamp
 | 
			
		||||
__pycache__/
 | 
			
		||||
tests/atconfig
 | 
			
		||||
tests/package.m4
 | 
			
		||||
tests/*/*Test
 | 
			
		||||
tests/*/*_test
 | 
			
		||||
tests/emu/pcu_emu
 | 
			
		||||
tests/testsuite
 | 
			
		||||
tests/testsuite.log
 | 
			
		||||
 | 
			
		||||
# ignore debian files
 | 
			
		||||
.tarball-version
 | 
			
		||||
debian/autoreconf.after
 | 
			
		||||
debian/autoreconf.before
 | 
			
		||||
debian/files
 | 
			
		||||
debian/*.debhelper*
 | 
			
		||||
debian/*.substvars
 | 
			
		||||
debian/osmo-pcu-dbg/
 | 
			
		||||
debian/osmo-pcu.substvars
 | 
			
		||||
debian/osmo-pcu/
 | 
			
		||||
debian/tmp/
 | 
			
		||||
 | 
			
		||||
osmo-pcu.pc
 | 
			
		||||
 | 
			
		||||
# manuals
 | 
			
		||||
doc/manuals/*.html
 | 
			
		||||
doc/manuals/*.svg
 | 
			
		||||
doc/manuals/*.pdf
 | 
			
		||||
doc/manuals/*__*.png
 | 
			
		||||
doc/manuals/*.check
 | 
			
		||||
doc/manuals/generated/
 | 
			
		||||
doc/manuals/osmomsc-usermanual.xml
 | 
			
		||||
doc/manuals/common
 | 
			
		||||
doc/manuals/build
 | 
			
		||||
 | 
			
		||||
contrib/osmo-pcu.spec
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
[gerrit]
 | 
			
		||||
host=gerrit.osmocom.org
 | 
			
		||||
project=osmo-pcu
 | 
			
		||||
							
								
								
									
										13
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Makefile.am
									
									
									
									
									
								
							@@ -1,3 +1,14 @@
 | 
			
		||||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
 | 
			
		||||
 | 
			
		||||
SUBDIRS = src
 | 
			
		||||
SUBDIRS = include src doc tests contrib
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	     README.md \
 | 
			
		||||
	     contrib/osmo-pcu.spec.in \
 | 
			
		||||
	     debian \
 | 
			
		||||
	     osmoappdesc.py \
 | 
			
		||||
	     $(NULL)
 | 
			
		||||
 | 
			
		||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
			
		||||
	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 | 
			
		||||
 | 
			
		||||
@RELMAKE@
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								README
									
									
									
									
									
								
							@@ -1,22 +0,0 @@
 | 
			
		||||
This is an implementation of Packet Control Unit (PCU) according to TS 04.60
 | 
			
		||||
 | 
			
		||||
The PCU is part of BSS, so it connects directly to SGSN.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
== Current limitations ==
 | 
			
		||||
 | 
			
		||||
 * No PFC support
 | 
			
		||||
 * No fixed allocation support
 | 
			
		||||
 * No extended dynamic allocation support
 | 
			
		||||
 * No unacknowledged mode operation
 | 
			
		||||
 * No PCCCH/PBCCH support
 | 
			
		||||
 * Only single slot assignment on uplink direction
 | 
			
		||||
 * No half-duplex class support
 | 
			
		||||
 * No two-phase access support
 | 
			
		||||
 * No handover support
 | 
			
		||||
 * No measurement support
 | 
			
		||||
 * No polling for control ack on assignment
 | 
			
		||||
 * No TA loop
 | 
			
		||||
 * No power loop
 | 
			
		||||
 * No CS loop
 | 
			
		||||
 * No EGPRS
 | 
			
		||||
							
								
								
									
										85
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
osmo-pcu - Osmocom Packet Control Unit
 | 
			
		||||
======================================
 | 
			
		||||
 | 
			
		||||
This repository contains a C/C++-language implementation of a GPRS
 | 
			
		||||
Packet Control Unit, as specified by ETSI/3GPP.  It is part of the
 | 
			
		||||
[Osmocom](https://osmocom.org/) Open Source Mobile Communications
 | 
			
		||||
project.
 | 
			
		||||
 | 
			
		||||
The Packet Control Unit is terminating the Layer 2 (RLC/MAC) of the GPRS
 | 
			
		||||
radio interface and adapting it to the Gb Interface (BSSGP+NS Protocol)
 | 
			
		||||
towards the SGSN.
 | 
			
		||||
 | 
			
		||||
The PCU interfaces with the physical layer of the radio interface.
 | 
			
		||||
OsmoPCU is typically used co-located with the BTS, specifically
 | 
			
		||||
[OsmoBTS](https://osmocom.org/projects/osmobts/wiki).
 | 
			
		||||
For legacy BTSs that run proprietary sotware without an interface to
 | 
			
		||||
OsmoPCU, you may also co-locate it with the BSC, specifically
 | 
			
		||||
[OsmoBSC](https://osmocom.org/projects/openbsc/wiki/Osmo-bsc)
 | 
			
		||||
 | 
			
		||||
Homepage
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
The official homepage of the project is
 | 
			
		||||
https://osmocom.org/projects/osmopcu/wiki/OsmoPCU
 | 
			
		||||
 | 
			
		||||
GIT Repository
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
You can clone from the official osmo-pcu.git repository using
 | 
			
		||||
 | 
			
		||||
	git clone git://git.osmocom.org/osmo-pcu.git
 | 
			
		||||
 | 
			
		||||
There is a cgit interface at http://git.osmocom.org/osmo-pcu/
 | 
			
		||||
 | 
			
		||||
Documentation
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
We provide a
 | 
			
		||||
[user manual](http://ftp.osmocom.org/docs/latest/osmopcu-usermanual.pdf)
 | 
			
		||||
as well as a
 | 
			
		||||
[vty reference manual](http://ftp.osmocom.org/docs/latest/osmopcu-vty-reference.pdf)
 | 
			
		||||
 | 
			
		||||
Please note that a lot of the PCU configuration actually happens inside
 | 
			
		||||
the BSC, which passes this configuration via A-bis OML to the BTS, which
 | 
			
		||||
then in turn passes it via the PCU socket into OsmoPCU.
 | 
			
		||||
 | 
			
		||||
Mailing List
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Discussions related to osmo-pcu are happening on the
 | 
			
		||||
osmocom-net-gprs@lists.osmocom.org mailing list, please see
 | 
			
		||||
https://lists.osmocom.org/mailman/listinfo/osmocom-net-gprs for
 | 
			
		||||
subscription options and the list archive.
 | 
			
		||||
 | 
			
		||||
Please observe the [Osmocom Mailing List
 | 
			
		||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
 | 
			
		||||
when posting.
 | 
			
		||||
 | 
			
		||||
Contributing
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Our coding standards are described at
 | 
			
		||||
https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards
 | 
			
		||||
 | 
			
		||||
We us a gerrit based patch submission/review process for managing
 | 
			
		||||
contributions.  Please see
 | 
			
		||||
https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
 | 
			
		||||
more details
 | 
			
		||||
 | 
			
		||||
The current patch queue for osmo-pcu can be seen at
 | 
			
		||||
https://gerrit.osmocom.org/#/q/project:osmo-pcu+status:open
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Current limitations
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
 * No PFC support
 | 
			
		||||
 * No fixed allocation support (was removed from 3GPP Rel >= 5 anyway)
 | 
			
		||||
 * No extended dynamic allocation support
 | 
			
		||||
 * No unacknowledged mode operation
 | 
			
		||||
 * Only single slot assignment on uplink direction
 | 
			
		||||
 * No half-duplex class support (only semi-duplex)
 | 
			
		||||
 * No TA loop
 | 
			
		||||
 * No power loop
 | 
			
		||||
 * Multi-BTS support not tested
 | 
			
		||||
							
								
								
									
										34
									
								
								TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								TODO
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
* Change functions with 100 parameters to get a struct as param
 | 
			
		||||
* Move move into the TBF class
 | 
			
		||||
* tbf/llc window code appears to be duplicated and nested in other
 | 
			
		||||
  methods. This needs to be cleaned.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
* Possible race condition:
 | 
			
		||||
 When scheduling a Downlink Assignment on the UL-TBF we need to make
 | 
			
		||||
 sure that the assignment is sent _before_ the final ack. With my fairness
 | 
			
		||||
 changes it gets more likely that this event is trigerred.
 | 
			
		||||
 | 
			
		||||
* Optimize:
 | 
			
		||||
After receiving an ACK/NACK.. schedule another one if the window
 | 
			
		||||
is kind of stalled anyway. This way we avoid resending frames that
 | 
			
		||||
might have already arrived. It could increase the throughput..
 | 
			
		||||
 | 
			
		||||
Do not re-transmit after we got ack/nacked and where in the resending
 | 
			
		||||
mode... and increase the window.
 | 
			
		||||
 | 
			
		||||
<0004> tbf.cpp:907 - Sending new block at BSN 111
 | 
			
		||||
...
 | 
			
		||||
tbf.cpp:858 - Restarting at BSN 48, because all window is stalled.
 | 
			
		||||
...
 | 
			
		||||
tbf.cpp:1383 - V(B): (V(A)=59)"NNAAAAAAANAAAAANNAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXX"(V(S)-1=111)  A=Acked N=Nacked U=Unacked X=Resend-Unacked I=Invalid
 | 
			
		||||
.. retransmitting the nacked.. and then the ones that migh have
 | 
			
		||||
already arrived
 | 
			
		||||
<0004> tbf.cpp:834 TBF(TFI=0 TLLI=0xd7b78810 DIR=DL) downlink (V(A)==59 .. V(S)==112)
 | 
			
		||||
<0004> tbf.cpp:840 - Resending BSN 111
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Figure out scheduling issue. Why do we reach the 20 re-transmits and
 | 
			
		||||
stil haven't received the ACK/NACK? was it scheduled? The whole
 | 
			
		||||
scheduler could be re-worked to be more determestic.. and answer
 | 
			
		||||
questions like if it has been sent or not
 | 
			
		||||
							
								
								
									
										11
									
								
								TODO-RELEASE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								TODO-RELEASE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
 | 
			
		||||
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
 | 
			
		||||
# In short:
 | 
			
		||||
# LIBVERSION=c:r:a
 | 
			
		||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
 | 
			
		||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
 | 
			
		||||
# If any interfaces have been added since the last public release: c:r:a + 1.
 | 
			
		||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
 | 
			
		||||
#library        what            description / commit summary line
 | 
			
		||||
libosmocore     struct bssgp_bvc_ctx->is_sgsn   field used available only on libosmocore >1.5.1
 | 
			
		||||
libosmocore     gprs_ns2_ip_bind_set_priority   function used available only on libosmocore >1.5.1
 | 
			
		||||
							
								
								
									
										248
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										248
									
								
								configure.ac
									
									
									
									
									
								
							@@ -1,13 +1,24 @@
 | 
			
		||||
dnl Process this file with autoconf to produce a configure script
 | 
			
		||||
AC_INIT([osmo-pcu],
 | 
			
		||||
	m4_esyscmd([./git-version-gen .tarball-version]),
 | 
			
		||||
	[osmocom-pcu@lists.osmocom.org])
 | 
			
		||||
	[osmocom-net-gprs@lists.osmocom.org])
 | 
			
		||||
 | 
			
		||||
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
 | 
			
		||||
AC_CONFIG_AUX_DIR([.])
 | 
			
		||||
 | 
			
		||||
AM_INIT_AUTOMAKE([dist-bzip2])
 | 
			
		||||
AC_CONFIG_TESTDIR(tests)
 | 
			
		||||
 | 
			
		||||
CXXFLAGS="$CXXFLAGS -std=gnu++03"
 | 
			
		||||
CFLAGS="$CFLAGS -std=gnu11"
 | 
			
		||||
 | 
			
		||||
dnl kernel style compile messages
 | 
			
		||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 | 
			
		||||
 | 
			
		||||
dnl include release helper
 | 
			
		||||
RELMAKE='-include osmo-release.mk'
 | 
			
		||||
AC_SUBST([RELMAKE])
 | 
			
		||||
 | 
			
		||||
dnl checks for programs
 | 
			
		||||
AC_PROG_MAKE_SET
 | 
			
		||||
AC_PROG_CC
 | 
			
		||||
@@ -15,25 +26,236 @@ AC_PROG_CXX
 | 
			
		||||
AC_PROG_INSTALL
 | 
			
		||||
LT_INIT
 | 
			
		||||
 | 
			
		||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
 | 
			
		||||
AS_CASE(["$LD"],[*clang*],
 | 
			
		||||
  [AS_CASE(["${host_os}"],
 | 
			
		||||
     [*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
 | 
			
		||||
 | 
			
		||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
 | 
			
		||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
 | 
			
		||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
 | 
			
		||||
        AC_MSG_WARN([You need to install pkg-config])
 | 
			
		||||
fi
 | 
			
		||||
PKG_PROG_PKG_CONFIG([0.20])
 | 
			
		||||
 | 
			
		||||
dnl checks for header files
 | 
			
		||||
AC_HEADER_STDC
 | 
			
		||||
 | 
			
		||||
dnl Checks for typedefs, structures and compiler characteristics
 | 
			
		||||
 | 
			
		||||
dnl checks for libraries
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore  >= 0.3.9)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.1.4)
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
		[AS_HELP_STRING([--enable-sanitize], [Compile with address sanitizer enabled], )],
 | 
			
		||||
		[sanitize=$enableval], [sanitize="no"])
 | 
			
		||||
if test x"$sanitize" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
 | 
			
		||||
	CXXFLAGS="$CXXFLAGS -fsanitize=address -fsanitize=undefined"
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
 | 
			
		||||
	LDFLAGS="$LDFLAGS -fsanitize=address -fsanitize=undefined"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
 | 
			
		||||
AC_ARG_ENABLE(sysmocom-bts,
 | 
			
		||||
		AC_HELP_STRING([--enable-sysmocom-bts],
 | 
			
		||||
				[enable code for sysmocom femto-bts [default=no]]),
 | 
			
		||||
		[enable_sysmocom_bts="yes"],[enable_sysmocom_bts="no"])
 | 
			
		||||
AC_MSG_RESULT([$enable_sysmocom_bts])
 | 
			
		||||
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
 | 
			
		||||
AC_ARG_ENABLE(werror,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
		[--enable-werror],
 | 
			
		||||
		[Turn all compiler warnings into errors, with exceptions:
 | 
			
		||||
		 a) deprecation (allow upstream to mark deprecation without breaking builds);
 | 
			
		||||
		 b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
 | 
			
		||||
		]
 | 
			
		||||
	)],
 | 
			
		||||
	[werror=$enableval], [werror="no"])
 | 
			
		||||
if test x"$werror" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	WERROR_FLAGS="-Werror"
 | 
			
		||||
	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
 | 
			
		||||
	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
 | 
			
		||||
	CFLAGS="$CFLAGS $WERROR_FLAGS"
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(profile,
 | 
			
		||||
		[AS_HELP_STRING([--enable-profile], [Compile with profiling support enabled], )],
 | 
			
		||||
		[profile=$enableval], [profile="no"])
 | 
			
		||||
if test x"$profile" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	CFLAGS="$CFLAGS -pg"
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS -pg"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
dnl checks for libraries
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 1.5.0)
 | 
			
		||||
 | 
			
		||||
AC_MSG_CHECKING([whether to enable direct DSP access for PDCH of sysmocom-bts])
 | 
			
		||||
AC_ARG_ENABLE(sysmocom-dsp,
 | 
			
		||||
	      AC_HELP_STRING([--enable-sysmocom-dsp],
 | 
			
		||||
			     [enable code for direct sysmocom DSP access[default=no]]),
 | 
			
		||||
	      [enable_sysmocom_dsp="$enableval"], [enable_sysmocom_dsp="unset"])
 | 
			
		||||
AC_ARG_WITH([sysmobts],
 | 
			
		||||
	    [AS_HELP_STRING([--with-sysmobts=INCLUDE_DIR],
 | 
			
		||||
			    [Location of the sysmobts API header files, implies --enable-sysmocom-dsp])],
 | 
			
		||||
	    [sysmobts_incdir="$withval"], [sysmobts_incdir=""])
 | 
			
		||||
if test "x$sysmobts_incdir" != "x"; then
 | 
			
		||||
	# --with-sysmobts was passed, imply enable_sysmocom_dsp
 | 
			
		||||
	if test "x$enable_sysmocom_dsp" = "xno"; then
 | 
			
		||||
		AC_MSG_RESULT([error])
 | 
			
		||||
		AC_MSG_ERROR([--with-sysmobts does not work with --disable-sysmocom-dsp])
 | 
			
		||||
	fi
 | 
			
		||||
	enable_sysmocom_dsp="yes"
 | 
			
		||||
	# 'readlink' should make an absolute path, but must not return empty if the path does not exist,
 | 
			
		||||
	# so we can still report on it below.
 | 
			
		||||
	sysmobts_incdir="$(readlink -fm "$sysmobts_incdir")"
 | 
			
		||||
	AC_SUBST([SYSMOBTS_INCDIR], $sysmobts_incdir)
 | 
			
		||||
	AC_MSG_RESULT([yes, using -I$SYSMOBTS_INCDIR])
 | 
			
		||||
else
 | 
			
		||||
	AC_SUBST([SYSMOBTS_INCDIR], "")
 | 
			
		||||
	AC_MSG_RESULT([$enable_sysmocom_dsp])
 | 
			
		||||
fi
 | 
			
		||||
AM_CONDITIONAL(ENABLE_SYSMODSP, test "x$enable_sysmocom_dsp" = "xyes")
 | 
			
		||||
if test "x$enable_sysmocom_dsp" = "xyes"; then
 | 
			
		||||
	oldCPPFLAGS="$CPPFLAGS"
 | 
			
		||||
	_sysmobts_include=""
 | 
			
		||||
	_sysmobts_include_msg=""
 | 
			
		||||
	if test -n "$SYSMOBTS_INCDIR"; then
 | 
			
		||||
		_sysmobts_include="-I$SYSMOBTS_INCDIR"
 | 
			
		||||
		_sysmobts_include_msg=" in -I$SYSMOBTS_INCDIR"
 | 
			
		||||
	fi
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS $_sysmobts_include -I$srcdir/include $LIBOSMOCORE_CFLAGS"
 | 
			
		||||
	AC_CHECK_HEADER([sysmocom/femtobts/superfemto.h],[],
 | 
			
		||||
			[AC_MSG_ERROR([sysmocom/femtobts/superfemto.h can not be found$_sysmobts_include_msg, see --with-sysmobts])],
 | 
			
		||||
			[#include <sysmocom/femtobts/superfemto.h>])
 | 
			
		||||
	CPPFLAGS="$oldCPPFLAGS"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_MSG_CHECKING([whether to enable direct PHY access for PDCH of NuRAN Wireless Litecell 1.5 BTS])
 | 
			
		||||
AC_ARG_ENABLE(lc15bts-phy,
 | 
			
		||||
                AC_HELP_STRING([--enable-lc15bts-phy],
 | 
			
		||||
                                [enable code for Litecell 1.5 PHY [default=no]]),
 | 
			
		||||
                [enable_lc15bts_phy="$enableval"],[enable_lc15bts_phy="no"])
 | 
			
		||||
AC_ARG_WITH([litecell15], [AS_HELP_STRING([--with-litecell15=INCLUDE_DIR], [Location of the litecell 1.5 API header files])],
 | 
			
		||||
			 [litecell15_cflags="-I$withval"],[litecell15_cflags=""])
 | 
			
		||||
AC_SUBST([LITECELL15_CFLAGS], $litecell15_cflags)
 | 
			
		||||
AC_MSG_RESULT([$enable_lc15bts_phy])
 | 
			
		||||
AM_CONDITIONAL(ENABLE_LC15BTS_PHY, test "x$enable_lc15bts_phy" = "xyes")
 | 
			
		||||
if test "$enable_litecell15" = "yes"; then
 | 
			
		||||
	oldCPPFLAGS="$CPPFLAGS"
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS $LITECELL15_CFLAGS -I$srcdir/include $LIBOSMOCORE_CFLAGS"
 | 
			
		||||
	AC_CHECK_HEADER([nrw/litecell15/litecell15.h],[],
 | 
			
		||||
			[AC_MSG_ERROR([nrw/litecell15/litecell15.h can not be found using $litecell15_cflags])],
 | 
			
		||||
			[#include <nrw/litecell15/litecell15.h>])
 | 
			
		||||
	CPPFLAGS="$oldCPPFLAGS"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_MSG_CHECKING([whether to enable direct PHY access for PDCH of NuRAN Wireless OC-2G BTS])
 | 
			
		||||
AC_ARG_ENABLE(oc2gbts-phy,
 | 
			
		||||
                AC_HELP_STRING([--enable-oc2gbts-phy],
 | 
			
		||||
                                [enable code for OC-2G PHY [default=no]]),
 | 
			
		||||
                [enable_oc2gbts_phy="$enableval"],[enable_oc2gbts_phy="no"])
 | 
			
		||||
AC_ARG_WITH([oc2g], [AS_HELP_STRING([--with-oc2g=INCLUDE_DIR], [Location of the OC-2G API header files])],
 | 
			
		||||
			 [oc2g_incdir="$withval"],[oc2g_incdir="$incdir"])
 | 
			
		||||
AC_SUBST([OC2G_INCDIR], $oc2g_incdir)
 | 
			
		||||
AC_MSG_RESULT([$enable_oc2gbts_phy])
 | 
			
		||||
AM_CONDITIONAL(ENABLE_OC2GBTS_PHY, test "x$enable_oc2gbts_phy" = "xyes")
 | 
			
		||||
if test "$enable_oc2g" = "yes"; then
 | 
			
		||||
	oldCPPFLAGS=$CPPFLAGS
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS -I$OC2G_INCDIR -I$srcdir/include $LIBOSMOCORE_CFLAGS"
 | 
			
		||||
	AC_CHECK_HEADER([nrw/oc2g/oc2g.h],[],
 | 
			
		||||
			[AC_MSG_ERROR([nrw/oc2g/oc2g.h can not be found in $oc2g_incdir])],
 | 
			
		||||
			[#include <nrw/oc2g/oc2g.h>])
 | 
			
		||||
	CPPFLAGS=$oldCPPFLAGS
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE([vty_tests],
 | 
			
		||||
		AC_HELP_STRING([--enable-vty-tests],
 | 
			
		||||
				[Include the VTY tests in make check [default=no]]),
 | 
			
		||||
		[enable_vty_tests="$enableval"],[enable_vty_tests="no"])
 | 
			
		||||
if test "x$enable_vty_tests" = "xyes" ; then
 | 
			
		||||
	AM_PATH_PYTHON
 | 
			
		||||
	AC_CHECK_PROG(OSMOTESTVTY_CHECK,osmotestvty.py,yes)
 | 
			
		||||
	 if test "x$OSMOTESTVTY_CHECK" != "xyes" ; then
 | 
			
		||||
		AC_MSG_ERROR([Please install osmocom-python to run the vty tests.])
 | 
			
		||||
	fi
 | 
			
		||||
fi
 | 
			
		||||
AC_MSG_CHECKING([whether to enable VTY tests])
 | 
			
		||||
AC_MSG_RESULT([$enable_vty_tests])
 | 
			
		||||
AM_CONDITIONAL(ENABLE_VTY_TESTS, test "x$enable_vty_tests" = "xyes")
 | 
			
		||||
 | 
			
		||||
STD_DEFINES_AND_INCLUDES="-Wall"
 | 
			
		||||
AC_SUBST(STD_DEFINES_AND_INCLUDES)
 | 
			
		||||
 | 
			
		||||
# Generate manuals
 | 
			
		||||
AC_ARG_ENABLE(manuals,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
		[--enable-manuals],
 | 
			
		||||
		[Generate manual PDFs [default=no]],
 | 
			
		||||
	)],
 | 
			
		||||
	[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
 | 
			
		||||
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
 | 
			
		||||
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
 | 
			
		||||
	fallback])
 | 
			
		||||
if test x"$osmo_ac_build_manuals" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
 | 
			
		||||
	if test -n "$OSMO_GSM_MANUALS_DIR"; then
 | 
			
		||||
		echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
 | 
			
		||||
	else
 | 
			
		||||
		OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
 | 
			
		||||
		if test -n "$OSMO_GSM_MANUALS_DIR"; then
 | 
			
		||||
			echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
 | 
			
		||||
		else
 | 
			
		||||
			OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
 | 
			
		||||
			echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
 | 
			
		||||
		fi
 | 
			
		||||
	fi
 | 
			
		||||
	if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
 | 
			
		||||
		AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	# Find and run check-depends
 | 
			
		||||
	CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
 | 
			
		||||
	if ! test -x "$CHECK_DEPENDS"; then
 | 
			
		||||
		CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
 | 
			
		||||
	fi
 | 
			
		||||
	if ! $CHECK_DEPENDS; then
 | 
			
		||||
		AC_MSG_ERROR("missing dependencies for --enable-manuals")
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	# Put in Makefile with absolute path
 | 
			
		||||
	OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
 | 
			
		||||
	AC_SUBST([OSMO_GSM_MANUALS_DIR])
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# https://www.freedesktop.org/software/systemd/man/daemon.html
 | 
			
		||||
AC_ARG_WITH([systemdsystemunitdir],
 | 
			
		||||
     [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
 | 
			
		||||
     [with_systemdsystemunitdir=auto])
 | 
			
		||||
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
 | 
			
		||||
     def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
 | 
			
		||||
 | 
			
		||||
     AS_IF([test "x$def_systemdsystemunitdir" = "x"],
 | 
			
		||||
   [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
 | 
			
		||||
    [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
 | 
			
		||||
    with_systemdsystemunitdir=no],
 | 
			
		||||
   [with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
 | 
			
		||||
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
 | 
			
		||||
      [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
 | 
			
		||||
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
 | 
			
		||||
 | 
			
		||||
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
 | 
			
		||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
 | 
			
		||||
AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
 | 
			
		||||
AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
 | 
			
		||||
 | 
			
		||||
AC_OUTPUT(
 | 
			
		||||
    include/Makefile
 | 
			
		||||
    src/Makefile
 | 
			
		||||
    doc/Makefile
 | 
			
		||||
    doc/examples/Makefile
 | 
			
		||||
    tests/Makefile
 | 
			
		||||
    doc/manuals/Makefile
 | 
			
		||||
    contrib/Makefile
 | 
			
		||||
    contrib/systemd/Makefile
 | 
			
		||||
    contrib/osmo-pcu.spec
 | 
			
		||||
    Makefile)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								contrib/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								contrib/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
SUBDIRS = systemd
 | 
			
		||||
							
								
								
									
										107
									
								
								contrib/jenkins.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										107
									
								
								contrib/jenkins.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
# jenkins build helper script for osmo-pcu.  This is how we build on jenkins.osmocom.org
 | 
			
		||||
#
 | 
			
		||||
# environment variables:
 | 
			
		||||
# * with_dsp: the DSP to configure ("sysmo", "lc15" or "none")
 | 
			
		||||
# * with_vty: enable VTY tests if set to "True"
 | 
			
		||||
# * WITH_MANUALS: build manual PDFs if set to "1"
 | 
			
		||||
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
 | 
			
		||||
	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
 | 
			
		||||
	exit 2
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
set -ex
 | 
			
		||||
 | 
			
		||||
if [ -z "$MAKE" ]; then
 | 
			
		||||
  echo 'The $MAKE variable is not defined, cannot build'
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
base="$PWD"
 | 
			
		||||
deps="$base/deps"
 | 
			
		||||
inst="$deps/install"
 | 
			
		||||
export deps inst
 | 
			
		||||
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
 | 
			
		||||
mkdir "$deps" || true
 | 
			
		||||
 | 
			
		||||
# Collect configure options for osmo-pcu
 | 
			
		||||
PCU_CONFIG=""
 | 
			
		||||
if [ "$with_dsp" = sysmo ]; then
 | 
			
		||||
  PCU_CONFIG="$PCU_CONFIG --enable-werror --enable-sysmocom-dsp --with-sysmobts=$inst/include/"
 | 
			
		||||
 | 
			
		||||
  # For direct sysmo DSP access, provide the SysmoBTS Layer 1 API
 | 
			
		||||
  cd "$deps"
 | 
			
		||||
  osmo-layer1-headers.sh sysmo
 | 
			
		||||
  mkdir -p "$inst/include/sysmocom/femtobts"
 | 
			
		||||
  ln -s $deps/layer1-headers/include/* "$inst/include/sysmocom/femtobts/"
 | 
			
		||||
  cd "$base"
 | 
			
		||||
 | 
			
		||||
elif [ "$with_dsp" = lc15 ]; then
 | 
			
		||||
  PCU_CONFIG="$PCU_CONFIG --enable-lc15bts-phy --with-litecell15=$deps/layer1-headers/inc"
 | 
			
		||||
  cd "$deps"
 | 
			
		||||
  osmo-layer1-headers.sh lc15 "$FIRMWARE_VERSION"
 | 
			
		||||
  cd "$base"
 | 
			
		||||
 | 
			
		||||
elif [ "$with_dsp" = oc2g ]; then
 | 
			
		||||
  PCU_CONFIG="$PCU_CONFIG --enable-oc2gbts-phy --with-oc2g=$deps/layer1-headers/inc"
 | 
			
		||||
  cd "$deps"
 | 
			
		||||
  osmo-layer1-headers.sh oc2g "$FIRMWARE_VERSION"
 | 
			
		||||
  cd "$base"
 | 
			
		||||
 | 
			
		||||
elif [ -z "$with_dsp" -o "$with_dsp" = none ]; then
 | 
			
		||||
  echo "Direct DSP access disabled, sanitizer enabled"
 | 
			
		||||
  PCU_CONFIG="$PCU_CONFIG --enable-werror --enable-sanitize"
 | 
			
		||||
else
 | 
			
		||||
  echo 'Invalid $with_dsp value:' $with_dsp
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$with_vty" = "True" ]; then
 | 
			
		||||
  PCU_CONFIG="$PCU_CONFIG --enable-vty-tests"
 | 
			
		||||
elif [ -z "$with_vty" -o "$with_vty" = "False" ]; then
 | 
			
		||||
  echo "VTY tests disabled"
 | 
			
		||||
else
 | 
			
		||||
  echo 'Invalid $with_vty value:' $with_vty
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
 | 
			
		||||
 | 
			
		||||
# Build deps
 | 
			
		||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
 | 
			
		||||
 | 
			
		||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
 | 
			
		||||
export LD_LIBRARY_PATH="$inst/lib"
 | 
			
		||||
export PATH="$inst/bin:$PATH"
 | 
			
		||||
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ]; then
 | 
			
		||||
  PCU_CONFIG="$PCU_CONFIG --enable-manuals"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
set +x
 | 
			
		||||
echo
 | 
			
		||||
echo
 | 
			
		||||
echo
 | 
			
		||||
echo " =============================== osmo-pcu ==============================="
 | 
			
		||||
echo
 | 
			
		||||
set -x
 | 
			
		||||
 | 
			
		||||
autoreconf --install --force
 | 
			
		||||
./configure $PCU_CONFIG
 | 
			
		||||
$MAKE $PARALLEL_MAKE
 | 
			
		||||
DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" \
 | 
			
		||||
  $MAKE $PARALLEL_MAKE distcheck \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
 | 
			
		||||
  make -C "$base/doc/manuals" publish
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
$MAKE $PARALLEL_MAKE maintainer-clean
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
							
								
								
									
										83
									
								
								contrib/osmo-pcu.spec.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								contrib/osmo-pcu.spec.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
#
 | 
			
		||||
# spec file for package osmo-pcu
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
 | 
			
		||||
#
 | 
			
		||||
# All modifications and additions to the file contributed by third parties
 | 
			
		||||
# remain the property of their copyright owners, unless otherwise agreed
 | 
			
		||||
# upon. The license for this file, and modifications and additions to the
 | 
			
		||||
# file, is the same license as for the pristine package itself (unless the
 | 
			
		||||
# license for the pristine package is not an Open Source License, in which
 | 
			
		||||
# case the license is the MIT License). An "Open Source License" is a
 | 
			
		||||
# license that conforms to the Open Source Definition (Version 1.9)
 | 
			
		||||
# published by the Open Source Initiative.
 | 
			
		||||
 | 
			
		||||
Name:           osmo-pcu
 | 
			
		||||
Version:        @VERSION@
 | 
			
		||||
Release:        0
 | 
			
		||||
Summary:        Osmocom GPRS Packet Control Unit (PCU)
 | 
			
		||||
License:        GPL-2.0-only
 | 
			
		||||
Group:          Productivity/Telephony/Servers
 | 
			
		||||
URL:            https://osmocom.org/projects/osmopcu
 | 
			
		||||
Source:         %{name}-%{version}.tar.xz
 | 
			
		||||
BuildRequires:  autoconf
 | 
			
		||||
BuildRequires:  automake
 | 
			
		||||
BuildRequires:  gcc-c++
 | 
			
		||||
BuildRequires:  libtool
 | 
			
		||||
BuildRequires:  pkgconfig >= 0.20
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
BuildRequires:  systemd-rpm-macros
 | 
			
		||||
%endif
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocore) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmogb) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmogsm) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmovty) >= 1.5.0
 | 
			
		||||
BuildRequires:  pkgconfig(libosmoctrl) >= 1.5.0
 | 
			
		||||
%{?systemd_requires}
 | 
			
		||||
 | 
			
		||||
%description
 | 
			
		||||
Osmocom PCU code (RLC/MAC/PCU) for OpenBTS and OsmoBTS.
 | 
			
		||||
 | 
			
		||||
%prep
 | 
			
		||||
%setup -q
 | 
			
		||||
 | 
			
		||||
%build
 | 
			
		||||
echo "%{version}" >.tarball-version
 | 
			
		||||
autoreconf -fi
 | 
			
		||||
%configure \
 | 
			
		||||
  --enable-shared \
 | 
			
		||||
  --disable-static \
 | 
			
		||||
  --docdir=%{_docdir}/%{name} \
 | 
			
		||||
  --with-systemdsystemunitdir=%{_unitdir}
 | 
			
		||||
make %{?_smp_mflags}
 | 
			
		||||
 | 
			
		||||
%install
 | 
			
		||||
%make_install
 | 
			
		||||
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
%preun
 | 
			
		||||
%service_del_preun %{name}.service
 | 
			
		||||
 | 
			
		||||
%postun
 | 
			
		||||
%service_del_postun %{name}.service
 | 
			
		||||
 | 
			
		||||
%pre
 | 
			
		||||
%service_add_pre %{name}.service
 | 
			
		||||
 | 
			
		||||
%post
 | 
			
		||||
%service_add_post %{name}.service
 | 
			
		||||
%endif
 | 
			
		||||
 | 
			
		||||
%check
 | 
			
		||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
 | 
			
		||||
 | 
			
		||||
%files
 | 
			
		||||
%license COPYING
 | 
			
		||||
%doc README.md
 | 
			
		||||
%doc %{_docdir}/%{name}/examples
 | 
			
		||||
%{_bindir}/osmo-pcu
 | 
			
		||||
%dir %{_sysconfdir}/osmocom
 | 
			
		||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-pcu.cfg
 | 
			
		||||
%{_unitdir}/%{name}.service
 | 
			
		||||
 | 
			
		||||
%changelog
 | 
			
		||||
							
								
								
									
										6
									
								
								contrib/systemd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contrib/systemd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
EXTRA_DIST = osmo-pcu.service
 | 
			
		||||
 | 
			
		||||
if HAVE_SYSTEMD
 | 
			
		||||
systemdsystemunit_DATA = \
 | 
			
		||||
  osmo-pcu.service
 | 
			
		||||
endif
 | 
			
		||||
							
								
								
									
										16
									
								
								contrib/systemd/osmo-pcu.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								contrib/systemd/osmo-pcu.service
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom osmo-pcu
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
 | 
			
		||||
Restart=always
 | 
			
		||||
RestartSec=2
 | 
			
		||||
RestartPreventExitStatus=1
 | 
			
		||||
 | 
			
		||||
# The msg queues must be read fast enough
 | 
			
		||||
CPUSchedulingPolicy=rr
 | 
			
		||||
CPUSchedulingPriority=1
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
							
								
								
									
										882
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										882
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,882 @@
 | 
			
		||||
osmo-pcu (0.9.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * Pass paging group instead of imsi where later is not needed
 | 
			
		||||
  * Allow Gb PAGING-PS without P-TMSI
 | 
			
		||||
  * Support Gb PAGING-CS
 | 
			
		||||
  * Support PAGING-CS and PAGING-PS on on PTP-BVCI
 | 
			
		||||
  * tests/rlcmac: print test name at the start
 | 
			
		||||
  * tests/rlcmac: Memzero decoded struct
 | 
			
		||||
  * tests/rlcmac: Fix missing commas with unexpected results
 | 
			
		||||
  * tests/rlcmac: Use osmo_hexdump to print buffers
 | 
			
		||||
  * tests/rlcmac: Don't check stderr output
 | 
			
		||||
  * tests/rlcmac: Add test to showcase that decode_gsm_ra_cap() fails
 | 
			
		||||
  * csn1: Extend CSN_SERIALIZE to allow 0 bit of length
 | 
			
		||||
  * csn1: Allow CHOICE elements to re-process the bits used for the choice
 | 
			
		||||
  * csn1: Fix pedantic compiler warnings in csn.1 dissectors
 | 
			
		||||
  * csn1: Drop format_p union from CSN_DESCR
 | 
			
		||||
  * gsm_rlcmac.cpp: Fix trailing whitespace
 | 
			
		||||
  * cosmetic: csn1.cpp: Fix whitespace
 | 
			
		||||
  * csn1.cpp: Rework ProcessError() function to print errors
 | 
			
		||||
  * rlcmac: Return error code from csn1 encoder/decoder
 | 
			
		||||
  * Check return code of rlcmac decode/encode functions
 | 
			
		||||
  * rlcmac: Transform a few LOGPC messages to LOGP
 | 
			
		||||
  * Fix trailing newline mess with LOGP(C) in rlcmac/csn1
 | 
			
		||||
  * llc_queue::{dequeue,enqueue}() refactor
 | 
			
		||||
  * gsm_rlcmac: fix Packet_Resource_Request_t: s/Slot/I_LEVEL_TN/
 | 
			
		||||
  * tests/llc: Change unrealistic time jump to avoid runtime error under ARM
 | 
			
		||||
  * Use clock_gettime(CLOCK_MONOTONIC) and timespec everywhere
 | 
			
		||||
  * Use downlink BSSGP RA Cap IE
 | 
			
		||||
  * tests/RLCMACTest: free allocated bitvectors
 | 
			
		||||
  * tests/RLCMACTest: Several fixes and improvements to RAcap tests
 | 
			
		||||
  * rlcmac: Don't pass array element to CSN1 descriptors
 | 
			
		||||
  * csn1: Validate recursive array max size during decoding
 | 
			
		||||
  * rlcmac: Fix bug receiving RA cap
 | 
			
		||||
  * rlcmac: Log names of de/encoded rlcmac packet types
 | 
			
		||||
  * rlcmac: Introduce MS Radio Access Capabilities 2 to fix related spare bits
 | 
			
		||||
  * cosmetic: rlcmac: Fix comment typo and whitespace introduced recently
 | 
			
		||||
  * rlcmac: Rename field to MS RA Cap2 in Additional_MS_Rad_Access_Cap_t
 | 
			
		||||
  * pcu_l1_if.cpp: Add missing header ctype.h
 | 
			
		||||
  * gsm_rlcmac: Use 'struct bitvec' instead of 'bitvec'
 | 
			
		||||
  * cosmetic: Do not indent header includes inside extern C block
 | 
			
		||||
  * gsm_rlcmac.cpp: Avoid declaring variable in for loop
 | 
			
		||||
  * csn1.h: Fix trailing whitespace
 | 
			
		||||
  * tbf.cpp: Include c++ <new> header required for new operator's replacement type
 | 
			
		||||
  * gsm_rlcmac: Disable unused CSN1 descriptors
 | 
			
		||||
  * Move gsm_rlcmac.cpp -> .c
 | 
			
		||||
  * rlcmac: support decode FDD_CELL_INFORMATION of "UTRAN FDD Description
 | 
			
		||||
  * rlcmac: add dissection of 2G->3G/4G PS handover
 | 
			
		||||
  * csn1: Fix Several typos & whitespace
 | 
			
		||||
  * csn1: verify enough bits present to decode whole CSN_UINT_ARRAY
 | 
			
		||||
  * csn1: Properly verify CSN_BITMAP length
 | 
			
		||||
  * csn1: Remove code block from CSN_NEXT_EXIST_LH
 | 
			
		||||
  * pcu_l1_if: Don't use GSMTAP_CHANNEL_PACCH when sending unknown gsmtap blocks
 | 
			
		||||
  * pdch: Avoid sending GSMTAP_CHANNEL_UNKOWN for rejected UL EGPRS data block
 | 
			
		||||
  * tbf: Avoid crash: don't set TBF window size if setup failed
 | 
			
		||||
  * bts: Rename mslot_class_from_ra
 | 
			
		||||
  * bts: Fix Decoding EGPRS MultislotClass from 11-bit EGPRS PACKET CHANNEL REQUEST
 | 
			
		||||
  * bts: Return uint8_t in egprs_mslot_class_from_ra()
 | 
			
		||||
  * Use OSMO_FD_* instead of deprecated BSC_FD_*
 | 
			
		||||
  * Expect ms object to exist before calling tbf_alloc_ul_tbf()
 | 
			
		||||
  * Expect ms object to exist before calling tbf_alloc_dl_tbf()
 | 
			
		||||
  * pdch: rcv_resource_request(): Clarify tbf_free only needed if MS used to exist beforehand
 | 
			
		||||
  * Drop unneeded arg 'ta' in tbf_alloc_ul()
 | 
			
		||||
  * bts: Drop specific functions to increase counters
 | 
			
		||||
  * bts: Drop specific functions to add values to counters
 | 
			
		||||
  * bts: Drop specific functions to add values to stats
 | 
			
		||||
  * pcu: tbf_ul: Clean up maybe_schedule_uplink_acknack()
 | 
			
		||||
  * sysmo: femtobts.h: Avoid redefining global variables
 | 
			
		||||
  * rlc: Drop unused function gprs_rlc_data::put_data
 | 
			
		||||
  * rlc: Move prepare() function out of gprs_rlc_data struct
 | 
			
		||||
  * tbf_ul: Fix UL ACK not sent to MS if intermediate UL block is lost
 | 
			
		||||
  * Get rid of class GprsCodingScheme
 | 
			
		||||
  * gsmtap: Set signal level and SNR fields
 | 
			
		||||
  * gprs_ms: Small clean ups in IMSI storage related code
 | 
			
		||||
  * gprs_ms: Transfer known EGPRS MS class when mergling old MS
 | 
			
		||||
  * tbf: Drop unneeded method set_tlli_from_ul
 | 
			
		||||
  * pdch.cpp: Fix logging line format in rcv_block wrong length
 | 
			
		||||
  * Set correct GSMTAP channel type for PDTCH messages returning error
 | 
			
		||||
  * decoding.cpp: Improve logging in malformed UL data parsing
 | 
			
		||||
  * tbf_dl: uint8_t is enough to store a TA value
 | 
			
		||||
  * encoding: Encode TA as unsigned and check validty against GSM48_TA_INVALID
 | 
			
		||||
  * encoding.cpp: Fix missing spacing in function param
 | 
			
		||||
  * pdch.cpp: Avoid dropping existing DL TBF during rcv_resource_request
 | 
			
		||||
  * pdch.cpp: Avoid resetting (egprs_)ms_class to unknown if not found in MS RadioAccCap
 | 
			
		||||
  * pdch.cpp: Fix wrong annoying log line about non-scheduled ResourceReq received
 | 
			
		||||
  * pdch.cpp: Store TLLI promptly on newly created TLLI in rcv_resource_request
 | 
			
		||||
  * Fix typo in log message
 | 
			
		||||
  * pdch: Drop unneeded notice log message in rcv pkt meas report
 | 
			
		||||
  * Introduce log macro helper LOGPMS
 | 
			
		||||
  * configure.ac: Fix trailing whitespace
 | 
			
		||||
  * doc: Update VTY reference xml file
 | 
			
		||||
  * Support setting rt-prio and cpu-affinity mask through VTY
 | 
			
		||||
  * pdch: rcv pkt meas rep: Allocate MS object early in path and use it
 | 
			
		||||
  * Fix recent typo preventing MS from registering
 | 
			
		||||
  * gitignore: Add __pychache__ dir
 | 
			
		||||
  * tbf: Don't log rlcmac_diag() output in separate lines
 | 
			
		||||
  * gprs_ms_storage.h: Set pointer to NULL instead of 0
 | 
			
		||||
  * Free all MS TBFs when receiving GPRS Suspension Request
 | 
			
		||||
  * cosmetic: fix indentation alignment
 | 
			
		||||
  * vty: Add 'show bts pdch' command
 | 
			
		||||
  * cosmetic: Fix indentation of for loops
 | 
			
		||||
  * cosmetic: Fix typo in comment
 | 
			
		||||
  * Fix crash accessing NULL tbf->pdch[first_ts]
 | 
			
		||||
  * contrib/jenkins: Enable parallel make in make distcheck
 | 
			
		||||
  * Improve debug logging for alloc algos
 | 
			
		||||
  * Fix several calls to LOGPAL
 | 
			
		||||
  * Move gprs_rlcmac_ul_tbf::window to correct file
 | 
			
		||||
  * Move constructor gprs_rlcmac_dl_tbf::BandWidth to correct file
 | 
			
		||||
  * tbf: Make window() available to tbf base class
 | 
			
		||||
  * tbf: Implement enable_egprs() once
 | 
			
		||||
  * tbf: Set MS during constructor time
 | 
			
		||||
  * Move ul_tbf allocation code to correct file
 | 
			
		||||
  * Move dl_tbf allocation code to correct file
 | 
			
		||||
  * tbf: Drop unused function disable_egprs()
 | 
			
		||||
  * tests: ms: Pass correct pointer in constructor instead of NULL
 | 
			
		||||
  * tbf: Clean up gprs_rlcmac_dl_tbf::handle()
 | 
			
		||||
  * alloc_algo_b: Select TRX with least assigned TFIs during TBF alloc
 | 
			
		||||
  * bts: define egprs_enabled as bool
 | 
			
		||||
  * cosmetic: Fix ws between if keyword and parenthesis
 | 
			
		||||
  * tbf_dl: Update (egprs_)ms_class for already known MS
 | 
			
		||||
  * cosmetic: tests: pcu_emu: fix trailing whitespace
 | 
			
		||||
  * gprs_ms: Use proper function to get CS
 | 
			
		||||
  * Move BTS initial values inside bts.cpp
 | 
			
		||||
  * pcuif: Improve BTS-supported CS/MCS handling
 | 
			
		||||
  * Move EGPRS MS mode set to gprs_ms.cpp
 | 
			
		||||
  * Take into account BTS supported (M)CS values when retrieving the maximum
 | 
			
		||||
  * Enable egprs support through PCUIF from BTS/BSC
 | 
			
		||||
  * pdch: Process received CS1-4 data blocks regardless of egprs_enabled
 | 
			
		||||
  * tbf_dl: Don't fake EGPRS MS class when no related info is available
 | 
			
		||||
  * tbf_ul: Allow non-egprs phones if EGPRS is enabled
 | 
			
		||||
  * Get rid of bts->egprs_enabled
 | 
			
		||||
  * Fix configuration of initial_(m)cs
 | 
			
		||||
  * Fix mcs_is_valid(): UNKNOWN value is not a valid (M)CS
 | 
			
		||||
  * gprs_ms: Avoid enabling EGPRS if no MCS are supported
 | 
			
		||||
  * tbf_ul: Log mismatching TLLI on log message
 | 
			
		||||
  * Fix ctr reports: Remove ctr description from already removed counter
 | 
			
		||||
  * encoding: Fix duplicate word in log str
 | 
			
		||||
  * sched: Fix sending GSMTAP DL data blocks with unset USF
 | 
			
		||||
  * sched: Use correct GMSTAP category for EGPRS DL data blocks
 | 
			
		||||
  * Support multiplexing of GPRS and EGPRS TBFs in one PDCH
 | 
			
		||||
  * pdch: packet_paging_request: Put back non-fitting paging entry where where it was
 | 
			
		||||
  * pdch: Log hexdump of decde failure for dl rlcmac block
 | 
			
		||||
  * csn1: Fix readIndex pointer change in CSN_VARIABLE_ARRAY
 | 
			
		||||
  * csn1: Log CSN_VARIABLE_ARRAY values as hex
 | 
			
		||||
  * main: generate coredump and exit upon SIGABRT received
 | 
			
		||||
  * tbf: Log previous TS when changing Control TS
 | 
			
		||||
  * Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
 | 
			
		||||
  * Dl TBF: Get rid of LLC UI dummy blocks following other data
 | 
			
		||||
  * rlcmac: Fix typo in MT_PACKET_CELL_CHANGE_NOTIFICATION value_string
 | 
			
		||||
  * gprs_rlcmac_sched: Use helper structure to store several tbf pointer params
 | 
			
		||||
  * sched: Convert code handling next_list array to be size independant
 | 
			
		||||
  * Convert GprsMS and helpers classes to C
 | 
			
		||||
  * tbf: Fix wrong verb used in log message
 | 
			
		||||
  * .gitignore: ignore files ending with ~
 | 
			
		||||
  * doc: Improve CS/MCS GPRS/EGPRS considerations in User Manual
 | 
			
		||||
  * tbf: remove 'software error' logs from tbf_free
 | 
			
		||||
  * ms: Replace struct var with rate_ctr
 | 
			
		||||
  * AllocTest: Avoid queuing tons of to-be-freed ms
 | 
			
		||||
  * gprs_ms: Mark ms_ctrg_desc static
 | 
			
		||||
  * Workaround ASan false positive runtime errors under some platforms
 | 
			
		||||
  * Split PCU global PCU object from BTS object
 | 
			
		||||
  * Move T_defs_pcu from BTS to PCU object
 | 
			
		||||
  * Move force_two_phase field from BTS to PCU
 | 
			
		||||
  * Move alpha,gamma fields from BTS to PCU
 | 
			
		||||
  * Move dl_tbf_preemptive_retransmission field from BTS to PCU
 | 
			
		||||
  * Move dl_arq_type field from BTS to PCU
 | 
			
		||||
  * Move cs_adj* fields from BTS to PCU
 | 
			
		||||
  * Move cs_downgrade_threshold field from BTS to PCU
 | 
			
		||||
  * Move (m)cs_lqual_ranges fields from BTS to PCU
 | 
			
		||||
  * Move ns_dialect field from BTS to PCU
 | 
			
		||||
  * Move fc_* fields from BTS to PCU
 | 
			
		||||
  * tests/tbf: Allocate PCU per test instead of globally
 | 
			
		||||
  * Move ws_* fields from BTS to PCU
 | 
			
		||||
  * Move llc_* fields from BTS to PCU
 | 
			
		||||
  * Fix configuration mess of initial_cs/mcs between PCUIF and VTY
 | 
			
		||||
  * Unify BTS into a C usable structure
 | 
			
		||||
  * Get rid of bts singletons
 | 
			
		||||
  * Rename 'bts_data' leftovers to 'bts'
 | 
			
		||||
  * bts: combine bts_{init,cleanup} into consturctor/destructor methods
 | 
			
		||||
  * Get rid of unused gsm_timer.{cpp,h}
 | 
			
		||||
  * Convert gprs_bssgp_pcu.cpp to C
 | 
			
		||||
  * Move tbf::free_all static methods to proper object files
 | 
			
		||||
  * Convert osmo_bts_sock.cpp to C
 | 
			
		||||
  * Allow multiple bts objects in PCU
 | 
			
		||||
  * bts: Store RAC+CI from info_ind
 | 
			
		||||
  * Get rid of singleton gprs_bssgp_pcu_current_bctx()
 | 
			
		||||
  * Initial handling support for RIM messages
 | 
			
		||||
  * gprs_pcu: Use libosmocore osmo_cgi_ps_cmp API
 | 
			
		||||
  * ms: Drop always-false check
 | 
			
		||||
  * sched: Check if egprs is enabled in TBF rather than MS being egprs capable
 | 
			
		||||
  * tbf: Drop always-true condition checking for MS
 | 
			
		||||
  * encoding: fix typos in comment
 | 
			
		||||
  * ms: Set proper initial MCS values setting mode EGPRS_GMSK
 | 
			
		||||
  * ms: Properly handle EGPRS_GMSK mode in ms_max_cs_dl/ul()
 | 
			
		||||
  * Fix Dl EGPRS data blocks being generated occasionally on GPRS TBFs
 | 
			
		||||
  * sched: Avoid picking TBF with nacked dl blocks when GMSK is required
 | 
			
		||||
  * tbf: Make tbf_ms() param const
 | 
			
		||||
  * Introduce NACC support
 | 
			
		||||
  * NACC: Fix crash freeing struct if CTRL conn was refused during alloc
 | 
			
		||||
  * NACC: delay CTRL conn socket init until it's needed
 | 
			
		||||
  * NACC: allow setting keep time for entries in neigh and si cache
 | 
			
		||||
  * NACC: Configure neighbor and SI resolution timeout values
 | 
			
		||||
  * NACC: Send only Pkt Cell Chg Continue if SI retrieve fails
 | 
			
		||||
  * doc: Mark PCU node red in network node diagram
 | 
			
		||||
  * doc: Introduce section documenting NACC support
 | 
			
		||||
  * nacc: Improve log line failing to establish CTRL neigh conn
 | 
			
		||||
  * Update TS 04.60 references to new TS 44.060
 | 
			
		||||
  * Drop comment about an already implemented TODO
 | 
			
		||||
  * Move src/tbf.txt to doc/
 | 
			
		||||
  * encoding: Fix comment description of S/P field
 | 
			
		||||
  * tbf: Reuse stored result in variable in check_polling()
 | 
			
		||||
  * tbf: Constify some methods
 | 
			
		||||
  * nacc: Fix typo in function name
 | 
			
		||||
  * nacc: Implement Pkt Cell Change Continue retransmission
 | 
			
		||||
  * nacc: Avoid RIM procedures targeting cells under same PCU
 | 
			
		||||
  * rlc.h: Fix struct bit fields on big endian systems
 | 
			
		||||
  * cosmetic: fix typo in comment
 | 
			
		||||
  * nacc_fsm: Move code filling struct to helper function
 | 
			
		||||
  * nacc_fsm: Remove NACC_EV_RX_SI from in_event_mask of some states
 | 
			
		||||
  * nacc_fsm: Support receiving Pkt Cell Change Notify in state WAIT_RESOLVE_RAC_CI
 | 
			
		||||
  * nacc_fsm: nacc_fsm: Support receiving Pkt Cell Change Notify in state WAIT_REQUEST_SI
 | 
			
		||||
  * nacc_fsm: Support receiving Pkt Cell Chg Notif while in some advanced states
 | 
			
		||||
  * nacc_fsm: Improve log when sending RIM RAN-INFO to gather SI from remote cell
 | 
			
		||||
  * vty: Write 'neighbor resolution' config to file
 | 
			
		||||
  * cosmetic: fix line indentation
 | 
			
		||||
  * sched: Avoid selecting TBF to tx NACC Dl msg if no TFI is assigned
 | 
			
		||||
  * tests: Explicitly drop category from log
 | 
			
		||||
  * tests: Replace deprecated API log_set_print_filename
 | 
			
		||||
  * Use NULL as default value for pointer type
 | 
			
		||||
  * find_multi_slots: Avoid calling mslot_class_get_tx() on each iteration
 | 
			
		||||
  * find_multi_slots: Avoid multiple calls to mslot_class_get_rx()
 | 
			
		||||
  * find_multi_slots: Mark mslot_class properties const
 | 
			
		||||
  * find_multi_slots: Avoid multiple calls to mslot_class_get_type()
 | 
			
		||||
  * Use ALPHA value received in SI13 from PCUIF
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * pcu_l1_if.cpp: fix NULL-pointer dereference in imsi2paging_group()
 | 
			
		||||
  * gsm_timer: fix comparison of constant LONG_MAX with an integer
 | 
			
		||||
  * encoding: fix log_alert_exit(): do not treat error as format string
 | 
			
		||||
  * tests/alloc: fix implicit conversion from 'double' to 'int8_t'
 | 
			
		||||
  * gprs_bssgp_pcu: fix invalid use of non-static data member 'frame'
 | 
			
		||||
  * gprs_bssgp_pcu: fixup: fix length check in gprs_bssgp_pcu_rx_dl_ud()
 | 
			
		||||
  * csn1: fix csnStreamDecoder(): avoid conditional calls to bitvec_read_field()
 | 
			
		||||
  * VTY: get rid of pcu_vty_go_parent() / pcu_vty_is_config_node()
 | 
			
		||||
  * VTY: install talloc context introspection commands
 | 
			
		||||
  * pcu_sock: fix memleak, allocate pcu_sock_state on stack
 | 
			
		||||
  * pcu_sock: cosmetic: fix typo in a comment message
 | 
			
		||||
  * tbf: cosmetic: fix spacing in gprs_rlcmac_tbf::create_ul_ass()
 | 
			
		||||
  * tbf: fix NULL pointer dereference in create_[ul|dl]_ass()
 | 
			
		||||
  * encoding: assert return value of bitvec_set_u64()
 | 
			
		||||
  * csn1: fix some mistaken CSN.1 error names
 | 
			
		||||
  * csn1: fix csnStreamDecoder(): catch unknown CSN_CHOICE values
 | 
			
		||||
  * tests/rlcmac: mark Packet Polling Request as malformed
 | 
			
		||||
  * csn1: fix existNextElement(): use bitvec_get_bit_pos()
 | 
			
		||||
  * tests/rlcmac: additionally match debug output of the CSN.1 codec
 | 
			
		||||
  * csn1: get rid of C++ specific code, compile with GCC
 | 
			
		||||
  * csn1: fix csnStreamDecoder(): do not subtract no_of_bits twice
 | 
			
		||||
  * csn1: fix csnStreamDecoder(): always keep remaining_bits_len updated
 | 
			
		||||
  * csn1: fix csnStreamDecoder(): update bit_offset in CSN_EXIST{_LH}
 | 
			
		||||
  * csn1: bitvec_get_uint() may return a negative, use %d
 | 
			
		||||
  * csn1: use proper format specifier for unsigned integers
 | 
			
		||||
  * gsm_rlcmac: fix misleading LOGP statement in decode_gsm_ra_cap()
 | 
			
		||||
  * tests/rlcmac: fix malformed MS RA capability in testRAcap()
 | 
			
		||||
  * tests/rlcmac: also verify encoding of MS RA Capability
 | 
			
		||||
  * tests/rlcmac: add a new test vector for Packet Resource Request
 | 
			
		||||
  * csn1: fix csnStreamDecoder(): skip bits unhandled by serialize()
 | 
			
		||||
  * tests/rlcmac: also enable logging for DRLCMACDATA category
 | 
			
		||||
  * rlcmac: fix encode_gsm_*(): do not suppress encoding errors
 | 
			
		||||
  * csn1: fix: do not return 0 if no bits left in the buffer
 | 
			
		||||
  * BSSGP: cosmetic use OSMO_IMSI_BUF_SIZE from libosmocore
 | 
			
		||||
  * BSSGP: fix: properly encode P-TMSI in RR Paging Request
 | 
			
		||||
  * pdch: fix packet_paging_request(): properly print paging MI
 | 
			
		||||
  * pdch: cosmetic: use GSM_MI_TYPE_* constants from libosmocore
 | 
			
		||||
  * fix: properly include pure C headers from C++ code
 | 
			
		||||
  * l1if: fix pcu_rx_rach_ind(): use proper format string specifiers
 | 
			
		||||
  * sba: fix possible memleak in SBAController::alloc()
 | 
			
		||||
  * TBF/UL: fix rcv_data_block_acknowledged(): print the actual TLLI
 | 
			
		||||
  * fix egprs_mslot_class_from_ra(): multislot class may not be present
 | 
			
		||||
  * l1if: fix: s/pcu_rx_rach_ind_pdtch/pcu_rx_rach_ind_ptcch/g
 | 
			
		||||
  * csn1: fix M_CHOICE: restirct maximum length of the choice list
 | 
			
		||||
  * csn1: fix csnStreamEncoder(): also check length of the choice list
 | 
			
		||||
  * csn1: fix csnStreamEncoder(): always check the choice index
 | 
			
		||||
  * csn1: fix: never use enumerated types in codec structures
 | 
			
		||||
  * encoding: cosmetic: use RLC_MODE_ACKNOWLEDGED where possible
 | 
			
		||||
  * RLC/MAC: implement decoding of EGPRS Packet Channel Request
 | 
			
		||||
  * encoding: fix write_ia_rest_egprs_uplink_sba(): add missing CHECK(rc)
 | 
			
		||||
  * doc/manuals: fix typo in overview.adoc: s/Omsocom/Osmocom/g
 | 
			
		||||
  * bts: refactor handling and parsing of RACH.ind
 | 
			
		||||
  * BTS::parse_rach_ind(): properly handle EGPRS Packet Channel Request
 | 
			
		||||
  * bts: add send_gsmtap_rach(), also send PTCCH/U over GSMTAP
 | 
			
		||||
  * bts: fix send_gsmtap_rach(): properly pack 11 bit RA
 | 
			
		||||
  * bts: cosmetic: use DUMMY_VEC for padding where possible
 | 
			
		||||
  * encoding: drop log_alert_exit(), use OSMO_ASSERT() instead
 | 
			
		||||
  * encoding: assert() presence of Downlink TBF
 | 
			
		||||
  * direct-phy: fix handle_ph_ra_ind(): handle PH-RA.ind on PRACH SAPI
 | 
			
		||||
  * debian/control: change maintainer to the Osmocom team / mailing list
 | 
			
		||||
  * pcu_l1_if: use proper format specifier for PCUIF version
 | 
			
		||||
  * pcu_l1_if: constify the argument of pcu_rx_info_ind()
 | 
			
		||||
  * pcu_l1_if: cosmetic: rename both 'trx'/'ts' to 'trx_nr'/'ts_nr'
 | 
			
		||||
  * pcu_l1_if: cosmetic: move struct 'gprs_rlcmac_pdch' into the for loop
 | 
			
		||||
  * pcu_l1_if: cosmetic: correct error message in pcu_rx_info_ind()
 | 
			
		||||
  * gsm_rlcmac: use consistent naming for [Extended] Packet Timing Advance
 | 
			
		||||
  * tbf: cosmetic: use GSM_MACBLOCK_LEN where possible
 | 
			
		||||
  * tbf: allocate the bitvec on stack in create_{dl,ul}_ass()
 | 
			
		||||
  * encoding: constify 'tbf' in UL/DL assignment functions
 | 
			
		||||
  * encoding: do not encode out of range Timing Advance values
 | 
			
		||||
  * encoding: fix RRBP field in write_packet_uplink_assignment()
 | 
			
		||||
  * encoding: use bool for use_egprs in write_packet_uplink_assignment()
 | 
			
		||||
  * encoding: pass pdch slot directly to encoding functions
 | 
			
		||||
  * encoding: clarify docstring for write_packet_downlink_assignment()
 | 
			
		||||
  * encoding: use CSN.1 codec to generate Packet Uplink Assignment
 | 
			
		||||
  * encoding: implement handing of hopping parameters
 | 
			
		||||
  * encoding: fix gen_freq_params(): do not check pdch twice
 | 
			
		||||
  * pcuif_proto: version 10: add frequency hopping parameters
 | 
			
		||||
  * pcu_l1_if: cosmetic: use ARRAY_SIZE() in pcu_rx_info_ind()
 | 
			
		||||
  * pcu_l1_if: correct logging level in pcu_rx_info_ind()
 | 
			
		||||
  * pcu_l1_if: cosmetic: make {local,remote}_sockaddr scoped variables
 | 
			
		||||
  * pcu_l1_if: use proper format string specifiers: %d -> %u
 | 
			
		||||
  * pcu_l1_if: print NSVC address in more common format
 | 
			
		||||
  * gprs_bssgp_pcu: make osmo_sockaddr local/sgsn arguments const
 | 
			
		||||
  * gprs_bssgp_pcu: fix possible memleak in gprs_nsvc_create_and_connect()
 | 
			
		||||
  * struct gprs_rlcmac_bts: remove unused 'nsei' field
 | 
			
		||||
  * gprs_bssgp_pcu: fix: do not crash on receipt of subsequent INFO.ind
 | 
			
		||||
  * doc/manuals: (re-)generate XML VTY reference automatically
 | 
			
		||||
  * fix tbf_select_slot_set(): use LOGP() instead of LOGPC()
 | 
			
		||||
  * main: remove line breaks in print_help(), increase spacing
 | 
			
		||||
  * main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
 | 
			
		||||
  * BSSGP: use tlvp_val8() in gprs_bssgp_pcu_rx_paging_cs()
 | 
			
		||||
  * BSSGP: constify argument 'tp' of gprs_bssgp_pcu_rx_paging_{cs,ps}
 | 
			
		||||
  * TLLI 0x00000000 is a valid TLLI, use 0xffffffff instead
 | 
			
		||||
  * gprs_rlcmac_sched: fix incorrect SBA frame number assignment
 | 
			
		||||
  * bts: fix uninitialized memaccess in BTS::send_gsmtap_rach()
 | 
			
		||||
  * bts: fix uninitialized memaccess in BTS::send_gsmtap()
 | 
			
		||||
  * tests/rlcmac: add more test vectors for Packet Resource Request
 | 
			
		||||
  * contrib/osmo-pcu.spec.in: require libosmo* version 1.4.0
 | 
			
		||||
  * contrib/osmo-pcu.spec.in: add missing libosmoctrl dependency
 | 
			
		||||
  * vty: register libosmocore's FSM introspection commands
 | 
			
		||||
 | 
			
		||||
  [ Anders Broman ]
 | 
			
		||||
  * csn1: Update M_NULL CSN_DESCR to match wireshark
 | 
			
		||||
  * csn1: packet-csn1.c:179: warning: 'pui8' may be used uninitialized in this function
 | 
			
		||||
  * csn1: Fix warning with -Wmissing-prototypes
 | 
			
		||||
  * csn1: Try to fix cast discards '__attribute__((const))' qualifier from pointer target type
 | 
			
		||||
  * gsm_rlcmac.cpp: hanged all M_BIT macros to M_UINT, as M_BIT does not use the referenced hf.
 | 
			
		||||
 | 
			
		||||
  [ Jeff Morriss ]
 | 
			
		||||
  * csn1: shuffle decrements of remaining_bits_len
 | 
			
		||||
 | 
			
		||||
  [ Pascal Quantin ]
 | 
			
		||||
  * csn1: Fix an infinite loop in CSN.1 dissector when having more than 255 padding bits
 | 
			
		||||
  * gsm_rlcmac.h: Remove Uplink messages from the RlcMacDownlink_t structure
 | 
			
		||||
  * gsm_rlcmac: Enhance dissection of PSI1
 | 
			
		||||
  * gsm_rlcmac.cpp: Do not skip too many lines of the CSN_DESCR when the field is missing
 | 
			
		||||
  * gsm_rlcmac.cpp: fix an out of bounds access
 | 
			
		||||
  * gsm_rlcmac.cpp: fix another global-buffer-overflow error reported by ASAN
 | 
			
		||||
  * gsm_rlcmac.cpp: fix global-buffer-overflow error reported by ASAN
 | 
			
		||||
 | 
			
		||||
  [ Guy Harris ]
 | 
			
		||||
  * csn1: Don't cast away constness
 | 
			
		||||
 | 
			
		||||
  [ Alexis La Goutte ]
 | 
			
		||||
  * csn1: fix this statement may fall through [-Werror=implicit-fallthrough=] found by gcc7
 | 
			
		||||
 | 
			
		||||
  [ Bill Meier ]
 | 
			
		||||
  * gsm_rlcmac.h: #if 0 unused stuff
 | 
			
		||||
 | 
			
		||||
  [ Gerald Combs ]
 | 
			
		||||
  * gsm_rlcmac.h: Make sure we have a corresponding 'u' member to RlcMacDownlink_t for every call
 | 
			
		||||
 | 
			
		||||
  [ Vincent Helfre ]
 | 
			
		||||
  * gsm_rlcmac: add dissection of NAS container
 | 
			
		||||
  * gsm_rlcmac: improve dissection of MS RA Capability IE
 | 
			
		||||
 | 
			
		||||
  [ AndersBroman ]
 | 
			
		||||
  * gsm_rlcmac: Update : PACKET RESOURCE REQUEST to Release 14.0.0
 | 
			
		||||
 | 
			
		||||
  [ Keith ]
 | 
			
		||||
  * Send UL-CTRL Packet to GSMTAP even if we fail to decode.
 | 
			
		||||
  * Don't check ul_control_block before decoding into it.
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * csn1.c: Almost all of the logging is DEBUG, not NOTICE
 | 
			
		||||
  * TODO: remove those that have obviously been implemented 5+ years ago
 | 
			
		||||
  * bts.cpp: Increase constructor priority
 | 
			
		||||
  * Use osmo_fd_setup() whenever applicable
 | 
			
		||||
  * Use osmo_fd_*_{disable,enable}
 | 
			
		||||
  * gb manual: 08.16 -> 48.016 / 08.18 -> 48.018
 | 
			
		||||
  * gb manual: NS is implemented in libosmogb, not libosmocore
 | 
			
		||||
  * manuals/gb/ns.adoc: Update documentation regarding SNS capability
 | 
			
		||||
  * migrate to DLBSSGP as log sub-system for BSSGP
 | 
			
		||||
 | 
			
		||||
  [ Eric ]
 | 
			
		||||
  * configure.ac: fix libtool issue  with clang and sanitizer
 | 
			
		||||
  * tbf: add virtual destructor
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * gprs_debug: Use only LOGL_NOTICE as default loglevel
 | 
			
		||||
  * vty: add attributes to VTY commands indicating when they apply
 | 
			
		||||
  * pcu_main: add commandline option --vty-ref-xml
 | 
			
		||||
  * gprs_bssgp_rim: add serving BSS NACC application
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * contrib: import RPM spec
 | 
			
		||||
  * contrib: integrate RPM spec
 | 
			
		||||
  * Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
 | 
			
		||||
  * contrib/jenkins: don't build osmo-gsm-manuals
 | 
			
		||||
  * configure.ac: set -std=gnu11
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * use new osmo_mobile_identity api (avoid deprecation)
 | 
			
		||||
  * paging: pass struct osmo_mobile_identity, not encoded IE bytes
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * pcuif_proto: version 0xa: add support for IPv6 NSVCs
 | 
			
		||||
  * Revert "pcuif_proto: version 0xa: add support for IPv6 NSVCs"
 | 
			
		||||
  * pcuif_proto: version 10: add support for IPv6 NSVCs
 | 
			
		||||
  * Use the new NS2 lib
 | 
			
		||||
  * Rework NS configuration over the info indication
 | 
			
		||||
  * pcu_l1_if: fix misaligned assignment of remote address
 | 
			
		||||
  * NS2: follow the change of ownership
 | 
			
		||||
  * gprs_bssgp_pcu: follow ns2 library changes
 | 
			
		||||
  * NS2: rework handling of unknown primitive
 | 
			
		||||
  * ns2: follow ns2 dialect changes
 | 
			
		||||
  * ns2: follow changes to add a unique name to all binds
 | 
			
		||||
  * ns2: follow ns2 sns api changes
 | 
			
		||||
  * gprs_ns2: set default dialect to ipaccess
 | 
			
		||||
  * gprs_rlcmac_sched: don't leak a sched_dummy()
 | 
			
		||||
  * gprs_rlc_ts_alloc: ensure no rolling slots are allocated
 | 
			
		||||
  * follow gprs_ns2 API enum changes
 | 
			
		||||
  * gprs_ns2: migrate to the new vty syntax
 | 
			
		||||
  * gprs_bssgp: rework and rename ns_create_nsvc -> ns_configure_nse
 | 
			
		||||
  * gprs_bssgp: rename gprs_ns_config -> gprs_ns_update_config
 | 
			
		||||
  * gprs_bssgp: use gprs_ns2_sns_add_bind() to allow the NSE to use the binds for IP-SNS configuration
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 23 Feb 2021 14:41:00 +0100
 | 
			
		||||
 | 
			
		||||
osmo-pcu (0.8.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * tests: test encoding of egprs ul ack/nacks
 | 
			
		||||
  * tbf_dl: add comments to the scheduler
 | 
			
		||||
  * encoding: fix space, tabs
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * osmobts_sock.cpp: pcu_sock_cb(): use libosmocore's socket API
 | 
			
		||||
  * osmobts_sock.cpp: pcu_sock_read(): use stack buffer, not heap
 | 
			
		||||
  * osmobts_sock.cpp: pcu_sock_read(): further simplify the code
 | 
			
		||||
  * osmobts_sock.cpp: do not print the same debug message twice
 | 
			
		||||
  * VTY: refactor pcu_vty_show_ms_all(): use show_ms()
 | 
			
		||||
  * VTY: fix command 'show tbf all': properly filter TBFs
 | 
			
		||||
  * BSSGP: do not reject SUSPEND ACK / NACK messages
 | 
			
		||||
  * BSSGP: properly print BVCI for signalling messages (BVCI=0)
 | 
			
		||||
  * tests/tbf: suspend warnings about the link quality measurements
 | 
			
		||||
  * GprsMs::update_cs_ul(): clarify the meaning of old_link_qual
 | 
			
		||||
  * gprs_bssgp_destroy(): fix memleak and NULL-pointer dereference
 | 
			
		||||
  * PTCCH: implement basic message codec and API
 | 
			
		||||
  * PTCCH: properly handle RTS.req for PCU_IF_SAPI_PTCCH
 | 
			
		||||
  * pcuif_proto.h: extend RACH.ind with TRX / TS numbers
 | 
			
		||||
  * PTCCH: properly handle RACH.ind for PCU_IF_SAPI_PTCCH
 | 
			
		||||
  * VTY: add warning about changing PCU socket path at run-time
 | 
			
		||||
  * VTY: cosmetic: use osmo_talloc_replace_string()
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * cosmetic: tbf: Rename T and N arrays
 | 
			
		||||
  * Use proper API osmo_timer_setup() to set up timer struct
 | 
			
		||||
  * Introduce osmo_tdef infra and timer VTY commands
 | 
			
		||||
  * bts.cpp: Fix osmo_tdef initialization on older g++ compilers
 | 
			
		||||
  * Use osmo_tdef for BSSGP T1 and T2
 | 
			
		||||
  * Use osmo_tdef to implement T3190
 | 
			
		||||
  * tests: TbfTest: Unify stderr and stdout to ease debugging
 | 
			
		||||
  * Use osmo_tdef to implement ms-idle-time
 | 
			
		||||
  * Use osmo_tdef to implement dl-tbf-idle-time
 | 
			
		||||
  * pdch.cpp: Refactor bitvec param passing in rcv_control_block
 | 
			
		||||
  * pdch.cpp: Use pcu_l1_meas previously filled by lower layers
 | 
			
		||||
  * cosmetic: fix whitespace
 | 
			
		||||
  * Move out tbf subclasses from tbf.h to their own headers
 | 
			
		||||
  * Move tbf_{dl,ul} child constructors to respective .cpp files
 | 
			
		||||
  * tbf_dl: Setup m_llc_timer in constructor using osmocom API
 | 
			
		||||
  * tbf_dl.cpp: Remove dup call to tbf_update_ms_class() in state GPRS_RLCMAC_WAIT_RELEASE
 | 
			
		||||
  * vty: Fix osmo_tdef timers not listed in write config
 | 
			
		||||
  * Log RACH Requests using GSMTAP
 | 
			
		||||
  * Log AGCH and PCH blocks using GSMTAP
 | 
			
		||||
  * pcu_l1_if.cpp: Fix GSMTAP Imm Assign PCH wrong encoding
 | 
			
		||||
  * pcu_l1_if.cpp: Drop unneeded byte in Imm Ass PCH buffer
 | 
			
		||||
  * pcu_l1_if.cpp: Imm Assign PCH: clarify size of different items
 | 
			
		||||
  * pcu_l1_if.cpp: Replace value 23 with libosmocore's GSM_MACBLOCK_LEN
 | 
			
		||||
  * Fix assertion hit upon CCCH Paging Request
 | 
			
		||||
  * doc: vty: Update osmo-pcu_vty_reference.xml
 | 
			
		||||
  * Clarify (M)CS related VTY attributes
 | 
			
		||||
  * Remove dash from name used in VTY cmd prompt
 | 
			
		||||
  * tbf_dl.cpp: Fix typo in log line
 | 
			
		||||
  * pcu_l1_if: Check pag_req id_lv len fits buffer
 | 
			
		||||
  * prs_bssgp_pcu.cpp: Mark priv funcs as static and remove trailing whitespace
 | 
			
		||||
  * Fix trailing whitespace
 | 
			
		||||
  * fix typo in log message
 | 
			
		||||
  * Log BVCI PTP value upon msg recv
 | 
			
		||||
  * Split identity_lv param into mi+mi_len
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * doc: update generated VTY reference
 | 
			
		||||
  * tbf_dl: make preemptive retransmission optional
 | 
			
		||||
  * Forward ETWS Primary Notification to MS
 | 
			
		||||
  * tests/app_info: fix compiling with older g++
 | 
			
		||||
  * configure.ac: set C and C++ dialects
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * manual: Fix copy+paste error
 | 
			
		||||
  * manual: Fix documentation missing "-D" command line option
 | 
			
		||||
  * manual: Add missing documentation for '-i' command line option
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 03 Jan 2020 19:40:02 +0100
 | 
			
		||||
 | 
			
		||||
osmo-pcu (0.7.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Rafael Diniz ]
 | 
			
		||||
  * Added support for daemonize to osmo-pcu.
 | 
			
		||||
  * Fix help message formatting of osmo-pcu.
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
  * Don't install pcuif_proto.h header
 | 
			
		||||
  * Move C include to proper place
 | 
			
		||||
  * Add define for dummy burst string
 | 
			
		||||
  * Add encoding tests for Immediate Assignment
 | 
			
		||||
  * Clarify write_immediate_assignment() signature
 | 
			
		||||
  * Restructure IA Rest Octets encoders
 | 
			
		||||
  * Rewrite Packet Downlink Assignment
 | 
			
		||||
  * Rewrite EGPRS Packet Uplink IA Rest Octets for MBA
 | 
			
		||||
  * Rewrite EGPRS Packet Uplink IA Rest Octets for SBA
 | 
			
		||||
  * MCS: internalize 'family' parameter
 | 
			
		||||
  * EDGE tests: reduce code duplication
 | 
			
		||||
  * MCS: remove dead code
 | 
			
		||||
  * EDGE tests: remove no-op check
 | 
			
		||||
  * Use msgb_eq_data_print() in tests
 | 
			
		||||
  * Tighten lqual table limits check
 | 
			
		||||
  * Enable LGLOBAL logging for TBF tests
 | 
			
		||||
  * Log (M)CS UL update errors
 | 
			
		||||
  * MCS: move Coding Scheme enum outside of class definition
 | 
			
		||||
  * Make get_retx_mcs() into regular function
 | 
			
		||||
  * MCS: remove unused function
 | 
			
		||||
  * Debian: bump copyright year
 | 
			
		||||
  * Use unique NSEI/BVCI/NSVCI in TBF tests
 | 
			
		||||
  * MS store: move test helper to unit test
 | 
			
		||||
  * Explicitly clean up BTS singleton
 | 
			
		||||
  * MCS: move HeaderType enum outside of class definition
 | 
			
		||||
  * MCS: use value_string for conversion
 | 
			
		||||
  * TBF-DL: log MCS as string
 | 
			
		||||
  * Fix TA index encoder
 | 
			
		||||
  * MCS: move Mode enum outside of class definition
 | 
			
		||||
  * MCS: add mcs_is_*() helpers
 | 
			
		||||
  * MCS: add Channel Coding Command encoder
 | 
			
		||||
  * Fix Channel Coding Command for MCS
 | 
			
		||||
  * Rewrite Packet Uplink IA Rest Octets for MBA
 | 
			
		||||
  * Rewrite Packet Uplink IA Rest Octets for SBA
 | 
			
		||||
  * Use Timing Advance Index in UL assignments
 | 
			
		||||
  * TBF: update MCS counters
 | 
			
		||||
  * TBF-DL: cosmetic update for helper routines
 | 
			
		||||
  * Update IA Rest Octets encoding
 | 
			
		||||
  * TS alloc: expand tests log
 | 
			
		||||
  * vty: add commands to show TBF of a certain kind
 | 
			
		||||
  * Update MCS selection for retransmission
 | 
			
		||||
  * cosmetic: use const pointer for bts_data
 | 
			
		||||
  * Add test for MS mode and (M)CS settings
 | 
			
		||||
  * Use libosmocore for IMSI parsing
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * Mark gprs_ns_reconnect() as static (not used outside of C file)
 | 
			
		||||
  * Optionally Use the NS Sub-Network-Service (SNS) on Gb
 | 
			
		||||
  * pcu_l1_if: Fix erroneous endian-swapping of the CellID
 | 
			
		||||
  * Forward GPRS SUSPEND REQ from BTS to SGSN using BSSGP
 | 
			
		||||
  * gprs_debug: Use named initializers and explicit array indicies
 | 
			
		||||
  * bssgp: Fix dead code: PDUT_STATUS can never reach this part
 | 
			
		||||
 | 
			
		||||
  [ JF Dionne ]
 | 
			
		||||
  * encoding: Fixes TMSI vs MI bit selection in repeated page info
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * tests: use -no-install libtool flag to avoid ./lt-* scripts
 | 
			
		||||
  * debian: create -doc subpackage with pdf manuals
 | 
			
		||||
  * contrib/jenkins.sh: run "make maintainer-clean"
 | 
			
		||||
 | 
			
		||||
  [ Daniel Willmann ]
 | 
			
		||||
  * Include pdch.h in bts.h even if we're not compiling C++
 | 
			
		||||
  * oc2g: Remove custom alarms
 | 
			
		||||
  * oc2g: Change log type (Litecell15->Oc2g)
 | 
			
		||||
  * jenkins.sh: Add oc2g build support
 | 
			
		||||
  * manuals: Add script to regenerate vty/counter documentation
 | 
			
		||||
  * manuals: Update VTY documentation
 | 
			
		||||
 | 
			
		||||
  [ Jean-Francois Dionne ]
 | 
			
		||||
  * Initial commit for OC-2G support.
 | 
			
		||||
 | 
			
		||||
  [ Minh-Quang Nguyen ]
 | 
			
		||||
  * OC-2G: Fix missing header
 | 
			
		||||
  * OC-2G: Fix TA adjustment
 | 
			
		||||
  * OC-2G: Always use positive TA information provided in PH-RA-IND
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * gprs_bssgp_pcu: make gprs_bssgp_ns_cb public
 | 
			
		||||
  * gprs_bssgp_pcu: explicit allocate & initialize bssgp_nsi instance
 | 
			
		||||
  * encoding: correct encoding of CRBB in ACK/NACK when not byte aligned
 | 
			
		||||
  * encoding: use `/* */` for comments instead of `#if 0 #endif`
 | 
			
		||||
  * egprs_rlc_compression: fix white spaces
 | 
			
		||||
  * tests/BitcompTest: fix wording in log message
 | 
			
		||||
  * rlc: replace int with uint16_t
 | 
			
		||||
  * Encoding: drop struct gprs_rlcmac_bts* from all functions
 | 
			
		||||
  * decompress_crbb: add length argument for search_runlen
 | 
			
		||||
  * Encoding: write_packet_ack_nack_desc_egprs: don't use a reference for rest_bits
 | 
			
		||||
  * bts.cpp: ensure left-shift operation does not exceed uint32_t
 | 
			
		||||
  * Encoding: use uint16_t when interacting with the window object
 | 
			
		||||
  * Encoding: ACK/NACK: always encode with length field present
 | 
			
		||||
 | 
			
		||||
  [ Keith ]
 | 
			
		||||
  * Cosmetic: Osmcoom -> Osmocom
 | 
			
		||||
 | 
			
		||||
  [ Vadim Yanitskiy ]
 | 
			
		||||
  * src/pcu_l1_if.cpp: fix: properly pass measurements from PCUIF
 | 
			
		||||
  * gprs_bssgp_pcu_rx_dl_ud(): fix: BSSGP_IE_IMSI is optional
 | 
			
		||||
  * gprs_bssgp_pcu.cpp: check return code of gsm48_mi_to_string()
 | 
			
		||||
  * gprs_bssgp_pcu_rx_dl_ud(): use OSMO_IMSI_BUF_SIZE
 | 
			
		||||
 | 
			
		||||
  [ Thorsten Alteholz ]
 | 
			
		||||
  * fix spelling errors detected by lintian
 | 
			
		||||
 | 
			
		||||
  [ Eric Wild ]
 | 
			
		||||
  * ubsan: fix shift
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds
 | 
			
		||||
  * Require newer libosmocore to avoid compile failures
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Wed, 07 Aug 2019 21:09:53 +0200
 | 
			
		||||
 | 
			
		||||
osmo-pcu (0.6.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * debian/rules: Don't overwrite .tarball-version
 | 
			
		||||
  * gprs_rlcmac_received_lost(): Fix regression / uninitialized now_tv
 | 
			
		||||
  * initial checkin of manuals to public repo
 | 
			
		||||
  * Add link to Asciidoc source code of manual
 | 
			
		||||
  * Initial place-holder for the new Gb/IP interface documentation
 | 
			
		||||
  * Gb message sequence chart: flip sides (SGSN should be right)
 | 
			
		||||
  * Gb message sequence chart: Add notion of PCU unix domain socket
 | 
			
		||||
  * Gb: Various spelling fixes
 | 
			
		||||
  * gb: Some language improvements, formatting changes
 | 
			
		||||
  * consistently use '3GPP TS' not sometimes 3GPP TS and sometimes TS.
 | 
			
		||||
  * gb/NS: Clarify the language regarding the UDP port numbers / socket
 | 
			
		||||
  * vty-ref: Update URI of docbook 5.0 schema
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * Cleanup of systemd service files
 | 
			
		||||
  * configure.ac: Set CXXFLAGS during --enable-sanitize
 | 
			
		||||
  * Install systemd services with autotools
 | 
			
		||||
  * Move examples/ to doc/examples/
 | 
			
		||||
  * Install osmo-pcu.cfg to docdir/examples
 | 
			
		||||
  * Allow easily disabling GFDL references
 | 
			
		||||
 | 
			
		||||
  [ Stefan Sperling ]
 | 
			
		||||
  * check for overlong unix socket paths
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
  * deb: add missing copyright file
 | 
			
		||||
  * OsmoPCU: fix Gb documentation front page
 | 
			
		||||
  * OsmoPCU: expand NS documentation
 | 
			
		||||
  * OsmoPCU: expand BSSGP documentation
 | 
			
		||||
  * OsmoPCU: add MSC chart
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * Importing history from osmo-gsm-manuals.git
 | 
			
		||||
  * make clean: also remove generated image files
 | 
			
		||||
  * add 'make check' target
 | 
			
		||||
  * fix 'make clean': shell glob, ignore failure
 | 
			
		||||
  * refactor Makefile build rules, don't use the FORCE
 | 
			
		||||
 | 
			
		||||
  [ Jonathan Brielmaier ]
 | 
			
		||||
  * fix various typos across all manuals
 | 
			
		||||
 | 
			
		||||
  [ Philipp ]
 | 
			
		||||
  * configuration: fixing typos
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * OsmoPCU: add rate counter documentation
 | 
			
		||||
 | 
			
		||||
  [ Daniel Willmann ]
 | 
			
		||||
  * Change OpenBSC mentions to OsmoBSC where applicable
 | 
			
		||||
 | 
			
		||||
  [ Oliver Smith ]
 | 
			
		||||
  * build manuals moved here from osmo-gsm-manuals.git
 | 
			
		||||
  * Fix DISTCHECK_CONFIGURE_FLAGS override
 | 
			
		||||
  * contrib/jenkins.sh: build and publish manuals
 | 
			
		||||
  * contrib: fix makedistcheck with disabled systemd
 | 
			
		||||
 | 
			
		||||
 -- Harald Welte <laforge@gnumonks.org>  Mon, 21 Jan 2019 19:03:52 +0100
 | 
			
		||||
 | 
			
		||||
osmo-pcu (0.5.1) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * Don't register SIGHUP handler without actually handling SIGHUP
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * tbf: Fix memset(0) on object with no trivial copy-assignment
 | 
			
		||||
  * rlc: Fix memset(0) on object with no trivial copy-assignment
 | 
			
		||||
  * jenkins.sh: use flag --enable-werror for sysmo and none
 | 
			
		||||
  * tbf: Use incrementing id for rate_ctr_group_alloc
 | 
			
		||||
  * tbf: Replace '.' in counter names with ':'
 | 
			
		||||
 | 
			
		||||
  [ Stefan Sperling ]
 | 
			
		||||
  * fix a one-byte stack buffer overrun in osmo-pcu
 | 
			
		||||
  * read monotonic clock with clock_gettime() instead of gettimeofday()
 | 
			
		||||
  * fix time-delta calculations for measurement reports
 | 
			
		||||
  * change log level of "DL packet loss" log messages
 | 
			
		||||
  * check bssgp_tlv_parse() return code in gprs_bssgp_pcu_rcvmsg()
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 27 Jul 2018 21:56:38 +0200
 | 
			
		||||
 | 
			
		||||
osmo-pcu (0.5.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * jenkins: use osmo-clean-workspace.sh before and after build
 | 
			
		||||
  * vty: skip installing cmds now always installed by default
 | 
			
		||||
  * implement support for 3-digit MNC with leading zeros
 | 
			
		||||
  * configure: add --enable-werror
 | 
			
		||||
  * mslot_class: find_free_tfi(): use uint32_t to shift 1 << 31
 | 
			
		||||
  * mslot_class: two more: use uint32_t to shift 1 << 31
 | 
			
		||||
  * Revert "Use Timing Advance Index in UL assignments"
 | 
			
		||||
  * Revert "Rewrite Packet Uplink Assignment"
 | 
			
		||||
  * Revert "Rewrite Packet Downlink Assignment"
 | 
			
		||||
  * configure: fix --enable-sysmocom-dsp and --with-sysmobts flags
 | 
			
		||||
  * configure: properly quote CFLAGS in lc15 check
 | 
			
		||||
  * Revert "Rewrite EGPRS Packet Uplink Assignment"
 | 
			
		||||
  * use osmo_init_logging2() with proper talloc ctx
 | 
			
		||||
 | 
			
		||||
  [ Minh-Quang Nguyen ]
 | 
			
		||||
  * PCU: Fix TA adjustment
 | 
			
		||||
  * PCU: display TA information in TBF stats
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
  * Remove unused parameter
 | 
			
		||||
  * Move multislot table to separate file
 | 
			
		||||
  * Replace '.' in counter names with ':'
 | 
			
		||||
  * Fix compiler warning
 | 
			
		||||
  * TBF: log timer override
 | 
			
		||||
  * TBF: fix compiler warning in test
 | 
			
		||||
  * TBF: expand timer logging
 | 
			
		||||
  * vty: print class and TBFs for each MS
 | 
			
		||||
  * DL window: constify resend_needed() function
 | 
			
		||||
  * TBF: move EGPRS enablement into (U|D)L-TBF
 | 
			
		||||
  * TBF-DL: fix misleading idle time check
 | 
			
		||||
  * TBF: remove unused variable
 | 
			
		||||
  * Remove unused includes and forward declarations
 | 
			
		||||
  * Fix tests after rate_ctr change
 | 
			
		||||
  * Introduce LOGTBF* for consistent logging
 | 
			
		||||
  * TBF: implement independent T31xx timers
 | 
			
		||||
  * TBF: add N3101 counter
 | 
			
		||||
  * Fix warnings
 | 
			
		||||
  * Add function to get max supported MS class
 | 
			
		||||
  * Add --enable-sanitize configure option
 | 
			
		||||
  * Enable sanitize for CI test
 | 
			
		||||
  * Add tests for pcu_lsb()
 | 
			
		||||
  * Add optional profiling support
 | 
			
		||||
  * TBF: unify timer handling
 | 
			
		||||
  * TBF: log timer invocation source
 | 
			
		||||
  * TBF: bail out for unknown timers
 | 
			
		||||
  * Fix llc_queue_size() type
 | 
			
		||||
  * TBF-DL: mark rcvd_dl_ack() parameters as boolean
 | 
			
		||||
  * window: move encoding into functions
 | 
			
		||||
  * cosmetic: clarify coding scheme and puncturing
 | 
			
		||||
  * Make TBF state private
 | 
			
		||||
  * TBF: cleanup state flag handling
 | 
			
		||||
  * Clarify RACH-related interfaces
 | 
			
		||||
  * TBF-UL: add simpler test helper
 | 
			
		||||
  * Avoid code duplication in TBF test
 | 
			
		||||
  * TBF: move window parameters to UL/DL level
 | 
			
		||||
  * TBF-DL: move priority computation into function
 | 
			
		||||
  * TBF: unify EGPRS window calculation
 | 
			
		||||
  * Don't access TBF internals in vty functions
 | 
			
		||||
  * Fix jenkins.sh to match jenkins job axis filter
 | 
			
		||||
  * Allocate global context for TypesTest
 | 
			
		||||
  * Fix sanitizer build
 | 
			
		||||
  * Rewrite EGPRS Packet Uplink Assignment
 | 
			
		||||
  * Rewrite Packet Downlink Assignment
 | 
			
		||||
  * Rewrite Packet Uplink Assignment
 | 
			
		||||
  * Use Timing Advance Index in UL assignments
 | 
			
		||||
  * Allow specifying sysmocom headers explicitly
 | 
			
		||||
  * TBF: log source of state transitions
 | 
			
		||||
  * jenkins.sh: Disable building doxygen for deps
 | 
			
		||||
  * Set V_N and V_B to known initial state
 | 
			
		||||
  * TBF: add dedicated log categories
 | 
			
		||||
  * TBF: make UL/DL state internal
 | 
			
		||||
  * TBF: make UL ack state internal
 | 
			
		||||
  * TBF: make poll state internal
 | 
			
		||||
  * TBF: adjust test log levels
 | 
			
		||||
  * Add tests for find_multi_slots()
 | 
			
		||||
  * AllocTest: adjust test_alloc_b()
 | 
			
		||||
  * AllocTest: expand test output
 | 
			
		||||
  * AllocTest: remove assumption on max MS class
 | 
			
		||||
  * Add multislot classes from latest spec
 | 
			
		||||
  * cosmetic: fix whitespace issue with include files
 | 
			
		||||
  * TBF: decrease L1 logging verbosity in test
 | 
			
		||||
  * TBF: override send function via linker option
 | 
			
		||||
  * Simplify TS alloc: adjust allocator signatures
 | 
			
		||||
  * Simplify TS alloc: fix allocation calls
 | 
			
		||||
  * Simplify TS alloc: avoid TS reassignment
 | 
			
		||||
  * Simplify TS alloc: use defines for constants
 | 
			
		||||
  * Simplify TS alloc: adjust function signatures
 | 
			
		||||
  * TS alloc: print suggested TRX on allocation errors
 | 
			
		||||
  * Simplify TS alloc: internalize TRX check
 | 
			
		||||
  * TBF: decrease logging verbosity for traffic
 | 
			
		||||
  * TBF: add helpers for assignment type handling
 | 
			
		||||
  * TBF: show assignment kind in vty
 | 
			
		||||
  * vty: drop unused function
 | 
			
		||||
  * RACH: improve single block detection
 | 
			
		||||
  * TBF: move common test code into functions
 | 
			
		||||
  * emu: use libosmocore definitions
 | 
			
		||||
  * Use explicit type for pcu_lsb()
 | 
			
		||||
  * Move paging generation into PDCH
 | 
			
		||||
  * Move include guard to the top
 | 
			
		||||
  * Update header includes
 | 
			
		||||
  * Simplify TS alloc: split off RX mask computation
 | 
			
		||||
  * Simplify TS alloc: separate capacity computation
 | 
			
		||||
  * Simplify TS alloc: split allocation
 | 
			
		||||
  * Simplify TS alloc: split USF/UL allocation
 | 
			
		||||
  * Move PDCH-related functions into separate files
 | 
			
		||||
  * Simplify TS alloc: don't use PDCH for free TFI
 | 
			
		||||
  * Simplify TS alloc: constify max dl slot func
 | 
			
		||||
  * TBF: make network counters internal
 | 
			
		||||
  * Simplify TS alloc: move slot assignment
 | 
			
		||||
  * Simplify TS alloc: move slot check into functions
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * Print error cause of pcu socket connect failure
 | 
			
		||||
  * gprs_bssgp_pcu.cpp: Comment unused function parse_ra_cap
 | 
			
		||||
 | 
			
		||||
  [ Stefan Sperling ]
 | 
			
		||||
  * Make osmo-pcu wait for BTS to become available at start-up time.
 | 
			
		||||
  * improve documentation of Encoding::write_paging_request()
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * pcuif_proto.h: fix whitespaces and indention
 | 
			
		||||
  * pcuif_proto: add version 8 features
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * cosmetic: remove runaway semicolon
 | 
			
		||||
  * pcu_l1_if: add frame number to log output
 | 
			
		||||
  * tbf: add frame number to log output
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Thu, 03 May 2018 16:20:00 +0200
 | 
			
		||||
 | 
			
		||||
osmo-pcu (0.4.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Holger Hans Peter Freyther ]
 | 
			
		||||
  * Initial release.
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
  * Use value string check from osmo-ci
 | 
			
		||||
  * cosmetic: tighten direct-phy related code
 | 
			
		||||
  * Support receiving SI13 from BTS
 | 
			
		||||
  * Move gsmtap and accounting into separate function
 | 
			
		||||
  * cosmetic: convert explicit warnings to fixme/todo
 | 
			
		||||
  * Assert valid CS
 | 
			
		||||
  * TBF-DL: extend index check for RLC block copy
 | 
			
		||||
  * TS alloc: properly count UL slots
 | 
			
		||||
  * cosmetic: reformat multislot classes table
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * gb: allow only packets from a specific SGSN
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * tests: Don't use private version of log_info but global gprs_log_info
 | 
			
		||||
  * Call osmo_init_logging() before static BTS constructor
 | 
			
		||||
  * Forward GPRS SUSPEND REQ from BTS to SGSN using BSSGP
 | 
			
		||||
  * Debian: Cosmetic changes to control file; add better Description
 | 
			
		||||
  * Debian: print test results in case of failure + clean-up autotest
 | 
			
		||||
  * Debian: migrate from DEB_BUILD_HARDENING to DEB_BUILD_MAINT_OPTIONS
 | 
			
		||||
  * Debian: upgrade to debhelper 9 / Standards 3.9.8
 | 
			
		||||
 | 
			
		||||
 -- Harald Welte <laforge@gnumonks.org>  Sun, 29 Oct 2017 12:03:05 +0100
 | 
			
		||||
 | 
			
		||||
osmo-pcu (0.3.0) UNRELEASED; urgency=medium
 | 
			
		||||
 | 
			
		||||
  * Initial release.
 | 
			
		||||
 | 
			
		||||
 -- Holger Hans Peter Freyther <holger@moiji-mobile.com>  Fri, 01 Apr 2016 18:59:00 +0200
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
9
 | 
			
		||||
							
								
								
									
										47
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
Source: osmo-pcu
 | 
			
		||||
Section: net
 | 
			
		||||
Priority: optional
 | 
			
		||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
			
		||||
Build-Depends: debhelper (>= 9),
 | 
			
		||||
               dh-autoreconf,
 | 
			
		||||
               dh-systemd (>= 1.5),
 | 
			
		||||
               autotools-dev,
 | 
			
		||||
               pkg-config,
 | 
			
		||||
               libosmocore-dev (>= 1.5.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.1.0)
 | 
			
		||||
Standards-Version: 3.9.8
 | 
			
		||||
Homepage: http://osmocom.org/projects/osmopcu
 | 
			
		||||
Vcs-Git: git://git.osmocom.org/osmo-pcu
 | 
			
		||||
Vcs-Browser: http://git.osmocom.org/osmo-pcu/
 | 
			
		||||
 | 
			
		||||
Package: osmo-pcu
 | 
			
		||||
Architecture: any
 | 
			
		||||
Depends: ${shlibs:Depends}, ${misc:Depends}
 | 
			
		||||
Description: Osmocom GPRS/EDGE Packet Control Unit (PCU)
 | 
			
		||||
 The GPRS Packet Control Unit is co-located with the GSM BTS or GSM BSC
 | 
			
		||||
 in order to provide packet-switched services for 2G (2.5G, 2.75G)
 | 
			
		||||
 networks.  OsmoPCU is the Osmocom implementation of this network
 | 
			
		||||
 element. It interfaces to osmo-bts via the PCU socket of OsmoBTS
 | 
			
		||||
 and via Gb (NS-over-IP) interface with the SGSN such as OsmoSGSN.
 | 
			
		||||
 | 
			
		||||
Package: osmo-pcu-dbg
 | 
			
		||||
Architecture: any
 | 
			
		||||
Section: debug
 | 
			
		||||
Priority: extra
 | 
			
		||||
Depends: osmo-pcu (= ${binary:Version}),
 | 
			
		||||
         ${misc:Depends}
 | 
			
		||||
Description: Debug symbols for the Osmocom GPRS/EDGE Packet Control Unit (PCU)
 | 
			
		||||
 The GPRS Packet Control Unit is co-located with the GSM BTS or GSM BSC
 | 
			
		||||
 in order to provide packet-switched services for 2G (2.5G, 2.75G)
 | 
			
		||||
 networks.  OsmoPCU is the Osmocom implementation of this network
 | 
			
		||||
 element. It interfaces to osmo-bts via the PCU socket of OsmoBTS
 | 
			
		||||
 and via Gb (NS-over-IP) interface with the SGSN such as OsmoSGSN.
 | 
			
		||||
 | 
			
		||||
Package: osmo-pcu-doc
 | 
			
		||||
Architecture: all
 | 
			
		||||
Section: doc
 | 
			
		||||
Priority: optional
 | 
			
		||||
Depends: ${misc:Depends}
 | 
			
		||||
Description: ${misc:Package} PDF documentation
 | 
			
		||||
 Various manuals: user manual, VTY reference manual and/or
 | 
			
		||||
 protocol/interface manuals.
 | 
			
		||||
							
								
								
									
										131
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 | 
			
		||||
Upstream-Name: osmo-pcu
 | 
			
		||||
Source: git://git.osmocom.org/osmo-pcu
 | 
			
		||||
Files-Excluded: debian
 | 
			
		||||
 | 
			
		||||
Files:     *
 | 
			
		||||
Copyright: 2009-2015 Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
           2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
           2016-2019 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
 | 
			
		||||
           2015 by Yves Godin <support@nuranwireless.com>
 | 
			
		||||
License:   AGPL-3.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/gprs_ms_storage.h
 | 
			
		||||
           src/gprs_ms_storage.cpp
 | 
			
		||||
           src/gprs_ms.h
 | 
			
		||||
           src/gprs_coding_scheme.cpp
 | 
			
		||||
           src/gprs_coding_scheme.h
 | 
			
		||||
           src/coding_scheme.c
 | 
			
		||||
           src/coding_scheme.h
 | 
			
		||||
           src/cxx_linuxlist.h
 | 
			
		||||
           src/pcu_vty_functions.cpp
 | 
			
		||||
           src/pcu_vty_functions.h
 | 
			
		||||
           src/pcu_utils.h
 | 
			
		||||
           src/gprs_codel.c
 | 
			
		||||
           src/gprs_codel.h
 | 
			
		||||
Copyright: 2016-2019 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
 | 
			
		||||
License:   GPL-2.0+
 | 
			
		||||
 | 
			
		||||
Files:     osmoappdesc.py
 | 
			
		||||
Copyright: 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
 | 
			
		||||
License:   GPL-3.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/gprs_debug.cpp
 | 
			
		||||
           src/gprs_debug.h
 | 
			
		||||
           src/pcu_main.cpp
 | 
			
		||||
           src/pcu_l1_if.h
 | 
			
		||||
Copyright: 2012 Ivan Klyuchnikov
 | 
			
		||||
License:   GPL-2.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/tbf.cpp
 | 
			
		||||
           src/tbf_ul.cpp
 | 
			
		||||
           src/tbf_dl.cpp
 | 
			
		||||
           src/sba.cpp
 | 
			
		||||
           src/sba.h
 | 
			
		||||
           src/llc.cpp
 | 
			
		||||
           src/encoding.cpp
 | 
			
		||||
           src/encoding.h
 | 
			
		||||
           src/gprs_rlcmac.cpp
 | 
			
		||||
           src/gprs_rlcmac_ts_alloc.cpp
 | 
			
		||||
Copyright: 2012 Ivan Klyuchnikov
 | 
			
		||||
           2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
           2013 by Holger Hans Peter Freyther
 | 
			
		||||
License:   GPL-2.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/gprs_rlcmac.h
 | 
			
		||||
           src/gprs_bssgp_pcu.cpp
 | 
			
		||||
           src/gprs_bssgp_pcu.h
 | 
			
		||||
           src/bts.h
 | 
			
		||||
Copyright: 2012 Ivan Klyuchnikov
 | 
			
		||||
           2013 by Holger Hans Peter Freyther
 | 
			
		||||
License:   GPL-2.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/rlc.h
 | 
			
		||||
           src/decoding.cpp
 | 
			
		||||
           src/decoding.h
 | 
			
		||||
Copyright: 2012 Ivan Klyuchnikov
 | 
			
		||||
           2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
License:   GPL-2.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/rlc.cpp
 | 
			
		||||
           src/llc.h
 | 
			
		||||
           src/tbf.h
 | 
			
		||||
Copyright: 2013 by Holger Hans Peter Freyther
 | 
			
		||||
License:   GPL-2.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/pcu_l1_if.cpp
 | 
			
		||||
           src/gprs_rlcmac_meas.cpp
 | 
			
		||||
           src/gprs_rlcmac_sched.cpp
 | 
			
		||||
           src/osmobts_sock.cpp
 | 
			
		||||
Copyright: 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
License:   GPL-2.0+
 | 
			
		||||
 | 
			
		||||
Files:     src/csn1.h
 | 
			
		||||
           src/csn1.cpp
 | 
			
		||||
           src/gsm_rlcmac.cpp
 | 
			
		||||
           src/gsm_rlcmac.h
 | 
			
		||||
Copyright: 2011 Vincent Helfre
 | 
			
		||||
           2011 ST-Ericsson (Jari Sassi)
 | 
			
		||||
License:   GPL-2.0+
 | 
			
		||||
 | 
			
		||||
License:   AGPL-3.0+
 | 
			
		||||
 All rights not specifically granted under this license are reserved.
 | 
			
		||||
 .
 | 
			
		||||
 This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 under the terms of the GNU Affero General Public License as published by the
 | 
			
		||||
 Free Software Foundation; either version 3 of the License, or (at your
 | 
			
		||||
 option) any later version.
 | 
			
		||||
 | 
			
		||||
License:   GPL-3.0+
 | 
			
		||||
 This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 it under the terms of the GNU General Public License as published by
 | 
			
		||||
 the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 (at your option) any later version.
 | 
			
		||||
 .
 | 
			
		||||
 This program is distributed in the hope that it will be useful,
 | 
			
		||||
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 GNU General Public License for more details.
 | 
			
		||||
 .
 | 
			
		||||
 You should have received a copy of the GNU General Public License
 | 
			
		||||
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 .
 | 
			
		||||
 On Debian systems, the complete text of the GNU General Public License
 | 
			
		||||
 Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
 | 
			
		||||
 | 
			
		||||
License:   GPL-2.0+
 | 
			
		||||
 This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 it under the terms of the GNU General Public License as published by
 | 
			
		||||
 the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 (at your option) any later version.
 | 
			
		||||
 .
 | 
			
		||||
 This program is distributed in the hope that it will be useful,
 | 
			
		||||
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 GNU General Public License for more details.
 | 
			
		||||
 .
 | 
			
		||||
 You should have received a copy of the GNU General Public License
 | 
			
		||||
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 .
 | 
			
		||||
 On Debian systems, the complete text of the GNU General Public License
 | 
			
		||||
 Version 2 can be found in `/usr/share/common-licenses/GPL-2'.
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/osmo-pcu-doc.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-pcu-doc.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
usr/share/doc/osmo-pcu-doc/*.pdf
 | 
			
		||||
							
								
								
									
										4
									
								
								debian/osmo-pcu.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								debian/osmo-pcu.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
etc/osmocom/osmo-pcu.cfg
 | 
			
		||||
lib/systemd/system/osmo-pcu.service
 | 
			
		||||
usr/bin/osmo-pcu
 | 
			
		||||
usr/share/doc/osmo-pcu/examples/osmo-pcu/osmo-pcu.cfg
 | 
			
		||||
							
								
								
									
										31
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										31
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
#!/usr/bin/make -f
 | 
			
		||||
 | 
			
		||||
DEBIAN  := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2)
 | 
			
		||||
DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
 | 
			
		||||
VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
 | 
			
		||||
 | 
			
		||||
#export DH_VERBOSE=1
 | 
			
		||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
%:
 | 
			
		||||
	dh $@ --with=systemd --with autoreconf --fail-missing
 | 
			
		||||
 | 
			
		||||
override_dh_strip:
 | 
			
		||||
	dh_strip --dbg-package=osmo-pcu-dbg
 | 
			
		||||
 | 
			
		||||
override_dh_clean:
 | 
			
		||||
	dh_clean
 | 
			
		||||
	$(RM) tests/package.m4
 | 
			
		||||
	$(RM) test/testsuite
 | 
			
		||||
 | 
			
		||||
# Print test results in case of a failure
 | 
			
		||||
override_dh_auto_test:
 | 
			
		||||
	dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
 | 
			
		||||
 | 
			
		||||
override_dh_auto_configure:
 | 
			
		||||
	dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
 | 
			
		||||
 | 
			
		||||
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
 | 
			
		||||
override_dh_compress:
 | 
			
		||||
	dh_compress -X.pdf
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
3.0 (native)
 | 
			
		||||
							
								
								
									
										4
									
								
								doc/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								doc/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	examples \
 | 
			
		||||
	manuals \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
							
								
								
									
										7
									
								
								doc/examples/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								doc/examples/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
examples_pcudir = $(docdir)/examples/osmo-pcu
 | 
			
		||||
examples_pcu_DATA = osmo-pcu.cfg
 | 
			
		||||
 | 
			
		||||
osmoconfdir = $(sysconfdir)/osmocom
 | 
			
		||||
osmoconf_DATA = osmo-pcu.cfg
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = osmo-pcu.cfg
 | 
			
		||||
							
								
								
									
										5
									
								
								doc/examples/osmo-pcu.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								doc/examples/osmo-pcu.cfg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
pcu
 | 
			
		||||
 flow-control-interval 10
 | 
			
		||||
 cs 2
 | 
			
		||||
 alloc-algorithm dynamic
 | 
			
		||||
 gamma 0
 | 
			
		||||
							
								
								
									
										28
									
								
								doc/manuals/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								doc/manuals/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
EXTRA_DIST = osmopcu-gb.adoc \
 | 
			
		||||
    osmopcu-gb-docinfo.xml \
 | 
			
		||||
    osmopcu-usermanual.adoc \
 | 
			
		||||
    osmopcu-usermanual-docinfo.xml \
 | 
			
		||||
    osmopcu-vty-reference.xml \
 | 
			
		||||
    regen_doc.sh \
 | 
			
		||||
    chapters \
 | 
			
		||||
    gb \
 | 
			
		||||
    vty
 | 
			
		||||
 | 
			
		||||
if BUILD_MANUALS
 | 
			
		||||
  ASCIIDOC = osmopcu-usermanual.adoc osmopcu-gb.adoc
 | 
			
		||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
 | 
			
		||||
  osmopcu-gb.pdf: $(srcdir)/gb/*.adoc $(srcdir)/gb/*.msc
 | 
			
		||||
  osmopcu-usermanual.pdf: $(srcdir)/chapters/*.adoc
 | 
			
		||||
 | 
			
		||||
  VTY_REFERENCE = osmopcu-vty-reference.xml
 | 
			
		||||
 | 
			
		||||
  BUILT_REFERENCE_XML = $(builddir)/vty/pcu_vty_reference.xml
 | 
			
		||||
  $(builddir)/vty/pcu_vty_reference.xml: $(top_builddir)/src/osmo-pcu
 | 
			
		||||
	mkdir -p $(builddir)/vty
 | 
			
		||||
	$(top_builddir)/src/osmo-pcu --vty-ref-xml > $@
 | 
			
		||||
 | 
			
		||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
 | 
			
		||||
 | 
			
		||||
  OSMO_REPOSITORY = osmo-pcu
 | 
			
		||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
 | 
			
		||||
endif
 | 
			
		||||
							
								
								
									
										400
									
								
								doc/manuals/chapters/configuration.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										400
									
								
								doc/manuals/chapters/configuration.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,400 @@
 | 
			
		||||
== Configuring OsmoPCU
 | 
			
		||||
 | 
			
		||||
Contrary to other network elements (like OsmoBSC, OsmoNITB), the
 | 
			
		||||
OsmoPCU has a relatively simple minimum configuration.
 | 
			
		||||
 | 
			
		||||
This is primarily because most of the PCU configuration happens
 | 
			
		||||
indirectly from the BSC, who passes the configuation over A-bis OML via
 | 
			
		||||
OsmoBTS and its PCU socket into OsmoPCU.
 | 
			
		||||
 | 
			
		||||
A minimal OsmoPCU configuration file is provided below for your reference:
 | 
			
		||||
 | 
			
		||||
.Example: Minimal OsmoPCU configuration file (`osmo-pcu.cfg`)
 | 
			
		||||
----
 | 
			
		||||
pcu
 | 
			
		||||
 flow-control-interval 10 <1>
 | 
			
		||||
 cs 2 <2>
 | 
			
		||||
 alloc-algorithm dynamic <3>
 | 
			
		||||
 gamma 0
 | 
			
		||||
----
 | 
			
		||||
<1> send a BSSGP flow-control PDU every 10 seconds
 | 
			
		||||
<2> start a TBF with the initial coding scheme 2
 | 
			
		||||
<3> dynamically chose between single-slot or multi-slot TBF allocations
 | 
			
		||||
    depending on system load
 | 
			
		||||
 | 
			
		||||
However, there are plenty of tuning parameters for people interested to
 | 
			
		||||
optimize PCU throughput or latency according to their requirements.
 | 
			
		||||
 | 
			
		||||
=== Configuring the Coding Schemes and Rate Adaption
 | 
			
		||||
 | 
			
		||||
As a reminder:
 | 
			
		||||
 | 
			
		||||
- GPRS supports Coding Schemes 1-4 (CS1-4), all of them use GMSK modulation
 | 
			
		||||
  (same as GSM).
 | 
			
		||||
- EGPRS supports MCS1-9, where MCS1-4 is GMSK, and MCS5-9 use 8-PSK modulation.
 | 
			
		||||
 | 
			
		||||
The range of Coding Schemes above only apply to RLCMAC data blocks; RLCMAC
 | 
			
		||||
control blocks are always transmitted with CS1 (GMSK). Hence, CS1 is always
 | 
			
		||||
supported and must be always permitted.
 | 
			
		||||
 | 
			
		||||
The BSC includes a bit-mask of permitted [E]GPRS coding schemes as part of the
 | 
			
		||||
A-bis OML configuration, controlled by VTY `gprs mode (none|gprs|egprs)`.  This
 | 
			
		||||
is passed from the BTS via the PCU socket into OsmoPCU, and the resulting set
 | 
			
		||||
can be further constrained by OsmoPCU VTY configuration.
 | 
			
		||||
 | 
			
		||||
Some additional OsmoPCU parameters can be set as described below.
 | 
			
		||||
 | 
			
		||||
==== Initial Coding Scheme
 | 
			
		||||
 | 
			
		||||
You can use the `cs <1-4> [<1-4>]` command at the `pcu` VTY config node
 | 
			
		||||
to set the initial GPRS coding scheme to be used.  The optional second
 | 
			
		||||
value allows to specify a different initial coding scheme for uplink.
 | 
			
		||||
 | 
			
		||||
Similarly, `mcs <1-9> [<1-9>]` can be used to set up the initial EGPRS coding
 | 
			
		||||
scheme.
 | 
			
		||||
 | 
			
		||||
[[max_cs_mcs]]
 | 
			
		||||
==== Maximum Coding Scheme
 | 
			
		||||
 | 
			
		||||
You can use the `cs max <1-4> [<1-4>]` command at the `pcu` VTY config
 | 
			
		||||
node to set the maximum GPRS coding scheme that should be used as part of the
 | 
			
		||||
rate adaption.  The optional second value allows to specify a different maximum
 | 
			
		||||
coding scheme for uplink.
 | 
			
		||||
 | 
			
		||||
Similarly, `mcs max <1-9> [<1-9>]` can be used to set up the maximum EGPRS
 | 
			
		||||
coding scheme.
 | 
			
		||||
 | 
			
		||||
The actual Maximum Coding Scheme for each direction used at runtime is actually
 | 
			
		||||
the result of taking the maximum value from the permitted GPRS coding schemes
 | 
			
		||||
received by the BSC (or BTS) over PCUIF which is equal or lower tho the
 | 
			
		||||
configured value.
 | 
			
		||||
 | 
			
		||||
Example: PCUIF announces permitted MCS bit-mask (`MCS1 MCS2 MCS3 MCS4`) and
 | 
			
		||||
OsmoPCU is configured `mcs max 6`, then the actual maximum MCS used at runtime
 | 
			
		||||
will be `MCS4`.
 | 
			
		||||
 | 
			
		||||
==== Rate Adaption Error Thresholds
 | 
			
		||||
 | 
			
		||||
You can use the `cs threshold <0-100> <0-100>` command at the `pcu` VTY
 | 
			
		||||
config node to determine the upper and lower limit for the error rate
 | 
			
		||||
percentage to use in the rate adaption.  If the upper threshold is
 | 
			
		||||
reached, a lower coding sheme is chosen, and if the lower threshold is
 | 
			
		||||
reached, a higher coding scheme is chosen.
 | 
			
		||||
 | 
			
		||||
==== Rate Adation Link Quality Thresholds
 | 
			
		||||
 | 
			
		||||
You can use the `cs link-quality-ranges cs1 <0-35> cs2 <0-35> <0-35> cs3
 | 
			
		||||
<0-35> <0-35> cs4 <0-35>` command at the `pcu` VTY config node to tune
 | 
			
		||||
the link quality ranges for the respective coding schemes.
 | 
			
		||||
 | 
			
		||||
==== Data Size based CS downgrade Threshold
 | 
			
		||||
 | 
			
		||||
You can use the `cs downgrade-threshold <1-10000>` command at the `pcu`
 | 
			
		||||
VTY config node to ask the PCU to down-grade the coding scheme if less
 | 
			
		||||
than the specified number of octets are left to be transmitted.
 | 
			
		||||
 | 
			
		||||
=== Miscellaneous Configuration / Tuning Parameters
 | 
			
		||||
 | 
			
		||||
==== Downlink TBF idle time
 | 
			
		||||
 | 
			
		||||
After a down-link TBF is idle (all data in the current LLC downlink
 | 
			
		||||
queue for the MS has been transmitted), we can keep the TBF established
 | 
			
		||||
for a configurable time.  This avoids having to go through a new one or
 | 
			
		||||
two phase TBF establishment once the next data for downlink arrives.
 | 
			
		||||
 | 
			
		||||
You can use the `dl-tbf-idle-time <1-5000>` to specify that time in
 | 
			
		||||
units of milli-seconds.  The default is 2 seconds.
 | 
			
		||||
 | 
			
		||||
==== MS idle time
 | 
			
		||||
 | 
			
		||||
Using the `ms-idle-time <1-7200>` command at the `pcu` VTY config node
 | 
			
		||||
you can configure the number of seconds for which the PCU should keep
 | 
			
		||||
the MS data structure alive before releasing it if there are no active
 | 
			
		||||
TBF for this MS.
 | 
			
		||||
 | 
			
		||||
The OsmoPCU default value is 60 seconds, which is slightly more than
 | 
			
		||||
what 3GPP TS 24.008 recommends for T3314 (44s).
 | 
			
		||||
 | 
			
		||||
The MS data structure only consumes memory in the PCU and does not
 | 
			
		||||
require any resources of the air interface.
 | 
			
		||||
 | 
			
		||||
==== Forcing two-phase access
 | 
			
		||||
 | 
			
		||||
If the MS is using a single-phase access, you can still force it to
 | 
			
		||||
use a two-phase access using the `two-phase-access` VTY configuration
 | 
			
		||||
command at the `pcu` VTY config node.
 | 
			
		||||
 | 
			
		||||
=== Configuring BSSGP flow control
 | 
			
		||||
 | 
			
		||||
BSSGP between SGSN and PCU contains a two-level nested flow control
 | 
			
		||||
mechanism:
 | 
			
		||||
 | 
			
		||||
. one global flow control instance for the overall (downlink) traffic
 | 
			
		||||
  from the SGSN to this PCU
 | 
			
		||||
. a per-MS flow control instance for each individual MS served by this
 | 
			
		||||
  PCU
 | 
			
		||||
 | 
			
		||||
Each of the flow control instance is implemented as a TBF (token bucket
 | 
			
		||||
filter).
 | 
			
		||||
 | 
			
		||||
==== Normal BSSGP Flow Control Tuning parameters
 | 
			
		||||
 | 
			
		||||
You can use the following commands at the `pcu` VTY config node to tune
 | 
			
		||||
the BSSGP flow control parameters:
 | 
			
		||||
 | 
			
		||||
`flow-control-interval <1-10>`::
 | 
			
		||||
	configure the interval (in seconds) between subsequent flow
 | 
			
		||||
	control PDUs from PCU to SGSN
 | 
			
		||||
`flow-control bucket-time <1-65534>`::
 | 
			
		||||
	set the target downlink maximum queueing time in centi-seconds.
 | 
			
		||||
	The PCU will attempt to adjust the advertised bucket size to match this
 | 
			
		||||
	target.
 | 
			
		||||
 | 
			
		||||
==== Extended BSSGP Flow Control Tuning parameters
 | 
			
		||||
 | 
			
		||||
There are some extended flow control related parameters at the `pcu` VTY
 | 
			
		||||
config node that override the automatic flow control as specified in the
 | 
			
		||||
BSSGP specification.  Use them with care!
 | 
			
		||||
 | 
			
		||||
`flow-control force-bvc-bucket-size <1-6553500>`::
 | 
			
		||||
	force the BVC (global) bucket size to the given number of octets
 | 
			
		||||
`flow-control force-bvc-leak-rate <1-6553500>`::
 | 
			
		||||
	force the BVC (global) bucket leak rate to the given number of bits/s
 | 
			
		||||
`flow-control force-ms-bucket-size <1-6553500>`::
 | 
			
		||||
	force the per-MS bucket size to the given number of octets
 | 
			
		||||
`flow-control force-ms-leak-rate <1-6553500>`::
 | 
			
		||||
	force the per-MS bucket leak rate to the given number of bits/s
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Configuring LLC queue
 | 
			
		||||
 | 
			
		||||
The downlink LLC queue in the PCU towards the MS can be tuned with a
 | 
			
		||||
variety of parameters at the `pcu` VTY config node, depending on your
 | 
			
		||||
needs.
 | 
			
		||||
 | 
			
		||||
`queue lifetime <1-65534>`::
 | 
			
		||||
	Each downlink LLC PDU is assigned a lifetime by the SGSN, which
 | 
			
		||||
	is respected by the PDU *unless* you use this command to
 | 
			
		||||
	override the PDU lifetime with a larger value (in centi-seconds)
 | 
			
		||||
`queue lifetime infinite`::
 | 
			
		||||
	Never drop LLC PDUs, i.e. give them an unlimited lifetime.
 | 
			
		||||
`queue hysteresis <1-65535>`::
 | 
			
		||||
	When the downlink LLC queue is full, the PCU starts dropping
 | 
			
		||||
	packets.  Using this parameter, we can set the lifetime
 | 
			
		||||
	hysteresis in centi-seconds, i.e. it will continue discarding
 | 
			
		||||
	until "lifetime - hysteresis" is reached.
 | 
			
		||||
`queue codel`::
 | 
			
		||||
	Use the 'CoDel' (Controlled Delay) scheduling algorithm, which
 | 
			
		||||
	is designed to overcome buffer bloat.  It will use a default
 | 
			
		||||
	interval of 4 seconds.
 | 
			
		||||
`queue codel interval <1-1000>`::
 | 
			
		||||
	Use the 'CoDel' (Controlled Delay) scheduling algorithm, which
 | 
			
		||||
	is designed to overcome buffer bloat.  Use the specified
 | 
			
		||||
	interval in centi-seconds.
 | 
			
		||||
`queue idle-ack-delay <1-65535>`::
 | 
			
		||||
	Delay the request for an ACK after the last downlink LLC frame
 | 
			
		||||
	by the specified amount of centi-seconds.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Configuring MS power control
 | 
			
		||||
 | 
			
		||||
GPRS MS power control works completely different than the close MS power
 | 
			
		||||
control loop in circuit-switched GSM.
 | 
			
		||||
 | 
			
		||||
Rather than instructing the MS constantly about which transmit power to
 | 
			
		||||
use, some parameters are provided to the MS by which the MS-based power
 | 
			
		||||
control algorithm is tuned.
 | 
			
		||||
 | 
			
		||||
See 3GPP TS 05.08 for further information on the algorithm and the
 | 
			
		||||
parameters.
 | 
			
		||||
 | 
			
		||||
You can set those parameters at the `pcu` VTY config node as follows:
 | 
			
		||||
 | 
			
		||||
`gamma <0-62>`::
 | 
			
		||||
	Set the gamma parameter for MS power control in units of dB.
 | 
			
		||||
 | 
			
		||||
Parameter `ALPHA` is set on the BSC VTY configuration file on a per-BTS basis,
 | 
			
		||||
and forwarded by OsmoPCU to the MS through the SI13 received from the former
 | 
			
		||||
over PCUIF. OsmoPCU VTY command `alpha <0-10>` overrides the value received over
 | 
			
		||||
PCUIF to keep backward compatibility with existing config files, but it is
 | 
			
		||||
currently deprecated and its use is discouraged; one should configure it per-BTS
 | 
			
		||||
in OsmoBSC VTY instead.
 | 
			
		||||
 | 
			
		||||
=== Configuring Network Assisted Cell Change (NACC)
 | 
			
		||||
 | 
			
		||||
Network Assisted Cell Change, defined in 3GPP TS 44.060 sub-clause 8.8, is a
 | 
			
		||||
feature providing the MS aid when changing to a new cell due to autonomous
 | 
			
		||||
reselection. In summary, the MS informs the current cell its intention to change
 | 
			
		||||
to a new target cell, and the network decides whether to grant the intended cell
 | 
			
		||||
change or order a change to another neighbor cell. It also provides several
 | 
			
		||||
System Informations of the target cell to the MS to allow for quicker
 | 
			
		||||
reselection towards it.
 | 
			
		||||
 | 
			
		||||
OsmoPCU will automatically provide the required neighbor System Information when
 | 
			
		||||
the MS requests NACC towards a target cell also under the management of the same
 | 
			
		||||
OsmoPCU instance, since it already has the System Information of all BTS under
 | 
			
		||||
their control, obtained through PCUIF when the BTS registers against OsmoPCU, so
 | 
			
		||||
no specific user configuration is required here.
 | 
			
		||||
 | 
			
		||||
In general, OsmoPCU requires to gather the information from somewhere else
 | 
			
		||||
before being able to provide it to the MS requesting the NACC.
 | 
			
		||||
 | 
			
		||||
If OsmoPCU fails to gather the System Information, it will simply answer the MS
 | 
			
		||||
allowing the proposed changed but without previously providing the System
 | 
			
		||||
Information of the target cell.
 | 
			
		||||
 | 
			
		||||
==== Neighbor Address Resolution
 | 
			
		||||
 | 
			
		||||
First of all, it needs to translate the <ARFCN + BSIC> identity of the target
 | 
			
		||||
cell to change to, provided by the MS, into an identity that the Core Network
 | 
			
		||||
can use and understand to identify the target cell, which happens to be a key
 | 
			
		||||
composed of <RAI + Cell Identity>. This key is also named conveniently as
 | 
			
		||||
CGI-PS, since it actually equals to the Circuit Switch CGI + RAC.
 | 
			
		||||
 | 
			
		||||
In order to apply this target cell identity translation, OsmoPCU uses the
 | 
			
		||||
OsmoBSC Neighbor Resolution CTRL interface (see OsmoBSC User Manual), since the
 | 
			
		||||
BSC is the node holding all the neighbor related information.
 | 
			
		||||
By default, the use of this interface is not configured and hence disabled in
 | 
			
		||||
OsmoPCU. As a result, until configured, the network won't be able to provide the
 | 
			
		||||
System Information to the MS prior to allowing the change during NACC against
 | 
			
		||||
remote cells, which means the cell change will take longer to complete. In order
 | 
			
		||||
to configure the interface, the OsmoBSC IP address and port to connect to must
 | 
			
		||||
be configured in OsmoPCU VTY.
 | 
			
		||||
 | 
			
		||||
These neighbor address resolutions (<ARFCN + BSIC> => <RAI + CI>) are by default
 | 
			
		||||
cached for a while in order to avoid querying the BSC frequently and, as a
 | 
			
		||||
result, optimizing the resolution time too.
 | 
			
		||||
 | 
			
		||||
.Example: Configure Neighbor Resolution CTRL interface against OsmoBSC
 | 
			
		||||
----
 | 
			
		||||
pcu
 | 
			
		||||
 neighbor resolution 172.18.13.10 4248 <1>
 | 
			
		||||
 timer X1 500 <2>
 | 
			
		||||
 timer X0 60 <3>
 | 
			
		||||
----
 | 
			
		||||
<1> Port 4248 is the default and hence could be omitted in this case
 | 
			
		||||
<2> Time out if the BSC doesn't answer our CTRL resolution request after 500 ms
 | 
			
		||||
<3> Keep resolved neighbor addresses cached for 60 seconds
 | 
			
		||||
 | 
			
		||||
==== System Information Resolution
 | 
			
		||||
 | 
			
		||||
Once OsmoPCU gains knowledge of the target cell's address in the Core Network,
 | 
			
		||||
it can query its System Information.
 | 
			
		||||
 | 
			
		||||
OsmoPCU will gather the requested System Information of target cells under its
 | 
			
		||||
control without need for any external query, since the System Information of all
 | 
			
		||||
BTSs it manages are received over PCUIF and stored internally in OsmoPCU.
 | 
			
		||||
 | 
			
		||||
For those targets cells not managed by the OsmoPCU instance, the query is
 | 
			
		||||
accomplished by using RIM procedures (NACC RAN-INFO application) over the Gb
 | 
			
		||||
interface against the SGSN that OsmoPCU is connected to. In its turn, the SGSN
 | 
			
		||||
will potentially forward this query to the PCU serving the target cell, which
 | 
			
		||||
will provide back the System Information of that cell.
 | 
			
		||||
 | 
			
		||||
The System Information received from external PCUs over RIM are by default
 | 
			
		||||
cached for a while in order to avoid querying the SGSN frequently and, as a
 | 
			
		||||
result, optimizing the resolution time too.
 | 
			
		||||
 | 
			
		||||
.Example: Configure System Information resolution
 | 
			
		||||
----
 | 
			
		||||
pcu
 | 
			
		||||
 timer X2 500 <1>
 | 
			
		||||
 timer X11 60 <2>
 | 
			
		||||
----
 | 
			
		||||
<1> Time out if the SGSN doesn't answer our RIM RAN-INFO request request after 500 ms
 | 
			
		||||
<2> Keep resolved remote neighbor System Information cached for 60 seconds
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== GPRS vs EGPRS considerations
 | 
			
		||||
 | 
			
		||||
==== Configuration
 | 
			
		||||
 | 
			
		||||
OsmoPCU can be configured to either:
 | 
			
		||||
 | 
			
		||||
- Allocate only GPRS TBFs to all MS (no EGPRS)
 | 
			
		||||
- Allocate EGPRS TBFs to EGPRS capable phones while still falling back to
 | 
			
		||||
  allocating GPRS TBFs on GPRS-only capable MS.
 | 
			
		||||
 | 
			
		||||
These two different modes of operation are selected by properly configuring the
 | 
			
		||||
Coding Schemes (see <<max_cs_mcs>>).
 | 
			
		||||
 | 
			
		||||
The first mode of operation (GPRS-only for all MS) can be accomplished
 | 
			
		||||
configuring OsmoPCU so that the resulting MCS set is empty. This can be done in
 | 
			
		||||
two ways:
 | 
			
		||||
 | 
			
		||||
- Announcing an empty MCS bit-mask over PCUIF to OsmoPCU:
 | 
			
		||||
  That's actually done automatically by OsmoBSC on BTS with VTY config set to
 | 
			
		||||
  `gprs mode gprs`.
 | 
			
		||||
- Configuring OsmoPCU to force an empty set by using VTY command `mcs max 0`.
 | 
			
		||||
 | 
			
		||||
Hence, if the resulting MCS bit-mask is not empty, (BSC configuring the BTS with
 | 
			
		||||
`gprs mode egprs` and OsmoPCU VTY containing something other than 'mcs max 0'),
 | 
			
		||||
EGPRS TBFs will be allocated for all MS announcing EGPRS capabilities.
 | 
			
		||||
 | 
			
		||||
It is important to remark that in order to use MCS5-9, the BTS must support
 | 
			
		||||
8-PSK modulation. Nevertheless, in case 8-PSK is not supported by the BTS, one
 | 
			
		||||
can still enable EGPRS and simply make sure 8-PSK MCS are never used by
 | 
			
		||||
configuring OsmoPCU with `mcs max 4 4`.
 | 
			
		||||
 | 
			
		||||
Similarly, a BTS may support 8-PSK modulation only on downlink, since it is
 | 
			
		||||
easier to implement than the uplink, together with the fact that higher downlink
 | 
			
		||||
throughput is usually more interesting from user point of view. In this
 | 
			
		||||
scenario, OsmoPCU can be configured to allow for full MCS range in downlink
 | 
			
		||||
while still preventing use of 8-PSK on the uplink: `mcs max 9 4`.
 | 
			
		||||
 | 
			
		||||
Some other interesting configurations to control use of EGPRS in the network
 | 
			
		||||
which lay outside OsmoPCU include:
 | 
			
		||||
 | 
			
		||||
- If `osmo-bts-trx` together with `osmo-trx` is used, remember to enable EGPRS
 | 
			
		||||
  support (OsmoTRX VTY `egprs enable`).
 | 
			
		||||
 | 
			
		||||
- It is possible to improve EGPRS performance (in particular, the TBF
 | 
			
		||||
  establishment timing) a bit by enabling 11-bit Access Burst support. This
 | 
			
		||||
  allows EGPRS capable phones to indicate their EGPRS capability, establishment
 | 
			
		||||
  cause, and multi-slot class directly in the Access Burst (OsmoTRX VTY
 | 
			
		||||
  `ext-rach enable`, OsmoBSC VTY `gprs egprs-packet-channel-request`).
 | 
			
		||||
 | 
			
		||||
NOTE: If you enable MCS5-9 you will also need an 8-PSK capable OsmoBTS+PHY,
 | 
			
		||||
which means `osmo-bts-sysmo` or `osmo-bts-litecell15` with their associated PHY,
 | 
			
		||||
or `osmo-bts-trx` with `osmo-trx` properly configured.
 | 
			
		||||
 | 
			
		||||
==== GPRS+EGPRS multiplexing
 | 
			
		||||
 | 
			
		||||
Both EGPRS and GPRS-only capable MS can be driven concurrently in the same PDCH
 | 
			
		||||
timeslot by the PCU, hence no special configuration is required per timeslot
 | 
			
		||||
regarding this topic; OsmoPCU scheduler takes care of the specific requirements
 | 
			
		||||
when driving MS with different capabilities.
 | 
			
		||||
 | 
			
		||||
These specific requirements translate to some restrictions regarding which
 | 
			
		||||
Coding Schemes can be used at given frame numbers, and hence which kind of
 | 
			
		||||
RLCMAC blocks can be sent, which means serving a GPRS-only MS in a PDCH may end
 | 
			
		||||
up affecting slightly the downlink throughput of EGPRS capable MS.
 | 
			
		||||
 | 
			
		||||
Throughput loss based on MS capabilities with TBF attached to a certain PDCH
 | 
			
		||||
timeslot:
 | 
			
		||||
 | 
			
		||||
All UEs are EGPRS capable::
 | 
			
		||||
 No throughput loss, since all data is sent using EGPRS, and EGPRS control
 | 
			
		||||
 messages are used when appropriate.
 | 
			
		||||
 | 
			
		||||
All UEs are GPRS-only (doesn't support EGPRS)::
 | 
			
		||||
 No throughput loss, since all data and control blocks use GPRS.
 | 
			
		||||
 | 
			
		||||
Some UEs are GPRS-only, some EGPRS::
 | 
			
		||||
In general EGPRS capable UEs will use EGPRS, and GPRS-only UEs will use GPRS,
 | 
			
		||||
with some restrictions affecting throughput of EGPRS capable UEs:
 | 
			
		||||
- Whenever a GPRS-only MS is to be polled to send uplink data to PCU, then a
 | 
			
		||||
downlink RLCMAC block modulated using GMSK must be sent, which means that if the
 | 
			
		||||
scheduler selects a EGPRS MS for downlink on that block it will force sending of
 | 
			
		||||
data with MCS1-4 (if it's new data, if it's a retransmission it cannot be
 | 
			
		||||
selected since MCS from original message cannot be changed). In the worst case
 | 
			
		||||
if no control block needs to be sent or no new data in MCS1-4 is available to
 | 
			
		||||
send, then an RLCMAC Dummy Block is sent.
 | 
			
		||||
- For synchronization purposes, each MS needs to receive an RLCMAC block which
 | 
			
		||||
it can fully decode at least every 360ms, which means the scheduler must enforce
 | 
			
		||||
a downlink block in CS1-4 every 360ms, that is, every 18th RLCMAC block. In
 | 
			
		||||
general this is not a big issue since anyway all control RLCMAC blocks are
 | 
			
		||||
encoded in CS1, so in case any control block is sent from time to time it's
 | 
			
		||||
accomplished and there's no penalty. However, if only EGPRS downlink data is sent
 | 
			
		||||
over that time frame, then the scheduler will force sending a RLCMAC Dummy
 | 
			
		||||
Block.
 | 
			
		||||
							
								
								
									
										4
									
								
								doc/manuals/chapters/counters.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								doc/manuals/chapters/counters.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
[[counters]]
 | 
			
		||||
== Counters
 | 
			
		||||
 | 
			
		||||
include::./counters_generated.adoc[]
 | 
			
		||||
							
								
								
									
										178
									
								
								doc/manuals/chapters/counters_generated.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								doc/manuals/chapters/counters_generated.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
			
		||||
// autogenerated by show asciidoc counters
 | 
			
		||||
These counters and their description based on Osmo-PCU 0.4.0.4-8d55 (Osmo-PCU).
 | 
			
		||||
 | 
			
		||||
// generating tables for rate_ctr_group
 | 
			
		||||
// rate_ctr_group table BSSGP Peer Statistics
 | 
			
		||||
.bssgp:bss_ctx - BSSGP Peer Statistics 
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description
 | 
			
		||||
| packets:in | <<bssgp:bss_ctx_packets:in>> | Packets at BSSGP Level ( In)
 | 
			
		||||
| packets:out | <<bssgp:bss_ctx_packets:out>> | Packets at BSSGP Level (Out)
 | 
			
		||||
| bytes:in | <<bssgp:bss_ctx_bytes:in>> | Bytes at BSSGP Level   ( In)
 | 
			
		||||
| bytes:out | <<bssgp:bss_ctx_bytes:out>> | Bytes at BSSGP Level   (Out)
 | 
			
		||||
| blocked | <<bssgp:bss_ctx_blocked>> | BVC Blocking count
 | 
			
		||||
| discarded | <<bssgp:bss_ctx_discarded>> | BVC LLC Discarded count
 | 
			
		||||
| status | <<bssgp:bss_ctx_status>> | BVC Status count
 | 
			
		||||
|===
 | 
			
		||||
// rate_ctr_group table NSVC Peer Statistics
 | 
			
		||||
.ns:nsvc - NSVC Peer Statistics 
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description
 | 
			
		||||
| packets:in | <<ns:nsvc_packets:in>> | Packets at NS Level  ( In)
 | 
			
		||||
| packets:out | <<ns:nsvc_packets:out>> | Packets at NS Level  (Out)
 | 
			
		||||
| bytes:in | <<ns:nsvc_bytes:in>> | Bytes at NS Level    ( In)
 | 
			
		||||
| bytes:out | <<ns:nsvc_bytes:out>> | Bytes at NS Level    (Out)
 | 
			
		||||
| blocked | <<ns:nsvc_blocked>> | NS-VC Block count         
 | 
			
		||||
| dead | <<ns:nsvc_dead>> | NS-VC gone dead count     
 | 
			
		||||
| replaced | <<ns:nsvc_replaced>> | NS-VC replaced other count
 | 
			
		||||
| nsei-chg | <<ns:nsvc_nsei-chg>> | NS-VC changed NSEI count  
 | 
			
		||||
| inv-nsvci | <<ns:nsvc_inv-nsvci>> | NS-VCI was invalid count  
 | 
			
		||||
| inv-nsei | <<ns:nsvc_inv-nsei>> | NSEI was invalid count    
 | 
			
		||||
| lost:alive | <<ns:nsvc_lost:alive>> | ALIVE ACK missing count   
 | 
			
		||||
| lost:reset | <<ns:nsvc_lost:reset>> | RESET ACK missing count   
 | 
			
		||||
|===
 | 
			
		||||
// rate_ctr_group table NSVC Peer Statistics
 | 
			
		||||
.ns:nsvc - NSVC Peer Statistics 
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description
 | 
			
		||||
| packets:in | <<ns:nsvc_packets:in>> | Packets at NS Level  ( In)
 | 
			
		||||
| packets:out | <<ns:nsvc_packets:out>> | Packets at NS Level  (Out)
 | 
			
		||||
| bytes:in | <<ns:nsvc_bytes:in>> | Bytes at NS Level    ( In)
 | 
			
		||||
| bytes:out | <<ns:nsvc_bytes:out>> | Bytes at NS Level    (Out)
 | 
			
		||||
| blocked | <<ns:nsvc_blocked>> | NS-VC Block count         
 | 
			
		||||
| dead | <<ns:nsvc_dead>> | NS-VC gone dead count     
 | 
			
		||||
| replaced | <<ns:nsvc_replaced>> | NS-VC replaced other count
 | 
			
		||||
| nsei-chg | <<ns:nsvc_nsei-chg>> | NS-VC changed NSEI count  
 | 
			
		||||
| inv-nsvci | <<ns:nsvc_inv-nsvci>> | NS-VCI was invalid count  
 | 
			
		||||
| inv-nsei | <<ns:nsvc_inv-nsei>> | NSEI was invalid count    
 | 
			
		||||
| lost:alive | <<ns:nsvc_lost:alive>> | ALIVE ACK missing count   
 | 
			
		||||
| lost:reset | <<ns:nsvc_lost:reset>> | RESET ACK missing count   
 | 
			
		||||
|===
 | 
			
		||||
// rate_ctr_group table BTS Statistics
 | 
			
		||||
.bts - BTS Statistics 
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description
 | 
			
		||||
| tbf:dl:alloc | <<bts_tbf:dl:alloc>> | TBF DL Allocated     
 | 
			
		||||
| tbf:dl:freed | <<bts_tbf:dl:freed>> | TBF DL Freed         
 | 
			
		||||
| tbf:dl:aborted | <<bts_tbf:dl:aborted>> | TBF DL Aborted       
 | 
			
		||||
| tbf:ul:alloc | <<bts_tbf:ul:alloc>> | TBF UL Allocated     
 | 
			
		||||
| tbf:ul:freed | <<bts_tbf:ul:freed>> | TBF UL Freed         
 | 
			
		||||
| tbf:ul:aborted | <<bts_tbf:ul:aborted>> | TBF UL Aborted       
 | 
			
		||||
| tbf:reused | <<bts_tbf:reused>> | TBF Reused           
 | 
			
		||||
| tbf:alloc:algo-a | <<bts_tbf:alloc:algo-a>> | TBF Alloc Algo A     
 | 
			
		||||
| tbf:alloc:algo-b | <<bts_tbf:alloc:algo-b>> | TBF Alloc Algo B     
 | 
			
		||||
| tbf:failed:egprs-only | <<bts_tbf:failed:egprs-only>> | TBF Failed EGPRS-only
 | 
			
		||||
| rlc:sent | <<bts_rlc:sent>> | RLC Sent             
 | 
			
		||||
| rlc:resent | <<bts_rlc:resent>> | RLC Resent           
 | 
			
		||||
| rlc:restarted | <<bts_rlc:restarted>> | RLC Restarted        
 | 
			
		||||
| rlc:stalled | <<bts_rlc:stalled>> | RLC Stalled          
 | 
			
		||||
| rlc:nacked | <<bts_rlc:nacked>> | RLC Nacked           
 | 
			
		||||
| rlc:final_block_resent | <<bts_rlc:final_block_resent>> | RLC Final Blk resent 
 | 
			
		||||
| rlc:ass:timedout | <<bts_rlc:ass:timedout>> | RLC Assign Timeout   
 | 
			
		||||
| rlc:ass:failed | <<bts_rlc:ass:failed>> | RLC Assign Failed    
 | 
			
		||||
| rlc:ack:timedout | <<bts_rlc:ack:timedout>> | RLC Ack Timeout      
 | 
			
		||||
| rlc:ack:failed | <<bts_rlc:ack:failed>> | RLC Ack Failed       
 | 
			
		||||
| rlc:rel:timedout | <<bts_rlc:rel:timedout>> | RLC Release Timeout  
 | 
			
		||||
| rlc:late-block | <<bts_rlc:late-block>> | RLC Late Block       
 | 
			
		||||
| rlc:sent-dummy | <<bts_rlc:sent-dummy>> | RLC Sent Dummy       
 | 
			
		||||
| rlc:sent-control | <<bts_rlc:sent-control>> | RLC Sent Control     
 | 
			
		||||
| rlc:dl_bytes | <<bts_rlc:dl_bytes>> | RLC DL Bytes         
 | 
			
		||||
| rlc:dl_payload_bytes | <<bts_rlc:dl_payload_bytes>> | RLC DL Payload Bytes 
 | 
			
		||||
| rlc:ul_bytes | <<bts_rlc:ul_bytes>> | RLC UL Bytes         
 | 
			
		||||
| rlc:ul_payload_bytes | <<bts_rlc:ul_payload_bytes>> | RLC UL Payload Bytes 
 | 
			
		||||
| decode:errors | <<bts_decode:errors>> | Decode Errors        
 | 
			
		||||
| sba:allocated | <<bts_sba:allocated>> | SBA Allocated        
 | 
			
		||||
| sba:freed | <<bts_sba:freed>> | SBA Freed            
 | 
			
		||||
| sba:timedout | <<bts_sba:timedout>> | SBA Timeout          
 | 
			
		||||
| llc:timeout | <<bts_llc:timeout>> | Timedout Frames      
 | 
			
		||||
| llc:dropped | <<bts_llc:dropped>> | Dropped Frames       
 | 
			
		||||
| llc:scheduled | <<bts_llc:scheduled>> | Scheduled Frames     
 | 
			
		||||
| llc:dl_bytes | <<bts_llc:dl_bytes>> | RLC encapsulated PDUs
 | 
			
		||||
| llc:ul_bytes | <<bts_llc:ul_bytes>> | full PDUs received   
 | 
			
		||||
| rach:requests | <<bts_rach:requests>> | RACH requests        
 | 
			
		||||
| 11bit_rach:requests | <<bts_11bit_rach:requests>> | 11BIT_RACH requests  
 | 
			
		||||
| spb:uplink_first_segment | <<bts_spb:uplink_first_segment>> | First seg of UL SPB  
 | 
			
		||||
| spb:uplink_second_segment | <<bts_spb:uplink_second_segment>> | Second seg of UL SPB 
 | 
			
		||||
| spb:downlink_first_segment | <<bts_spb:downlink_first_segment>> | First seg of DL SPB  
 | 
			
		||||
| spb:downlink_second_segment | <<bts_spb:downlink_second_segment>> | Second seg of DL SPB 
 | 
			
		||||
| immediate:assignment_UL | <<bts_immediate:assignment_UL>> | Immediate Assign UL  
 | 
			
		||||
| immediate:assignment_rej | <<bts_immediate:assignment_rej>> | Immediate Assign Rej 
 | 
			
		||||
| immediate:assignment_DL | <<bts_immediate:assignment_DL>> | Immediate Assign DL  
 | 
			
		||||
| channel:request_description | <<bts_channel:request_description>> | Channel Request Desc 
 | 
			
		||||
| pkt:ul_assignment | <<bts_pkt:ul_assignment>> | Packet UL Assignment 
 | 
			
		||||
| pkt:access_reject | <<bts_pkt:access_reject>> | Packet Access Reject 
 | 
			
		||||
| pkt:dl_assignment | <<bts_pkt:dl_assignment>> | Packet DL Assignment 
 | 
			
		||||
| ul:control | <<bts_ul:control>> | UL control Block     
 | 
			
		||||
| ul:assignment_poll_timeout | <<bts_ul:assignment_poll_timeout>> | UL Assign Timeout    
 | 
			
		||||
| ul:assignment_failed | <<bts_ul:assignment_failed>> | UL Assign Failed     
 | 
			
		||||
| dl:assignment_timeout | <<bts_dl:assignment_timeout>> | DL Assign Timeout    
 | 
			
		||||
| dl:assignment_failed | <<bts_dl:assignment_failed>> | DL Assign Failed     
 | 
			
		||||
| pkt:ul_ack_nack_timeout | <<bts_pkt:ul_ack_nack_timeout>> | PUAN Poll Timeout    
 | 
			
		||||
| pkt:ul_ack_nack_failed | <<bts_pkt:ul_ack_nack_failed>> | PUAN poll Failed     
 | 
			
		||||
| pkt:dl_ack_nack_timeout | <<bts_pkt:dl_ack_nack_timeout>> | PDAN poll Timeout    
 | 
			
		||||
| pkt:dl_ack_nack_failed | <<bts_pkt:dl_ack_nack_failed>> | PDAN poll Failed     
 | 
			
		||||
| gprs:downlink_cs1 | <<bts_gprs:downlink_cs1>> | CS1 downlink         
 | 
			
		||||
| gprs:downlink_cs2 | <<bts_gprs:downlink_cs2>> | CS2 downlink         
 | 
			
		||||
| gprs:downlink_cs3 | <<bts_gprs:downlink_cs3>> | CS3 downlink         
 | 
			
		||||
| gprs:downlink_cs4 | <<bts_gprs:downlink_cs4>> | CS4 downlink         
 | 
			
		||||
| egprs:downlink_mcs1 | <<bts_egprs:downlink_mcs1>> | MCS1 downlink        
 | 
			
		||||
| egprs:downlink_mcs2 | <<bts_egprs:downlink_mcs2>> | MCS2 downlink        
 | 
			
		||||
| egprs:downlink_mcs3 | <<bts_egprs:downlink_mcs3>> | MCS3 downlink        
 | 
			
		||||
| egprs:downlink_mcs4 | <<bts_egprs:downlink_mcs4>> | MCS4 downlink        
 | 
			
		||||
| egprs:downlink_mcs5 | <<bts_egprs:downlink_mcs5>> | MCS5 downlink        
 | 
			
		||||
| egprs:downlink_mcs6 | <<bts_egprs:downlink_mcs6>> | MCS6 downlink        
 | 
			
		||||
| egprs:downlink_mcs7 | <<bts_egprs:downlink_mcs7>> | MCS7 downlink        
 | 
			
		||||
| egprs:downlink_mcs8 | <<bts_egprs:downlink_mcs8>> | MCS8 downlink        
 | 
			
		||||
| egprs:downlink_mcs9 | <<bts_egprs:downlink_mcs9>> | MCS9 downlink        
 | 
			
		||||
| gprs:uplink_cs1 | <<bts_gprs:uplink_cs1>> | CS1 Uplink           
 | 
			
		||||
| gprs:uplink_cs2 | <<bts_gprs:uplink_cs2>> | CS2 Uplink           
 | 
			
		||||
| gprs:uplink_cs3 | <<bts_gprs:uplink_cs3>> | CS3 Uplink           
 | 
			
		||||
| gprs:uplink_cs4 | <<bts_gprs:uplink_cs4>> | CS4 Uplink           
 | 
			
		||||
| egprs:uplink_mcs1 | <<bts_egprs:uplink_mcs1>> | MCS1 Uplink          
 | 
			
		||||
| egprs:uplink_mcs2 | <<bts_egprs:uplink_mcs2>> | MCS2 Uplink          
 | 
			
		||||
| egprs:uplink_mcs3 | <<bts_egprs:uplink_mcs3>> | MCS3 Uplink          
 | 
			
		||||
| egprs:uplink_mcs4 | <<bts_egprs:uplink_mcs4>> | MCS4 Uplink          
 | 
			
		||||
| egprs:uplink_mcs5 | <<bts_egprs:uplink_mcs5>> | MCS5 Uplink          
 | 
			
		||||
| egprs:uplink_mcs6 | <<bts_egprs:uplink_mcs6>> | MCS6 Uplink          
 | 
			
		||||
| egprs:uplink_mcs7 | <<bts_egprs:uplink_mcs7>> | MCS7 Uplink          
 | 
			
		||||
| egprs:uplink_mcs8 | <<bts_egprs:uplink_mcs8>> | MCS8 Uplink          
 | 
			
		||||
| egprs:uplink_mcs9 | <<bts_egprs:uplink_mcs9>> | MCS9 Uplink          
 | 
			
		||||
|===
 | 
			
		||||
// generating tables for osmo_stat_items
 | 
			
		||||
NSVC Peer Statistics
 | 
			
		||||
// osmo_stat_item_group table NSVC Peer Statistics
 | 
			
		||||
.ns.nsvc - NSVC Peer Statistics 
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description | Unit
 | 
			
		||||
| alive.delay | <<ns.nsvc_alive.delay>> | ALIVE response time         | ms
 | 
			
		||||
|===
 | 
			
		||||
NSVC Peer Statistics
 | 
			
		||||
// osmo_stat_item_group table NSVC Peer Statistics
 | 
			
		||||
.ns.nsvc - NSVC Peer Statistics 
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description | Unit
 | 
			
		||||
| alive.delay | <<ns.nsvc_alive.delay>> | ALIVE response time         | ms
 | 
			
		||||
|===
 | 
			
		||||
BTS Statistics
 | 
			
		||||
// osmo_stat_item_group table BTS Statistics
 | 
			
		||||
.bts - BTS Statistics 
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description | Unit
 | 
			
		||||
| ms.present | <<bts_ms.present>> | MS Present            | 
 | 
			
		||||
|===
 | 
			
		||||
// generating tables for osmo_counters
 | 
			
		||||
// ungrouped osmo_counters
 | 
			
		||||
.ungrouped osmo counters
 | 
			
		||||
[options="header"]
 | 
			
		||||
|===
 | 
			
		||||
| Name | Reference | Description
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										68
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
== Overview
 | 
			
		||||
 | 
			
		||||
=== About OsmoPCU
 | 
			
		||||
 | 
			
		||||
OsmoPCU is the Osmocom implementation of the GPRS PCU (Packet Control
 | 
			
		||||
Unit) element inside the GPRS network.
 | 
			
		||||
 | 
			
		||||
The OsmoPCU is co-located within the BTS and connects to OsmoBTS via its
 | 
			
		||||
PCU socket interface.
 | 
			
		||||
 | 
			
		||||
On the other side, OsmoPCU is connected via the Gb interface to the
 | 
			
		||||
SGSN.
 | 
			
		||||
 | 
			
		||||
[[fig-gprs-pcubts]]
 | 
			
		||||
.GPRS network architecture with PCU in BTS
 | 
			
		||||
[graphviz]
 | 
			
		||||
----
 | 
			
		||||
digraph G {
 | 
			
		||||
	rankdir=LR;
 | 
			
		||||
	MS0 [label="MS"]
 | 
			
		||||
	MS1 [label="MS"]
 | 
			
		||||
	MS0->BTS [label="Um"]
 | 
			
		||||
	MS1->BTS [label="Um"]
 | 
			
		||||
	BTS->BSC [label="Abis"]
 | 
			
		||||
	BSC->MSC [label="A"]
 | 
			
		||||
	BTS->PCU [label="pcu_sock"]
 | 
			
		||||
	PCU->SGSN [label="Gb"]
 | 
			
		||||
	SGSN->GGSN [label="GTP"]
 | 
			
		||||
	PCU [color=red]
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
=== Software Components
 | 
			
		||||
 | 
			
		||||
OsmoPCU consists of a variety of components, including
 | 
			
		||||
 | 
			
		||||
* Gb interface (NS/BSSGP protocol)
 | 
			
		||||
* `pcu_sock` interface towards OsmoBTS
 | 
			
		||||
* TBF management for uplink and downlink TBF
 | 
			
		||||
* RLC/MAC protocol implementation
 | 
			
		||||
* per-MS context for each MS currently served
 | 
			
		||||
* CSN.1 encoding/decoding routines
 | 
			
		||||
 | 
			
		||||
==== Gb Implementation
 | 
			
		||||
 | 
			
		||||
OsmoPCU implements the ETSI/3GPP specified Gb interface, including TS
 | 
			
		||||
08.16 (NS), TS 08.18 (BSSGP) protocols. As transport layer for NS, it
 | 
			
		||||
supports NS/IP (NS encapsulated in UDP/IP).
 | 
			
		||||
 | 
			
		||||
The actual Gb Implementation is part of the libosmogb library, which is
 | 
			
		||||
in turn part of the libosmocore software package.  This allows the same
 | 
			
		||||
Gb implementation to be used from OsmoPCU, OsmoGbProxy as well as
 | 
			
		||||
OsmoSGSN.
 | 
			
		||||
 | 
			
		||||
==== `pcu_sock` Interface to OsmoBTS
 | 
			
		||||
 | 
			
		||||
The interface towards OsmoBTS is called 'pcu_sock' and implemented as a
 | 
			
		||||
set of non-standardized primitives over a unix domain socket.  The
 | 
			
		||||
default file system path for this socket is `/tmp/pcu_bts`.
 | 
			
		||||
 | 
			
		||||
The PCU socket can be changed on both OsmoBTS and OsmoPCU to a different
 | 
			
		||||
file/path name, primarily to permit running multiple independent BTS+PCU
 | 
			
		||||
pairs on a single Linux machine without having to use filesystem
 | 
			
		||||
namespaces or other complex configurations.
 | 
			
		||||
 | 
			
		||||
NOTE: If you change the PCU socket path on OsmoBTS by means of the
 | 
			
		||||
`pcu-socket` VTY configuration command, you must ensure to make the
 | 
			
		||||
identical change on the OsmoPCU side.
 | 
			
		||||
							
								
								
									
										44
									
								
								doc/manuals/chapters/qos-example.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								doc/manuals/chapters/qos-example.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
==== Full example of QoS for osmo-pcu uplink QoS
 | 
			
		||||
 | 
			
		||||
In the below example we will show the full set of configuration required
 | 
			
		||||
for both DSCP and PCP differentiation of uplink Gb traffic by osmo-pcu.
 | 
			
		||||
 | 
			
		||||
What we want to achieve in this example is the following configuration:
 | 
			
		||||
 | 
			
		||||
.DSCP and PCP assignments for osmo-bts uplink traffic in this example
 | 
			
		||||
[options="header",width="30%",cols="2,1,1"]
 | 
			
		||||
|===
 | 
			
		||||
|Traffic      |DSCP|PCP
 | 
			
		||||
|Gb (NS)      |  10|  1
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
. configure the osmocom program to set the DSCP value
 | 
			
		||||
 * osmo-pcu.cfg: `dscp 10` in `udp bind` vty node
 | 
			
		||||
. configure an egrees QoS map to map from priority to PCP
 | 
			
		||||
 | 
			
		||||
.Example Step 1: add related VTY configuration to `osmo-pcu.cfg`
 | 
			
		||||
----
 | 
			
		||||
...
 | 
			
		||||
pcu
 | 
			
		||||
 gb ip-dscp 10
 | 
			
		||||
 gb socket-priority 1
 | 
			
		||||
 ...
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
.Example Step 2: egress QoS map to map from DSCP values to priority values
 | 
			
		||||
----
 | 
			
		||||
$ sudo ip link set dev eth0.9<1> type vlan egress-qos-map 0:0 1:1 5:5 6:6 7:7 <2>
 | 
			
		||||
----
 | 
			
		||||
<1> make sure to specify your specific VLAN interface name here instead of `eth0.9`.
 | 
			
		||||
<2> create a egress QoS map that maps the priority value 1:1 to the PCP. We also
 | 
			
		||||
    include the mappings for 5, 6, and 7 from the osmo-bts example here (see
 | 
			
		||||
    <<userman-osmobts>>).
 | 
			
		||||
 | 
			
		||||
NOTE:: The settings of the `ip` command are volatile and only active until
 | 
			
		||||
the next reboot (or the network device or VLAN is removed).  Please refer to
 | 
			
		||||
the documentation of your specific Linux distribution in order to find out how
 | 
			
		||||
to make such settings persistent by means of an `ifup` hook whenever the interface
 | 
			
		||||
comes up.  For CentOS/RHEL 8 this can e.g. be achieved by means of an `/sbin/ifup-local
 | 
			
		||||
script` (when using `network-scripts` and not NetworkManager).  For Debian or Ubuntu,
 | 
			
		||||
this typically involves adding `up` lines to `/etc/network/interfaces` or a `/etc/network/if-up.d`
 | 
			
		||||
script.
 | 
			
		||||
							
								
								
									
										35
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
== Running OsmoPCU
 | 
			
		||||
 | 
			
		||||
The OsmoPCU executable (`osmo-pcu`) offers the following command-line
 | 
			
		||||
options:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== SYNOPSIS
 | 
			
		||||
 | 
			
		||||
*osmo-pcu* [-h|-V] [-D] [-c 'CONFIGFILE'] [-r 'PRIO'] [-m 'MCC'] [-n 'MNC'] [-i A.B.C.D]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== OPTIONS
 | 
			
		||||
 | 
			
		||||
*-h, --help*::
 | 
			
		||||
	Print a short help message about the supported options
 | 
			
		||||
*-V, --version*::
 | 
			
		||||
	Print the compile-time version number of the program
 | 
			
		||||
*-D, --daemonize*::
 | 
			
		||||
	Fork the process as a daemon into background.
 | 
			
		||||
*-c, --config-file 'CONFIGFILE'*::
 | 
			
		||||
	Specify the file and path name of the configuration file to be
 | 
			
		||||
	used. If none is specified, use `osmo-pcu.cfg` in the current
 | 
			
		||||
	working directory.
 | 
			
		||||
*-r, --realtime 'PRIO'*::
 | 
			
		||||
	Enable use of the Linux kernel realtime priority scheduler with
 | 
			
		||||
	the specified priority.
 | 
			
		||||
	It is recommended you use this option on low-performance
 | 
			
		||||
	embedded systems or systems that encounter high non-GSM/GPRS
 | 
			
		||||
	load.
 | 
			
		||||
*-m, --mcc 'MCC'*::
 | 
			
		||||
	Use the given MCC instead of that provided by BTS via PCU socket
 | 
			
		||||
*-n, --mnc 'MNC'*::
 | 
			
		||||
	Use the given MNC instead of that provided by BTS via PCU socket
 | 
			
		||||
*-i, --gsmtap-ip 'A.B.C.D'*::
 | 
			
		||||
        Send Um interface trace via GSMTAP to specified IP address
 | 
			
		||||
							
								
								
									
										501
									
								
								doc/manuals/gb/bssgp.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										501
									
								
								doc/manuals/gb/bssgp.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,501 @@
 | 
			
		||||
[[bssgp]]
 | 
			
		||||
== BSS GPRS Protocol (BSSGP)
 | 
			
		||||
 | 
			
		||||
=== List of Messages
 | 
			
		||||
 | 
			
		||||
The following tables list the BSSGP messages used by OsmoPCU, grouped
 | 
			
		||||
by their level of compliance with 3GPP TS 48.018.
 | 
			
		||||
 | 
			
		||||
==== Messages Compliant With TS 48.018
 | 
			
		||||
 | 
			
		||||
.Messages compliant with TS 48.018
 | 
			
		||||
[options="header",cols="10%,10%,20%,35%,5%,20%"]
 | 
			
		||||
|===
 | 
			
		||||
| TS 48.018 § | type code (hex) | This document § | Message | <-/-> | Received/Sent by OsmoPCU
 | 
			
		||||
6+<| *RL and BSSGP SAP Messages:*
 | 
			
		||||
| 10.2.1 | 0x00 | <<dl_unit_data>> | DL-UNITDATA | <- | Received
 | 
			
		||||
| 10.2.2 | 0x01 | <<ul_unit_data>> | UL-UNITDATA | -> | Sent
 | 
			
		||||
| 10.2.3 | 0x02 | <<ra_capab>> | RA-CAPABILITY | <- | Received
 | 
			
		||||
6+<| *GMM SAP Messages:*
 | 
			
		||||
| 10.3.1 | 0x06 | <<paging_ps>> | PAGING PS | <- | Received
 | 
			
		||||
| 10.3.2 | 0x07 | <<paging_cs>> | PAGING CS | <- | Received
 | 
			
		||||
| 10.3.7 | 0x0c | <<susp_ack>> | SUSPEND-ACK | <- | Received
 | 
			
		||||
| 10.3.8 | 0x0d | <<susp_nack>> | SUSPEND-NACK | <- | Received
 | 
			
		||||
| 10.3.10 | 0x0f | <<res_ack>> | RESUME-ACK | <- | Received
 | 
			
		||||
| 10.3.11 | 0x10 | <<res_nack>> | RESUME-NACK | <- | Received
 | 
			
		||||
6+<| *NM SAP Messages:*
 | 
			
		||||
| 10.4.9 | 0x21 | <<block_ack>> | BVC-BLOCK-ACK | <- | Received
 | 
			
		||||
| 10.4.12 | 0x22 | <<bvc_reset>> | BVC-RESET | <-/-> | Received/Sent
 | 
			
		||||
| 10.4.13 | 0x23 | <<reset_ack>> | BVC-RESET-ACK | <- | Received
 | 
			
		||||
| 10.4.10 | 0x24 | <<bvc_unblock>> | BVC-UNBLOCK | -> | Sent
 | 
			
		||||
| 10.4.11 | 0x25 | <<unblock_ack>> | BVC-UNBLOCK-ACK | <- | Received
 | 
			
		||||
| 10.4.4 | 0x26 | <<flow_bvc>> | FLOW-CONTROL-BVC | -> | Sent
 | 
			
		||||
| 10.4.5 | 0x27 | <<flow_bvc_ack>> | FLOW-CONTROL-BVC-ACK | <- | Received
 | 
			
		||||
| 10.4.7 | 0x29 | <<flow_ms_ack>> | FLOW-CONTROL-MS-ACK | <- | Received
 | 
			
		||||
| 10.4.1 | 0x2a | <<flush_ll>> | FLUSH-LL | <- | Received
 | 
			
		||||
| 10.4.15 | 0x40 | <<invoke_trace>> | SGSN-INVOKE-TRACE | <- | Received
 | 
			
		||||
| 10.4.14 | 0x41 | <<bssgp_status>> | STATUS | <-/->  | Received/Sent
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
==== Messages Specific to OsmoPCU
 | 
			
		||||
 | 
			
		||||
There are no OsmoPCU specific BSSGP messages.
 | 
			
		||||
 | 
			
		||||
[[not_impl]]
 | 
			
		||||
==== Messages Not Implemented by OsmoPCU
 | 
			
		||||
 | 
			
		||||
.3GPP TS 48.018 messages not implemented by OsmoPCU
 | 
			
		||||
[options="header",cols="10%,10%,80%"]
 | 
			
		||||
|===
 | 
			
		||||
| TS 48.018 § | type code (hex) | Message
 | 
			
		||||
3+<| *RL (relay) and BSSGP SAP Messages:*
 | 
			
		||||
| 10.2.4 | 0x03 | PTM-UNITDATA
 | 
			
		||||
3+<| *GMM (GPRS mobility management) SAP Messages:*
 | 
			
		||||
| 10.3.3 | 0x08 | RA-CAPABILITY-UPDATE
 | 
			
		||||
| 10.3.4 | 0x09 | RA-CAPABILITY-UPDATE-ACK
 | 
			
		||||
| 10.3.5 | 0x0a | RADIO-STATUS
 | 
			
		||||
| 10.3.6 | 0x0b | SUSPEND
 | 
			
		||||
| 10.3.9 | 0x0e | RESUME
 | 
			
		||||
3+<| *NM (network management) SAP Messages:*
 | 
			
		||||
| 10.4.8 | 0x20 | BVC-BLOCK
 | 
			
		||||
| 10.4.6 | 0x28 | FLOW-CONTROL-MS
 | 
			
		||||
| 10.4.2 | 0x2b | FLUSH-LL-ACK
 | 
			
		||||
| 10.4.3 | 0x2c | LLC-DISCARDED
 | 
			
		||||
3+<| *PFM (packet flow management) SAP Messages:*
 | 
			
		||||
| 10.4.16 | 0x50 | DOWNLOAD-BSS-PFC
 | 
			
		||||
| 10.4.17 | 0x51 | CREATE-BSS-PFC
 | 
			
		||||
| 10.4.18 | 0x52 | CREATE-BSS-PFC-ACK
 | 
			
		||||
| 10.4.19 | 0x53 | CREATE-BSS-PFC-NACK
 | 
			
		||||
| 10.4.20 | 0x54 | MODIFY-BSS-PFC
 | 
			
		||||
| 10.4.21 | 0x55 | MODIFY-BSS-PFC-ACK
 | 
			
		||||
| 10.4.22 | 0x56 | DELETE-BSS-PFC
 | 
			
		||||
| 10.4.23 | 0x57 | DELETE-BSS-PFC-ACK
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Details on Compliant BSSGP Messages
 | 
			
		||||
 | 
			
		||||
[[dl_unit_data]]
 | 
			
		||||
==== DL-UNITDATA
 | 
			
		||||
 | 
			
		||||
This message conforms to 3GPP TS 48.018 § 10.2.1, with the following
 | 
			
		||||
limitations:
 | 
			
		||||
 | 
			
		||||
* OsmoPCU does not support QoS
 | 
			
		||||
* all optional IEs except for IMSI and old TLLI are ignored.
 | 
			
		||||
 | 
			
		||||
._DL-UNITDATA_ IE limitations
 | 
			
		||||
[options="header",cols="10%,30%,60%"]
 | 
			
		||||
|===
 | 
			
		||||
| TS 48.018 § | IE Name | Handling
 | 
			
		||||
| 11.3.28 | QoS Profile | _ignored_
 | 
			
		||||
| 11.3.22 | MS Radio Access Capability | _ignored_
 | 
			
		||||
| 11.3.27 | Priority | _ignored_
 | 
			
		||||
| 11.3.11 | DRX Parameters | _ignored_
 | 
			
		||||
| 1.3.42 | PFI | _ignored_
 | 
			
		||||
| 11.3.19 | LSA Information | _ignored_
 | 
			
		||||
| 11.3.47 | Service UTRAN CCO | _ignored_
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
[[ul_unit_data]]
 | 
			
		||||
==== UL-UNITDATA
 | 
			
		||||
 | 
			
		||||
This message conforms to 3GPP TS 48.018 § 10.2.2, with the following limitations:
 | 
			
		||||
 | 
			
		||||
* OsmoPCU does not send optional IEs - PFI (§ 12.3.42) and LSA
 | 
			
		||||
  Identifier List (§ 11.3.18).
 | 
			
		||||
* QoS Profile (§ 11.3.28) IE is always set to 0x04.
 | 
			
		||||
 | 
			
		||||
[[ra_capab]]
 | 
			
		||||
==== RA-CAPABILITY
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the moment.
 | 
			
		||||
 | 
			
		||||
[[paging_ps]]
 | 
			
		||||
==== PAGING PS
 | 
			
		||||
 | 
			
		||||
This message conforms to 3GPP TS 48.018 § 10.3.1, with the following
 | 
			
		||||
limitations:
 | 
			
		||||
 | 
			
		||||
* only IMSI and P-TMSI are parsed by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
._DL-UNITDATA_ IE limitations
 | 
			
		||||
[options="header",cols="10%,30%,60%"]
 | 
			
		||||
|===
 | 
			
		||||
| TS 48.018 § | IE Name | Handling
 | 
			
		||||
| 11.3.11 | DRX Parameters | _ignored_
 | 
			
		||||
| 11.3.6 | BVCI | _ignored_
 | 
			
		||||
| 11.3.17 | Location Are | _ignored_
 | 
			
		||||
| 11.3.31 | Routeing Area | _ignored_
 | 
			
		||||
| 11.3.3 | BSS Area Indication | _ignored_
 | 
			
		||||
| 11.3.42 | PFI | _ignored_
 | 
			
		||||
| 11.3.43 | ABQP | _ignored_
 | 
			
		||||
| 11.3.28 | QoS Profile | _ignored_
 | 
			
		||||
| 11.3.36 | P-TMSI | treated as mandatory (in case of absence paging with 0-length P-TMSI will be sent)
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[[paging_cs]]
 | 
			
		||||
==== PAGING CS
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the
 | 
			
		||||
moment.
 | 
			
		||||
 | 
			
		||||
[[susp_ack]]
 | 
			
		||||
==== SUSPEND-ACK
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the
 | 
			
		||||
moment.
 | 
			
		||||
 | 
			
		||||
[[susp_nack]]
 | 
			
		||||
==== SUSPEND-NACK
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the
 | 
			
		||||
moment.
 | 
			
		||||
 | 
			
		||||
[[res_ack]]
 | 
			
		||||
==== RESUME-ACK
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the
 | 
			
		||||
moment.
 | 
			
		||||
 | 
			
		||||
[[res_nack]]
 | 
			
		||||
==== RESUME-NACK
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the
 | 
			
		||||
moment.
 | 
			
		||||
 | 
			
		||||
[[block_ack]]
 | 
			
		||||
==== BVC-BLOCK-ACK
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the
 | 
			
		||||
moment.
 | 
			
		||||
 | 
			
		||||
[[bvc_reset]]
 | 
			
		||||
==== BVC-RESET
 | 
			
		||||
 | 
			
		||||
OsmoPCU never transmits optional Feature bitmap (3GPP TS 48.018 §
 | 
			
		||||
11.3.40) IE.
 | 
			
		||||
 | 
			
		||||
Receiving BVC RESET will cause OsmoPCU to respond with "Unknown BVCI"
 | 
			
		||||
status message.
 | 
			
		||||
 | 
			
		||||
[[reset_ack]]
 | 
			
		||||
==== BVC-RESET-ACK
 | 
			
		||||
 | 
			
		||||
This message conforms to 3GPP TS 48.018 § 10.4.13.
 | 
			
		||||
 | 
			
		||||
After receiving it OsmoPCU completes the RESET procedure for BVC
 | 
			
		||||
according to 3GPP TS 48.018 § 8.4.
 | 
			
		||||
 | 
			
		||||
[[unblock_ack]]
 | 
			
		||||
==== BVC-UNBLOCK-ACK
 | 
			
		||||
 | 
			
		||||
This message conforms to 3GPP TS 48.018 § 10.4.11.
 | 
			
		||||
 | 
			
		||||
After receiving it OsmoPCU completes the RESET procedure for BVC
 | 
			
		||||
according to 3GPP TS 48.018 § 8.3.
 | 
			
		||||
 | 
			
		||||
[[bvc_unblock]]
 | 
			
		||||
==== BVC-UNBLOCK
 | 
			
		||||
 | 
			
		||||
This message conforms to 3GPP TS 48.018 § 10.4.10 and is send by
 | 
			
		||||
OsmoPCU as part of UNBLOCK procedure described in 3GPP TS 48.018 § 8.3.
 | 
			
		||||
 | 
			
		||||
[[flow_ms_ack]]
 | 
			
		||||
==== FLOW-CONTROL-MS-ACK
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the
 | 
			
		||||
moment.
 | 
			
		||||
 | 
			
		||||
[[flow_bvc_ack]]
 | 
			
		||||
==== FLOW-CONTROL-BVC-ACK
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the
 | 
			
		||||
moment.
 | 
			
		||||
 | 
			
		||||
[[flow_bvc]]
 | 
			
		||||
==== FLOW-CONTROL-BVC
 | 
			
		||||
 | 
			
		||||
This message conforms to 3GPP TS 48.018 § 10.4.4, with the following
 | 
			
		||||
limitations:
 | 
			
		||||
 | 
			
		||||
* OsmoPCU does not support Current Bucket Level (CBL) feature so
 | 
			
		||||
  Bucket_Full Ratio (TS 48.018 § 11.3.46) IE is not transmitted as part
 | 
			
		||||
  of this message.
 | 
			
		||||
 | 
			
		||||
[[flush_ll]]
 | 
			
		||||
==== FLUSH-LL
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the
 | 
			
		||||
moment.
 | 
			
		||||
 | 
			
		||||
[[invoke_trace]]
 | 
			
		||||
==== SGSN-INVOKE-TRACE
 | 
			
		||||
 | 
			
		||||
This message is received and logged but ignored by OsmoPCU at the
 | 
			
		||||
moment.
 | 
			
		||||
 | 
			
		||||
[[bssgp_status]]
 | 
			
		||||
==== STATUS
 | 
			
		||||
 | 
			
		||||
This message conforms to 3GPP TS 48.018 § 10.4.14.
 | 
			
		||||
 | 
			
		||||
=== Information Elements Overview
 | 
			
		||||
 | 
			
		||||
All of the IEs handled by OsmoPCU are listed below, with limitations
 | 
			
		||||
and additions to 3GPP TS 48.018 specified in more detail.
 | 
			
		||||
 | 
			
		||||
==== IEs Conforming to 3GPP TS 48.018
 | 
			
		||||
 | 
			
		||||
The following Information Elements are accepted by OsmoPCU. Not all
 | 
			
		||||
IEs are actually evaluated.
 | 
			
		||||
 | 
			
		||||
.IEs conforming to 3GPP TS 48.018
 | 
			
		||||
[options="header",cols="5%,10%,40%,5%,40%"]
 | 
			
		||||
|===
 | 
			
		||||
| tag (hex) | TS 48.018 § | IE name | <-/-> | Received/Sent by OsmoPCU
 | 
			
		||||
| 0x00 | 11.3.1 | Alignment Octets | <-/-> | Received/Sent
 | 
			
		||||
| 0x01 | 11.3.2 | Bmax default MS | -> | Sent
 | 
			
		||||
| 0x02 | 11.3.3 | BSS Area Indication | <- | Received
 | 
			
		||||
| 0x03 | 11.3.4 | Bucket Leak Rate | -> | Sent
 | 
			
		||||
| 0x04 | 11.3.6 | BVCI | <-/-> | Received/Sent
 | 
			
		||||
| 0x05 | 11.3.5 | BVC Bucket Size | -> | Sent
 | 
			
		||||
| 0x06 | 11.3.7 | BVC Measurement | -> | Sent
 | 
			
		||||
| 0x07 | 11.3.8 | Cause | <-/-> | Received/Sent
 | 
			
		||||
| 0x08 | 11.3.9 | Cell Identifier | -> | Sent
 | 
			
		||||
| 0x09 | 11.3.10 | Channel needed | <- | Received
 | 
			
		||||
| 0x0a | 11.3.11 | DRX Parameters | <- | Received
 | 
			
		||||
| 0x0b | 11.3.12 | eMLPP-Priority | <- | Received
 | 
			
		||||
| 0x0c | 11.3.13 | Flush Action | <- | Received
 | 
			
		||||
| 0x0d | 11.3.14 | IMSI | <-/-> | Received/Sent
 | 
			
		||||
| 0x0e | 11.3.15 | LLC-PDU | <-/-> | Received/Sent
 | 
			
		||||
| 0x0f | 11.3.16 | LLC Frames Discarded | -> | Sent
 | 
			
		||||
| 0x10 | 11.3.17 | Location Area | <- | Received
 | 
			
		||||
| 0x11 | 11.3.20 | Mobile Id | <- | Received
 | 
			
		||||
| 0x12 | 11.3.21 | MS Bucket Size | -> | Sent
 | 
			
		||||
| 0x13 | 11.3.22 | MS Radio Access Capability | <- | Received
 | 
			
		||||
| 0x14 | 11.3.23 | OMC Id | <- | Received
 | 
			
		||||
| 0x15 | 11.3.24 | PDU In Error | <-/-> | Received/Sent
 | 
			
		||||
| 0x16 | 11.3.25 | PDU Lifetime | <- | Received
 | 
			
		||||
| 0x17 | 11.3.27 | Priority | <- | Received
 | 
			
		||||
| 0x19 | 11.3.29 | Radio Cause | -> | Sent
 | 
			
		||||
| 0x1a | 11.3.30 | RA-Cap-UPD-Cause | -> | Sent
 | 
			
		||||
| 0x1b | 11.3.31 | Routeing Area | <-/-> | Received/Sent
 | 
			
		||||
| 0x1c | 11.3.32 | R_default_MS | -> | Sent
 | 
			
		||||
| 0x1d | 11.3.33 | Suspend Reference Number | <-/-> | Received/Sent
 | 
			
		||||
| 0x1e | 11.3.34 | Tag | <-/-> | Received/Sent
 | 
			
		||||
| 0x1f | 11.3.35 | TLLI | <-/-> | Received/Sent
 | 
			
		||||
| 0x20 | 11.3.36 | TMSI | <-/-> | Received/Sent
 | 
			
		||||
| 0x21 | 11.3.37 | Trace Reference | <- | Received
 | 
			
		||||
| 0x22 | 11.3.38 | Trace Type | <- | Received
 | 
			
		||||
| 0x23 | 11.3.39 | TransactionId | <- | Received
 | 
			
		||||
| 0x24 | 11.3.40 | Trigger Id | <- | Received
 | 
			
		||||
| 0x25 | 11.3.41 | Number of octets affected | -> | Sent
 | 
			
		||||
| 0x26 | 11.3.18 | LSA Identifier List | -> | Sent
 | 
			
		||||
| 0x27 | 11.3.19 | LSA Information | <- | Received
 | 
			
		||||
| 0x28 | 11.3.42 | Packet Flow Identifier | <-/-> | Received/Sent
 | 
			
		||||
| 0x3a | 11.3.43 | Aggregate BSS QoS Profile (ABQP) | <-/-> | Received/Sent
 | 
			
		||||
| 0x3b | 11.3.45 | Feature Bitmap | <-/-> | Received/Sent
 | 
			
		||||
| 0x3c | 11.3.46 | Bucket_Full Ratio | -> | Sent
 | 
			
		||||
| 0x3d | 11.3.47 | Service UTRAN CCO (Cell Change Order) | <- | Received
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
==== IEs Not Conforming to 3GPP TS 48.018
 | 
			
		||||
 | 
			
		||||
.IEs not conforming to 3GPP TS 48.018
 | 
			
		||||
[options="header",cols="5%,10%,30%,55%"]
 | 
			
		||||
|===
 | 
			
		||||
| tag (hex) | TS 48.018 § | IE name | Description
 | 
			
		||||
| 0x18 | 11.3.28 | QoS Profile | Received value is ignored. Sent value is hard-coded to 0x4 (3 octets).
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
==== Additional Attributes and Parameters
 | 
			
		||||
 | 
			
		||||
There are no OsmoPCU specific additional Attributes and Parameters.
 | 
			
		||||
 | 
			
		||||
=== Details on IEs
 | 
			
		||||
 | 
			
		||||
==== BSS Area Indication
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
==== Bucket Leak Rate
 | 
			
		||||
 | 
			
		||||
The value used by OsmoPCU for this IE can be set through configuration
 | 
			
		||||
file or vty via "flow-control force-ms-leak-rate <1-6553500>" command.
 | 
			
		||||
 | 
			
		||||
==== BVC Bucket Size
 | 
			
		||||
 | 
			
		||||
The value used by OsmoPCU for this IE can be set through configuration file or vty via
 | 
			
		||||
"flow-control force-bvc-bucket-size <1-6553500>" command.
 | 
			
		||||
 | 
			
		||||
==== Channel needed
 | 
			
		||||
 | 
			
		||||
This IE is ignored because entire message which contains it is ignored
 | 
			
		||||
by OsmoPCU - see <<paging_cs>> for details.
 | 
			
		||||
 | 
			
		||||
==== DRX Parameters
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
==== eMLPP-Priority
 | 
			
		||||
 | 
			
		||||
This IE is ignored because entire message which contains it is ignored
 | 
			
		||||
by OsmoPCU - see <<paging_cs>> for details.
 | 
			
		||||
 | 
			
		||||
==== Flush Action
 | 
			
		||||
 | 
			
		||||
This IE is ignored because entire message which contains it is ignored
 | 
			
		||||
by OsmoPCU - see <<flush_ll>> for details.
 | 
			
		||||
 | 
			
		||||
==== LLC Frames Discarded
 | 
			
		||||
 | 
			
		||||
This IE is not available because entire message which contains it
 | 
			
		||||
(LLC-DISCARDED) is not implemented by OsmoPCU - see for <<not_impl>>
 | 
			
		||||
details.
 | 
			
		||||
 | 
			
		||||
==== Location Area
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
==== Mobile Id
 | 
			
		||||
 | 
			
		||||
This IE is ignored because entire message which contains it is ignored
 | 
			
		||||
by OsmoPCU - see <<invoke_trace>> for details.
 | 
			
		||||
 | 
			
		||||
==== MS Bucket Size
 | 
			
		||||
 | 
			
		||||
The value used by OsmoPCU for this IE can be set through configuration
 | 
			
		||||
file or vty via "flow-control force-ms-bucket-size <1-6553500>"
 | 
			
		||||
command.
 | 
			
		||||
 | 
			
		||||
==== MS Radio Access Capability
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
==== OMC Id
 | 
			
		||||
 | 
			
		||||
This IE is ignored because entire message which contains it is ignored
 | 
			
		||||
by OsmoPCU - see <<invoke_trace>> for details.
 | 
			
		||||
 | 
			
		||||
==== Priority
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
==== QoS Profile
 | 
			
		||||
 | 
			
		||||
No QoS is supported by OsmoPCU so this IE is ignored or safe default
 | 
			
		||||
used when mandatory.
 | 
			
		||||
 | 
			
		||||
==== Radio Cause
 | 
			
		||||
 | 
			
		||||
This IE is not available because entire message which contains it
 | 
			
		||||
(RADIO-STATUS) is not implemented by OsmoPCU - see for <<not_impl>>
 | 
			
		||||
details.
 | 
			
		||||
 | 
			
		||||
==== RA-Cap-UPD-Cause
 | 
			
		||||
 | 
			
		||||
This IE is not available because entire message which contains it
 | 
			
		||||
(RA-CAPABILITY-UPDATE-ACK) is not implemented by OsmoPCU - see for
 | 
			
		||||
<<not_impl>> details.
 | 
			
		||||
 | 
			
		||||
==== Routeing Area
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU upon receiving.
 | 
			
		||||
 | 
			
		||||
The messages which might require this IE to be send are not
 | 
			
		||||
implemented by OsmoPCU - see for <<not_impl>> details.
 | 
			
		||||
 | 
			
		||||
==== Suspend Reference Number
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU upon receiving.
 | 
			
		||||
 | 
			
		||||
The messages which might require this IE to be send are not
 | 
			
		||||
implemented by OsmoPCU - see for <<not_impl>> details.
 | 
			
		||||
 | 
			
		||||
==== Tag
 | 
			
		||||
 | 
			
		||||
This IE currently only used by OsmoPCU for Flow Control procedure (TS
 | 
			
		||||
48.018 § 8.2). In other cases it's either ignored or unavailable.
 | 
			
		||||
 | 
			
		||||
==== Trace Reference
 | 
			
		||||
 | 
			
		||||
This IE is ignored because entire message which contains it is ignored
 | 
			
		||||
by OsmoPCU - see <<invoke_trace>> for details.
 | 
			
		||||
 | 
			
		||||
==== Trace Type
 | 
			
		||||
 | 
			
		||||
This IE is ignored because entire message which contains it is ignored
 | 
			
		||||
by OsmoPCU - see <<invoke_trace>> for details.
 | 
			
		||||
 | 
			
		||||
==== TransactionId
 | 
			
		||||
 | 
			
		||||
This IE is ignored because entire message which contains it is ignored
 | 
			
		||||
by OsmoPCU - see <<invoke_trace>> for details.
 | 
			
		||||
 | 
			
		||||
==== Trigger Id
 | 
			
		||||
 | 
			
		||||
This IE is ignored because entire message which contains it is ignored
 | 
			
		||||
by OsmoPCU - see <<invoke_trace>> for details.
 | 
			
		||||
 | 
			
		||||
==== Number of octets affected
 | 
			
		||||
 | 
			
		||||
This IE is not available because the messages which contains it
 | 
			
		||||
(FLUSH-LL-ACK and LLC-DISCARDE) are not implemented by OsmoPCU - see
 | 
			
		||||
for <<not_impl>> details.
 | 
			
		||||
 | 
			
		||||
==== LSA Information
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
==== LSA Identifier List
 | 
			
		||||
 | 
			
		||||
This IE is not implemented by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
==== Packet Flow Identifier
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU upon receiving.
 | 
			
		||||
 | 
			
		||||
The messages which might require this IE to be send are not
 | 
			
		||||
implemented by OsmoPCU - see for <<not_impl>> details.
 | 
			
		||||
 | 
			
		||||
==== Aggregate BSS QoS Profile (ABQP)
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU upon receiving.
 | 
			
		||||
 | 
			
		||||
The messages which might require this IE to be send are not
 | 
			
		||||
implemented by OsmoPCU - see for <<not_impl>> details.
 | 
			
		||||
 | 
			
		||||
==== Feature Bitmap
 | 
			
		||||
 | 
			
		||||
This IE is not implemented by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU when received.
 | 
			
		||||
 | 
			
		||||
Absence of Feature Bitmap automatically disables optional features for
 | 
			
		||||
Network Service Entity (NSE) communicating with OsmoPCU.
 | 
			
		||||
 | 
			
		||||
==== Bucket_Full Ratio
 | 
			
		||||
 | 
			
		||||
This IE is not implemented by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
==== Service UTRAN CCO (Cell Change Order)
 | 
			
		||||
 | 
			
		||||
This IE is ignored by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
=== Gb BSSGP Initialization / PCU bring-up
 | 
			
		||||
 | 
			
		||||
The BSSGP initialization directly follows NS connection establishment
 | 
			
		||||
described in <<ns_init>>.
 | 
			
		||||
 | 
			
		||||
OsmoPCU allocates a BVC context for the BVCI given by OsmoBTS, which
 | 
			
		||||
in turn receives it from OsmoBSC or OsmoNITB via OML procedures.
 | 
			
		||||
 | 
			
		||||
In addition to the BVCI identifying the OsmoPCU side of BSSGP
 | 
			
		||||
connection, there is also special BVCI which is accepted by OsmoPCU in
 | 
			
		||||
accordance with 3GPP TS 48.018 § 5.4.1: BVCI = 0 represents signaling data
 | 
			
		||||
between SGSN and PCU in contrast to PTP (Peer-To-Peer) user's data.
 | 
			
		||||
 | 
			
		||||
The mapping between BSSGP PDUs and signaling or PTP BVCIs is available
 | 
			
		||||
in 3GPP TS 48.018 Table 5.4.
 | 
			
		||||
							
								
								
									
										27
									
								
								doc/manuals/gb/gb-startup.msc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								doc/manuals/gb/gb-startup.msc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
msc {
 | 
			
		||||
	hscale="1.2";
 | 
			
		||||
	bsc [label="BSC"], bts [label="BTS"], pcu [label="PCU"], sgsn [label="SGSN"];
 | 
			
		||||
 | 
			
		||||
	|||;
 | 
			
		||||
	bts box bsc [label="A-bis OML connection"];
 | 
			
		||||
	bsc => bts [label="Set OML Attrbibutes (NSVC,CELL)"];
 | 
			
		||||
	bts rbox pcu [label="PCU Unix Domain Socket"];
 | 
			
		||||
	pcu => bts [label="connect to PCU socket"];
 | 
			
		||||
	pcu <: bts  [label="Config. parameters"];
 | 
			
		||||
	pcu rbox pcu [label="bind/connect UDP socket"];
 | 
			
		||||
	pcu note sgsn [label="NS-over-IP (UDP port 23000)"];
 | 
			
		||||
	pcu => sgsn [label="NS RESET"];
 | 
			
		||||
	pcu <= sgsn [label="NS RESET ACK"];
 | 
			
		||||
	...;
 | 
			
		||||
	pcu => sgsn [label="NS UNBLOCK"];
 | 
			
		||||
	pcu <= sgsn [label="NS UNBLOCK ACK"];
 | 
			
		||||
	pcu box sgsn [label="NS link established"];
 | 
			
		||||
	...;
 | 
			
		||||
	pcu => sgsn [label="BVC RESET"];
 | 
			
		||||
	pcu <= sgsn [label="BVC RESET ACK"];
 | 
			
		||||
	...;
 | 
			
		||||
	pcu => sgsn [label="BVC UNBLOCK"];
 | 
			
		||||
	pcu <= sgsn [label="BVC UNBLOCK ACK"];
 | 
			
		||||
	pcu box sgsn [label="BSSGP link established"];
 | 
			
		||||
	|||;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										278
									
								
								doc/manuals/gb/ns.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								doc/manuals/gb/ns.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,278 @@
 | 
			
		||||
== Network Service (NS)
 | 
			
		||||
 | 
			
		||||
=== List of Messages
 | 
			
		||||
 | 
			
		||||
The following tables list the NS messages used by osmo-pcu and osmo-gbproxy, grouped by their level of
 | 
			
		||||
compliance with 3GPP TS 48.016.
 | 
			
		||||
 | 
			
		||||
==== Messages Compliant With 3GPP TS 48.016
 | 
			
		||||
 | 
			
		||||
The NS protocol is implemented inside libosmogb so none of the messages below are sent by OsmoPCU explicitly.
 | 
			
		||||
Instead corresponding functions from libosmogb are called which send and receive messages as necessary. See <<ns_init>> for details
 | 
			
		||||
on establishing NS connection.
 | 
			
		||||
 | 
			
		||||
.Messages compliant with 3GPP TS 48.016
 | 
			
		||||
[options="header",cols="10%,10%,20%,35%,5%,20%"]
 | 
			
		||||
|===
 | 
			
		||||
| TS 48.016 § | type code (hex) | This document § | Message | <-/-> | Received/Sent by OsmoPCU
 | 
			
		||||
| 9.2.1 | 0x0a | <<ns_alive>> | NS-ALIVE | <-/-> | Received/Sent
 | 
			
		||||
| 9.2.2 | 0x0b | <<ns_alive_ack>> | NS-ALIVE-ACK | <-/-> | Received/Sent
 | 
			
		||||
| 9.2.3 | 0x04 | <<ns_block>> | NS-BLOCK | <-/-> | Received/Sent
 | 
			
		||||
| 9.2.4 | 0x05 | <<ns_block_ack>> | NS-BLOCK-ACK | <-/-> | Received/Sent
 | 
			
		||||
| 9.2.5 | 0x02 | <<ns_reset>> | NS-RESET | <-/-> | Received/Sent
 | 
			
		||||
| 9.2.6 | 0x03 | <<ns_reset_ack>> | NS-RESET-ACK | <-/-> | Received/Sent
 | 
			
		||||
| 9.2.7 | 0x08 | <<ns_status>> | NS-STATUS | <-/-> | Received/Sent
 | 
			
		||||
| 9.2.8 | 0x06 | <<ns_unblock>> | NS-UNBLOCK | <-/-> | Received/Sent
 | 
			
		||||
| 9.2.9 | 0x07 | <<ns_unblock_ack>> | NS-UNBLOCK-ACK | <-/-> | Received/Sent
 | 
			
		||||
| 9.2.10 | 0x00 | <<ns_unit_data>> | NS-UNITDATA | <-/-> | Received/Sent
 | 
			
		||||
| 9.3.1 | 0x0c | <<sns_ack>> | SNS-ACK | <-/-> | Received/Sent
 | 
			
		||||
| 9.3.2 | 0x0d | <<sns_add>> | SNS-ADD | <-/-> | Received/Sent
 | 
			
		||||
| 9.3.3 | 0x0e | <<sns_changeweight>> | SNS-CHANGEWEIGHT | <-/-> | Received/Sent
 | 
			
		||||
| 9.3.4 | 0x0f | <<sns_config>> | SNS-CONFIG | <-/-> | Received/Sent
 | 
			
		||||
| 9.3.5 | 0x10 | <<sns_config_ack>> | SNS-CONFIG | <-/-> | Received/Sent
 | 
			
		||||
| 9.3.6 | 0x11 | <<sns_delete>> | SNS-DELETE | <-/-> | Received/Sent
 | 
			
		||||
| 9.3.7 | 0x12 | <<sns_size>> | SNS-SIZE | <-/-> | Received/Sent
 | 
			
		||||
| 9.3.8 | 0x13 | <<sns_size_ack>> | SNS-SIZE-ACK | <-/-> | Received/Sent
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
==== Messages Specific to OsmoPCU
 | 
			
		||||
 | 
			
		||||
There are no OsmoPCU specific NS messages.
 | 
			
		||||
 | 
			
		||||
==== Messages Not Implemented by OsmoPCU
 | 
			
		||||
 | 
			
		||||
All the NS protocol messages from 3GPP TS 48.016 are implemented in OsmoPCU.
 | 
			
		||||
 | 
			
		||||
=== Details on Compliant NS Messages
 | 
			
		||||
 | 
			
		||||
[[ns_unit_data]]
 | 
			
		||||
==== NS-UNITDATA
 | 
			
		||||
 | 
			
		||||
This PDU transfers one NS SDU (specified in 3GPP TS 08.18) between
 | 
			
		||||
OsmoPCU and SGSN. Upon receiving it OsmoPCU passes it to BSSGP
 | 
			
		||||
implementation to handle. It is also sent by BSSGP as necessary - see
 | 
			
		||||
<<bssgp>> for details.
 | 
			
		||||
 | 
			
		||||
It contains BVCI (<<ie_bvci>>) and NS SDU (<<ie_nssdu>>) IEs.
 | 
			
		||||
 | 
			
		||||
[[ns_reset]]
 | 
			
		||||
==== NS-RESET
 | 
			
		||||
 | 
			
		||||
This message is send by OsmoPCU in order to initiate reset procedure
 | 
			
		||||
described in 3GPP TS 48.016 § 7.3. The expected reply is NS-RESET-ACK
 | 
			
		||||
(<<ns_reset_ack>>) message. If no expected reply is received in 3
 | 
			
		||||
seconds than the sending is retried up to 3 times.  When this message
 | 
			
		||||
is received it is replied with NS-RESET-ACK (<<ns_reset_ack>>).
 | 
			
		||||
It might be ignored under conditions described in 3GPP TS 48.016 § 7.3.1.
 | 
			
		||||
 | 
			
		||||
The message conforms to 3GPP TS 48.016 § 9.2.5 specification.
 | 
			
		||||
 | 
			
		||||
It contains Cause (<<ie_cause>>), NSVCI (<<ie_nsvci>>) and NSEI (<<ie_nsei>>) IEs.
 | 
			
		||||
 | 
			
		||||
[[ns_reset_ack]]
 | 
			
		||||
==== NS-RESET-ACK
 | 
			
		||||
 | 
			
		||||
This message is sent as a response to proper NS-RESET (<<ns_reset>>)
 | 
			
		||||
message initiating reset procedure.
 | 
			
		||||
 | 
			
		||||
The message conforms to 3GPP TS 48.016 § 9.2.6 specification.
 | 
			
		||||
 | 
			
		||||
It contains NSVCI (<<ie_nsvci>>) and NSEI (<<ie_nsei>>) IEs.
 | 
			
		||||
 | 
			
		||||
[[ns_block]]
 | 
			
		||||
==== NS-BLOCK
 | 
			
		||||
 | 
			
		||||
Upon receiving this message corresponding NS-VC is marked as blocked
 | 
			
		||||
by OsmoPCU and NS-BLOCK-ACK (<<ns_block_ack>>) reply is transmitted.
 | 
			
		||||
When this message is sent by OsmoPCU corresponding NS-BLOCK-ACK
 | 
			
		||||
(<<ns_block_ack>>) reply is expected before NS-VC is actually marked
 | 
			
		||||
as blocked.  This behavior follows the blocking procedure described in
 | 
			
		||||
3GPP TS 48.016 § 7.2.
 | 
			
		||||
 | 
			
		||||
The message conforms to 3GPP TS 48.016 § 9.2.3 specification.
 | 
			
		||||
 | 
			
		||||
It contains Cause (<<ie_cause>>) and NSVCI (<<ie_nsvci>>) IEs.
 | 
			
		||||
 | 
			
		||||
[[ns_block_ack]]
 | 
			
		||||
==== NS-BLOCK-ACK
 | 
			
		||||
 | 
			
		||||
This message is sent by OsmoPCU automatically upon reception of
 | 
			
		||||
correct NS-BLOCK (<<ns_block>>) message.  It is expected as a reply
 | 
			
		||||
for NS-BLOCK (<<ns_block>>) message sent by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
The message conforms to 3GPP TS 48.016 § 9.2.4 specification.
 | 
			
		||||
 | 
			
		||||
It contains NSVCI (<<ie_nsvci>>) IE.
 | 
			
		||||
 | 
			
		||||
[[ns_unblock]]
 | 
			
		||||
==== NS-UNBLOCK
 | 
			
		||||
 | 
			
		||||
Upon receiving this message corresponding NS-VC is unblocked by
 | 
			
		||||
OsmoPCU and NS-UNBLOCK-ACK (<<ns_unblock_ack>>) reply is sent.  When
 | 
			
		||||
this message is sent by OsmoPCU corresponding NS-UNBLOCK-ACK
 | 
			
		||||
(<<ns_unblock_ack>>) reply is expected before NS-VC is actually marked
 | 
			
		||||
as unblocked.  This behavior follows the blocking procedure described
 | 
			
		||||
in 3GPP TS 48.016 § 7.2.
 | 
			
		||||
 | 
			
		||||
The message conforms to 3GPP TS 48.016 § 9.2.8 specification.
 | 
			
		||||
 | 
			
		||||
[[ns_unblock_ack]]
 | 
			
		||||
==== NS-UNBLOCK-ACK
 | 
			
		||||
 | 
			
		||||
Receiving this message notifies OsmoPCU that NS-VC unblocking request
 | 
			
		||||
is confirmed and thus NS-VC is marked as unblocked.  This message is
 | 
			
		||||
also sent as a reply to NS-UNBLOCK (<<ns_unblock>>) message.
 | 
			
		||||
 | 
			
		||||
The message conforms to 3GPP TS 48.016 § 9.2.9 specification.
 | 
			
		||||
 | 
			
		||||
[[ns_status]]
 | 
			
		||||
==== NS-STATUS
 | 
			
		||||
 | 
			
		||||
This message is sent to inform other party about error conditions as a
 | 
			
		||||
response to various unexpected PDUs or PDUs with unexpected/missing
 | 
			
		||||
data. If this message is received for unknown NS-VC it is ignored in
 | 
			
		||||
accordance with 3GPP TS 48.016 § 7.5.1, otherwise the error cause is
 | 
			
		||||
logged if present in NS-STATUS.
 | 
			
		||||
 | 
			
		||||
The message conforms to 3GPP TS 48.016 § 9.2.7 specification.
 | 
			
		||||
 | 
			
		||||
It contains Cause (<<ie_cause>>) and might (depending on actual error)
 | 
			
		||||
contain NSVCI (<<ie_nsvci>>), NS PDU (<<ie_nspdu>>) and BVCI
 | 
			
		||||
(<<ie_bvci>>) IEs.
 | 
			
		||||
 | 
			
		||||
[[ns_alive]]
 | 
			
		||||
==== NS-ALIVE
 | 
			
		||||
 | 
			
		||||
This message is sent periodically to test connectivity according to
 | 
			
		||||
3GPP TS 48.016 § 4.5.3. The expected response is NS-ALIVE-ACK
 | 
			
		||||
(<<ns_alive_ack>>). If no such response arrives within given amount of
 | 
			
		||||
time (3 seconds) than another NS-ALIVE message is sent and failed test
 | 
			
		||||
attempt is recorded.  After 10 failed attempts NS connection is
 | 
			
		||||
considered dead and OsmoPCU tries to reconnect.
 | 
			
		||||
 | 
			
		||||
The message conforms to 3GPP TS 48.016 § 9.2.1 specification.
 | 
			
		||||
 | 
			
		||||
[[ns_alive_ack]]
 | 
			
		||||
==== NS-ALIVE-ACK
 | 
			
		||||
 | 
			
		||||
This message is sent automatically in reply to NS-ALIVE (<<ns_alive>>)
 | 
			
		||||
message.
 | 
			
		||||
 | 
			
		||||
The message conforms to 3GPP TS 48.016 § 9.2.2 specification.
 | 
			
		||||
 | 
			
		||||
[[sns_ack]]
 | 
			
		||||
==== SNS-ACK
 | 
			
		||||
 | 
			
		||||
[[sns_add]]
 | 
			
		||||
==== SNS-ADD
 | 
			
		||||
 | 
			
		||||
[[sns_changeweight]]
 | 
			
		||||
==== SNS-CHANGEWEIGHT
 | 
			
		||||
 | 
			
		||||
[[sns_config]]
 | 
			
		||||
==== SNS-CONFIG
 | 
			
		||||
 | 
			
		||||
[[sns_config_ack]]
 | 
			
		||||
==== SNS-CONFIG-ACK
 | 
			
		||||
 | 
			
		||||
[[sns_delete]]
 | 
			
		||||
==== SNS-DELETE
 | 
			
		||||
 | 
			
		||||
[[ssn_size]]
 | 
			
		||||
==== SNS-SIZE
 | 
			
		||||
 | 
			
		||||
[[sns_size_ack]]
 | 
			
		||||
==== SNS-SIZE-ACK
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Information Elements Overview
 | 
			
		||||
 | 
			
		||||
All of the IEs handled by OsmoPCU are listed below, with limitations and
 | 
			
		||||
additions to 3GPP TS 48.016 specified in more detail.
 | 
			
		||||
 | 
			
		||||
==== IEs Conforming to 3GPP TS 48.016
 | 
			
		||||
 | 
			
		||||
The following Information Elements are accepted by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
.IEs conforming to 3GPP TS 48.016
 | 
			
		||||
[options="header",cols="5%,10%,40%,5%,40%"]
 | 
			
		||||
|===
 | 
			
		||||
| tag (hex) | TS 48.016 § | IE name | <-/-> | Received/Sent by OsmoPCU
 | 
			
		||||
| 0x03 | 10.3.1  | BVCI | <-/-> | Received/Sent
 | 
			
		||||
| 0x00 | 10.3.2  | Cause | <-/-> | Received/Sent
 | 
			
		||||
| -    | 10.3.2a | End Flag | <-/-> | Received/Sent
 | 
			
		||||
| 0x0b | 10.3.2b | IP Address | <-/-> | Received/Sent
 | 
			
		||||
| 0x05 | 10.3.2c | List of IP4 Elements | <-/-> | Received/Sent
 | 
			
		||||
| 0x06 | 10.3.2d | List of IP6 Elements | <-/-> | Received/Sent
 | 
			
		||||
| 0x07 | 10.3.2e | Maximum Number of NS-VCs | <-/-> | Received/Sent
 | 
			
		||||
| 0x08 | 10.3.2f | Number of IP4 Endpoints | <-/-> | Received/Sent
 | 
			
		||||
| 0x09 | 10.3.2g | Number of IP6 Endpoints | <-/-> | Received/Sent
 | 
			
		||||
| 0x02 | 10.3.3  | NS PDU | <-/-> | Received/Sent
 | 
			
		||||
| 0x01 | 10.3.5  | NSVCI | <-/-> | Received/Sent
 | 
			
		||||
| 0x04 | 10.3.6  | NSEI | <-/-> | Received/Sent
 | 
			
		||||
| -    | 10.3.7  | PDU Type | <-/-> | Received/Sent
 | 
			
		||||
| 0x0a | 10.3.7a | Reset Flag | <-/-> | Received/Sent
 | 
			
		||||
| -    | 10.3.8  | Spare Octet | <-/-> | Received/Sent
 | 
			
		||||
| -    | 10.3.10 | Transaction ID | <-/-> | Received/Sent
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
==== IEs Not Conforming to 3GPP TS 48.016
 | 
			
		||||
 | 
			
		||||
.IEs conforming to 3GPP TS 48.016
 | 
			
		||||
[options="header",cols="5%,10%,40%,5%,40%"]
 | 
			
		||||
|===
 | 
			
		||||
| tag (hex) | TS 48.016 § | IE name | <-/-> | Notice
 | 
			
		||||
| -    | 10.3.9  | NS-SDU Control Bits | <-/-> | Not implemented yet
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
All other IEs defined in 3GPP TS 48.016 § 10.3 are supported by OsmoPCU.
 | 
			
		||||
 | 
			
		||||
==== Additional Attributes and Parameters
 | 
			
		||||
 | 
			
		||||
There are no OsmoPCU specific additional Attributes and Parameters.
 | 
			
		||||
 | 
			
		||||
=== Details on IEs
 | 
			
		||||
 | 
			
		||||
[[ie_cause]]
 | 
			
		||||
==== Cause
 | 
			
		||||
 | 
			
		||||
This IE contains reason for a procedure or error as described in 3GPP TS 48.016 § 10.3.2.
 | 
			
		||||
 | 
			
		||||
[[ie_nsvci]]
 | 
			
		||||
==== NSVCI
 | 
			
		||||
 | 
			
		||||
This IE represents NSVCI identity described in <<ident>> and 3GPP TS 48.016 § 10.3.5.
 | 
			
		||||
 | 
			
		||||
[[ie_nspdu]]
 | 
			
		||||
==== NS PDU
 | 
			
		||||
 | 
			
		||||
This IE contains PDU (possibly truncated) which cause error described
 | 
			
		||||
in NS-STATUS message (<<ns_status>>) as described in 3GPP TS 48.016 §
 | 
			
		||||
10.3.3.
 | 
			
		||||
 | 
			
		||||
[[ie_nssdu]]
 | 
			
		||||
==== NS SDU
 | 
			
		||||
 | 
			
		||||
This IE contains BSSGP data - see <<bssgp>> for details.
 | 
			
		||||
 | 
			
		||||
[[ie_bvci]]
 | 
			
		||||
==== BVCI
 | 
			
		||||
 | 
			
		||||
This IE represents BSSGP identity described in <<ident>> and 3GPP TS 48.016
 | 
			
		||||
§ 10.3.1.
 | 
			
		||||
 | 
			
		||||
[[ie_nsei]]
 | 
			
		||||
==== NSEI
 | 
			
		||||
 | 
			
		||||
This IE represents NSEI identity described in <<ident>> and 3GPP TS 48.016 §
 | 
			
		||||
10.3.6.
 | 
			
		||||
 | 
			
		||||
[[ns_init]]
 | 
			
		||||
=== Gb NS Initialization / PCU bring-up
 | 
			
		||||
 | 
			
		||||
OsmoPCU binds and connects an UDP socket for NS using port numbers and IP
 | 
			
		||||
information given by OsmoBTS via the PCU socket. OsmoBTS in turn
 | 
			
		||||
receives this information from the BSC vi A-bis OML.
 | 
			
		||||
 | 
			
		||||
Following successful initialization of the UDP socket, the reset
 | 
			
		||||
procedure is initiated as described in <<ns_reset>>.
 | 
			
		||||
							
								
								
									
										46
									
								
								doc/manuals/osmopcu-gb-docinfo.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								doc/manuals/osmopcu-gb-docinfo.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
<authorgroup>
 | 
			
		||||
  <author>
 | 
			
		||||
    <firstname>Max</firstname>
 | 
			
		||||
    <surname>Suraev</surname>
 | 
			
		||||
    <email>msuraev@sysmocom.de</email>
 | 
			
		||||
    <authorinitials>MS</authorinitials>
 | 
			
		||||
    <affiliation>
 | 
			
		||||
      <shortaffil>sysmocom</shortaffil>
 | 
			
		||||
      <orgname>sysmocom - s.f.m.c. GmbH</orgname>
 | 
			
		||||
      <jobtitle>Software Developer</jobtitle>
 | 
			
		||||
    </affiliation>
 | 
			
		||||
  </author>
 | 
			
		||||
  <author>
 | 
			
		||||
    <firstname>Harald</firstname>
 | 
			
		||||
    <surname>Welte</surname>
 | 
			
		||||
    <email>hwelte@sysmocom.de</email>
 | 
			
		||||
    <authorinitials>HW</authorinitials>
 | 
			
		||||
    <affiliation>
 | 
			
		||||
      <shortaffil>sysmocom</shortaffil>
 | 
			
		||||
      <orgname>sysmocom - s.f.m.c. GmbH</orgname>
 | 
			
		||||
      <jobtitle>Managing Director</jobtitle>
 | 
			
		||||
    </affiliation>
 | 
			
		||||
  </author>
 | 
			
		||||
</authorgroup>
 | 
			
		||||
 | 
			
		||||
<copyright>
 | 
			
		||||
  <year>2015-2021</year>
 | 
			
		||||
  <holder>sysmocom - s.f.m.c. GmbH</holder>
 | 
			
		||||
</copyright>
 | 
			
		||||
 | 
			
		||||
<legalnotice>
 | 
			
		||||
  <para>
 | 
			
		||||
	Permission is granted to copy, distribute and/or modify this
 | 
			
		||||
	document under the terms of the GNU Free Documentation License,
 | 
			
		||||
	Version 1.3 or any later version published by the Free Software
 | 
			
		||||
	Foundation; with no Invariant Sections, no Front-Cover Texts,
 | 
			
		||||
	and no Back-Cover Texts.  A copy of the license is included in
 | 
			
		||||
	the section entitled "GNU Free Documentation License".
 | 
			
		||||
  </para>
 | 
			
		||||
  <para>
 | 
			
		||||
	The Asciidoc source code of this manual can be found at
 | 
			
		||||
	<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
 | 
			
		||||
		http://git.osmocom.org/osmo-gsm-manuals/
 | 
			
		||||
	</ulink>
 | 
			
		||||
  </para>
 | 
			
		||||
</legalnotice>
 | 
			
		||||
							
								
								
									
										95
									
								
								doc/manuals/osmopcu-gb.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								doc/manuals/osmopcu-gb.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
:gfdl-enabled:
 | 
			
		||||
 | 
			
		||||
OsmoPCU Gb Protocol Specification
 | 
			
		||||
=================================
 | 
			
		||||
Harald Welte <hwelte@sysmocom.de>
 | 
			
		||||
 | 
			
		||||
== Introduction
 | 
			
		||||
 | 
			
		||||
This document describes the Gb interface of *OsmoPCU*. Based on 3GPP TS
 | 
			
		||||
48.016 and 48.018, this document indicates which of the 3GPP specified Gb
 | 
			
		||||
messages and IEs are implemented according to 3GPP specifications, which of
 | 
			
		||||
these are not or not fully implemented, as well as OsmoPCU-specific extensions
 | 
			
		||||
to the Gb interface not specified by 3GPP.
 | 
			
		||||
 | 
			
		||||
Extensions to the Gb interface specific to OsmoPCU are detailed in this
 | 
			
		||||
document. For details on the messages and IEs that comply with above-mentioned
 | 
			
		||||
3GPP specifications, please refer to those documents.
 | 
			
		||||
 | 
			
		||||
.3GPP document versions referred to by this document
 | 
			
		||||
[cols="20%,80%"]
 | 
			
		||||
|===
 | 
			
		||||
|3GPP TS 08.56 | version 8.0.1 Release 1999
 | 
			
		||||
|3GPP TS 08.58 | version 8.6.0 Release 1999
 | 
			
		||||
|3GPP TS 08.60 | version 8.2.1 Release 1999
 | 
			
		||||
|3GPP TS 12.21 | version 8.0.0 Release 1999
 | 
			
		||||
|3GPP TS 48.016 | version 15.0.0 Release 15
 | 
			
		||||
|3GPP TS 48.018 | version 15.0.0 Release 15
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
.IETF documents referred to by his document
 | 
			
		||||
[cols="20%,80%"]
 | 
			
		||||
|===
 | 
			
		||||
|IETF RFC 768 | User Datagram Protocol
 | 
			
		||||
|IETF RFC 791 | Internet Protocol
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
== Overview
 | 
			
		||||
 | 
			
		||||
The OsmoPCU Gb interface consists of the NS (Network Services) and
 | 
			
		||||
BSSGP (Base Station Subsystem Gateway Protocol), encapsulated in UDP
 | 
			
		||||
(User Datagram Protocol) and IP (Internet Protocol) version 4.
 | 
			
		||||
Use of other underlying protocols (e. g. Frame Relay) is not supported.
 | 
			
		||||
 | 
			
		||||
.UDP port numbers used by OsmoPCU Gb/IP
 | 
			
		||||
[options="header",width="50%",cols="35%,65%"]
 | 
			
		||||
|===
 | 
			
		||||
|TCP Port Number|Usage
 | 
			
		||||
|23000|NS over UDP (default port)
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
The NS-over-UDP link is established in the PCU -> SGSN direction, i.e.
 | 
			
		||||
the PCU is running as client while the SGSN is running as server.
 | 
			
		||||
 | 
			
		||||
Establishment of the NS-over-UDP link is only possible after OsmoPCU
 | 
			
		||||
has been configured via the *PCU socket* interface from OsmoBTS.
 | 
			
		||||
 | 
			
		||||
OsmoBTS in turn receives relevant configuration parameters from
 | 
			
		||||
OsmoBSC or the BSC component inside OsmoNITB.
 | 
			
		||||
 | 
			
		||||
.Overview of Gb link establishment
 | 
			
		||||
["mscgen"]
 | 
			
		||||
----
 | 
			
		||||
include::{srcdir}/gb/gb-startup.msc[]
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
[[ident]]
 | 
			
		||||
=== Identities
 | 
			
		||||
 | 
			
		||||
The Gb interface identities of the PCU are configured via BSC ->
 | 
			
		||||
OsmoBTS -> PCU Socket. They consist of
 | 
			
		||||
 | 
			
		||||
NSEI:: NS Equipment Identifier
 | 
			
		||||
NSVCI:: NS Virtual Connection Identifier
 | 
			
		||||
BVCI:: BSSGP Virtual Connection Identifier
 | 
			
		||||
 | 
			
		||||
For an explanation of those identifiers and their use in the NS and
 | 
			
		||||
BSSGP protocols, please see the relevant 3GPP specifications for NS (TS 48.016)
 | 
			
		||||
and BSSGP (TS 48.018).
 | 
			
		||||
 | 
			
		||||
In most cases, all above identities belong to different namespaces and
 | 
			
		||||
must be unique within their respective namespace and within the SGSN
 | 
			
		||||
they connect to.
 | 
			
		||||
 | 
			
		||||
This means that typically each OsmoPCU has one unique set of NSEI,
 | 
			
		||||
NSVCI and BVCI in your network.
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/gb/ns.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/gb/bssgp.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/port_numbers.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/glossary.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/gfdl.adoc[]
 | 
			
		||||
							
								
								
									
										35
									
								
								doc/manuals/osmopcu-usermanual-docinfo.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								doc/manuals/osmopcu-usermanual-docinfo.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
<authorgroup>
 | 
			
		||||
  <author>
 | 
			
		||||
    <firstname>Harald</firstname>
 | 
			
		||||
    <surname>Welte</surname>
 | 
			
		||||
    <email>hwelte@sysmocom.de</email>
 | 
			
		||||
    <authorinitials>HW</authorinitials>
 | 
			
		||||
    <affiliation>
 | 
			
		||||
      <shortaffil>sysmocom</shortaffil>
 | 
			
		||||
      <orgname>sysmocom - s.f.m.c. GmbH</orgname>
 | 
			
		||||
      <jobtitle>Managing Director</jobtitle>
 | 
			
		||||
    </affiliation>
 | 
			
		||||
  </author>
 | 
			
		||||
</authorgroup>
 | 
			
		||||
 | 
			
		||||
<copyright>
 | 
			
		||||
  <year>2013-2021</year>
 | 
			
		||||
  <holder>sysmocom - s.f.m.c. GmbH</holder>
 | 
			
		||||
</copyright>
 | 
			
		||||
 | 
			
		||||
<legalnotice>
 | 
			
		||||
  <para>
 | 
			
		||||
	Permission is granted to copy, distribute and/or modify this
 | 
			
		||||
	document under the terms of the GNU Free Documentation License,
 | 
			
		||||
	Version 1.3 or any later version published by the Free Software
 | 
			
		||||
	Foundation; with no Invariant Sections, no Front-Cover Texts,
 | 
			
		||||
	and no Back-Cover Texts.  A copy of the license is included in
 | 
			
		||||
	the section entitled "GNU Free Documentation License".
 | 
			
		||||
  </para>
 | 
			
		||||
  <para>
 | 
			
		||||
	The Asciidoc source code of this manual can be found at
 | 
			
		||||
	<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
 | 
			
		||||
		http://git.osmocom.org/osmo-gsm-manuals/
 | 
			
		||||
	</ulink>
 | 
			
		||||
  </para>
 | 
			
		||||
</legalnotice>
 | 
			
		||||
							
								
								
									
										34
									
								
								doc/manuals/osmopcu-usermanual.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								doc/manuals/osmopcu-usermanual.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
:gfdl-enabled:
 | 
			
		||||
 | 
			
		||||
OsmoPCU User Manual
 | 
			
		||||
===================
 | 
			
		||||
Harald Welte <hwelte@sysmocom.de>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/preface.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/overview.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/running.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/vty.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/logging.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/configuration.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/counters.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/gb.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/qos-dscp-pcp.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/vty_cpu_sched.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/port_numbers.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/bibliography.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/glossary.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/gfdl.adoc[]
 | 
			
		||||
							
								
								
									
										29
									
								
								doc/manuals/osmopcu-vty-reference.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								doc/manuals/osmopcu-vty-reference.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!--
 | 
			
		||||
  ex:ts=2:sw=42sts=2:et
 | 
			
		||||
  -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
-->
 | 
			
		||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
 | 
			
		||||
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
 | 
			
		||||
<!ENTITY chapter-vty      SYSTEM      "./common/chapters/vty.xml">
 | 
			
		||||
<!ENTITY sections-vty     SYSTEM      "generated/docbook_vty.xml" >
 | 
			
		||||
]>
 | 
			
		||||
 | 
			
		||||
<book>
 | 
			
		||||
  <info>
 | 
			
		||||
 | 
			
		||||
    <title>OsmoPCU VTY Reference</title>
 | 
			
		||||
 | 
			
		||||
    <copyright>
 | 
			
		||||
      <year>2014-2021</year>
 | 
			
		||||
    </copyright>
 | 
			
		||||
 | 
			
		||||
    <legalnotice>
 | 
			
		||||
      <para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
 | 
			
		||||
      </para>
 | 
			
		||||
    </legalnotice>
 | 
			
		||||
  </info>
 | 
			
		||||
 | 
			
		||||
  <!-- Main chapters-->
 | 
			
		||||
  &chapter-vty;
 | 
			
		||||
</book>
 | 
			
		||||
							
								
								
									
										17
									
								
								doc/manuals/regen_doc.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								doc/manuals/regen_doc.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
#!/bin/sh -x
 | 
			
		||||
 | 
			
		||||
if [ -z "$DOCKER_PLAYGROUND" ]; then
 | 
			
		||||
	echo "You need to set DOCKER_PLAYGROUND"
 | 
			
		||||
	exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
SCRIPT=$(realpath "$0")
 | 
			
		||||
MANUAL_DIR=$(dirname "$SCRIPT")
 | 
			
		||||
 | 
			
		||||
COMMIT=${COMMIT:-$(git log -1 --format=format:%H)}
 | 
			
		||||
 | 
			
		||||
cd "$DOCKER_PLAYGROUND/scripts" || exit 1
 | 
			
		||||
 | 
			
		||||
OSMO_PCU_BRANCH=$COMMIT ./regen_doc.sh osmo-pcu 4240 \
 | 
			
		||||
	"$MANUAL_DIR/chapters/counters_generated.adoc" \
 | 
			
		||||
	"$MANUAL_DIR/vty/osmo-pcu_vty_reference.xml"
 | 
			
		||||
							
								
								
									
										9
									
								
								doc/manuals/vty/osmo-pcu_vty_additions.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								doc/manuals/vty/osmo-pcu_vty_additions.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
 | 
			
		||||
    <node id='14'>
 | 
			
		||||
    <child_of id='4' />
 | 
			
		||||
    <name>PCU Configuration Node</name>
 | 
			
		||||
    <description>The main PCU configuration including the timeslot
 | 
			
		||||
    assignment algorithm and other parameters.</description>
 | 
			
		||||
    </node>
 | 
			
		||||
</vtydoc>
 | 
			
		||||
 | 
			
		||||
@@ -14,16 +14,16 @@ Notes:
 | 
			
		||||
    Queue of next frames to be transmitted.
 | 
			
		||||
 | 
			
		||||
States:
 | 
			
		||||
  GPRS_RLCMAC_ASSIGN
 | 
			
		||||
  TBF_ST_ASSIGN
 | 
			
		||||
    After a downlink TBF is created, it resides in this state until the
 | 
			
		||||
    block flow can start. This is required to give the mobile time to listen
 | 
			
		||||
    to connect to downlink PDCH.
 | 
			
		||||
 | 
			
		||||
   GPRS_RLCMAC_FLOW,
 | 
			
		||||
   TBF_ST_FLOW,
 | 
			
		||||
    During packet flow, this state indicates downlink and uplink TBF block
 | 
			
		||||
    flow.
 | 
			
		||||
 | 
			
		||||
   GPRS_RLCMAC_FINISHED,
 | 
			
		||||
   TBF_ST_FINISHED,
 | 
			
		||||
    Uplink TBF:
 | 
			
		||||
    After final block is received AND all other blocks are completely
 | 
			
		||||
    received, the state is entered. The PACKET CONTROL ACK is still not
 | 
			
		||||
@@ -33,11 +33,11 @@ States:
 | 
			
		||||
    downlink blocks are acknowledged yet. (Counter N3015 is counted on each
 | 
			
		||||
    poll request.)
 | 
			
		||||
 | 
			
		||||
   GPRS_RLCMAC_WAIT_RELEASE,
 | 
			
		||||
   TBF_ST_WAIT_RELEASE,
 | 
			
		||||
    The all blocks on downlink TBF have been acked by mobile. The penalty
 | 
			
		||||
    timer T3192 is running on mobile.
 | 
			
		||||
 | 
			
		||||
   GPRS_RLCMAC_RELEASING,
 | 
			
		||||
   TBF_ST_RELEASING,
 | 
			
		||||
    Wait for TFI/USF to be re-used. This state is entered when a counter
 | 
			
		||||
    reaches it's maximum and T3169 is running.
 | 
			
		||||
 | 
			
		||||
@@ -117,13 +117,17 @@ Control TS:
 | 
			
		||||
 | 
			
		||||
Polling:
 | 
			
		||||
  In order to poll uplink control block from MS, a special poll state and
 | 
			
		||||
  frame number is stored at TBF. The scheduler reads that value and will not
 | 
			
		||||
  assign uplink ressource for other TBFs at that frame number.
 | 
			
		||||
  frame number is stored at PDCH UL Controller. The scheduler reads that value
 | 
			
		||||
  and will not assign uplink resource for other TBFs at that frame number.
 | 
			
		||||
 | 
			
		||||
  When there is no uplink transmission received on the block, a timeout is
 | 
			
		||||
  indicated by layer 1 interface. There are two ways of checking timeout:
 | 
			
		||||
   - The received frame is bad (BFI).
 | 
			
		||||
   - The GSM indicates that the block should have been already received.
 | 
			
		||||
  On receipt of an Uplink RLCMAC block, it's the duty of each specific message
 | 
			
		||||
  handler to release the expectancies previously stored in the PDCH UL
 | 
			
		||||
  Controller. After the specific handler returns, the generic return path will
 | 
			
		||||
  expire all reserved events up to the curent FN for that PDCH, eventually
 | 
			
		||||
  calling timeout functions on TBFs and SBAs owning those reservations. Hence,
 | 
			
		||||
  the layer 1 interface is expected to provide DATA.ind for each FN block,
 | 
			
		||||
  containing data=0 if decoding failed, in order to keep the clock up-to-date in
 | 
			
		||||
  upper layers.
 | 
			
		||||
 | 
			
		||||
  Because polling requires uplink response from MS, the polling must be
 | 
			
		||||
  performed at control TS.
 | 
			
		||||
@@ -131,13 +135,21 @@ Polling:
 | 
			
		||||
 | 
			
		||||
Data structures of TBFs and PDCHs:
 | 
			
		||||
 | 
			
		||||
  There is a global structure for BTS.
 | 
			
		||||
  There is a global structure for PCU (struct gprs_pcu the_pcu).
 | 
			
		||||
 | 
			
		||||
  The BTS structure has 8 TRX structures.
 | 
			
		||||
  A BTS is created and put into PCU's list for each PCUIF INFO_IND with a unique
 | 
			
		||||
  bts_nr received.
 | 
			
		||||
 | 
			
		||||
  Each BTS structure has 8 TRX structures.
 | 
			
		||||
 | 
			
		||||
  Each TRX structure has 8 PDCH structures, one for each timeslot.
 | 
			
		||||
 | 
			
		||||
  There are two linked lists of TBF instances:
 | 
			
		||||
  Each BTS structure has a list of MS (struct GprsMs).
 | 
			
		||||
 | 
			
		||||
  Each MS can have 1 UL TBF and 1 DL TBF, and 1 TBF is always assigned to an
 | 
			
		||||
  existing GprsMS structure.
 | 
			
		||||
 | 
			
		||||
  Each BTS also has two linked lists of TBF instances:
 | 
			
		||||
   - uplink TBFs
 | 
			
		||||
   - downlink TBFs
 | 
			
		||||
 | 
			
		||||
@@ -147,7 +159,7 @@ Data structures of TBFs and PDCHs:
 | 
			
		||||
   - an array of 8 PDCH structures, one for each assigned timeslot
 | 
			
		||||
   - in case of uplink TBF: an array of 8 USFs, one for each assigned timeslot
 | 
			
		||||
 | 
			
		||||
  Each PDCH structure also has:
 | 
			
		||||
  Each TRX and all it's PDCH structures also have:
 | 
			
		||||
   - an array of 32 uplink TBFs that are assigned to this PDCH
 | 
			
		||||
   - an array of 32 downlink TBFs that are assigned to this PDCH
 | 
			
		||||
 | 
			
		||||
@@ -158,4 +170,6 @@ Data structures of TBFs and PDCHs:
 | 
			
		||||
  On release of a TBF, the link to this PDCH is removed from all assigned
 | 
			
		||||
  PDCHs. The TBF is removed from the list of TBFs. The TBF is destroyed.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  GprsMs structures are kept alive for a while once they become unused, in order to
 | 
			
		||||
  have a cache of MS related information it would otherwise need to acquire
 | 
			
		||||
  again once it interacts with the PCU again.
 | 
			
		||||
							
								
								
									
										2
									
								
								include/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								include/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	osmocom/pcu/pcuif_proto.h
 | 
			
		||||
							
								
								
									
										273
									
								
								include/osmocom/pcu/pcuif_proto.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								include/osmocom/pcu/pcuif_proto.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,273 @@
 | 
			
		||||
#ifndef _PCUIF_PROTO_H
 | 
			
		||||
#define _PCUIF_PROTO_H
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/l1sap.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#define PCU_SOCK_DEFAULT	"/tmp/pcu_bts"
 | 
			
		||||
 | 
			
		||||
#define PCU_IF_VERSION		0x0a
 | 
			
		||||
#define TXT_MAX_LEN	128
 | 
			
		||||
 | 
			
		||||
/* msg_type */
 | 
			
		||||
#define PCU_IF_MSG_DATA_REQ	0x00	/* send data to given channel */
 | 
			
		||||
#define PCU_IF_MSG_DATA_CNF	0x01	/* confirm (e.g. transmission on PCH) */
 | 
			
		||||
#define PCU_IF_MSG_DATA_IND	0x02	/* receive data from given channel */
 | 
			
		||||
#define PCU_IF_MSG_SUSP_REQ	0x03	/* BTS forwards GPRS SUSP REQ to PCU */
 | 
			
		||||
#define PCU_IF_MSG_APP_INFO_REQ	0x04	/* BTS asks PCU to transmit APP INFO via PACCH */
 | 
			
		||||
#define PCU_IF_MSG_RTS_REQ	0x10	/* ready to send request */
 | 
			
		||||
#define PCU_IF_MSG_DATA_CNF_DT	0x11	/* confirm (with direct tlli) */
 | 
			
		||||
#define PCU_IF_MSG_RACH_IND	0x22	/* receive RACH */
 | 
			
		||||
#define PCU_IF_MSG_INFO_IND	0x32	/* retrieve BTS info */
 | 
			
		||||
#define PCU_IF_MSG_ACT_REQ	0x40	/* activate/deactivate PDCH */
 | 
			
		||||
#define PCU_IF_MSG_TIME_IND	0x52	/* GSM time indication */
 | 
			
		||||
#define PCU_IF_MSG_INTERF_IND	0x53	/* interference report */
 | 
			
		||||
#define PCU_IF_MSG_PAG_REQ	0x60	/* paging request */
 | 
			
		||||
#define PCU_IF_MSG_TXT_IND	0x70	/* Text indication for BTS */
 | 
			
		||||
#define PCU_IF_MSG_CONTAINER	0x80	/* Transparent container message */
 | 
			
		||||
 | 
			
		||||
/* msg_type coming from BSC (inside PCU_IF_MSG_CONTAINER) */
 | 
			
		||||
#define PCU_IF_MSG_ANR_REQ	0x81	/* Automatic Neighbor Registration Request */
 | 
			
		||||
#define PCU_IF_MSG_ANR_CNF	0x82	/* Automatic Neighbor Registration Confirmation (meas results) */
 | 
			
		||||
 | 
			
		||||
/* 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 */
 | 
			
		||||
#define PCU_IF_SAPI_AGCH_DT	0x08	/* assignment on AGCH but with additional TLLI */
 | 
			
		||||
 | 
			
		||||
/* 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)
 | 
			
		||||
 | 
			
		||||
/* NSVC address type */
 | 
			
		||||
#define PCU_IF_ADDR_TYPE_UNSPEC	0x00	/* No address - empty entry */
 | 
			
		||||
#define PCU_IF_ADDR_TYPE_IPV4	0x04	/* IPv4 address */
 | 
			
		||||
#define PCU_IF_ADDR_TYPE_IPV6	0x29	/* IPv6 address */
 | 
			
		||||
 | 
			
		||||
#define PCU_IF_NUM_NSVC 2
 | 
			
		||||
 | 
			
		||||
enum gsm_pcu_if_text_type {
 | 
			
		||||
	PCU_VERSION,
 | 
			
		||||
	PCU_OML_ALERT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gsm_pcu_if_txt_ind {
 | 
			
		||||
	uint8_t		type; /* gsm_pcu_if_text_type */
 | 
			
		||||
	char		text[TXT_MAX_LEN]; /* Text to be transmitted to BTS */
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	uint16_t	ber10k;		/* !< \brief BER in units of 0.01% */
 | 
			
		||||
	int16_t		ta_offs_qbits;	/* !< \brief Burst TA Offset in quarter bits */
 | 
			
		||||
	int16_t		lqual_cb;	/* !< \brief Link quality in centiBel */
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
/* data confirmation with direct tlli (instead of raw mac block with tlli) */
 | 
			
		||||
struct gsm_pcu_if_data_cnf_dt {
 | 
			
		||||
	uint8_t		sapi;
 | 
			
		||||
	uint32_t	tlli;
 | 
			
		||||
	uint32_t	fn;
 | 
			
		||||
	uint16_t	arfcn;
 | 
			
		||||
	uint8_t		trx_nr;
 | 
			
		||||
	uint8_t		ts_nr;
 | 
			
		||||
	uint8_t		block_nr;
 | 
			
		||||
	int8_t		rssi;
 | 
			
		||||
	uint16_t	ber10k;		/* !< \brief BER in units of 0.01% */
 | 
			
		||||
	int16_t		ta_offs_qbits;	/* !< \brief Burst TA Offset in quarter bits */
 | 
			
		||||
	int16_t		lqual_cb;	/* !< \brief Link quality in centiBel */
 | 
			
		||||
} __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;
 | 
			
		||||
	uint16_t	ra;
 | 
			
		||||
	int16_t		qta;
 | 
			
		||||
	uint32_t	fn;
 | 
			
		||||
	uint16_t	arfcn;
 | 
			
		||||
	uint8_t		is_11bit;
 | 
			
		||||
	uint8_t		burst_type;
 | 
			
		||||
	uint8_t		trx_nr;
 | 
			
		||||
	uint8_t		ts_nr;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct gsm_pcu_if_info_ts {
 | 
			
		||||
	uint8_t		tsc;
 | 
			
		||||
	uint8_t		h;
 | 
			
		||||
	uint8_t		hsn;
 | 
			
		||||
	uint8_t		maio;
 | 
			
		||||
	uint8_t		ma_bit_len;
 | 
			
		||||
	uint8_t		ma[8];
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct gsm_pcu_if_info_trx {
 | 
			
		||||
	uint16_t	arfcn;
 | 
			
		||||
	uint8_t		pdch_mask;		/* PDCH timeslot mask */
 | 
			
		||||
	uint8_t		spare;
 | 
			
		||||
	uint32_t	hlayer1;
 | 
			
		||||
	struct gsm_pcu_if_info_ts ts[8];	/* timeslots per TRX */
 | 
			
		||||
} __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;
 | 
			
		||||
	uint8_t		mnc_3_digits;
 | 
			
		||||
	uint16_t	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[PCU_IF_NUM_NSVC];
 | 
			
		||||
	uint16_t	local_port[PCU_IF_NUM_NSVC];
 | 
			
		||||
	uint16_t	remote_port[PCU_IF_NUM_NSVC];
 | 
			
		||||
	uint8_t		address_type[PCU_IF_NUM_NSVC];
 | 
			
		||||
	union {
 | 
			
		||||
		struct in_addr v4;
 | 
			
		||||
		struct in6_addr v6;
 | 
			
		||||
	} remote_ip[PCU_IF_NUM_NSVC];
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct gsm_pcu_if_act_req {
 | 
			
		||||
	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));
 | 
			
		||||
 | 
			
		||||
/* BTS tells PCU to [once] send given application data via PACCH to all UE with active TBF */
 | 
			
		||||
struct gsm_pcu_if_app_info_req {
 | 
			
		||||
	uint8_t		application_type; /* 4bit field, see TS 44.060 11.2.47 */
 | 
			
		||||
	uint8_t		len;		  /* length of data */
 | 
			
		||||
	uint8_t		data[162];	  /* random size choice; ETWS needs 56 bytes */
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
/* BTS tells PCU about a GPRS SUSPENSION REQUEST received on DCCH */
 | 
			
		||||
struct gsm_pcu_if_susp_req {
 | 
			
		||||
	uint32_t	tlli;
 | 
			
		||||
	uint8_t		ra_id[6];
 | 
			
		||||
	uint8_t		cause;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
/* Interference measurements on PDCH timeslots */
 | 
			
		||||
struct gsm_pcu_if_interf_ind {
 | 
			
		||||
	uint8_t		trx_nr;
 | 
			
		||||
	uint8_t		spare[3];
 | 
			
		||||
	uint32_t	fn;
 | 
			
		||||
	uint8_t		interf[8];
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
/* Contains messages transmitted BSC<->PCU, potentially forwarded by BTS via IPA/PCU */
 | 
			
		||||
struct gsm_pcu_if_container {
 | 
			
		||||
	uint8_t		msg_type;
 | 
			
		||||
	uint8_t 	spare;
 | 
			
		||||
	uint16_t	length; /* network byte order */
 | 
			
		||||
	uint8_t		data[0];
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
/* Used inside container: */
 | 
			
		||||
struct gsm_pcu_if_anr_req {
 | 
			
		||||
	uint8_t		num_cells;
 | 
			
		||||
	uint16_t	cell_list[96];  /* struct gsm48_cell_desc */
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
/* PCU confirms back with measurements of target cells */
 | 
			
		||||
struct gsm_pcu_if_anr_cnf {
 | 
			
		||||
	uint8_t		num_cells;
 | 
			
		||||
	uint16_t	cell_list[32];  /* struct gsm48_cell_desc */
 | 
			
		||||
	uint8_t		rxlev_list[32]; /* value 0xff: invalid */
 | 
			
		||||
} __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_cnf_dt	data_cnf_dt;
 | 
			
		||||
		struct gsm_pcu_if_data		data_ind;
 | 
			
		||||
		struct gsm_pcu_if_susp_req	susp_req;
 | 
			
		||||
		struct gsm_pcu_if_rts_req	rts_req;
 | 
			
		||||
		struct gsm_pcu_if_rach_ind	rach_ind;
 | 
			
		||||
		struct gsm_pcu_if_txt_ind	txt_ind;
 | 
			
		||||
		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;
 | 
			
		||||
		struct gsm_pcu_if_app_info_req	app_info_req;
 | 
			
		||||
		struct gsm_pcu_if_interf_ind	interf_ind;
 | 
			
		||||
		struct gsm_pcu_if_container	container;
 | 
			
		||||
	} u;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
#endif /* _PCUIF_PROTO_H */
 | 
			
		||||
							
								
								
									
										26
									
								
								osmoappdesc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								osmoappdesc.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
 | 
			
		||||
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
app_configs = {
 | 
			
		||||
    "osmo-pcu": ["doc/examples/osmo-pcu.cfg"]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apps = [(4240, "src/osmo-pcu", "OsmoPCU", "osmo-pcu"),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
vty_command = ["src/osmo-pcu", "-c", "doc/examples/osmo-pcu.cfg"]
 | 
			
		||||
 | 
			
		||||
vty_app = apps[0]
 | 
			
		||||
							
								
								
									
										188
									
								
								src/Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										188
									
								
								src/Makefile.am
									
									
									
									
									
								
							@@ -18,61 +18,191 @@
 | 
			
		||||
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)
 | 
			
		||||
AUTOMAKE_OPTIONS = subdir-objects
 | 
			
		||||
AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOGSM_CFLAGS)
 | 
			
		||||
 | 
			
		||||
if ENABLE_SYSMODSP
 | 
			
		||||
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if ENABLE_LC15BTS_PHY
 | 
			
		||||
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if ENABLE_OC2GBTS_PHY
 | 
			
		||||
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
AM_CXXFLAGS = -Wall -ldl -pthread
 | 
			
		||||
AM_LDFLAGS = -lrt
 | 
			
		||||
 | 
			
		||||
noinst_LTLIBRARIES = libgprs.la
 | 
			
		||||
 | 
			
		||||
libgprs_la_SOURCES = \
 | 
			
		||||
	gprs_debug.cpp \
 | 
			
		||||
	csn1.cpp \
 | 
			
		||||
	gsm_rlcmac.cpp \
 | 
			
		||||
	gprs_bssgp_pcu.cpp \
 | 
			
		||||
	csn1.c \
 | 
			
		||||
	gsm_rlcmac.c \
 | 
			
		||||
	gprs_bssgp_pcu.c \
 | 
			
		||||
	gprs_bssgp_rim.c \
 | 
			
		||||
	gprs_rlcmac.cpp \
 | 
			
		||||
	gprs_rlcmac_data.cpp \
 | 
			
		||||
	gprs_rlcmac_sched.cpp \
 | 
			
		||||
	gsm_timer.cpp \
 | 
			
		||||
	bitvector.cpp \
 | 
			
		||||
	gprs_rlcmac_meas.cpp \
 | 
			
		||||
	gprs_rlcmac_ts_alloc.cpp \
 | 
			
		||||
	gprs_ms.c \
 | 
			
		||||
	gprs_ms_storage.cpp \
 | 
			
		||||
	gprs_pcu.c \
 | 
			
		||||
	pcu_l1_if.cpp \
 | 
			
		||||
	pcu_vty.c
 | 
			
		||||
 | 
			
		||||
if ENABLE_SYSMOBTS
 | 
			
		||||
libgprs_la_SOURCES += \
 | 
			
		||||
	sysmo_sock.cpp
 | 
			
		||||
else
 | 
			
		||||
libgprs_la_SOURCES += \
 | 
			
		||||
	openbts_sock.cpp
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
	RLCMACTest
 | 
			
		||||
	pcu_vty.c \
 | 
			
		||||
	pcu_vty_functions.cpp \
 | 
			
		||||
	mslot_class.c \
 | 
			
		||||
	nacc_fsm.c \
 | 
			
		||||
	neigh_cache.c \
 | 
			
		||||
	tbf.cpp \
 | 
			
		||||
	tbf_fsm.c \
 | 
			
		||||
	tbf_ul.cpp \
 | 
			
		||||
	tbf_dl.cpp \
 | 
			
		||||
	bts.cpp \
 | 
			
		||||
	bts_anr_fsm.c \
 | 
			
		||||
	ms_anr_fsm.c \
 | 
			
		||||
	pdch.cpp \
 | 
			
		||||
	pdch_ul_controller.c \
 | 
			
		||||
	encoding.cpp \
 | 
			
		||||
	sba.c \
 | 
			
		||||
	decoding.cpp \
 | 
			
		||||
	llc.cpp \
 | 
			
		||||
	rlc.cpp \
 | 
			
		||||
	osmobts_sock.c \
 | 
			
		||||
	gprs_codel.c \
 | 
			
		||||
	coding_scheme.c \
 | 
			
		||||
	egprs_rlc_compression.cpp \
 | 
			
		||||
	gprs_rlcmac_sched.cpp
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS = \
 | 
			
		||||
	osmo-pcu
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS =
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	gprs_debug.h \
 | 
			
		||||
	csn1.h \
 | 
			
		||||
	gsm_rlcmac.h \
 | 
			
		||||
	gprs_bssgp_pcu.h \
 | 
			
		||||
	gprs_bssgp_rim.h \
 | 
			
		||||
	gprs_rlcmac.h \
 | 
			
		||||
	pcuif_proto.h \
 | 
			
		||||
	gprs_ms.h \
 | 
			
		||||
	gprs_ms_storage.h \
 | 
			
		||||
	gprs_pcu.h \
 | 
			
		||||
	pcu_l1_if.h \
 | 
			
		||||
	gsm_timer.h \
 | 
			
		||||
	bitvector.h \
 | 
			
		||||
	pcu_vty.h
 | 
			
		||||
 | 
			
		||||
RLCMACTest_SOURCES = RLCMACTest.cpp
 | 
			
		||||
RLCMACTest_LDADD = \
 | 
			
		||||
	libgprs.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
	pcu_vty.h \
 | 
			
		||||
	pcu_vty_functions.h \
 | 
			
		||||
	mslot_class.h \
 | 
			
		||||
	nacc_fsm.h \
 | 
			
		||||
	neigh_cache.h \
 | 
			
		||||
	tbf.h \
 | 
			
		||||
	tbf_fsm.h \
 | 
			
		||||
	tbf_ul.h \
 | 
			
		||||
	tbf_dl.h \
 | 
			
		||||
	bts.h \
 | 
			
		||||
	bts_anr_fsm.h \
 | 
			
		||||
	ms_anr_fsm.h \
 | 
			
		||||
	pdch.h \
 | 
			
		||||
	pdch_ul_controller.h \
 | 
			
		||||
	encoding.h \
 | 
			
		||||
	sba.h \
 | 
			
		||||
	rlc.h \
 | 
			
		||||
	decoding.h \
 | 
			
		||||
	llc.h \
 | 
			
		||||
	pcu_utils.h \
 | 
			
		||||
	cxx_linuxlist.h \
 | 
			
		||||
	gprs_codel.h \
 | 
			
		||||
	coding_scheme.h \
 | 
			
		||||
	egprs_rlc_compression.h \
 | 
			
		||||
	wireshark_compat.h
 | 
			
		||||
 | 
			
		||||
osmo_pcu_SOURCES = pcu_main.cpp
 | 
			
		||||
 | 
			
		||||
if ENABLE_SYSMODSP
 | 
			
		||||
AM_CPPFLAGS += -I$(srcdir)/osmo-bts-sysmo -I$(SYSMOBTS_INCDIR)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	osmo-bts-sysmo/sysmo_l1_if.c \
 | 
			
		||||
        osmo-bts-sysmo/sysmo_l1_if.h \
 | 
			
		||||
        osmo-bts-sysmo/sysmo_l1_hw.c \
 | 
			
		||||
        osmo-bts-sysmo/femtobts.c \
 | 
			
		||||
        osmo-bts-sysmo/femtobts.h
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS += \
 | 
			
		||||
        osmo-bts-sysmo/sysmo_l1_if.h \
 | 
			
		||||
        osmo-bts-sysmo/femtobts.h
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS += \
 | 
			
		||||
	osmo-pcu-remote
 | 
			
		||||
 | 
			
		||||
osmo_pcu_SOURCES += \
 | 
			
		||||
	osmo-bts-sysmo/sysmo_l1_if.c \
 | 
			
		||||
	osmo-bts-sysmo/sysmo_l1_hw.c \
 | 
			
		||||
	osmo-bts-sysmo/femtobts.c
 | 
			
		||||
 | 
			
		||||
osmo_pcu_remote_SOURCES = \
 | 
			
		||||
	pcu_main.cpp \
 | 
			
		||||
	osmo-bts-sysmo/sysmo_l1_if.c \
 | 
			
		||||
	osmo-bts-sysmo/sysmo_l1_fwd.c \
 | 
			
		||||
	osmo-bts-sysmo/femtobts.c
 | 
			
		||||
 | 
			
		||||
osmo_pcu_remote_LDADD = \
 | 
			
		||||
	libgprs.la \
 | 
			
		||||
	$(LIBOSMOGB_LIBS) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if ENABLE_LC15BTS_PHY
 | 
			
		||||
AM_CPPFLAGS += $(LITECELL15_CFLAGS) -I$(srcdir)/osmo-bts-litecell15
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	osmo-bts-litecell15/lc15_l1_if.c \
 | 
			
		||||
        osmo-bts-litecell15/lc15_l1_if.h \
 | 
			
		||||
        osmo-bts-litecell15/lc15_l1_hw.c \
 | 
			
		||||
        osmo-bts-litecell15/lc15bts.c \
 | 
			
		||||
        osmo-bts-litecell15/lc15bts.h
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS += \
 | 
			
		||||
        osmo-bts-litecell15/lc15_l1_if.h \
 | 
			
		||||
        osmo-bts-litecell15/lc15bts.h
 | 
			
		||||
 | 
			
		||||
osmo_pcu_SOURCES += \
 | 
			
		||||
	osmo-bts-litecell15/lc15_l1_if.c \
 | 
			
		||||
	osmo-bts-litecell15/lc15_l1_hw.c \
 | 
			
		||||
	osmo-bts-litecell15/lc15bts.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if ENABLE_OC2GBTS_PHY
 | 
			
		||||
AM_CPPFLAGS += -I$(OC2G_INCDIR) -I$(srcdir)/osmo-bts-oc2g
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	osmo-bts-oc2g/oc2g_l1_if.c \
 | 
			
		||||
        osmo-bts-oc2g/oc2g_l1_if.h \
 | 
			
		||||
        osmo-bts-oc2g/oc2g_l1_hw.c \
 | 
			
		||||
        osmo-bts-oc2g/oc2gbts.c \
 | 
			
		||||
        osmo-bts-oc2g/oc2gbts.h
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS += \
 | 
			
		||||
        osmo-bts-oc2g/oc2g_l1_if.h \
 | 
			
		||||
        osmo-bts-oc2g/oc2gbts.h
 | 
			
		||||
 | 
			
		||||
osmo_pcu_SOURCES += \
 | 
			
		||||
	osmo-bts-oc2g/oc2g_l1_if.c \
 | 
			
		||||
	osmo-bts-oc2g/oc2g_l1_hw.c \
 | 
			
		||||
	osmo-bts-oc2g/oc2gbts.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
osmo_pcu_LDADD = \
 | 
			
		||||
	libgprs.la \
 | 
			
		||||
	$(LIBOSMOGB_LIBS) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,205 +0,0 @@
 | 
			
		||||
/* RLCMACTest.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2011 Ivan Klyuchnikov
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//#include <BitVector.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include "csn1.h"
 | 
			
		||||
#include "gsm_rlcmac.h"
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
void printSizeofRLCMAC()
 | 
			
		||||
{
 | 
			
		||||
	cout << "sizeof RlcMacUplink_t                       " << sizeof(RlcMacUplink_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Cell_Change_Failure_t         " << sizeof(Packet_Cell_Change_Failure_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Control_Acknowledgement_t     " << sizeof(Packet_Control_Acknowledgement_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Downlink_Ack_Nack_t           " << sizeof(Packet_Downlink_Ack_Nack_t) << endl;
 | 
			
		||||
	cout << "sizeof EGPRS_PD_AckNack_t		     " << sizeof(EGPRS_PD_AckNack_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Uplink_Dummy_Control_Block_t  " << sizeof(Packet_Uplink_Dummy_Control_Block_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Measurement_Report_t          " << sizeof(Packet_Measurement_Report_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Resource_Request_t            " << sizeof(Packet_Resource_Request_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Mobile_TBF_Status_t           " << sizeof(Packet_Mobile_TBF_Status_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_PSI_Status_t                  " << sizeof(Packet_PSI_Status_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Enh_Measurement_Report_t      " << sizeof(Packet_Enh_Measurement_Report_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Cell_Change_Notification_t    " << sizeof(Packet_Cell_Change_Notification_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_SI_Status_t                   " << sizeof(Packet_SI_Status_t) << endl;
 | 
			
		||||
	cout << "sizeof Additional_MS_Rad_Access_Cap_t       " << sizeof(Additional_MS_Rad_Access_Cap_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Pause_t                       " << sizeof(Packet_Pause_t) << endl;
 | 
			
		||||
 | 
			
		||||
	cout << "sizeof RlcMacDownlink_t                       " << sizeof(RlcMacDownlink_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Access_Reject_t                 " << sizeof(Packet_Access_Reject_t) << endl;
 | 
			
		||||
       	cout << "sizeof Packet_Cell_Change_Order_t             " << sizeof(Packet_Cell_Change_Order_t) << endl;
 | 
			
		||||
       	cout << "sizeof Packet_Downlink_Assignment_t           " << sizeof(Packet_Downlink_Assignment_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Measurement_Order_Reduced_t     " << sizeof(Packet_Measurement_Order_Reduced_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Neighbour_Cell_Data_t           " << sizeof(Packet_Neighbour_Cell_Data_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Serving_Cell_Data_t             " << sizeof(Packet_Serving_Cell_Data_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Paging_Request_t                " << sizeof(Packet_Paging_Request_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_PDCH_Release_t                  " << sizeof(Packet_PDCH_Release_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Polling_Request_t               " << sizeof(Packet_Polling_Request_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Power_Control_Timing_Advance_t  " << sizeof(Packet_Power_Control_Timing_Advance_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_PRACH_Parameters_t              " << sizeof(Packet_PRACH_Parameters_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Queueing_Notification_t         " << sizeof(Packet_Queueing_Notification_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Timeslot_Reconfigure_t          " << sizeof(Packet_Timeslot_Reconfigure_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_TBF_Release_t                   " << sizeof(Packet_TBF_Release_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Uplink_Ack_Nack_t               " << sizeof(Packet_Uplink_Ack_Nack_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Uplink_Assignment_t             " << sizeof(Packet_Uplink_Assignment_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Cell_Change_Continue_t          " << sizeof(Packet_Cell_Change_Continue_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Handover_Command_t              " << sizeof(Packet_Handover_Command_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_PhysicalInformation_t           " << sizeof(Packet_PhysicalInformation_t) << endl;
 | 
			
		||||
	cout << "sizeof Packet_Downlink_Dummy_Control_Block_t  " << sizeof(Packet_Downlink_Dummy_Control_Block_t) << endl;
 | 
			
		||||
	cout << "sizeof PSI1_t                " << sizeof(PSI1_t) << endl;
 | 
			
		||||
	cout << "sizeof PSI2_t                " << sizeof(PSI2_t) << endl;
 | 
			
		||||
	cout << "sizeof PSI3_t                " << sizeof(PSI3_t) << endl;
 | 
			
		||||
	cout << "sizeof PSI3_BIS_t            " << sizeof(PSI3_BIS_t) << endl;
 | 
			
		||||
	cout << "sizeof PSI4_t                " << sizeof(PSI4_t) << endl;
 | 
			
		||||
	cout << "sizeof PSI13_t               " << sizeof(PSI13_t) << endl;
 | 
			
		||||
	cout << "sizeof PSI5_t                " << sizeof(PSI5_t) << endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void testRlcMacDownlink()
 | 
			
		||||
{
 | 
			
		||||
	struct bitvec *resultVector = bitvec_alloc(23);
 | 
			
		||||
	bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
 | 
			
		||||
 | 
			
		||||
	std::string testData[] = {
 | 
			
		||||
	"4e082500e3f1a81d080820800b2b2b2b2b2b2b2b2b2b2b", // Packet Downlink Assignment
 | 
			
		||||
	"48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
 | 
			
		||||
	"47240c00400000000000000079eb2ac9402b2b2b2b2b2b", // Packet Uplink Ack Nack
 | 
			
		||||
	"47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b"  // Packet Uplink Assignment
 | 
			
		||||
	"4913e00850884013a8048b2b2b2b2b2b2b2b2b2b2b2b2b"
 | 
			
		||||
	"412430007fffffffffffffffefd19c7ba12b2b2b2b2b2b"
 | 
			
		||||
	"41942b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	int testDataSize = sizeof(testData)/sizeof(testData[0]);
 | 
			
		||||
 | 
			
		||||
	cout << " DOWNLINK " << endl;
 | 
			
		||||
	for (int i = 0; i < testDataSize; i++)
 | 
			
		||||
	{
 | 
			
		||||
		bitvec *vector = bitvec_alloc(23);
 | 
			
		||||
		bitvec_unhex(vector, testData[i].c_str());
 | 
			
		||||
		cout << "vector1 = ";
 | 
			
		||||
		for (int i = 0; i < 23; i++)
 | 
			
		||||
		{
 | 
			
		||||
			cout << hex << (unsigned)*(vector->data + i);
 | 
			
		||||
		}
 | 
			
		||||
		cout << endl;
 | 
			
		||||
		RlcMacDownlink_t * data = (RlcMacDownlink_t *)malloc(sizeof(RlcMacDownlink_t));
 | 
			
		||||
		cout << "=========Start DECODE===========" << endl;
 | 
			
		||||
		decode_gsm_rlcmac_downlink(vector, data);
 | 
			
		||||
		cout << "+++++++++Finish DECODE++++++++++" << endl;
 | 
			
		||||
		cout << "=========Start ENCODE=============" << endl;
 | 
			
		||||
		encode_gsm_rlcmac_downlink(resultVector, data);
 | 
			
		||||
		cout << "+++++++++Finish ENCODE+++++++++++" << endl;
 | 
			
		||||
		cout << "vector1 = ";
 | 
			
		||||
		for (int i = 0; i < 23; i++)
 | 
			
		||||
		{
 | 
			
		||||
			cout << (unsigned)*(vector->data + i);
 | 
			
		||||
		}
 | 
			
		||||
		cout << endl;
 | 
			
		||||
		cout << "vector2 = ";
 | 
			
		||||
		for (int i = 0; i < 23; i++)
 | 
			
		||||
		{
 | 
			
		||||
			cout << (unsigned)*(resultVector->data + i);
 | 
			
		||||
		}
 | 
			
		||||
		cout << endl;
 | 
			
		||||
		if (memcmp(vector->data, resultVector->data, 23) == 0)
 | 
			
		||||
		{
 | 
			
		||||
			cout << "vector1 == vector2 : TRUE" << endl;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			cout << "vector1 == vector2 : FALSE" << endl;
 | 
			
		||||
		}
 | 
			
		||||
		bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
 | 
			
		||||
		bitvec_free(vector);
 | 
			
		||||
		free(data);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void testRlcMacUplink()
 | 
			
		||||
{
 | 
			
		||||
	struct bitvec *resultVector = bitvec_alloc(23);
 | 
			
		||||
	bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
 | 
			
		||||
 | 
			
		||||
	std::string testData[] = {
 | 
			
		||||
	"400e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Dummy Control Block
 | 
			
		||||
	"400b8020000000000000002480e00b2b2b2b2b2b2b2b2b", // Packet Downlink Ack/Nack
 | 
			
		||||
	"4016713dc094270ca2ae57ef909006aa0fc0001f80222b"  // Packet Resource Request
 | 
			
		||||
	"400a9020000000000000003010012a0800132b2b2b2b2b"
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	int testDataSize = sizeof(testData)/sizeof(testData[0]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	cout << " UPLINK " << endl;
 | 
			
		||||
	for (int i = 0; i < testDataSize; i++)
 | 
			
		||||
	{
 | 
			
		||||
		bitvec *vector = bitvec_alloc(23);
 | 
			
		||||
		bitvec_unhex(vector, testData[i].c_str());
 | 
			
		||||
		cout << "vector1 = ";
 | 
			
		||||
		for (int i = 0; i < 23; i++)
 | 
			
		||||
		{
 | 
			
		||||
			cout << hex << (unsigned)*(vector->data + i);
 | 
			
		||||
		}
 | 
			
		||||
		cout << endl;
 | 
			
		||||
		RlcMacUplink_t * data = (RlcMacUplink_t *)malloc(sizeof(RlcMacUplink_t));
 | 
			
		||||
		cout << "=========Start DECODE===========" << endl;
 | 
			
		||||
		decode_gsm_rlcmac_uplink(vector, data);
 | 
			
		||||
		cout << "+++++++++Finish DECODE++++++++++" << endl;
 | 
			
		||||
		cout << "=========Start ENCODE=============" << endl;
 | 
			
		||||
		encode_gsm_rlcmac_uplink(resultVector, data);
 | 
			
		||||
		cout << "+++++++++Finish ENCODE+++++++++++" << endl;
 | 
			
		||||
		cout << "vector1 = ";
 | 
			
		||||
		for (int i = 0; i < 23; i++)
 | 
			
		||||
		{
 | 
			
		||||
			cout << (unsigned)*(vector->data + i);
 | 
			
		||||
		}
 | 
			
		||||
		cout << endl;
 | 
			
		||||
		cout << "vector2 = ";
 | 
			
		||||
		for (int i = 0; i < 23; i++)
 | 
			
		||||
		{
 | 
			
		||||
			cout << (unsigned)*(resultVector->data + i);
 | 
			
		||||
		}
 | 
			
		||||
		cout << endl;
 | 
			
		||||
		if (memcmp(vector->data, resultVector->data, 23) == 0)
 | 
			
		||||
		{
 | 
			
		||||
			cout << "vector1 == vector2 : TRUE" << endl;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			cout << "vector1 == vector2 : FALSE" << endl;
 | 
			
		||||
		}
 | 
			
		||||
		bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
 | 
			
		||||
		bitvec_free(vector);
 | 
			
		||||
		free(data);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	//printSizeofRLCMAC();
 | 
			
		||||
	testRlcMacDownlink();
 | 
			
		||||
	testRlcMacUplink();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,118 +0,0 @@
 | 
			
		||||
/* bitvector.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*! \addtogroup bitvector
 | 
			
		||||
 *  @{
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*! \file bitvector.cpp
 | 
			
		||||
 *  \brief Additional functions for Osmocom bit vector abstraction.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <bitvector.h>
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *bv_tall_ctx;
 | 
			
		||||
 | 
			
		||||
struct bitvec *bitvec_alloc(unsigned size)
 | 
			
		||||
{
 | 
			
		||||
	struct bitvec *bv = talloc_zero(bv_tall_ctx, struct bitvec);
 | 
			
		||||
	bv->data_len = size;
 | 
			
		||||
	bv->cur_bit = 0;
 | 
			
		||||
	bv->data = talloc_zero_array(bv_tall_ctx, uint8_t, size);
 | 
			
		||||
	return bv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bitvec_free(struct bitvec *bv)
 | 
			
		||||
{
 | 
			
		||||
	talloc_free(bv->data);
 | 
			
		||||
	talloc_free(bv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bitvec_pack(struct bitvec *bv, uint8_t *buffer)
 | 
			
		||||
{
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	for (i = 0; i < bv->data_len; i++)
 | 
			
		||||
	{
 | 
			
		||||
		buffer[i] = bv->data[i];
 | 
			
		||||
	}
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bitvec_unpack(struct bitvec *bv, uint8_t *buffer)
 | 
			
		||||
{
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	for (i = 0; i < bv->data_len; i++)
 | 
			
		||||
	{
 | 
			
		||||
		bv->data[i] = buffer[i];
 | 
			
		||||
	}
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int bitvec_unhex(struct bitvec *bv, const char* src)
 | 
			
		||||
{
 | 
			
		||||
	unsigned val;
 | 
			
		||||
	unsigned write_index = 0;
 | 
			
		||||
	unsigned digits = bv->data_len*2;
 | 
			
		||||
	for (unsigned i=0; i<digits; i++) {
 | 
			
		||||
		if (sscanf(src+i, "%1x", &val) < 1) {
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
		bitvec_write_field(bv, write_index,val, 4);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	uint64_t ui = 0;
 | 
			
		||||
	bv->cur_bit = read_index;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < len; i++) {
 | 
			
		||||
		int bit = bitvec_get_bit_pos((const struct bitvec *)bv, bv->cur_bit);
 | 
			
		||||
		if (bit < 0)
 | 
			
		||||
			return bit;
 | 
			
		||||
		if (bit)
 | 
			
		||||
			ui |= ((uint64_t)1 << (len - i - 1));
 | 
			
		||||
		bv->cur_bit++;
 | 
			
		||||
	}
 | 
			
		||||
	read_index += len;
 | 
			
		||||
	return ui;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len)
 | 
			
		||||
{
 | 
			
		||||
	int i, rc;
 | 
			
		||||
	bv->cur_bit = write_index;
 | 
			
		||||
	for (i = 0; i < len; i++) {
 | 
			
		||||
		int bit = 0;
 | 
			
		||||
		if (val & ((uint64_t)1 << (len - i - 1)))
 | 
			
		||||
			bit = 1;
 | 
			
		||||
		rc = bitvec_set_bit(bv, (bit_value)bit);
 | 
			
		||||
		if (rc)
 | 
			
		||||
			return rc;
 | 
			
		||||
	}
 | 
			
		||||
	write_index += len;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1342
									
								
								src/bts.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1342
									
								
								src/bts.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										370
									
								
								src/bts.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										370
									
								
								src/bts.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,370 @@
 | 
			
		||||
/* bts.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/stat_item.h>
 | 
			
		||||
#include <osmocom/core/tdef.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns2.h>
 | 
			
		||||
#include <osmocom/gsm/l1sap.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
#include <osmocom/gsm/gsm48_rest_octets.h>
 | 
			
		||||
#include <osmocom/gsm/gsm48.h>
 | 
			
		||||
#include "mslot_class.h"
 | 
			
		||||
#include "gsm_rlcmac.h"
 | 
			
		||||
#include "gprs_pcu.h"
 | 
			
		||||
#include "bts_anr_fsm.h"
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
#include "coding_scheme.h"
 | 
			
		||||
 | 
			
		||||
struct GprsMs;
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_trx {
 | 
			
		||||
	void *fl1h;
 | 
			
		||||
	uint16_t arfcn;
 | 
			
		||||
	struct gprs_rlcmac_pdch pdch[8];
 | 
			
		||||
 | 
			
		||||
	/* back pointers */
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
	uint8_t trx_no;
 | 
			
		||||
 | 
			
		||||
	/* list of uplink TBFs */
 | 
			
		||||
	struct llist_head ul_tbfs; /* list of gprs_rlcmac_tbf */
 | 
			
		||||
	/* list of downlink TBFs */
 | 
			
		||||
	struct llist_head dl_tbfs; /* list of gprs_rlcmac_tbf */
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
void bts_trx_init(struct gprs_rlcmac_trx *trx, struct gprs_rlcmac_bts *bts, uint8_t trx_no);
 | 
			
		||||
void bts_trx_reserve_slots(struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
 | 
			
		||||
void bts_trx_unreserve_slots(struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
 | 
			
		||||
void bts_trx_free_all_tbf(struct gprs_rlcmac_trx *trx);
 | 
			
		||||
 | 
			
		||||
void bts_update_tbf_ta(struct gprs_rlcmac_bts *bts, const char *p, uint32_t fn,
 | 
			
		||||
		       uint8_t trx_no, uint8_t ts, int8_t ta, bool is_rach);
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	CTR_TBF_DL_ALLOCATED,
 | 
			
		||||
	CTR_TBF_DL_FREED,
 | 
			
		||||
	CTR_TBF_DL_ABORTED,
 | 
			
		||||
	CTR_TBF_UL_ALLOCATED,
 | 
			
		||||
	CTR_TBF_UL_FREED,
 | 
			
		||||
	CTR_TBF_UL_ABORTED,
 | 
			
		||||
	CTR_TBF_REUSED,
 | 
			
		||||
	CTR_TBF_ALLOC_ALGO_A,
 | 
			
		||||
	CTR_TBF_ALLOC_ALGO_B,
 | 
			
		||||
	CTR_TBF_ALLOC_FAIL,
 | 
			
		||||
	CTR_TBF_ALLOC_FAIL_NO_TFI,
 | 
			
		||||
	CTR_TBF_ALLOC_FAIL_NO_USF,
 | 
			
		||||
	CTR_TBF_ALLOC_FAIL_NO_SLOT_COMBI,
 | 
			
		||||
	CTR_TBF_ALLOC_FAIL_NO_SLOT_AVAIL,
 | 
			
		||||
	CTR_RLC_SENT,
 | 
			
		||||
	CTR_RLC_RESENT,
 | 
			
		||||
	CTR_RLC_RESTARTED,
 | 
			
		||||
	CTR_RLC_STALLED,
 | 
			
		||||
	CTR_RLC_NACKED,
 | 
			
		||||
	CTR_RLC_FINAL_BLOCK_RESENT,
 | 
			
		||||
	CTR_RLC_ASS_TIMEDOUT,
 | 
			
		||||
	CTR_RLC_ASS_FAILED,
 | 
			
		||||
	CTR_RLC_ACK_TIMEDOUT,
 | 
			
		||||
	CTR_RLC_ACK_FAILED,
 | 
			
		||||
	CTR_RLC_REL_TIMEDOUT,
 | 
			
		||||
	CTR_RLC_LATE_BLOCK,
 | 
			
		||||
	CTR_RLC_SENT_DUMMY,
 | 
			
		||||
	CTR_RLC_SENT_CONTROL,
 | 
			
		||||
	CTR_RLC_DL_BYTES,
 | 
			
		||||
	CTR_RLC_DL_PAYLOAD_BYTES,
 | 
			
		||||
	CTR_RLC_UL_BYTES,
 | 
			
		||||
	CTR_RLC_UL_PAYLOAD_BYTES,
 | 
			
		||||
	CTR_DECODE_ERRORS,
 | 
			
		||||
	CTR_SBA_ALLOCATED,
 | 
			
		||||
	CTR_SBA_FREED,
 | 
			
		||||
	CTR_SBA_TIMEDOUT,
 | 
			
		||||
	CTR_LLC_FRAME_TIMEDOUT,
 | 
			
		||||
	CTR_LLC_FRAME_DROPPED,
 | 
			
		||||
	CTR_LLC_FRAME_SCHED,
 | 
			
		||||
	CTR_LLC_DL_BYTES,
 | 
			
		||||
	CTR_LLC_UL_BYTES,
 | 
			
		||||
	CTR_RACH_REQUESTS,
 | 
			
		||||
	CTR_11BIT_RACH_REQUESTS,
 | 
			
		||||
	CTR_SPB_UL_FIRST_SEGMENT,
 | 
			
		||||
	CTR_SPB_UL_SECOND_SEGMENT,
 | 
			
		||||
	CTR_SPB_DL_FIRST_SEGMENT,
 | 
			
		||||
	CTR_SPB_DL_SECOND_SEGMENT,
 | 
			
		||||
	CTR_IMMEDIATE_ASSIGN_UL_TBF,
 | 
			
		||||
	CTR_IMMEDIATE_ASSIGN_REJ,
 | 
			
		||||
	CTR_IMMEDIATE_ASSIGN_DL_TBF,
 | 
			
		||||
	CTR_CHANNEL_REQUEST_DESCRIPTION,
 | 
			
		||||
	CTR_PKT_UL_ASSIGNMENT,
 | 
			
		||||
	CTR_PKT_ACCESS_REJ,
 | 
			
		||||
	CTR_PKT_DL_ASSIGNMENT,
 | 
			
		||||
	CTR_PKT_CELL_CHG_NOTIFICATION,
 | 
			
		||||
	CTR_PKT_CELL_CHG_CONTINUE,
 | 
			
		||||
	CTR_PKT_NEIGH_CELL_DATA,
 | 
			
		||||
	CTR_PKT_MEAS_ORDER,
 | 
			
		||||
	CTR_RLC_RECV_CONTROL,
 | 
			
		||||
	CTR_PUA_POLL_TIMEDOUT,
 | 
			
		||||
	CTR_PUA_POLL_FAILED,
 | 
			
		||||
	CTR_PDA_POLL_TIMEDOUT,
 | 
			
		||||
	CTR_PDA_POLL_FAILED,
 | 
			
		||||
	CTR_PUAN_POLL_TIMEDOUT,
 | 
			
		||||
	CTR_PUAN_POLL_FAILED,
 | 
			
		||||
	CTR_PDAN_POLL_TIMEDOUT,
 | 
			
		||||
	CTR_PDAN_POLL_FAILED,
 | 
			
		||||
	CTR_GPRS_DL_CS1,
 | 
			
		||||
	CTR_GPRS_DL_CS2,
 | 
			
		||||
	CTR_GPRS_DL_CS3,
 | 
			
		||||
	CTR_GPRS_DL_CS4,
 | 
			
		||||
	CTR_EGPRS_DL_MCS1,
 | 
			
		||||
	CTR_EGPRS_DL_MCS2,
 | 
			
		||||
	CTR_EGPRS_DL_MCS3,
 | 
			
		||||
	CTR_EGPRS_DL_MCS4,
 | 
			
		||||
	CTR_EGPRS_DL_MCS5,
 | 
			
		||||
	CTR_EGPRS_DL_MCS6,
 | 
			
		||||
	CTR_EGPRS_DL_MCS7,
 | 
			
		||||
	CTR_EGPRS_DL_MCS8,
 | 
			
		||||
	CTR_EGPRS_DL_MCS9,
 | 
			
		||||
	CTR_GPRS_UL_CS1,
 | 
			
		||||
	CTR_GPRS_UL_CS2,
 | 
			
		||||
	CTR_GPRS_UL_CS3,
 | 
			
		||||
	CTR_GPRS_UL_CS4,
 | 
			
		||||
	CTR_EGPRS_UL_MCS1,
 | 
			
		||||
	CTR_EGPRS_UL_MCS2,
 | 
			
		||||
	CTR_EGPRS_UL_MCS3,
 | 
			
		||||
	CTR_EGPRS_UL_MCS4,
 | 
			
		||||
	CTR_EGPRS_UL_MCS5,
 | 
			
		||||
	CTR_EGPRS_UL_MCS6,
 | 
			
		||||
	CTR_EGPRS_UL_MCS7,
 | 
			
		||||
	CTR_EGPRS_UL_MCS8,
 | 
			
		||||
	CTR_EGPRS_UL_MCS9,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	STAT_MS_PRESENT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* RACH.ind parameters (to be parsed) */
 | 
			
		||||
struct rach_ind_params {
 | 
			
		||||
	enum ph_burst_type burst_type;
 | 
			
		||||
	bool is_11bit;
 | 
			
		||||
	uint16_t ra;
 | 
			
		||||
	uint8_t trx_nr;
 | 
			
		||||
	uint8_t ts_nr;
 | 
			
		||||
	uint32_t rfn;
 | 
			
		||||
	int16_t qta;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* [EGPRS Packet] Channel Request parameters (parsed) */
 | 
			
		||||
struct chan_req_params {
 | 
			
		||||
	unsigned int egprs_mslot_class;
 | 
			
		||||
	unsigned int priority;
 | 
			
		||||
	bool single_block;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GprsMsStorage;
 | 
			
		||||
struct pcu_l1_meas;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * I represent a GSM BTS. I have one or more TRX, I know the current
 | 
			
		||||
 * GSM time and I have controllers that help with allocating resources
 | 
			
		||||
 * on my TRXs.
 | 
			
		||||
 */
 | 
			
		||||
struct gprs_rlcmac_bts {
 | 
			
		||||
	uint8_t nr; /* bts_nr */
 | 
			
		||||
	struct llist_head list; /* queued in pcu->bts_list */
 | 
			
		||||
	bool active;
 | 
			
		||||
	struct osmo_cell_global_id_ps cgi_ps;
 | 
			
		||||
	uint8_t bsic;
 | 
			
		||||
	uint8_t cs_mask; /* Allowed CS mask from BTS */
 | 
			
		||||
	uint16_t mcs_mask;  /* Allowed MCS mask from BTS */
 | 
			
		||||
	struct { /* information stored from last received PCUIF info_ind message */
 | 
			
		||||
		uint8_t initial_cs;
 | 
			
		||||
		uint8_t initial_mcs;
 | 
			
		||||
	} pcuif_info_ind;
 | 
			
		||||
	uint8_t initial_cs_dl, initial_cs_ul;
 | 
			
		||||
	uint8_t initial_mcs_dl, initial_mcs_ul;
 | 
			
		||||
	/* Timer defintions */
 | 
			
		||||
	struct osmo_tdef *T_defs_bts; /* timers controlled by BTS, received through PCUIF */
 | 
			
		||||
	uint8_t n3101;
 | 
			
		||||
	uint8_t n3103;
 | 
			
		||||
	uint8_t n3105;
 | 
			
		||||
	struct gprs_rlcmac_trx trx[8];
 | 
			
		||||
 | 
			
		||||
	uint8_t si1[GSM_MACBLOCK_LEN];
 | 
			
		||||
	bool si1_is_set;
 | 
			
		||||
	uint8_t si2[GSM_MACBLOCK_LEN];
 | 
			
		||||
	bool si2_is_set;
 | 
			
		||||
	struct gsm_sysinfo_freq si2_bcch_cell_list[1024];
 | 
			
		||||
	uint8_t si3[GSM_MACBLOCK_LEN];
 | 
			
		||||
	bool si3_is_set;
 | 
			
		||||
	uint8_t si13[GSM_MACBLOCK_LEN];
 | 
			
		||||
	struct osmo_gsm48_si13_info si13_ro_decoded;
 | 
			
		||||
	bool si13_is_set;
 | 
			
		||||
 | 
			
		||||
	/* State for dynamic algorithm selection */
 | 
			
		||||
	int multislot_disabled;
 | 
			
		||||
 | 
			
		||||
	/* Packet Application Information (3GPP TS 44.060 11.2.47, usually ETWS primary message). We don't need to store
 | 
			
		||||
	 * more than one message, because they get sent so rarely. */
 | 
			
		||||
	struct msgb *app_info;
 | 
			
		||||
	uint32_t app_info_pending; /* Count of MS with active TBF, to which we did not send app_info yet */
 | 
			
		||||
 | 
			
		||||
	/* main nsei */
 | 
			
		||||
	struct gprs_ns2_nse *nse;
 | 
			
		||||
 | 
			
		||||
	/* back pointer to PCU object */
 | 
			
		||||
	struct gprs_pcu *pcu;
 | 
			
		||||
 | 
			
		||||
	uint32_t cur_fn;
 | 
			
		||||
	int cur_blk_fn;
 | 
			
		||||
	uint8_t max_cs_dl, max_cs_ul;
 | 
			
		||||
	uint8_t max_mcs_dl, max_mcs_ul;
 | 
			
		||||
	struct rate_ctr_group *ratectrs;
 | 
			
		||||
	struct osmo_stat_item_group *statg;
 | 
			
		||||
 | 
			
		||||
	struct GprsMsStorage *ms_store;
 | 
			
		||||
 | 
			
		||||
	struct bts_anr_fsm_ctx *anr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct paging_req_cs {
 | 
			
		||||
	uint8_t chan_needed;
 | 
			
		||||
	uint32_t tlli; /* GSM_RESERVED_TMSI if not present */
 | 
			
		||||
	bool mi_tmsi_present;
 | 
			
		||||
	struct osmo_mobile_identity mi_tmsi;
 | 
			
		||||
	bool mi_imsi_present;
 | 
			
		||||
	struct osmo_mobile_identity mi_imsi;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GprsMs *bts_alloc_ms(struct gprs_rlcmac_bts *bts, uint8_t ms_class, uint8_t egprs_ms_class);
 | 
			
		||||
int bts_add_paging(struct gprs_rlcmac_bts *bts, const struct paging_req_cs *req, struct GprsMs *ms);
 | 
			
		||||
 | 
			
		||||
uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, int32_t rfn);
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
 | 
			
		||||
struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
 | 
			
		||||
 | 
			
		||||
void bts_snd_dl_ass(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, uint16_t pgroup);
 | 
			
		||||
 | 
			
		||||
void bts_set_current_frame_number(struct gprs_rlcmac_bts *bts, uint32_t frame_number);
 | 
			
		||||
void bts_set_current_block_frame_number(struct gprs_rlcmac_bts *bts, int frame_number);
 | 
			
		||||
static inline uint32_t bts_current_frame_number(const struct gprs_rlcmac_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	return bts->cur_fn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bts_tfi_find_free(const struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_direction dir,
 | 
			
		||||
		      uint8_t *_trx, int8_t use_trx);
 | 
			
		||||
 | 
			
		||||
int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
 | 
			
		||||
int bts_rcv_ptcch_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
 | 
			
		||||
int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t fn);
 | 
			
		||||
 | 
			
		||||
void bts_send_gsmtap(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
		     enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
 | 
			
		||||
		     uint8_t ts_no, uint8_t channel, uint32_t fn,
 | 
			
		||||
		     const uint8_t *data, unsigned int len);
 | 
			
		||||
void bts_send_gsmtap_meas(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
			  enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
 | 
			
		||||
			  uint8_t ts_no, uint8_t channel, uint32_t fn,
 | 
			
		||||
			  const uint8_t *data, unsigned int len, struct pcu_l1_meas *meas);
 | 
			
		||||
void bts_send_gsmtap_rach(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
			  enum pcu_gsmtap_category categ, uint8_t channel,
 | 
			
		||||
			  const struct rach_ind_params *rip);
 | 
			
		||||
 | 
			
		||||
struct GprsMsStorage *bts_ms_store(const struct gprs_rlcmac_bts *bts);
 | 
			
		||||
 | 
			
		||||
struct GprsMs *bts_ms_by_tlli(struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli);
 | 
			
		||||
 | 
			
		||||
static inline struct rate_ctr_group *bts_rate_counters(struct gprs_rlcmac_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	return bts->ratectrs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct osmo_stat_item_group *bts_stat_items(struct gprs_rlcmac_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	return bts->statg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void bts_do_rate_ctr_inc(const struct gprs_rlcmac_bts *bts, unsigned int ctr_id) {
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(bts->ratectrs, ctr_id));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void bts_do_rate_ctr_add(const struct gprs_rlcmac_bts *bts, unsigned int ctr_id, int inc) {
 | 
			
		||||
	rate_ctr_add(rate_ctr_group_get_ctr(bts->ratectrs, ctr_id), inc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void bts_stat_item_add(struct gprs_rlcmac_bts *bts, unsigned int stat_id, int inc) {
 | 
			
		||||
	struct osmo_stat_item *item = osmo_stat_item_group_get_item(bts->statg, stat_id);
 | 
			
		||||
	int32_t val = osmo_stat_item_get_last(item);
 | 
			
		||||
	osmo_stat_item_set(item, val + inc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts *bts_alloc(struct gprs_pcu *pcu, uint8_t bts_nr);
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_sba *bts_alloc_sba(struct gprs_rlcmac_bts *bts, uint8_t ta);
 | 
			
		||||
 | 
			
		||||
void bts_recalc_initial_cs(struct gprs_rlcmac_bts *bts);
 | 
			
		||||
void bts_recalc_initial_mcs(struct gprs_rlcmac_bts *bts);
 | 
			
		||||
void bts_recalc_max_cs(struct gprs_rlcmac_bts *bts);
 | 
			
		||||
void bts_recalc_max_mcs(struct gprs_rlcmac_bts *bts);
 | 
			
		||||
struct GprsMs *bts_ms_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi);
 | 
			
		||||
uint8_t bts_max_cs_dl(const struct gprs_rlcmac_bts *bts);
 | 
			
		||||
uint8_t bts_max_cs_ul(const struct gprs_rlcmac_bts *bts);
 | 
			
		||||
uint8_t bts_max_mcs_dl(const struct gprs_rlcmac_bts *bts);
 | 
			
		||||
uint8_t bts_max_mcs_ul(const struct gprs_rlcmac_bts *bts);
 | 
			
		||||
void bts_set_max_cs_dl(struct gprs_rlcmac_bts *bts, uint8_t cs_dl);
 | 
			
		||||
void bts_set_max_cs_ul(struct gprs_rlcmac_bts *bts, uint8_t cs_ul);
 | 
			
		||||
void bts_set_max_mcs_dl(struct gprs_rlcmac_bts *bts, uint8_t mcs_dl);
 | 
			
		||||
void bts_set_max_mcs_ul(struct gprs_rlcmac_bts *bts, uint8_t mcs_ul);
 | 
			
		||||
bool bts_cs_dl_is_supported(const struct gprs_rlcmac_bts *bts, enum CodingScheme cs);
 | 
			
		||||
const struct llist_head* bts_ms_list(struct gprs_rlcmac_bts *bts);
 | 
			
		||||
uint8_t bts_get_ms_pwr_alpha(const struct gprs_rlcmac_bts *bts);
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										388
									
								
								src/bts_anr_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								src/bts_anr_fsm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,388 @@
 | 
			
		||||
/* bts_anr_fsm.c
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <talloc.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/ctrl/control_cmd.h>
 | 
			
		||||
#include <osmocom/ctrl/control_if.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/gsm48.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp_rim.h>
 | 
			
		||||
 | 
			
		||||
#include <bts_anr_fsm.h>
 | 
			
		||||
#include <ms_anr_fsm.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <gprs_ms.h>
 | 
			
		||||
#include <encoding.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#include <neigh_cache.h>
 | 
			
		||||
 | 
			
		||||
#define X(s) (1 << (s))
 | 
			
		||||
 | 
			
		||||
/* Ask the MS to measure up to 5 neighbors at a time */
 | 
			
		||||
#define ANR_MAX_NEIGH_SUBSET 5
 | 
			
		||||
 | 
			
		||||
static const struct osmo_tdef_state_timeout bts_anr_fsm_timeouts[32] = {
 | 
			
		||||
	[BTS_ANR_ST_DISABLED] = {},
 | 
			
		||||
	[BTS_ANR_ST_ENABLED] = { .T = PCU_TDEF_ANR_SCHED_TBF },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
 | 
			
		||||
 * The actual timeout value is in turn obtained from conn->T_defs.
 | 
			
		||||
 * Assumes local variable fi exists. */
 | 
			
		||||
 | 
			
		||||
#define bts_anr_fsm_state_chg(fi, NEXT_STATE) \
 | 
			
		||||
	osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
 | 
			
		||||
				     bts_anr_fsm_timeouts, \
 | 
			
		||||
				     ((struct bts_anr_fsm_ctx*)(fi->priv))->bts->pcu->T_defs, \
 | 
			
		||||
				     -1)
 | 
			
		||||
 | 
			
		||||
const struct value_string bts_anr_fsm_event_names[] = {
 | 
			
		||||
	{ BTS_ANR_EV_RX_ANR_REQ, "RX_ANR_REQ" },
 | 
			
		||||
	{ BTS_ANR_EV_SCHED_MS_MEAS, "SCHED_MS_MEAS" },
 | 
			
		||||
	{ BTS_ANR_EV_MS_MEAS_COMPL, "MS_MEAS_COMPL" },
 | 
			
		||||
	{ BTS_ANR_EV_MS_MEAS_ABORTED, "MS_MEAS_ABORTED" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void copy_sort_arfcn_bsic(struct bts_anr_fsm_ctx *ctx, const struct gsm48_cell_desc *cell_list, unsigned int num_cells)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(num_cells <= ARRAY_SIZE(ctx->cell_list));
 | 
			
		||||
	uint16_t last_min_arfcn = 0;
 | 
			
		||||
	uint8_t last_min_bsic = 0;
 | 
			
		||||
	ctx->num_cells = 0;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = ctx->bts;
 | 
			
		||||
 | 
			
		||||
	/* Copy over ARFCN+BSIC in an ARFCN then BSIC ascending ordered way */
 | 
			
		||||
	while (ctx->num_cells < num_cells) {
 | 
			
		||||
		bool found = false;
 | 
			
		||||
		uint16_t curr_min_arfcn = 0xffff;
 | 
			
		||||
		uint8_t curr_min_bsic = 0xff;
 | 
			
		||||
		int i;
 | 
			
		||||
		for (i = 0; i < num_cells; i++) {
 | 
			
		||||
			uint16_t arfcn = (cell_list[i].arfcn_hi << 8) | cell_list[i].arfcn_lo;
 | 
			
		||||
			uint8_t bsic = (cell_list[i].ncc << 3) | cell_list[i].bcc;
 | 
			
		||||
			if ((arfcn > last_min_arfcn || (arfcn == last_min_arfcn && bsic > last_min_bsic)) &&
 | 
			
		||||
			    (arfcn < curr_min_arfcn || (arfcn == curr_min_arfcn && bsic < curr_min_bsic))) {
 | 
			
		||||
				found = true;
 | 
			
		||||
				curr_min_arfcn = arfcn;
 | 
			
		||||
				curr_min_bsic = bsic;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (!found)
 | 
			
		||||
			break; /* we are done before copying all, probably due to duplicated arfcn in list */
 | 
			
		||||
 | 
			
		||||
		/* Copy lower ARFCN+BSIC to dst */
 | 
			
		||||
		if (curr_min_arfcn != bts->trx[0].arfcn || curr_min_bsic != bts->bsic) {
 | 
			
		||||
			ctx->cell_list[ctx->num_cells] = (struct arfcn_bsic){
 | 
			
		||||
				.arfcn = curr_min_arfcn,
 | 
			
		||||
				.bsic =  curr_min_bsic,
 | 
			
		||||
			};
 | 
			
		||||
			ctx->num_cells++;
 | 
			
		||||
			LOGPFSML(ctx->fi, LOGL_DEBUG, "Added neigh cell to ANR list: ARFCN=%u BSIC=%u\n",
 | 
			
		||||
				 curr_min_arfcn, curr_min_bsic);
 | 
			
		||||
		} else {
 | 
			
		||||
			LOGPFSML(ctx->fi, LOGL_DEBUG, "Skip neigh cell to ANR list (itself): ARFCN=%u BSIC=%u\n",
 | 
			
		||||
				 curr_min_arfcn, curr_min_bsic);
 | 
			
		||||
		}
 | 
			
		||||
		last_min_arfcn = curr_min_arfcn;
 | 
			
		||||
		last_min_bsic = curr_min_bsic;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void rx_new_cell_list(struct bts_anr_fsm_ctx *ctx, const struct gsm_pcu_if_anr_req *anr_req)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int num_cells = anr_req->num_cells;
 | 
			
		||||
	if (anr_req->num_cells > ARRAY_SIZE(anr_req->cell_list)) {
 | 
			
		||||
		LOGPFSML(ctx->fi, LOGL_ERROR, "Too many cells received %u > %zu (max), trimming it\n",
 | 
			
		||||
			 anr_req->num_cells, ARRAY_SIZE(anr_req->cell_list));
 | 
			
		||||
		num_cells = ARRAY_SIZE(anr_req->cell_list);
 | 
			
		||||
	}
 | 
			
		||||
	copy_sort_arfcn_bsic(ctx, (const struct gsm48_cell_desc *)anr_req->cell_list, num_cells);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct GprsMs *select_candidate_ms(struct gprs_rlcmac_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	struct llist_head *tmp;
 | 
			
		||||
	/* prio top to bottom: 0,1,2: */
 | 
			
		||||
	struct GprsMs *ms_dl_tbf_assign = NULL;
 | 
			
		||||
 | 
			
		||||
	/* We'll need a DL TBF. Take with higher priority an MS which already
 | 
			
		||||
	 * has one, otherwise one in process of acquiring one. In last place an
 | 
			
		||||
	 * MS which has no DL-TBF yet. */
 | 
			
		||||
	llist_for_each(tmp, bts_ms_list(bts)) {
 | 
			
		||||
		struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
 | 
			
		||||
		if (ms->anr) /* Don't pick MS already busy doing ANR */
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!ms->dl_tbf)
 | 
			
		||||
			continue;
 | 
			
		||||
		switch (tbf_state((struct gprs_rlcmac_tbf*)ms->dl_tbf)) {
 | 
			
		||||
		case TBF_ST_FLOW: /* Pick active DL-TBF as best option, early return: */
 | 
			
		||||
			return ms;
 | 
			
		||||
		case TBF_ST_ASSIGN:
 | 
			
		||||
			ms_dl_tbf_assign = ms;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ms_dl_tbf_assign)
 | 
			
		||||
		return ms_dl_tbf_assign;
 | 
			
		||||
 | 
			
		||||
	llist_for_each(tmp, bts_ms_list(bts)) {
 | 
			
		||||
		struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
 | 
			
		||||
		if (ms->anr) /* Don't pick MS already busy doing ANR */
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!ms->dl_tbf) {
 | 
			
		||||
			/* Trigger a Pkt Dl Assignment and do ANR procedure once it is active: */
 | 
			
		||||
			struct gprs_rlcmac_dl_tbf *new_dl_tbf;
 | 
			
		||||
			int rc;
 | 
			
		||||
			rc = tbf_new_dl_assignment(ms->bts, ms, &new_dl_tbf);
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				continue;
 | 
			
		||||
			/* Fill the TBF with some LLC Dummy Command, since everyone expectes we send something to that DL TBF... */
 | 
			
		||||
			uint16_t delay_csec = 0xffff;
 | 
			
		||||
			/* The shortest dummy command (the spec requests at least 6 octets) */
 | 
			
		||||
			const uint8_t llc_dummy_command[] = {
 | 
			
		||||
			0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
 | 
			
		||||
			};
 | 
			
		||||
			dl_tbf_append_data(new_dl_tbf, delay_csec, &llc_dummy_command[0], ARRAY_SIZE(llc_dummy_command));
 | 
			
		||||
			return ms;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Build up cell list subset for this MS to measure: */
 | 
			
		||||
static size_t take_next_cell_list_chunk(struct bts_anr_fsm_ctx *ctx, struct arfcn_bsic ms_cell_li[MAX_NEIGH_LIST_LEN])
 | 
			
		||||
{
 | 
			
		||||
	unsigned int subset_len = ANR_MAX_NEIGH_SUBSET;
 | 
			
		||||
	if (ctx->num_cells <= subset_len) {
 | 
			
		||||
		memcpy(ms_cell_li, ctx->cell_list, ctx->num_cells * sizeof(ctx->cell_list[0]));
 | 
			
		||||
		subset_len = ctx->num_cells;
 | 
			
		||||
	} else if ((ctx->num_cells - ctx->next_cell) >= subset_len) {
 | 
			
		||||
		memcpy(ms_cell_li, &ctx->cell_list[ctx->next_cell], subset_len * sizeof(ctx->cell_list[0]));
 | 
			
		||||
		ctx->next_cell = (ctx->next_cell + subset_len) % ctx->num_cells;
 | 
			
		||||
	} else {
 | 
			
		||||
		unsigned int len = (ctx->num_cells - ctx->next_cell);
 | 
			
		||||
		memcpy(ms_cell_li, &ctx->cell_list[ctx->next_cell], len * sizeof(ctx->cell_list[0]));
 | 
			
		||||
		memcpy(&ms_cell_li[len], &ctx->cell_list[0], subset_len - len);
 | 
			
		||||
		ctx->next_cell = subset_len - len;
 | 
			
		||||
	}
 | 
			
		||||
	return subset_len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sched_ms_meas_report(struct bts_anr_fsm_ctx *ctx, const struct arfcn_bsic* cell_list,
 | 
			
		||||
				unsigned int num_cells)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = ctx->bts;
 | 
			
		||||
	struct GprsMs *ms;
 | 
			
		||||
	struct arfcn_bsic ms_cell_li[MAX_NEIGH_LIST_LEN];
 | 
			
		||||
	/* HERE we'll:
 | 
			
		||||
	 * 1- Select a TBF candidate in the BTS
 | 
			
		||||
	 * 2- Pick a subset from ctx->cell_list (increasing index round buffer in array)
 | 
			
		||||
	 * 3- Send event to it to schedule the meas report [osmo_fsm_inst_dispatch(ms->meas_rep_fsm, MEAS_REP_EV_SCHEDULE, cell_sublist)]
 | 
			
		||||
	 * 4- Wait for event BTS_ANR_EV_MEAS_REP containing "Packet Measurement Report" as data
 | 
			
		||||
	 * 5- Filter out the list and submit it back over PCUIF */
 | 
			
		||||
 | 
			
		||||
	/* First poor-man impl: pick first MS having a FLOW TBF: */
 | 
			
		||||
	ms = select_candidate_ms(bts);
 | 
			
		||||
	if (!ms) {
 | 
			
		||||
		LOGPFSML(ctx->fi, LOGL_INFO, "Unable to find MS to start ANR measurements\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	LOGPMS(ms, DANR, LOGL_DEBUG, "Selected for ANR measurements\n");
 | 
			
		||||
 | 
			
		||||
	/* Build up cell list subset for this MS to measure: */
 | 
			
		||||
	if (!cell_list) {
 | 
			
		||||
		num_cells = take_next_cell_list_chunk(ctx, &ms_cell_li[0]);
 | 
			
		||||
		cell_list = &ms_cell_li[0];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ms_anr_start(ms, cell_list, num_cells) < 0)
 | 
			
		||||
		LOGPFSML(ctx->fi, LOGL_ERROR, "Unable to start ANR measurements on MS\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_ms_meas_report(struct bts_anr_fsm_ctx *ctx, const struct ms_anr_ev_meas_compl* result)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = ctx->bts;
 | 
			
		||||
	pcu_tx_anr_cnf(bts, result->cell_list, result->meas_list, result->num_cells);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
////////////////
 | 
			
		||||
// FSM states //
 | 
			
		||||
////////////////
 | 
			
		||||
 | 
			
		||||
static void st_disabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	struct bts_anr_fsm_ctx *ctx = (struct bts_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	struct llist_head *tmp;
 | 
			
		||||
 | 
			
		||||
	/* Abort ongoing scheduled ms_anr_fsm: */
 | 
			
		||||
	llist_for_each(tmp, bts_ms_list(ctx->bts)) {
 | 
			
		||||
		struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
 | 
			
		||||
		/* Remark: ms_anr_fsm_abort does NOT send BTS_ANR_EV_MS_MEAS_ABORTED back at us */
 | 
			
		||||
		if (ms->anr)
 | 
			
		||||
			ms_anr_fsm_abort(ms->anr);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_disabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct bts_anr_fsm_ctx *ctx = (struct bts_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	const struct gsm_pcu_if_anr_req *anr_req;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case BTS_ANR_EV_RX_ANR_REQ:
 | 
			
		||||
		anr_req = (const struct gsm_pcu_if_anr_req *)data;
 | 
			
		||||
		rx_new_cell_list(ctx, anr_req);
 | 
			
		||||
		if (ctx->num_cells > 0)
 | 
			
		||||
			bts_anr_fsm_state_chg(fi, BTS_ANR_ST_ENABLED);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	struct bts_anr_fsm_ctx *ctx = (struct bts_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	sched_ms_meas_report(ctx, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct bts_anr_fsm_ctx *ctx = (struct bts_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	const struct gsm_pcu_if_anr_req *anr_req;
 | 
			
		||||
	struct ms_anr_ev_abort *ev_abort_data;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case BTS_ANR_EV_RX_ANR_REQ:
 | 
			
		||||
		anr_req = (const struct gsm_pcu_if_anr_req *)data;
 | 
			
		||||
		rx_new_cell_list(ctx, anr_req);
 | 
			
		||||
		if (ctx->num_cells == 0)
 | 
			
		||||
			bts_anr_fsm_state_chg(fi, BTS_ANR_ST_DISABLED);
 | 
			
		||||
		break;
 | 
			
		||||
	case BTS_ANR_EV_SCHED_MS_MEAS:
 | 
			
		||||
		sched_ms_meas_report(ctx, NULL, 0);
 | 
			
		||||
		break;
 | 
			
		||||
	case BTS_ANR_EV_MS_MEAS_ABORTED:
 | 
			
		||||
		ev_abort_data = (struct ms_anr_ev_abort*)data;
 | 
			
		||||
		sched_ms_meas_report(ctx, ev_abort_data->cell_list, ev_abort_data->num_cells);
 | 
			
		||||
		break;
 | 
			
		||||
	case BTS_ANR_EV_MS_MEAS_COMPL:
 | 
			
		||||
		handle_ms_meas_report(ctx, (const struct ms_anr_ev_meas_compl*)data);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*TODO: we need to track how many chunks are created, how many are in progress, how many are completed, etc. */
 | 
			
		||||
static int bts_anr_fsm_timer_cb(struct osmo_fsm_inst *fi)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long timeout;
 | 
			
		||||
 | 
			
		||||
	switch (fi->T) {
 | 
			
		||||
	case PCU_TDEF_ANR_SCHED_TBF:
 | 
			
		||||
		/* Re-schedule the timer */
 | 
			
		||||
		timeout = osmo_tdef_get(((struct bts_anr_fsm_ctx*)(fi->priv))->bts->pcu->T_defs,
 | 
			
		||||
					fi->T, OSMO_TDEF_S, -1);
 | 
			
		||||
		osmo_timer_schedule(&fi->timer, timeout, 0);
 | 
			
		||||
		/* Dispatch the schedule TBF MEAS event */
 | 
			
		||||
		osmo_fsm_inst_dispatch(fi, BTS_ANR_EV_SCHED_MS_MEAS, NULL);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct osmo_fsm_state bts_anr_fsm_states[] = {
 | 
			
		||||
	[BTS_ANR_ST_DISABLED] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(BTS_ANR_EV_RX_ANR_REQ),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(BTS_ANR_ST_ENABLED),
 | 
			
		||||
		.name = "DISABLED",
 | 
			
		||||
		.onenter = st_disabled_on_enter,
 | 
			
		||||
		.action = st_disabled,
 | 
			
		||||
	},
 | 
			
		||||
	[BTS_ANR_ST_ENABLED] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(BTS_ANR_EV_RX_ANR_REQ) |
 | 
			
		||||
			X(BTS_ANR_EV_SCHED_MS_MEAS) |
 | 
			
		||||
			X(BTS_ANR_EV_MS_MEAS_COMPL) |
 | 
			
		||||
			X(BTS_ANR_EV_MS_MEAS_ABORTED),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(BTS_ANR_ST_DISABLED),
 | 
			
		||||
		.name = "ENABLED",
 | 
			
		||||
		.onenter = st_enabled_on_enter,
 | 
			
		||||
		.action = st_enabled,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct osmo_fsm bts_anr_fsm = {
 | 
			
		||||
	.name = "BTS_ANR",
 | 
			
		||||
	.states = bts_anr_fsm_states,
 | 
			
		||||
	.num_states = ARRAY_SIZE(bts_anr_fsm_states),
 | 
			
		||||
	.timer_cb = bts_anr_fsm_timer_cb,
 | 
			
		||||
	.log_subsys = DANR,
 | 
			
		||||
	.event_names = bts_anr_fsm_event_names,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static __attribute__((constructor)) void bts_anr_fsm_init(void)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(osmo_fsm_register(&bts_anr_fsm) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int bts_anr_fsm_ctx_talloc_destructor(struct bts_anr_fsm_ctx *ctx)
 | 
			
		||||
{
 | 
			
		||||
	if (ctx->fi) {
 | 
			
		||||
		osmo_fsm_inst_free(ctx->fi);
 | 
			
		||||
		ctx->fi = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct bts_anr_fsm_ctx *bts_anr_fsm_alloc(struct gprs_rlcmac_bts* bts)
 | 
			
		||||
{
 | 
			
		||||
	struct bts_anr_fsm_ctx *ctx = talloc_zero(bts, struct bts_anr_fsm_ctx);
 | 
			
		||||
	char buf[64];
 | 
			
		||||
 | 
			
		||||
	talloc_set_destructor(ctx, bts_anr_fsm_ctx_talloc_destructor);
 | 
			
		||||
 | 
			
		||||
	ctx->bts = bts;
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof(buf), "BTS-%u", bts->nr);
 | 
			
		||||
	ctx->fi = osmo_fsm_inst_alloc(&bts_anr_fsm, ctx, ctx, LOGL_INFO, buf);
 | 
			
		||||
	if (!ctx->fi)
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
 | 
			
		||||
	return ctx;
 | 
			
		||||
free_ret:
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								src/bts_anr_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/bts_anr_fsm.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
/* bts_anr_fsm.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/fsm.h>
 | 
			
		||||
#include <osmocom/gsm/gsm23003.h>
 | 
			
		||||
 | 
			
		||||
#include <pcu_utils.h>
 | 
			
		||||
 | 
			
		||||
#define MAX_NEIGH_LIST_LEN 96
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
 | 
			
		||||
enum bts_anr_fsm_event {
 | 
			
		||||
	BTS_ANR_EV_RX_ANR_REQ, /* data: struct gsm_pcu_if_anr_req* */
 | 
			
		||||
	BTS_ANR_EV_SCHED_MS_MEAS,
 | 
			
		||||
	BTS_ANR_EV_MS_MEAS_COMPL, /* data: struct ms_anr_ev_meas_compl* */
 | 
			
		||||
	BTS_ANR_EV_MS_MEAS_ABORTED, /* data: struct ms_anr_ev_meas_abort* */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum bts_anr_fsm_states {
 | 
			
		||||
	BTS_ANR_ST_DISABLED,
 | 
			
		||||
	BTS_ANR_ST_ENABLED
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct bts_anr_fsm_ctx {
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts; /* back pointer */
 | 
			
		||||
	struct arfcn_bsic cell_list[MAX_NEIGH_LIST_LEN]; /* ordered by ascending ARFCN */
 | 
			
		||||
	unsigned int num_cells;
 | 
			
		||||
	unsigned int next_cell; /* Next cell list subset starts from this index */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* passed as data in BTS_ANR_EV_MS_MEAS_COMPL */
 | 
			
		||||
 | 
			
		||||
struct ms_anr_ev_meas_compl {
 | 
			
		||||
	const struct arfcn_bsic *cell_list; /* len() = num_cells */
 | 
			
		||||
	const uint8_t *meas_list; /* len() = num_cells, value 0xff means invalid */
 | 
			
		||||
	unsigned int num_cells;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* passed as data in BTS_ANR_EV_MS_MEAS_ABORT */
 | 
			
		||||
struct ms_anr_ev_abort {
 | 
			
		||||
	const struct arfcn_bsic* cell_list;
 | 
			
		||||
	unsigned int num_cells;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct bts_anr_fsm_ctx *bts_anr_fsm_alloc(struct gprs_rlcmac_bts* bts);
 | 
			
		||||
							
								
								
									
										408
									
								
								src/coding_scheme.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										408
									
								
								src/coding_scheme.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,408 @@
 | 
			
		||||
/* coding_scheme.c
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2019 by sysmocom s.f.m.c. GmbH
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include "coding_scheme.h"
 | 
			
		||||
 | 
			
		||||
const struct value_string mcs_names[] = {
 | 
			
		||||
	{ UNKNOWN, "UNKNOWN" },
 | 
			
		||||
	{ CS1, "CS-1" },
 | 
			
		||||
	{ CS2, "CS-2" },
 | 
			
		||||
	{ CS3, "CS-3" },
 | 
			
		||||
	{ CS4, "CS-4" },
 | 
			
		||||
	{ MCS1, "MCS-1" },
 | 
			
		||||
	{ MCS2, "MCS-2" },
 | 
			
		||||
	{ MCS3, "MCS-3" },
 | 
			
		||||
	{ MCS4, "MCS-4" },
 | 
			
		||||
	{ MCS5, "MCS-5" },
 | 
			
		||||
	{ MCS6, "MCS-6" },
 | 
			
		||||
	{ MCS7, "MCS-7" },
 | 
			
		||||
	{ MCS8, "MCS-8" },
 | 
			
		||||
	{ MCS9, "MCS-9" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum Family {
 | 
			
		||||
	FAMILY_INVALID,
 | 
			
		||||
	FAMILY_A,
 | 
			
		||||
	FAMILY_B,
 | 
			
		||||
	FAMILY_C,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
	struct {
 | 
			
		||||
		uint8_t bytes;
 | 
			
		||||
		uint8_t ext_bits;
 | 
			
		||||
		uint8_t data_header_bits;
 | 
			
		||||
	} uplink, downlink;
 | 
			
		||||
	uint8_t data_bytes;
 | 
			
		||||
	uint8_t optional_padding_bits;
 | 
			
		||||
	enum HeaderType data_hdr;
 | 
			
		||||
	enum Family family;
 | 
			
		||||
} mcs_info[NUM_SCHEMES] = {
 | 
			
		||||
	{{0, 0},   {0, 0},    0,  0,
 | 
			
		||||
		HEADER_INVALID, FAMILY_INVALID},
 | 
			
		||||
	{{23, 0},  {23, 0},  20,  0,
 | 
			
		||||
		HEADER_GPRS_DATA, FAMILY_INVALID},
 | 
			
		||||
	{{33, 7},  {33, 7},  30,  0,
 | 
			
		||||
		HEADER_GPRS_DATA, FAMILY_INVALID},
 | 
			
		||||
	{{39, 3},  {39, 3},  36,  0,
 | 
			
		||||
		HEADER_GPRS_DATA, FAMILY_INVALID},
 | 
			
		||||
	{{53, 7},  {53, 7},  50,  0,
 | 
			
		||||
		HEADER_GPRS_DATA, FAMILY_INVALID},
 | 
			
		||||
 | 
			
		||||
	{{26, 1},  {26, 1},  22,  0,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
 | 
			
		||||
	{{32, 1},  {32, 1},  28,  0,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_3, FAMILY_B},
 | 
			
		||||
	{{41, 1},  {41, 1},  37, 48,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_3, FAMILY_A},
 | 
			
		||||
	{{48, 1},  {48, 1},  44,  0,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
 | 
			
		||||
 | 
			
		||||
	{{60, 7},  {59, 6},  56,  0,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_2, FAMILY_B},
 | 
			
		||||
	{{78, 7},  {77, 6},  74, 48,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_2, FAMILY_A},
 | 
			
		||||
	{{118, 2}, {117, 4}, 56,  0,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_1, FAMILY_B},
 | 
			
		||||
	{{142, 2}, {141, 4}, 68,  0,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
 | 
			
		||||
	{{154, 2}, {153, 4}, 74,  0,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char *mcs_name(enum CodingScheme val) {
 | 
			
		||||
	return get_value_string(mcs_names, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mcs_is_gprs(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return CS1 <= cs && cs <= CS4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mcs_is_edge(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return MCS1 <= cs && cs <= MCS9;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mcs_is_edge_gmsk(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	if (mcs_is_edge(cs))
 | 
			
		||||
		return cs <= MCS4;
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return 3GPP TS 44.060 §12.10d (EDGE) or Table 11.2.28.2 (GPRS) Channel Coding Command value */
 | 
			
		||||
uint8_t mcs_chan_code(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	if (mcs_is_gprs(cs))
 | 
			
		||||
		return cs - CS1;
 | 
			
		||||
 | 
			
		||||
	if (mcs_is_edge(cs))
 | 
			
		||||
		return cs - MCS1;
 | 
			
		||||
 | 
			
		||||
	/* Defaults to (M)CS1 */
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum CodingScheme mcs_get_by_size_ul(unsigned size)
 | 
			
		||||
{
 | 
			
		||||
	switch (size) {
 | 
			
		||||
		case 23: return CS1;
 | 
			
		||||
		case 27: return MCS1;
 | 
			
		||||
		case 33: return MCS2;
 | 
			
		||||
		case 34: return CS2;
 | 
			
		||||
		case 40: return CS3;
 | 
			
		||||
		case 42: return MCS3;
 | 
			
		||||
		case 49: return MCS4;
 | 
			
		||||
		case 54: return CS4;
 | 
			
		||||
		case 61: return MCS5;
 | 
			
		||||
		case 79: return MCS6;
 | 
			
		||||
		case 119: return MCS7;
 | 
			
		||||
		case 143: return MCS8;
 | 
			
		||||
		case 155: return MCS9;
 | 
			
		||||
		default: return UNKNOWN;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum CodingScheme mcs_get_gprs_by_num(unsigned num)
 | 
			
		||||
{
 | 
			
		||||
	if (num < 1 || num > 4)
 | 
			
		||||
		return UNKNOWN;
 | 
			
		||||
	return CS1 + (num - 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum CodingScheme mcs_get_egprs_by_num(unsigned num)
 | 
			
		||||
{
 | 
			
		||||
	if (num < 1 || num > 9)
 | 
			
		||||
		return UNKNOWN;
 | 
			
		||||
	return MCS1 + (num - 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mcs_is_valid(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return UNKNOWN < cs && cs <= MCS9;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mcs_is_compat_kind(enum CodingScheme cs, enum mcs_kind mode)
 | 
			
		||||
{
 | 
			
		||||
	switch (mode) {
 | 
			
		||||
	case GPRS: return mcs_is_gprs(cs);
 | 
			
		||||
	case EGPRS_GMSK: return mcs_is_edge_gmsk(cs);
 | 
			
		||||
	case EGPRS: return mcs_is_edge(cs);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mcs_is_compat(enum CodingScheme cs, enum CodingScheme o)
 | 
			
		||||
{
 | 
			
		||||
	return (mcs_is_gprs(cs) && mcs_is_gprs(o)) || (mcs_is_edge(cs) && mcs_is_edge(o));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_size_ul(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[cs].uplink.bytes + (mcs_spare_bits_ul(cs) ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_size_dl(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[cs].downlink.bytes + (mcs_spare_bits_dl(cs) ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_used_size_ul(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	if (mcs_info[cs].data_hdr == HEADER_GPRS_DATA)
 | 
			
		||||
		return mcs_info[cs].uplink.bytes;
 | 
			
		||||
	else
 | 
			
		||||
		return mcs_size_ul(cs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_used_size_dl(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	if (mcs_info[cs].data_hdr == HEADER_GPRS_DATA)
 | 
			
		||||
		return mcs_info[cs].downlink.bytes;
 | 
			
		||||
	else
 | 
			
		||||
		return mcs_size_dl(cs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_max_bytes_ul(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[cs].uplink.bytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_max_bytes_dl(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[cs].downlink.bytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_spare_bits_ul(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[cs].uplink.ext_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_spare_bits_dl(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[cs].downlink.ext_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_max_data_block_bytes(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[cs].data_bytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_opt_padding_bits(enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[cs].optional_padding_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mcs_inc_kind(enum CodingScheme *cs, enum mcs_kind mode)
 | 
			
		||||
{
 | 
			
		||||
	if (!mcs_is_compat_kind(*cs, mode))
 | 
			
		||||
		/* This should not happen. TODO: Use assert? */
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	enum CodingScheme new_cs = *cs + 1;
 | 
			
		||||
	if (!mcs_is_compat_kind(new_cs, mode))
 | 
			
		||||
		/* Clipping, do not change the value */
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	*cs = new_cs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mcs_dec_kind(enum CodingScheme *cs, enum mcs_kind mode)
 | 
			
		||||
{
 | 
			
		||||
	if (!mcs_is_compat_kind(*cs, mode))
 | 
			
		||||
		/* This should not happen. TODO: Use assert? */
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	enum CodingScheme new_cs = *cs - 1;
 | 
			
		||||
	if (!mcs_is_compat_kind(new_cs, mode))
 | 
			
		||||
		/* Clipping, do not change the value */
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	*cs = new_cs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mcs_inc(enum CodingScheme *cs)
 | 
			
		||||
{
 | 
			
		||||
	if (mcs_is_gprs(*cs) && *cs == CS4)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (mcs_is_edge(*cs) && *cs == MCS9)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!mcs_is_valid(*cs))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	*cs = *cs + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mcs_dec(enum CodingScheme *cs)
 | 
			
		||||
{
 | 
			
		||||
	if (mcs_is_gprs(*cs) && *cs == CS1)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (mcs_is_edge(*cs) && *cs == MCS1)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!mcs_is_valid(*cs))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	*cs = *cs - 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mcs_is_family_compat(enum CodingScheme cs, enum CodingScheme o)
 | 
			
		||||
{
 | 
			
		||||
	if (cs == o)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	if (mcs_info[cs].family == FAMILY_INVALID)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return mcs_info[cs].family == mcs_info[o].family;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mcs_dec_to_single_block(enum CodingScheme *cs, bool *need_stuffing)
 | 
			
		||||
{
 | 
			
		||||
	switch (*cs) {
 | 
			
		||||
	case MCS7: *need_stuffing = false; *cs = MCS5; break;
 | 
			
		||||
	case MCS8: *need_stuffing =  true; *cs = MCS6; break;
 | 
			
		||||
	case MCS9: *need_stuffing = false; *cs = MCS6; break;
 | 
			
		||||
	default:   *need_stuffing = false; break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
	struct {
 | 
			
		||||
		uint8_t data_header_bits;
 | 
			
		||||
	} uplink, downlink;
 | 
			
		||||
	uint8_t data_block_header_bits;
 | 
			
		||||
	uint8_t num_blocks;
 | 
			
		||||
	const char *name;
 | 
			
		||||
} hdr_type_info[NUM_HEADER_TYPES] = {
 | 
			
		||||
	{ { 0 },         { 0 },         0, 0, "INVALID" },
 | 
			
		||||
	{ { 1 * 8 + 0 }, { 1 * 8 + 0 }, 0, 0, "CONTROL" },
 | 
			
		||||
	{ { 3 * 8 + 0 }, { 3 * 8 + 0 }, 0, 1, "GPRS_DATA" },
 | 
			
		||||
	{ { 5 * 8 + 6 }, { 5 * 8 + 0 }, 2, 2, "EGPRS_DATA_TYPE1" },
 | 
			
		||||
	{ { 4 * 8 + 5 }, { 3 * 8 + 4 }, 2, 1, "EGPRS_DATA_TYPE2" },
 | 
			
		||||
	{ { 3 * 8 + 7 }, { 3 * 8 + 7 }, 2, 1, "EGPRS_DATA_TYPE3" },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum HeaderType mcs_header_type(enum CodingScheme mcs)
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[mcs].data_hdr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t num_data_blocks(enum HeaderType ht)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(ht < NUM_HEADER_TYPES);
 | 
			
		||||
	return hdr_type_info[ht].num_blocks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t num_data_header_bits_UL(enum HeaderType ht)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(ht < NUM_HEADER_TYPES);
 | 
			
		||||
	return hdr_type_info[ht].uplink.data_header_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t num_data_header_bits_DL(enum HeaderType ht)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(ht < NUM_HEADER_TYPES);
 | 
			
		||||
	return hdr_type_info[ht].downlink.data_header_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t num_data_block_header_bits(enum HeaderType ht)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(ht < NUM_HEADER_TYPES);
 | 
			
		||||
	return hdr_type_info[ht].data_block_header_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct value_string mode_names[] = {
 | 
			
		||||
	{ GPRS, "GPRS" },
 | 
			
		||||
	{ EGPRS_GMSK, "EGPRS_GMSK-only"},
 | 
			
		||||
	{ EGPRS, "EGPRS"},
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char *mode_name(enum mcs_kind val) {
 | 
			
		||||
	return get_value_string(mode_names, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* FIXME: take into account padding and special cases of commanded MCS (MCS-6-9 and MCS-5-7) */
 | 
			
		||||
enum CodingScheme get_retx_mcs(enum CodingScheme initial_mcs, enum CodingScheme commanded_mcs, bool resegment_bit)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(mcs_is_edge(initial_mcs));
 | 
			
		||||
	OSMO_ASSERT(mcs_is_edge(commanded_mcs));
 | 
			
		||||
	OSMO_ASSERT(NUM_SCHEMES - MCS1 == 9);
 | 
			
		||||
 | 
			
		||||
	if (resegment_bit) { /* 3GPP TS 44.060 Table 8.1.1.1, reflected over antidiagonal */
 | 
			
		||||
		enum CodingScheme egprs_reseg[NUM_SCHEMES - MCS1][NUM_SCHEMES - MCS1] = {
 | 
			
		||||
			{ MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1 },
 | 
			
		||||
			{ MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2 },
 | 
			
		||||
			{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3 },
 | 
			
		||||
			{ MCS1, MCS1, MCS1, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4 },
 | 
			
		||||
			{ MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7 },
 | 
			
		||||
			{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9 },
 | 
			
		||||
			{ MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7 },
 | 
			
		||||
			{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS8, MCS8 },
 | 
			
		||||
			{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9 },
 | 
			
		||||
		};
 | 
			
		||||
		return egprs_reseg[mcs_chan_code(initial_mcs)][mcs_chan_code(commanded_mcs)];
 | 
			
		||||
	} else { /* 3GPP TS 44.060 Table 8.1.1.2, reflected over antidiagonal */
 | 
			
		||||
		enum CodingScheme egprs_no_reseg[NUM_SCHEMES - MCS1][NUM_SCHEMES - MCS1] = {
 | 
			
		||||
			{ MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1 },
 | 
			
		||||
			{ MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2 },
 | 
			
		||||
			{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3 },
 | 
			
		||||
			{ MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4 },
 | 
			
		||||
			{ MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7 },
 | 
			
		||||
			{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9 },
 | 
			
		||||
			{ MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7 },
 | 
			
		||||
			{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS8, MCS8 },
 | 
			
		||||
			{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9 },
 | 
			
		||||
		};
 | 
			
		||||
		return egprs_no_reseg[mcs_chan_code(initial_mcs)][mcs_chan_code(commanded_mcs)];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										99
									
								
								src/coding_scheme.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/coding_scheme.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
/* coding_scheme.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015-2019 by sysmocom s.f.m.c. GmbH
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
enum CodingScheme {
 | 
			
		||||
	UNKNOWN,
 | 
			
		||||
	/* GPRS Coding Schemes: */
 | 
			
		||||
	CS1, CS2, CS3, CS4,
 | 
			
		||||
	/* EDGE/EGPRS Modulation and Coding Schemes: */
 | 
			
		||||
	MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9,
 | 
			
		||||
	NUM_SCHEMES
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum mcs_kind {
 | 
			
		||||
	GPRS,
 | 
			
		||||
	EGPRS_GMSK,
 | 
			
		||||
	EGPRS,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum egprs_arq_type {
 | 
			
		||||
	EGPRS_ARQ1 = 0,
 | 
			
		||||
	EGPRS_ARQ2 = 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct value_string mcs_names[];
 | 
			
		||||
const char *mcs_name(enum CodingScheme val);
 | 
			
		||||
enum CodingScheme get_retx_mcs(enum CodingScheme initial_mcs, enum CodingScheme commanded_mcs, bool resegment_bit);
 | 
			
		||||
 | 
			
		||||
bool mcs_is_gprs(enum CodingScheme cs);
 | 
			
		||||
bool mcs_is_edge(enum CodingScheme cs);
 | 
			
		||||
bool mcs_is_edge_gmsk(enum CodingScheme cs);
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_chan_code(enum CodingScheme cs);
 | 
			
		||||
 | 
			
		||||
enum CodingScheme mcs_get_by_size_ul(unsigned size);
 | 
			
		||||
enum CodingScheme mcs_get_gprs_by_num(unsigned num);
 | 
			
		||||
enum CodingScheme mcs_get_egprs_by_num(unsigned num);
 | 
			
		||||
bool mcs_is_valid(enum CodingScheme cs);
 | 
			
		||||
bool mcs_is_compat(enum CodingScheme cs, enum CodingScheme o);
 | 
			
		||||
bool mcs_is_compat_kind(enum CodingScheme cs, enum mcs_kind mode);
 | 
			
		||||
 | 
			
		||||
uint8_t mcs_size_ul(enum CodingScheme cs);
 | 
			
		||||
uint8_t mcs_size_dl(enum CodingScheme cs);
 | 
			
		||||
uint8_t mcs_used_size_ul(enum CodingScheme cs);
 | 
			
		||||
uint8_t mcs_used_size_dl(enum CodingScheme cs);
 | 
			
		||||
uint8_t mcs_max_bytes_ul(enum CodingScheme cs);
 | 
			
		||||
uint8_t mcs_max_bytes_dl(enum CodingScheme cs);
 | 
			
		||||
uint8_t mcs_spare_bits_ul(enum CodingScheme cs);
 | 
			
		||||
uint8_t mcs_spare_bits_dl(enum CodingScheme cs);
 | 
			
		||||
uint8_t mcs_max_data_block_bytes(enum CodingScheme cs);
 | 
			
		||||
uint8_t mcs_opt_padding_bits(enum CodingScheme cs);
 | 
			
		||||
 | 
			
		||||
void mcs_inc_kind(enum CodingScheme *cs, enum mcs_kind mode);
 | 
			
		||||
void mcs_dec_kind(enum CodingScheme *cs, enum mcs_kind mode);
 | 
			
		||||
void mcs_inc(enum CodingScheme *cs);
 | 
			
		||||
void mcs_dec(enum CodingScheme *cs);
 | 
			
		||||
 | 
			
		||||
bool mcs_is_family_compat(enum CodingScheme cs, enum CodingScheme o);
 | 
			
		||||
void mcs_dec_to_single_block(enum CodingScheme *cs, bool *need_stuffing);
 | 
			
		||||
 | 
			
		||||
enum HeaderType {
 | 
			
		||||
	HEADER_INVALID,
 | 
			
		||||
	HEADER_GPRS_CONTROL,
 | 
			
		||||
	HEADER_GPRS_DATA,
 | 
			
		||||
	HEADER_EGPRS_DATA_TYPE_1,
 | 
			
		||||
	HEADER_EGPRS_DATA_TYPE_2,
 | 
			
		||||
	HEADER_EGPRS_DATA_TYPE_3,
 | 
			
		||||
	NUM_HEADER_TYPES
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum HeaderType mcs_header_type(enum CodingScheme mcs);
 | 
			
		||||
 | 
			
		||||
uint8_t num_data_blocks(enum HeaderType ht);
 | 
			
		||||
uint8_t num_data_header_bits_UL(enum HeaderType ht);
 | 
			
		||||
uint8_t num_data_header_bits_DL(enum HeaderType ht);
 | 
			
		||||
uint8_t num_data_block_header_bits(enum HeaderType ht);
 | 
			
		||||
 | 
			
		||||
const char *mode_name(enum mcs_kind val);
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										225
									
								
								src/csn1.h
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								src/csn1.h
									
									
									
									
									
								
							@@ -25,15 +25,11 @@
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <bitvector.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#ifndef _PACKET_CSN1_H_
 | 
			
		||||
#define _PACKET_CSN1_H_
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define MIN(a,b) (((a)<(b))?(a):(b))
 | 
			
		||||
//#define max(a,b) (((a)>(b))?(a):(b))
 | 
			
		||||
#include <osmocom/core/bitvec.h>
 | 
			
		||||
#include "wireshark_compat.h"
 | 
			
		||||
 | 
			
		||||
/* Error codes */
 | 
			
		||||
#define  CSN_OK                               0
 | 
			
		||||
@@ -48,15 +44,6 @@
 | 
			
		||||
#define  CSN_ERROR_MESSAGE_TOO_LONG          -9
 | 
			
		||||
#define  CSN_ERROR_                         -10
 | 
			
		||||
 | 
			
		||||
#define FALSE (0)
 | 
			
		||||
typedef signed int gint32;
 | 
			
		||||
typedef signed short gint16;
 | 
			
		||||
typedef int gint;
 | 
			
		||||
typedef gint gboolean;
 | 
			
		||||
typedef unsigned char guint8;
 | 
			
		||||
typedef unsigned short guint16;
 | 
			
		||||
typedef unsigned int guint32;
 | 
			
		||||
typedef unsigned long guint64;
 | 
			
		||||
/* CallBack return status */
 | 
			
		||||
typedef gint16 CSN_CallBackStatus_t;
 | 
			
		||||
 | 
			
		||||
@@ -73,6 +60,7 @@ typedef gint16 CSN_CallBackStatus_t;
 | 
			
		||||
#ifndef ElementsOf
 | 
			
		||||
#define ElementsOf(array) (sizeof(array) / sizeof(array[0]))
 | 
			
		||||
#endif
 | 
			
		||||
typedef void(*void_fn_t)(void);
 | 
			
		||||
 | 
			
		||||
/* Context holding CSN1 parameters */
 | 
			
		||||
typedef struct
 | 
			
		||||
@@ -82,7 +70,9 @@ typedef struct
 | 
			
		||||
  gint  direction;           /* 0 - decode; 1 - encode */
 | 
			
		||||
} csnStream_t;
 | 
			
		||||
 | 
			
		||||
typedef gint16 (*StreamSerializeFcn_t)(csnStream_t* ar, bitvec *vector, unsigned& readIndex, void* data);
 | 
			
		||||
typedef gint16 (*StreamSerializeFcn_t)(csnStream_t* ar, struct bitvec *vector, unsigned *readIndex, void* data);
 | 
			
		||||
typedef CSN_CallBackStatus_t (*DissectorCallbackFcn_t)(struct bitvec *vector, unsigned *readIndex, void* param1, void* param2);
 | 
			
		||||
 | 
			
		||||
typedef enum
 | 
			
		||||
{
 | 
			
		||||
  CSN_END = 0,
 | 
			
		||||
@@ -99,6 +89,7 @@ typedef enum
 | 
			
		||||
  CSN_VARIABLE_BITMAP_1,     /* <bitmap: bit**> i.e. to the end of message (R99) */
 | 
			
		||||
  CSN_LEFT_ALIGNED_VAR_BMP,  /* As variable bitmap but the result is left aligned (R99) */
 | 
			
		||||
  CSN_LEFT_ALIGNED_VAR_BMP_1,/* As above only size is to the end of message (R99) */
 | 
			
		||||
  CSN_PADDING_BITS,          /* Padding bits fill to the end of the buffer */
 | 
			
		||||
  CSN_VARIABLE_ARRAY,        /* Array with length specified in parameter: <N: bit(4)> <list: octet(N + offset)> */
 | 
			
		||||
  CSN_VARIABLE_TARRAY,       /* Type Array with length specified in parameter: <N: bit(x)> <Type>*N */
 | 
			
		||||
  CSN_VARIABLE_TARRAY_OFFSET,/* As above but with offset. The offset is stored as third parameter of CSN_DESCR (descr.value) */
 | 
			
		||||
@@ -127,7 +118,7 @@ typedef enum
 | 
			
		||||
 *
 | 
			
		||||
 * i:
 | 
			
		||||
 *       Depending on the contents of the type parameter,  the parameter "i" may have following meaning:
 | 
			
		||||
 *       - specifies the number of bits for the CSN_UINT type
 | 
			
		||||
 *       - specifies the number of bits for the CSN_UINT or CSN_UINT_OR_NULL types
 | 
			
		||||
 *       - the offset for an array size by which the size is incremented
 | 
			
		||||
 *          for the CSN_VAR_ARRAY type
 | 
			
		||||
 *       - the length of each element of an array for the CSN_REC_ARRAY type
 | 
			
		||||
@@ -142,6 +133,7 @@ typedef enum
 | 
			
		||||
 *          CSN_VAR_BITMAP, CSN_LEFT_VAR_BMP, and CSN_LEFT_BMP_1 types
 | 
			
		||||
 *       - the offset to param1 for the CSN_CALLBACK type
 | 
			
		||||
 *       - ERRORCODE  used by the CSN_ERROR type
 | 
			
		||||
 *       - the bit-length of the LENGTH field in a CSN_SERIALISE type
 | 
			
		||||
 *
 | 
			
		||||
 * descr
 | 
			
		||||
 *       This parameter has different meaning depending on the value of the type parameter:
 | 
			
		||||
@@ -167,6 +159,10 @@ typedef enum
 | 
			
		||||
 *         - an offset to the variable Exist for CSN_NEXT_EXIST and CSN_NEXT_EXIST_LH types
 | 
			
		||||
 *         - an offset to param2 in the CSN_CALLBACK  type
 | 
			
		||||
 *
 | 
			
		||||
 * may_be_null
 | 
			
		||||
 *         TRUE: if dissection may be attempted at an offset beyond the length of existing data bits
 | 
			
		||||
 *         FALSE: othewise
 | 
			
		||||
 *
 | 
			
		||||
 * sz
 | 
			
		||||
 *    - is the name of the parameter within the descr where their unpacked or packed value shall be stored or fetched.
 | 
			
		||||
 *      This paramater is pointed out by the offset parameter in the same CSN_DESCR variable as the sz.
 | 
			
		||||
@@ -184,23 +180,21 @@ typedef struct
 | 
			
		||||
  gint16      i;
 | 
			
		||||
  union
 | 
			
		||||
  {
 | 
			
		||||
    void*     ptr;
 | 
			
		||||
    const void*     ptr;
 | 
			
		||||
    guint32   value;
 | 
			
		||||
  } descr;
 | 
			
		||||
  unsigned      offset;
 | 
			
		||||
  gboolean    may_be_null;
 | 
			
		||||
  const char* sz;
 | 
			
		||||
  union
 | 
			
		||||
  {
 | 
			
		||||
    StreamSerializeFcn_t  fcn;
 | 
			
		||||
    guint32               value;
 | 
			
		||||
    int*                  hf_ptr;
 | 
			
		||||
  } serialize;
 | 
			
		||||
  guint32     value;
 | 
			
		||||
  void_fn_t   aux_fn;
 | 
			
		||||
} CSN_DESCR;
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  guint8     bits;
 | 
			
		||||
  guint8     value;
 | 
			
		||||
  gboolean   keep_bits;
 | 
			
		||||
  CSN_DESCR descr;
 | 
			
		||||
} CSN_ChoiceElement_t;
 | 
			
		||||
 | 
			
		||||
@@ -223,16 +217,16 @@ void csnStreamInit(csnStream_t* ar,gint BitOffset,gint BitCount);
 | 
			
		||||
* RETURNS:  int  Number of bits left to be unpacked. Negative Error code if failed to unpack all bits
 | 
			
		||||
******************************************************************************/
 | 
			
		||||
 | 
			
		||||
gint16 csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsigned& readIndex, void* data);
 | 
			
		||||
gint16 csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, struct bitvec *vector, unsigned *readIndex, void* data);
 | 
			
		||||
 | 
			
		||||
gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsigned& readIndex, void* data);
 | 
			
		||||
gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, struct bitvec *vector, unsigned *writeIndex, void* data);
 | 
			
		||||
 | 
			
		||||
/* CSN struct macro's */
 | 
			
		||||
#define  CSN_DESCR_BEGIN(_STRUCT)\
 | 
			
		||||
        CSN_DESCR CSNDESCR_##_STRUCT[] = {
 | 
			
		||||
 | 
			
		||||
#define  CSN_DESCR_END(_STRUCT)\
 | 
			
		||||
        {CSN_END, 0, {0}, 0, "", {(StreamSerializeFcn_t)0}} };
 | 
			
		||||
         {CSN_END, 0, {0}, 0, FALSE, "", 0, NULL} };
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * CSN_ERROR(Par1, Par2, Par3)
 | 
			
		||||
@@ -243,7 +237,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *      Par3: Error code
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define CSN_ERROR(_STRUCT, _Text, _ERRCODE)\
 | 
			
		||||
        {CSN_TRAP_ERROR, _ERRCODE, {(void*)_Text}, 0, _Text, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_TRAP_ERROR, _ERRCODE, {_Text}, 0, FALSE, _Text, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_BIT(Par1, Par2)
 | 
			
		||||
@@ -252,11 +246,21 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *      Par2: C structure element name
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_BIT(_STRUCT, _MEMBER)\
 | 
			
		||||
        {CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_BIT_OR_NULL(Par1, Par2)
 | 
			
		||||
 * Similar to the M_BIT except that not only bit 0 or 1 but also the
 | 
			
		||||
 * end of the message may be encountered when looking for the next element in
 | 
			
		||||
 * the message.
 | 
			
		||||
 * Covers the case {null | 0 | 1}
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 #define M_BIT_OR_NULL(_STRUCT, _MEMBER)\
 | 
			
		||||
         {CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_NEXT_EXIST(Par1, Par2, Par3)
 | 
			
		||||
 * Indicates whether the next element or a group of elements defined in the 
 | 
			
		||||
 * Indicates whether the next element or a group of elements defined in the
 | 
			
		||||
 * structure is present or not.
 | 
			
		||||
 *      Par1: C structure name
 | 
			
		||||
 *      Par2: C structure element name
 | 
			
		||||
@@ -264,37 +268,37 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *            element(s) does not exist
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_NEXT_EXIST(_STRUCT, _MEMBER, _NoOfExisting)\
 | 
			
		||||
        {CSN_NEXT_EXIST, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_NEXT_EXIST, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_NEXT_EXIST_LH(Par1, Par2, Par3)
 | 
			
		||||
 * similar to the M_NEXT_EXIST except that instead of bit 0/1 which is fetched
 | 
			
		||||
 * from the message in order to find out whether the next element/elements are
 | 
			
		||||
 * present in the message, the logical operation XOR with the background 
 | 
			
		||||
 * present in the message, the logical operation XOR with the background
 | 
			
		||||
 * pattern 0x2B is performed on the read bit before the decision is made.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_NEXT_EXIST_LH(_STRUCT, _MEMBER, _NoOfExisting)\
 | 
			
		||||
        {CSN_NEXT_EXIST_LH, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_NEXT_EXIST_LH, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_NEXT_EXIST_OR_NULL(Par1, Par2, Par3)
 | 
			
		||||
 * Similar to the M_NEXT_EXIST except that not only bit 0 or 1 but also the end
 | 
			
		||||
 * of the message may be encountered when looking for the next element in the 
 | 
			
		||||
 * of the message may be encountered when looking for the next element in the
 | 
			
		||||
 * message.
 | 
			
		||||
 * Covers the case {null | 0 | 1 < IE >} 
 | 
			
		||||
 * Covers the case {null | 0 | 1 < IE >}
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_NEXT_EXIST_OR_NULL(_STRUCT, _MEMBER, _NoOfExisting)\
 | 
			
		||||
        {CSN_NEXT_EXIST, _NoOfExisting, {(void*)1}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_NEXT_EXIST, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_NEXT_EXIST_OR_NULL_LH(Par1, Par2, Par3)
 | 
			
		||||
 * Similar to the M_NEXT_EXIST_LH except that not only bit 0 or 1 but also the
 | 
			
		||||
 * end of the message may be encountered when looking for the next element in
 | 
			
		||||
 * the message.
 | 
			
		||||
 * Covers the case {null | L | H < IE >} 
 | 
			
		||||
 * Covers the case {null | L | H < IE >}
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_NEXT_EXIST_OR_NULL_LH(_STRUCT, _MEMBER, _NoOfExisting)\
 | 
			
		||||
        {CSN_NEXT_EXIST_LH, _NoOfExisting, {(void*)1}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_NEXT_EXIST_LH, _NoOfExisting, {(void*)1}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_UINT(Par1, Par2, Par3)
 | 
			
		||||
@@ -304,17 +308,27 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *      Par3: number of bits used to code the element (between 1 and 32)
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_UINT(_STRUCT, _MEMBER, _BITS)\
 | 
			
		||||
        {CSN_UINT, _BITS, {(void*)1}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_UINT, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
 /******************************************************************************
 | 
			
		||||
 * M_UINT_OR_NULL(Par1, Par2, Par3)
 | 
			
		||||
 * Similar to the M_UINT except that not only the request set of bits but also the
 | 
			
		||||
 * end of the message may be encountered when looking for the next element in
 | 
			
		||||
 * the message.
 | 
			
		||||
 * Covers the case {null | 0 | 1 < IE >}
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 #define M_UINT_OR_NULL(_STRUCT, _MEMBER, _BITS)\
 | 
			
		||||
         {CSN_UINT, _BITS, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_UINT(Par1, Par2, Par3)
 | 
			
		||||
 * This macro has the same functionality as M_UINT except that  in addition the
 | 
			
		||||
 * logical "exclusive or" operation with the background value "0x2B" is 
 | 
			
		||||
 * performed before the final value of the integer number is delivered from the 
 | 
			
		||||
 * logical "exclusive or" operation with the background value "0x2B" is
 | 
			
		||||
 * performed before the final value of the integer number is delivered from the
 | 
			
		||||
 * received CSN.1 message
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_UINT_LH(_STRUCT, _MEMBER, _BITS)\
 | 
			
		||||
        {CSN_UINT_LH, _BITS, {(void*)1}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_UINT_LH, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_UINT_OFFSET(Par1, Par2, Par3, Par4)
 | 
			
		||||
@@ -325,7 +339,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *      Par4: value added to the returned integer (offset)
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_UINT_OFFSET(_STRUCT, _MEMBER, _BITS, _OFFSET)\
 | 
			
		||||
        {CSN_UINT_OFFSET, _BITS, {(void*)_OFFSET}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_UINT_OFFSET, _BITS, {(void*)_OFFSET}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_UINT_ARRAY(Par1, Par2, Par3, Par4)
 | 
			
		||||
@@ -336,7 +350,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *      Par4: number of elements in the array (fixed integer value)
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_UINT_ARRAY(_STRUCT, _MEMBER, _BITS, _ElementCount)\
 | 
			
		||||
        {CSN_UINT_ARRAY, _BITS, {(void*)_ElementCount}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_UINT_ARRAY, _BITS, {(void*)_ElementCount}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_VAR_UINT_ARRAY(Par1, Par2, Par3, Par4)
 | 
			
		||||
@@ -348,7 +362,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *            structure member holding the length value
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_VAR_UINT_ARRAY(_STRUCT, _MEMBER, _BITS, _ElementCountField)\
 | 
			
		||||
        {CSN_UINT_ARRAY, _BITS, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)1}}
 | 
			
		||||
        {CSN_UINT_ARRAY, _BITS, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 1, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_VAR_ARRAY(Par1, Par2, Par3, Par4)
 | 
			
		||||
@@ -359,7 +373,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *      Par4: offset that is added to the Par3 to get the actual size of the array
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_VAR_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
 | 
			
		||||
        {CSN_VARIABLE_ARRAY, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_VARIABLE_ARRAY, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_VAR_TARRAY(Par1, Par2, Par3, Par4)
 | 
			
		||||
@@ -370,32 +384,32 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *      Par4: name of the structure member holding the size of the array
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_VAR_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
 | 
			
		||||
        {CSN_VARIABLE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
 | 
			
		||||
        {CSN_VARIABLE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_VAR_TARRAY_OFFSET(Par1, Par2, Par3, Par4)
 | 
			
		||||
 * Same as M_VAR_TARRAY with offset
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_VAR_TARRAY_OFFSET(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
 | 
			
		||||
        {CSN_VARIABLE_TARRAY_OFFSET, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
 | 
			
		||||
        {CSN_VARIABLE_TARRAY_OFFSET, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_REC_ARRAY(Par1, Par2, Par3, Par4)
 | 
			
		||||
 * similar to the M_VAR_ARRAY. The difference is that the size of the array is 
 | 
			
		||||
 * similar to the M_VAR_ARRAY. The difference is that the size of the array is
 | 
			
		||||
 * not known in advance and it has to be calculated during unpacking. Its value
 | 
			
		||||
 * is stored in a variable which belongs to the same structure as the array. 
 | 
			
		||||
 * A zero element terminates the array. The CSN.1 syntax describes it 
 | 
			
		||||
 * is stored in a variable which belongs to the same structure as the array.
 | 
			
		||||
 * A zero element terminates the array. The CSN.1 syntax describes it
 | 
			
		||||
 * recursively as:
 | 
			
		||||
 * <array> ::={1 <element> <array>| 0} 
 | 
			
		||||
 * <array> ::={1 <element> <array>| 0}
 | 
			
		||||
 *
 | 
			
		||||
 *      Par1: C structure name
 | 
			
		||||
 *      Par2: C structure element name
 | 
			
		||||
 *      Par3: name of the structure member where the calculated the size of the 
 | 
			
		||||
 *      Par3: name of the structure member where the calculated the size of the
 | 
			
		||||
 *            array will be stored
 | 
			
		||||
 *      Par4: length of each element in bits
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_REC_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _BITS)\
 | 
			
		||||
        {CSN_RECURSIVE_ARRAY, _BITS, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_RECURSIVE_ARRAY, _BITS, {(const void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_VAR_TYPE_ARRAY(Par1, Par2, Par3, Par4)
 | 
			
		||||
@@ -406,7 +420,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *      Par4: number of elements in the array (fixed integer value)
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_TYPE_ARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCount)\
 | 
			
		||||
        {CSN_TYPE_ARRAY, _ElementCount, {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
 | 
			
		||||
        {CSN_TYPE_ARRAY, _ElementCount, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_REC_TARRAY(Par1, Par2, Par3, Par4)
 | 
			
		||||
@@ -418,7 +432,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *      Par4: will hold the number of element in the array after unpacking
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_REC_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
 | 
			
		||||
        {CSN_RECURSIVE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
 | 
			
		||||
        {CSN_RECURSIVE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_REC_TARRAY1(Par1, Par2, Par3, Par4)
 | 
			
		||||
@@ -426,7 +440,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 * <list> ::= <type> {1 <type>} ** 0 ;
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_REC_TARRAY_1(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
 | 
			
		||||
        {CSN_RECURSIVE_TARRAY_1, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
 | 
			
		||||
        {CSN_RECURSIVE_TARRAY_1, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_REC_TARRAY2(Par1, Par2, Par3, Par4)
 | 
			
		||||
@@ -434,7 +448,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 * <lists> ::= <type> { 0 <type> } ** 1 ;
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_REC_TARRAY_2(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
 | 
			
		||||
        {CSN_RECURSIVE_TARRAY_2, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
 | 
			
		||||
        {CSN_RECURSIVE_TARRAY_2, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_TYPE(Par1, Par2, Par3)
 | 
			
		||||
@@ -445,126 +459,133 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 *      Par3: type of member
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_TYPE(_STRUCT, _MEMBER, _MEMBER_TYPE)\
 | 
			
		||||
        {CSN_TYPE, 0, {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_TYPE, 0, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_UNION(Par1, Par2)
 | 
			
		||||
 * Informs the CSN.1 library that a union follows and how many possible choices
 | 
			
		||||
 * there are in the union. The actual value of the choice, which points out the
 | 
			
		||||
 * chosen element of the union is stored in the uint8 variable and is usually 
 | 
			
		||||
 * called UnionType. The elements of the union have to be listed directly after 
 | 
			
		||||
 * chosen element of the union is stored in the uint8 variable and is usually
 | 
			
		||||
 * called UnionType. The elements of the union have to be listed directly after
 | 
			
		||||
 * the M_UNION statement.
 | 
			
		||||
 *      Par1: C structure name
 | 
			
		||||
 *      Par2: number of possible choice in the union
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_UNION(_STRUCT, _COUNT)\
 | 
			
		||||
        {CSN_UNION, _COUNT, {0}, offsetof(_STRUCT, UnionType), "UnionType", {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_UNION, _COUNT, {0}, offsetof(_STRUCT, UnionType), FALSE, "UnionType", 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_UNION_LH(Par1, Par2)
 | 
			
		||||
 * Same as M_UNION but masked with background value 0x2B
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_UNION_LH(_STRUCT, _COUNT)\
 | 
			
		||||
        {CSN_UNION_LH, _COUNT, {0}, offsetof(_STRUCT, UnionType), "UnionType", {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_UNION_LH, _COUNT, {0}, offsetof(_STRUCT, UnionType), FALSE, "UnionType", 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_CHOICE(Par1, Par2, Par3, Par4)
 | 
			
		||||
 * Similar to the M_UNION. In the M_UNION the selected element of all possible 
 | 
			
		||||
 * choices in the union is referred as a sequential numbers,  i.e., the first 
 | 
			
		||||
 * choice is addressed as choice 0 the second as choice 1, the third as choice 
 | 
			
		||||
 * Similar to the M_UNION. In the M_UNION the selected element of all possible
 | 
			
		||||
 * choices in the union is referred as a sequential numbers,  i.e., the first
 | 
			
		||||
 * choice is addressed as choice 0 the second as choice 1, the third as choice
 | 
			
		||||
 * 2 and so on, both in the encoded message and in the variable UnionType which
 | 
			
		||||
 * is the part of the message.  In the CSN_CHOICE case, this rule does not 
 | 
			
		||||
 * apply. There is free but predefined mapping of the element of the union and 
 | 
			
		||||
 * is the part of the message.  In the CSN_CHOICE case, this rule does not
 | 
			
		||||
 * apply. There is free but predefined mapping of the element of the union and
 | 
			
		||||
 * the value which addresses this element.
 | 
			
		||||
 * The value of the address is called a selector.
 | 
			
		||||
 * The value of the address is called a selector. Up to 256 (UCHAR_MAX) unique
 | 
			
		||||
 * selectors can be handled, longer choice list would cause CSN_ERROR_IN_SCRIPT.
 | 
			
		||||
 * After unpacking, this value is then converted to the sequential number of the
 | 
			
		||||
 * element in the union and stored in the UnionType variable.
 | 
			
		||||
 * element in the union and stored in the UnionType variable (Par2).
 | 
			
		||||
 *      Par1: C structure name
 | 
			
		||||
 *      Par2: C structure element name
 | 
			
		||||
 *      Par2: C structure field name holding sequential number of the chosen element.
 | 
			
		||||
 *            BEWARE! Never use an enumerated type here, because its length is
 | 
			
		||||
 *            compiler/machine dependent, while decoder would cast it to guint8.
 | 
			
		||||
 *      Par3: address of an array of type CSN_ChoiceElement_t where all possible
 | 
			
		||||
 *            values of the selector are provided, together with the selector 
 | 
			
		||||
 *            length expressed in bits and the address of  the CSN_DESCR type 
 | 
			
		||||
 *            where the element is defined. For every element in the union 
 | 
			
		||||
 *            there is one line in the Choice variable. These lines have to 
 | 
			
		||||
 *            appear in the _CHOICE in the same order as the elements in the 
 | 
			
		||||
 *            union. The element of the union selected in the message through 
 | 
			
		||||
 *            the _CHOICE parameter is after unpacking translated to the 
 | 
			
		||||
 *            corresponding sequential number of this element and stored in 
 | 
			
		||||
 *            values of the selector are provided, together with the selector
 | 
			
		||||
 *            length expressed in bits and the address of  the CSN_DESCR type
 | 
			
		||||
 *            where the element is defined. For every element in the union
 | 
			
		||||
 *            there is one line in the Choice variable. These lines have to
 | 
			
		||||
 *            appear in the _CHOICE in the same order as the elements in the
 | 
			
		||||
 *            union. The element of the union selected in the message through
 | 
			
		||||
 *            the _CHOICE parameter is after unpacking translated to the
 | 
			
		||||
 *            corresponding sequential number of this element and stored in
 | 
			
		||||
 *            the variable pointed out by the _MEMBER
 | 
			
		||||
 *      Par4: number of possible choices in the union
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_CHOICE(_STRUCT, _MEMBER, _CHOICE, _ElementCount)\
 | 
			
		||||
        {CSN_CHOICE, _ElementCount, {(void*)_CHOICE}, offsetof(_STRUCT, _MEMBER), #_CHOICE, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_CHOICE, _ElementCount, {(const void*)_CHOICE}, offsetof(_STRUCT, _MEMBER), FALSE, #_CHOICE, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_FIXED(Par1, Par2, Par3)
 | 
			
		||||
 * Defines a fixed value of type integer which should be fetched from or stored 
 | 
			
		||||
 * Defines a fixed value of type integer which should be fetched from or stored
 | 
			
		||||
 * in  the message
 | 
			
		||||
 *      Par1: C structure name
 | 
			
		||||
 *      Par2: gives the length of the fixed number in bits.
 | 
			
		||||
 *      Par3: the value of the number. If the expected value is not present in 
 | 
			
		||||
 *      Par3: the value of the number. If the expected value is not present in
 | 
			
		||||
*             the message the unpacking procedure is aborted
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_FIXED(_STRUCT, _BITS, _BITVALUE)\
 | 
			
		||||
        {CSN_FIXED, _BITS, {0}, _BITVALUE, #_BITVALUE, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_FIXED, _BITS, {0}, _BITVALUE, FALSE, #_BITVALUE, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_SERIALIZE(Par1, Par2, Par3)
 | 
			
		||||
 * Allows using a complete free format of data being encoded or decoded. 
 | 
			
		||||
 * When the M_SERIALIZE is uncounted during encoding or decoding of a message 
 | 
			
		||||
 * the CSNstream program passes the control over to the specified function 
 | 
			
		||||
 * together with all necessary parameters about the current position within 
 | 
			
		||||
 * the message being unpacked or packed.  When transferring of "serialized" 
 | 
			
		||||
 * data to or from the message is finished by the function the CSNstream gets 
 | 
			
		||||
 * Allows using a complete free format of data being encoded or decoded.
 | 
			
		||||
 * When the M_SERIALIZE is encounted during encoding or decoding of a message
 | 
			
		||||
 * the CSNstream program passes the control over to the specified function
 | 
			
		||||
 * together with all necessary parameters about the current position within
 | 
			
		||||
 * the message being unpacked or packed.  When transferring of "serialized"
 | 
			
		||||
 * data to or from the message is finished by the function the CSNstream gets
 | 
			
		||||
 * back control over the data stream and continues to work with the message.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_SERIALIZE(_STRUCT, _MEMBER, _SERIALIZEFCN)\
 | 
			
		||||
        {CSN_SERIALIZE, 1, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {_SERIALIZEFCN}}
 | 
			
		||||
#define M_SERIALIZE(_STRUCT, _MEMBER, _LENGTH_LEN, _SERIALIZEFCN)\
 | 
			
		||||
        {CSN_SERIALIZE, _LENGTH_LEN, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, (void_fn_t)_SERIALIZEFCN}
 | 
			
		||||
 | 
			
		||||
#define M_CALLBACK(_STRUCT, _CSNCALLBACKFCN, _PARAM1, _PARAM2)\
 | 
			
		||||
        {CSN_CALLBACK, offsetof(_STRUCT, _PARAM1), {_CSNCALLBACKFCN}, offsetof(_STRUCT, _PARAM2), "CallBack_"#_CSNCALLBACKFCN, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_CALLBACK, offsetof(_STRUCT, _PARAM1), {0}, offsetof(_STRUCT, _PARAM2), FALSE, "CallBack_"#_CSNCALLBACKFCN, 0, (void_fn_t)_CSNCALLBACKFCN}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * M_BITMAP(Par1, Par2, Par3)
 | 
			
		||||
 * Defines a type which consists of a bitmap. The size of the bitmap in bits 
 | 
			
		||||
 * Defines a type which consists of a bitmap. The size of the bitmap in bits
 | 
			
		||||
 * is fixed and provided by the parameter Par3
 | 
			
		||||
 *      Par1: C structure name
 | 
			
		||||
 *      Par2: C structure element name
 | 
			
		||||
 *      Par3: length of the bitmap expressed in bits
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
#define M_BITMAP(_STRUCT, _MEMBER, _BITS)\
 | 
			
		||||
        {CSN_BITMAP, _BITS, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_BITMAP, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/* variable length, right aligned bitmap i.e. _ElementCountField = 11 => 00000111 11111111 */
 | 
			
		||||
#define M_VAR_BITMAP(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
 | 
			
		||||
        {CSN_VARIABLE_BITMAP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_VARIABLE_BITMAP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/* variable length, right aligned bitmap filling the rest of message
 | 
			
		||||
 * - when unpacking the _ElementCountField will be set in runtime
 | 
			
		||||
 * - when packing _ElementCountField contains the size of bitmap
 | 
			
		||||
 */
 | 
			
		||||
#define M_VAR_BITMAP_1(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
 | 
			
		||||
        {CSN_VARIABLE_BITMAP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_VARIABLE_BITMAP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/* variable length, left aligned bitmap i.e. _ElementCountField = 11 => 11111111 11100000 */
 | 
			
		||||
#define M_LEFT_VAR_BMP(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
 | 
			
		||||
        {CSN_LEFT_ALIGNED_VAR_BMP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_LEFT_ALIGNED_VAR_BMP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
/* variable length, left aligned bitmap filling the rest of message
 | 
			
		||||
 *- when unpacking the _ElementCountField will be set in runtime
 | 
			
		||||
 * - when packing _ElementCountField contains the size of bitmap
 | 
			
		||||
 */
 | 
			
		||||
#define M_LEFT_VAR_BMP_1(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
 | 
			
		||||
        {CSN_LEFT_ALIGNED_VAR_BMP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_LEFT_ALIGNED_VAR_BMP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
#define M_NULL(_STRUCT, _MEMBER)\
 | 
			
		||||
        {CSN_NULL, 0, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
			
		||||
/* todo: dissect padding bits looking for unexpected extensions */
 | 
			
		||||
#define M_PADDING_BITS(_STRUCT)\
 | 
			
		||||
        {CSN_PADDING_BITS, 0, {0}, 0, TRUE, "Padding", 0, NULL}
 | 
			
		||||
 | 
			
		||||
#define M_NULL(_STRUCT, _MEMBER, _SKIP_BITS)\
 | 
			
		||||
        {CSN_NULL, _SKIP_BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
 | 
			
		||||
 | 
			
		||||
#define M_THIS_EXIST(_STRUCT)\
 | 
			
		||||
        {CSN_EXIST, 0, {0}, offsetof(_STRUCT, Exist), "Exist", {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_EXIST, 0, {0}, offsetof(_STRUCT, Exist), FALSE, "Exist", 0, NULL}
 | 
			
		||||
 | 
			
		||||
#define M_THIS_EXIST_LH(_STRUCT)\
 | 
			
		||||
        {CSN_EXIST_LH, 0, {0}, offsetof(_STRUCT, Exist), "Exist", {(StreamSerializeFcn_t)0}}
 | 
			
		||||
        {CSN_EXIST_LH, 0, {0}, offsetof(_STRUCT, Exist), FALSE, "Exist", 0, NULL}
 | 
			
		||||
 | 
			
		||||
/* return value 0 if ok else discontionue the unpacking */
 | 
			
		||||
typedef gint16 (*CsnCallBackFcn_t)(void* pv ,...);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										133
									
								
								src/cxx_linuxlist.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								src/cxx_linuxlist.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
/* cxx_linuxlist.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
 | 
			
		||||
 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
	#include <osmocom/core/linuxlist.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct LListHead {
 | 
			
		||||
	typedef T entry_t;
 | 
			
		||||
 | 
			
		||||
	/* This must match the declaration of struct llist_head */
 | 
			
		||||
	LListHead<T> *next;
 | 
			
		||||
	LListHead<T> *prev;
 | 
			
		||||
 | 
			
		||||
	LListHead() : m_back(0) { INIT_LLIST_HEAD(this); }
 | 
			
		||||
	LListHead(T* entry) : m_back(entry) {
 | 
			
		||||
		next = (LListHead<T> *)LLIST_POISON1;
 | 
			
		||||
		prev = (LListHead<T> *)LLIST_POISON2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	T *entry() {return m_back;}
 | 
			
		||||
	const T *entry() const {return m_back;}
 | 
			
		||||
 | 
			
		||||
	llist_head &llist() {
 | 
			
		||||
		return *static_cast<llist_head *>(static_cast<void *>(this));
 | 
			
		||||
	}
 | 
			
		||||
	const llist_head &llist() const {
 | 
			
		||||
		return *static_cast<const llist_head *>(static_cast<const void *>(this));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	T *const m_back;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Define a family of casting functions */
 | 
			
		||||
template <typename T>
 | 
			
		||||
llist_head &llist(LListHead<T> &l)
 | 
			
		||||
{
 | 
			
		||||
	return l->llist();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
const llist_head &llist(const LListHead<T> &l)
 | 
			
		||||
{
 | 
			
		||||
	return l->llist();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
llist_head *llptr(LListHead<T> *l)
 | 
			
		||||
{
 | 
			
		||||
	return &(l->llist());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
const llist_head *llptr(const LListHead<T> *l)
 | 
			
		||||
{
 | 
			
		||||
	return &(l->llist());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Define type-safe wrapper for the existing linux_list.h functions */
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void llist_add(LListHead<T> *new_, LListHead<T> *head)
 | 
			
		||||
{
 | 
			
		||||
	llist_add(llptr(new_), llptr(head));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void llist_add_tail(LListHead<T> *new_, LListHead<T> *head)
 | 
			
		||||
{
 | 
			
		||||
	llist_add_tail(llptr(new_), llptr(head));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void llist_del(LListHead<T> *entry)
 | 
			
		||||
{
 | 
			
		||||
	llist_del(llptr(entry));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void llist_del_init(LListHead<T> *entry)
 | 
			
		||||
{
 | 
			
		||||
	llist_del_init(llptr(entry));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void llist_move(LListHead<T> *list, LListHead<T> *head)
 | 
			
		||||
{
 | 
			
		||||
	llist_move(llptr(list), llptr(head));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void llist_move_tail(LListHead<T> *list, LListHead<T> *head)
 | 
			
		||||
{
 | 
			
		||||
	llist_move_tail(llptr(list), llptr(head));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline int llist_empty(const LListHead<T> *head)
 | 
			
		||||
{
 | 
			
		||||
	return llist_empty(llptr(head));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void llist_splice(LListHead<T> *list, LListHead<T> *head)
 | 
			
		||||
{
 | 
			
		||||
	llist_splice(llptr(list), llptr(head));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void llist_splice_init(LListHead<T> *list, LListHead<T> *head)
 | 
			
		||||
{
 | 
			
		||||
	llist_splice_init(llptr(list), llptr(head));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										799
									
								
								src/decoding.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										799
									
								
								src/decoding.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,799 @@
 | 
			
		||||
/* decoding
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
#include <decoding.h>
 | 
			
		||||
#include <rlc.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <egprs_rlc_compression.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/bitcomp.h>
 | 
			
		||||
#include <osmocom/gprs/protocol/gsm_04_60.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#define LENGTH_TO_END 255
 | 
			
		||||
/*!
 | 
			
		||||
 * \returns num extensions fields (num frames == offset) on success,
 | 
			
		||||
 *          -errno otherwise.
 | 
			
		||||
 */
 | 
			
		||||
static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
 | 
			
		||||
	unsigned int *offs,
 | 
			
		||||
	bool is_last_block,
 | 
			
		||||
	Decoding::RlcData *chunks, unsigned int chunks_size)
 | 
			
		||||
{
 | 
			
		||||
	const struct rlc_li_field_egprs *li;
 | 
			
		||||
	uint8_t e;
 | 
			
		||||
	unsigned int num_chunks = 0;
 | 
			
		||||
 | 
			
		||||
	e = 0;
 | 
			
		||||
	while (!e) {
 | 
			
		||||
		if (*offs > data_len) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
 | 
			
		||||
				"but no more data\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* get new E */
 | 
			
		||||
		li = (struct rlc_li_field_egprs *)&data[*offs];
 | 
			
		||||
		e = li->e;
 | 
			
		||||
		*offs += 1;
 | 
			
		||||
 | 
			
		||||
		if (!chunks)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (num_chunks == chunks_size) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
 | 
			
		||||
				"but no more chunks possible\n");
 | 
			
		||||
			return -ENOSPC;
 | 
			
		||||
		}
 | 
			
		||||
		if (li->li == 0 && num_chunks == 0) {
 | 
			
		||||
			/* TS 44.060, table 10.4.14a.1, row 2a */
 | 
			
		||||
			/* TS 44.060, table 10.4.14a.1, row 4 */
 | 
			
		||||
			chunks[num_chunks].length = 0;
 | 
			
		||||
			chunks[num_chunks].is_complete = true;
 | 
			
		||||
		} else if (li->li == 127 && li->e == 1) {
 | 
			
		||||
			/* TS 44.060, table 10.4.14a.1, row 3 & 5 */
 | 
			
		||||
			/* only filling bytes left */
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
 | 
			
		||||
				"only filling bytes with extension octet: LI=%d, E=%d, count=%d\n",
 | 
			
		||||
				li->li, li->e, num_chunks);
 | 
			
		||||
			break;
 | 
			
		||||
		} else if (li->li > 0) {
 | 
			
		||||
			/* TS 44.060, table 10.4.14a.1, row 1 & 2b */
 | 
			
		||||
			chunks[num_chunks].length = li->li;
 | 
			
		||||
			chunks[num_chunks].is_complete = true;
 | 
			
		||||
		} else {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI contains "
 | 
			
		||||
				"invalid extension octet: LI=%d, E=%d, count=%d\n",
 | 
			
		||||
				li->li, li->e, num_chunks);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
 | 
			
		||||
			"extension octet: LI=%d, E=%d, count=%d\n",
 | 
			
		||||
			li->li, li->e, num_chunks);
 | 
			
		||||
		num_chunks += 1;
 | 
			
		||||
 | 
			
		||||
		if (e == 1) {
 | 
			
		||||
			/* There is space after the last chunk, add a final one */
 | 
			
		||||
			if (num_chunks == chunks_size) {
 | 
			
		||||
				LOGP(DRLCMACUL, LOGL_NOTICE,
 | 
			
		||||
					"UL DATA LI possibly extended, "
 | 
			
		||||
					"but no more chunks possible\n");
 | 
			
		||||
				return -ENOSPC;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			chunks[num_chunks].length = LENGTH_TO_END;
 | 
			
		||||
			chunks[num_chunks].is_complete = is_last_block;
 | 
			
		||||
			num_chunks += 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return num_chunks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len,
 | 
			
		||||
	unsigned int *offs,
 | 
			
		||||
	bool is_last_block,
 | 
			
		||||
	Decoding::RlcData *chunks, unsigned int chunks_size)
 | 
			
		||||
{
 | 
			
		||||
	const struct rlc_li_field *li;
 | 
			
		||||
	uint8_t m, e;
 | 
			
		||||
	unsigned int num_chunks = 0;
 | 
			
		||||
 | 
			
		||||
	e = 0;
 | 
			
		||||
	while (!e) {
 | 
			
		||||
		if (*offs > data_len) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
 | 
			
		||||
				"but no more data\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* get new E */
 | 
			
		||||
		li = (const struct rlc_li_field *)&data[*offs];
 | 
			
		||||
		e = li->e;
 | 
			
		||||
		m = li->m;
 | 
			
		||||
		*offs += 1;
 | 
			
		||||
 | 
			
		||||
		if (li->li == 0) {
 | 
			
		||||
			/* TS 44.060, 10.4.14, par 6 */
 | 
			
		||||
			e = 1;
 | 
			
		||||
			m = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* TS 44.060, table 10.4.13.1 */
 | 
			
		||||
		if (m == 0 && e == 0) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA "
 | 
			
		||||
				"ignored, because M='0' and E='0'.\n");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!chunks)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (num_chunks == chunks_size) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
 | 
			
		||||
				"but no more chunks possible\n");
 | 
			
		||||
			return -ENOSPC;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (li->li == 0)
 | 
			
		||||
			/* e is 1 here */
 | 
			
		||||
			chunks[num_chunks].length = LENGTH_TO_END;
 | 
			
		||||
		else
 | 
			
		||||
			chunks[num_chunks].length = li->li;
 | 
			
		||||
 | 
			
		||||
		chunks[num_chunks].is_complete = li->li || is_last_block;
 | 
			
		||||
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
 | 
			
		||||
			"extension octet: LI=%d, M=%d, E=%d, count=%d\n",
 | 
			
		||||
			li->li, li->m, li->e, num_chunks);
 | 
			
		||||
		num_chunks += 1;
 | 
			
		||||
 | 
			
		||||
		if (e == 1 && m == 1) {
 | 
			
		||||
			if (num_chunks == chunks_size) {
 | 
			
		||||
				LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
 | 
			
		||||
					"but no more chunks possible\n");
 | 
			
		||||
				return -ENOSPC;
 | 
			
		||||
			}
 | 
			
		||||
			/* TS 44.060, 10.4.13.1, row 4 */
 | 
			
		||||
			chunks[num_chunks].length = LENGTH_TO_END;
 | 
			
		||||
			chunks[num_chunks].is_complete = is_last_block;
 | 
			
		||||
			num_chunks += 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return num_chunks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Decoding::rlc_data_from_ul_data(
 | 
			
		||||
	const struct gprs_rlc_data_block_info *rdbi, enum CodingScheme cs,
 | 
			
		||||
	const uint8_t *data, RlcData *chunks, unsigned int chunks_size,
 | 
			
		||||
	uint32_t *tlli)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t e;
 | 
			
		||||
	unsigned int data_len = rdbi->data_len;
 | 
			
		||||
	int num_chunks = 0, i;
 | 
			
		||||
	unsigned int offs = 0;
 | 
			
		||||
	bool is_last_block = (rdbi->cv == 0);
 | 
			
		||||
 | 
			
		||||
	if (!chunks)
 | 
			
		||||
		chunks_size = 0;
 | 
			
		||||
 | 
			
		||||
	e = rdbi->e;
 | 
			
		||||
	if (e) {
 | 
			
		||||
		if (chunks_size > 0) {
 | 
			
		||||
			/* Block without LI means it only contains data of one LLC PDU */
 | 
			
		||||
			chunks[num_chunks].offset = offs;
 | 
			
		||||
			chunks[num_chunks].length = LENGTH_TO_END;
 | 
			
		||||
			chunks[num_chunks].is_complete = is_last_block;
 | 
			
		||||
			num_chunks += 1;
 | 
			
		||||
		} else if (chunks) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "No extension, "
 | 
			
		||||
				"but no more chunks possible\n");
 | 
			
		||||
			return -ENOSPC;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (mcs_is_edge(cs)) {
 | 
			
		||||
		/* if E is not set (LI follows), EGPRS */
 | 
			
		||||
		num_chunks = parse_extensions_egprs(data, data_len, &offs,
 | 
			
		||||
			is_last_block,
 | 
			
		||||
			chunks, chunks_size);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* if E is not set (LI follows), GPRS */
 | 
			
		||||
		num_chunks = parse_extensions_gprs(data, data_len, &offs,
 | 
			
		||||
			is_last_block,
 | 
			
		||||
			chunks, chunks_size);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (num_chunks < 0)
 | 
			
		||||
		return num_chunks;
 | 
			
		||||
 | 
			
		||||
	/* TLLI */
 | 
			
		||||
	if (rdbi->ti) {
 | 
			
		||||
		uint32_t tlli_enc;
 | 
			
		||||
		if (offs + 4 > data_len) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of block "
 | 
			
		||||
				"border\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		memcpy(&tlli_enc, data + offs, sizeof(tlli_enc));
 | 
			
		||||
		if (mcs_is_gprs(cs))
 | 
			
		||||
			/* The TLLI is encoded in big endian for GPRS (see
 | 
			
		||||
			 * TS 44.060, figure 10.2.2.1, note) */
 | 
			
		||||
			*tlli = be32toh(tlli_enc);
 | 
			
		||||
		else
 | 
			
		||||
			/* The TLLI is encoded in little endian for EGPRS (see
 | 
			
		||||
			 * TS 44.060, figure 10.3a.2.1, note 2) */
 | 
			
		||||
			*tlli = le32toh(tlli_enc);
 | 
			
		||||
 | 
			
		||||
		offs += sizeof(tlli_enc);
 | 
			
		||||
	} else {
 | 
			
		||||
		*tlli = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* PFI */
 | 
			
		||||
	if (rdbi->pi) {
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
 | 
			
		||||
			"please disable in SYSTEM INFORMATION\n");
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
 | 
			
		||||
		/* TODO: Skip all extensions with E=0 (see TS 44.060, 10.4.11 */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (chunks_size == 0)
 | 
			
		||||
		return num_chunks;
 | 
			
		||||
 | 
			
		||||
	/* LLC */
 | 
			
		||||
	for (i = 0; i < num_chunks; i++) {
 | 
			
		||||
		chunks[i].offset = offs;
 | 
			
		||||
		if (chunks[i].length == LENGTH_TO_END) {
 | 
			
		||||
			if (offs == data_len) {
 | 
			
		||||
				/* There is no place for an additional chunk,
 | 
			
		||||
				 * so drop it (this may happen with EGPRS since
 | 
			
		||||
				 * there is no M flag. */
 | 
			
		||||
				num_chunks -= 1;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			chunks[i].length = data_len - offs;
 | 
			
		||||
		}
 | 
			
		||||
		offs += chunks[i].length;
 | 
			
		||||
		if (offs > data_len) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA out of block "
 | 
			
		||||
				"border, chunk idx: %d, offset: %u, size: %d, data_len: %u\n",
 | 
			
		||||
				i, offs, chunks[i].length, data_len);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return num_chunks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
 | 
			
		||||
		if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_GPRS_multislot_class)
 | 
			
		||||
			continue;
 | 
			
		||||
		return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.GPRS_multislot_class;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
 | 
			
		||||
		if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_EGPRS_multislot_class)
 | 
			
		||||
			continue;
 | 
			
		||||
		return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.EGPRS_multislot_class;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * show_rbb needs to be an array with 65 elements
 | 
			
		||||
 * The index of the array is the bit position in the rbb
 | 
			
		||||
 * (show_rbb[63] relates to BSN ssn-1)
 | 
			
		||||
 */
 | 
			
		||||
void Decoding::extract_rbb(const uint8_t *rbb, char *show_rbb)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < 64; i++) {
 | 
			
		||||
		uint8_t bit;
 | 
			
		||||
 | 
			
		||||
		bit = !!(rbb[i/8] & (1<<(7-i%8)));
 | 
			
		||||
		show_rbb[i] = bit ? 'R' : 'I';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	show_rbb[64] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	for (i = 0; i < rbb->cur_bit; i++) {
 | 
			
		||||
		uint8_t bit;
 | 
			
		||||
		bit = bitvec_get_bit_pos(rbb, i);
 | 
			
		||||
		show_rbb[i] = bit == 1 ? 'R' : 'I';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	show_rbb[i] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	const uint8_t *data, enum CodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int cur_bit = 0;
 | 
			
		||||
	switch(mcs_header_type(cs)) {
 | 
			
		||||
	case HEADER_GPRS_DATA :
 | 
			
		||||
		cur_bit = rlc_parse_ul_data_header_gprs(rlc, data, cs);
 | 
			
		||||
		break;
 | 
			
		||||
	case HEADER_EGPRS_DATA_TYPE_3 :
 | 
			
		||||
		cur_bit = rlc_parse_ul_data_header_egprs_type_3(rlc, data, cs);
 | 
			
		||||
		break;
 | 
			
		||||
	case HEADER_EGPRS_DATA_TYPE_2 :
 | 
			
		||||
		cur_bit = rlc_parse_ul_data_header_egprs_type_2(rlc, data, cs);
 | 
			
		||||
		break;
 | 
			
		||||
	case HEADER_EGPRS_DATA_TYPE_1 :
 | 
			
		||||
		cur_bit = rlc_parse_ul_data_header_egprs_type_1(rlc, data, cs);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DRLCMACDL, LOGL_ERROR,
 | 
			
		||||
			"Decoding of uplink %s data blocks not yet supported.\n",
 | 
			
		||||
			mcs_name(cs));
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	return cur_bit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Decoding::rlc_parse_ul_data_header_egprs_type_3(
 | 
			
		||||
	struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	const uint8_t *data,
 | 
			
		||||
	const enum CodingScheme &cs)
 | 
			
		||||
{
 | 
			
		||||
	int punct, punct2, with_padding, cps;
 | 
			
		||||
	unsigned int e_ti_header, offs, cur_bit = 0;
 | 
			
		||||
	const struct gprs_rlc_ul_header_egprs_3 *egprs3;
 | 
			
		||||
 | 
			
		||||
	egprs3 = static_cast < struct gprs_rlc_ul_header_egprs_3 * >
 | 
			
		||||
			((void *)data);
 | 
			
		||||
 | 
			
		||||
	cps    = (egprs3->cps_hi << 0)  | (egprs3->cps_lo << 2);
 | 
			
		||||
	gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
 | 
			
		||||
	gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
 | 
			
		||||
 | 
			
		||||
	rlc->r      = egprs3->r;
 | 
			
		||||
	rlc->si     = egprs3->si;
 | 
			
		||||
	rlc->tfi    = (egprs3->tfi_hi << 0)  | (egprs3->tfi_lo << 2);
 | 
			
		||||
	rlc->cps    = cps;
 | 
			
		||||
	rlc->rsb    = egprs3->rsb;
 | 
			
		||||
 | 
			
		||||
	rlc->num_data_blocks = 1;
 | 
			
		||||
	rlc->block_info[0].cv  = egprs3->cv;
 | 
			
		||||
	rlc->block_info[0].pi  = egprs3->pi;
 | 
			
		||||
	rlc->block_info[0].spb = egprs3->spb;
 | 
			
		||||
	rlc->block_info[0].bsn =
 | 
			
		||||
			(egprs3->bsn1_hi << 0) | (egprs3->bsn1_lo << 5);
 | 
			
		||||
 | 
			
		||||
	cur_bit += rlc->data_offs_bits[0] - 2;
 | 
			
		||||
	offs = rlc->data_offs_bits[0] / 8;
 | 
			
		||||
	OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 1);
 | 
			
		||||
	e_ti_header = (data[offs-1] + (data[offs] << 8)) >> 7;
 | 
			
		||||
	rlc->block_info[0].e   = !!(e_ti_header & 0x01);
 | 
			
		||||
	rlc->block_info[0].ti  = !!(e_ti_header & 0x02);
 | 
			
		||||
	cur_bit += 2;
 | 
			
		||||
	/* skip data area */
 | 
			
		||||
	cur_bit += mcs_max_data_block_bytes(cs) * 8;
 | 
			
		||||
 | 
			
		||||
	return cur_bit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Decoding::rlc_parse_ul_data_header_egprs_type_2(
 | 
			
		||||
	struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	const uint8_t *data,
 | 
			
		||||
	const enum CodingScheme &cs)
 | 
			
		||||
{
 | 
			
		||||
	const struct gprs_rlc_ul_header_egprs_2 *egprs2;
 | 
			
		||||
	unsigned int e_ti_header, offs, cur_bit = 0;
 | 
			
		||||
	int punct, punct2, with_padding, cps;
 | 
			
		||||
 | 
			
		||||
	egprs2 = static_cast < struct gprs_rlc_ul_header_egprs_2 * >
 | 
			
		||||
			((void *)data);
 | 
			
		||||
 | 
			
		||||
	cps    = (egprs2->cps_hi << 0)  | (egprs2->cps_lo << 2);
 | 
			
		||||
	gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
 | 
			
		||||
	gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
 | 
			
		||||
 | 
			
		||||
	rlc->r      = egprs2->r;
 | 
			
		||||
	rlc->si     = egprs2->si;
 | 
			
		||||
	rlc->tfi    = (egprs2->tfi_hi << 0)  | (egprs2->tfi_lo << 2);
 | 
			
		||||
	rlc->cps    = cps;
 | 
			
		||||
	rlc->rsb    = egprs2->rsb;
 | 
			
		||||
 | 
			
		||||
	rlc->num_data_blocks = 1;
 | 
			
		||||
	rlc->block_info[0].cv  = egprs2->cv;
 | 
			
		||||
	rlc->block_info[0].pi  = egprs2->pi;
 | 
			
		||||
	rlc->block_info[0].bsn =
 | 
			
		||||
		(egprs2->bsn1_hi << 0) | (egprs2->bsn1_lo << 5);
 | 
			
		||||
 | 
			
		||||
	cur_bit += rlc->data_offs_bits[0] - 2;
 | 
			
		||||
 | 
			
		||||
	offs = rlc->data_offs_bits[0] / 8;
 | 
			
		||||
	OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 7);
 | 
			
		||||
 | 
			
		||||
	e_ti_header = (data[offs] & 0x60) >> 5;
 | 
			
		||||
	rlc->block_info[0].e   = !!(e_ti_header & 0x01);
 | 
			
		||||
	rlc->block_info[0].ti  = !!(e_ti_header & 0x02);
 | 
			
		||||
	cur_bit += 2;
 | 
			
		||||
 | 
			
		||||
	/* skip data area */
 | 
			
		||||
	cur_bit += mcs_max_data_block_bytes(cs) * 8;
 | 
			
		||||
 | 
			
		||||
	return cur_bit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Decoding::rlc_parse_ul_data_header_egprs_type_1(
 | 
			
		||||
	struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	const uint8_t *data, const enum CodingScheme &cs)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlc_ul_header_egprs_1 *egprs1;
 | 
			
		||||
	unsigned int e_ti_header, cur_bit = 0, offs;
 | 
			
		||||
	int punct, punct2, with_padding;
 | 
			
		||||
 | 
			
		||||
	egprs1 = static_cast < struct gprs_rlc_ul_header_egprs_1 * >
 | 
			
		||||
		((void *)data);
 | 
			
		||||
	gprs_rlc_mcs_cps_decode(egprs1->cps, cs, &punct, &punct2,
 | 
			
		||||
		&with_padding);
 | 
			
		||||
	gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
 | 
			
		||||
 | 
			
		||||
	rlc->r      = egprs1->r;
 | 
			
		||||
	rlc->si     = egprs1->si;
 | 
			
		||||
	rlc->tfi    = (egprs1->tfi_hi << 0)  | (egprs1->tfi_lo << 2);
 | 
			
		||||
	rlc->cps    = egprs1->cps;
 | 
			
		||||
	rlc->rsb    = egprs1->rsb;
 | 
			
		||||
	rlc->num_data_blocks = 2;
 | 
			
		||||
	rlc->block_info[0].cv  = egprs1->cv;
 | 
			
		||||
	rlc->block_info[0].pi  = egprs1->pi;
 | 
			
		||||
	rlc->block_info[0].bsn =
 | 
			
		||||
			(egprs1->bsn1_hi << 0) | (egprs1->bsn1_lo << 5);
 | 
			
		||||
 | 
			
		||||
	cur_bit += rlc->data_offs_bits[0] - 2;
 | 
			
		||||
	offs = rlc->data_offs_bits[0] / 8;
 | 
			
		||||
	OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 0);
 | 
			
		||||
 | 
			
		||||
	e_ti_header = data[offs - 1] >> 6;
 | 
			
		||||
	rlc->block_info[0].e   = (e_ti_header & 0x01);
 | 
			
		||||
	rlc->block_info[0].ti  = !!(e_ti_header & 0x02);
 | 
			
		||||
	cur_bit += 2;
 | 
			
		||||
 | 
			
		||||
	rlc->block_info[1].cv  = egprs1->cv;
 | 
			
		||||
	rlc->block_info[1].pi  = egprs1->pi;
 | 
			
		||||
	rlc->block_info[1].bsn = rlc->block_info[0].bsn +
 | 
			
		||||
		((egprs1->bsn2_hi << 0) | (egprs1->bsn2_lo << 2));
 | 
			
		||||
	rlc->block_info[1].bsn = rlc->block_info[1].bsn &  (RLC_EGPRS_SNS - 1);
 | 
			
		||||
 | 
			
		||||
	if ((rlc->block_info[1].bsn != rlc->block_info[0].bsn) &&
 | 
			
		||||
			(rlc->block_info[0].cv == 0))
 | 
			
		||||
		rlc->block_info[0].cv = 1;
 | 
			
		||||
 | 
			
		||||
	cur_bit = rlc->data_offs_bits[1] - 2;
 | 
			
		||||
 | 
			
		||||
	offs = rlc->data_offs_bits[1] / 8;
 | 
			
		||||
	OSMO_ASSERT(rlc->data_offs_bits[1] % 8 == 2);
 | 
			
		||||
 | 
			
		||||
	e_ti_header = (data[offs] & (0x03));
 | 
			
		||||
	rlc->block_info[1].e   = (e_ti_header & 0x01);
 | 
			
		||||
	rlc->block_info[1].ti  = !!(e_ti_header & 0x02);
 | 
			
		||||
	cur_bit += 2;
 | 
			
		||||
	/* skip data area */
 | 
			
		||||
	cur_bit += mcs_max_data_block_bytes(cs) * 8;
 | 
			
		||||
 | 
			
		||||
	return cur_bit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Decoding::rlc_parse_ul_data_header_gprs(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	const uint8_t *data, const enum CodingScheme &cs)
 | 
			
		||||
{
 | 
			
		||||
	const struct rlc_ul_header *gprs;
 | 
			
		||||
	unsigned int cur_bit = 0;
 | 
			
		||||
 | 
			
		||||
	gprs = static_cast < struct rlc_ul_header * >
 | 
			
		||||
		((void *)data);
 | 
			
		||||
 | 
			
		||||
	gprs_rlc_data_info_init_ul(rlc, cs, false);
 | 
			
		||||
 | 
			
		||||
	rlc->r      = gprs->r;
 | 
			
		||||
	rlc->si     = gprs->si;
 | 
			
		||||
	rlc->tfi    = gprs->tfi;
 | 
			
		||||
	rlc->cps    = 0;
 | 
			
		||||
	rlc->rsb    = 0;
 | 
			
		||||
	rlc->num_data_blocks = 1;
 | 
			
		||||
	rlc->block_info[0].cv  = gprs->cv;
 | 
			
		||||
	rlc->block_info[0].pi  = gprs->pi;
 | 
			
		||||
	rlc->block_info[0].bsn = gprs->bsn;
 | 
			
		||||
	rlc->block_info[0].e   = gprs->e;
 | 
			
		||||
	rlc->block_info[0].ti  = gprs->ti;
 | 
			
		||||
	rlc->block_info[0].spb = 0;
 | 
			
		||||
	cur_bit += rlc->data_offs_bits[0];
 | 
			
		||||
	/* skip data area */
 | 
			
		||||
	cur_bit += mcs_max_data_block_bytes(cs) * 8;
 | 
			
		||||
 | 
			
		||||
	return cur_bit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Copy LSB bitstream RLC data block to byte aligned buffer.
 | 
			
		||||
 *
 | 
			
		||||
 * Note that the bitstream is encoded in LSB first order, so the two octets
 | 
			
		||||
 * 654321xx xxxxxx87 contain the octet 87654321 starting at bit position 3
 | 
			
		||||
 * (LSB has bit position 1). This is a different order than the one used by
 | 
			
		||||
 * CSN.1.
 | 
			
		||||
 *
 | 
			
		||||
 * \param data_block_idx  The block index, 0..1 for header type 1, 0 otherwise
 | 
			
		||||
 * \param src     A pointer to the start of the RLC block (incl. the header)
 | 
			
		||||
 * \param buffer  A data area of a least the size of the RLC block
 | 
			
		||||
 * \returns  the number of bytes copied
 | 
			
		||||
 */
 | 
			
		||||
unsigned int Decoding::rlc_copy_to_aligned_buffer(
 | 
			
		||||
	const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	unsigned int data_block_idx,
 | 
			
		||||
	const uint8_t *src, uint8_t *buffer)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int hdr_bytes;
 | 
			
		||||
	unsigned int extra_bits;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	uint8_t c, last_c;
 | 
			
		||||
	uint8_t *dst;
 | 
			
		||||
	const struct gprs_rlc_data_block_info *rdbi;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(data_block_idx < rlc->num_data_blocks);
 | 
			
		||||
	rdbi = &rlc->block_info[data_block_idx];
 | 
			
		||||
 | 
			
		||||
	hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3;
 | 
			
		||||
	extra_bits = (rlc->data_offs_bits[data_block_idx] & 7);
 | 
			
		||||
 | 
			
		||||
	if (extra_bits == 0) {
 | 
			
		||||
		/* It is aligned already */
 | 
			
		||||
		memmove(buffer, src + hdr_bytes, rdbi->data_len);
 | 
			
		||||
		return rdbi->data_len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dst = buffer;
 | 
			
		||||
	src = src + hdr_bytes;
 | 
			
		||||
	last_c = *(src++);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < rdbi->data_len; i++) {
 | 
			
		||||
		c = src[i];
 | 
			
		||||
		*(dst++) = (last_c >> extra_bits) | (c << (8 - extra_bits));
 | 
			
		||||
		last_c = c;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rdbi->data_len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \brief Get a pointer to byte aligned RLC data.
 | 
			
		||||
 *
 | 
			
		||||
 * Since the RLC data may not be byte aligned to the RLC block data such that a
 | 
			
		||||
 * single RLC data byte is spread over two RLC block bytes, this function
 | 
			
		||||
 * eventually uses the provided buffer as data storage.
 | 
			
		||||
 *
 | 
			
		||||
 * \param src     A pointer to the start of the RLC block (incl. the header)
 | 
			
		||||
 * \param buffer  A data area of a least the size of the RLC block
 | 
			
		||||
 * \returns A pointer to the RLC data start within src if it is aligned, and
 | 
			
		||||
 *          buffer otherwise.
 | 
			
		||||
 */
 | 
			
		||||
const uint8_t *Decoding::rlc_get_data_aligned(
 | 
			
		||||
	const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	unsigned int data_block_idx,
 | 
			
		||||
	const uint8_t *src, uint8_t *buffer)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int hdr_bytes;
 | 
			
		||||
	unsigned int extra_bits;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(data_block_idx < ARRAY_SIZE(rlc->data_offs_bits));
 | 
			
		||||
 | 
			
		||||
	hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3;
 | 
			
		||||
	extra_bits = (rlc->data_offs_bits[data_block_idx] & 7);
 | 
			
		||||
 | 
			
		||||
	if (extra_bits == 0)
 | 
			
		||||
		/* It is aligned already, return a pointer that refers to the
 | 
			
		||||
		 * original data. */
 | 
			
		||||
		return src + hdr_bytes;
 | 
			
		||||
 | 
			
		||||
	Decoding::rlc_copy_to_aligned_buffer(rlc, data_block_idx, src, buffer);
 | 
			
		||||
	return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_final_ack(bitvec *bits, int *bsn_begin, int *bsn_end,
 | 
			
		||||
	gprs_rlc_dl_window *window)
 | 
			
		||||
{
 | 
			
		||||
	int num_blocks, i;
 | 
			
		||||
 | 
			
		||||
	num_blocks = window->mod_sns(window->v_s() - window->v_a());
 | 
			
		||||
	for (i = 0; i < num_blocks; i++)
 | 
			
		||||
		bitvec_set_bit(bits, ONE);
 | 
			
		||||
 | 
			
		||||
	*bsn_begin = window->v_a();
 | 
			
		||||
	*bsn_end   = window->mod_sns(*bsn_begin + num_blocks);
 | 
			
		||||
	return num_blocks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Decoding::decode_egprs_acknack_bits(const EGPRS_AckNack_Desc_t *desc,
 | 
			
		||||
	bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window)
 | 
			
		||||
{
 | 
			
		||||
	int urbb_len = desc->URBB_LENGTH;
 | 
			
		||||
	int crbb_len = 0;
 | 
			
		||||
	int num_blocks = 0;
 | 
			
		||||
	struct bitvec urbb;
 | 
			
		||||
	int i;
 | 
			
		||||
	bool have_bitmap;
 | 
			
		||||
	int implicitly_acked_blocks;
 | 
			
		||||
	int ssn = desc->STARTING_SEQUENCE_NUMBER;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (desc->FINAL_ACK_INDICATION)
 | 
			
		||||
		return handle_final_ack(bits, bsn_begin, bsn_end, window);
 | 
			
		||||
 | 
			
		||||
	if (desc->Exist_CRBB)
 | 
			
		||||
		crbb_len = desc->CRBB_LENGTH;
 | 
			
		||||
 | 
			
		||||
	have_bitmap = (urbb_len + crbb_len) > 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * bow & bitmap present:
 | 
			
		||||
	 *   V(A)-> [ 11111...11111 0 SSN-> BBBBB...BBBBB ] (SSN+Nbits) .... V(S)
 | 
			
		||||
	 * bow & not bitmap present:
 | 
			
		||||
	 *   V(A)-> [ 11111...11111 ] . SSN .... V(S)
 | 
			
		||||
	 * not bow & bitmap present:
 | 
			
		||||
	 *   V(A)-> ... [ 0 SSN-> BBBBB...BBBBB ](SSN+N) .... V(S)
 | 
			
		||||
	 * not bow & not bitmap present:
 | 
			
		||||
	 *   V(A)-> ... [] . SSN .... V(S)
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (desc->BEGINNING_OF_WINDOW) {
 | 
			
		||||
		implicitly_acked_blocks = window->mod_sns(ssn - 1 - window->v_a());
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < implicitly_acked_blocks; i++)
 | 
			
		||||
			bitvec_set_bit(bits, ONE);
 | 
			
		||||
 | 
			
		||||
		num_blocks += implicitly_acked_blocks;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!have_bitmap)
 | 
			
		||||
		goto aborted;
 | 
			
		||||
 | 
			
		||||
	/* next bit refers to V(Q) and thus is always zero (and not
 | 
			
		||||
	 * transmitted) */
 | 
			
		||||
	bitvec_set_bit(bits, ZERO);
 | 
			
		||||
	num_blocks += 1;
 | 
			
		||||
 | 
			
		||||
	if (crbb_len > 0) {
 | 
			
		||||
		int old_len = bits->cur_bit;
 | 
			
		||||
 | 
			
		||||
		LOGP(DRLCMACDL, LOGL_DEBUG, "Compress bitmap exists, "
 | 
			
		||||
			"CRBB LEN = %d and Starting color code = %d",
 | 
			
		||||
			desc->CRBB_LENGTH, desc->CRBB_STARTING_COLOR_CODE);
 | 
			
		||||
		rc = egprs_compress::decompress_crbb(desc->CRBB_LENGTH,
 | 
			
		||||
			desc->CRBB_STARTING_COLOR_CODE, desc->CRBB, bits);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE,
 | 
			
		||||
				"Failed to decode CRBB: length %d, data '%s'\n",
 | 
			
		||||
				desc->CRBB_LENGTH, osmo_hexdump(
 | 
			
		||||
					desc->CRBB, (desc->CRBB_LENGTH + 7)/8));
 | 
			
		||||
			/* We don't know the SSN offset for the URBB,
 | 
			
		||||
			 * return what we have so far and assume the
 | 
			
		||||
			 * bitmap has stopped here */
 | 
			
		||||
			goto aborted;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		LOGP(DRLCMACDL, LOGL_DEBUG,
 | 
			
		||||
			"CRBB len: %d, decoded len: %d, cc: %d, crbb: '%s'\n",
 | 
			
		||||
			desc->CRBB_LENGTH, bits->cur_bit - old_len,
 | 
			
		||||
			desc->CRBB_STARTING_COLOR_CODE,
 | 
			
		||||
			osmo_hexdump(
 | 
			
		||||
				desc->CRBB, (desc->CRBB_LENGTH + 7)/8)
 | 
			
		||||
		    );
 | 
			
		||||
 | 
			
		||||
		num_blocks += (bits->cur_bit - old_len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	urbb.cur_bit = 0;
 | 
			
		||||
	urbb.data = (uint8_t *)desc->URBB;
 | 
			
		||||
	urbb.data_len = sizeof(desc->URBB);
 | 
			
		||||
 | 
			
		||||
	for (i = urbb_len; i > 0; i--) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Set bit at the appropriate position (see 3GPP TS
 | 
			
		||||
		 * 44.060 12.3.1)
 | 
			
		||||
		 */
 | 
			
		||||
		int is_ack = bitvec_get_bit_pos(&urbb, i-1);
 | 
			
		||||
		bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO);
 | 
			
		||||
	}
 | 
			
		||||
	num_blocks += urbb_len;
 | 
			
		||||
 | 
			
		||||
aborted:
 | 
			
		||||
	*bsn_begin = window->v_a();
 | 
			
		||||
	*bsn_end   = window->mod_sns(*bsn_begin + num_blocks);
 | 
			
		||||
 | 
			
		||||
	return num_blocks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Decoding::decode_gprs_acknack_bits(const Ack_Nack_Description_t *desc,
 | 
			
		||||
	bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window)
 | 
			
		||||
{
 | 
			
		||||
	int urbb_len = RLC_GPRS_WS;
 | 
			
		||||
	int num_blocks;
 | 
			
		||||
	struct bitvec urbb;
 | 
			
		||||
 | 
			
		||||
	if (desc->FINAL_ACK_INDICATION)
 | 
			
		||||
		return handle_final_ack(bits, bsn_begin, bsn_end, window);
 | 
			
		||||
 | 
			
		||||
	*bsn_begin = window->v_a();
 | 
			
		||||
	*bsn_end   = desc->STARTING_SEQUENCE_NUMBER;
 | 
			
		||||
 | 
			
		||||
	num_blocks = window->mod_sns(*bsn_end - *bsn_begin);
 | 
			
		||||
 | 
			
		||||
	if (num_blocks < 0 || num_blocks > urbb_len) {
 | 
			
		||||
		*bsn_end  = *bsn_begin;
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_NOTICE,
 | 
			
		||||
			"Invalid GPRS Ack/Nack window %d:%d (length %d)\n",
 | 
			
		||||
			*bsn_begin, *bsn_end, num_blocks);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	urbb.cur_bit = 0;
 | 
			
		||||
	urbb.data = (uint8_t *)desc->RECEIVED_BLOCK_BITMAP;
 | 
			
		||||
	urbb.data_len = sizeof(desc->RECEIVED_BLOCK_BITMAP);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * TS 44.060, 12.3:
 | 
			
		||||
	 * BSN = (SSN - bit_number) modulo 128, for bit_number = 1 to 64.
 | 
			
		||||
	 * The BSN values represented range from (SSN - 1) mod 128 to (SSN - 64) mod 128.
 | 
			
		||||
	 *
 | 
			
		||||
	 * We are only interested in the range from V(A) to SSN-1 which is
 | 
			
		||||
	 * num_blocks large. The RBB is laid out as
 | 
			
		||||
	 *   [SSN-1] [SSN-2] ... [V(A)] ... [SSN-64]
 | 
			
		||||
	 * so we want to start with [V(A)] and go backwards until we reach
 | 
			
		||||
	 * [SSN-1] to get the needed BSNs in an increasing order. Note that
 | 
			
		||||
	 * the bit numbers are counted from the end of the buffer.
 | 
			
		||||
	 */
 | 
			
		||||
	for (int i = num_blocks; i > 0; i--) {
 | 
			
		||||
		int is_ack = bitvec_get_bit_pos(&urbb, urbb_len - i);
 | 
			
		||||
		bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return num_blocks;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										102
									
								
								src/decoding.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/decoding.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
/* decoding
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "gsm_rlcmac.h"
 | 
			
		||||
#include "coding_scheme.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
struct bitvec;
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
 | 
			
		||||
class Decoding {
 | 
			
		||||
public:
 | 
			
		||||
	/* represents (parts) LLC PDUs within one RLC Data block */
 | 
			
		||||
	struct RlcData {
 | 
			
		||||
		uint8_t	offset;
 | 
			
		||||
		uint8_t	length;
 | 
			
		||||
		bool	is_complete; /* if this PDU ends in this block */
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static int rlc_data_from_ul_data(
 | 
			
		||||
		const struct gprs_rlc_data_block_info *rdbi,
 | 
			
		||||
		enum CodingScheme cs, const uint8_t *data, RlcData *chunks,
 | 
			
		||||
		unsigned int chunks_size, uint32_t *tlli);
 | 
			
		||||
 | 
			
		||||
	static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
 | 
			
		||||
	static void extract_rbb(const struct bitvec *rbb, char *show_rbb);
 | 
			
		||||
	static int rlc_parse_ul_data_header_egprs_type_3(
 | 
			
		||||
		struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		const uint8_t *data,
 | 
			
		||||
		const enum CodingScheme &cs);
 | 
			
		||||
	static int rlc_parse_ul_data_header_egprs_type_2(
 | 
			
		||||
		struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		const uint8_t *data,
 | 
			
		||||
		const enum CodingScheme &cs);
 | 
			
		||||
	static int rlc_parse_ul_data_header_egprs_type_1(
 | 
			
		||||
		struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		const uint8_t *data,
 | 
			
		||||
		const enum CodingScheme &cs);
 | 
			
		||||
	static int rlc_parse_ul_data_header_gprs(
 | 
			
		||||
		struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		const uint8_t *data,
 | 
			
		||||
		const enum CodingScheme &cs);
 | 
			
		||||
	static int rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		const uint8_t *data, enum CodingScheme cs);
 | 
			
		||||
	static unsigned int rlc_copy_to_aligned_buffer(
 | 
			
		||||
		const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		unsigned int data_block_idx,
 | 
			
		||||
		const uint8_t *src, uint8_t *buffer);
 | 
			
		||||
	static const uint8_t *rlc_get_data_aligned(
 | 
			
		||||
		const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		unsigned int data_block_idx,
 | 
			
		||||
		const uint8_t *src, uint8_t *buffer);
 | 
			
		||||
	static int decode_egprs_acknack_bits(
 | 
			
		||||
		const EGPRS_AckNack_Desc_t *desc,
 | 
			
		||||
		struct bitvec *bits, int *bsn_begin, int *bsn_end,
 | 
			
		||||
		struct gprs_rlc_dl_window *window);
 | 
			
		||||
	static int decode_gprs_acknack_bits(
 | 
			
		||||
		const Ack_Nack_Description_t *desc,
 | 
			
		||||
		bitvec *bits, int *bsn_begin, int *bsn_end,
 | 
			
		||||
		gprs_rlc_dl_window *window);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* #ifdef __cplusplus */
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
 | 
			
		||||
uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										696
									
								
								src/egprs_rlc_compression.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										696
									
								
								src/egprs_rlc_compression.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,696 @@
 | 
			
		||||
/* egprs_rlc_compression.h
 | 
			
		||||
*  Routines for EGPRS RLC bitmap compression handling
 | 
			
		||||
*/
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <decoding.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <egprs_rlc_compression.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/stats.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define EGPRS_CODEWORDS		79 /* total number of codewords */
 | 
			
		||||
 | 
			
		||||
struct egprs_compress_node{
 | 
			
		||||
	struct egprs_compress_node *left;
 | 
			
		||||
	struct egprs_compress_node *right;
 | 
			
		||||
	int run_length;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
egprs_compress *egprs_compress::s_instance = 0;
 | 
			
		||||
 | 
			
		||||
egprs_compress_node *egprs_compress::create_tree_node(void *parent)
 | 
			
		||||
{
 | 
			
		||||
	egprs_compress_node *new_node;
 | 
			
		||||
 | 
			
		||||
	new_node = talloc_zero(parent, egprs_compress_node);
 | 
			
		||||
	new_node->left = NULL;
 | 
			
		||||
	new_node->right = NULL;
 | 
			
		||||
	new_node->run_length = -1;
 | 
			
		||||
	return new_node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
egprs_compress *egprs_compress::instance()
 | 
			
		||||
{
 | 
			
		||||
	if (!egprs_compress::s_instance)
 | 
			
		||||
		egprs_compress::s_instance = new egprs_compress;
 | 
			
		||||
	return egprs_compress::s_instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Expands the given tree by incorporating
 | 
			
		||||
 * the given codewords.
 | 
			
		||||
 * \param root[in] Root of ones or zeros tree
 | 
			
		||||
 * \param cdwd[in] Array of code words
 | 
			
		||||
 * number of codewords is EGPRS_CODEWORDS
 | 
			
		||||
 */
 | 
			
		||||
void egprs_compress::build_codewords(egprs_compress_node *root, const char *cdwd[])
 | 
			
		||||
{
 | 
			
		||||
	egprs_compress_node *iter;
 | 
			
		||||
	int len;
 | 
			
		||||
	int i;
 | 
			
		||||
	int idx;
 | 
			
		||||
 | 
			
		||||
	for (idx = 0; idx < EGPRS_CODEWORDS; idx++) {
 | 
			
		||||
		len = strlen((const char *)cdwd[idx]);
 | 
			
		||||
		iter = root;
 | 
			
		||||
		for (i = 0; i < len; i++) {
 | 
			
		||||
			if (cdwd[idx][i] == '0') {
 | 
			
		||||
				if (!iter->left)
 | 
			
		||||
					iter->left = create_tree_node(root);
 | 
			
		||||
				iter = iter->left;
 | 
			
		||||
			} else {
 | 
			
		||||
				if (!iter->right)
 | 
			
		||||
					iter->right = create_tree_node(root);
 | 
			
		||||
				iter = iter->right;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (iter) {
 | 
			
		||||
			/* The first 64 run lengths are 0, 1, 2, ..., 63
 | 
			
		||||
			 * and the following ones are 64, 128, 192 described in
 | 
			
		||||
			 * section 9.1.10 of 3gpp 44.060 */
 | 
			
		||||
			if (idx < 64)
 | 
			
		||||
				iter->run_length = idx;
 | 
			
		||||
			else
 | 
			
		||||
				iter->run_length = (idx - 63) * 64;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Terminating codes for uninterrupted sequences of 0 and 1 up to 64 bit length
 | 
			
		||||
 * according to TS 44.060 9.1.10
 | 
			
		||||
 */
 | 
			
		||||
static const unsigned t4_term[2][64] = {
 | 
			
		||||
	{
 | 
			
		||||
		0b0000110111,
 | 
			
		||||
		0b10,
 | 
			
		||||
		0b11,
 | 
			
		||||
		0b010,
 | 
			
		||||
		0b011,
 | 
			
		||||
		0b0011,
 | 
			
		||||
		0b0010,
 | 
			
		||||
		0b00011,
 | 
			
		||||
		0b000101,
 | 
			
		||||
		0b000100,
 | 
			
		||||
		0b0000100,
 | 
			
		||||
		0b0000101,
 | 
			
		||||
		0b0000111,
 | 
			
		||||
		0b00000100,
 | 
			
		||||
		0b00000111,
 | 
			
		||||
		0b000011000,
 | 
			
		||||
		0b0000010111,
 | 
			
		||||
		0b0000011000,
 | 
			
		||||
		0b0000001000,
 | 
			
		||||
		0b00001100111,
 | 
			
		||||
		0b00001101000,
 | 
			
		||||
		0b00001101100,
 | 
			
		||||
		0b00000110111,
 | 
			
		||||
		0b00000101000,
 | 
			
		||||
		0b00000010111,
 | 
			
		||||
		0b00000011000,
 | 
			
		||||
		0b000011001010,
 | 
			
		||||
		0b000011001011,
 | 
			
		||||
		0b000011001100,
 | 
			
		||||
		0b000011001101,
 | 
			
		||||
		0b000001101000,
 | 
			
		||||
		0b000001101001,
 | 
			
		||||
		0b000001101010,
 | 
			
		||||
		0b000001101011,
 | 
			
		||||
		0b000011010010,
 | 
			
		||||
		0b000011010011,
 | 
			
		||||
		0b000011010100,
 | 
			
		||||
		0b000011010101,
 | 
			
		||||
		0b000011010110,
 | 
			
		||||
		0b000011010111,
 | 
			
		||||
		0b000001101100,
 | 
			
		||||
		0b000001101101,
 | 
			
		||||
		0b000011011010,
 | 
			
		||||
		0b000011011011,
 | 
			
		||||
		0b000001010100,
 | 
			
		||||
		0b000001010101,
 | 
			
		||||
		0b000001010110,
 | 
			
		||||
		0b000001010111,
 | 
			
		||||
		0b000001100100,
 | 
			
		||||
		0b000001100101,
 | 
			
		||||
		0b000001010010,
 | 
			
		||||
		0b000001010011,
 | 
			
		||||
		0b000000100100,
 | 
			
		||||
		0b000000110111,
 | 
			
		||||
		0b000000111000,
 | 
			
		||||
		0b000000100111,
 | 
			
		||||
		0b000000101000,
 | 
			
		||||
		0b000001011000,
 | 
			
		||||
		0b000001011001,
 | 
			
		||||
		0b000000101011,
 | 
			
		||||
		0b000000101100,
 | 
			
		||||
		0b000001011010,
 | 
			
		||||
		0b000001100110,
 | 
			
		||||
		0b000001100111
 | 
			
		||||
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		0b00110101,
 | 
			
		||||
		0b000111,
 | 
			
		||||
		0b0111,
 | 
			
		||||
		0b1000,
 | 
			
		||||
		0b1011,
 | 
			
		||||
		0b1100,
 | 
			
		||||
		0b1110,
 | 
			
		||||
		0b1111,
 | 
			
		||||
		0b10011,
 | 
			
		||||
		0b10100,
 | 
			
		||||
		0b00111,
 | 
			
		||||
		0b01000,
 | 
			
		||||
		0b001000,
 | 
			
		||||
		0b000011,
 | 
			
		||||
		0b110100,
 | 
			
		||||
		0b110101,
 | 
			
		||||
		0b101010,
 | 
			
		||||
		0b101011,
 | 
			
		||||
		0b0100111,
 | 
			
		||||
		0b0001100,
 | 
			
		||||
		0b0001000,
 | 
			
		||||
		0b0010111,
 | 
			
		||||
		0b0000011,
 | 
			
		||||
		0b0000100,
 | 
			
		||||
		0b0101000,
 | 
			
		||||
		0b0101011,
 | 
			
		||||
		0b0010011,
 | 
			
		||||
		0b0100100,
 | 
			
		||||
		0b0011000,
 | 
			
		||||
		0b00000010,
 | 
			
		||||
		0b00000011,
 | 
			
		||||
		0b00011010,
 | 
			
		||||
		0b00011011,
 | 
			
		||||
		0b00010010,
 | 
			
		||||
		0b00010011,
 | 
			
		||||
		0b00010100,
 | 
			
		||||
		0b00010101,
 | 
			
		||||
		0b00010110,
 | 
			
		||||
		0b00010111,
 | 
			
		||||
		0b00101000,
 | 
			
		||||
		0b00101001,
 | 
			
		||||
		0b00101010,
 | 
			
		||||
		0b00101011,
 | 
			
		||||
		0b00101100,
 | 
			
		||||
		0b00101101,
 | 
			
		||||
		0b00000100,
 | 
			
		||||
		0b00000101,
 | 
			
		||||
		0b00001010,
 | 
			
		||||
		0b00001011,
 | 
			
		||||
		0b01010010,
 | 
			
		||||
		0b01010011,
 | 
			
		||||
		0b01010100,
 | 
			
		||||
		0b01010101,
 | 
			
		||||
		0b00100100,
 | 
			
		||||
		0b00100101,
 | 
			
		||||
		0b01011000,
 | 
			
		||||
		0b01011001,
 | 
			
		||||
		0b01011010,
 | 
			
		||||
		0b01011011,
 | 
			
		||||
		0b01001010,
 | 
			
		||||
		0b01001011,
 | 
			
		||||
		0b00110010,
 | 
			
		||||
		0b00110011,
 | 
			
		||||
		0b00110100
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
static const unsigned t4_term_length[2][64] = {
 | 
			
		||||
	{10, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
 | 
			
		||||
	{8, 6, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const unsigned t4_min_term_length[] = {2, 4};
 | 
			
		||||
static const unsigned t4_min_make_up_length[] = {10, 5};
 | 
			
		||||
 | 
			
		||||
static const unsigned t4_max_term_length[] = {12, 8};
 | 
			
		||||
static const unsigned t4_max_make_up_length[] = {13, 9};
 | 
			
		||||
 | 
			
		||||
static const unsigned t4_make_up_length[2][15] = {
 | 
			
		||||
	{10, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13},
 | 
			
		||||
	{5, 5, 6, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const unsigned t4_make_up_ind[15] = {64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960};
 | 
			
		||||
 | 
			
		||||
static const unsigned t4_make_up[2][15] = {
 | 
			
		||||
	{
 | 
			
		||||
		0b0000001111,
 | 
			
		||||
		0b000011001000,
 | 
			
		||||
		0b000011001001,
 | 
			
		||||
		0b000001011011,
 | 
			
		||||
		0b000000110011,
 | 
			
		||||
		0b000000110100,
 | 
			
		||||
		0b000000110101,
 | 
			
		||||
		0b0000001101100,
 | 
			
		||||
		0b0000001101101,
 | 
			
		||||
		0b0000001001010,
 | 
			
		||||
		0b0000001001011,
 | 
			
		||||
		0b0000001001100,
 | 
			
		||||
		0b0000001001101,
 | 
			
		||||
		0b0000001110010,
 | 
			
		||||
		0b0000001110011
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		0b11011,
 | 
			
		||||
		0b10010,
 | 
			
		||||
		0b010111,
 | 
			
		||||
		0b0110111,
 | 
			
		||||
		0b00110110,
 | 
			
		||||
		0b00110111,
 | 
			
		||||
		0b01100100,
 | 
			
		||||
		0b01100101,
 | 
			
		||||
		0b01101000,
 | 
			
		||||
		0b01100111,
 | 
			
		||||
		0b011001100,
 | 
			
		||||
		0b011001101,
 | 
			
		||||
		0b011010010,
 | 
			
		||||
		0b011010011,
 | 
			
		||||
		0b011010100
 | 
			
		||||
	 }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* The code words for one run length and zero
 | 
			
		||||
 * run length are described in table 9.1.10.1
 | 
			
		||||
 * of 3gpp 44.060
 | 
			
		||||
 */
 | 
			
		||||
const char *one_run_len_code_list[EGPRS_CODEWORDS] = {
 | 
			
		||||
	"00110101",
 | 
			
		||||
	"000111",
 | 
			
		||||
	"0111",
 | 
			
		||||
	"1000",
 | 
			
		||||
	"1011",
 | 
			
		||||
	"1100",
 | 
			
		||||
	"1110",
 | 
			
		||||
	"1111",
 | 
			
		||||
	"10011",
 | 
			
		||||
	"10100",
 | 
			
		||||
	"00111",
 | 
			
		||||
	"01000",
 | 
			
		||||
	"001000",
 | 
			
		||||
	"000011",
 | 
			
		||||
	"110100",
 | 
			
		||||
	"110101",
 | 
			
		||||
	"101010",
 | 
			
		||||
	"101011",
 | 
			
		||||
	"0100111",
 | 
			
		||||
	"0001100",
 | 
			
		||||
	"0001000",
 | 
			
		||||
	"0010111",
 | 
			
		||||
	"0000011",
 | 
			
		||||
	"0000100",
 | 
			
		||||
	"0101000",
 | 
			
		||||
	"0101011",
 | 
			
		||||
	"0010011",
 | 
			
		||||
	"0100100",
 | 
			
		||||
	"0011000",
 | 
			
		||||
	"00000010",
 | 
			
		||||
	"00000011",
 | 
			
		||||
	"00011010",
 | 
			
		||||
	"00011011",
 | 
			
		||||
	"00010010",
 | 
			
		||||
	"00010011",
 | 
			
		||||
	"00010100",
 | 
			
		||||
	"00010101",
 | 
			
		||||
	"00010110",
 | 
			
		||||
	"00010111",
 | 
			
		||||
	"00101000",
 | 
			
		||||
	"00101001",
 | 
			
		||||
	"00101010",
 | 
			
		||||
	"00101011",
 | 
			
		||||
	"00101100",
 | 
			
		||||
	"00101101",
 | 
			
		||||
	"00000100",
 | 
			
		||||
	"00000101",
 | 
			
		||||
	"00001010",
 | 
			
		||||
	"00001011",
 | 
			
		||||
	"01010010",
 | 
			
		||||
	"01010011",
 | 
			
		||||
	"01010100",
 | 
			
		||||
	"01010101",
 | 
			
		||||
	"00100100",
 | 
			
		||||
	"00100101",
 | 
			
		||||
	"01011000",
 | 
			
		||||
	"01011001",
 | 
			
		||||
	"01011010",
 | 
			
		||||
	"01011011",
 | 
			
		||||
	"01001010",
 | 
			
		||||
	"01001011",
 | 
			
		||||
	"00110010",
 | 
			
		||||
	"00110011",
 | 
			
		||||
	"00110100",
 | 
			
		||||
	"11011",
 | 
			
		||||
	"10010",
 | 
			
		||||
	"010111",
 | 
			
		||||
	"0110111",
 | 
			
		||||
	"00110110",
 | 
			
		||||
	"00110111",
 | 
			
		||||
	"01100100",
 | 
			
		||||
	"01100101",
 | 
			
		||||
	"01101000",
 | 
			
		||||
	"01100111",
 | 
			
		||||
	"011001100",
 | 
			
		||||
	"011001101",
 | 
			
		||||
	"011010010",
 | 
			
		||||
	"011010011",
 | 
			
		||||
	"011010100"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char *zero_run_len_code_list[EGPRS_CODEWORDS] = {
 | 
			
		||||
	"0000110111",
 | 
			
		||||
	"10",
 | 
			
		||||
	"11",
 | 
			
		||||
	"010",
 | 
			
		||||
	"011",
 | 
			
		||||
	"0011",
 | 
			
		||||
	"0010",
 | 
			
		||||
	"00011",
 | 
			
		||||
	"000101",
 | 
			
		||||
	"000100",
 | 
			
		||||
	"0000100",
 | 
			
		||||
	"0000101",
 | 
			
		||||
	"0000111",
 | 
			
		||||
	"00000100",
 | 
			
		||||
	"00000111",
 | 
			
		||||
	"000011000",
 | 
			
		||||
	"0000010111",
 | 
			
		||||
	"0000011000",
 | 
			
		||||
	"0000001000",
 | 
			
		||||
	"00001100111",
 | 
			
		||||
	"00001101000",
 | 
			
		||||
	"00001101100",
 | 
			
		||||
	"00000110111",
 | 
			
		||||
	"00000101000",
 | 
			
		||||
	"00000010111",
 | 
			
		||||
	"00000011000",
 | 
			
		||||
	"000011001010",
 | 
			
		||||
	"000011001011",
 | 
			
		||||
	"000011001100",
 | 
			
		||||
	"000011001101",
 | 
			
		||||
	"000001101000",
 | 
			
		||||
	"000001101001",
 | 
			
		||||
	"000001101010",
 | 
			
		||||
	"000001101011",
 | 
			
		||||
	"000011010010",
 | 
			
		||||
	"000011010011",
 | 
			
		||||
	"000011010100",
 | 
			
		||||
	"000011010101",
 | 
			
		||||
	"000011010110",
 | 
			
		||||
	"000011010111",
 | 
			
		||||
	"000001101100",
 | 
			
		||||
	"000001101101",
 | 
			
		||||
	"000011011010",
 | 
			
		||||
	"000011011011",
 | 
			
		||||
	"000001010100",
 | 
			
		||||
	"000001010101",
 | 
			
		||||
	"000001010110",
 | 
			
		||||
	"000001010111",
 | 
			
		||||
	"000001100100",
 | 
			
		||||
	"000001100101",
 | 
			
		||||
	"000001010010",
 | 
			
		||||
	"000001010011",
 | 
			
		||||
	"000000100100",
 | 
			
		||||
	"000000110111",
 | 
			
		||||
	"000000111000",
 | 
			
		||||
	"000000100111",
 | 
			
		||||
	"000000101000",
 | 
			
		||||
	"000001011000",
 | 
			
		||||
	"000001011001",
 | 
			
		||||
	"000000101011",
 | 
			
		||||
	"000000101100",
 | 
			
		||||
	"000001011010",
 | 
			
		||||
	"000001100110",
 | 
			
		||||
	"000001100111",
 | 
			
		||||
	"0000001111",
 | 
			
		||||
	"000011001000",
 | 
			
		||||
	"000011001001",
 | 
			
		||||
	"000001011011",
 | 
			
		||||
	"000000110011",
 | 
			
		||||
	"000000110100",
 | 
			
		||||
	"000000110101",
 | 
			
		||||
	"0000001101100",
 | 
			
		||||
	"0000001101101",
 | 
			
		||||
	"0000001001010",
 | 
			
		||||
	"0000001001011",
 | 
			
		||||
	"0000001001100",
 | 
			
		||||
	"0000001001101",
 | 
			
		||||
	"0000001110010",
 | 
			
		||||
	"0000001110011"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Calculate runlength of a  codeword
 | 
			
		||||
 * \param root[in]  Root of Ones or Zeros tree
 | 
			
		||||
 * \param bmbuf[in] Received compressed bitmap buf
 | 
			
		||||
 * \param length[in] Length of bitmap buf in bits
 | 
			
		||||
 * \param bit_pos[in] The start bit pos to read codeword
 | 
			
		||||
 * \param len_codewd[in] Length of code word
 | 
			
		||||
 * \param rlen[out] Calculated run length
 | 
			
		||||
 */
 | 
			
		||||
static int search_runlen(
 | 
			
		||||
		egprs_compress_node *root,
 | 
			
		||||
		const uint8_t *bmbuf,
 | 
			
		||||
		uint8_t length,
 | 
			
		||||
		uint8_t bit_pos,
 | 
			
		||||
		uint8_t *len_codewd,
 | 
			
		||||
		uint16_t *rlen)
 | 
			
		||||
{
 | 
			
		||||
	egprs_compress_node *iter;
 | 
			
		||||
	uint8_t dir;
 | 
			
		||||
 | 
			
		||||
	iter = root;
 | 
			
		||||
	*len_codewd = 0;
 | 
			
		||||
 | 
			
		||||
	while (iter->run_length == -1) {
 | 
			
		||||
		if ((!iter->left) && (!iter->right))
 | 
			
		||||
			return -1;
 | 
			
		||||
		if (bit_pos >= length)
 | 
			
		||||
			return -1;
 | 
			
		||||
 | 
			
		||||
		/* get the bit value at the bitpos and put it in right most of dir */
 | 
			
		||||
		dir = (bmbuf[bit_pos/8] >> (7 - (bit_pos & 0x07))) & 0x01;
 | 
			
		||||
		bit_pos++;
 | 
			
		||||
		(*len_codewd)++;
 | 
			
		||||
		if (!dir && (iter->left != NULL))
 | 
			
		||||
			iter = iter->left;
 | 
			
		||||
		else if (dir && (iter->right != NULL))
 | 
			
		||||
			iter = iter->right;
 | 
			
		||||
		else
 | 
			
		||||
			return -1;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DRLCMACUL, LOGL_DEBUG, "Run_length = %d\n", iter->run_length);
 | 
			
		||||
	*rlen = iter->run_length;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Decompress received block bitmap
 | 
			
		||||
 * \param compress_bmap_len[in] Compressed bitmap length
 | 
			
		||||
 * \param start[in] Starting Color Code, true if bitmap starts with a run
 | 
			
		||||
 *	    	    length of ones, false if zeros; see 9.1.10, 3GPP 44.060.
 | 
			
		||||
 * \param orig_crbb_buf[in] Received block crbb bitmap
 | 
			
		||||
 * \param dest[out] Uncompressed bitvector
 | 
			
		||||
 */
 | 
			
		||||
int egprs_compress::decompress_crbb(
 | 
			
		||||
		int8_t compress_bmap_len,
 | 
			
		||||
		bool start,
 | 
			
		||||
		const uint8_t *orig_crbb_buf,
 | 
			
		||||
		bitvec *dest)
 | 
			
		||||
{
 | 
			
		||||
	int8_t remaining_bmap_len = compress_bmap_len;
 | 
			
		||||
	uint8_t bit_pos = 0;
 | 
			
		||||
	uint8_t data;
 | 
			
		||||
	egprs_compress_node *list = NULL;
 | 
			
		||||
	uint8_t nbits = 0; /* number of bits of codeword */
 | 
			
		||||
	uint16_t run_length = 0;
 | 
			
		||||
	uint16_t cbmaplen = 0; /* compressed bitmap part after decompression */
 | 
			
		||||
	unsigned wp = dest->cur_bit;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	egprs_compress *compress = instance();
 | 
			
		||||
 | 
			
		||||
	while (remaining_bmap_len > 0) {
 | 
			
		||||
		if (start) {
 | 
			
		||||
			data = 0xff;
 | 
			
		||||
			list = compress->ones_list;
 | 
			
		||||
		} else {
 | 
			
		||||
			data = 0x00;
 | 
			
		||||
			list = compress->zeros_list;
 | 
			
		||||
		}
 | 
			
		||||
		rc = search_runlen(list, orig_crbb_buf, compress_bmap_len,
 | 
			
		||||
				bit_pos, &nbits, &run_length);
 | 
			
		||||
		if (rc == -1)
 | 
			
		||||
			return -1;
 | 
			
		||||
		/* If run length > 64, need makeup and terminating code */
 | 
			
		||||
		if (run_length < 64)
 | 
			
		||||
			start = !start;
 | 
			
		||||
		cbmaplen = cbmaplen + run_length;
 | 
			
		||||
 | 
			
		||||
		/* put run length of Ones in uncompressed bitmap */
 | 
			
		||||
		while (run_length != 0) {
 | 
			
		||||
			if (run_length > 8) {
 | 
			
		||||
				bitvec_write_field(dest, &wp, data, 8);
 | 
			
		||||
				run_length = run_length - 8;
 | 
			
		||||
			} else {
 | 
			
		||||
				bitvec_write_field(dest, &wp, data, run_length);
 | 
			
		||||
				run_length = 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		bit_pos = bit_pos + nbits;
 | 
			
		||||
		remaining_bmap_len = remaining_bmap_len - nbits;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void egprs_compress::decode_tree_init()
 | 
			
		||||
{
 | 
			
		||||
	ones_list = create_tree_node(tall_pcu_ctx);
 | 
			
		||||
	zeros_list = create_tree_node(tall_pcu_ctx);
 | 
			
		||||
	build_codewords(ones_list, one_run_len_code_list);
 | 
			
		||||
	build_codewords(zeros_list, zero_run_len_code_list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
egprs_compress::egprs_compress()
 | 
			
		||||
{
 | 
			
		||||
	decode_tree_init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compress received block bitmap
 | 
			
		||||
 * \param run_len_cnt[in] Count of number of 1's and 0's
 | 
			
		||||
 * \param codewrd_bitmap[in] Code word for coresponding run length.
 | 
			
		||||
 * \param crbb_vec[out] compressed bitvector.
 | 
			
		||||
 */
 | 
			
		||||
static void compress_bitmap(
 | 
			
		||||
		uint16_t *run_len_cnt,    /* cnt: run length count */
 | 
			
		||||
		uint16_t *codewrd_bitmap, /* code word */
 | 
			
		||||
		int16_t *codewrd_len, /* number of bits in the code word */
 | 
			
		||||
		bitvec *crbb_vec,  /* bitmap buffer to put code word in */
 | 
			
		||||
		bool start)
 | 
			
		||||
{
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	unsigned writeIndex = crbb_vec->cur_bit;
 | 
			
		||||
	*codewrd_bitmap = 0;
 | 
			
		||||
	*codewrd_len = 0;
 | 
			
		||||
	if (*run_len_cnt >= 64) {
 | 
			
		||||
		for (i = 0; i < 15; i++) {
 | 
			
		||||
			if (t4_make_up_ind[i] == *run_len_cnt) {
 | 
			
		||||
				*codewrd_bitmap = t4_make_up[start][i];
 | 
			
		||||
				*codewrd_len = t4_make_up_length[start][i];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		*codewrd_bitmap = t4_term[start][*run_len_cnt];
 | 
			
		||||
		*codewrd_len = t4_term_length[start][*run_len_cnt];
 | 
			
		||||
	}
 | 
			
		||||
	bitvec_write_field(crbb_vec, &writeIndex, *codewrd_bitmap, *codewrd_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compress received block bitmap */
 | 
			
		||||
int egprs_compress::osmo_t4_compress(struct bitvec *bv)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t crbb_len = 0;
 | 
			
		||||
	uint8_t uclen_crbb = 0;
 | 
			
		||||
	uint8_t crbb_bitmap[127] = {'\0'};
 | 
			
		||||
	bool start = (bv->data[0] & 0x80)>>7;
 | 
			
		||||
	struct bitvec crbb_vec;
 | 
			
		||||
 | 
			
		||||
	crbb_vec.data = crbb_bitmap;
 | 
			
		||||
	crbb_vec.cur_bit = 0;
 | 
			
		||||
	crbb_vec.data_len = 127;
 | 
			
		||||
	bv->data_len = bv->cur_bit;
 | 
			
		||||
	bv->cur_bit = 0;
 | 
			
		||||
	if (egprs_compress::compress_rbb(bv, &crbb_vec, &uclen_crbb, 23*8)) {
 | 
			
		||||
		memcpy(bv->data, crbb_bitmap, (crbb_len+7)/8);
 | 
			
		||||
		bv->cur_bit = crbb_len;
 | 
			
		||||
		bv->data_len = (crbb_len+7)/8;
 | 
			
		||||
		return start;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		printf("Encode failed\n");
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! \brief compression algorithm using T4 encoding
 | 
			
		||||
 *  the compressed bitmap's are copied in crbb_bitmap
 | 
			
		||||
 *  \param[in] rbb_vec bit vector to be encoded
 | 
			
		||||
 *  \return 1 if compression is success or 0 for failure
 | 
			
		||||
 */
 | 
			
		||||
int egprs_compress::compress_rbb(
 | 
			
		||||
		struct bitvec *urbb_vec,
 | 
			
		||||
		struct bitvec *crbb_vec,
 | 
			
		||||
		uint8_t *uclen_crbb,  /* Uncompressed bitmap len in CRBB */
 | 
			
		||||
		uint8_t max_bits)     /* max remaining bits */
 | 
			
		||||
{
 | 
			
		||||
	bool run_len_bit;
 | 
			
		||||
	int buflen = urbb_vec->cur_bit;
 | 
			
		||||
	int total_bits = urbb_vec->cur_bit;
 | 
			
		||||
	uint16_t rlen;
 | 
			
		||||
	uint16_t temprl = 0;
 | 
			
		||||
	uint16_t cbmap = 0;     /* Compressed code word */
 | 
			
		||||
	int16_t nbits;          /* Length of code word */
 | 
			
		||||
	uint16_t uclen = 0;
 | 
			
		||||
	int16_t clen = 0;
 | 
			
		||||
	bool start;		/* Starting color code see 9.1.10, 3GPP 44.060 */
 | 
			
		||||
	urbb_vec->cur_bit = 0;
 | 
			
		||||
	run_len_bit = (urbb_vec->data[0] & 0x80)>>7;
 | 
			
		||||
	while (buflen > 0) {
 | 
			
		||||
		temprl = 0;
 | 
			
		||||
		/* Find Run length */
 | 
			
		||||
		if (run_len_bit == 1)
 | 
			
		||||
			rlen = bitvec_rl_curbit(urbb_vec, true, total_bits);
 | 
			
		||||
		else
 | 
			
		||||
			rlen = bitvec_rl_curbit(urbb_vec, false, total_bits);
 | 
			
		||||
		buflen = buflen - rlen;
 | 
			
		||||
		/* if rlen > 64 need Makeup code word */
 | 
			
		||||
		/*Compress the bits */
 | 
			
		||||
		if (run_len_bit == 0) {
 | 
			
		||||
			start = 0;
 | 
			
		||||
			if (rlen >= 64) {
 | 
			
		||||
				temprl = (rlen/64)*64;
 | 
			
		||||
				compress_bitmap(&temprl, &cbmap, &nbits,
 | 
			
		||||
						crbb_vec, start);
 | 
			
		||||
				clen = clen + nbits;
 | 
			
		||||
			}
 | 
			
		||||
			temprl = MOD64(rlen);
 | 
			
		||||
			compress_bitmap(&temprl, &cbmap, &nbits,
 | 
			
		||||
						crbb_vec, start);
 | 
			
		||||
			/* next time the run length will be Ones */
 | 
			
		||||
			run_len_bit = 1;
 | 
			
		||||
		} else {
 | 
			
		||||
			start = 1;
 | 
			
		||||
			if (rlen >= 64) {
 | 
			
		||||
				temprl = (rlen/64)*64;
 | 
			
		||||
				compress_bitmap(&temprl, &cbmap, &nbits,
 | 
			
		||||
						crbb_vec, start);
 | 
			
		||||
				clen = clen + nbits;
 | 
			
		||||
			}
 | 
			
		||||
			temprl = MOD64(rlen);
 | 
			
		||||
			compress_bitmap(&temprl, &cbmap, &nbits,
 | 
			
		||||
						crbb_vec, start);
 | 
			
		||||
 | 
			
		||||
			/* next time the run length will be Zeros */
 | 
			
		||||
			run_len_bit = 0;
 | 
			
		||||
		}
 | 
			
		||||
		uclen = uclen + rlen;
 | 
			
		||||
		clen = clen + nbits;
 | 
			
		||||
		/*compressed bitmap exceeds the buffer space */
 | 
			
		||||
		if (clen > max_bits) {
 | 
			
		||||
			uclen = uclen - rlen;
 | 
			
		||||
			clen = clen - nbits;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	crbb_vec->cur_bit = clen;
 | 
			
		||||
	*uclen_crbb = uclen;
 | 
			
		||||
	if (clen >= uclen)
 | 
			
		||||
		/* No Gain is observed, So no need to compress */
 | 
			
		||||
		return 0;
 | 
			
		||||
	LOGP(DRLCMACUL, LOGL_DEBUG, "CRBB bitmap = %s\n", osmo_hexdump(crbb_vec->data, (crbb_vec->cur_bit+7)/8));
 | 
			
		||||
	/* Add compressed bitmap to final buffer */
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								src/egprs_rlc_compression.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/egprs_rlc_compression.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
/* egprs_rlc_compression.h
 | 
			
		||||
 *  Routines for EGPRS RLC bitmap compression handling
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct egprs_compress_node;
 | 
			
		||||
#define	 MOD64(X)	(((X) + 64) & 0x3F)
 | 
			
		||||
 | 
			
		||||
/* Singleton to manage the EGPRS compression algorithm. */
 | 
			
		||||
class egprs_compress
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	static int decompress_crbb(int8_t compress_bmap_len,
 | 
			
		||||
		bool start, const uint8_t *orig_buf,
 | 
			
		||||
		bitvec *dest);
 | 
			
		||||
	egprs_compress();
 | 
			
		||||
	int osmo_t4_compress(struct bitvec *bv);
 | 
			
		||||
	static int compress_rbb(struct bitvec *urbb_vec, struct bitvec *crbb_vec,
 | 
			
		||||
		uint8_t *uclen_crbb, uint8_t max_bits);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	egprs_compress_node *ones_list;
 | 
			
		||||
	egprs_compress_node *zeros_list;
 | 
			
		||||
 | 
			
		||||
	void decode_tree_init(void);
 | 
			
		||||
	static egprs_compress *s_instance;
 | 
			
		||||
	static egprs_compress*instance();
 | 
			
		||||
	egprs_compress_node *create_tree_node(void *);
 | 
			
		||||
	void build_codewords(egprs_compress_node *root, const char *cdwd[]);
 | 
			
		||||
	/* singleton class, so this private destructor is left unimplemented. */
 | 
			
		||||
	~egprs_compress();
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										1860
									
								
								src/encoding.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1860
									
								
								src/encoding.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										141
									
								
								src/encoding.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/encoding.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
/* encoding.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
#include <osmocom/gsm/l1sap.h>
 | 
			
		||||
#include "coding_scheme.h"
 | 
			
		||||
#include "gsm_rlcmac.h"
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf;
 | 
			
		||||
struct bitvec;
 | 
			
		||||
struct gprs_llc;
 | 
			
		||||
struct gprs_rlc_data_block_info;
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
/**
 | 
			
		||||
 * I help with encoding data into CSN1 messages.
 | 
			
		||||
 * TODO: Nobody can remember a function signature like this. One should
 | 
			
		||||
 * fill out a struct with the request parameters and then hand the struct
 | 
			
		||||
 * to the code.
 | 
			
		||||
 */
 | 
			
		||||
class Encoding {
 | 
			
		||||
public:
 | 
			
		||||
	static int write_immediate_assignment(
 | 
			
		||||
			const struct gprs_rlcmac_pdch *pdch,
 | 
			
		||||
			struct gprs_rlcmac_tbf *tbf,
 | 
			
		||||
			bitvec * dest, bool downlink, uint16_t ra,
 | 
			
		||||
			uint32_t ref_fn, uint8_t ta,
 | 
			
		||||
			uint8_t usf, bool polling,
 | 
			
		||||
			uint32_t fn, uint8_t alpha, uint8_t gamma,
 | 
			
		||||
			int8_t ta_idx,
 | 
			
		||||
			enum ph_burst_type burst_type);
 | 
			
		||||
 | 
			
		||||
	static int write_immediate_assignment_reject(
 | 
			
		||||
			bitvec *dest, uint16_t ra,
 | 
			
		||||
			uint32_t ref_fn,
 | 
			
		||||
			enum ph_burst_type burst_type,
 | 
			
		||||
			uint8_t t3142
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
	static void write_packet_uplink_assignment(
 | 
			
		||||
			RlcMacDownlink_t * block, uint8_t old_tfi,
 | 
			
		||||
			uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
 | 
			
		||||
			const struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t rrbp,
 | 
			
		||||
			uint8_t alpha, uint8_t gamma, int8_t ta_idx,
 | 
			
		||||
			bool use_egprs);
 | 
			
		||||
 | 
			
		||||
	static void write_packet_downlink_assignment(RlcMacDownlink_t * block,
 | 
			
		||||
			bool old_tfi_is_valid, uint8_t old_tfi, uint8_t old_downlink,
 | 
			
		||||
			const struct gprs_rlcmac_dl_tbf *tbf, uint8_t poll, uint8_t rrbp,
 | 
			
		||||
			uint8_t alpha, uint8_t gamma,
 | 
			
		||||
			int8_t ta_idx, uint8_t ta_ts, bool use_egprs);
 | 
			
		||||
 | 
			
		||||
	static void encode_rbb(const char *show_rbb, uint8_t *rbb);
 | 
			
		||||
 | 
			
		||||
	static void write_packet_access_reject(bitvec * dest, uint32_t tlli,
 | 
			
		||||
					       unsigned long t3172_ms);
 | 
			
		||||
 | 
			
		||||
	static void write_packet_uplink_ack(
 | 
			
		||||
			bitvec * dest, struct gprs_rlcmac_ul_tbf *tbf, bool is_final,
 | 
			
		||||
			uint8_t rrbp);
 | 
			
		||||
 | 
			
		||||
	static int write_paging_request(bitvec * dest, const struct osmo_mobile_identity *mi);
 | 
			
		||||
 | 
			
		||||
	static unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
 | 
			
		||||
			uint8_t *identity, uint8_t chan_needed);
 | 
			
		||||
 | 
			
		||||
	static unsigned write_packet_paging_request(bitvec * dest);
 | 
			
		||||
 | 
			
		||||
	static int rlc_write_dl_data_header(
 | 
			
		||||
			const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
			uint8_t *data);
 | 
			
		||||
	static unsigned int rlc_copy_from_aligned_buffer(
 | 
			
		||||
			const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
			unsigned int data_block_idx,
 | 
			
		||||
			uint8_t *dst, const uint8_t *buffer);
 | 
			
		||||
 | 
			
		||||
	enum AppendResult {
 | 
			
		||||
		AR_NEED_MORE_BLOCKS,
 | 
			
		||||
		AR_COMPLETED_SPACE_LEFT,
 | 
			
		||||
		AR_COMPLETED_BLOCK_FILLED,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static AppendResult rlc_data_to_dl_append(
 | 
			
		||||
		struct gprs_rlc_data_block_info *rdbi, enum CodingScheme cs,
 | 
			
		||||
		gprs_llc *llc, int *offset, int *num_chunks,
 | 
			
		||||
		uint8_t *data, bool is_final, int *count_payload);
 | 
			
		||||
	static void rlc_data_to_dl_append_egprs_li_padding(
 | 
			
		||||
		const struct gprs_rlc_data_block_info *rdbi,
 | 
			
		||||
		int *offset, int *num_chunks, uint8_t *data_block);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* ifdef __cplusplus */
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void write_packet_neighbour_cell_data(RlcMacDownlink_t *block,
 | 
			
		||||
		bool tfi_is_dl, uint8_t tfi, uint8_t container_id,
 | 
			
		||||
		uint8_t container_idx, PNCDContainer_t *container);
 | 
			
		||||
 | 
			
		||||
void write_packet_cell_change_continue(RlcMacDownlink_t *block, uint8_t poll, uint8_t rrbp,
 | 
			
		||||
				       bool tfi_is_dl, uint8_t tfi, bool exist_id,
 | 
			
		||||
				       uint16_t arfcn, uint8_t bsic, uint8_t container_id);
 | 
			
		||||
 | 
			
		||||
void write_packet_measurement_order(RlcMacDownlink_t *block, uint8_t poll, uint8_t rrbp,
 | 
			
		||||
				    bool tfi_assigned, bool tfi_is_dl, uint8_t tfi,
 | 
			
		||||
				    uint32_t tlli, uint8_t pmo_idx, uint8_t pmo_count,
 | 
			
		||||
				    uint8_t nco, bool exist_nc, uint8_t non_drx_period,
 | 
			
		||||
				    uint8_t nc_report_period_i, uint8_t nc_report_period_t,
 | 
			
		||||
				    const NC_Frequency_list_t *NC_Frequency_list);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										1332
									
								
								src/gprs_bssgp_pcu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1332
									
								
								src/gprs_bssgp_pcu.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,525 +0,0 @@
 | 
			
		||||
/* gprs_bssgp_pcu.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gprs_bssgp_pcu.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
 | 
			
		||||
struct sgsn_instance *sgsn;
 | 
			
		||||
void *tall_bsc_ctx;
 | 
			
		||||
struct bssgp_bvc_ctx *bctx = NULL;
 | 
			
		||||
struct gprs_nsvc *nsvc = NULL;
 | 
			
		||||
extern uint16_t spoof_mcc, spoof_mnc;
 | 
			
		||||
 | 
			
		||||
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
 | 
			
		||||
{
 | 
			
		||||
	struct bssgp_ud_hdr *budh;
 | 
			
		||||
 | 
			
		||||
	int8_t tfi; /* must be signed */
 | 
			
		||||
	uint32_t tlli;
 | 
			
		||||
	int i, j;
 | 
			
		||||
	uint8_t *data;
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf;
 | 
			
		||||
 | 
			
		||||
	budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
 | 
			
		||||
	tlli = ntohl(budh->tlli);
 | 
			
		||||
 | 
			
		||||
	/* LLC_PDU is mandatory IE */
 | 
			
		||||
	if (!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU))
 | 
			
		||||
	{
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD missing mandatory IE\n", tlli);
 | 
			
		||||
		return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
 | 
			
		||||
	len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
 | 
			
		||||
	if (len > sizeof(tbf->llc_frame))
 | 
			
		||||
	{
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD IE_LLC_PDU too large\n", tlli);
 | 
			
		||||
		return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* read IMSI. if no IMSI exists, use first paging block (any paging),
 | 
			
		||||
	 * because during attachment the IMSI might not be known, so the MS
 | 
			
		||||
	 * will listen to all paging blocks. */
 | 
			
		||||
	char imsi[16] = "000";
 | 
			
		||||
	if (TLVP_PRESENT(tp, BSSGP_IE_IMSI))
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI);
 | 
			
		||||
		uint8_t *bcd_imsi = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_IMSI);
 | 
			
		||||
		if ((bcd_imsi[0] & 0x08))
 | 
			
		||||
			imsi_len = imsi_len * 2 - 1;
 | 
			
		||||
		else
 | 
			
		||||
			imsi_len = (imsi_len - 1) * 2;
 | 
			
		||||
		for (i = 0, j = 0; j < imsi_len && j < 16; j++)
 | 
			
		||||
		{
 | 
			
		||||
			if (!(j & 1)) {
 | 
			
		||||
				imsi[j] = (bcd_imsi[i] >> 4) + '0';
 | 
			
		||||
				i++;
 | 
			
		||||
			} else
 | 
			
		||||
				imsi[j] = (bcd_imsi[i] & 0xf) + '0';
 | 
			
		||||
		}
 | 
			
		||||
		imsi[j] = '\0';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* parse ms radio access capability */
 | 
			
		||||
	uint8_t ms_class = 0;
 | 
			
		||||
	if (TLVP_PRESENT(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP))
 | 
			
		||||
	{
 | 
			
		||||
		bitvec *block;
 | 
			
		||||
		uint8_t cap_len = TLVP_LEN(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
 | 
			
		||||
		uint8_t *cap = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
 | 
			
		||||
		unsigned rp = 0;
 | 
			
		||||
 | 
			
		||||
		block = bitvec_alloc(cap_len);
 | 
			
		||||
		bitvec_unpack(block, cap);
 | 
			
		||||
		bitvec_read_field(block, rp, 4); // Access Technology Type
 | 
			
		||||
		bitvec_read_field(block, rp, 7); // Length of Access Capabilities
 | 
			
		||||
		bitvec_read_field(block, rp, 3); // RF Power Capability
 | 
			
		||||
		if (bitvec_read_field(block, rp, 1)) // A5 Bits Present
 | 
			
		||||
			bitvec_read_field(block, rp, 7); // A5 Bits
 | 
			
		||||
		bitvec_read_field(block, rp, 1); // ES IND
 | 
			
		||||
		bitvec_read_field(block, rp, 1); // PS
 | 
			
		||||
		bitvec_read_field(block, rp, 1); // VGCS
 | 
			
		||||
		bitvec_read_field(block, rp, 1); // VBS
 | 
			
		||||
		if (bitvec_read_field(block, rp, 1)) { // Multislot Cap Present
 | 
			
		||||
			if (bitvec_read_field(block, rp, 1)) // HSCSD Present
 | 
			
		||||
				bitvec_read_field(block, rp, 5); // Class
 | 
			
		||||
			if (bitvec_read_field(block, rp, 1)) { // GPRS Present
 | 
			
		||||
				ms_class = bitvec_read_field(block, rp, 5); // Class
 | 
			
		||||
				bitvec_read_field(block, rp, 1); // Ext.
 | 
			
		||||
			}
 | 
			
		||||
			if (bitvec_read_field(block, rp, 1)) // SMS Present
 | 
			
		||||
				bitvec_read_field(block, rp, 4); // SMS Value
 | 
			
		||||
				bitvec_read_field(block, rp, 4); // SMS Value
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	/* get lifetime */
 | 
			
		||||
	uint16_t delay_csec = 0xffff;
 | 
			
		||||
	if (TLVP_PRESENT(tp, BSSGP_IE_PDU_LIFETIME))
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t lt_len = TLVP_LEN(tp, BSSGP_IE_PDU_LIFETIME);
 | 
			
		||||
		uint16_t *lt = (uint16_t *) TLVP_VAL(tp, BSSGP_IE_PDU_LIFETIME);
 | 
			
		||||
		if (lt_len == 2)
 | 
			
		||||
			delay_csec = ntohs(*lt);
 | 
			
		||||
		else
 | 
			
		||||
			LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of "
 | 
			
		||||
				"PDU_LIFETIME IE\n");
 | 
			
		||||
	} else
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "BSSGP missing mandatory "
 | 
			
		||||
			"PDU_LIFETIME IE\n");
 | 
			
		||||
 | 
			
		||||
	LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
 | 
			
		||||
 | 
			
		||||
	/* check for existing TBF */
 | 
			
		||||
	if ((tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF))) {
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_INFO, "TBF: APPEND TFI: %u TLLI: 0x%08x\n", tbf->tfi, tbf->tlli);
 | 
			
		||||
		if (tbf->state == GPRS_RLCMAC_WAIT_RELEASE) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_DEBUG, "TBF in WAIT RELEASE state "
 | 
			
		||||
				"(T3193), so reuse TBF\n");
 | 
			
		||||
			memcpy(tbf->llc_frame, data, len);
 | 
			
		||||
			tbf->llc_length = len;
 | 
			
		||||
			memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset
 | 
			
		||||
								rlc states */
 | 
			
		||||
			if (!tbf->ms_class && ms_class)
 | 
			
		||||
				tbf->ms_class = ms_class;
 | 
			
		||||
			tbf_update(tbf);
 | 
			
		||||
			gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL);
 | 
			
		||||
		} else {
 | 
			
		||||
			/* the TBF exists, so we must write it in the queue
 | 
			
		||||
			 * we prepend lifetime in front of PDU */
 | 
			
		||||
			struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 | 
			
		||||
			struct timeval *tv;
 | 
			
		||||
			struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv),
 | 
			
		||||
				"llc_pdu_queue");
 | 
			
		||||
			if (!llc_msg)
 | 
			
		||||
				return -ENOMEM;
 | 
			
		||||
			tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
 | 
			
		||||
			if (bts->force_llc_lifetime)
 | 
			
		||||
				delay_csec = bts->force_llc_lifetime;
 | 
			
		||||
			/* keep timestap at 0 for infinite delay */
 | 
			
		||||
			if (delay_csec != 0xffff) {
 | 
			
		||||
				/* calculate timestamp of timeout */
 | 
			
		||||
				gettimeofday(tv, NULL);
 | 
			
		||||
				tv->tv_usec += (delay_csec % 100) * 10000;
 | 
			
		||||
				tv->tv_sec += delay_csec / 100;
 | 
			
		||||
				if (tv->tv_usec > 999999) {
 | 
			
		||||
					tv->tv_usec -= 1000000;
 | 
			
		||||
					tv->tv_sec++;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			memcpy(msgb_put(llc_msg, len), data, len);
 | 
			
		||||
			msgb_enqueue(&tbf->llc_queue, llc_msg);
 | 
			
		||||
			/* set ms class for updating TBF */
 | 
			
		||||
			if (!tbf->ms_class && ms_class)
 | 
			
		||||
				tbf->ms_class = ms_class;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		uint8_t trx, ts, use_trx, first_ts, ta, ss;
 | 
			
		||||
		struct gprs_rlcmac_tbf *old_tbf;
 | 
			
		||||
 | 
			
		||||
		/* check for uplink data, so we copy our informations */
 | 
			
		||||
		tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
 | 
			
		||||
		if (tbf && tbf->dir.ul.contention_resolution_done
 | 
			
		||||
		 && !tbf->dir.ul.final_ack_sent) {
 | 
			
		||||
			use_trx = tbf->trx;
 | 
			
		||||
			first_ts = tbf->first_ts;
 | 
			
		||||
			ta = tbf->ta;
 | 
			
		||||
			ss = 0;
 | 
			
		||||
			old_tbf = tbf;
 | 
			
		||||
		} else {
 | 
			
		||||
			use_trx = -1;
 | 
			
		||||
			first_ts = -1;
 | 
			
		||||
			ta = 0; /* FIXME: initial TA */
 | 
			
		||||
			ss = 1; /* PCH assignment only allows one timeslot */
 | 
			
		||||
			old_tbf = NULL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Create new TBF (any TRX)
 | 
			
		||||
		tfi = tfi_alloc(GPRS_RLCMAC_DL_TBF, &trx, &ts, use_trx, first_ts);
 | 
			
		||||
		if (tfi < 0) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
 | 
			
		||||
			/* FIXME: send reject */
 | 
			
		||||
			return -EBUSY;
 | 
			
		||||
		}
 | 
			
		||||
		/* set number of downlink slots according to multislot class */
 | 
			
		||||
		tbf = tbf_alloc(tbf, GPRS_RLCMAC_DL_TBF, tfi, trx, ts, ms_class,
 | 
			
		||||
			ss);
 | 
			
		||||
		if (!tbf) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
 | 
			
		||||
			/* FIXME: send reject */
 | 
			
		||||
			return -EBUSY;
 | 
			
		||||
		}
 | 
			
		||||
		tbf->tlli = tlli;
 | 
			
		||||
		tbf->tlli_valid = 1;
 | 
			
		||||
		tbf->ta = ta;
 | 
			
		||||
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %d TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
 | 
			
		||||
 | 
			
		||||
		/* new TBF, so put first frame */
 | 
			
		||||
		memcpy(tbf->llc_frame, data, len);
 | 
			
		||||
		tbf->llc_length = len;
 | 
			
		||||
 | 
			
		||||
		/* trigger downlink assignment and set state to ASSIGN.
 | 
			
		||||
		 * we don't use old_downlink, so the possible uplink is used
 | 
			
		||||
		 * to trigger downlink assignment. if there is no uplink,
 | 
			
		||||
		 * AGCH is used. */
 | 
			
		||||
		gprs_rlcmac_trigger_downlink_assignment(tbf, old_tbf, imsi);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
 | 
			
		||||
int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
 | 
			
		||||
{
 | 
			
		||||
	struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
 | 
			
		||||
	uint8_t pdu_type = bgph->pdu_type;
 | 
			
		||||
	unsigned rc = 0;
 | 
			
		||||
 | 
			
		||||
	if (!bctx)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* If traffic is received on a BVC that is marked as blocked, the
 | 
			
		||||
	* received PDU shall not be accepted and a STATUS PDU (Cause value:
 | 
			
		||||
	* BVC Blocked) shall be sent to the peer entity on the signalling BVC */
 | 
			
		||||
	if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS)
 | 
			
		||||
	{
 | 
			
		||||
		uint16_t bvci = msgb_bvci(msg);
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "rx BVC_S_BLOCKED\n");
 | 
			
		||||
		return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (pdu_type) {
 | 
			
		||||
	case BSSGP_PDUT_DL_UNITDATA:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
 | 
			
		||||
		gprs_bssgp_pcu_rx_dl_ud(msg, tp);
 | 
			
		||||
		break;
 | 
			
		||||
	case BSSGP_PDUT_PAGING_PS:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
 | 
			
		||||
		break;
 | 
			
		||||
	case BSSGP_PDUT_PAGING_CS:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
 | 
			
		||||
		break;
 | 
			
		||||
	case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RA_CAPA_UPDATE_ACK\n");
 | 
			
		||||
		break;
 | 
			
		||||
	case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_BVC_ACK\n");
 | 
			
		||||
		break;
 | 
			
		||||
	case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_MS_ACK\n");
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type);
 | 
			
		||||
		rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Receive a BSSGP PDU from a SGSN on a SIGNALLING BVCI */
 | 
			
		||||
int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
 | 
			
		||||
{
 | 
			
		||||
	struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	switch (bgph->pdu_type) {
 | 
			
		||||
	case BSSGP_PDUT_STATUS:
 | 
			
		||||
		/* Some exception has occurred */
 | 
			
		||||
		DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
 | 
			
		||||
		/* FIXME: send NM_STATUS.ind to NM */
 | 
			
		||||
		break;
 | 
			
		||||
		case BSSGP_PDUT_SUSPEND_ACK:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case BSSGP_PDUT_SUSPEND_NACK:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_NACK\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case BSSGP_PDUT_BVC_RESET_ACK:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case BSSGP_PDUT_PAGING_PS:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case BSSGP_PDUT_PAGING_CS:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case BSSGP_PDUT_RESUME_ACK:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_ACK\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case BSSGP_PDUT_RESUME_NACK:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_NACK\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case BSSGP_PDUT_FLUSH_LL:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLUSH_LL\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case BSSGP_PDUT_BVC_BLOCK_ACK:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case BSSGP_PDUT_BVC_UNBLOCK_ACK:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case BSSGP_PDUT_SGSN_INVOKE_TRACE:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", bctx->bvci, bgph->pdu_type);
 | 
			
		||||
			rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
 | 
			
		||||
	struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
 | 
			
		||||
	struct tlv_parsed tp;
 | 
			
		||||
	uint8_t pdu_type = bgph->pdu_type;
 | 
			
		||||
	uint16_t ns_bvci = msgb_bvci(msg);
 | 
			
		||||
	int data_len;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	struct bssgp_bvc_ctx *bctx;
 | 
			
		||||
 | 
			
		||||
	if (pdu_type == BSSGP_PDUT_STATUS) {
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u received STATUS\n",
 | 
			
		||||
			msgb_nsei(msg), ns_bvci);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
 | 
			
		||||
 | 
			
		||||
	/* UNITDATA BSSGP headers have TLLI in front */
 | 
			
		||||
	if (pdu_type != BSSGP_PDUT_UL_UNITDATA && pdu_type != BSSGP_PDUT_DL_UNITDATA)
 | 
			
		||||
	{
 | 
			
		||||
		data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
 | 
			
		||||
		rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		data_len = msgb_bssgp_len(msg) - sizeof(*budh);
 | 
			
		||||
		rc = bssgp_tlv_parse(&tp, budh->data, data_len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* look-up or create the BTS context for this BVC */
 | 
			
		||||
	bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
 | 
			
		||||
 | 
			
		||||
	if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET_ACK)
 | 
			
		||||
	{
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
 | 
			
		||||
			"type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
 | 
			
		||||
			pdu_type);
 | 
			
		||||
		return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bctx)
 | 
			
		||||
	{
 | 
			
		||||
		log_set_context(BSC_CTX_BVC, bctx);
 | 
			
		||||
		rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
 | 
			
		||||
		rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN], msgb_bssgp_len(msg));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ns_bvci == BVCI_SIGNALLING)
 | 
			
		||||
	{
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_SIGNALLING gprs_bssgp_rx_sign\n");
 | 
			
		||||
		rc = gprs_bssgp_pcu_rx_sign(msg, &tp, bctx);
 | 
			
		||||
	}
 | 
			
		||||
	else if (ns_bvci == BVCI_PTM)
 | 
			
		||||
	{
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTM bssgp_tx_status\n");
 | 
			
		||||
		rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTP gprs_bssgp_rx_ptp\n");
 | 
			
		||||
		rc = gprs_bssgp_pcu_rx_ptp(msg, &tp, bctx);
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case GPRS_NS_EVT_UNIT_DATA:
 | 
			
		||||
		/* hand the message into the BSSGP implementation */
 | 
			
		||||
		rc = gprs_bssgp_pcu_rcvmsg(msg);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DPCU, LOGL_NOTICE, "RLCMAC: Unknown event %u from NS\n", event);
 | 
			
		||||
		if (msg)
 | 
			
		||||
			talloc_free(msg);
 | 
			
		||||
		rc = -EIO;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nsvc_unblocked = 0;
 | 
			
		||||
 | 
			
		||||
static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
 | 
			
		||||
	void *handler_data, void *signal_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ns_signal_data *nssd;
 | 
			
		||||
 | 
			
		||||
	if (subsys != SS_L_NS)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	nssd = (struct ns_signal_data *)signal_data;
 | 
			
		||||
	if (nssd->nsvc != nsvc) {
 | 
			
		||||
		LOGP(DPCU, LOGL_ERROR, "Signal received of unknown NSVC\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (signal) {
 | 
			
		||||
	case S_NS_UNBLOCK:
 | 
			
		||||
		if (!nsvc_unblocked) {
 | 
			
		||||
			nsvc_unblocked = 1;
 | 
			
		||||
			LOGP(DPCU, LOGL_NOTICE, "NS-VC is unblocked.\n");
 | 
			
		||||
			bssgp_tx_bvc_reset(bctx, bctx->bvci,
 | 
			
		||||
				BSSGP_CAUSE_PROTO_ERR_UNSPEC);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case S_NS_BLOCK:
 | 
			
		||||
		if (nsvc_unblocked) {
 | 
			
		||||
			nsvc_unblocked = 0;
 | 
			
		||||
			LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n");
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* create BSSGP/NS layer instances */
 | 
			
		||||
int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
 | 
			
		||||
	uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
 | 
			
		||||
	uint16_t rac, uint16_t cell_id)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in dest;
 | 
			
		||||
 | 
			
		||||
	if (bctx)
 | 
			
		||||
		return 0; /* if already created, must return 0: no error */
 | 
			
		||||
 | 
			
		||||
	bssgp_nsi = gprs_ns_instantiate(&sgsn_ns_cb, NULL);
 | 
			
		||||
	if (!bssgp_nsi) {
 | 
			
		||||
		LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	gprs_ns_nsip_listen(bssgp_nsi);
 | 
			
		||||
 | 
			
		||||
	dest.sin_family = AF_INET;
 | 
			
		||||
	dest.sin_port = htons(sgsn_port);
 | 
			
		||||
	dest.sin_addr.s_addr = htonl(sgsn_ip);
 | 
			
		||||
 | 
			
		||||
	nsvc = gprs_ns_nsip_connect(bssgp_nsi, &dest, nsei, nsvci);
 | 
			
		||||
	if (!nsvc) {
 | 
			
		||||
		LOGP(DBSSGP, LOGL_ERROR, "Failed to create NSVCt\n");
 | 
			
		||||
		gprs_ns_destroy(bssgp_nsi);
 | 
			
		||||
		bssgp_nsi = NULL;
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bctx = btsctx_alloc(bvci, nsei);
 | 
			
		||||
	if (!bctx) {
 | 
			
		||||
		LOGP(DBSSGP, LOGL_ERROR, "Failed to create BSSGP context\n");
 | 
			
		||||
		nsvc = NULL;
 | 
			
		||||
		gprs_ns_destroy(bssgp_nsi);
 | 
			
		||||
		bssgp_nsi = NULL;
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	bctx->ra_id.mcc = spoof_mcc ? : mcc;
 | 
			
		||||
	bctx->ra_id.mnc = spoof_mnc ? : mnc;
 | 
			
		||||
	bctx->ra_id.lac = lac;
 | 
			
		||||
	bctx->ra_id.rac = rac;
 | 
			
		||||
	bctx->cell_id = cell_id;
 | 
			
		||||
 | 
			
		||||
	osmo_signal_register_handler(SS_L_NS, nsvc_signal_cb, NULL);
 | 
			
		||||
 | 
			
		||||
//	bssgp_tx_bvc_reset(bctx, bctx->bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_bssgp_destroy(void)
 | 
			
		||||
{
 | 
			
		||||
	if (!bssgp_nsi)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL);
 | 
			
		||||
 | 
			
		||||
	nsvc = NULL;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: move this to libgb: btsctx_free() */
 | 
			
		||||
	llist_del(&bctx->list);
 | 
			
		||||
	talloc_free(bctx);
 | 
			
		||||
	bctx = NULL;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: blocking... */
 | 
			
		||||
 | 
			
		||||
	gprs_ns_destroy(bssgp_nsi);
 | 
			
		||||
	bssgp_nsi = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
/* gprs_bssgp_pcu.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
@@ -20,44 +21,88 @@
 | 
			
		||||
#ifndef GPRS_BSSGP_PCU_H
 | 
			
		||||
#define GPRS_BSSGP_PCU_H
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/signal.h>
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns2.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp_bss.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_msgb.h>
 | 
			
		||||
 | 
			
		||||
int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause);
 | 
			
		||||
 | 
			
		||||
int bssgp_tx_ul_ud(struct bssgp_bvc_ctx *bctx, uint32_t tlli, const uint8_t *qos_profile, struct msgb *llc_pdu);
 | 
			
		||||
 | 
			
		||||
struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
 | 
			
		||||
#define QOS_PROFILE 0
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#define QOS_PROFILE 4
 | 
			
		||||
#define BSSGP_HDR_LEN 53
 | 
			
		||||
#define NS_HDR_LEN 4
 | 
			
		||||
#define IE_LLC_PDU 14
 | 
			
		||||
 | 
			
		||||
extern struct bssgp_bvc_ctx *bctx;
 | 
			
		||||
struct gprs_bssgp_pcu {
 | 
			
		||||
	struct bssgp_bvc_ctx *bctx;
 | 
			
		||||
 | 
			
		||||
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp);
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
 | 
			
		||||
int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
 | 
			
		||||
	struct osmo_timer_list bvc_timer;
 | 
			
		||||
 | 
			
		||||
int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
 | 
			
		||||
	/* state: is the NSVC unblocked? */
 | 
			
		||||
	int nsvc_unblocked;
 | 
			
		||||
 | 
			
		||||
int gprs_bssgp_pcu_rcvmsg(struct msgb *msg);
 | 
			
		||||
	/* state: true if bvc signalling needs to be reseted or waiting for reset ack */
 | 
			
		||||
	int bvc_sig_reset;
 | 
			
		||||
	/* state: true if bvc ptp needs to be reseted or waiting for reset ack */
 | 
			
		||||
	int bvc_reset;
 | 
			
		||||
	/* state: true if bvc ptp is unblocked */
 | 
			
		||||
	int bvc_unblocked;
 | 
			
		||||
 | 
			
		||||
int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
 | 
			
		||||
        uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
 | 
			
		||||
	        uint16_t rac, uint16_t cell_id);
 | 
			
		||||
	/* Flow control */
 | 
			
		||||
	struct timespec queue_delay_sum;
 | 
			
		||||
	unsigned queue_delay_count;
 | 
			
		||||
	uint8_t fc_tag;
 | 
			
		||||
	unsigned queue_frames_sent;
 | 
			
		||||
	unsigned queue_bytes_recv;
 | 
			
		||||
	unsigned queue_frames_recv;
 | 
			
		||||
 | 
			
		||||
void gprs_bssgp_destroy(void);
 | 
			
		||||
	/** callbacks below */
 | 
			
		||||
 | 
			
		||||
	/* The BSSGP has been unblocked */
 | 
			
		||||
	void (*on_unblock_ack)(struct gprs_bssgp_pcu *pcu);
 | 
			
		||||
 | 
			
		||||
	/* When BSSGP data arrives. The msgb is not only for reference */
 | 
			
		||||
	void (*on_dl_unit_data)(struct gprs_bssgp_pcu *pcu, struct msgb *msg,
 | 
			
		||||
				struct tlv_parsed *tp);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int gprs_gp_send_cb(void *ctx, struct msgb *msg);
 | 
			
		||||
int gprs_ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx);
 | 
			
		||||
void gprs_bssgp_update_queue_delay(const struct timespec *tv_recv,
 | 
			
		||||
		const struct timespec *tv_now);
 | 
			
		||||
void gprs_bssgp_update_frames_sent();
 | 
			
		||||
void gprs_bssgp_update_bytes_received(unsigned bytes_recv, unsigned frames_recv);
 | 
			
		||||
 | 
			
		||||
struct gprs_bssgp_pcu *gprs_bssgp_init(
 | 
			
		||||
		struct gprs_rlcmac_bts *bts,
 | 
			
		||||
		uint16_t nsei, uint16_t bvci,
 | 
			
		||||
		uint16_t mcc, uint16_t mnc, bool mnc_3_digits,
 | 
			
		||||
		uint16_t lac, uint16_t rac, uint16_t cell_id);
 | 
			
		||||
 | 
			
		||||
int gprs_ns_update_config(struct gprs_rlcmac_bts *bts, uint16_t nsei,
 | 
			
		||||
		   const struct osmo_sockaddr *local,
 | 
			
		||||
		   const struct osmo_sockaddr *remote,
 | 
			
		||||
		   uint16_t *nsvci, uint16_t valid);
 | 
			
		||||
 | 
			
		||||
void gprs_bssgp_destroy(struct gprs_rlcmac_bts *bts);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif // GPRS_BSSGP_PCU_H
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										294
									
								
								src/gprs_bssgp_rim.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								src/gprs_bssgp_rim.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,294 @@
 | 
			
		||||
/* gprs_bssgp_pcu.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp_rim.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/gsm/tlv.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/core/prim.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
 | 
			
		||||
#include "gprs_debug.h"
 | 
			
		||||
#include "gprs_pcu.h"
 | 
			
		||||
#include "bts.h"
 | 
			
		||||
#include "gprs_ms.h"
 | 
			
		||||
#include "nacc_fsm.h"
 | 
			
		||||
 | 
			
		||||
#define LOGPRIM(nsei, level, fmt, args...) \
 | 
			
		||||
	LOGP(DRIM, level, "(NSEI=%u) " fmt, nsei, ## args)
 | 
			
		||||
 | 
			
		||||
static inline void gprs_ra_id_ci_to_cgi_ps(struct osmo_cell_global_id_ps *cgi_ps,
 | 
			
		||||
					   const struct gprs_ra_id *raid, uint16_t cid)
 | 
			
		||||
{
 | 
			
		||||
	*cgi_ps = (struct osmo_cell_global_id_ps) {
 | 
			
		||||
		.rai.lac.plmn.mcc = raid->mcc,
 | 
			
		||||
		.rai.lac.plmn.mnc = raid->mnc,
 | 
			
		||||
		.rai.lac.plmn.mnc_3_digits = raid->mnc_3_digits,
 | 
			
		||||
		.rai.lac.lac = raid->lac,
 | 
			
		||||
		.rai.rac = raid->rac,
 | 
			
		||||
		.cell_identity = cid,
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mirror RIM routing information of a given PDU, see also 3GPP TS 48.018, section 8c.1.4.3 */
 | 
			
		||||
static void mirror_rim_routing_info(struct bssgp_ran_information_pdu *to_pdu,
 | 
			
		||||
				    const struct bssgp_ran_information_pdu *from_pdu)
 | 
			
		||||
{
 | 
			
		||||
	memcpy(&to_pdu->routing_info_dest, &from_pdu->routing_info_src, sizeof(to_pdu->routing_info_dest));
 | 
			
		||||
	memcpy(&to_pdu->routing_info_src, &from_pdu->routing_info_dest, sizeof(to_pdu->routing_info_src));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Fill NACC application container with data (cell identifier, sysinfo) */
 | 
			
		||||
#define SI_HDR_LEN 2
 | 
			
		||||
static void fill_app_cont_nacc(struct bssgp_ran_inf_app_cont_nacc *app_cont, const struct gprs_rlcmac_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	struct bssgp_bvc_ctx *bctx = the_pcu->bssgp.bctx;
 | 
			
		||||
 | 
			
		||||
	gprs_ra_id_ci_to_cgi_ps(&app_cont->reprt_cell, &bctx->ra_id, bctx->cell_id);
 | 
			
		||||
	app_cont->num_si = 0;
 | 
			
		||||
 | 
			
		||||
	/* Note: The BTS struct stores the system information including its pseudo header. The RIM application
 | 
			
		||||
	 * container defines the system information without pseudo header, so we need to chop it off. */
 | 
			
		||||
 | 
			
		||||
	if (bts->si1_is_set) {
 | 
			
		||||
		app_cont->si[app_cont->num_si] = bts->si1 + SI_HDR_LEN;
 | 
			
		||||
		app_cont->num_si++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bts->si3_is_set) {
 | 
			
		||||
		app_cont->si[app_cont->num_si] = bts->si3 + SI_HDR_LEN;
 | 
			
		||||
		app_cont->num_si++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bts->si13_is_set) {
 | 
			
		||||
		app_cont->si[app_cont->num_si] = bts->si13 + SI_HDR_LEN;
 | 
			
		||||
		app_cont->num_si++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Note: It is possible that the resulting PDU will not contain any system information, even if this is
 | 
			
		||||
	 * an unlikely case since the BTS immediately updates the system information after startup. The
 | 
			
		||||
	 * specification permits to send zero system information, see also: 3GPP TS 48.018 section 11.3.63.2.1 */
 | 
			
		||||
 | 
			
		||||
	if (!bts->si1_is_set || !bts->si3_is_set || !bts->si13_is_set)
 | 
			
		||||
		LOGP(DNACC, LOGL_INFO, "TX RAN INFO RESPONSE (NACC) %s: Some SI are missing:%s%s%s\n",
 | 
			
		||||
		     osmo_cgi_ps_name(&app_cont->reprt_cell),
 | 
			
		||||
		     bts->si1_is_set ? "" : " SI1",
 | 
			
		||||
		     bts->si3_is_set ? "" : " SI3",
 | 
			
		||||
		     bts->si13_is_set ? "" : " SI13");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Format a RAN INFORMATION PDU that contains the requested system information */
 | 
			
		||||
static void format_response_pdu(struct bssgp_ran_information_pdu *resp_pdu,
 | 
			
		||||
				const struct bssgp_ran_information_pdu *req_pdu,
 | 
			
		||||
				const struct gprs_rlcmac_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	memset(resp_pdu, 0, sizeof(*resp_pdu));
 | 
			
		||||
	mirror_rim_routing_info(resp_pdu, req_pdu);
 | 
			
		||||
 | 
			
		||||
	resp_pdu->decoded.rim_cont = (struct bssgp_ran_inf_rim_cont) {
 | 
			
		||||
		.app_id = BSSGP_RAN_INF_APP_ID_NACC,
 | 
			
		||||
		.seq_num = 1,	/* single report has only one message in response */
 | 
			
		||||
		.pdu_ind = {
 | 
			
		||||
			    .pdu_type_ext = RIM_PDU_TYPE_SING_REP,
 | 
			
		||||
			     },
 | 
			
		||||
		.prot_ver = 1,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	fill_app_cont_nacc(&resp_pdu->decoded.rim_cont.u.app_cont_nacc, bts);
 | 
			
		||||
	resp_pdu->decoded_present = true;
 | 
			
		||||
	resp_pdu->rim_cont_iei = BSSGP_IE_RI_RIM_CONTAINER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Format a RAN INFORMATION ERROR PDU */
 | 
			
		||||
static void format_response_pdu_err(struct bssgp_ran_information_pdu *resp_pdu,
 | 
			
		||||
				    const struct bssgp_ran_information_pdu *req_pdu)
 | 
			
		||||
{
 | 
			
		||||
	memset(resp_pdu, 0, sizeof(*resp_pdu));
 | 
			
		||||
	mirror_rim_routing_info(resp_pdu, req_pdu);
 | 
			
		||||
 | 
			
		||||
	resp_pdu->decoded.err_rim_cont = (struct bssgp_ran_inf_err_rim_cont) {
 | 
			
		||||
		.app_id = BSSGP_RAN_INF_APP_ID_NACC,
 | 
			
		||||
		.prot_ver = 1,
 | 
			
		||||
		.err_pdu = req_pdu->rim_cont,
 | 
			
		||||
		.err_pdu_len = req_pdu->rim_cont_len,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	resp_pdu->decoded_present = true;
 | 
			
		||||
	resp_pdu->rim_cont_iei = BSSGP_IE_RI_ERROR_RIM_COINTAINER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ran_info_response_nacc(const struct bssgp_ran_inf_app_cont_nacc *nacc, struct gprs_rlcmac_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	struct si_cache_value val;
 | 
			
		||||
	struct si_cache_entry *entry;
 | 
			
		||||
	struct llist_head *tmp;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRIM, LOGL_INFO, "Rx RAN-INFO cell=%s type=%sBCCH num_si=%d\n",
 | 
			
		||||
	     osmo_cgi_ps_name(&nacc->reprt_cell),
 | 
			
		||||
	     nacc->type_psi ? "P" : "", nacc->num_si);
 | 
			
		||||
 | 
			
		||||
	val.type_psi = nacc->type_psi;
 | 
			
		||||
	val.si_len = 0;
 | 
			
		||||
	for (i = 0; i < nacc->num_si; i++) {
 | 
			
		||||
		size_t len = val.type_psi ? BSSGP_RIM_PSI_LEN : BSSGP_RIM_SI_LEN;
 | 
			
		||||
		memcpy(&val.si_buf[val.si_len], nacc->si[i], len);
 | 
			
		||||
		val.si_len += len;
 | 
			
		||||
	}
 | 
			
		||||
	entry = si_cache_add(bts->pcu->si_cache, &nacc->reprt_cell, &val);
 | 
			
		||||
 | 
			
		||||
	llist_for_each(tmp, bts_ms_list(bts)) {
 | 
			
		||||
		struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
 | 
			
		||||
		if (!ms->nacc)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (ms->nacc->fi->state != NACC_ST_WAIT_REQUEST_SI)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (osmo_cgi_ps_cmp(&nacc->reprt_cell, &ms->nacc->cgi_ps) != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
		osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_SI, entry);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ran_info_request(const struct bssgp_ran_information_pdu *pdu,
 | 
			
		||||
				   struct gprs_rlcmac_bts *bts, uint16_t nsei)
 | 
			
		||||
{
 | 
			
		||||
	const struct bssgp_ran_inf_req_rim_cont *ri_req = &pdu->decoded.req_rim_cont;
 | 
			
		||||
	const struct bssgp_ran_inf_req_app_cont_nacc *nacc_req;
 | 
			
		||||
	struct bssgp_ran_information_pdu resp_pdu;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* Check if we support the application ID */
 | 
			
		||||
	if (ri_req->app_id != BSSGP_RAN_INF_APP_ID_NACC) {
 | 
			
		||||
		LOGPRIM(nsei, LOGL_ERROR,
 | 
			
		||||
			"Rx RAN-INFO-REQ for cell %s with unknown/wrong application ID %s -- reject.\n",
 | 
			
		||||
			osmo_cgi_ps_name(&bts->cgi_ps), bssgp_ran_inf_app_id_str(ri_req->app_id));
 | 
			
		||||
		format_response_pdu_err(&resp_pdu, pdu);
 | 
			
		||||
		rc = -ENOTSUP;
 | 
			
		||||
		goto tx_ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nacc_req = &ri_req->u.app_cont_nacc;
 | 
			
		||||
	rc = osmo_cgi_ps_cmp(&bts->cgi_ps, &nacc_req->reprt_cell);
 | 
			
		||||
	if (rc != 0) {
 | 
			
		||||
		LOGPRIM(nsei, LOGL_ERROR, "reporting cell in RIM application container %s "
 | 
			
		||||
			"does not match destination cell in RIM routing info %s -- rejected.\n",
 | 
			
		||||
			osmo_cgi_ps_name(&nacc_req->reprt_cell),
 | 
			
		||||
			osmo_cgi_ps_name2(&bts->cgi_ps));
 | 
			
		||||
		format_response_pdu_err(&resp_pdu, pdu);
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGPRIM(nsei, LOGL_INFO, "Responding to RAN INFORMATION REQUEST %s ...\n",
 | 
			
		||||
			osmo_cgi_ps_name(&nacc_req->reprt_cell));
 | 
			
		||||
		format_response_pdu(&resp_pdu, pdu, bts);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
tx_ret:
 | 
			
		||||
	bssgp_tx_rim(&resp_pdu, nsei);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ran_info_response(const struct bssgp_ran_information_pdu *pdu, struct gprs_rlcmac_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	const struct bssgp_ran_inf_rim_cont *ran_info = &pdu->decoded.rim_cont;
 | 
			
		||||
	char ri_src_str[64];
 | 
			
		||||
 | 
			
		||||
	/* Check if we support the application ID */
 | 
			
		||||
	if (ran_info->app_id != BSSGP_RAN_INF_APP_ID_NACC) {
 | 
			
		||||
		LOGP(DRIM, LOGL_ERROR,
 | 
			
		||||
		     "Rx RAN-INFO for cell %s with unknown/wrong application ID %s received -- reject.\n",
 | 
			
		||||
		     osmo_cgi_ps_name(&bts->cgi_ps), bssgp_ran_inf_app_id_str(ran_info->app_id));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ran_info->app_err) {
 | 
			
		||||
		LOGP(DRIM, LOGL_ERROR,
 | 
			
		||||
		     "%s Rx RAN-INFO with an app error! cause: %s\n",
 | 
			
		||||
		     bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &pdu->routing_info_src),
 | 
			
		||||
		     bssgp_nacc_cause_str(ran_info->u.app_err_cont_nacc.nacc_cause));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	handle_ran_info_response_nacc(&ran_info->u.app_cont_nacc, bts);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int handle_rim(struct osmo_bssgp_prim *bp)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = bp->oph.msg;
 | 
			
		||||
	uint16_t nsei = msgb_nsei(msg);
 | 
			
		||||
	struct bssgp_ran_information_pdu *pdu = &bp->u.rim_pdu;
 | 
			
		||||
	struct bssgp_ran_information_pdu resp_pdu;
 | 
			
		||||
	struct osmo_cell_global_id_ps dst_addr;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT (bp->oph.sap == SAP_BSSGP_RIM);
 | 
			
		||||
 | 
			
		||||
	/* At the moment we only support GERAN, so we block all other network
 | 
			
		||||
	 * types here. */
 | 
			
		||||
	if (pdu->routing_info_dest.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
 | 
			
		||||
		LOGPRIM(nsei, LOGL_ERROR,
 | 
			
		||||
			"Only GERAN supported, destination cell is not a GERAN cell -- rejected.\n");
 | 
			
		||||
		return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the RIM pdu is really addressed to this PCU. In case we
 | 
			
		||||
	 * receive a RIM PDU for a cell that is not parented by this PCU we
 | 
			
		||||
	 * are supposed to reject it with a BSSGP STATUS.
 | 
			
		||||
	 * see also: 3GPP TS 48.018, section 8c.3.1.2 */
 | 
			
		||||
	gprs_ra_id_ci_to_cgi_ps(&dst_addr, &pdu->routing_info_dest.geran.raid,
 | 
			
		||||
				pdu->routing_info_dest.geran.cid);
 | 
			
		||||
	bts = gprs_pcu_get_bts_by_cgi_ps(the_pcu, &dst_addr);
 | 
			
		||||
	if (!bts) {
 | 
			
		||||
		LOGPRIM(nsei, LOGL_ERROR, "Destination cell %s unknown to this pcu\n",
 | 
			
		||||
			osmo_cgi_ps_name(&dst_addr));
 | 
			
		||||
		return bssgp_tx_status(BSSGP_CAUSE_UNKN_DST, NULL, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the incoming RIM PDU is parseable, if not we must report
 | 
			
		||||
	 * an error to the controlling BSS 3GPP TS 48.018, 8c.3.4 and 8c.3.4.2 */
 | 
			
		||||
	if (!pdu->decoded_present) {
 | 
			
		||||
		LOGPRIM(nsei, LOGL_ERROR, "Erroneous RIM PDU received for cell %s -- reject.\n",
 | 
			
		||||
			osmo_cgi_ps_name(&dst_addr));
 | 
			
		||||
		format_response_pdu_err(&resp_pdu, pdu);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Handle incoming RIM container */
 | 
			
		||||
	switch (pdu->rim_cont_iei) {
 | 
			
		||||
	case BSSGP_IE_RI_REQ_RIM_CONTAINER:
 | 
			
		||||
		return handle_ran_info_request(pdu, bts, nsei);
 | 
			
		||||
	case BSSGP_IE_RI_RIM_CONTAINER:
 | 
			
		||||
		return handle_ran_info_response(pdu, bts);
 | 
			
		||||
	case BSSGP_IE_RI_APP_ERROR_RIM_CONT:
 | 
			
		||||
	case BSSGP_IE_RI_ACK_RIM_CONTAINER:
 | 
			
		||||
	case BSSGP_IE_RI_ERROR_RIM_COINTAINER:
 | 
			
		||||
		LOGPRIM(nsei, LOGL_ERROR, "RIM PDU not handled by this application\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	default:
 | 
			
		||||
		/* This should never happen. If the RIM PDU is parsed correctly, then the rim_cont_iei will
 | 
			
		||||
		 * be set to one of the cases above and if parsing fails this switch statement is guarded
 | 
			
		||||
		 * by the check on decoded_present above */
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								src/gprs_bssgp_rim.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/gprs_bssgp_rim.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
/* gprs_bssgp_rim.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct osmo_bssgp_prim;
 | 
			
		||||
 | 
			
		||||
int handle_rim(struct osmo_bssgp_prim *bp);
 | 
			
		||||
							
								
								
									
										180
									
								
								src/gprs_codel.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								src/gprs_codel.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
			
		||||
/* gprs_codel.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
 | 
			
		||||
 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gprs_codel.h"
 | 
			
		||||
#include "gprs_debug.h"
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/timer_compat.h>
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
static void control_law(struct gprs_codel *state, struct timespec *delta)
 | 
			
		||||
{
 | 
			
		||||
	/* 256 / sqrt(x), limited to 255 */
 | 
			
		||||
	static uint8_t inv_sqrt_tab[] = {255,
 | 
			
		||||
		255, 181, 147, 128, 114, 104, 96, 90, 85, 80, 77, 73, 71, 68,
 | 
			
		||||
		66, 64, 62, 60, 58, 57, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
 | 
			
		||||
		45, 45, 44, 43, 43, 42, 42, 41, 40, 40, 39, 39, 39, 38, 38, 37,
 | 
			
		||||
		37, 36, 36, 36, 35, 35, 35, 34, 34, 34, 33, 33, 33, 33, 32, 32,
 | 
			
		||||
		32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 29, 28,
 | 
			
		||||
		28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26,
 | 
			
		||||
		26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24,
 | 
			
		||||
		24, 24, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 22, 22, 22,
 | 
			
		||||
		22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 21, 21, 21, 21, 21, 21,
 | 
			
		||||
		21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
 | 
			
		||||
		20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
 | 
			
		||||
		19, 19, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 18, 18, 18,
 | 
			
		||||
		18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17,
 | 
			
		||||
		17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
 | 
			
		||||
		17, 17, 17, 17
 | 
			
		||||
	};
 | 
			
		||||
	uint_fast32_t delta_usecs;
 | 
			
		||||
	uint_fast32_t inv_sqrt;
 | 
			
		||||
	div_t q;
 | 
			
		||||
 | 
			
		||||
	if (state->count >= ARRAY_SIZE(inv_sqrt_tab))
 | 
			
		||||
		inv_sqrt = 16;
 | 
			
		||||
	else
 | 
			
		||||
		inv_sqrt = inv_sqrt_tab[state->count];
 | 
			
		||||
 | 
			
		||||
	/* delta = state->interval / sqrt(count) */
 | 
			
		||||
	delta_usecs = state->interval.tv_sec * 1000000 + state->interval.tv_nsec/1000;
 | 
			
		||||
	delta_usecs = delta_usecs * inv_sqrt / 256;
 | 
			
		||||
 | 
			
		||||
	q = div(delta_usecs, 1000000);
 | 
			
		||||
	delta->tv_sec = q.quot;
 | 
			
		||||
	delta->tv_nsec = q.rem * 1000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_codel_init(struct gprs_codel *state)
 | 
			
		||||
{
 | 
			
		||||
	static const struct gprs_codel init_state = {0};
 | 
			
		||||
 | 
			
		||||
	*state = init_state;
 | 
			
		||||
	gprs_codel_set_interval(state, -1);
 | 
			
		||||
	gprs_codel_set_maxpacket(state, -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms)
 | 
			
		||||
{
 | 
			
		||||
	div_t q;
 | 
			
		||||
 | 
			
		||||
	if (interval_ms <= 0)
 | 
			
		||||
		interval_ms = GPRS_CODEL_DEFAULT_INTERVAL_MS;
 | 
			
		||||
 | 
			
		||||
	q = div(interval_ms, 1000);
 | 
			
		||||
	state->interval.tv_sec = q.quot;
 | 
			
		||||
	state->interval.tv_nsec = q.rem * 1000000;
 | 
			
		||||
 | 
			
		||||
	/* target ~ 5% of interval */
 | 
			
		||||
	q = div(interval_ms * 13 / 256, 1000);
 | 
			
		||||
	state->target.tv_sec = q.quot;
 | 
			
		||||
	state->target.tv_nsec = q.rem * 1000000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	if (maxpacket < 0)
 | 
			
		||||
		maxpacket = GPRS_CODEL_DEFAULT_MAXPACKET;
 | 
			
		||||
 | 
			
		||||
	state->maxpacket = maxpacket;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is an broken up variant of the algorithm being described in
 | 
			
		||||
 * http://queue.acm.org/appendices/codel.html
 | 
			
		||||
 */
 | 
			
		||||
int gprs_codel_control(struct gprs_codel *state, const struct timespec *recv,
 | 
			
		||||
	const struct timespec *now, int bytes)
 | 
			
		||||
{
 | 
			
		||||
	struct timespec sojourn_time;
 | 
			
		||||
	struct timespec delta;
 | 
			
		||||
 | 
			
		||||
	if (recv == NULL)
 | 
			
		||||
		goto stop_dropping;
 | 
			
		||||
 | 
			
		||||
	timespecsub(now, recv, &sojourn_time);
 | 
			
		||||
 | 
			
		||||
	if (timespeccmp(&sojourn_time, &state->target, <))
 | 
			
		||||
		goto stop_dropping;
 | 
			
		||||
 | 
			
		||||
	if (bytes >= 0 && (unsigned)bytes <= state->maxpacket)
 | 
			
		||||
		goto stop_dropping;
 | 
			
		||||
 | 
			
		||||
	if (!timespecisset(&state->first_above_time)) {
 | 
			
		||||
		timespecadd(now, &state->interval, &state->first_above_time);
 | 
			
		||||
		goto not_ok_to_drop;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (timespeccmp(now, &state->first_above_time, <))
 | 
			
		||||
		goto not_ok_to_drop;
 | 
			
		||||
 | 
			
		||||
	/* Ok to drop */
 | 
			
		||||
 | 
			
		||||
	if (!state->dropping) {
 | 
			
		||||
		int recently = 0;
 | 
			
		||||
		int in_drop_cycle = 0;
 | 
			
		||||
		if (timespecisset(&state->drop_next)) {
 | 
			
		||||
			timespecsub(now, &state->drop_next, &delta);
 | 
			
		||||
			in_drop_cycle = timespeccmp(&delta, &state->interval, <);
 | 
			
		||||
			recently = in_drop_cycle;
 | 
			
		||||
		}
 | 
			
		||||
		if (!recently) {
 | 
			
		||||
			timespecsub(now, &state->first_above_time, &delta);
 | 
			
		||||
			recently = !timespeccmp(&delta, &state->interval, <);
 | 
			
		||||
		};
 | 
			
		||||
		if (!recently)
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		state->dropping = 1;
 | 
			
		||||
 | 
			
		||||
		if (in_drop_cycle && state->count > 2)
 | 
			
		||||
			state->count -= 2;
 | 
			
		||||
		else
 | 
			
		||||
			state->count = 1;
 | 
			
		||||
 | 
			
		||||
		state->drop_next = *now;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (timespeccmp(now, &state->drop_next, <))
 | 
			
		||||
			return 0;
 | 
			
		||||
 | 
			
		||||
		state->count += 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	control_law(state, &delta);
 | 
			
		||||
	timespecadd(&state->drop_next, &delta, &state->drop_next);
 | 
			
		||||
 | 
			
		||||
#if 1
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_INFO,
 | 
			
		||||
		"CoDel decided to drop packet, window = %d.%03dms, count = %d\n",
 | 
			
		||||
		(int)delta.tv_sec, (int)(delta.tv_nsec / 1000000), state->count);
 | 
			
		||||
#endif
 | 
			
		||||
	return 1;
 | 
			
		||||
 | 
			
		||||
stop_dropping:
 | 
			
		||||
	timespecclear(&state->first_above_time);
 | 
			
		||||
not_ok_to_drop:
 | 
			
		||||
	state->dropping = 0;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										108
									
								
								src/gprs_codel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/gprs_codel.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
/* gprs_codel.h
 | 
			
		||||
 *
 | 
			
		||||
 * This is an implementation of the CoDel algorithm based on the reference
 | 
			
		||||
 * pseudocode (see http://queue.acm.org/appendices/codel.html).
 | 
			
		||||
 * Instead of abstracting the queue itself, the following implementation
 | 
			
		||||
 * provides a time stamp based automaton. The main work is done by a single
 | 
			
		||||
 * decision function which updates the state and tells whether to pass or to
 | 
			
		||||
 * drop a packet after it has been taken from the queue.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
 | 
			
		||||
 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
/* Spec default values */
 | 
			
		||||
#define GPRS_CODEL_DEFAULT_INTERVAL_MS 100
 | 
			
		||||
#define GPRS_CODEL_DEFAULT_MAXPACKET 512
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct gprs_codel {
 | 
			
		||||
	int dropping;
 | 
			
		||||
	unsigned count;
 | 
			
		||||
	struct timespec first_above_time;
 | 
			
		||||
	struct timespec drop_next;
 | 
			
		||||
	struct timespec target;
 | 
			
		||||
	struct timespec interval;
 | 
			
		||||
	unsigned maxpacket;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
 * \brief Decide about packet drop and update CoDel state
 | 
			
		||||
 *
 | 
			
		||||
 * This function takes timing information and decides whether the packet in
 | 
			
		||||
 * question should be dropped in order to keep related queue in a 'good' state.
 | 
			
		||||
 * The function is meant to be called when the packet is dequeued.
 | 
			
		||||
 *
 | 
			
		||||
 * The CoDel state is updated by this function.
 | 
			
		||||
 *
 | 
			
		||||
 * \param state	 A pointer to the CoDel state of this queue
 | 
			
		||||
 * \param recv	 The time when the packet has entered the queue,
 | 
			
		||||
 *		 use NULL if dequeueing was not possible because the queue is
 | 
			
		||||
 *		 empty
 | 
			
		||||
 * \param now	 The current (dequeueing) time
 | 
			
		||||
 * \param bytes	 The number of bytes currently stored in the queue (-1 if
 | 
			
		||||
 *		 unknown)
 | 
			
		||||
 *
 | 
			
		||||
 * \return != 0 if the packet should be dropped, 0 otherwise
 | 
			
		||||
 */
 | 
			
		||||
int gprs_codel_control(struct gprs_codel *state, const struct timespec *recv,
 | 
			
		||||
	const struct timespec *now, int bytes);
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
 * \brief Initialise CoDel state
 | 
			
		||||
 *
 | 
			
		||||
 * This function initialises the CoDel state object. It sets the interval time
 | 
			
		||||
 * to the default value (GPRS_CODEL_DEFAULT_INTERVAL_MS).
 | 
			
		||||
 *
 | 
			
		||||
 * \param state		A pointer to the CoDel state of this queue
 | 
			
		||||
 */
 | 
			
		||||
void gprs_codel_init(struct gprs_codel *state);
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
 * \brief Set interval time
 | 
			
		||||
 *
 | 
			
		||||
 * This function changes the interval time.
 | 
			
		||||
 * The target time is derived from the interval time as proposed in the spec
 | 
			
		||||
 * (5% of interval time).
 | 
			
		||||
 *
 | 
			
		||||
 * \param state		A pointer to the CoDel state of this queue
 | 
			
		||||
 * \param interval_ms	The initial interval in ms to be used (<= 0 selects the
 | 
			
		||||
 *			default value)
 | 
			
		||||
 */
 | 
			
		||||
void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms);
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
 * \brief Set max packet size
 | 
			
		||||
 *
 | 
			
		||||
 * This function changes the maxpacket value. If no more than this number of
 | 
			
		||||
 * bytes are still stored in the queue, no dropping will be done.
 | 
			
		||||
 *
 | 
			
		||||
 * \param state		A pointer to the CoDel state of this queue
 | 
			
		||||
 * \param maxpacket	The value in bytes
 | 
			
		||||
 */
 | 
			
		||||
void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
/* gprs_debug.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2019 Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
@@ -16,56 +17,145 @@
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <strings.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
 | 
			
		||||
/* default categories */
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat default_categories[] = {
 | 
			
		||||
 	{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_INFO, 0},
 | 
			
		||||
 	{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_INFO, 1},
 | 
			
		||||
 	{"DRLCMAC", "\033[0;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_INFO, 1},
 | 
			
		||||
 	{"DRLCMACDATA", "\033[0;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
 | 
			
		||||
 	{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
 | 
			
		||||
 	{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
 | 
			
		||||
 	{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
 | 
			
		||||
	{"DRLCMACBW", "\033[1;31m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_INFO, 1},
 | 
			
		||||
 	{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO , 1},
 | 
			
		||||
	{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	_FLT_ALL = LOG_FILTER_ALL,	/* libosmocore */
 | 
			
		||||
	FLT_IMSI = 1,
 | 
			
		||||
	FLT_NSVC = 2,
 | 
			
		||||
	FLT_BVC  = 3,
 | 
			
		||||
	[DCSN1] = {
 | 
			
		||||
		.name = "DCSN1",
 | 
			
		||||
		.color = "\033[1;31m",
 | 
			
		||||
		.description = "Concrete Syntax Notation One (CSN1)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 0,
 | 
			
		||||
	},
 | 
			
		||||
	[DL1IF] = {
 | 
			
		||||
		.name = "DL1IF",
 | 
			
		||||
		.color = "\033[1;32m",
 | 
			
		||||
		.description = "GPRS PCU L1 interface (L1IF)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DRLCMAC] = {
 | 
			
		||||
		.name = "DRLCMAC",
 | 
			
		||||
		.color = "\033[0;33m",
 | 
			
		||||
		.description = "GPRS RLC/MAC layer (RLCMAC)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DRLCMACDATA] = {
 | 
			
		||||
		.name = "DRLCMACDATA",
 | 
			
		||||
		.color = "\033[0;33m",
 | 
			
		||||
		.description = "GPRS RLC/MAC layer Data (RLCMAC)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DRLCMACDL] = {
 | 
			
		||||
		.name = "DRLCMACDL",
 | 
			
		||||
		.color = "\033[1;33m",
 | 
			
		||||
		.description = "GPRS RLC/MAC layer Downlink (RLCMAC)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DRLCMACUL] = {
 | 
			
		||||
		.name = "DRLCMACUL",
 | 
			
		||||
		.color = "\033[1;36m",
 | 
			
		||||
		.description = "GPRS RLC/MAC layer Uplink (RLCMAC)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DRLCMACSCHED] = {
 | 
			
		||||
		.name = "DRLCMACSCHED",
 | 
			
		||||
		.color = "\033[0;36m",
 | 
			
		||||
		.description = "GPRS RLC/MAC layer Scheduling (RLCMAC)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DRLCMACMEAS] = {
 | 
			
		||||
		.name = "DRLCMACMEAS",
 | 
			
		||||
		.color = "\033[1;31m",
 | 
			
		||||
		.description = "GPRS RLC/MAC layer Measurements (RLCMAC)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DTBF] = {
 | 
			
		||||
		.name = "DTBF",
 | 
			
		||||
		.color = "\033[1;34m",
 | 
			
		||||
		.description = "Temporary Block Flow (TBF)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DTBFDL] = {
 | 
			
		||||
		.name = "DTBFDL",
 | 
			
		||||
		.color = "\033[1;34m",
 | 
			
		||||
		.description = "Temporary Block Flow (TBF) Downlink",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DTBFUL] = {
 | 
			
		||||
		.name = "DTBFUL",
 | 
			
		||||
		.color = "\033[1;34m",
 | 
			
		||||
		.description = "Temporary Block Flow (TBF) Uplink",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DNS] = {
 | 
			
		||||
		.name = "DNS",
 | 
			
		||||
		.color = "\033[1;34m",
 | 
			
		||||
		.description = "GPRS Network Service Protocol (NS)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DPCU] = {
 | 
			
		||||
		.name = "DPCU",
 | 
			
		||||
		.color = "\033[1;35m",
 | 
			
		||||
		.description = "GPRS Packet Control Unit (PCU)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DNACC] = {
 | 
			
		||||
		.name = "DNACC",
 | 
			
		||||
		.color = "\033[1;37m",
 | 
			
		||||
		.description = "Network Assisted Cell Change (NACC)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DRIM] = {
 | 
			
		||||
		.name = "DRIM",
 | 
			
		||||
		.color = "\033[1;38m",
 | 
			
		||||
		.description = "RAN Information Management (RIM)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
	[DANR] = {
 | 
			
		||||
		.name = "DANR",
 | 
			
		||||
		.color = "\033[1;37m",
 | 
			
		||||
		.description = "Automatic Neighbor Registration (ANR)",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int filter_fn(const struct log_context *ctx,
 | 
			
		||||
		     struct log_target *tar)
 | 
			
		||||
{
 | 
			
		||||
	const struct gprs_nsvc *nsvc = (const struct gprs_nsvc*)ctx->ctx[BSC_CTX_NSVC];
 | 
			
		||||
	const struct gprs_nsvc *bvc = (const struct gprs_nsvc*)ctx->ctx[BSC_CTX_BVC];
 | 
			
		||||
	const struct gprs_nsvc *nsvc = (const struct gprs_nsvc*)ctx->ctx[LOG_CTX_GB_NSVC];
 | 
			
		||||
	const struct gprs_nsvc *bvc = (const struct gprs_nsvc*)ctx->ctx[LOG_CTX_GB_BVC];
 | 
			
		||||
 | 
			
		||||
	/* Filter on the NS Virtual Connection */
 | 
			
		||||
	if ((tar->filter_map & (1 << FLT_NSVC)) != 0
 | 
			
		||||
	    && nsvc && (nsvc == tar->filter_data[FLT_NSVC]))
 | 
			
		||||
	if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0
 | 
			
		||||
	    && nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC]))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	/* Filter on the BVC */
 | 
			
		||||
	if ((tar->filter_map & (1 << FLT_BVC)) != 0
 | 
			
		||||
	    && bvc && (bvc == tar->filter_data[FLT_BVC]))
 | 
			
		||||
	if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0
 | 
			
		||||
	    && bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC]))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -16,15 +16,21 @@
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
#ifndef GPRS_DEBUG_H
 | 
			
		||||
#define GPRS_DEBUG_H
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* we used to have DBSSGP definded in each application, and applications telling
 | 
			
		||||
 * libosmogb which sub-system to use.  That creates problems and has been deprecated */
 | 
			
		||||
#define DBSSGP DLBSSGP
 | 
			
		||||
 | 
			
		||||
/* Debug Areas of the code */
 | 
			
		||||
enum {
 | 
			
		||||
	DCSN1,
 | 
			
		||||
@@ -34,39 +40,16 @@ enum {
 | 
			
		||||
	DRLCMACDL,
 | 
			
		||||
	DRLCMACUL,
 | 
			
		||||
	DRLCMACSCHED,
 | 
			
		||||
	DRLCMACBW,
 | 
			
		||||
	DBSSGP,
 | 
			
		||||
	DRLCMACMEAS,
 | 
			
		||||
	DTBF,
 | 
			
		||||
	DTBFDL,
 | 
			
		||||
	DTBFUL,
 | 
			
		||||
	DNS,
 | 
			
		||||
	DPCU,
 | 
			
		||||
	DNACC,
 | 
			
		||||
	DRIM,
 | 
			
		||||
	DANR,
 | 
			
		||||
	aDebug_LastEntry
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* context */
 | 
			
		||||
#define BSC_CTX_SUBSCR	1
 | 
			
		||||
#define BSC_CTX_NSVC	4
 | 
			
		||||
#define BSC_CTX_BVC		5
 | 
			
		||||
 | 
			
		||||
/* target */
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	//DEBUG_FILTER_ALL = 1 << 0,
 | 
			
		||||
	LOG_FILTER_IMSI = 1 << 1,
 | 
			
		||||
	LOG_FILTER_NSVC = 1 << 2,
 | 
			
		||||
	LOG_FILTER_BVC  = 1 << 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* we don't need a header dependency for this... */
 | 
			
		||||
 | 
			
		||||
struct gprs_nsvc;
 | 
			
		||||
struct bssgp_bvc_ctx;
 | 
			
		||||
 | 
			
		||||
void log_set_imsi_filter(struct log_target *target, const char *imsi);
 | 
			
		||||
void log_set_nsvc_filter(struct log_target *target,
 | 
			
		||||
			 struct gprs_nsvc *nsvc);
 | 
			
		||||
void log_set_bvc_filter(struct log_target *target,
 | 
			
		||||
			struct bssgp_bvc_ctx *bctx);
 | 
			
		||||
 | 
			
		||||
extern const struct log_info gprs_log_info;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // GPRS_DEBUG_H
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1040
									
								
								src/gprs_ms.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1040
									
								
								src/gprs_ms.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										265
									
								
								src/gprs_ms.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								src/gprs_ms.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,265 @@
 | 
			
		||||
/* gprs_ms.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015-2020 by Sysmocom s.f.m.c. GmbH
 | 
			
		||||
 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct gprs_codel;
 | 
			
		||||
 | 
			
		||||
#include "llc.h"
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
#include "tbf_ul.h"
 | 
			
		||||
#include "tbf_dl.h"
 | 
			
		||||
#include "pcu_l1_if.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
 | 
			
		||||
#include <osmocom/gsm/gsm48.h>
 | 
			
		||||
 | 
			
		||||
#include "coding_scheme.h"
 | 
			
		||||
#include <gsm_rlcmac.h>
 | 
			
		||||
#include "pcu_utils.h"
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
enum ms_counter_id {
 | 
			
		||||
	MS_CTR_DL_CTRL_MSG_SCHED,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
struct gprs_rlcmac_trx;
 | 
			
		||||
struct GprsMs;
 | 
			
		||||
 | 
			
		||||
struct gpr_ms_callback {
 | 
			
		||||
	void (*ms_idle)(struct GprsMs *);
 | 
			
		||||
	void (*ms_active)(struct GprsMs *);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GprsMs {
 | 
			
		||||
	struct llist_head list; /* list of all GprsMs */
 | 
			
		||||
	struct gpr_ms_callback cb;
 | 
			
		||||
	bool app_info_pending;
 | 
			
		||||
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
	struct gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
	struct gprs_rlcmac_dl_tbf *dl_tbf;
 | 
			
		||||
	struct llist_head old_tbfs; /* list of gprs_rlcmac_tbf */
 | 
			
		||||
 | 
			
		||||
	uint32_t tlli;
 | 
			
		||||
	uint32_t new_ul_tlli;
 | 
			
		||||
	uint32_t new_dl_tlli;
 | 
			
		||||
 | 
			
		||||
	/* store IMSI for look-up and PCH retransmission */
 | 
			
		||||
	char imsi[OSMO_IMSI_BUF_SIZE];
 | 
			
		||||
	uint8_t ta;
 | 
			
		||||
	uint8_t ms_class;
 | 
			
		||||
	uint8_t egprs_ms_class;
 | 
			
		||||
	/* current coding scheme */
 | 
			
		||||
	enum CodingScheme current_cs_ul;
 | 
			
		||||
	enum CodingScheme current_cs_dl;
 | 
			
		||||
 | 
			
		||||
	struct gprs_llc_queue llc_queue;
 | 
			
		||||
 | 
			
		||||
	bool is_idle;
 | 
			
		||||
	int ref;
 | 
			
		||||
	struct osmo_timer_list timer;
 | 
			
		||||
	unsigned delay;
 | 
			
		||||
 | 
			
		||||
	int64_t last_cs_not_low;
 | 
			
		||||
 | 
			
		||||
	struct pcu_l1_meas l1_meas;
 | 
			
		||||
	unsigned nack_rate_dl;
 | 
			
		||||
	uint8_t reserved_dl_slots;
 | 
			
		||||
	uint8_t reserved_ul_slots;
 | 
			
		||||
	struct gprs_rlcmac_trx *current_trx;
 | 
			
		||||
 | 
			
		||||
	struct gprs_codel *codel_state;
 | 
			
		||||
	enum mcs_kind mode;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr_group *ctrs;
 | 
			
		||||
	struct nacc_fsm_ctx *nacc;
 | 
			
		||||
	struct ms_anr_fsm_ctx *anr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli);
 | 
			
		||||
 | 
			
		||||
int ms_first_common_ts(const struct GprsMs *ms);
 | 
			
		||||
void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
 | 
			
		||||
			   uint8_t ul_slots, uint8_t dl_slots);
 | 
			
		||||
struct GprsMs *ms_ref(struct GprsMs *ms);
 | 
			
		||||
void ms_unref(struct GprsMs *ms);
 | 
			
		||||
void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode);
 | 
			
		||||
void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_);
 | 
			
		||||
void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_);
 | 
			
		||||
void ms_set_ta(struct GprsMs *ms, uint8_t ta_);
 | 
			
		||||
 | 
			
		||||
enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind);
 | 
			
		||||
enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms);
 | 
			
		||||
enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms);
 | 
			
		||||
void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme);
 | 
			
		||||
 | 
			
		||||
void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate);
 | 
			
		||||
uint8_t ms_current_pacch_slots(const struct GprsMs *ms);
 | 
			
		||||
 | 
			
		||||
void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms);
 | 
			
		||||
 | 
			
		||||
void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
void ms_set_tlli(struct GprsMs *ms, uint32_t tlli);
 | 
			
		||||
bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli);
 | 
			
		||||
void ms_set_imsi(struct GprsMs *ms, const char *imsi);
 | 
			
		||||
 | 
			
		||||
void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas);
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
static inline struct gprs_rlcmac_ul_tbf *ms_ul_tbf(const struct GprsMs *ms) {return ms->ul_tbf;}
 | 
			
		||||
static inline struct gprs_rlcmac_dl_tbf *ms_dl_tbf(const struct GprsMs *ms) {return ms->dl_tbf;}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb);
 | 
			
		||||
 | 
			
		||||
int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif);
 | 
			
		||||
bool ms_nacc_rts(const struct GprsMs *ms);
 | 
			
		||||
struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
 | 
			
		||||
 | 
			
		||||
int ms_anr_start(struct GprsMs *ms, const struct arfcn_bsic* cell_list, unsigned int num_cells);
 | 
			
		||||
bool ms_anr_rts(const struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
struct msgb *ms_anr_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
 | 
			
		||||
bool ms_anr_tbf_keep_open(const struct GprsMs *ms, const struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
static inline bool ms_is_idle(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return !ms->ul_tbf && !ms->dl_tbf && !ms->ref && llist_empty(&ms->old_tbfs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct gprs_llc_queue *ms_llc_queue(struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return &ms->llc_queue;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool ms_need_dl_tbf(struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	if (ms_dl_tbf(ms) != NULL &&
 | 
			
		||||
	    tbf_state((const struct gprs_rlcmac_tbf *)ms_dl_tbf(ms)) != TBF_ST_WAIT_RELEASE)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return llc_queue_size(ms_llc_queue(ms)) > 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint32_t ms_tlli(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	if (ms->new_ul_tlli != GSM_RESERVED_TMSI)
 | 
			
		||||
		return ms->new_ul_tlli;
 | 
			
		||||
	if (ms->tlli != GSM_RESERVED_TMSI)
 | 
			
		||||
		return ms->tlli;
 | 
			
		||||
 | 
			
		||||
	return ms->new_dl_tlli;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool ms_check_tlli(struct GprsMs *ms, uint32_t tlli)
 | 
			
		||||
{
 | 
			
		||||
	return tlli != GSM_RESERVED_TMSI &&
 | 
			
		||||
		(tlli == ms->tlli || tlli == ms->new_ul_tlli || tlli == ms->new_dl_tlli);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline const char *ms_imsi(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->imsi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool ms_imsi_valid(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->imsi[0] != '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint8_t ms_ta(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->ta;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint8_t ms_ms_class(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->ms_class;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint8_t ms_egprs_ms_class(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->egprs_ms_class;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline enum CodingScheme ms_current_cs_ul(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->current_cs_ul;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline enum mcs_kind ms_mode(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void ms_set_timeout(struct GprsMs *ms, unsigned secs)
 | 
			
		||||
{
 | 
			
		||||
	ms->delay = secs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct gprs_codel *ms_codel_state(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->codel_state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline unsigned ms_nack_rate_dl(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->nack_rate_dl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint8_t ms_reserved_dl_slots(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->reserved_dl_slots;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint8_t ms_reserved_ul_slots(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->reserved_ul_slots;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct gprs_rlcmac_trx *ms_current_trx(const struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	return ms->current_trx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define LOGPMS(ms, category, level, fmt, args...) \
 | 
			
		||||
	LOGP(category, level, "MS(TLLI=0x%08x, IMSI=%s, TA=%" PRIu8 ", %" PRIu8 "/%" PRIu8 ",%s%s) " fmt, \
 | 
			
		||||
	     ms_tlli(ms), ms_imsi(ms), ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms), \
 | 
			
		||||
	     ms_ul_tbf(ms) ? " UL": "", \
 | 
			
		||||
	     ms_dl_tbf(ms) ? " DL": "", \
 | 
			
		||||
	     ## args)
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										115
									
								
								src/gprs_ms_storage.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/gprs_ms_storage.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
			
		||||
/* gprs_ms_storage.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
 | 
			
		||||
 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "gprs_ms_storage.h"
 | 
			
		||||
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
#include "bts.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
	#include <osmocom/core/linuxlist.h>
 | 
			
		||||
	#include <osmocom/gsm/gsm48.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GPRS_UNDEFINED_IMSI "000"
 | 
			
		||||
 | 
			
		||||
static void ms_storage_ms_idle_cb(struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	llist_del(&ms->list);
 | 
			
		||||
	if (ms->bts)
 | 
			
		||||
		bts_stat_item_add(ms->bts, STAT_MS_PRESENT, -1);
 | 
			
		||||
	if (ms_is_idle(ms))
 | 
			
		||||
		talloc_free(ms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ms_storage_ms_active_cb(struct GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	/* Nothing to do */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct gpr_ms_callback ms_storage_ms_cb = {
 | 
			
		||||
	.ms_idle = ms_storage_ms_idle_cb,
 | 
			
		||||
	.ms_active = ms_storage_ms_active_cb,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GprsMsStorage::GprsMsStorage(struct gprs_rlcmac_bts *bts) :
 | 
			
		||||
	m_bts(bts)
 | 
			
		||||
{
 | 
			
		||||
	INIT_LLIST_HEAD(&m_list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GprsMsStorage::~GprsMsStorage()
 | 
			
		||||
{
 | 
			
		||||
	cleanup();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMsStorage::cleanup()
 | 
			
		||||
{
 | 
			
		||||
	struct llist_head *pos, *tmp;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_safe(pos, tmp, &m_list) {
 | 
			
		||||
		struct GprsMs *ms = llist_entry(pos, typeof(*ms), list);
 | 
			
		||||
		ms_set_callback(ms, NULL);
 | 
			
		||||
		ms_storage_ms_idle_cb(ms);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GprsMs *GprsMsStorage::get_ms(uint32_t tlli, uint32_t old_tlli, const char *imsi) const
 | 
			
		||||
{
 | 
			
		||||
	struct llist_head *tmp;
 | 
			
		||||
	GprsMs *ms;
 | 
			
		||||
 | 
			
		||||
	if (tlli != GSM_RESERVED_TMSI || old_tlli != GSM_RESERVED_TMSI) {
 | 
			
		||||
		llist_for_each(tmp, &m_list) {
 | 
			
		||||
			ms = llist_entry(tmp, typeof(*ms), list);
 | 
			
		||||
			if (ms_check_tlli(ms, tlli))
 | 
			
		||||
				return ms;
 | 
			
		||||
			if (ms_check_tlli(ms, old_tlli))
 | 
			
		||||
				return ms;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* not found by TLLI */
 | 
			
		||||
 | 
			
		||||
	if (imsi && imsi[0] && strcmp(imsi, GPRS_UNDEFINED_IMSI) != 0) {
 | 
			
		||||
		llist_for_each(tmp, &m_list) {
 | 
			
		||||
			ms = llist_entry(tmp, typeof(*ms), list);
 | 
			
		||||
			if (strcmp(imsi, ms_imsi(ms)) == 0)
 | 
			
		||||
				return ms;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GprsMs *GprsMsStorage::create_ms()
 | 
			
		||||
{
 | 
			
		||||
	GprsMs *ms;
 | 
			
		||||
 | 
			
		||||
	ms = ms_alloc(m_bts, GSM_RESERVED_TMSI);
 | 
			
		||||
 | 
			
		||||
	ms_set_callback(ms, &ms_storage_ms_cb);
 | 
			
		||||
	llist_add(&ms->list, &m_list);
 | 
			
		||||
	if (m_bts)
 | 
			
		||||
		bts_stat_item_add(m_bts, STAT_MS_PRESENT, 1);
 | 
			
		||||
 | 
			
		||||
	return ms;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								src/gprs_ms_storage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/gprs_ms_storage.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
/* gprs_ms_storage.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
 | 
			
		||||
 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "gprs_ms.h"
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
 | 
			
		||||
struct GprsMsStorage {
 | 
			
		||||
public:
 | 
			
		||||
	GprsMsStorage(struct gprs_rlcmac_bts *bts);
 | 
			
		||||
	~GprsMsStorage();
 | 
			
		||||
 | 
			
		||||
	void cleanup();
 | 
			
		||||
 | 
			
		||||
	GprsMs *get_ms(uint32_t tlli, uint32_t old_tlli = GSM_RESERVED_TMSI, const char *imsi = NULL) const;
 | 
			
		||||
	GprsMs *create_ms();
 | 
			
		||||
 | 
			
		||||
	const struct llist_head* ms_list() const {return &m_list;}
 | 
			
		||||
private:
 | 
			
		||||
	struct gprs_rlcmac_bts *m_bts;
 | 
			
		||||
	struct llist_head m_list; /* list of struct GprsMs */
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										196
									
								
								src/gprs_pcu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/gprs_pcu.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,196 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/ctrl/ports.h>
 | 
			
		||||
 | 
			
		||||
#include "gprs_pcu.h"
 | 
			
		||||
#include "bts.h"
 | 
			
		||||
 | 
			
		||||
struct gprs_pcu *the_pcu;
 | 
			
		||||
 | 
			
		||||
static struct osmo_tdef T_defs_pcu[] = {
 | 
			
		||||
	{ .T=3190,  .default_val=5,   .unit=OSMO_TDEF_S,  .desc="Return to packet idle mode after Packet DL Assignment on CCCH (s)", .val=0},
 | 
			
		||||
	{ .T=3141,  .default_val=10, .unit=OSMO_TDEF_S, .desc="Timeout for contention resolution procedure (s)", .val=0 },
 | 
			
		||||
	{ .T=PCU_TDEF_NEIGH_RESOLVE_TO,    .default_val=1000,  .unit=OSMO_TDEF_MS,   .desc="[ARFCN+BSIC]->[RAC+CI] resolution timeout (ms)", .val=0 },
 | 
			
		||||
	{ .T=PCU_TDEF_SI_RESOLVE_TO,	   .default_val=1000,  .unit=OSMO_TDEF_MS,   .desc="RIM RAN-INFO response timeout (ms)",	    .val=0 },
 | 
			
		||||
	{ .T=PCU_TDEF_NEIGH_CACHE_ALIVE,   .default_val=5,  .unit=OSMO_TDEF_S,   .desc="[ARFCN+BSIC]->[RAC+CI] resolution cache entry storage timeout (s)", .val=0 },
 | 
			
		||||
	{ .T=PCU_TDEF_SI_CACHE_ALIVE,      .default_val=5,  .unit=OSMO_TDEF_S,   .desc="[RAC+CI]->[SI] resolution cache entry storage timeout (s)", .val=0 },
 | 
			
		||||
	{ .T=PCU_TDEF_ANR_SCHED_TBF,      .default_val=10,  .unit=OSMO_TDEF_S,   .desc="[ANR] Frequency to schedule a new Measurement Report on any MS (s)", .val=0 },
 | 
			
		||||
	{ .T=PCU_TDEF_ANR_MS_TIMEOUT, .default_val=60, .unit=OSMO_TDEF_S, .desc="[ANR] Time to wait for Packet Measurement Report (s)",  .val=0 },
 | 
			
		||||
	{ .T=-101,  .default_val=30,  .unit=OSMO_TDEF_S,  .desc="BSSGP (un)blocking procedures timer (s)",  .val=0 },
 | 
			
		||||
	{ .T=-102,  .default_val=30,  .unit=OSMO_TDEF_S,  .desc="BSSGP reset procedure timer (s)",          .val=0 },
 | 
			
		||||
	{ .T=-2000, .default_val=2,   .unit=OSMO_TDEF_MS, .desc="Delay release of UL TBF after tx Packet Access Reject (PACCH) (ms)", .val=0 },
 | 
			
		||||
	{ .T=-2001, .default_val=2,   .unit=OSMO_TDEF_S,  .desc="PACCH assignment timeout (s)",               .val=0 },
 | 
			
		||||
	{ .T=-2002, .default_val=200, .unit=OSMO_TDEF_MS, .desc="Waiting after IMM.ASS confirm timer (ms)", .val=0 },
 | 
			
		||||
	{ .T=-2030, .default_val=60,  .unit=OSMO_TDEF_S,  .desc="Time to keep an idle MS object alive (s)", .val=0 }, /* slightly above T3314 (default 44s, 24.008, 11.2.2) */
 | 
			
		||||
	{ .T=-2031, .default_val=2000, .unit=OSMO_TDEF_MS, .desc="Time to keep an idle DL TBF alive (ms)",  .val=0 },
 | 
			
		||||
	{ .T=0, .default_val=0, .unit=OSMO_TDEF_S, .desc=NULL, .val=0 } /* empty item at the end */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int gprs_pcu_talloc_destructor(struct gprs_pcu *pcu)
 | 
			
		||||
{
 | 
			
		||||
	neigh_cache_free(pcu->neigh_cache);
 | 
			
		||||
	si_cache_free(pcu->si_cache);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gprs_pcu *gprs_pcu_alloc(void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_pcu *pcu;
 | 
			
		||||
 | 
			
		||||
	pcu = (struct gprs_pcu *)talloc_zero(ctx, struct gprs_pcu);
 | 
			
		||||
	OSMO_ASSERT(pcu);
 | 
			
		||||
	talloc_set_destructor(pcu, gprs_pcu_talloc_destructor);
 | 
			
		||||
 | 
			
		||||
	pcu->vty.fc_interval = 1;
 | 
			
		||||
	pcu->vty.max_cs_ul = MAX_GPRS_CS;
 | 
			
		||||
	pcu->vty.max_cs_dl = MAX_GPRS_CS;
 | 
			
		||||
	pcu->vty.max_mcs_ul = MAX_EDGE_MCS;
 | 
			
		||||
	pcu->vty.max_mcs_dl = MAX_EDGE_MCS;
 | 
			
		||||
	pcu->vty.force_alpha = (uint8_t)-1; /* don't force by default, use BTS SI13 provided value */
 | 
			
		||||
	pcu->vty.dl_tbf_preemptive_retransmission = true;
 | 
			
		||||
	/* By default resegmentation is supported in DL can also be configured
 | 
			
		||||
	 * through VTY */
 | 
			
		||||
	pcu->vty.dl_arq_type = EGPRS_ARQ1;
 | 
			
		||||
	pcu->vty.cs_adj_enabled = true;
 | 
			
		||||
	pcu->vty.cs_adj_upper_limit = 33; /* Decrease CS if the error rate is above */
 | 
			
		||||
	pcu->vty.cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
 | 
			
		||||
	pcu->vty.cs_downgrade_threshold = 200;
 | 
			
		||||
	/* CS-1 to CS-4 */
 | 
			
		||||
	pcu->vty.cs_lqual_ranges[0].low = -256;
 | 
			
		||||
	pcu->vty.cs_lqual_ranges[0].high = 6;
 | 
			
		||||
	pcu->vty.cs_lqual_ranges[1].low = 5;
 | 
			
		||||
	pcu->vty.cs_lqual_ranges[1].high = 8;
 | 
			
		||||
	pcu->vty.cs_lqual_ranges[2].low = 7;
 | 
			
		||||
	pcu->vty.cs_lqual_ranges[2].high = 13;
 | 
			
		||||
	pcu->vty.cs_lqual_ranges[3].low = 12;
 | 
			
		||||
	pcu->vty.cs_lqual_ranges[3].high = 256;
 | 
			
		||||
	/* MCS-1 to MCS-9 */
 | 
			
		||||
	/* Default thresholds are referenced from literature */
 | 
			
		||||
	/* Fig. 2.3, Chapter 2, Optimizing Wireless Communication Systems, Springer (2009) */
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[0].low = -256;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[0].high = 6;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[1].low = 5;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[1].high = 8;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[2].low = 7;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[2].high = 13;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[3].low = 12;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[3].high = 15;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[4].low = 14;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[4].high = 17;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[5].low = 16;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[5].high = 18;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[6].low = 17;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[6].high = 20;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[7].low = 19;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[7].high = 24;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[8].low = 23;
 | 
			
		||||
	pcu->vty.mcs_lqual_ranges[8].high = 256;
 | 
			
		||||
	pcu->vty.ns_dialect = GPRS_NS2_DIALECT_IPACCESS;
 | 
			
		||||
	pcu->vty.ns_ip_dscp = -1;
 | 
			
		||||
	pcu->vty.ns_priority = -1;
 | 
			
		||||
	/* TODO: increase them when CRBB decoding is implemented */
 | 
			
		||||
	pcu->vty.ws_base = 64;
 | 
			
		||||
	pcu->vty.ws_pdch = 0;
 | 
			
		||||
	pcu->vty.llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
 | 
			
		||||
	pcu->vty.llc_idle_ack_csec = 10;
 | 
			
		||||
	pcu->vty.neigh_ctrl_addr = talloc_strdup(pcu, "127.0.0.1");
 | 
			
		||||
	pcu->vty.neigh_ctrl_port = OSMO_CTRL_PORT_BSC_NEIGH;
 | 
			
		||||
 | 
			
		||||
	pcu->T_defs = T_defs_pcu;
 | 
			
		||||
	osmo_tdefs_reset(pcu->T_defs);
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&pcu->bts_list);
 | 
			
		||||
 | 
			
		||||
	pcu->neigh_cache = neigh_cache_alloc(pcu, osmo_tdef_get(pcu->T_defs, PCU_TDEF_NEIGH_CACHE_ALIVE, OSMO_TDEF_S, -1));
 | 
			
		||||
	pcu->si_cache = si_cache_alloc(pcu, osmo_tdef_get(pcu->T_defs, PCU_TDEF_SI_CACHE_ALIVE, OSMO_TDEF_S, -1));
 | 
			
		||||
 | 
			
		||||
	return pcu;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_nr(struct gprs_pcu *pcu, uint8_t bts_nr)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *pos;
 | 
			
		||||
	llist_for_each_entry(pos, &pcu->bts_list, list) {
 | 
			
		||||
		if (pos->nr == bts_nr)
 | 
			
		||||
			return pos;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_cgi_ps(struct gprs_pcu *pcu, struct osmo_cell_global_id_ps *cgi_ps)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *pos;
 | 
			
		||||
	llist_for_each_entry(pos, &pcu->bts_list, list) {
 | 
			
		||||
		if (osmo_cgi_ps_cmp(&pos->cgi_ps, cgi_ps) == 0)
 | 
			
		||||
			return pos;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_pcu_set_initial_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
 | 
			
		||||
	the_pcu->vty.initial_cs_dl = cs_dl;
 | 
			
		||||
	the_pcu->vty.initial_cs_ul = cs_ul;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(bts, &pcu->bts_list, list) {
 | 
			
		||||
		bts_recalc_initial_cs(bts);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void gprs_pcu_set_initial_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
 | 
			
		||||
	the_pcu->vty.initial_mcs_dl = mcs_dl;
 | 
			
		||||
	the_pcu->vty.initial_mcs_ul = mcs_ul;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(bts, &pcu->bts_list, list) {
 | 
			
		||||
		bts_recalc_initial_mcs(bts);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_pcu_set_max_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
 | 
			
		||||
	the_pcu->vty.max_cs_dl = cs_dl;
 | 
			
		||||
	the_pcu->vty.max_cs_ul = cs_ul;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(bts, &pcu->bts_list, list) {
 | 
			
		||||
		bts_recalc_max_cs(bts);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void gprs_pcu_set_max_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
 | 
			
		||||
	the_pcu->vty.max_mcs_dl = mcs_dl;
 | 
			
		||||
	the_pcu->vty.max_mcs_ul = mcs_ul;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(bts, &pcu->bts_list, list) {
 | 
			
		||||
		bts_recalc_max_mcs(bts);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										148
									
								
								src/gprs_pcu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/gprs_pcu.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,148 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/gsmtap_util.h>
 | 
			
		||||
 | 
			
		||||
#include "gprs_bssgp_pcu.h"
 | 
			
		||||
#include "coding_scheme.h"
 | 
			
		||||
 | 
			
		||||
#include "neigh_cache.h"
 | 
			
		||||
 | 
			
		||||
#define LLC_CODEL_DISABLE 0
 | 
			
		||||
#define LLC_CODEL_USE_DEFAULT (-1)
 | 
			
		||||
 | 
			
		||||
#define MAX_EDGE_MCS 9
 | 
			
		||||
#define MAX_GPRS_CS 4
 | 
			
		||||
 | 
			
		||||
#define PCU_TDEF_NEIGH_RESOLVE_TO (-1)
 | 
			
		||||
#define PCU_TDEF_SI_RESOLVE_TO (-2)
 | 
			
		||||
#define PCU_TDEF_NEIGH_CACHE_ALIVE (-10)
 | 
			
		||||
#define PCU_TDEF_SI_CACHE_ALIVE    (-11)
 | 
			
		||||
#define PCU_TDEF_ANR_SCHED_TBF	(-20)
 | 
			
		||||
#define PCU_TDEF_ANR_MS_TIMEOUT	(-21)
 | 
			
		||||
 | 
			
		||||
/* see bts->gsmtap_categ_mask */
 | 
			
		||||
enum pcu_gsmtap_category {
 | 
			
		||||
	PCU_GSMTAP_C_DL_UNKNOWN		= 0,	/* unknown or undecodable downlink blocks */
 | 
			
		||||
	PCU_GSMTAP_C_DL_DUMMY		= 1, 	/* downlink dummy blocks */
 | 
			
		||||
	PCU_GSMTAP_C_DL_CTRL		= 2,	/* downlink control blocks */
 | 
			
		||||
	PCU_GSMTAP_C_DL_DATA_GPRS	= 3,	/* downlink GPRS data blocks */
 | 
			
		||||
	PCU_GSMTAP_C_DL_DATA_EGPRS	= 4,	/* downlink EGPRS data blocks */
 | 
			
		||||
	PCU_GSMTAP_C_DL_PTCCH		= 5,	/* downlink PTCCH blocks */
 | 
			
		||||
	PCU_GSMTAP_C_DL_AGCH		= 6,	/* downlink AGCH blocks */
 | 
			
		||||
	PCU_GSMTAP_C_DL_PCH		= 7,	/* downlink PCH blocks */
 | 
			
		||||
 | 
			
		||||
	PCU_GSMTAP_C_UL_UNKNOWN		= 15,	/* unknown or undecodable uplink blocks */
 | 
			
		||||
	PCU_GSMTAP_C_UL_DUMMY		= 16,	/* uplink dummy blocks */
 | 
			
		||||
	PCU_GSMTAP_C_UL_CTRL		= 17,	/* uplink control blocks */
 | 
			
		||||
	PCU_GSMTAP_C_UL_DATA_GPRS	= 18,	/* uplink GPRS data blocks */
 | 
			
		||||
	PCU_GSMTAP_C_UL_DATA_EGPRS	= 19,	/* uplink EGPRS data blocks */
 | 
			
		||||
	PCU_GSMTAP_C_UL_RACH		= 20,	/* uplink RACH bursts */
 | 
			
		||||
	PCU_GSMTAP_C_UL_PTCCH		= 21,	/* uplink PTCCH bursts */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
struct GprsMs;
 | 
			
		||||
struct gprs_rlcmac_tbf;
 | 
			
		||||
 | 
			
		||||
typedef int (*alloc_algorithm_func_t)(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
				      struct gprs_rlcmac_tbf *tbf,
 | 
			
		||||
				      bool single, int8_t use_tbf);
 | 
			
		||||
 | 
			
		||||
struct gprs_pcu {
 | 
			
		||||
 | 
			
		||||
	/* Path to be used for the pcu-bts socket */
 | 
			
		||||
	char *pcu_sock_path;
 | 
			
		||||
 | 
			
		||||
	struct { /* Config Values set by VTY */
 | 
			
		||||
		uint8_t fc_interval;
 | 
			
		||||
		uint16_t fc_bucket_time;
 | 
			
		||||
		uint32_t fc_bvc_bucket_size;
 | 
			
		||||
		uint32_t fc_bvc_leak_rate;
 | 
			
		||||
		uint32_t fc_ms_bucket_size;
 | 
			
		||||
		uint32_t fc_ms_leak_rate;
 | 
			
		||||
		bool force_initial_cs;	/* false=use from BTS true=use from VTY */
 | 
			
		||||
		bool force_initial_mcs;	/* false=use from BTS true=use from VTY */
 | 
			
		||||
		uint8_t initial_cs_dl, initial_cs_ul;
 | 
			
		||||
		uint8_t initial_mcs_dl, initial_mcs_ul;
 | 
			
		||||
		uint8_t max_cs_dl, max_cs_ul;
 | 
			
		||||
		uint8_t max_mcs_dl, max_mcs_ul;
 | 
			
		||||
		uint8_t force_two_phase;
 | 
			
		||||
		uint8_t force_alpha, gamma;
 | 
			
		||||
		bool dl_tbf_preemptive_retransmission;
 | 
			
		||||
		enum egprs_arq_type dl_arq_type; /* EGPRS_ARQ1 to support resegmentation in DL, EGPRS_ARQ2 for no reseg */
 | 
			
		||||
		bool cs_adj_enabled; /* whether cs_adj_{upper,lower}_limit are used to adjust DL CS */
 | 
			
		||||
		uint8_t cs_adj_upper_limit; /* downgrade DL CS if error rate above its value */
 | 
			
		||||
		uint8_t cs_adj_lower_limit; /* upgrade DL CS if error rate below its value */
 | 
			
		||||
		/* downgrade DL CS when less than specified octets are left in tx queue. Optimization, see paper:
 | 
			
		||||
		  "Theoretical Analysis of GPRS Throughput and Delay" */
 | 
			
		||||
		uint16_t cs_downgrade_threshold;
 | 
			
		||||
		/* Link quality range for each UL (M)CS. Below or above, next/prev (M)CS is selected. */
 | 
			
		||||
		struct {int16_t low; int16_t high; } cs_lqual_ranges[MAX_GPRS_CS];
 | 
			
		||||
		struct {int16_t low; int16_t high; } mcs_lqual_ranges[MAX_EDGE_MCS];
 | 
			
		||||
		enum gprs_ns2_dialect ns_dialect; /* Are we talking Gb with IP-SNS (true) or classic Gb? */
 | 
			
		||||
		int ns_ip_dscp;
 | 
			
		||||
		int ns_priority;
 | 
			
		||||
		uint16_t ws_base;
 | 
			
		||||
		uint16_t ws_pdch; /* increase WS by this value per PDCH */
 | 
			
		||||
		uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
 | 
			
		||||
		uint32_t llc_discard_csec;
 | 
			
		||||
		uint32_t llc_idle_ack_csec;
 | 
			
		||||
		uint32_t llc_codel_interval_msec; /* 0=disabled, -1=use default interval */
 | 
			
		||||
		/* Remote BSS resolution sevice (CTRL iface) */
 | 
			
		||||
		char *neigh_ctrl_addr;
 | 
			
		||||
		uint16_t neigh_ctrl_port;
 | 
			
		||||
	} vty;
 | 
			
		||||
 | 
			
		||||
	struct gsmtap_inst *gsmtap;
 | 
			
		||||
	uint32_t gsmtap_categ_mask;
 | 
			
		||||
 | 
			
		||||
	struct llist_head bts_list; /* list of gprs_rlcmac_tbf */
 | 
			
		||||
 | 
			
		||||
	struct gprs_ns2_inst *nsi;
 | 
			
		||||
 | 
			
		||||
	alloc_algorithm_func_t alloc_algorithm;
 | 
			
		||||
 | 
			
		||||
	struct gprs_bssgp_pcu bssgp;
 | 
			
		||||
 | 
			
		||||
	struct osmo_tdef *T_defs; /* timers controlled by PCU */
 | 
			
		||||
 | 
			
		||||
	struct neigh_cache *neigh_cache; /* ARFC+BSIC -> CGI PS cache */
 | 
			
		||||
	struct si_cache *si_cache; /* ARFC+BSIC -> CGI PS cache */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
extern struct gprs_pcu *the_pcu;
 | 
			
		||||
 | 
			
		||||
struct gprs_pcu *gprs_pcu_alloc(void *ctx);
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_nr(struct gprs_pcu *pcu, uint8_t bts_nr);
 | 
			
		||||
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_cgi_ps(struct gprs_pcu *pcu, struct osmo_cell_global_id_ps *cgi_ps);
 | 
			
		||||
 | 
			
		||||
void gprs_pcu_set_initial_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul);
 | 
			
		||||
void gprs_pcu_set_initial_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul);
 | 
			
		||||
void gprs_pcu_set_max_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul);
 | 
			
		||||
void gprs_pcu_set_max_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul);
 | 
			
		||||
							
								
								
									
										1485
									
								
								src/gprs_rlcmac.cpp
									
									
									
									
									
								
							
							
						
						
									
										1485
									
								
								src/gprs_rlcmac.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,6 +1,7 @@
 | 
			
		||||
/* gprs_rlcmac.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
@@ -16,18 +17,25 @@
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
#ifndef GPRS_RLCMAC_H
 | 
			
		||||
#define GPRS_RLCMAC_H
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
#include <bitvector.h>
 | 
			
		||||
#include <gsm_rlcmac.h>
 | 
			
		||||
#include <gsm_timer.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/core/bitvec.h>
 | 
			
		||||
#include <osmocom/gsm/gsm48.h>
 | 
			
		||||
#include <osmocom/pcu/pcuif_proto.h>
 | 
			
		||||
#include "gsm_rlcmac.h"
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -37,177 +45,13 @@ extern "C" {
 | 
			
		||||
 */
 | 
			
		||||
//#define DEBUG_DL_ASS_IDLE
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * PDCH instanc
 | 
			
		||||
 */
 | 
			
		||||
#define DUMMY_VEC "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf;
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_pdch {
 | 
			
		||||
	uint8_t enable; /* TS is enabled */
 | 
			
		||||
	uint8_t tsc; /* TSC of this slot */
 | 
			
		||||
	uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
 | 
			
		||||
	uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
 | 
			
		||||
	struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
 | 
			
		||||
	struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
 | 
			
		||||
	struct llist_head paging_list; /* list of paging messages */
 | 
			
		||||
	uint32_t last_rts_fn; /* store last frame number of RTS */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_trx {
 | 
			
		||||
	uint16_t arfcn;
 | 
			
		||||
	struct gprs_rlcmac_pdch pdch[8];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts {
 | 
			
		||||
	uint8_t cs1;
 | 
			
		||||
	uint8_t cs2;
 | 
			
		||||
	uint8_t cs3;
 | 
			
		||||
	uint8_t cs4;
 | 
			
		||||
	uint8_t initial_cs;
 | 
			
		||||
	uint8_t force_cs;	/* 0=use from BTS 1=use from VTY */
 | 
			
		||||
	uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
 | 
			
		||||
	uint8_t t3142;
 | 
			
		||||
	uint8_t t3169;
 | 
			
		||||
	uint8_t t3191;
 | 
			
		||||
	uint16_t t3193_msec;
 | 
			
		||||
	uint8_t t3195;
 | 
			
		||||
	uint8_t n3101;
 | 
			
		||||
	uint8_t n3103;
 | 
			
		||||
	uint8_t n3105;
 | 
			
		||||
	struct gprs_rlcmac_trx trx[8];
 | 
			
		||||
	int (*alloc_algorithm)(struct gprs_rlcmac_tbf *old_tbf,
 | 
			
		||||
		struct gprs_rlcmac_tbf *tbf, uint32_t cust);
 | 
			
		||||
	uint32_t alloc_algorithm_curst; /* options to customize algorithm */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct gprs_rlcmac_bts *gprs_rlcmac_bts;
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
struct GprsMs;
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
/*
 | 
			
		||||
 * TBF instance
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define LLC_MAX_LEN 1543
 | 
			
		||||
#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */
 | 
			
		||||
#define RLC_MAX_WS  64 /* max window size */
 | 
			
		||||
#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
 | 
			
		||||
 | 
			
		||||
#define Tassign_agch 0,800000/* FIXME: we need a confirm from BTS */
 | 
			
		||||
 | 
			
		||||
enum gprs_rlcmac_tbf_state {
 | 
			
		||||
	GPRS_RLCMAC_NULL = 0,	/* new created TBF */
 | 
			
		||||
	GPRS_RLCMAC_ASSIGN,	/* wait for downlink assignment */
 | 
			
		||||
	GPRS_RLCMAC_FLOW,	/* RLC/MAC flow, ressource needed */
 | 
			
		||||
	GPRS_RLCMAC_FINISHED,	/* flow finished, wait for release */
 | 
			
		||||
	GPRS_RLCMAC_WAIT_RELEASE,/* wait for release or restart of DL TBF */
 | 
			
		||||
	GPRS_RLCMAC_RELEASING,	/* releasing, wait to free TBI/USF */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gprs_rlcmac_tbf_poll_state {
 | 
			
		||||
	GPRS_RLCMAC_POLL_NONE = 0,
 | 
			
		||||
	GPRS_RLCMAC_POLL_SCHED, /* a polling was scheduled */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gprs_rlcmac_tbf_dl_ass_state {
 | 
			
		||||
	GPRS_RLCMAC_DL_ASS_NONE = 0,
 | 
			
		||||
	GPRS_RLCMAC_DL_ASS_SEND_ASS, /* send downlink assignment on next RTS */
 | 
			
		||||
	GPRS_RLCMAC_DL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gprs_rlcmac_tbf_ul_ass_state {
 | 
			
		||||
	GPRS_RLCMAC_UL_ASS_NONE = 0,
 | 
			
		||||
	GPRS_RLCMAC_UL_ASS_SEND_ASS, /* send uplink assignment on next RTS */
 | 
			
		||||
	GPRS_RLCMAC_UL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gprs_rlcmac_tbf_ul_ack_state {
 | 
			
		||||
	GPRS_RLCMAC_UL_ACK_NONE = 0,
 | 
			
		||||
	GPRS_RLCMAC_UL_ACK_SEND_ACK, /* send acknowledge on next RTS */
 | 
			
		||||
	GPRS_RLCMAC_UL_ACK_WAIT_ACK, /* wait for PACKET CONTROL ACK */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gprs_rlcmac_tbf_direction {
 | 
			
		||||
	GPRS_RLCMAC_DL_TBF,
 | 
			
		||||
	GPRS_RLCMAC_UL_TBF
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf {
 | 
			
		||||
	struct llist_head list;
 | 
			
		||||
	enum gprs_rlcmac_tbf_state state;
 | 
			
		||||
	enum gprs_rlcmac_tbf_direction direction;
 | 
			
		||||
	uint8_t tfi;
 | 
			
		||||
	uint32_t tlli;
 | 
			
		||||
	uint8_t tlli_valid;
 | 
			
		||||
	uint8_t trx;
 | 
			
		||||
	uint16_t arfcn;
 | 
			
		||||
	uint8_t tsc;
 | 
			
		||||
	uint8_t first_ts; /* first TS used by TBF */
 | 
			
		||||
	uint8_t first_common_ts; /* first TS that the phone can send and
 | 
			
		||||
		reveive simultaniously */
 | 
			
		||||
	uint8_t control_ts; /* timeslot control messages and polling */
 | 
			
		||||
	uint8_t ms_class;
 | 
			
		||||
	struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
 | 
			
		||||
	uint16_t ta;
 | 
			
		||||
	uint8_t llc_frame[LLC_MAX_LEN]; /* current DL or UL frame */
 | 
			
		||||
	uint16_t llc_index; /* current write/read position of frame */
 | 
			
		||||
	uint16_t llc_length; /* len of current DL LLC_frame, 0 == no frame */
 | 
			
		||||
	struct llist_head llc_queue; /* queued LLC DL data */
 | 
			
		||||
 | 
			
		||||
	enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
 | 
			
		||||
	enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
 | 
			
		||||
	enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
 | 
			
		||||
 | 
			
		||||
	enum gprs_rlcmac_tbf_poll_state poll_state;
 | 
			
		||||
	uint32_t poll_fn; /* frame number to poll */
 | 
			
		||||
 | 
			
		||||
	uint16_t ws;	/* window size */
 | 
			
		||||
	uint16_t sns;	/* sequence number space */
 | 
			
		||||
 | 
			
		||||
	/* Please note that all variables here will be reset when changing
 | 
			
		||||
	 * from WAIT RELEASE back to FLOW state (re-use of TBF).
 | 
			
		||||
	 * All states that need reset must be in this struct, so this is why
 | 
			
		||||
	 * variables are in both (dl and ul) structs and not outside union.
 | 
			
		||||
	 */
 | 
			
		||||
	union {
 | 
			
		||||
		struct {
 | 
			
		||||
			uint16_t bsn;	/* block sequence number */
 | 
			
		||||
			uint16_t v_s;	/* send state */
 | 
			
		||||
			uint16_t v_a;	/* ack state */
 | 
			
		||||
			char v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
 | 
			
		||||
			int32_t tx_counter; /* count all transmitted blocks */
 | 
			
		||||
			uint8_t n3105;	/* N3105 counter */
 | 
			
		||||
		} dl;
 | 
			
		||||
		struct {
 | 
			
		||||
			uint16_t bsn;	/* block sequence number */
 | 
			
		||||
			uint16_t v_r;	/* receive state */
 | 
			
		||||
			uint16_t v_q;	/* receive window state */
 | 
			
		||||
			char v_n[RLC_MAX_SNS/2]; /* receive state array */
 | 
			
		||||
			int32_t rx_counter; /* count all received blocks */
 | 
			
		||||
			uint8_t n3103;	/* N3103 counter */
 | 
			
		||||
			uint8_t usf[8];	/* list USFs per PDCH (timeslot) */
 | 
			
		||||
			uint8_t contention_resolution_done; /* set after done */
 | 
			
		||||
			uint8_t final_ack_sent; /* set if we sent final ack */
 | 
			
		||||
		} ul;
 | 
			
		||||
	} dir;
 | 
			
		||||
	uint8_t rlc_block[RLC_MAX_SNS/2][RLC_MAX_LEN]; /* block history */
 | 
			
		||||
	uint8_t rlc_block_len[RLC_MAX_SNS/2]; /* block len  of history */
 | 
			
		||||
	
 | 
			
		||||
	struct osmo_timer_list	timer;
 | 
			
		||||
	unsigned int T; /* Txxxx number */
 | 
			
		||||
	unsigned int num_T_exp; /* number of consecutive T expirations */
 | 
			
		||||
	
 | 
			
		||||
	struct osmo_gsm_timer_list	gsm_timer;
 | 
			
		||||
	unsigned int fT; /* fTxxxx number */
 | 
			
		||||
	unsigned int num_fT_exp; /* number of consecutive fT expirations */
 | 
			
		||||
 | 
			
		||||
	struct timeval bw_tv; /* timestamp for bandwidth calculation */
 | 
			
		||||
	uint32_t bw_octets; /* number of octets transmitted since bw_tv */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct llist_head gprs_rlcmac_ul_tbfs; /* list of uplink TBFs */
 | 
			
		||||
extern struct llist_head gprs_rlcmac_dl_tbfs; /* list of downlink TBFs */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * paging entry
 | 
			
		||||
 */
 | 
			
		||||
@@ -217,120 +61,63 @@ struct gprs_rlcmac_paging {
 | 
			
		||||
	uint8_t identity_lv[9];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int tfi_alloc(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, uint8_t *_ts,
 | 
			
		||||
	uint8_t use_trx, uint8_t first_ts);
 | 
			
		||||
/*
 | 
			
		||||
 * coding scheme info
 | 
			
		||||
 */
 | 
			
		||||
struct gprs_rlcmac_cs {
 | 
			
		||||
	uint8_t	block_length;
 | 
			
		||||
	uint8_t block_data;
 | 
			
		||||
	uint8_t block_payload;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_tbf *old_tbf,
 | 
			
		||||
	enum gprs_rlcmac_tbf_direction dir, uint8_t tfi, uint8_t trx,
 | 
			
		||||
	uint8_t first_ts, uint8_t ms_class, uint8_t single_slot);
 | 
			
		||||
/* TS allocation internal functions */
 | 
			
		||||
int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots);
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts,
 | 
			
		||||
        enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
 | 
			
		||||
	uint16_t lost);
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
 | 
			
		||||
	enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
 | 
			
		||||
int gprs_rlcmac_meas_rep(GprsMs *ms, Packet_Measurement_Report_t *pmr);
 | 
			
		||||
 | 
			
		||||
void tbf_free(struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi);
 | 
			
		||||
 | 
			
		||||
int tbf_update(struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
 | 
			
		||||
        enum gprs_rlcmac_tbf_state state);
 | 
			
		||||
 | 
			
		||||
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
 | 
			
		||||
                        unsigned int seconds, unsigned int microseconds);
 | 
			
		||||
 | 
			
		||||
void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets);
 | 
			
		||||
 | 
			
		||||
/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
 | 
			
		||||
enum gprs_rlcmac_block_type {
 | 
			
		||||
	GPRS_RLCMAC_DATA_BLOCK = 0x0,
 | 
			
		||||
	GPRS_RLCMAC_CONTROL_BLOCK = 0x1, 
 | 
			
		||||
	GPRS_RLCMAC_CONTROL_BLOCK = 0x1,
 | 
			
		||||
	GPRS_RLCMAC_CONTROL_BLOCK_OPT = 0x2,
 | 
			
		||||
	GPRS_RLCMAC_RESERVED = 0x3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
 | 
			
		||||
	uint32_t fn);
 | 
			
		||||
 | 
			
		||||
int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra, 
 | 
			
		||||
        uint32_t fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc, 
 | 
			
		||||
        uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
 | 
			
		||||
	uint32_t poll_fn);
 | 
			
		||||
 | 
			
		||||
void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
 | 
			
		||||
	uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, 
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf, uint8_t poll);
 | 
			
		||||
 | 
			
		||||
void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
 | 
			
		||||
	uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
 | 
			
		||||
        uint8_t final);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
void tbf_timer_cb(void *_tbf);
 | 
			
		||||
struct msgb *gprs_rlcmac_app_info_msg(const struct gsm_pcu_if_app_info_req *req);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts,
 | 
			
		||||
	uint32_t fn);
 | 
			
		||||
 | 
			
		||||
struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
 | 
			
		||||
        struct gprs_rlcmac_tbf *tbf, uint32_t fn);
 | 
			
		||||
 | 
			
		||||
struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
 | 
			
		||||
        struct gprs_rlcmac_tbf *tbf, uint32_t fn);
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_trigger_downlink_assignment(struct gprs_rlcmac_tbf *tbf,
 | 
			
		||||
        struct gprs_rlcmac_tbf *old_tbf, char *imsi);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
 | 
			
		||||
        uint8_t ssn, uint8_t *rbb);
 | 
			
		||||
 | 
			
		||||
unsigned write_packet_paging_request(bitvec * dest);
 | 
			
		||||
 | 
			
		||||
unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
 | 
			
		||||
	uint8_t *identity, uint8_t chan_needed);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
 | 
			
		||||
	uint8_t *data, uint8_t len);
 | 
			
		||||
 | 
			
		||||
struct msgb *gprs_rlcmac_send_data_block_acknowledged(
 | 
			
		||||
        struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
 | 
			
		||||
 | 
			
		||||
struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
 | 
			
		||||
        uint32_t fn);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn, 
 | 
			
		||||
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	uint8_t trx, uint8_t ts,
 | 
			
		||||
        uint32_t fn, uint8_t block_nr);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv);
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging(
 | 
			
		||||
	struct gprs_rlcmac_pdch *pdch);
 | 
			
		||||
 | 
			
		||||
struct msgb *gprs_rlcmac_send_packet_paging_request(
 | 
			
		||||
	struct gprs_rlcmac_pdch *pdch);
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
int alloc_algorithm_a(struct gprs_rlcmac_tbf *old_tbf,
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf, uint32_t cust);
 | 
			
		||||
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
 | 
			
		||||
		      int8_t use_trx);
 | 
			
		||||
 | 
			
		||||
int alloc_algorithm_b(struct gprs_rlcmac_tbf *old_tbf,
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf, uint32_t cust);
 | 
			
		||||
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
 | 
			
		||||
		      int8_t use_trx);
 | 
			
		||||
 | 
			
		||||
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
 | 
			
		||||
			    int8_t use_trx);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_paging_request(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi, uint16_t pgroup);
 | 
			
		||||
int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class);
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // GPRS_RLCMAC_H
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										198
									
								
								src/gprs_rlcmac_meas.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								src/gprs_rlcmac_meas.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,198 @@
 | 
			
		||||
/* Measurements
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/timer_compat.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
#include <tbf_dl.h>
 | 
			
		||||
#include <gprs_ms.h>
 | 
			
		||||
#include <ms_anr_fsm.h>
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * downlink measurement
 | 
			
		||||
 */
 | 
			
		||||
/* TODO: trigger the measurement report from the pollcontroller and use it for flow control */
 | 
			
		||||
 | 
			
		||||
/* received Measurement Report */
 | 
			
		||||
int gprs_rlcmac_meas_rep(GprsMs *ms, Packet_Measurement_Report_t *pmr)
 | 
			
		||||
{
 | 
			
		||||
	NC_Measurement_Report_t *ncr;
 | 
			
		||||
	NC_Measurements_t *nc;
 | 
			
		||||
	int i;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	LOGPMS(ms, DRLCMACMEAS, LOGL_INFO, "Rx Measurement Report:");
 | 
			
		||||
 | 
			
		||||
	switch (pmr->UnionType) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		ncr = &pmr->u.NC_Measurement_Report;
 | 
			
		||||
		LOGPC(DRLCMACMEAS, LOGL_INFO, " NC%u Serv %d dbm",
 | 
			
		||||
			ncr->NC_MODE + 1,
 | 
			
		||||
			ncr->Serving_Cell_Data.RXLEV_SERVING_CELL - 110);
 | 
			
		||||
		for (i = 0; i < ncr->NUMBER_OF_NC_MEASUREMENTS; i++) {
 | 
			
		||||
			nc = &ncr->NC_Measurements[i];
 | 
			
		||||
			LOGPC(DRLCMACMEAS, LOGL_DEBUG, ", Neigh %u %d dbm",
 | 
			
		||||
				nc->FREQUENCY_N, nc->RXLEV_N - 110);
 | 
			
		||||
		}
 | 
			
		||||
		LOGPC(DRLCMACMEAS, LOGL_INFO, "\n");
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
	case 1:
 | 
			
		||||
		LOGPC(DRLCMACMEAS, LOGL_INFO,
 | 
			
		||||
			" <EXT Reporting not supported!>\n");
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ms->anr && ms->anr->fi->state == MS_ANR_ST_WAIT_PKT_MEAS_REPORT)
 | 
			
		||||
		rc = osmo_fsm_inst_dispatch(ms->anr->fi, MS_ANR_EV_RX_PKT_MEAS_REPORT, pmr);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * uplink measurement
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* RSSI values received from MS */
 | 
			
		||||
int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi)
 | 
			
		||||
{
 | 
			
		||||
	struct timespec now_tv, *rssi_tv = &tbf->meas.rssi_tv;
 | 
			
		||||
	struct timespec elapsed;
 | 
			
		||||
 | 
			
		||||
	tbf->meas.rssi_sum += rssi;
 | 
			
		||||
	tbf->meas.rssi_num++;
 | 
			
		||||
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &now_tv);
 | 
			
		||||
 | 
			
		||||
	timespecsub(&now_tv, rssi_tv, &elapsed);
 | 
			
		||||
	if (elapsed.tv_sec < 1)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	gprs_rlcmac_rssi_rep(tbf);
 | 
			
		||||
 | 
			
		||||
	/* reset rssi values and timestamp */
 | 
			
		||||
	memcpy(rssi_tv, &now_tv, sizeof(*rssi_tv));
 | 
			
		||||
	tbf->meas.rssi_sum = 0;
 | 
			
		||||
	tbf->meas.rssi_num = 0;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Give RSSI report */
 | 
			
		||||
int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf)
 | 
			
		||||
{
 | 
			
		||||
	/* No measurement values */
 | 
			
		||||
	if (!tbf->meas.rssi_num)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	LOGPMS(tbf->ms(), DRLCMACMEAS, LOGL_INFO, "UL RSSI: %d dBm\n",
 | 
			
		||||
	       tbf->meas.rssi_sum / tbf->meas.rssi_num);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * lost frames
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Lost frames reported from RLCMAC layer */
 | 
			
		||||
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
 | 
			
		||||
	uint16_t lost)
 | 
			
		||||
{
 | 
			
		||||
	struct timespec now_tv, *loss_tv = &tbf->m_bw.dl_loss_tv;
 | 
			
		||||
	struct timespec elapsed;
 | 
			
		||||
	uint16_t sum = received + lost;
 | 
			
		||||
 | 
			
		||||
	/* No measurement values */
 | 
			
		||||
	if (!sum)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d  "
 | 
			
		||||
		"Lost: %4d  Sum: %4d\n", tbf->tlli(), received, lost, sum);
 | 
			
		||||
 | 
			
		||||
	tbf->m_bw.dl_loss_received += received;
 | 
			
		||||
	tbf->m_bw.dl_loss_lost += lost;
 | 
			
		||||
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &now_tv);
 | 
			
		||||
	timespecsub(&now_tv, loss_tv, &elapsed);
 | 
			
		||||
	if (elapsed.tv_sec < 1)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	gprs_rlcmac_lost_rep(tbf);
 | 
			
		||||
 | 
			
		||||
	/* reset lost values and timestamp */
 | 
			
		||||
	memcpy(loss_tv, &now_tv, sizeof(*loss_tv));
 | 
			
		||||
	tbf->m_bw.dl_loss_received = 0;
 | 
			
		||||
	tbf->m_bw.dl_loss_lost = 0;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Give Lost report */
 | 
			
		||||
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t sum = tbf->m_bw.dl_loss_lost + tbf->m_bw.dl_loss_received;
 | 
			
		||||
 | 
			
		||||
	/* No measurement values */
 | 
			
		||||
	if (!sum)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL packet loss of IMSI=%s / TLLI=0x%08x: "
 | 
			
		||||
		"%d%%\n", tbf->imsi(), tbf->tlli(),
 | 
			
		||||
		tbf->m_bw.dl_loss_lost * 100 / sum);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * downlink bandwidth
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
 | 
			
		||||
{
 | 
			
		||||
	struct timespec now_tv, *bw_tv = &tbf->m_bw.dl_bw_tv;
 | 
			
		||||
	struct timespec elapsed;
 | 
			
		||||
 | 
			
		||||
	tbf->m_bw.dl_bw_octets += octets;
 | 
			
		||||
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &now_tv);
 | 
			
		||||
	timespecsub(&now_tv, bw_tv, &elapsed);
 | 
			
		||||
	if (elapsed.tv_sec < 1)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	tbf->m_bw.dl_throughput = (tbf->m_bw.dl_bw_octets << 10) / ((elapsed.tv_sec << 10) + (elapsed.tv_nsec >> 20));
 | 
			
		||||
	LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
 | 
			
		||||
		"%d KBits/s\n", tbf->imsi(), tbf->tlli(), tbf->m_bw.dl_throughput);
 | 
			
		||||
 | 
			
		||||
	/* reset bandwidth values timestamp */
 | 
			
		||||
	memcpy(bw_tv, &now_tv, sizeof(*bw_tv));
 | 
			
		||||
	tbf->m_bw.dl_bw_octets = 0;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -16,170 +16,354 @@
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
#include <gprs_bssgp_pcu.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
#include <tbf_ul.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <gprs_ms.h>
 | 
			
		||||
#include <rlc.h>
 | 
			
		||||
#include <sba.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
#include "pcu_utils.h"
 | 
			
		||||
 | 
			
		||||
uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
 | 
			
		||||
		    struct gprs_rlcmac_tbf **poll_tbf,
 | 
			
		||||
		    struct gprs_rlcmac_tbf **ul_ass_tbf,
 | 
			
		||||
		    struct gprs_rlcmac_tbf **dl_ass_tbf,
 | 
			
		||||
		    struct gprs_rlcmac_tbf **ul_ack_tbf)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf;
 | 
			
		||||
	uint32_t poll_fn;
 | 
			
		||||
 | 
			
		||||
	/* check special TBF for events */
 | 
			
		||||
	poll_fn = fn + 4;
 | 
			
		||||
	if ((block_nr % 3) == 2)
 | 
			
		||||
		poll_fn ++;
 | 
			
		||||
	poll_fn = poll_fn % 2715648;
 | 
			
		||||
	llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
 | 
			
		||||
		/* this trx, this ts */
 | 
			
		||||
		if (tbf->trx != trx || tbf->control_ts != ts)
 | 
			
		||||
			continue;
 | 
			
		||||
		/* polling for next uplink block */
 | 
			
		||||
		if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
 | 
			
		||||
		 && tbf->poll_fn == poll_fn)
 | 
			
		||||
			*poll_tbf = tbf;
 | 
			
		||||
		if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
 | 
			
		||||
			*ul_ack_tbf = tbf;
 | 
			
		||||
		if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
 | 
			
		||||
			*dl_ass_tbf = tbf;
 | 
			
		||||
		if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
 | 
			
		||||
			*ul_ass_tbf = tbf;
 | 
			
		||||
	}
 | 
			
		||||
	llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
 | 
			
		||||
		/* this trx, this ts */
 | 
			
		||||
		if (tbf->trx != trx || tbf->control_ts != ts)
 | 
			
		||||
			continue;
 | 
			
		||||
		/* polling for next uplink block */
 | 
			
		||||
		if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
 | 
			
		||||
		 && tbf->poll_fn == poll_fn)
 | 
			
		||||
			*poll_tbf = tbf;
 | 
			
		||||
		if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
 | 
			
		||||
			*dl_ass_tbf = tbf;
 | 
			
		||||
		if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
 | 
			
		||||
			*ul_ass_tbf = tbf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return poll_fn;
 | 
			
		||||
extern "C" {
 | 
			
		||||
	#include <osmocom/core/gsmtap.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
 | 
			
		||||
	uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
 | 
			
		||||
struct tbf_sched_candidates {
 | 
			
		||||
	struct gprs_rlcmac_tbf *ul_ass;
 | 
			
		||||
	struct gprs_rlcmac_tbf *dl_ass;
 | 
			
		||||
	struct gprs_rlcmac_tbf *nacc;
 | 
			
		||||
	struct gprs_rlcmac_tbf *anr;
 | 
			
		||||
	struct gprs_rlcmac_ul_tbf *ul_ack;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void get_ctrl_msg_tbf_candidates(const struct gprs_rlcmac_pdch *pdch,
 | 
			
		||||
					struct tbf_sched_candidates *tbf_cand)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf;
 | 
			
		||||
	uint8_t usf = 0x07;
 | 
			
		||||
	struct gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
	struct gprs_rlcmac_dl_tbf *dl_tbf;
 | 
			
		||||
	struct llist_item *pos;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry(pos, &pdch->trx->ul_tbfs, list) {
 | 
			
		||||
		ul_tbf = as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
 | 
			
		||||
		OSMO_ASSERT(ul_tbf);
 | 
			
		||||
		/* this trx, this ts */
 | 
			
		||||
		if (!ul_tbf->is_control_ts(pdch->ts_no))
 | 
			
		||||
			continue;
 | 
			
		||||
		if (ul_tbf->ul_ack_state_is(GPRS_RLCMAC_UL_ACK_SEND_ACK))
 | 
			
		||||
			tbf_cand->ul_ack = ul_tbf;
 | 
			
		||||
		if (ul_tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_SEND_ASS))
 | 
			
		||||
			tbf_cand->dl_ass = ul_tbf;
 | 
			
		||||
		if (ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
 | 
			
		||||
		    || ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
 | 
			
		||||
			tbf_cand->ul_ass = ul_tbf;
 | 
			
		||||
		/* NACC ready to send. TFI assigned is needed to send messages */
 | 
			
		||||
		if (ul_tbf->is_tfi_assigned() && ms_nacc_rts(ul_tbf->ms()))
 | 
			
		||||
			tbf_cand->nacc = ul_tbf;
 | 
			
		||||
		/* Don't pick ->anr here, only DL TBFs are useful */
 | 
			
		||||
/* FIXME: Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all
 | 
			
		||||
states? */
 | 
			
		||||
	}
 | 
			
		||||
	llist_for_each_entry(pos, &pdch->trx->dl_tbfs, list) {
 | 
			
		||||
		dl_tbf = as_dl_tbf((struct gprs_rlcmac_tbf *)pos->entry);
 | 
			
		||||
		OSMO_ASSERT(dl_tbf);
 | 
			
		||||
		/* this trx, this ts */
 | 
			
		||||
		if (!dl_tbf->is_control_ts(pdch->ts_no))
 | 
			
		||||
			continue;
 | 
			
		||||
		if (dl_tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_SEND_ASS))
 | 
			
		||||
			tbf_cand->dl_ass = dl_tbf;
 | 
			
		||||
		if (dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
 | 
			
		||||
		    || dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
 | 
			
		||||
			tbf_cand->ul_ass = dl_tbf;
 | 
			
		||||
		/* NACC ready to send. TFI assigned is needed to send messages */
 | 
			
		||||
		if (dl_tbf->is_tfi_assigned() && ms_nacc_rts(dl_tbf->ms()))
 | 
			
		||||
			tbf_cand->nacc = dl_tbf;
 | 
			
		||||
		if (ms_anr_rts(dl_tbf->ms(), dl_tbf))
 | 
			
		||||
			tbf_cand->anr = dl_tbf;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct gprs_rlcmac_ul_tbf *sched_select_uplink(struct gprs_rlcmac_pdch *pdch, bool require_gprs_only)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_ul_tbf *tbf;
 | 
			
		||||
	uint8_t i, tfi;
 | 
			
		||||
 | 
			
		||||
	/* select uplink ressource */
 | 
			
		||||
	/* select uplink resource */
 | 
			
		||||
	for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
 | 
			
		||||
	     i++, tfi = (tfi + 1) & 31) {
 | 
			
		||||
		tbf = pdch->ul_tbf[tfi];
 | 
			
		||||
		tbf = pdch->ul_tbf_by_tfi(tfi);
 | 
			
		||||
		/* no TBF for this tfi, go next */
 | 
			
		||||
		if (!tbf)
 | 
			
		||||
			continue;
 | 
			
		||||
		/* no UL ressources needed, go next */
 | 
			
		||||
		/* we don't need to give ressources in FINISHED state,
 | 
			
		||||
		/* no UL resources needed, go next */
 | 
			
		||||
		/* we don't need to give resources in FINISHED state,
 | 
			
		||||
		 * because we have received all blocks and only poll
 | 
			
		||||
		 * for packet control ack. */
 | 
			
		||||
		if (tbf->state != GPRS_RLCMAC_FLOW)
 | 
			
		||||
		if (tbf->state_is_not(TBF_ST_FLOW))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* use this USF */
 | 
			
		||||
		usf = tbf->dir.ul.usf[ts];
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
 | 
			
		||||
			"TS=%d FN=%d block_nr=%d scheduling USF=%d for "
 | 
			
		||||
			"required uplink ressource of UL TBF=%d\n", trx, ts, fn,
 | 
			
		||||
			block_nr, usf, tfi);
 | 
			
		||||
		/* next TBF to handle ressource is the next one */
 | 
			
		||||
		if (require_gprs_only && tbf->is_egprs_enabled())
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* use this USF (tbf) */
 | 
			
		||||
		/* next TBF to handle resource is the next one */
 | 
			
		||||
		pdch->next_ul_tfi = (tfi + 1) & 31;
 | 
			
		||||
		break;
 | 
			
		||||
		return tbf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return usf;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn,
 | 
			
		||||
		    uint8_t block_nr, struct gprs_rlcmac_pdch *pdch,
 | 
			
		||||
		    struct gprs_rlcmac_tbf *ul_ass_tbf,
 | 
			
		||||
		    struct gprs_rlcmac_tbf *dl_ass_tbf,
 | 
			
		||||
		    struct gprs_rlcmac_tbf *ul_ack_tbf)
 | 
			
		||||
{
 | 
			
		||||
struct msgb *sched_app_info(struct gprs_rlcmac_tbf *tbf) {
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
	struct msgb *msg = NULL;
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf = NULL;
 | 
			
		||||
 | 
			
		||||
	/* schedule PACKET UPLINK ASSIGNMENT (1st priority) */
 | 
			
		||||
	if (ul_ass_tbf) {
 | 
			
		||||
		tbf = ul_ass_tbf;
 | 
			
		||||
		msg = gprs_rlcmac_send_packet_uplink_assignment(tbf, fn);
 | 
			
		||||
	}
 | 
			
		||||
	/* schedule PACKET DOWNLINK ASSIGNMENT (2nd priotiry) */
 | 
			
		||||
	if (!msg && dl_ass_tbf) {
 | 
			
		||||
		tbf = dl_ass_tbf;
 | 
			
		||||
		msg = gprs_rlcmac_send_packet_downlink_assignment(tbf, fn);
 | 
			
		||||
	}
 | 
			
		||||
	/* schedule PACKET UPLINK ACK (3rd priority) */
 | 
			
		||||
	if (!msg && ul_ack_tbf) {
 | 
			
		||||
		tbf = ul_ack_tbf;
 | 
			
		||||
		msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
 | 
			
		||||
	}
 | 
			
		||||
	/* any message */
 | 
			
		||||
	if (msg) {
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
 | 
			
		||||
			"message at RTS for %s TBF=%d (TRX=%d, TS=%d)\n",
 | 
			
		||||
			(tbf->direction == GPRS_RLCMAC_UL_TBF)
 | 
			
		||||
					? "UL" : "DL", tbf->tfi, trx, ts);
 | 
			
		||||
		return msg;
 | 
			
		||||
	}
 | 
			
		||||
	/* schedule PACKET PAGING REQUEST */
 | 
			
		||||
	if (llist_empty(&pdch->paging_list))
 | 
			
		||||
	if (!tbf || !tbf->ms()->app_info_pending)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	msg = gprs_rlcmac_send_packet_paging_request(pdch);
 | 
			
		||||
	if (msg)
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request "
 | 
			
		||||
			"message at RTS for (TRX=%d, TS=%d)\n", trx, ts);
 | 
			
		||||
 | 
			
		||||
	bts = tbf->bts;
 | 
			
		||||
 | 
			
		||||
	if (bts->app_info) {
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Sending Packet Application Information message\n");
 | 
			
		||||
		msg = msgb_copy(bts->app_info, "app_info_msg_sched");
 | 
			
		||||
	} else
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_ERROR, "MS has app_info_pending flag set, but no Packet Application Information"
 | 
			
		||||
		     " message stored in BTS!\n");
 | 
			
		||||
 | 
			
		||||
	tbf->ms()->app_info_pending = false;
 | 
			
		||||
	bts->app_info_pending--;
 | 
			
		||||
 | 
			
		||||
	if (!bts->app_info_pending) {
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Packet Application Information successfully sent to all MS with active"
 | 
			
		||||
		     " TBF\n");
 | 
			
		||||
		msgb_free(bts->app_info);
 | 
			
		||||
		bts->app_info = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn,
 | 
			
		||||
		    uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
 | 
			
		||||
static struct msgb *sched_select_ctrl_msg(struct gprs_rlcmac_pdch *pdch, uint32_t fn,
 | 
			
		||||
					  uint8_t block_nr, struct tbf_sched_candidates *tbfs)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = NULL;
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf = NULL;
 | 
			
		||||
	uint8_t i, tfi;
 | 
			
		||||
	struct gprs_rlcmac_tbf *next_list[] = { tbfs->ul_ass,
 | 
			
		||||
						tbfs->dl_ass,
 | 
			
		||||
						tbfs->ul_ack,
 | 
			
		||||
						tbfs->nacc,
 | 
			
		||||
						tbfs->anr };
 | 
			
		||||
	uint8_t ts = pdch->ts_no;
 | 
			
		||||
 | 
			
		||||
	/* select downlink ressource */
 | 
			
		||||
	/* Send Packet Application Information first (ETWS primary notifications) */
 | 
			
		||||
	msg = sched_app_info(tbfs->dl_ass);
 | 
			
		||||
 | 
			
		||||
	if (!msg) {
 | 
			
		||||
		for (size_t i = 0; i < ARRAY_SIZE(next_list); ++i) {
 | 
			
		||||
			tbf = next_list[(pdch->next_ctrl_prio + i) % ARRAY_SIZE(next_list)];
 | 
			
		||||
			if (!tbf)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			/*
 | 
			
		||||
			 * Assignments for the same direction have lower precedence,
 | 
			
		||||
			 * because they may kill the TBF when the CONTROL ACK is
 | 
			
		||||
			 * received, thus preventing the others from being processed.
 | 
			
		||||
			 */
 | 
			
		||||
			if (tbf == tbfs->ul_ass && tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
 | 
			
		||||
				msg = tbfs->ul_ass->create_packet_access_reject();
 | 
			
		||||
			else if (tbf == tbfs->ul_ass && tbf->direction == GPRS_RLCMAC_DL_TBF)
 | 
			
		||||
				msg = tbfs->ul_ass->create_ul_ass(fn, ts);
 | 
			
		||||
			else if (tbf == tbfs->dl_ass && tbf->direction == GPRS_RLCMAC_UL_TBF)
 | 
			
		||||
				msg = tbfs->dl_ass->create_dl_ass(fn, ts);
 | 
			
		||||
			else if (tbf == tbfs->ul_ack)
 | 
			
		||||
				msg = tbfs->ul_ack->create_ul_ack(fn, ts);
 | 
			
		||||
			else if (tbf == tbfs->nacc)
 | 
			
		||||
				msg = ms_nacc_create_rlcmac_msg(tbf->ms(), tbf, fn, ts);
 | 
			
		||||
			else if (tbf == tbfs->anr)
 | 
			
		||||
				msg = ms_anr_create_rlcmac_msg(tbf->ms(), tbf, fn, ts);
 | 
			
		||||
 | 
			
		||||
			if (!msg) {
 | 
			
		||||
				tbf = NULL;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pdch->next_ctrl_prio = (pdch->next_ctrl_prio + 1) % ARRAY_SIZE(next_list);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!msg) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * If one of these is left, the response (CONTROL ACK) from the
 | 
			
		||||
		 * MS will kill the current TBF, only one of them can be
 | 
			
		||||
		 * non-NULL
 | 
			
		||||
		 */
 | 
			
		||||
		if (tbfs->dl_ass) {
 | 
			
		||||
			tbf = tbfs->dl_ass;
 | 
			
		||||
			msg = tbfs->dl_ass->create_dl_ass(fn, ts);
 | 
			
		||||
		} else if (tbfs->ul_ass) {
 | 
			
		||||
			tbf = tbfs->ul_ass;
 | 
			
		||||
			msg = tbfs->ul_ass->create_ul_ass(fn, ts);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* any message */
 | 
			
		||||
	if (msg) {
 | 
			
		||||
		if (!tbf) {
 | 
			
		||||
			LOGPDCH(pdch, DRLCMACSCHED, LOGL_ERROR, "FN=%" PRIu32
 | 
			
		||||
				" Control message to be scheduled, but no TBF\n", fn);
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
		tbf->rotate_in_list();
 | 
			
		||||
		LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
 | 
			
		||||
			" Scheduling control message at RTS for %s\n",
 | 
			
		||||
			fn, tbf_name(tbf));
 | 
			
		||||
		rate_ctr_inc(rate_ctr_group_get_ctr(tbf->ms()->ctrs, MS_CTR_DL_CTRL_MSG_SCHED));
 | 
			
		||||
		return msg;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* schedule PACKET PAGING REQUEST, if any are pending */
 | 
			
		||||
	msg = pdch->packet_paging_request();
 | 
			
		||||
	if (msg) {
 | 
			
		||||
		LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
 | 
			
		||||
			" Scheduling paging request message at RTS\n", fn);
 | 
			
		||||
		return msg;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline enum tbf_dl_prio tbf_compute_priority(const struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_dl_tbf *tbf,
 | 
			
		||||
						    uint8_t ts, uint32_t fn, int age)
 | 
			
		||||
{
 | 
			
		||||
	const gprs_rlc_dl_window *w = static_cast<gprs_rlc_dl_window *>(tbf->window());
 | 
			
		||||
	unsigned long msecs_t3190 = osmo_tdef_get(the_pcu->T_defs, 3190, OSMO_TDEF_MS, -1);
 | 
			
		||||
	unsigned long dl_tbf_idle_msec = osmo_tdef_get(the_pcu->T_defs, -2031, OSMO_TDEF_MS, -1);
 | 
			
		||||
	int age_thresh1 = msecs_to_frames(200);
 | 
			
		||||
	int age_thresh2 = msecs_to_frames(OSMO_MIN(msecs_t3190/2, dl_tbf_idle_msec));
 | 
			
		||||
 | 
			
		||||
	if (tbf->is_control_ts(ts) && tbf->need_control_ts())
 | 
			
		||||
		return DL_PRIO_CONTROL;
 | 
			
		||||
 | 
			
		||||
	if (tbf->is_control_ts(ts) && age > age_thresh2 && age_thresh2 > 0)
 | 
			
		||||
		return DL_PRIO_HIGH_AGE;
 | 
			
		||||
 | 
			
		||||
	if ((tbf->state_is(TBF_ST_FLOW) && tbf->have_data()) || w->resend_needed() >= 0)
 | 
			
		||||
		return DL_PRIO_NEW_DATA;
 | 
			
		||||
 | 
			
		||||
	if (tbf->is_control_ts(ts) && age > age_thresh1 && tbf->keep_open(fn))
 | 
			
		||||
		return DL_PRIO_LOW_AGE;
 | 
			
		||||
 | 
			
		||||
	if (!w->window_empty())
 | 
			
		||||
		return DL_PRIO_SENT_DATA;
 | 
			
		||||
 | 
			
		||||
	return DL_PRIO_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if next data block of a TBF can be encoded in GMSK [(M)CS1-4]. */
 | 
			
		||||
static bool can_produce_gmsk_data_block_next(struct gprs_rlcmac_dl_tbf *tbf, enum tbf_dl_prio prio)
 | 
			
		||||
{
 | 
			
		||||
	const gprs_rlc_dl_window *w;
 | 
			
		||||
 | 
			
		||||
	/* GPRS TBFs can always send GMSK */
 | 
			
		||||
	if (!tbf->is_egprs_enabled())
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	switch (prio) {
 | 
			
		||||
	case DL_PRIO_CONTROL:
 | 
			
		||||
		/* Control blocks are always CS-1 */
 | 
			
		||||
		return true;
 | 
			
		||||
	case DL_PRIO_NEW_DATA:
 | 
			
		||||
		/* We can send any new data (no block generated yet) using any
 | 
			
		||||
		 * MCS. However, we don't (yet) support resegmenting already
 | 
			
		||||
		 * sent blocks (NACKed blocks in this case) into lower MCS of
 | 
			
		||||
		 * the same family. See OS#4966 */
 | 
			
		||||
		w = static_cast<gprs_rlc_dl_window *>(tbf->window());
 | 
			
		||||
		return w->resend_needed() < 0;
 | 
			
		||||
	default:
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_pdch *pdch, uint32_t fn,
 | 
			
		||||
		    uint8_t block_nr, enum mcs_kind req_mcs_kind, bool *is_egprs)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = NULL;
 | 
			
		||||
	struct gprs_rlcmac_dl_tbf *tbf, *prio_tbf = NULL;
 | 
			
		||||
	enum tbf_dl_prio prio, max_prio = DL_PRIO_NONE;
 | 
			
		||||
	uint8_t ts = pdch->ts_no;
 | 
			
		||||
 | 
			
		||||
	uint8_t i, tfi, prio_tfi;
 | 
			
		||||
	int age;
 | 
			
		||||
 | 
			
		||||
	/* select downlink resource */
 | 
			
		||||
	for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
 | 
			
		||||
	     i++, tfi = (tfi + 1) & 31) {
 | 
			
		||||
		tbf = pdch->dl_tbf[tfi];
 | 
			
		||||
		tbf = pdch->dl_tbf_by_tfi(tfi);
 | 
			
		||||
		/* no TBF for this tfi, go next */
 | 
			
		||||
		if (!tbf)
 | 
			
		||||
			continue;
 | 
			
		||||
		/* no DL TBF, go next */
 | 
			
		||||
		if (tbf->direction != GPRS_RLCMAC_DL_TBF)
 | 
			
		||||
			continue;
 | 
			
		||||
		/* no DL ressources needed, go next */
 | 
			
		||||
		if (tbf->state != GPRS_RLCMAC_FLOW
 | 
			
		||||
		 && tbf->state != GPRS_RLCMAC_FINISHED)
 | 
			
		||||
		/* no DL resources needed, go next */
 | 
			
		||||
		if (tbf->state_is_not(TBF_ST_FLOW)
 | 
			
		||||
		 && tbf->state_is_not(TBF_ST_FINISHED))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
 | 
			
		||||
			"RTS for DL TBF=%d (TRX=%d, TS=%d)\n", tfi, trx, ts);
 | 
			
		||||
		/* next TBF to handle ressource is the next one */
 | 
			
		||||
		pdch->next_dl_tfi = (tfi + 1) & 31;
 | 
			
		||||
		/* waiting for CCCH IMM.ASS confirm */
 | 
			
		||||
		if (tbf->m_wait_confirm)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* If a GPRS (CS1-4) Dl block is required, skip EGPRS(_GSMK) tbfs: */
 | 
			
		||||
		if (req_mcs_kind == GPRS && tbf->is_egprs_enabled())
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		age = tbf->frames_since_last_poll(fn);
 | 
			
		||||
 | 
			
		||||
		/* compute priority */
 | 
			
		||||
		prio = tbf_compute_priority(bts, tbf, ts, fn, age);
 | 
			
		||||
		if (prio == DL_PRIO_NONE)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* If a GPRS (CS1-4/MCS1-4) Dl block is required, downgrade MCS
 | 
			
		||||
		 * below instead of skipping. However, downgrade can only be
 | 
			
		||||
		 * done on new data BSNs (not yet sent) and control blocks. */
 | 
			
		||||
		if (req_mcs_kind == EGPRS_GMSK && !can_produce_gmsk_data_block_next(tbf, prio)) {
 | 
			
		||||
			LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
 | 
			
		||||
				" Cannot downgrade EGPRS TBF with prio %d for %s\n",
 | 
			
		||||
				fn, prio, tbf_name(tbf));
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* get the TBF with the highest priority */
 | 
			
		||||
		if (prio > max_prio) {
 | 
			
		||||
			prio_tfi = tfi;
 | 
			
		||||
			prio_tbf = tbf;
 | 
			
		||||
			max_prio = prio;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (prio_tbf) {
 | 
			
		||||
		LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
 | 
			
		||||
			" Scheduling data message at RTS for DL TFI=%d prio=%d mcs_mode_restrict=%s\n",
 | 
			
		||||
			fn, prio_tfi, max_prio, mode_name(req_mcs_kind));
 | 
			
		||||
		/* next TBF to handle resource is the next one */
 | 
			
		||||
		pdch->next_dl_tfi = (prio_tfi + 1) & 31;
 | 
			
		||||
		/* generate DL data block */
 | 
			
		||||
		msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn,
 | 
			
		||||
			ts);
 | 
			
		||||
		break;
 | 
			
		||||
		msg = prio_tbf->create_dl_acked_block(fn, ts, req_mcs_kind);
 | 
			
		||||
		*is_egprs = prio_tbf->is_egprs_enabled();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
static uint8_t rlcmac_dl_idle[23] = {
 | 
			
		||||
 | 
			
		||||
static const uint8_t rlcmac_dl_idle[23] = {
 | 
			
		||||
	0x47, /* control without optional header octets, no polling, USF=111 */
 | 
			
		||||
	0x94, /* dummy downlink control message, paging mode 00 */
 | 
			
		||||
	0x2b, /* no persistance level, 7 bits spare pattern */
 | 
			
		||||
@@ -187,7 +371,7 @@ static uint8_t rlcmac_dl_idle[23] = {
 | 
			
		||||
	0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct msgb *sched_dummy(void)
 | 
			
		||||
static struct msgb *sched_dummy(void)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
 | 
			
		||||
@@ -199,22 +383,56 @@ struct msgb *sched_dummy(void)
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
 | 
			
		||||
static inline void tap_n_acc(const struct msgb *msg, struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
 | 
			
		||||
			     uint32_t fn, enum pcu_gsmtap_category cat)
 | 
			
		||||
{
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	switch(cat) {
 | 
			
		||||
	case PCU_GSMTAP_C_DL_CTRL:
 | 
			
		||||
		bts_do_rate_ctr_inc(bts, CTR_RLC_SENT_CONTROL);
 | 
			
		||||
		bts_send_gsmtap(bts, PCU_GSMTAP_C_DL_CTRL, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
 | 
			
		||||
				      msg->len);
 | 
			
		||||
		break;
 | 
			
		||||
	case PCU_GSMTAP_C_DL_DATA_GPRS:
 | 
			
		||||
	case PCU_GSMTAP_C_DL_DATA_EGPRS:
 | 
			
		||||
		bts_do_rate_ctr_inc(bts, CTR_RLC_SENT);
 | 
			
		||||
		bts_send_gsmtap(bts, cat, false, trx, ts, GSMTAP_CHANNEL_PDTCH, fn, msg->data,
 | 
			
		||||
				      msg->len);
 | 
			
		||||
		break;
 | 
			
		||||
	case PCU_GSMTAP_C_DL_DUMMY:
 | 
			
		||||
		bts_do_rate_ctr_inc(bts, CTR_RLC_SENT_DUMMY);
 | 
			
		||||
		bts_send_gsmtap(bts, PCU_GSMTAP_C_DL_DUMMY, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
 | 
			
		||||
				      msg->len);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	uint8_t trx, uint8_t ts,
 | 
			
		||||
        uint32_t fn, uint8_t block_nr)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 | 
			
		||||
	struct gprs_rlcmac_pdch *pdch;
 | 
			
		||||
	struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
 | 
			
		||||
		*ul_ass_tbf = NULL, *ul_ack_tbf = NULL;
 | 
			
		||||
	uint8_t usf = 0x7;
 | 
			
		||||
	struct tbf_sched_candidates tbf_cand = {0};
 | 
			
		||||
	struct gprs_rlcmac_tbf *poll_tbf;
 | 
			
		||||
	struct gprs_rlcmac_ul_tbf *usf_tbf = NULL;
 | 
			
		||||
	struct gprs_rlcmac_sba *sba;
 | 
			
		||||
	uint8_t usf;
 | 
			
		||||
	struct msgb *msg = NULL;
 | 
			
		||||
	uint32_t poll_fn;
 | 
			
		||||
	enum pcu_gsmtap_category gsmtap_cat;
 | 
			
		||||
	bool tx_is_egprs = false;
 | 
			
		||||
	bool require_gprs_only;
 | 
			
		||||
	enum mcs_kind req_mcs_kind; /* Restrict CS/MCS if DL Data block is to be sent */
 | 
			
		||||
 | 
			
		||||
	if (trx >= 8 || ts >= 8)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	pdch = &bts->trx[trx].pdch[ts];
 | 
			
		||||
 | 
			
		||||
	if (!pdch->enable) {
 | 
			
		||||
	if (!pdch->is_enabled()) {
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
 | 
			
		||||
			"TRX=%d TS=%d\n", trx, ts);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
@@ -223,42 +441,92 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
 | 
			
		||||
	/* store last frame number of RTS */
 | 
			
		||||
	pdch->last_rts_fn = fn;
 | 
			
		||||
 | 
			
		||||
	poll_fn = sched_poll(trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
 | 
			
		||||
		&dl_ass_tbf, &ul_ack_tbf);
 | 
			
		||||
	/* check uplink ressource for polling */
 | 
			
		||||
	if (poll_tbf)
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
 | 
			
		||||
			"TS=%d FN=%d block_nr=%d scheduling free USF for "
 | 
			
		||||
			"polling at FN=%d of %s TFI=%d\n", trx, ts, fn,
 | 
			
		||||
			block_nr, poll_fn,
 | 
			
		||||
			(poll_tbf->direction == GPRS_RLCMAC_UL_TBF)
 | 
			
		||||
				? "UL" : "DL", poll_tbf->tfi);
 | 
			
		||||
		/* use free USF */
 | 
			
		||||
	/* else, we search for uplink ressource */
 | 
			
		||||
	else
 | 
			
		||||
		usf = sched_select_uplink(trx, ts, fn, block_nr, pdch);
 | 
			
		||||
	/* require_gprs_only: Prioritize USF for GPRS-only MS here,
 | 
			
		||||
	 * since anyway we'll need to tx a Dl block with CS1-4 due to
 | 
			
		||||
	 * synchronization requirements. See 3GPP TS 03.64 version
 | 
			
		||||
	 * 8.12.0
 | 
			
		||||
	 */
 | 
			
		||||
	require_gprs_only = (pdch->fn_without_cs14 == MS_RESYNC_NUM_FRAMES - 1);
 | 
			
		||||
	if (require_gprs_only) {
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "TRX=%d TS=%d FN=%d "
 | 
			
		||||
		     "synchronization frame (every 18 frames), only CS1-4 allowed",
 | 
			
		||||
		     trx, ts, fn);
 | 
			
		||||
		req_mcs_kind = GPRS; /* only GPRS CS1-4 allowed, all MS need to be able to decode it */
 | 
			
		||||
	} else {
 | 
			
		||||
		req_mcs_kind = EGPRS; /* all kinds are fine */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* polling for next uplink block */
 | 
			
		||||
	poll_fn = rts_next_fn(fn, block_nr);
 | 
			
		||||
	/* check for sba */
 | 
			
		||||
	if ((sba = pdch_ulc_get_sba(pdch->ulc, poll_fn))) {
 | 
			
		||||
		LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: "
 | 
			
		||||
			"FN=%d block_nr=%d scheduling free USF for "
 | 
			
		||||
			"single block allocation at FN=%d\n", fn, block_nr, sba->fn);
 | 
			
		||||
	/* else, check uplink resource for polling */
 | 
			
		||||
	} else if ((poll_tbf = pdch_ulc_get_tbf_poll(pdch->ulc, poll_fn))) {
 | 
			
		||||
		LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: FN=%d "
 | 
			
		||||
			"block_nr=%d scheduling free USF for polling at FN=%d of %s\n",
 | 
			
		||||
			fn, block_nr, poll_fn, tbf_name(poll_tbf));
 | 
			
		||||
		/* If POLL TBF is UL and already has a USF assigned on this TS,
 | 
			
		||||
		 * let's set its USF in the DL msg. This is not really needed,
 | 
			
		||||
		 * but it helps understand better the flow when looking at
 | 
			
		||||
		 * pcaps. */
 | 
			
		||||
		if (poll_tbf->direction == GPRS_RLCMAC_UL_TBF && as_ul_tbf(poll_tbf)->m_usf[ts] != USF_INVALID)
 | 
			
		||||
			usf_tbf = as_ul_tbf(poll_tbf);
 | 
			
		||||
	/* else, search for uplink tbf */
 | 
			
		||||
	} else if ((usf_tbf = sched_select_uplink(pdch, require_gprs_only))) {
 | 
			
		||||
		LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: FN=%d "
 | 
			
		||||
			"block_nr=%d scheduling USF=%d for %s, expect answer on UL FN=%d\n",
 | 
			
		||||
			fn, block_nr, usf_tbf->m_usf[pdch->ts_no], tbf_name(usf_tbf), poll_fn);
 | 
			
		||||
		pdch_ulc_reserve_tbf_usf(pdch->ulc, poll_fn, usf_tbf);
 | 
			
		||||
	}
 | 
			
		||||
	/* If MS selected for USF is GPRS-only, then it will only be
 | 
			
		||||
	 * able to read USF if dl block uses GMSK * (CS1-4, MCS1-4) */
 | 
			
		||||
	if (usf_tbf && req_mcs_kind == EGPRS && ms_mode(usf_tbf->ms()) != EGPRS)
 | 
			
		||||
		req_mcs_kind = EGPRS_GMSK;
 | 
			
		||||
 | 
			
		||||
	get_ctrl_msg_tbf_candidates(pdch, &tbf_cand);
 | 
			
		||||
 | 
			
		||||
	/* Prio 1: select control message */
 | 
			
		||||
	msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
 | 
			
		||||
		dl_ass_tbf, ul_ack_tbf);
 | 
			
		||||
 | 
			
		||||
	if ((msg = sched_select_ctrl_msg(pdch, fn, block_nr, &tbf_cand))) {
 | 
			
		||||
			gsmtap_cat = PCU_GSMTAP_C_DL_CTRL;
 | 
			
		||||
	}
 | 
			
		||||
	/* Prio 2: select data message for downlink */
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		msg = sched_select_downlink(trx, ts, fn, block_nr, pdch);
 | 
			
		||||
 | 
			
		||||
	else if((msg = sched_select_downlink(bts, pdch, fn, block_nr, req_mcs_kind, &tx_is_egprs))) {
 | 
			
		||||
		gsmtap_cat = tx_is_egprs ? PCU_GSMTAP_C_DL_DATA_EGPRS :
 | 
			
		||||
					   PCU_GSMTAP_C_DL_DATA_GPRS;
 | 
			
		||||
	}
 | 
			
		||||
	/* Prio 3: send dummy contol message */
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		msg = sched_dummy();
 | 
			
		||||
 | 
			
		||||
	if (!msg)
 | 
			
		||||
	else if ((msg = sched_dummy())) {
 | 
			
		||||
		/* increase counter */
 | 
			
		||||
		gsmtap_cat = PCU_GSMTAP_C_DL_DUMMY;
 | 
			
		||||
	} else {
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (tx_is_egprs && pdch->has_gprs_only_tbf_attached()) {
 | 
			
		||||
		pdch->fn_without_cs14 += 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		pdch->fn_without_cs14 = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* msg is now available */
 | 
			
		||||
	bts_do_rate_ctr_add(bts, CTR_RLC_DL_BYTES, msgb_length(msg));
 | 
			
		||||
 | 
			
		||||
	/* set USF */
 | 
			
		||||
	OSMO_ASSERT(msgb_length(msg) > 0);
 | 
			
		||||
	usf = usf_tbf ? usf_tbf->m_usf[ts] : USF_UNUSED;
 | 
			
		||||
	msg->data[0] = (msg->data[0] & 0xf8) | usf;
 | 
			
		||||
 | 
			
		||||
	/* Used to measure the leak rate, count all blocks */
 | 
			
		||||
	gprs_bssgp_update_frames_sent();
 | 
			
		||||
 | 
			
		||||
	/* Send to GSMTAP */
 | 
			
		||||
	tap_n_acc(msg, bts, trx, ts, fn, gsmtap_cat);
 | 
			
		||||
 | 
			
		||||
	/* send PDTCH/PACCH to L1 */
 | 
			
		||||
	pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
 | 
			
		||||
	pcu_l1if_tx_pdtch(msg, bts, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1026
									
								
								src/gprs_rlcmac_ts_alloc.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1026
									
								
								src/gprs_rlcmac_ts_alloc.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										547
									
								
								src/gsm_rlcmac.h
									
									
									
									
									
								
							
							
						
						
									
										547
									
								
								src/gsm_rlcmac.h
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,226 +0,0 @@
 | 
			
		||||
/* gsm_timer.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
/* These store the amount of frame number that we wait until next timer expires. */
 | 
			
		||||
static int nearest;
 | 
			
		||||
static int *nearest_p;
 | 
			
		||||
 | 
			
		||||
/*! \addtogroup gsm_timer
 | 
			
		||||
 *  @{
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*! \file gsm_timer.cpp
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <gsm_timer.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
 | 
			
		||||
static struct rb_root timer_root = RB_ROOT;
 | 
			
		||||
 | 
			
		||||
static void __add_gsm_timer(struct osmo_gsm_timer_list *timer)
 | 
			
		||||
{
 | 
			
		||||
	struct rb_node **new_node = &(timer_root.rb_node);
 | 
			
		||||
	struct rb_node *parent = NULL;
 | 
			
		||||
 | 
			
		||||
	while (*new_node) {
 | 
			
		||||
		struct osmo_gsm_timer_list *this_timer;
 | 
			
		||||
 | 
			
		||||
		this_timer = container_of(*new_node, struct osmo_gsm_timer_list, node);
 | 
			
		||||
 | 
			
		||||
		parent = *new_node;
 | 
			
		||||
		if (timer->fn < this_timer->fn)
 | 
			
		||||
			new_node = &((*new_node)->rb_left);
 | 
			
		||||
		else
 | 
			
		||||
			new_node = &((*new_node)->rb_right);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rb_link_node(&timer->node, parent, new_node);
 | 
			
		||||
		rb_insert_color(&timer->node, &timer_root);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! \brief add a new timer to the timer management
 | 
			
		||||
 *  \param[in] timer the timer that should be added
 | 
			
		||||
 */
 | 
			
		||||
void osmo_gsm_timer_add(struct osmo_gsm_timer_list *timer)
 | 
			
		||||
{
 | 
			
		||||
	osmo_gsm_timer_del(timer);
 | 
			
		||||
	timer->active = 1;
 | 
			
		||||
	INIT_LLIST_HEAD(&timer->list);
 | 
			
		||||
	__add_gsm_timer(timer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! \brief schedule a gsm timer at a given future relative time
 | 
			
		||||
 *  \param[in] timer the to-be-added timer
 | 
			
		||||
 *  \param[in] number of frames from now
 | 
			
		||||
 *
 | 
			
		||||
 * This function can be used to (re-)schedule a given timer at a
 | 
			
		||||
 * specified number of frames in the future.  It will
 | 
			
		||||
 * internally add it to the timer management data structures, thus
 | 
			
		||||
 * osmo_timer_add() is automatically called.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
osmo_gsm_timer_schedule(struct osmo_gsm_timer_list *timer, int fn)
 | 
			
		||||
{
 | 
			
		||||
	int current_fn;
 | 
			
		||||
 | 
			
		||||
	current_fn = get_current_fn();
 | 
			
		||||
	timer->fn = current_fn + fn;
 | 
			
		||||
	osmo_gsm_timer_add(timer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! \brief delete a gsm timer from timer management
 | 
			
		||||
 *  \param[in] timer the to-be-deleted timer
 | 
			
		||||
 *
 | 
			
		||||
 * This function can be used to delete a previously added/scheduled
 | 
			
		||||
 * timer from the timer management code.
 | 
			
		||||
 */
 | 
			
		||||
void osmo_gsm_timer_del(struct osmo_gsm_timer_list *timer)
 | 
			
		||||
{
 | 
			
		||||
	if (timer->active) {
 | 
			
		||||
		timer->active = 0;
 | 
			
		||||
		rb_erase(&timer->node, &timer_root);
 | 
			
		||||
		/* make sure this is not already scheduled for removal. */
 | 
			
		||||
		if (!llist_empty(&timer->list))
 | 
			
		||||
			llist_del_init(&timer->list);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! \brief check if given timer is still pending
 | 
			
		||||
 *  \param[in] timer the to-be-checked timer
 | 
			
		||||
 *  \return 1 if pending, 0 otherwise
 | 
			
		||||
 *
 | 
			
		||||
 * This function can be used to determine whether a given timer
 | 
			
		||||
 * has alredy expired (returns 0) or is still pending (returns 1)
 | 
			
		||||
 */
 | 
			
		||||
int osmo_gsm_timer_pending(struct osmo_gsm_timer_list *timer)
 | 
			
		||||
{
 | 
			
		||||
	return timer->active;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * if we have a nearest frame number return the delta between the current
 | 
			
		||||
 * FN and the FN of the nearest timer.
 | 
			
		||||
 * If the nearest timer timed out return NULL and then we will
 | 
			
		||||
 * dispatch everything after the select
 | 
			
		||||
 */
 | 
			
		||||
int *osmo_gsm_timers_nearest(void)
 | 
			
		||||
{
 | 
			
		||||
	/* nearest_p is exactly what we need already: NULL if nothing is
 | 
			
		||||
	 * waiting, {0,0} if we must dispatch immediately, and the correct
 | 
			
		||||
	 * delay if we need to wait */
 | 
			
		||||
	return nearest_p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_nearest(int *cand, int *current)
 | 
			
		||||
{
 | 
			
		||||
	if (*cand != LONG_MAX) {
 | 
			
		||||
		if (*cand > *current)
 | 
			
		||||
			nearest = *cand - *current;
 | 
			
		||||
		else {
 | 
			
		||||
			/* loop again inmediately */
 | 
			
		||||
			nearest = 0;
 | 
			
		||||
		}
 | 
			
		||||
		nearest_p = &nearest;
 | 
			
		||||
	} else {
 | 
			
		||||
		nearest_p = NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Find the nearest FN and update s_nearest_time
 | 
			
		||||
 */
 | 
			
		||||
void osmo_gsm_timers_prepare(void)
 | 
			
		||||
{
 | 
			
		||||
	struct rb_node *node;
 | 
			
		||||
	int current_fn;
 | 
			
		||||
 | 
			
		||||
	current_fn = get_current_fn();
 | 
			
		||||
 | 
			
		||||
	node = rb_first(&timer_root);
 | 
			
		||||
	if (node) {
 | 
			
		||||
		struct osmo_gsm_timer_list *this_timer;
 | 
			
		||||
		this_timer = container_of(node, struct osmo_gsm_timer_list, node);
 | 
			
		||||
		update_nearest(&this_timer->fn, ¤t_fn);
 | 
			
		||||
	} else {
 | 
			
		||||
		nearest_p = NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * fire all timers... and remove them
 | 
			
		||||
 */
 | 
			
		||||
int osmo_gsm_timers_update(void)
 | 
			
		||||
{
 | 
			
		||||
	int current_fn;
 | 
			
		||||
	struct rb_node *node;
 | 
			
		||||
	struct llist_head timer_eviction_list;
 | 
			
		||||
	struct osmo_gsm_timer_list *this_timer;
 | 
			
		||||
	int work = 0;
 | 
			
		||||
 | 
			
		||||
	current_fn = get_current_fn();
 | 
			
		||||
 | 
			
		||||
	INIT_LLIST_HEAD(&timer_eviction_list);
 | 
			
		||||
	for (node = rb_first(&timer_root); node; node = rb_next(node)) {
 | 
			
		||||
		this_timer = container_of(node, struct osmo_gsm_timer_list, node);
 | 
			
		||||
 | 
			
		||||
		if (this_timer->fn > current_fn)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		llist_add(&this_timer->list, &timer_eviction_list);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The callbacks might mess with our list and in this case
 | 
			
		||||
	 * even llist_for_each_entry_safe is not safe to use. To allow
 | 
			
		||||
	 * osmo_gsm_timer_del to be called from within the callback we need
 | 
			
		||||
	 * to restart the iteration for each element scheduled for removal.
 | 
			
		||||
	 *
 | 
			
		||||
	 * The problematic scenario is the following: Given two timers A
 | 
			
		||||
	 * and B that have expired at the same time. Thus, they are both
 | 
			
		||||
	 * in the eviction list in this order: A, then B. If we remove
 | 
			
		||||
	 * timer B from the A's callback, we continue with B in the next
 | 
			
		||||
	 * iteration step, leading to an access-after-release.
 | 
			
		||||
	 */
 | 
			
		||||
restart:
 | 
			
		||||
	llist_for_each_entry(this_timer, &timer_eviction_list, list) {
 | 
			
		||||
		osmo_gsm_timer_del(this_timer);
 | 
			
		||||
		this_timer->cb(this_timer->data);
 | 
			
		||||
		work = 1;
 | 
			
		||||
		goto restart;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return work;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmo_gsm_timers_check(void)
 | 
			
		||||
{
 | 
			
		||||
	struct rb_node *node;
 | 
			
		||||
	int i = 0;
 | 
			
		||||
 | 
			
		||||
	for (node = rb_first(&timer_root); node; node = rb_next(node)) {
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! }@ */
 | 
			
		||||
 | 
			
		||||
@@ -1,84 +0,0 @@
 | 
			
		||||
/* gsm_timer.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*! \defgroup timer GSM timers
 | 
			
		||||
 *  @{
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*! \file gsm_timer.h
 | 
			
		||||
 *  \brief GSM timer handling routines
 | 
			
		||||
 */
 | 
			
		||||
#ifndef GSM_TIMER_H
 | 
			
		||||
#define GSM_TIMER_H
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/linuxrbtree.h>
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * Timer management:
 | 
			
		||||
 *      - Create a struct osmo_gsm_timer_list
 | 
			
		||||
 *      - Fill out timeout and use add_gsm_timer or
 | 
			
		||||
 *        use schedule_gsm_timer to schedule a timer in
 | 
			
		||||
 *        x frames from now...
 | 
			
		||||
 *      - Use del_gsm_timer to remove the timer
 | 
			
		||||
 *
 | 
			
		||||
 *  Internally:
 | 
			
		||||
 *      - We hook into select.c to give a frame number of the
 | 
			
		||||
 *        nearest timer. On already passed timers we give
 | 
			
		||||
 *        it a 0 to immediately fire after the select.
 | 
			
		||||
 *      - update_gsm_timers will call the callbacks and remove
 | 
			
		||||
 *        the timers.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
/*! \brief A structure representing a single instance of a gsm timer */
 | 
			
		||||
struct osmo_gsm_timer_list {
 | 
			
		||||
	struct rb_node node;	  /*!< \brief rb-tree node header */
 | 
			
		||||
	struct llist_head list;   /*!< \brief internal list header */
 | 
			
		||||
	int fn;                   /*!< \brief expiration frame number */
 | 
			
		||||
	unsigned int active  : 1; /*!< \brief is it active? */
 | 
			
		||||
 | 
			
		||||
	void (*cb)(void*);	  /*!< \brief call-back called at timeout */
 | 
			
		||||
	void *data;		  /*!< \brief user data for callback */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * timer management
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void osmo_gsm_timer_add(struct osmo_gsm_timer_list *timer);
 | 
			
		||||
 | 
			
		||||
void osmo_gsm_timer_schedule(struct osmo_gsm_timer_list *timer, int fn);
 | 
			
		||||
 | 
			
		||||
void osmo_gsm_timer_del(struct osmo_gsm_timer_list *timer);
 | 
			
		||||
 | 
			
		||||
int osmo_gsm_timer_pending(struct osmo_gsm_timer_list *timer);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * internal timer list management
 | 
			
		||||
 */
 | 
			
		||||
int *osmo_gsm_timers_nearest(void);
 | 
			
		||||
void osmo_gsm_timers_prepare(void);
 | 
			
		||||
int osmo_gsm_timers_update(void);
 | 
			
		||||
int osmo_gsm_timers_check(void);
 | 
			
		||||
 | 
			
		||||
/*! }@ */
 | 
			
		||||
 | 
			
		||||
#endif // GSM_TIMER_H
 | 
			
		||||
							
								
								
									
										256
									
								
								src/llc.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								src/llc.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,256 @@
 | 
			
		||||
/* Copied from tbf.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include "pcu_utils.h"
 | 
			
		||||
 | 
			
		||||
/* reset LLC frame */
 | 
			
		||||
void gprs_llc::reset()
 | 
			
		||||
{
 | 
			
		||||
	m_index = 0;
 | 
			
		||||
	m_length = 0;
 | 
			
		||||
 | 
			
		||||
	memset(frame, 0x42, sizeof(frame));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_llc::reset_frame_space()
 | 
			
		||||
{
 | 
			
		||||
	m_index = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Put an Unconfirmed Information (UI) Dummy command, see GSM 44.064, 6.4.2.2 */
 | 
			
		||||
void gprs_llc::put_dummy_frame(size_t req_len)
 | 
			
		||||
{
 | 
			
		||||
	/* The shortest dummy command (the spec requests at least 6 octets) */
 | 
			
		||||
	static const uint8_t llc_dummy_command[] = {
 | 
			
		||||
		0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
 | 
			
		||||
	};
 | 
			
		||||
	static const size_t max_dummy_command_len = 79;
 | 
			
		||||
 | 
			
		||||
	put_frame(llc_dummy_command, sizeof(llc_dummy_command));
 | 
			
		||||
 | 
			
		||||
	if (req_len > max_dummy_command_len)
 | 
			
		||||
		req_len = max_dummy_command_len;
 | 
			
		||||
 | 
			
		||||
	/* Add further stuffing, if the requested length exceeds the minimum
 | 
			
		||||
	 * dummy command length */
 | 
			
		||||
	if (m_length < req_len) {
 | 
			
		||||
		memset(&frame[m_length], 0x2b, req_len - m_length);
 | 
			
		||||
		m_length = req_len;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_llc::put_frame(const uint8_t *data, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	/* only put frames when we are empty */
 | 
			
		||||
	OSMO_ASSERT(m_index == 0 && m_length == 0);
 | 
			
		||||
	append_frame(data, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_llc::append_frame(const uint8_t *data, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	/* TODO: bounds check */
 | 
			
		||||
	memcpy(frame + m_length, data, len);
 | 
			
		||||
	m_length += len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_llc::init()
 | 
			
		||||
{
 | 
			
		||||
	reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool gprs_llc::is_user_data_frame(uint8_t *data, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	if (len < 2)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if ((data[0] & 0x0f) == 1 /* GPRS_SAPI_GMM */)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if ((data[0] & 0xe0) != 0xc0 /* LLC UI */)
 | 
			
		||||
		/* It is not an LLC UI frame, see TS 44.064, 6.3 */
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void llc_queue_init(struct gprs_llc_queue *q)
 | 
			
		||||
{
 | 
			
		||||
	INIT_LLIST_HEAD(&q->m_queue);
 | 
			
		||||
	q->m_queue_size = 0;
 | 
			
		||||
	q->m_queue_octets = 0;
 | 
			
		||||
	q->m_avg_queue_delay = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void gprs_llc_queue::enqueue(struct msgb *llc_msg, const struct timespec *expire_time)
 | 
			
		||||
{
 | 
			
		||||
	MetaInfo *meta_storage;
 | 
			
		||||
 | 
			
		||||
	osmo_static_assert(sizeof(*meta_storage) <= sizeof(llc_msg->cb), info_does_not_fit);
 | 
			
		||||
 | 
			
		||||
	m_queue_size += 1;
 | 
			
		||||
	m_queue_octets += msgb_length(llc_msg);
 | 
			
		||||
 | 
			
		||||
	meta_storage = (MetaInfo *)&llc_msg->cb[0];
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time);
 | 
			
		||||
	meta_storage->expire_time = *expire_time;
 | 
			
		||||
 | 
			
		||||
	msgb_enqueue(&m_queue, llc_msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
 | 
			
		||||
	while ((msg = msgb_dequeue(&q->m_queue))) {
 | 
			
		||||
		if (bts)
 | 
			
		||||
			bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	q->m_queue_size = 0;
 | 
			
		||||
	q->m_queue_octets = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
 | 
			
		||||
	struct llist_head new_queue;
 | 
			
		||||
	size_t queue_size = 0;
 | 
			
		||||
	size_t queue_octets = 0;
 | 
			
		||||
	INIT_LLIST_HEAD(&new_queue);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		if (msg1 == NULL)
 | 
			
		||||
			msg1 = msgb_dequeue(&q->m_queue);
 | 
			
		||||
 | 
			
		||||
		if (msg2 == NULL)
 | 
			
		||||
			msg2 = msgb_dequeue(&o->m_queue);
 | 
			
		||||
 | 
			
		||||
		if (msg1 == NULL && msg2 == NULL)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (msg1 == NULL) {
 | 
			
		||||
			msg = msg2;
 | 
			
		||||
			msg2 = NULL;
 | 
			
		||||
		} else if (msg2 == NULL) {
 | 
			
		||||
			msg = msg1;
 | 
			
		||||
			msg1 = NULL;
 | 
			
		||||
		} else {
 | 
			
		||||
			const MetaInfo *mi1 = (MetaInfo *)&msg1->cb[0];
 | 
			
		||||
			const MetaInfo *mi2 = (MetaInfo *)&msg2->cb[0];
 | 
			
		||||
 | 
			
		||||
			if (timespeccmp(&mi2->recv_time, &mi1->recv_time, >)) {
 | 
			
		||||
				msg = msg1;
 | 
			
		||||
				msg1 = NULL;
 | 
			
		||||
			} else {
 | 
			
		||||
				msg = msg2;
 | 
			
		||||
				msg2 = NULL;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msgb_enqueue(&new_queue, msg);
 | 
			
		||||
		queue_size += 1;
 | 
			
		||||
		queue_octets += msgb_length(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(llist_empty(&q->m_queue));
 | 
			
		||||
	OSMO_ASSERT(llist_empty(&o->m_queue));
 | 
			
		||||
 | 
			
		||||
	o->m_queue_size = 0;
 | 
			
		||||
	o->m_queue_octets = 0;
 | 
			
		||||
 | 
			
		||||
	llist_splice_init(&new_queue, &q->m_queue);
 | 
			
		||||
	q->m_queue_size = queue_size;
 | 
			
		||||
	q->m_queue_octets = queue_octets;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define ALPHA 0.5f
 | 
			
		||||
 | 
			
		||||
struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct timespec *tv, tv_now, tv_result;
 | 
			
		||||
	uint32_t lifetime;
 | 
			
		||||
	const MetaInfo *meta_storage;
 | 
			
		||||
 | 
			
		||||
	msg = msgb_dequeue(&m_queue);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	meta_storage = (MetaInfo *)&msg->cb[0];
 | 
			
		||||
 | 
			
		||||
	if (info)
 | 
			
		||||
		*info = meta_storage;
 | 
			
		||||
 | 
			
		||||
	m_queue_size -= 1;
 | 
			
		||||
	m_queue_octets -= msgb_length(msg);
 | 
			
		||||
 | 
			
		||||
	/* take the second time */
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
 | 
			
		||||
	tv = (struct timespec *)&msg->data[sizeof(*tv)];
 | 
			
		||||
	timespecsub(&tv_now, &meta_storage->recv_time, &tv_result);
 | 
			
		||||
 | 
			
		||||
	lifetime = tv_result.tv_sec*1000 + tv_result.tv_nsec/1000000;
 | 
			
		||||
	m_avg_queue_delay = m_avg_queue_delay * ALPHA + lifetime * (1-ALPHA);
 | 
			
		||||
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_llc_queue::calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec, struct timespec *tv)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t delay_csec;
 | 
			
		||||
	if (bts->pcu->vty.force_llc_lifetime)
 | 
			
		||||
		delay_csec = bts->pcu->vty.force_llc_lifetime;
 | 
			
		||||
	else
 | 
			
		||||
		delay_csec = pdu_delay_csec;
 | 
			
		||||
 | 
			
		||||
	/* keep timestamp at 0 for infinite delay */
 | 
			
		||||
	if (delay_csec == 0xffff) {
 | 
			
		||||
		memset(tv, 0, sizeof(*tv));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* calculate timestamp of timeout */
 | 
			
		||||
	struct timespec now, csec;
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &now);
 | 
			
		||||
	csecs_to_timespec(delay_csec, &csec);
 | 
			
		||||
 | 
			
		||||
	timespecadd(&now, &csec, tv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool gprs_llc_queue::is_frame_expired(const struct timespec *tv_now,
 | 
			
		||||
	const struct timespec *tv)
 | 
			
		||||
{
 | 
			
		||||
	/* Timeout is infinite */
 | 
			
		||||
	if (tv->tv_sec == 0 && tv->tv_nsec == 0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return timespeccmp(tv_now, tv, >);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										134
									
								
								src/llc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/llc.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
	#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#define LLC_MAX_LEN 1543
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * I represent the LLC data to a MS
 | 
			
		||||
 */
 | 
			
		||||
struct gprs_llc {
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
	static bool is_user_data_frame(uint8_t *data, size_t len);
 | 
			
		||||
 | 
			
		||||
	void init();
 | 
			
		||||
	void reset();
 | 
			
		||||
	void reset_frame_space();
 | 
			
		||||
 | 
			
		||||
	void put_frame(const uint8_t *data, size_t len);
 | 
			
		||||
	void put_dummy_frame(size_t req_len);
 | 
			
		||||
	void append_frame(const uint8_t *data, size_t len);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	uint8_t frame[LLC_MAX_LEN]; /* current DL or UL frame */
 | 
			
		||||
	uint16_t m_index; /* current write/read position of frame */
 | 
			
		||||
	uint16_t m_length; /* len of current DL LLC_frame, 0 == no frame */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct MetaInfo {
 | 
			
		||||
	struct timespec recv_time;
 | 
			
		||||
	struct timespec expire_time;
 | 
			
		||||
};
 | 
			
		||||
/**
 | 
			
		||||
 * I store the LLC frames that come from the SGSN.
 | 
			
		||||
 */
 | 
			
		||||
struct gprs_llc_queue {
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
	static void calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec,
 | 
			
		||||
		struct timespec *tv);
 | 
			
		||||
	static bool is_frame_expired(const struct timespec *now,
 | 
			
		||||
		const struct timespec *tv);
 | 
			
		||||
	static bool is_user_data_frame(uint8_t *data, size_t len);
 | 
			
		||||
 | 
			
		||||
	void enqueue(struct msgb *llc_msg, const struct timespec *expire_time);
 | 
			
		||||
	struct msgb *dequeue(const MetaInfo **info = 0);
 | 
			
		||||
#endif
 | 
			
		||||
	uint32_t m_avg_queue_delay; /* Average delay of data going through the queue */
 | 
			
		||||
	size_t m_queue_size;
 | 
			
		||||
	size_t m_queue_octets;
 | 
			
		||||
	struct llist_head m_queue; /* queued LLC DL data */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
void llc_queue_init(struct gprs_llc_queue *q);
 | 
			
		||||
void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts);
 | 
			
		||||
void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o);
 | 
			
		||||
 | 
			
		||||
static inline uint16_t llc_chunk_size(const struct gprs_llc *llc)
 | 
			
		||||
{
 | 
			
		||||
	return llc->m_length - llc->m_index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint16_t llc_remaining_space(const struct gprs_llc *llc)
 | 
			
		||||
{
 | 
			
		||||
	return LLC_MAX_LEN - llc->m_length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint16_t llc_frame_length(const struct gprs_llc *llc)
 | 
			
		||||
{
 | 
			
		||||
	return llc->m_length;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void llc_consume(struct gprs_llc *llc, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	llc->m_index += len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void llc_consume_data(struct gprs_llc *llc, uint8_t *data, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	/* copy and increment index */
 | 
			
		||||
	memcpy(data, llc->frame + llc->m_index, len);
 | 
			
		||||
	llc_consume(llc, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool llc_fits_in_current_frame(const struct gprs_llc *llc, uint8_t chunk_size)
 | 
			
		||||
{
 | 
			
		||||
	return llc->m_length + chunk_size <= LLC_MAX_LEN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline size_t llc_queue_size(const struct gprs_llc_queue *q)
 | 
			
		||||
{
 | 
			
		||||
	return q->m_queue_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline size_t llc_queue_octets(const struct gprs_llc_queue *q)
 | 
			
		||||
{
 | 
			
		||||
	return q->m_queue_octets;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										757
									
								
								src/ms_anr_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										757
									
								
								src/ms_anr_fsm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,757 @@
 | 
			
		||||
/* ms_anr_fsm.c
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <talloc.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/ctrl/control_cmd.h>
 | 
			
		||||
#include <osmocom/ctrl/control_if.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/gsm48.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp_rim.h>
 | 
			
		||||
 | 
			
		||||
#include <ms_anr_fsm.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <gprs_ms.h>
 | 
			
		||||
#include <encoding.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
 | 
			
		||||
#define X(s) (1 << (s))
 | 
			
		||||
 | 
			
		||||
/* We add safety timer to any FSM since ending up into some unexpected scenario
 | 
			
		||||
 * can keep the FSM alive and hence the TBF kept open forever */
 | 
			
		||||
static const struct osmo_tdef_state_timeout ms_anr_fsm_timeouts[32] = {
 | 
			
		||||
	[MS_ANR_ST_INITIAL] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
 | 
			
		||||
	[MS_ANR_ST_TX_PKT_MEAS_RESET1] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
 | 
			
		||||
	[MS_ANR_ST_WAIT_CTRL_ACK1] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
 | 
			
		||||
	[MS_ANR_ST_TX_PKT_MEAS_ORDER] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
 | 
			
		||||
	[MS_ANR_ST_WAIT_PKT_MEAS_REPORT] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
 | 
			
		||||
	[MS_ANR_ST_TX_PKT_MEAS_RESET2] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
 | 
			
		||||
	[MS_ANR_ST_WAIT_CTRL_ACK2] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
 | 
			
		||||
	[MS_ANR_ST_DONE] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
 | 
			
		||||
 * The actual timeout value is in turn obtained from conn->T_defs.
 | 
			
		||||
 * Assumes local variable fi exists. */
 | 
			
		||||
#define ms_anr_fsm_state_chg(fi, NEXT_STATE) \
 | 
			
		||||
	osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
 | 
			
		||||
				     ms_anr_fsm_timeouts, \
 | 
			
		||||
				     ((struct ms_anr_fsm_ctx*)(fi->priv))->ms->bts->pcu->T_defs, \
 | 
			
		||||
				     -1)
 | 
			
		||||
 | 
			
		||||
const struct value_string ms_anr_fsm_event_names[] = {
 | 
			
		||||
	{ MS_ANR_EV_START, "START" },
 | 
			
		||||
	{ MS_ANR_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" },
 | 
			
		||||
	{ MS_ANR_EV_RX_PKT_MEAS_REPORT, "RX_PKT_MEAS_REPORT" },
 | 
			
		||||
	{ MS_ANR_EV_RX_PKT_CTRL_ACK_MSG, "RX_PKT_CTRL_ACK_MSG" },
 | 
			
		||||
	{ MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT, "RX_PKT_CTRL_ACK_TIMEOUT" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* TS 44 060 11.2.9b Packet Measurement Order */
 | 
			
		||||
static struct msgb *create_packet_meas_order(struct ms_anr_fsm_ctx *ctx,
 | 
			
		||||
					     const struct gprs_rlcmac_tbf *tbf,
 | 
			
		||||
					     uint8_t nco, uint8_t pmo_idx, uint8_t pmo_count,
 | 
			
		||||
					     const NC_Frequency_list_t *freq_li)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	int rc;
 | 
			
		||||
	RlcMacDownlink_t *mac_control_block;
 | 
			
		||||
	struct GprsMs *ms = tbf_ms(tbf);
 | 
			
		||||
	bool tfi_asigned, tfi_is_dl;
 | 
			
		||||
	uint8_t tfi;
 | 
			
		||||
	bool exist_nc;
 | 
			
		||||
	uint8_t non_drx_period, nc_report_period_i, nc_report_period_t;
 | 
			
		||||
 | 
			
		||||
	if (tbf_is_tfi_assigned(tbf)) {
 | 
			
		||||
		tfi_asigned = true;
 | 
			
		||||
		tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
 | 
			
		||||
		tfi = tbf_tfi(tbf);
 | 
			
		||||
	} else {
 | 
			
		||||
		tfi_asigned = false;
 | 
			
		||||
		tfi_is_dl = false;
 | 
			
		||||
		tfi = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_meas_order");
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Initialize a bit vector that uses allocated msgb as the data buffer. */
 | 
			
		||||
	struct bitvec bv = {
 | 
			
		||||
		.data = msgb_put(msg, GSM_MACBLOCK_LEN),
 | 
			
		||||
		.data_len = GSM_MACBLOCK_LEN,
 | 
			
		||||
	};
 | 
			
		||||
	bitvec_unhex(&bv, DUMMY_VEC);
 | 
			
		||||
 | 
			
		||||
	mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);
 | 
			
		||||
 | 
			
		||||
	/* First message, set NC Meas Params. As per spec:
 | 
			
		||||
	 * "If parameters for the NC measurements are not included, a previous
 | 
			
		||||
	 * Packet Measurement Order message belonging to the same set of messages
 | 
			
		||||
	 * shall still be valid." */
 | 
			
		||||
	exist_nc = pmo_idx == 0;
 | 
			
		||||
	non_drx_period = 2; /* default value, still need to check */
 | 
			
		||||
	nc_report_period_i = 5;//0;
 | 
			
		||||
	nc_report_period_t = 5;//0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	write_packet_measurement_order(mac_control_block, 0, 0, tfi_asigned, tfi_is_dl,tfi, ms_tlli(ms),
 | 
			
		||||
				       pmo_idx, pmo_count, nco, exist_nc, non_drx_period,
 | 
			
		||||
				       nc_report_period_i, nc_report_period_t, freq_li);
 | 
			
		||||
	LOGP(DANR, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Measurement Order +++++++++++++++++++++++++\n");
 | 
			
		||||
	rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DANR, LOGL_ERROR, "Encoding of Packet Measurement Order Data failed (%d)\n", rc);
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DANR, LOGL_DEBUG, "------------------------- TX : Packet Measurement Order -------------------------\n");
 | 
			
		||||
	rate_ctr_inc(&bts_rate_counters(ms->bts)->ctr[CTR_PKT_MEAS_ORDER]);
 | 
			
		||||
	talloc_free(mac_control_block);
 | 
			
		||||
 | 
			
		||||
	return msg;
 | 
			
		||||
 | 
			
		||||
free_ret:
 | 
			
		||||
	talloc_free(mac_control_block);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MAX_REMOVE_FREQ_PER_MSG 16
 | 
			
		||||
#define MAX_ADD_FREQ_PER_MSG 5
 | 
			
		||||
static void build_nc_freq_list(struct ms_anr_fsm_ctx *ctx, NC_Frequency_list_t *freq_li,
 | 
			
		||||
			       const uint16_t *freq_to_remove, unsigned *freq_to_remove_idx, unsigned freq_to_remove_cnt,
 | 
			
		||||
			       const struct arfcn_bsic *freq_to_add, unsigned *freq_to_add_idx, unsigned freq_to_add_cnt)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	unsigned to_remove_this_message;
 | 
			
		||||
	LOGP(DANR, LOGL_DEBUG, "Build NC Frequency List:\n");
 | 
			
		||||
 | 
			
		||||
	/* First, remove all ARFCNs from BS(GPRS): */
 | 
			
		||||
	if (*freq_to_remove_idx < freq_to_remove_cnt) {
 | 
			
		||||
		to_remove_this_message = OSMO_MIN(freq_to_remove_cnt - *freq_to_remove_idx, MAX_REMOVE_FREQ_PER_MSG);
 | 
			
		||||
		freq_li->Exist_REMOVED_FREQ = 1;
 | 
			
		||||
		freq_li->NR_OF_REMOVED_FREQ = to_remove_this_message; /* offset of 1 applied already by CSN1 encoder */
 | 
			
		||||
		for (i = 0; i < to_remove_this_message; i++) {
 | 
			
		||||
			LOGP(DANR, LOGL_DEBUG, "Remove_Frequency[%d] INDEX=%u\n", i, freq_to_remove[*freq_to_remove_idx]);
 | 
			
		||||
			freq_li->Removed_Freq_Index[i].REMOVED_FREQ_INDEX = freq_to_remove[(*freq_to_remove_idx)++];
 | 
			
		||||
		}
 | 
			
		||||
		/* We want in general to first remove all frequencies, and only once we
 | 
			
		||||
		 * are done removing, starting adding new ones */
 | 
			
		||||
		if (*freq_to_remove_idx < freq_to_remove_cnt) {
 | 
			
		||||
			freq_li->Count_Add_Frequency = 0;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		to_remove_this_message = 0;
 | 
			
		||||
		freq_li->Exist_REMOVED_FREQ = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Then, add selected ones for ANR. ctx->cell_list has ARFCNs stored in ascending order */
 | 
			
		||||
	freq_li->Count_Add_Frequency = OSMO_MIN(freq_to_add_cnt - *freq_to_add_idx,
 | 
			
		||||
						MAX_ADD_FREQ_PER_MSG - to_remove_this_message/4);
 | 
			
		||||
	for (i = 0; i < freq_li->Count_Add_Frequency; i++) {
 | 
			
		||||
		freq_li->Add_Frequency[i].START_FREQUENCY = freq_to_add[*freq_to_add_idx].arfcn;
 | 
			
		||||
		freq_li->Add_Frequency[i].BSIC = freq_to_add[*freq_to_add_idx].bsic;
 | 
			
		||||
		freq_li->Add_Frequency[i].Exist_Cell_Selection = 0;
 | 
			
		||||
		freq_li->Add_Frequency[i].NR_OF_FREQUENCIES = 0; /* TODO: optimize here checking if we can fit more with DIFF */
 | 
			
		||||
		freq_li->Add_Frequency[i].FREQ_DIFF_LENGTH = 0;
 | 
			
		||||
		LOGP(DANR, LOGL_DEBUG, "Add_Frequency[%d] START_FREQ=%u BSIC=%u\n", i,
 | 
			
		||||
			freq_li->Add_Frequency[i].START_FREQUENCY,
 | 
			
		||||
			freq_li->Add_Frequency[i].BSIC);
 | 
			
		||||
		(*freq_to_add_idx)++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int build_multipart_packet_meas_order(struct ms_anr_fsm_ctx *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = ctx->ms->bts;
 | 
			
		||||
	unsigned int i, j;
 | 
			
		||||
	/* TODO: decide early whether to use si2 or si5, and pick is related BA-IND */
 | 
			
		||||
	struct gsm_sysinfo_freq *bcch_freq_list = bts->si2_bcch_cell_list;
 | 
			
		||||
	unsigned int bcch_freq_list_len = ARRAY_SIZE(bts->si2_bcch_cell_list);
 | 
			
		||||
	unsigned int bcch_freq_list_cnt = 0; // Number of freqs in Neigh List */
 | 
			
		||||
 | 
			
		||||
	unsigned int freq_to_remove_cnt = 0, freq_to_add_cnt = 0;
 | 
			
		||||
	uint16_t freq_to_remove[1024]; /* freq list index */
 | 
			
		||||
	struct arfcn_bsic freq_to_add[1024];
 | 
			
		||||
 | 
			
		||||
	/* First calculate amount of REMOVE and ADD freq entries, to calculate
 | 
			
		||||
	 * required number of bits and hence number of RLCMAC messages */
 | 
			
		||||
	ctx->nc_measurement_list_len = 0;
 | 
			
		||||
	for (i = 0; i < bcch_freq_list_len; i++) {
 | 
			
		||||
		bool bcch_freq_marked = !!bcch_freq_list[i].mask;
 | 
			
		||||
 | 
			
		||||
		if (bcch_freq_marked) {
 | 
			
		||||
			/* Freqs from BCCH list occupy one slot in the Neighbour
 | 
			
		||||
			 * List, even if removed later by NC_FreqList in Pkt
 | 
			
		||||
			 * Meas Order */
 | 
			
		||||
			if (ctx->nc_measurement_list_len < ARRAY_SIZE(ctx->nc_measurement_list)) {
 | 
			
		||||
				ctx->nc_measurement_list[ctx->nc_measurement_list_len] = i;
 | 
			
		||||
				ctx->nc_measurement_list_len++;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Check if the ARFCN is in our target ANR subset,
 | 
			
		||||
			 * otherwise mark it from removal using Pkt Meas Order */
 | 
			
		||||
			bcch_freq_list_cnt++;
 | 
			
		||||
			bool found = false;
 | 
			
		||||
			for (j = 0; j < ctx->num_cells; j++) {
 | 
			
		||||
				/* early termination, arfcns are in ascending order */
 | 
			
		||||
				if (ctx->cell_list[j].arfcn > i)
 | 
			
		||||
					break;
 | 
			
		||||
				if (ctx->cell_list[j].arfcn == i) {
 | 
			
		||||
					found = true;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (!found) {
 | 
			
		||||
				freq_to_remove[freq_to_remove_cnt] = bcch_freq_list_cnt - 1;
 | 
			
		||||
				freq_to_remove_cnt++;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			for (j = 0; j < ctx->num_cells; j++) {
 | 
			
		||||
				/* early termination, arfcns are in ascending order */
 | 
			
		||||
				if (ctx->cell_list[j].arfcn > i)
 | 
			
		||||
					break;
 | 
			
		||||
				if (ctx->cell_list[j].arfcn == i) {
 | 
			
		||||
					freq_to_add[freq_to_add_cnt] = ctx->cell_list[j];
 | 
			
		||||
					freq_to_add_cnt++;
 | 
			
		||||
					/* Don't break here, there may be several ARFCN=N with different BSIC */
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPFSML(ctx->fi, LOGL_DEBUG, "NC_freq_list to_remove=%u to_add=%u\n", freq_to_remove_cnt, freq_to_add_cnt);
 | 
			
		||||
 | 
			
		||||
	/* Added frequency through Pkt Meas Order NC Freq list are indexed after existing arfcns from BA(GPRS) */
 | 
			
		||||
	for (i = 0; i < freq_to_add_cnt; i++) {
 | 
			
		||||
		if (ctx->nc_measurement_list_len >= ARRAY_SIZE(ctx->nc_measurement_list))
 | 
			
		||||
			break;
 | 
			
		||||
		ctx->nc_measurement_list[ctx->nc_measurement_list_len] = freq_to_add[i].arfcn;
 | 
			
		||||
		ctx->nc_measurement_list_len++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uint8_t pmo_index;
 | 
			
		||||
	uint8_t pmo_count = 0;
 | 
			
		||||
	unsigned int freq_to_remove_idx = 0, freq_to_add_idx = 0;
 | 
			
		||||
	NC_Frequency_list_t freq_li[8];
 | 
			
		||||
	do {
 | 
			
		||||
		OSMO_ASSERT(pmo_count < ARRAY_SIZE(freq_li)); /* TODO: print something here*/
 | 
			
		||||
		build_nc_freq_list(ctx, &freq_li[pmo_count],
 | 
			
		||||
				   freq_to_remove, &freq_to_remove_idx, freq_to_remove_cnt,
 | 
			
		||||
				   freq_to_add, &freq_to_add_idx, freq_to_add_cnt);
 | 
			
		||||
		pmo_count++;
 | 
			
		||||
	} while (freq_to_remove_idx < freq_to_remove_cnt || freq_to_add_idx < freq_to_add_cnt);
 | 
			
		||||
 | 
			
		||||
	/* Now build messages */
 | 
			
		||||
	for (pmo_index = 0; pmo_index < pmo_count; pmo_index++) {
 | 
			
		||||
		struct msgb *msg = create_packet_meas_order(ctx, ctx->tbf, NC_1, pmo_index, pmo_count - 1, &freq_li[pmo_index]);
 | 
			
		||||
		llist_add_tail(&msg->list, &ctx->meas_order_queue);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TS 44 060 11.2.9b Packet Measurement Order */
 | 
			
		||||
static struct msgb *create_packet_meas_order_reset(struct ms_anr_fsm_ctx *ctx,
 | 
			
		||||
						   struct ms_anr_ev_create_rlcmac_msg_ctx *data,
 | 
			
		||||
						   uint32_t *new_poll_fn)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	int rc;
 | 
			
		||||
	RlcMacDownlink_t *mac_control_block;
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf = ctx->tbf;
 | 
			
		||||
	struct GprsMs *ms = tbf_ms(tbf);
 | 
			
		||||
	bool tfi_asigned, tfi_is_dl;
 | 
			
		||||
	uint8_t tfi;
 | 
			
		||||
	uint8_t pmo_idx = 0, pmo_count = 0;
 | 
			
		||||
	uint8_t nco = NC_RESET;
 | 
			
		||||
	unsigned int rrbp;
 | 
			
		||||
 | 
			
		||||
	if (tbf_is_tfi_assigned(tbf)) {
 | 
			
		||||
		tfi_asigned = true;
 | 
			
		||||
		tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
 | 
			
		||||
		tfi = tbf_tfi(tbf);
 | 
			
		||||
	} else {
 | 
			
		||||
		tfi_asigned = false;
 | 
			
		||||
		tfi_is_dl = false;
 | 
			
		||||
		tfi = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = tbf_check_polling(tbf, data->fn, data->ts, new_poll_fn, &rrbp);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DANR, LOGL_ERROR, "Failed registering poll for Packet Measurement Order (reset) (%d)\n", rc);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_meas_order");
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Initialize a bit vector that uses allocated msgb as the data buffer. */
 | 
			
		||||
	struct bitvec bv = {
 | 
			
		||||
		.data = msgb_put(msg, GSM_MACBLOCK_LEN),
 | 
			
		||||
		.data_len = GSM_MACBLOCK_LEN,
 | 
			
		||||
	};
 | 
			
		||||
	bitvec_unhex(&bv, DUMMY_VEC);
 | 
			
		||||
 | 
			
		||||
	mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);
 | 
			
		||||
 | 
			
		||||
	write_packet_measurement_order(mac_control_block, 1, rrbp, tfi_asigned, tfi_is_dl,tfi, ms_tlli(ms),
 | 
			
		||||
					    pmo_idx, pmo_count, nco, false, 0, 0, 0, NULL);
 | 
			
		||||
	LOGP(DANR, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Measurement Order (reset) FN=%" PRIu32 " +++++++++++++++++++++++++\n", data->fn);
 | 
			
		||||
	rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DANR, LOGL_ERROR, "Encoding of Packet Measurement Order Data failed (%d)\n", rc);
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DANR, LOGL_DEBUG, "------------------------- TX : Packet Measurement Order (reset) POLL_FN=%" PRIu32 "-------------------------\n", *new_poll_fn);
 | 
			
		||||
	rate_ctr_inc(&bts_rate_counters(ms->bts)->ctr[CTR_PKT_MEAS_ORDER]);
 | 
			
		||||
	talloc_free(mac_control_block);
 | 
			
		||||
 | 
			
		||||
	tbf_set_polling(tbf, *new_poll_fn, data->ts, PDCH_ULC_POLL_MEAS_ORDER);
 | 
			
		||||
	return msg;
 | 
			
		||||
 | 
			
		||||
free_ret:
 | 
			
		||||
	talloc_free(mac_control_block);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_meas_report(struct ms_anr_fsm_ctx *ctx, uint8_t *meas, const Packet_Measurement_Report_t *pmr)
 | 
			
		||||
{
 | 
			
		||||
	//TODO: transmit meas back to BSC
 | 
			
		||||
	const NC_Measurement_Report_t *ncr;
 | 
			
		||||
	int i, j;
 | 
			
		||||
	memset(meas, 0xff, ctx->num_cells);
 | 
			
		||||
 | 
			
		||||
	switch (pmr->UnionType) {
 | 
			
		||||
	case 0: break;
 | 
			
		||||
	case 1: /* EXT Reporting, should not happen */
 | 
			
		||||
	default:
 | 
			
		||||
		LOGPFSML(ctx->fi, LOGL_NOTICE, "EXT Reporting not supported!\n");
 | 
			
		||||
		osmo_fsm_inst_term(ctx->fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ncr = &pmr->u.NC_Measurement_Report;
 | 
			
		||||
	LOGPFSML(ctx->fi, LOGL_NOTICE, "Rx MEAS REPORT %u neighbours\n", ncr->NUMBER_OF_NC_MEASUREMENTS);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ncr->NUMBER_OF_NC_MEASUREMENTS; i++) {
 | 
			
		||||
		const NC_Measurements_t *nc = &ncr->NC_Measurements[i];
 | 
			
		||||
		/* infer ARFCN from FREQUENCY_N using previously calculated data: */
 | 
			
		||||
		OSMO_ASSERT(nc->FREQUENCY_N < ARRAY_SIZE(ctx->nc_measurement_list));
 | 
			
		||||
		uint16_t arfcn = ctx->nc_measurement_list[nc->FREQUENCY_N];
 | 
			
		||||
		LOGPFSML(ctx->fi, LOGL_DEBUG, "Neigh arfcn_index=%u arfcn=%u bsic=%d %d dBm\n",
 | 
			
		||||
			 nc->FREQUENCY_N, arfcn, nc->Exist_BSIC_N ? nc->BSIC_N : -1, nc->RXLEV_N - 110);
 | 
			
		||||
		if (!nc->Exist_BSIC_N)
 | 
			
		||||
			continue; /* Skip measurement without BSIC, since there could be several cells with same ARFCN */
 | 
			
		||||
		for (j = 0; j < ctx->num_cells; j++) {
 | 
			
		||||
			if (ctx->cell_list[j].arfcn != arfcn || ctx->cell_list[j].bsic != nc->BSIC_N)
 | 
			
		||||
				continue;
 | 
			
		||||
			meas[j] = nc->RXLEV_N;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		if (j == ctx->num_cells) {
 | 
			
		||||
			LOGPFSML(ctx->fi, LOGL_NOTICE,
 | 
			
		||||
				 "Neigh arfcn_index=%u arfcn=%u bsic=%u %d dBm not found in target cell list!\n",
 | 
			
		||||
				 nc->FREQUENCY_N, arfcn, nc->BSIC_N, nc->RXLEV_N - 110);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
////////////////
 | 
			
		||||
// FSM states //
 | 
			
		||||
////////////////
 | 
			
		||||
 | 
			
		||||
static void st_initial(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	//struct gprs_rlcmac_bts *bts = ctx->ms->bts;
 | 
			
		||||
	const struct ms_anr_ev_start *start_data;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case MS_ANR_EV_START:
 | 
			
		||||
		start_data = (const struct ms_anr_ev_start *)data;
 | 
			
		||||
		/* Copy over cell list on which to ask for measurements */
 | 
			
		||||
		OSMO_ASSERT(start_data->tbf);
 | 
			
		||||
		ctx->tbf = start_data->tbf;
 | 
			
		||||
		OSMO_ASSERT(start_data->num_cells <= ARRAY_SIZE(ctx->cell_list));
 | 
			
		||||
		ctx->num_cells = start_data->num_cells;
 | 
			
		||||
		if (start_data->num_cells)
 | 
			
		||||
			memcpy(ctx->cell_list, start_data->cell_list, start_data->num_cells * sizeof(start_data->cell_list[0]));
 | 
			
		||||
		ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_RESET1);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_tx_pkt_meas_reset1(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	struct ms_anr_ev_create_rlcmac_msg_ctx *data_ctx;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case MS_ANR_EV_CREATE_RLCMAC_MSG:
 | 
			
		||||
		/* Set NC to RESET and drop NC_Freq_list for MS to go back to
 | 
			
		||||
		   network defaults. */
 | 
			
		||||
		data_ctx = (struct ms_anr_ev_create_rlcmac_msg_ctx *)data;
 | 
			
		||||
		data_ctx->msg = create_packet_meas_order_reset(ctx, data_ctx, &ctx->poll_fn);
 | 
			
		||||
		if (!data_ctx->msg) {
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		ctx->poll_ts = data_ctx->ts;
 | 
			
		||||
		ms_anr_fsm_state_chg(fi, MS_ANR_ST_WAIT_CTRL_ACK1);
 | 
			
		||||
		break;
 | 
			
		||||
	case MS_ANR_EV_RX_PKT_MEAS_REPORT:
 | 
			
		||||
		/* Ignore Meas Report from previously (potentially unfinished) prcoedures */
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_wait_ctrl_ack1(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	//struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case MS_ANR_EV_RX_PKT_CTRL_ACK_MSG:
 | 
			
		||||
		ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_ORDER);
 | 
			
		||||
		break;
 | 
			
		||||
	case MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT:
 | 
			
		||||
		ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_RESET1);
 | 
			
		||||
		break;
 | 
			
		||||
	case MS_ANR_EV_RX_PKT_MEAS_REPORT:
 | 
			
		||||
		/* Ignore Meas Report from previously (potentially unfinished) prcoedures */
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_tx_pkt_meas_order_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	build_multipart_packet_meas_order(ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_tx_pkt_meas_order(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	struct ms_anr_ev_create_rlcmac_msg_ctx *data_ctx;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case MS_ANR_EV_CREATE_RLCMAC_MSG:
 | 
			
		||||
		data_ctx = (struct ms_anr_ev_create_rlcmac_msg_ctx *)data;
 | 
			
		||||
		/* Set NC to 1 to force MS to send us Meas Rep */
 | 
			
		||||
		data_ctx->msg = llist_first_entry(&ctx->meas_order_queue, struct msgb, list);
 | 
			
		||||
		if (!data_ctx->msg) {
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		llist_del(&data_ctx->msg->list);
 | 
			
		||||
		if (llist_empty(&ctx->meas_order_queue)) /* DONE */
 | 
			
		||||
			ms_anr_fsm_state_chg(fi, MS_ANR_ST_WAIT_PKT_MEAS_REPORT);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_wait_rx_pkt_meas_report_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	/* DO NOTHING */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void st_wait_rx_pkt_meas_report(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	uint8_t *meas = alloca(ctx->num_cells);
 | 
			
		||||
	struct ms_anr_ev_meas_compl ev_compl = {
 | 
			
		||||
		.cell_list = ctx->cell_list,
 | 
			
		||||
		.meas_list = meas,
 | 
			
		||||
		.num_cells = ctx->num_cells,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case MS_ANR_EV_RX_PKT_MEAS_REPORT:
 | 
			
		||||
		handle_meas_report(ctx, meas, (const Packet_Measurement_Report_t *)data);
 | 
			
		||||
		osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_COMPL, &ev_compl);
 | 
			
		||||
		ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_RESET2);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_tx_pkt_meas_reset2(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	struct ms_anr_ev_create_rlcmac_msg_ctx *data_ctx;
 | 
			
		||||
	uint8_t *meas = alloca(ctx->num_cells);
 | 
			
		||||
	struct ms_anr_ev_meas_compl ev_compl = {
 | 
			
		||||
		.cell_list = ctx->cell_list,
 | 
			
		||||
		.meas_list = meas,
 | 
			
		||||
		.num_cells = ctx->num_cells,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case MS_ANR_EV_CREATE_RLCMAC_MSG:
 | 
			
		||||
		/* Set NC to RESET and drop NC_Freq_list for MS to go back to
 | 
			
		||||
		   network defaults. */
 | 
			
		||||
		data_ctx = (struct ms_anr_ev_create_rlcmac_msg_ctx *)data;
 | 
			
		||||
		data_ctx->msg = create_packet_meas_order_reset(ctx, data_ctx, &ctx->poll_fn);
 | 
			
		||||
		if (!data_ctx->msg) {
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		ctx->poll_ts = data_ctx->ts;
 | 
			
		||||
		ms_anr_fsm_state_chg(fi, MS_ANR_ST_WAIT_CTRL_ACK2);
 | 
			
		||||
		break;
 | 
			
		||||
	case MS_ANR_EV_RX_PKT_MEAS_REPORT:
 | 
			
		||||
		handle_meas_report(ctx, meas, (const Packet_Measurement_Report_t *)data);
 | 
			
		||||
		osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_COMPL, &ev_compl);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_wait_ctrl_ack2(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
	uint8_t *meas = alloca(ctx->num_cells);
 | 
			
		||||
	struct ms_anr_ev_meas_compl ev_compl = {
 | 
			
		||||
		.cell_list = ctx->cell_list,
 | 
			
		||||
		.meas_list = meas,
 | 
			
		||||
		.num_cells = ctx->num_cells,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case MS_ANR_EV_RX_PKT_CTRL_ACK_MSG:
 | 
			
		||||
		ms_anr_fsm_state_chg(fi, MS_ANR_ST_DONE);
 | 
			
		||||
		break;
 | 
			
		||||
	case MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT:
 | 
			
		||||
		ms_anr_fsm_state_chg(fi, fi->state == MS_ANR_ST_WAIT_CTRL_ACK1 ?
 | 
			
		||||
						MS_ANR_ST_TX_PKT_MEAS_RESET1 :
 | 
			
		||||
						MS_ANR_ST_TX_PKT_MEAS_RESET2);
 | 
			
		||||
		break;
 | 
			
		||||
	case MS_ANR_EV_RX_PKT_MEAS_REPORT:
 | 
			
		||||
		/* We may keep receiving meas report from MS while waiting to
 | 
			
		||||
		 * receive the CTRL ACK: */
 | 
			
		||||
		handle_meas_report(ctx, meas, (const Packet_Measurement_Report_t *)data);
 | 
			
		||||
		osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_COMPL, &ev_compl);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_done_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ms_anr_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
 | 
			
		||||
{
 | 
			
		||||
	struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
 | 
			
		||||
 | 
			
		||||
	/* after cleanup() finishes, FSM termination calls osmo_fsm_inst_free,
 | 
			
		||||
	   so we need to avoid double-freeing it during ctx talloc free
 | 
			
		||||
	   destructor */
 | 
			
		||||
	talloc_reparent(ctx, ctx->ms, ctx->fi);
 | 
			
		||||
	ctx->fi = NULL;
 | 
			
		||||
 | 
			
		||||
	/* remove references from owning MS and free entire ctx */
 | 
			
		||||
	ctx->ms->anr = NULL;
 | 
			
		||||
 | 
			
		||||
	if (cause != OSMO_FSM_TERM_REGULAR && cause != OSMO_FSM_TERM_REQUEST) {
 | 
			
		||||
		/* Signal to bts_anr_fsm that orchestrates us that we failed, so
 | 
			
		||||
		 * that it can schedule the procedure again */
 | 
			
		||||
		struct ms_anr_ev_abort ev_data = {
 | 
			
		||||
			.cell_list = &ctx->cell_list[0],
 | 
			
		||||
			.num_cells = ctx->num_cells,
 | 
			
		||||
		};
 | 
			
		||||
		osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_ABORTED, &ev_data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ms_anr_fsm_timer_cb(struct osmo_fsm_inst *fi)
 | 
			
		||||
{
 | 
			
		||||
	switch (fi->T) {
 | 
			
		||||
		case PCU_TDEF_ANR_MS_TIMEOUT:
 | 
			
		||||
			LOGPFSML(fi, LOGL_NOTICE, "ms_anr_fsm got stuck, freeing it. This probably indicates a bug somehwere (if not in state WAIT_PKT_MEAS_REPORT)\n");
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct osmo_fsm_state ms_anr_fsm_states[] = {
 | 
			
		||||
	[MS_ANR_ST_INITIAL] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(MS_ANR_EV_START),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(MS_ANR_ST_TX_PKT_MEAS_RESET1),
 | 
			
		||||
		.name = "INITIAL",
 | 
			
		||||
		.action = st_initial,
 | 
			
		||||
	},
 | 
			
		||||
	[MS_ANR_ST_TX_PKT_MEAS_RESET1] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(MS_ANR_EV_CREATE_RLCMAC_MSG) |
 | 
			
		||||
			X(MS_ANR_EV_RX_PKT_MEAS_REPORT),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(MS_ANR_ST_WAIT_CTRL_ACK1),
 | 
			
		||||
		.name = "TX_PKT_MEAS_RESET1",
 | 
			
		||||
		.action = st_tx_pkt_meas_reset1,
 | 
			
		||||
	},
 | 
			
		||||
	[MS_ANR_ST_WAIT_CTRL_ACK1] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(MS_ANR_EV_RX_PKT_CTRL_ACK_MSG) |
 | 
			
		||||
			X(MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT) |
 | 
			
		||||
			X(MS_ANR_EV_RX_PKT_MEAS_REPORT),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(MS_ANR_ST_TX_PKT_MEAS_RESET1) |
 | 
			
		||||
			X(MS_ANR_ST_TX_PKT_MEAS_ORDER),
 | 
			
		||||
		.name = "WAIT_CTRL_ACK1",
 | 
			
		||||
		.action = st_wait_ctrl_ack1,
 | 
			
		||||
	},
 | 
			
		||||
	[MS_ANR_ST_TX_PKT_MEAS_ORDER] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(MS_ANR_EV_CREATE_RLCMAC_MSG),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(MS_ANR_ST_WAIT_PKT_MEAS_REPORT),
 | 
			
		||||
		.name = "TX_PKT_MEAS_ORDER",
 | 
			
		||||
		.onenter = st_tx_pkt_meas_order_on_enter,
 | 
			
		||||
		.action = st_tx_pkt_meas_order,
 | 
			
		||||
	},
 | 
			
		||||
	[MS_ANR_ST_WAIT_PKT_MEAS_REPORT] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(MS_ANR_EV_RX_PKT_MEAS_REPORT),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(MS_ANR_ST_TX_PKT_MEAS_RESET2),
 | 
			
		||||
		.name = "WAIT_PKT_MEAS_REPORT",
 | 
			
		||||
		.onenter = st_wait_rx_pkt_meas_report_on_enter,
 | 
			
		||||
		.action = st_wait_rx_pkt_meas_report,
 | 
			
		||||
	},
 | 
			
		||||
	[MS_ANR_ST_TX_PKT_MEAS_RESET2] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(MS_ANR_EV_CREATE_RLCMAC_MSG) |
 | 
			
		||||
			X(MS_ANR_EV_RX_PKT_MEAS_REPORT),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(MS_ANR_ST_WAIT_CTRL_ACK2),
 | 
			
		||||
		.name = "TX_PKT_MEAS_RESET2",
 | 
			
		||||
		.action = st_tx_pkt_meas_reset2,
 | 
			
		||||
	},
 | 
			
		||||
	[MS_ANR_ST_WAIT_CTRL_ACK2] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(MS_ANR_EV_RX_PKT_CTRL_ACK_MSG) |
 | 
			
		||||
			X(MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT) |
 | 
			
		||||
			X(MS_ANR_EV_RX_PKT_MEAS_REPORT),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(MS_ANR_ST_TX_PKT_MEAS_RESET2) |
 | 
			
		||||
			X(MS_ANR_ST_DONE),
 | 
			
		||||
		.name = "WAIT_CTRL_ACK2",
 | 
			
		||||
		.action = st_wait_ctrl_ack2,
 | 
			
		||||
	},
 | 
			
		||||
	[MS_ANR_ST_DONE] = {
 | 
			
		||||
		.in_event_mask = 0,
 | 
			
		||||
		.out_state_mask = 0,
 | 
			
		||||
		.name = "DONE",
 | 
			
		||||
		.onenter = st_done_on_enter,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct osmo_fsm ms_anr_fsm = {
 | 
			
		||||
	.name = "MS_ANR",
 | 
			
		||||
	.states = ms_anr_fsm_states,
 | 
			
		||||
	.num_states = ARRAY_SIZE(ms_anr_fsm_states),
 | 
			
		||||
	.timer_cb = ms_anr_fsm_timer_cb,
 | 
			
		||||
	.cleanup = ms_anr_fsm_cleanup,
 | 
			
		||||
	.log_subsys = DANR,
 | 
			
		||||
	.event_names = ms_anr_fsm_event_names,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static __attribute__((constructor)) void ms_anr_fsm_init(void)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(osmo_fsm_register(&ms_anr_fsm) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ms_anr_fsm_ctx_talloc_destructor(struct ms_anr_fsm_ctx *ctx)
 | 
			
		||||
{
 | 
			
		||||
	/* if ctx->fi != NULL it means we come directly from talloc_free(ctx)
 | 
			
		||||
	 * without having passed through ms_anr_fsm_cleanup() as part of
 | 
			
		||||
	 * osmo_fsm_inst_term(). In this case, clean up manually similarly to
 | 
			
		||||
	 * ms_anr_fsm_cleanup() and free the ctx->fi. */
 | 
			
		||||
	if (ctx->fi) {
 | 
			
		||||
		/* Signal to bts_anr_fsm that orchestrates us that we failed, so
 | 
			
		||||
		 * that it can schedule the procedure again */
 | 
			
		||||
		struct ms_anr_ev_abort ev_data = {
 | 
			
		||||
			.cell_list = &ctx->cell_list[0],
 | 
			
		||||
			.num_cells = ctx->num_cells,
 | 
			
		||||
		};
 | 
			
		||||
		osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_ABORTED, &ev_data);
 | 
			
		||||
		osmo_fsm_inst_free(ctx->fi);
 | 
			
		||||
		ctx->fi = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ms_anr_fsm_ctx *ms_anr_fsm_alloc(struct GprsMs* ms)
 | 
			
		||||
{
 | 
			
		||||
	struct ms_anr_fsm_ctx *ctx = talloc_zero(ms, struct ms_anr_fsm_ctx);
 | 
			
		||||
	char buf[64];
 | 
			
		||||
 | 
			
		||||
	talloc_set_destructor(ctx, ms_anr_fsm_ctx_talloc_destructor);
 | 
			
		||||
 | 
			
		||||
	ctx->ms = ms;
 | 
			
		||||
	INIT_LLIST_HEAD(&ctx->meas_order_queue);
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof(buf), "TLLI-0x%08x", ms_tlli(ms));
 | 
			
		||||
	ctx->fi = osmo_fsm_inst_alloc(&ms_anr_fsm, ctx, ctx, LOGL_INFO, buf);
 | 
			
		||||
	if (!ctx->fi)
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
 | 
			
		||||
	return ctx;
 | 
			
		||||
free_ret:
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Used by bts_anr_fsm to abort ongoing procedure without need of being informed
 | 
			
		||||
 * back by BTS_ANR_EV_MS_MEAS_ABORTED */
 | 
			
		||||
void ms_anr_fsm_abort(struct ms_anr_fsm_ctx *ctx)
 | 
			
		||||
{
 | 
			
		||||
	osmo_fsm_inst_term(ctx->fi, OSMO_FSM_TERM_REQUEST, NULL);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								src/ms_anr_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/ms_anr_fsm.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
/* ms_anr_fsm.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/fsm.h>
 | 
			
		||||
#include <osmocom/gsm/gsm23003.h>
 | 
			
		||||
 | 
			
		||||
#include "pcu_utils.h"
 | 
			
		||||
 | 
			
		||||
struct GprsMs;
 | 
			
		||||
struct gprs_rlcmac_tbf;
 | 
			
		||||
 | 
			
		||||
#define MAX_NEIGH_LIST_LEN 96
 | 
			
		||||
#define MAX_NEIGH_MEAS_LIST_LEN 32
 | 
			
		||||
 | 
			
		||||
enum ms_anr_fsm_event {
 | 
			
		||||
	MS_ANR_EV_START, /* data: struct ms_anr_ev_start */
 | 
			
		||||
	MS_ANR_EV_CREATE_RLCMAC_MSG, /* data: struct anr_ev_create_rlcmac_msg_ctx* */
 | 
			
		||||
	MS_ANR_EV_RX_PKT_MEAS_REPORT, /* data: Packet_Measurement_Report_t */
 | 
			
		||||
	MS_ANR_EV_RX_PKT_CTRL_ACK_MSG,
 | 
			
		||||
	MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ms_anr_fsm_states {
 | 
			
		||||
	MS_ANR_ST_INITIAL,
 | 
			
		||||
	MS_ANR_ST_TX_PKT_MEAS_RESET1,
 | 
			
		||||
	MS_ANR_ST_WAIT_CTRL_ACK1,
 | 
			
		||||
	MS_ANR_ST_TX_PKT_MEAS_ORDER,
 | 
			
		||||
	MS_ANR_ST_WAIT_PKT_MEAS_REPORT,
 | 
			
		||||
	MS_ANR_ST_TX_PKT_MEAS_RESET2,
 | 
			
		||||
	MS_ANR_ST_WAIT_CTRL_ACK2,
 | 
			
		||||
	MS_ANR_ST_DONE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ms_anr_fsm_ctx {
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct GprsMs* ms; /* back pointer */
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf; /* target tbf to create messages for, selected upon first MS_ANR_EV_CREATE_RLCMAC_MSG */
 | 
			
		||||
	struct arfcn_bsic cell_list[MAX_NEIGH_MEAS_LIST_LEN]; /* ordered by ascending ARFCN */
 | 
			
		||||
	unsigned int num_cells;
 | 
			
		||||
	struct llist_head meas_order_queue; /* list of msgb PMO_IDX=0..PMO_COUNT */
 | 
			
		||||
	uint16_t nc_measurement_list[MAX_NEIGH_LIST_LEN]; /* Used to infer ARFCN from Frequency index received at Measurement Report */
 | 
			
		||||
	unsigned int nc_measurement_list_len;
 | 
			
		||||
	uint32_t poll_fn; /* Scheduled poll FN to CTRL ACK the Pkt Meas Order (reset) */
 | 
			
		||||
	uint8_t poll_ts; /* Scheduled poll TS to CTRL ACK the Pkt Meas Order (reset */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* passed as data in MS_ANR_EV_START */
 | 
			
		||||
struct ms_anr_ev_start {
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf; /* target DL TBF to create messages for */
 | 
			
		||||
	const struct arfcn_bsic* cell_list;
 | 
			
		||||
	unsigned int num_cells;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* passed as data in MS_ANR_EV_CREATE_RLCMAC_MSG */
 | 
			
		||||
struct ms_anr_ev_create_rlcmac_msg_ctx {
 | 
			
		||||
	//struct gprs_rlcmac_tbf *tbf; /* target DL TBF to create messages for */
 | 
			
		||||
	uint32_t fn; /* FN where the created DL ctrl block is to be sent */
 | 
			
		||||
	uint8_t ts; /* TS where the created DL ctrl block is to be sent */
 | 
			
		||||
	struct msgb *msg; /* to be filled by FSM during event processing */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ms_anr_fsm_ctx *ms_anr_fsm_alloc(struct GprsMs* ms);
 | 
			
		||||
void ms_anr_fsm_abort(struct ms_anr_fsm_ctx *ctx);
 | 
			
		||||
 | 
			
		||||
/* 3GPP TS44.060 Table 11.2.4.2: PACKET CELL CHANGE ORDER
 | 
			
		||||
 * 3GPP TS 45.008 10.1.4 Network controlled Cell re-selection*/
 | 
			
		||||
enum network_control_order {
 | 
			
		||||
	NC_0 = 0x00,
 | 
			
		||||
	NC_1 = 0x01,
 | 
			
		||||
	NC_2 = 0x02,
 | 
			
		||||
	NC_RESET = 0x03
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										297
									
								
								src/mslot_class.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								src/mslot_class.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,297 @@
 | 
			
		||||
/* mslot_class.c
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 * Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <mslot_class.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/bits.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
/* 3GPP TS 45.002 Annex B Table B.1 */
 | 
			
		||||
 | 
			
		||||
struct gprs_ms_multislot_class {
 | 
			
		||||
	uint8_t rx, tx, sum;	/* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */
 | 
			
		||||
	uint8_t ta, tb, ra, rb;	/* Minimum Number of Slots */
 | 
			
		||||
	uint8_t type; /* Type of Mobile */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct gprs_ms_multislot_class gprs_ms_multislot_class[] = {
 | 
			
		||||
	/* M-S Class |  Max # of slots |       Min # of slots      | Type */
 | 
			
		||||
	/*           | Rx     Tx   Sum |  Tta    Ttb    Tra    Trb |      */
 | 
			
		||||
	/* N/A */ { MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA },
 | 
			
		||||
	/*  1 */  {   1,     1,     2,     3,     2,     4,     2,     1 },
 | 
			
		||||
	/*  2 */  {   2,     1,     3,     3,     2,     3,     1,     1 },
 | 
			
		||||
	/*  3 */  {   2,     2,     3,     3,     2,     3,     1,     1 },
 | 
			
		||||
	/*  4 */  {   3,     1,     4,     3,     1,     3,     1,     1 },
 | 
			
		||||
	/*  5 */  {   2,     2,     4,     3,     1,     3,     1,     1 },
 | 
			
		||||
	/*  6 */  {   3,     2,     4,     3,     1,     3,     1,     1 },
 | 
			
		||||
	/*  7 */  {   3,     3,     4,     3,     1,     3,     1,     1 },
 | 
			
		||||
	/*  8 */  {   4,     1,     5,     3,     1,     2,     1,     1 },
 | 
			
		||||
	/*  9 */  {   3,     2,     5,     3,     1,     2,     1,     1 },
 | 
			
		||||
	/* 10 */  {   4,     2,     5,     3,     1,     2,     1,     1 },
 | 
			
		||||
	/* 11 */  {   4,     3,     5,     3,     1,     2,     1,     1 },
 | 
			
		||||
	/* 12 */  {   4,     4,     5,     2,     1,     2,     1,     1 },
 | 
			
		||||
	/* 13 */  {   3,     3,   MS_NA, MS_NA, MS_A,    3,   MS_A,    2 },
 | 
			
		||||
	/* 14 */  {   4,     4,   MS_NA, MS_NA, MS_A,    3,   MS_A,    2 },
 | 
			
		||||
	/* 15 */  {   5,     5,   MS_NA, MS_NA, MS_A,    3,   MS_A,    2 },
 | 
			
		||||
	/* 16 */  {   6,     6,   MS_NA, MS_NA, MS_A,    2,   MS_A,    2 },
 | 
			
		||||
	/* 17 */  {   7,     7,   MS_NA, MS_NA, MS_A,    1,     0,     2 },
 | 
			
		||||
	/* 18 */  {   8,     8,   MS_NA, MS_NA,   0,     0,     0,     2 },
 | 
			
		||||
	/* 19 */  {   6,     2,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 20 */  {   6,     3,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 21 */  {   6,     4,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 22 */  {   6,     4,   MS_NA,   2,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 23 */  {   6,     6,   MS_NA,   2,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 24 */  {   8,     2,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 25 */  {   8,     3,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 26 */  {   8,     4,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 27 */  {   8,     4,   MS_NA,   2,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 28 */  {   8,     6,   MS_NA,   2,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 29 */  {   8,     8,   MS_NA,   2,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 30 */  {   5,     1,     6,     2,     1,     1,     1,     1 },
 | 
			
		||||
	/* 31 */  {   5,     2,     6,     2,     1,     1,     1,     1 },
 | 
			
		||||
	/* 32 */  {   5,     3,     6,     2,     1,     1,     1,     1 },
 | 
			
		||||
	/* 33 */  {   5,     4,     6,     2,     1,     1,     1,     1 },
 | 
			
		||||
	/* 34 */  {   5,     5,     6,     2,     1,     1,     1,     1 },
 | 
			
		||||
	/* 35 */  {   5,     1,     6,     2,     1,   MS_TO,   1,     1 },
 | 
			
		||||
	/* 36 */  {   5,     2,     6,     2,     1,   MS_TO,   1,     1 },
 | 
			
		||||
	/* 37 */  {   5,     3,     6,     2,     1,   MS_TO,   1,     1 },
 | 
			
		||||
	/* 38 */  {   5,     4,     6,     2,     1,   MS_TO,   1,     1 },
 | 
			
		||||
	/* 39 */  {   5,     5,     6,     2,     1,   MS_TO,   1,     1 },
 | 
			
		||||
	/* 40 */  {   6,     1,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
	/* 41 */  {   6,     2,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
	/* 42 */  {   6,     3,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
	/* 43 */  {   6,     4,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
	/* 44 */  {   6,     5,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
	/* 45 */  {   6,     6,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline const struct gprs_ms_multislot_class *get_mslot_table(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t index = ms_cl ? ms_cl : DEFAULT_MSLOT_CLASS;
 | 
			
		||||
 | 
			
		||||
	if (ms_cl >= ARRAY_SIZE(gprs_ms_multislot_class))
 | 
			
		||||
		index = 0;
 | 
			
		||||
 | 
			
		||||
	return &gprs_ms_multislot_class[index];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_max()
 | 
			
		||||
{
 | 
			
		||||
	return ARRAY_SIZE(gprs_ms_multislot_class);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_ta(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	return get_mslot_table(ms_cl)->ta;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO: Set it to 1 if FH is implemented and enabled
 | 
			
		||||
 * MS_A and MS_B are 0 iff FH is disabled and there is no Tx/Rx change.
 | 
			
		||||
 * This is never the case with the current implementation, so 1 will always be used. */
 | 
			
		||||
uint8_t mslot_class_get_tb(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
 | 
			
		||||
 | 
			
		||||
	switch (t->tb) {
 | 
			
		||||
	case MS_A:
 | 
			
		||||
		return 0;
 | 
			
		||||
	case MS_B:
 | 
			
		||||
		return 1;
 | 
			
		||||
	default:
 | 
			
		||||
		return t->tb;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_ra(uint8_t ms_cl, uint8_t ta)
 | 
			
		||||
{
 | 
			
		||||
	const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
 | 
			
		||||
 | 
			
		||||
	switch (t->ra) {
 | 
			
		||||
	case MS_TO:
 | 
			
		||||
		return ta + 1;
 | 
			
		||||
	default:
 | 
			
		||||
		return t->ra;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_rb(uint8_t ms_cl, uint8_t ta)
 | 
			
		||||
{
 | 
			
		||||
	const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
 | 
			
		||||
 | 
			
		||||
	switch (t->rb) {
 | 
			
		||||
	case MS_A:
 | 
			
		||||
		return 0;
 | 
			
		||||
	case MS_C:
 | 
			
		||||
		return 1;
 | 
			
		||||
	case MS_TO:
 | 
			
		||||
		return ta;
 | 
			
		||||
	default:
 | 
			
		||||
		return t->rb;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_tx(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	return get_mslot_table(ms_cl)->tx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_rx(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	return get_mslot_table(ms_cl)->rx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_sum(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	return get_mslot_table(ms_cl)->sum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_type(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	return get_mslot_table(ms_cl)->type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Fill in RX mask table for a given MS Class
 | 
			
		||||
 *
 | 
			
		||||
 *  \param[in] ms_cl MS Class pointer
 | 
			
		||||
 *  \param[in] num_tx Number of TX slots to consider
 | 
			
		||||
 *  \param[out] rx_mask RX mask table
 | 
			
		||||
 */
 | 
			
		||||
void mslot_fill_rx_mask(uint8_t mslot_class, uint8_t num_tx, uint8_t *rx_mask)
 | 
			
		||||
{
 | 
			
		||||
	static const char *digit[10] = { "0","1","2","3","4","5","6","7","8","9" };
 | 
			
		||||
	uint8_t Tx = mslot_class_get_tx(mslot_class),     /* Max number of Tx slots */
 | 
			
		||||
		Sum = mslot_class_get_sum(mslot_class),	  /* Max number of Tx + Rx slots */
 | 
			
		||||
		Type = mslot_class_get_type(mslot_class), /* Type of Mobile */
 | 
			
		||||
		Tta = mslot_class_get_ta(mslot_class),    /* Minimum number of slots */
 | 
			
		||||
		Ttb = mslot_class_get_tb(mslot_class),
 | 
			
		||||
		/* FIXME: use actual TA offset for computation - make sure to adjust "1 + MS_TO" accordingly
 | 
			
		||||
		   see also "Offset required" bit in 3GPP TS 24.008 §10.5.1.7 */
 | 
			
		||||
		Tra = mslot_class_get_ra(mslot_class, 0),
 | 
			
		||||
		Trb = mslot_class_get_rb(mslot_class, 0);
 | 
			
		||||
 | 
			
		||||
	if (num_tx == 1) /* it's enough to log this once per TX slot set iteration */
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_DEBUG,
 | 
			
		||||
		     "Rx=%d Tx=%d Sum Rx+Tx=%s, Tta=%s Ttb=%d, Tra=%d Trb=%d, Type=%d\n",
 | 
			
		||||
		     mslot_class_get_rx(mslot_class), Tx,
 | 
			
		||||
		     (Sum == MS_NA) ? "N/A" : digit[Sum],
 | 
			
		||||
		     (Tta == MS_NA) ? "N/A" : digit[Tta], Ttb, Tra, Trb, Type);
 | 
			
		||||
 | 
			
		||||
	if (Type == 1) {
 | 
			
		||||
		rx_mask[MASK_TT] = (0x100 >> OSMO_MAX(Ttb, Tta)) - 1;
 | 
			
		||||
		rx_mask[MASK_TT] &= ~((1 << (Trb + num_tx)) - 1);
 | 
			
		||||
		rx_mask[MASK_TR] = (0x100 >> Ttb) - 1;
 | 
			
		||||
		rx_mask[MASK_TR] &= ~((1 << (OSMO_MAX(Trb, Tra) + num_tx)) - 1);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Class type 2 MS have independant RX and TX */
 | 
			
		||||
		rx_mask[MASK_TT] = 0xff;
 | 
			
		||||
		rx_mask[MASK_TR] = 0xff;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rx_mask[MASK_TT] = (rx_mask[MASK_TT] << 3) | (rx_mask[MASK_TT] >> 5);
 | 
			
		||||
	rx_mask[MASK_TR] = (rx_mask[MASK_TR] << 3) | (rx_mask[MASK_TR] >> 5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* look for USF, don't use USF=7 */
 | 
			
		||||
int8_t find_free_usf(uint8_t usf_map)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t usf;
 | 
			
		||||
 | 
			
		||||
	if (usf_map == (1 << 7) - 1)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	for (usf = 0; usf < 7; usf++) {
 | 
			
		||||
		if (!(usf_map & (1 << usf)))
 | 
			
		||||
			return usf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* look for USF, don't use USF=7 */
 | 
			
		||||
int8_t find_free_tfi(uint32_t tfi_map)
 | 
			
		||||
{
 | 
			
		||||
	int8_t tfi;
 | 
			
		||||
 | 
			
		||||
	if (tfi_map == NO_FREE_TFI)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	for (tfi = 0; tfi < 32; tfi++) {
 | 
			
		||||
		if (!(tfi_map & (((uint32_t)1) << tfi)))
 | 
			
		||||
			return tfi;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void masked_override_with(char *buf, uint8_t mask, char set_char)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; mask; i++, mask >>= 1)
 | 
			
		||||
		if (mask & 1)
 | 
			
		||||
			buf[i] = set_char;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ts_format(char *buf, uint8_t dl_mask, uint8_t ul_mask)
 | 
			
		||||
{
 | 
			
		||||
	snprintf(buf, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(dl_mask, 'D'));
 | 
			
		||||
	masked_override_with(buf, ul_mask, 'U');
 | 
			
		||||
	masked_override_with(buf, ul_mask & dl_mask, 'C');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t mslot_wrap_window(uint16_t win)
 | 
			
		||||
{
 | 
			
		||||
	return (win | win >> 8) & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mslot_test_and_set_bit(uint32_t *bits, size_t elem)
 | 
			
		||||
{
 | 
			
		||||
	bool was_set = bits[elem/32] & (((uint32_t)1) << (elem % 32));
 | 
			
		||||
	bits[elem/32] |= (((uint32_t)1) << (elem % 32));
 | 
			
		||||
 | 
			
		||||
	return was_set;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Filter out bad slots
 | 
			
		||||
 *
 | 
			
		||||
 *  \param[in] mask TS selection mask
 | 
			
		||||
 *  \param[in] ul_slots set of UL timeslots
 | 
			
		||||
 *  \param[in] dl_slots set of DL timeslots
 | 
			
		||||
 *  \param[in] rx_valid_win Mask for valid RX window value
 | 
			
		||||
 *  \returns negative error code or RX window on success
 | 
			
		||||
 */
 | 
			
		||||
int16_t mslot_filter_bad(uint8_t mask, uint8_t ul_slots, uint8_t dl_slots, uint16_t rx_valid_win)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t rx_good;
 | 
			
		||||
	uint16_t rx_bad = (uint16_t)(0xFF & ~mask) << ul_slots;
 | 
			
		||||
 | 
			
		||||
	/* TODO: CHECK this calculation -> separate function for unit testing */
 | 
			
		||||
	rx_bad = (rx_bad | (rx_bad >> 8)) & 0xFF;
 | 
			
		||||
	rx_good = dl_slots & ~rx_bad;
 | 
			
		||||
	if (!rx_good)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	return rx_good & rx_valid_win;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										62
									
								
								src/mslot_class.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/mslot_class.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
/* mslot_class.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 * Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
/* 3GPP TS 05.02 Annex B.1 */
 | 
			
		||||
 | 
			
		||||
#define MS_NA	255 /* N/A */
 | 
			
		||||
#define MS_A	254 /* 1 with hopping, 0 without */
 | 
			
		||||
#define MS_B	253 /* 1 with hopping, 0 without (change Rx to Tx)*/
 | 
			
		||||
#define MS_C	252 /* 1 with hopping, 0 without (change Tx to Rx)*/
 | 
			
		||||
#define MS_TO	251 /* 31 symbol periods (this can be provided by a TA offset, i.e. a minimum TA value) */
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_MSLOT_CLASS 12
 | 
			
		||||
 | 
			
		||||
#define NO_FREE_TFI 0xffffffff
 | 
			
		||||
 | 
			
		||||
enum { MASK_TT = 0, MASK_TR = 1 };
 | 
			
		||||
 | 
			
		||||
/* multislot class selection routines */
 | 
			
		||||
uint8_t mslot_class_get_ta(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_get_tb(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_get_ra(uint8_t ms_cl, uint8_t ta);
 | 
			
		||||
uint8_t mslot_class_get_rb(uint8_t ms_cl, uint8_t ta);
 | 
			
		||||
uint8_t mslot_class_get_tx(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_get_rx(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_get_sum(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_get_type(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_max();
 | 
			
		||||
 | 
			
		||||
/* multislot allocation helper routines */
 | 
			
		||||
void mslot_fill_rx_mask(uint8_t mslot_class, uint8_t num_tx, uint8_t *rx_mask);
 | 
			
		||||
int8_t find_free_usf(uint8_t usf_map);
 | 
			
		||||
int8_t find_free_tfi(uint32_t tfi_map);
 | 
			
		||||
void masked_override_with(char *buf, uint8_t mask, char set_char);
 | 
			
		||||
void ts_format(char *buf, uint8_t dl_mask, uint8_t ul_mask);
 | 
			
		||||
uint16_t mslot_wrap_window(uint16_t win);
 | 
			
		||||
bool mslot_test_and_set_bit(uint32_t *bits, size_t elem);
 | 
			
		||||
int16_t mslot_filter_bad(uint8_t mask, uint8_t ul_slots, uint8_t dl_slots, uint16_t rx_valid_win);
 | 
			
		||||
							
								
								
									
										849
									
								
								src/nacc_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										849
									
								
								src/nacc_fsm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,849 @@
 | 
			
		||||
/* nacc_fsm.c
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <talloc.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/ctrl/control_cmd.h>
 | 
			
		||||
#include <osmocom/ctrl/control_if.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/gsm48.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp_rim.h>
 | 
			
		||||
 | 
			
		||||
#include <nacc_fsm.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <gprs_ms.h>
 | 
			
		||||
#include <encoding.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#include <neigh_cache.h>
 | 
			
		||||
 | 
			
		||||
#define X(s) (1 << (s))
 | 
			
		||||
 | 
			
		||||
/* Infer CTRL id (seqnum) for a given tgt arfcn+bsic (bsic range: 0-63) */
 | 
			
		||||
#define arfcn_bsic_2_ctrl_id(arfcn, bsic) ((arfcn) * 100 + (bsic))
 | 
			
		||||
 | 
			
		||||
static const struct osmo_tdef_state_timeout nacc_fsm_timeouts[32] = {
 | 
			
		||||
	[NACC_ST_INITIAL] = {},
 | 
			
		||||
	[NACC_ST_WAIT_RESOLVE_RAC_CI] = { .T = PCU_TDEF_NEIGH_RESOLVE_TO },
 | 
			
		||||
	[NACC_ST_WAIT_REQUEST_SI] = { .T = PCU_TDEF_SI_RESOLVE_TO },
 | 
			
		||||
	[NACC_ST_TX_NEIGHBOUR_DATA] = {},
 | 
			
		||||
	[NACC_ST_TX_CELL_CHG_CONTINUE] = {},
 | 
			
		||||
	[NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK] = {}, /* Timeout through event controlled by tbf::poll_timeout() */
 | 
			
		||||
	[NACC_ST_DONE] = {},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
 | 
			
		||||
 * The actual timeout value is in turn obtained from conn->T_defs.
 | 
			
		||||
 * Assumes local variable fi exists. */
 | 
			
		||||
 | 
			
		||||
#define nacc_fsm_state_chg(fi, NEXT_STATE) \
 | 
			
		||||
	osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
 | 
			
		||||
				     nacc_fsm_timeouts, \
 | 
			
		||||
				     ((struct nacc_fsm_ctx*)(fi->priv))->ms->bts->pcu->T_defs, \
 | 
			
		||||
				     -1)
 | 
			
		||||
 | 
			
		||||
const struct value_string nacc_fsm_event_names[] = {
 | 
			
		||||
	{ NACC_EV_RX_CELL_CHG_NOTIFICATION, "RX_CELL_CHG_NOTIFICATION" },
 | 
			
		||||
	{ NACC_EV_RX_RAC_CI, "RX_RAC_CI" },
 | 
			
		||||
	{ NACC_EV_RX_SI, "RX_SI" },
 | 
			
		||||
	{ NACC_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" },
 | 
			
		||||
	{ NACC_EV_RX_CELL_CHG_CONTINUE_ACK, "RX_CELL_CHG_CONTINUE_ACK"},
 | 
			
		||||
	{ NACC_EV_TIMEOUT_CELL_CHG_CONTINUE, "TIMEOUT_CELL_CHG_CONTINUE" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* TS 44 060 11.2.9e Packet Neighbour Cell Data */
 | 
			
		||||
static struct msgb *create_packet_neighbour_cell_data(struct nacc_fsm_ctx *ctx,
 | 
			
		||||
						      const struct gprs_rlcmac_tbf *tbf,
 | 
			
		||||
						      bool *all_si_info_sent)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	int rc;
 | 
			
		||||
	RlcMacDownlink_t *mac_control_block;
 | 
			
		||||
	struct GprsMs *ms = tbf_ms(tbf);
 | 
			
		||||
	OSMO_ASSERT(tbf_is_tfi_assigned(tbf));
 | 
			
		||||
	uint8_t tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
 | 
			
		||||
	uint8_t tfi = tbf_tfi(tbf);
 | 
			
		||||
	uint8_t container_id = 0;
 | 
			
		||||
	PNCDContainer_t container;
 | 
			
		||||
	size_t max_len, len_to_write;
 | 
			
		||||
	uint8_t *cont_buf;
 | 
			
		||||
	uint8_t si_type = ctx->si_info.type_psi ? 0x01 : 0x0;
 | 
			
		||||
 | 
			
		||||
	memset(&container, 0, sizeof(container));
 | 
			
		||||
	if (ctx->container_idx == 0) {
 | 
			
		||||
		container.UnionType = 1; /* with ID */
 | 
			
		||||
		container.u.PNCD_Container_With_ID.ARFCN = ctx->neigh_key.tgt_arfcn;
 | 
			
		||||
		container.u.PNCD_Container_With_ID.BSIC = ctx->neigh_key.tgt_bsic;
 | 
			
		||||
		cont_buf = &container.u.PNCD_Container_With_ID.CONTAINER[0];
 | 
			
		||||
		max_len = sizeof(container.u.PNCD_Container_With_ID.CONTAINER) - 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		container.UnionType = 0; /* without ID */
 | 
			
		||||
		cont_buf = &container.u.PNCD_Container_Without_ID.CONTAINER[0];
 | 
			
		||||
		max_len = sizeof(container.u.PNCD_Container_Without_ID.CONTAINER) - 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	len_to_write = ctx->si_info.si_len - ctx->si_info_bytes_sent;
 | 
			
		||||
 | 
			
		||||
	if (len_to_write == 0) {
 | 
			
		||||
		/* We sent all info on last message filing it exactly, we now send a zeroed one to finish */
 | 
			
		||||
		*all_si_info_sent = true;
 | 
			
		||||
		*cont_buf = (si_type << 5) | 0x00;
 | 
			
		||||
	} else if (len_to_write >= max_len) {
 | 
			
		||||
		/* We fill the rlcmac block, we'll need more messages */
 | 
			
		||||
		*all_si_info_sent = false;
 | 
			
		||||
		*cont_buf = (si_type << 5) |  0x1F;
 | 
			
		||||
		memcpy(cont_buf + 1, &ctx->si_info.si_buf[ctx->si_info_bytes_sent], max_len);
 | 
			
		||||
		ctx->si_info_bytes_sent += max_len;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Last block, we don't fill it exactly */
 | 
			
		||||
		*all_si_info_sent = true;
 | 
			
		||||
		*cont_buf = (si_type << 5) | (len_to_write & 0x1F);
 | 
			
		||||
		memcpy(cont_buf + 1, &ctx->si_info.si_buf[ctx->si_info_bytes_sent], len_to_write);
 | 
			
		||||
		ctx->si_info_bytes_sent += len_to_write;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg = msgb_alloc(GSM_MACBLOCK_LEN, "neighbour_cell_data");
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Initialize a bit vector that uses allocated msgb as the data buffer. */
 | 
			
		||||
	struct bitvec bv = {
 | 
			
		||||
		.data = msgb_put(msg, GSM_MACBLOCK_LEN),
 | 
			
		||||
		.data_len = GSM_MACBLOCK_LEN,
 | 
			
		||||
	};
 | 
			
		||||
	bitvec_unhex(&bv, DUMMY_VEC);
 | 
			
		||||
 | 
			
		||||
	mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);
 | 
			
		||||
 | 
			
		||||
	write_packet_neighbour_cell_data(mac_control_block,
 | 
			
		||||
					 tfi_is_dl, tfi, container_id,
 | 
			
		||||
					 ctx->container_idx, &container);
 | 
			
		||||
	LOGP(DNACC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Neighbour Cell Data +++++++++++++++++++++++++\n");
 | 
			
		||||
	rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DTBF, LOGL_ERROR, "Encoding of Packet Neighbour Cell Data failed (%d)\n", rc);
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DNACC, LOGL_DEBUG, "------------------------- TX : Packet Neighbour Cell Data -------------------------\n");
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(bts_rate_counters(ms->bts), CTR_PKT_NEIGH_CELL_DATA));
 | 
			
		||||
	talloc_free(mac_control_block);
 | 
			
		||||
 | 
			
		||||
	ctx->container_idx++;
 | 
			
		||||
 | 
			
		||||
	return msg;
 | 
			
		||||
 | 
			
		||||
free_ret:
 | 
			
		||||
	talloc_free(mac_control_block);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TS 44 060 11.2.2a Packet Cell Change Continue */
 | 
			
		||||
static struct msgb *create_packet_cell_chg_continue(const struct nacc_fsm_ctx *ctx,
 | 
			
		||||
						    const struct nacc_ev_create_rlcmac_msg_ctx *data,
 | 
			
		||||
						    uint32_t *new_poll_fn)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	int rc;
 | 
			
		||||
	RlcMacDownlink_t *mac_control_block;
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf = data->tbf;
 | 
			
		||||
	struct GprsMs *ms = tbf_ms(tbf);
 | 
			
		||||
	unsigned int rrbp;
 | 
			
		||||
 | 
			
		||||
	rc = tbf_check_polling(tbf, data->fn, data->ts, new_poll_fn, &rrbp);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DTBF, LOGL_ERROR, "Failed registering poll for Pkt Cell Chg Continue (%d)\n", rc);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_cell_chg_continue");
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Initialize a bit vector that uses allocated msgb as the data buffer. */
 | 
			
		||||
	struct bitvec bv = {
 | 
			
		||||
		.data = msgb_put(msg, GSM_MACBLOCK_LEN),
 | 
			
		||||
		.data_len = GSM_MACBLOCK_LEN,
 | 
			
		||||
	};
 | 
			
		||||
	bitvec_unhex(&bv, DUMMY_VEC);
 | 
			
		||||
 | 
			
		||||
	mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(tbf_is_tfi_assigned(tbf));
 | 
			
		||||
	uint8_t tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
 | 
			
		||||
	uint8_t tfi = tbf_tfi(tbf);
 | 
			
		||||
	uint8_t container_id = 0;
 | 
			
		||||
	write_packet_cell_change_continue(mac_control_block, 1, rrbp, tfi_is_dl, tfi, true,
 | 
			
		||||
			ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic, container_id);
 | 
			
		||||
	LOGP(DNACC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Cell Change Continue +++++++++++++++++++++++++\n");
 | 
			
		||||
	rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DTBF, LOGL_ERROR, "Encoding of Packet Cell Change Continue failed (%d)\n", rc);
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DNACC, LOGL_DEBUG, "------------------------- TX : Packet Cell Change Continue -------------------------\n");
 | 
			
		||||
	rate_ctr_inc(rate_ctr_group_get_ctr(bts_rate_counters(ms->bts), CTR_PKT_CELL_CHG_CONTINUE));
 | 
			
		||||
	talloc_free(mac_control_block);
 | 
			
		||||
	tbf_set_polling(tbf, *new_poll_fn, data->ts, PDCH_ULC_POLL_CELL_CHG_CONTINUE);
 | 
			
		||||
	return msg;
 | 
			
		||||
 | 
			
		||||
free_ret:
 | 
			
		||||
	talloc_free(mac_control_block);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fill_rim_ran_info_req(const struct nacc_fsm_ctx *ctx, struct bssgp_ran_information_pdu *pdu)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = ctx->ms->bts;
 | 
			
		||||
 | 
			
		||||
	*pdu = (struct bssgp_ran_information_pdu){
 | 
			
		||||
		.routing_info_dest = {
 | 
			
		||||
			.discr = BSSGP_RIM_ROUTING_INFO_GERAN,
 | 
			
		||||
			.geran = {
 | 
			
		||||
				.raid = {
 | 
			
		||||
					.mcc = ctx->cgi_ps.rai.lac.plmn.mcc,
 | 
			
		||||
					.mnc = ctx->cgi_ps.rai.lac.plmn.mnc,
 | 
			
		||||
					.mnc_3_digits = ctx->cgi_ps.rai.lac.plmn.mnc_3_digits,
 | 
			
		||||
					.lac = ctx->cgi_ps.rai.lac.lac,
 | 
			
		||||
					.rac = ctx->cgi_ps.rai.rac,
 | 
			
		||||
				},
 | 
			
		||||
				.cid = ctx->cgi_ps.cell_identity,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.routing_info_src = {
 | 
			
		||||
			.discr = BSSGP_RIM_ROUTING_INFO_GERAN,
 | 
			
		||||
			.geran = {
 | 
			
		||||
				.raid = {
 | 
			
		||||
					.mcc = bts->cgi_ps.rai.lac.plmn.mcc,
 | 
			
		||||
					.mnc = bts->cgi_ps.rai.lac.plmn.mnc,
 | 
			
		||||
					.mnc_3_digits = bts->cgi_ps.rai.lac.plmn.mnc_3_digits,
 | 
			
		||||
					.lac = bts->cgi_ps.rai.lac.lac,
 | 
			
		||||
					.rac = bts->cgi_ps.rai.rac,
 | 
			
		||||
				},
 | 
			
		||||
				.cid = bts->cgi_ps.cell_identity,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		.rim_cont_iei = BSSGP_IE_RI_REQ_RIM_CONTAINER,
 | 
			
		||||
		.decoded_present = true,
 | 
			
		||||
		.decoded = {
 | 
			
		||||
			.req_rim_cont = {
 | 
			
		||||
				.app_id = BSSGP_RAN_INF_APP_ID_NACC,
 | 
			
		||||
				.seq_num = 1,
 | 
			
		||||
				.pdu_ind = {
 | 
			
		||||
					.ack_requested = 0,
 | 
			
		||||
					.pdu_type_ext = RIM_PDU_TYPE_SING_REP,
 | 
			
		||||
				},
 | 
			
		||||
				.prot_ver = 1,
 | 
			
		||||
				.son_trans_app_id = NULL,
 | 
			
		||||
				.son_trans_app_id_len = 0,
 | 
			
		||||
				.u = {
 | 
			
		||||
					.app_cont_nacc = {
 | 
			
		||||
						.reprt_cell = ctx->cgi_ps,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fill_neigh_key_from_bts_pkt_cell_chg_not(struct neigh_cache_entry_key *neigh_key,
 | 
			
		||||
						    const struct gprs_rlcmac_bts *bts,
 | 
			
		||||
						    const Packet_Cell_Change_Notification_t *notif)
 | 
			
		||||
{
 | 
			
		||||
	switch (notif->Target_Cell.UnionType) {
 | 
			
		||||
	case 0: /* GSM */
 | 
			
		||||
		neigh_key->local_lac = bts->cgi_ps.rai.lac.lac;
 | 
			
		||||
		neigh_key->local_ci = bts->cgi_ps.cell_identity;
 | 
			
		||||
		neigh_key->tgt_arfcn = notif->Target_Cell.u.Target_Cell_GSM_Notif.ARFCN;
 | 
			
		||||
		neigh_key->tgt_bsic = notif->Target_Cell.u.Target_Cell_GSM_Notif.BSIC;
 | 
			
		||||
		return 0;
 | 
			
		||||
	default:
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define SI_HDR_LEN 2
 | 
			
		||||
static void bts_fill_si_cache_value(const struct gprs_rlcmac_bts *bts, struct si_cache_value *val)
 | 
			
		||||
{
 | 
			
		||||
	val->type_psi = false;
 | 
			
		||||
	val->si_len = 0;
 | 
			
		||||
	if (bts->si1_is_set) {
 | 
			
		||||
		osmo_static_assert(sizeof(bts->si1) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si1_header_size);
 | 
			
		||||
		memcpy(&val->si_buf[val->si_len], bts->si1 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
 | 
			
		||||
		val->si_len += BSSGP_RIM_SI_LEN;
 | 
			
		||||
	}
 | 
			
		||||
	if (bts->si3_is_set) {
 | 
			
		||||
		osmo_static_assert(sizeof(bts->si3) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si3_header_size);
 | 
			
		||||
		memcpy(&val->si_buf[val->si_len], bts->si3 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
 | 
			
		||||
		val->si_len += BSSGP_RIM_SI_LEN;
 | 
			
		||||
	}
 | 
			
		||||
	if (bts->si13_is_set) {
 | 
			
		||||
		osmo_static_assert(sizeof(bts->si13) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si13_header_size);
 | 
			
		||||
		memcpy(&val->si_buf[val->si_len], bts->si13 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
 | 
			
		||||
		val->si_len += BSSGP_RIM_SI_LEN;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called on event NACC_EV_RX_CELL_CHG_NOTIFICATION on states after
 | 
			
		||||
 * WAIT_RESOLVE_RAC_CI. Ignore duplicate messages, transition back if target
 | 
			
		||||
 * cell changed.
 | 
			
		||||
 */
 | 
			
		||||
static void handle_retrans_pkt_cell_chg_notif(struct nacc_fsm_ctx *ctx, const Packet_Cell_Change_Notification_t *notif)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = ctx->ms->bts;
 | 
			
		||||
	struct neigh_cache_entry_key neigh_key;
 | 
			
		||||
 | 
			
		||||
	if (fill_neigh_key_from_bts_pkt_cell_chg_not(&neigh_key, bts, notif) < 0) {
 | 
			
		||||
		LOGPFSML(ctx->fi, LOGL_NOTICE, "TargetCell type=0x%x not supported\n",
 | 
			
		||||
			 notif->Target_Cell.UnionType);
 | 
			
		||||
		if (ctx->fi->state != NACC_ST_TX_CELL_CHG_CONTINUE)
 | 
			
		||||
			nacc_fsm_state_chg(ctx->fi, NACC_ST_TX_CELL_CHG_CONTINUE);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	/* If tgt cell changed, restart resolving it */
 | 
			
		||||
	if (!neigh_cache_entry_key_eq(&ctx->neigh_key, &neigh_key)) {
 | 
			
		||||
		ctx->neigh_key = neigh_key;
 | 
			
		||||
		nacc_fsm_state_chg(ctx->fi, NACC_ST_WAIT_RESOLVE_RAC_CI);
 | 
			
		||||
	}
 | 
			
		||||
	/* else: ignore it, it's a dup, carry on what we were doing */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
////////////////
 | 
			
		||||
// FSM states //
 | 
			
		||||
////////////////
 | 
			
		||||
 | 
			
		||||
static void st_initial(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = ctx->ms->bts;
 | 
			
		||||
	Packet_Cell_Change_Notification_t *notif;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case NACC_EV_RX_CELL_CHG_NOTIFICATION:
 | 
			
		||||
		notif = (Packet_Cell_Change_Notification_t *)data;
 | 
			
		||||
		if (fill_neigh_key_from_bts_pkt_cell_chg_not(&ctx->neigh_key, bts, notif) < 0) {
 | 
			
		||||
			LOGPFSML(fi, LOGL_NOTICE, "TargetCell type=0x%x not supported\n",
 | 
			
		||||
				 notif->Target_Cell.UnionType);
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
		} else {
 | 
			
		||||
			nacc_fsm_state_chg(fi, NACC_ST_WAIT_RESOLVE_RAC_CI);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_wait_resolve_rac_ci_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = ctx->ms->bts;
 | 
			
		||||
	struct gprs_pcu *pcu = bts->pcu;
 | 
			
		||||
	const struct osmo_cell_global_id_ps *cgi_ps;
 | 
			
		||||
	struct ctrl_cmd *cmd = NULL;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* First try to find the value in the cache */
 | 
			
		||||
	cgi_ps = neigh_cache_lookup_value(pcu->neigh_cache, &ctx->neigh_key);
 | 
			
		||||
	if (cgi_ps) {
 | 
			
		||||
		ctx->cgi_ps = *cgi_ps;
 | 
			
		||||
		nacc_fsm_state_chg(fi, NACC_ST_WAIT_REQUEST_SI);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* CGI-PS not in cache, resolve it using BSC Neighbor Resolution CTRL interface */
 | 
			
		||||
 | 
			
		||||
	LOGPFSML(fi, LOGL_DEBUG, "No CGI-PS found in cache, resolving " NEIGH_CACHE_ENTRY_KEY_FMT "...\n",
 | 
			
		||||
		 NEIGH_CACHE_ENTRY_KEY_ARGS(&ctx->neigh_key));
 | 
			
		||||
 | 
			
		||||
	/* We may have changed to this state previously (eg: we are handling
 | 
			
		||||
	 * another Pkt cell Change Notify with different target). Avoid
 | 
			
		||||
	 * re-creating the socket in that case. */
 | 
			
		||||
	if (ctx->neigh_ctrl_conn->write_queue.bfd.fd == -1) {
 | 
			
		||||
		rc = osmo_sock_init2_ofd(&ctx->neigh_ctrl_conn->write_queue.bfd,
 | 
			
		||||
					 AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP,
 | 
			
		||||
					 NULL, 0, pcu->vty.neigh_ctrl_addr, pcu->vty.neigh_ctrl_port,
 | 
			
		||||
					 OSMO_SOCK_F_CONNECT);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			LOGPFSML(fi, LOGL_ERROR,
 | 
			
		||||
				"Failed to establish CTRL (neighbor resolution) connection to BSC r=%s:%u\n\n",
 | 
			
		||||
				pcu->vty.neigh_ctrl_addr, pcu->vty.neigh_ctrl_port);
 | 
			
		||||
			goto err_term;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd = ctrl_cmd_create(ctx, CTRL_TYPE_GET);
 | 
			
		||||
	if (!cmd) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR, "CTRL msg creation failed\n");
 | 
			
		||||
		goto err_term;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd->id = talloc_asprintf(cmd, "%u", arfcn_bsic_2_ctrl_id(ctx->neigh_key.tgt_arfcn,
 | 
			
		||||
								  ctx->neigh_key.tgt_bsic));
 | 
			
		||||
	cmd->variable = talloc_asprintf(cmd, "neighbor_resolve_cgi_ps_from_lac_ci.%d.%d.%d.%d",
 | 
			
		||||
					ctx->neigh_key.local_lac, ctx->neigh_key.local_ci,
 | 
			
		||||
					ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic);
 | 
			
		||||
	rc = ctrl_cmd_send(&ctx->neigh_ctrl_conn->write_queue, cmd);
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR, "CTRL msg sent failed: %d\n", rc);
 | 
			
		||||
		goto err_term;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(cmd);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
err_term:
 | 
			
		||||
	talloc_free(cmd);
 | 
			
		||||
	nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_wait_resolve_rac_ci(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
 | 
			
		||||
	const Packet_Cell_Change_Notification_t *notif;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case NACC_EV_RX_CELL_CHG_NOTIFICATION:
 | 
			
		||||
		notif = (const Packet_Cell_Change_Notification_t *)data;
 | 
			
		||||
		handle_retrans_pkt_cell_chg_notif(ctx, notif);
 | 
			
		||||
		break;
 | 
			
		||||
	case NACC_EV_RX_RAC_CI:
 | 
			
		||||
		/* Assumption: ctx->cgi_ps has been filled by caller of the event */
 | 
			
		||||
		nacc_fsm_state_chg(fi, NACC_ST_WAIT_REQUEST_SI);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* At this point, we expect correct tgt cell info to be already in ctx->cgi_ps */
 | 
			
		||||
static void st_wait_request_si_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = ctx->ms->bts;
 | 
			
		||||
	struct gprs_pcu *pcu = bts->pcu;
 | 
			
		||||
	struct bssgp_ran_information_pdu pdu;
 | 
			
		||||
	const struct si_cache_value *si;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts_i;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* First check if the CGI-PS addresses a cell managed by this PCU. If
 | 
			
		||||
	 * that's the case, we already have the info and there's no need to go
 | 
			
		||||
	 * the RIM way since we'd end up to this same PCU on the other end anyway.
 | 
			
		||||
	 */
 | 
			
		||||
	llist_for_each_entry(bts_i, &the_pcu->bts_list, list) {
 | 
			
		||||
		if (bts_i == bts) /* Makes no sense targeting the same cell */
 | 
			
		||||
			continue;
 | 
			
		||||
		if (osmo_cgi_ps_cmp(&ctx->cgi_ps, &bts_i->cgi_ps) != 0)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		LOGPFSML(fi, LOGL_DEBUG, "neighbor CGI-PS %s addresses local BTS %d\n",
 | 
			
		||||
			 osmo_cgi_ps_name(&ctx->cgi_ps), bts_i->nr);
 | 
			
		||||
		bts_fill_si_cache_value(bts, &ctx->si_info);
 | 
			
		||||
		/* Tell the PCU scheduler we are ready to go, from here one we
 | 
			
		||||
		 * are polled/driven by the scheduler */
 | 
			
		||||
		nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* First check if we have SI info for the target cell in cache */
 | 
			
		||||
	si = si_cache_lookup_value(pcu->si_cache, &ctx->cgi_ps);
 | 
			
		||||
	if (si) {
 | 
			
		||||
		/* Copy info since cache can be deleted at any point */
 | 
			
		||||
		memcpy(&ctx->si_info, si, sizeof(ctx->si_info));
 | 
			
		||||
		/* Tell the PCU scheduler we are ready to go, from here one we
 | 
			
		||||
		 * are polled/driven by the scheduler */
 | 
			
		||||
		nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* SI info not in cache, resolve it using RIM procedure against SGSN */
 | 
			
		||||
	if (fill_rim_ran_info_req(ctx, &pdu) < 0) {
 | 
			
		||||
		nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPFSML(fi, LOGL_INFO, "Tx RIM RAN-INFO to request SI of %s\n",
 | 
			
		||||
		 osmo_cgi_ps_name(&ctx->cgi_ps));
 | 
			
		||||
	rc = bssgp_tx_rim(&pdu, gprs_ns2_nse_nsei(ctx->ms->bts->nse));
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR, "Failed transmitting RIM RAN-INFO %s PDU: %d\n",
 | 
			
		||||
			 osmo_cgi_ps_name(&ctx->cgi_ps), rc);
 | 
			
		||||
		nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void st_wait_request_si(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
 | 
			
		||||
	const Packet_Cell_Change_Notification_t *notif;
 | 
			
		||||
	struct si_cache_entry *entry;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case NACC_EV_RX_CELL_CHG_NOTIFICATION:
 | 
			
		||||
		notif = (const Packet_Cell_Change_Notification_t *)data;
 | 
			
		||||
		handle_retrans_pkt_cell_chg_notif(ctx, notif);
 | 
			
		||||
		break;
 | 
			
		||||
	case NACC_EV_RX_SI:
 | 
			
		||||
		entry = (struct si_cache_entry *)data;
 | 
			
		||||
		/* Copy info since cache can be deleted at any point */
 | 
			
		||||
		memcpy(&ctx->si_info, &entry->value, sizeof(ctx->si_info));
 | 
			
		||||
		/* Tell the PCU scheduler we are ready to go, from here one we
 | 
			
		||||
		 * are polled/driven by the scheduler */
 | 
			
		||||
		nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* At this point, we already received all required SI information to send stored
 | 
			
		||||
 * in struct nacc_fsm_ctx. We now wait for scheduler to ask us to construct
 | 
			
		||||
 * RLCMAC DL CTRL messages to move FSM states forward
 | 
			
		||||
 */
 | 
			
		||||
static void st_tx_neighbour_data_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
 | 
			
		||||
	ctx->si_info_bytes_sent = 0;
 | 
			
		||||
	ctx->container_idx = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_tx_neighbour_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
 | 
			
		||||
	const Packet_Cell_Change_Notification_t *notif;
 | 
			
		||||
	struct nacc_ev_create_rlcmac_msg_ctx *data_ctx;
 | 
			
		||||
	bool all_si_info_sent;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case NACC_EV_RX_CELL_CHG_NOTIFICATION:
 | 
			
		||||
		notif = (const Packet_Cell_Change_Notification_t *)data;
 | 
			
		||||
		handle_retrans_pkt_cell_chg_notif(ctx, notif);
 | 
			
		||||
		break;
 | 
			
		||||
	case NACC_EV_CREATE_RLCMAC_MSG:
 | 
			
		||||
		data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx *)data;
 | 
			
		||||
		data_ctx->msg = create_packet_neighbour_cell_data(ctx, data_ctx->tbf, &all_si_info_sent);
 | 
			
		||||
		if (!data_ctx->msg) {
 | 
			
		||||
			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (all_si_info_sent) /* DONE */
 | 
			
		||||
			nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* st_cell_chg_continue_on_enter:
 | 
			
		||||
 * At this point, we already sent all Pkt Cell Neighbour Change rlcmac
 | 
			
		||||
 * blocks, and we only need to wait to be scheduled again to send PKT
 | 
			
		||||
 * CELL CHANGE NOTIFICATION and then we are done
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static void st_cell_chg_continue(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
 | 
			
		||||
	const Packet_Cell_Change_Notification_t *notif;
 | 
			
		||||
	struct nacc_ev_create_rlcmac_msg_ctx *data_ctx;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case NACC_EV_RX_CELL_CHG_NOTIFICATION:
 | 
			
		||||
		notif = (const Packet_Cell_Change_Notification_t *)data;
 | 
			
		||||
		handle_retrans_pkt_cell_chg_notif(ctx, notif);
 | 
			
		||||
		break;
 | 
			
		||||
	case NACC_EV_CREATE_RLCMAC_MSG:
 | 
			
		||||
		data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx *)data;
 | 
			
		||||
		data_ctx->msg = create_packet_cell_chg_continue(ctx, data_ctx, &ctx->continue_poll_fn);
 | 
			
		||||
		if (data_ctx->msg) {
 | 
			
		||||
			ctx->continue_poll_ts = data_ctx->ts;
 | 
			
		||||
			nacc_fsm_state_chg(fi, NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void st_wait_cell_chg_continue_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
 | 
			
		||||
	const Packet_Cell_Change_Notification_t *notif;
 | 
			
		||||
 | 
			
		||||
	switch (event) {
 | 
			
		||||
	case NACC_EV_RX_CELL_CHG_NOTIFICATION:
 | 
			
		||||
		notif = (const Packet_Cell_Change_Notification_t *)data;
 | 
			
		||||
		handle_retrans_pkt_cell_chg_notif(ctx, notif);
 | 
			
		||||
		break;
 | 
			
		||||
	case NACC_EV_TIMEOUT_CELL_CHG_CONTINUE:
 | 
			
		||||
		nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
 | 
			
		||||
		break;
 | 
			
		||||
	case NACC_EV_RX_CELL_CHG_CONTINUE_ACK:
 | 
			
		||||
		nacc_fsm_state_chg(fi, NACC_ST_DONE);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void st_done_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nacc_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
 | 
			
		||||
	/* after cleanup() finishes, FSM termination calls osmo_fsm_inst_free,
 | 
			
		||||
	   so we need to avoid double-freeing it during ctx talloc free
 | 
			
		||||
	   destructor */
 | 
			
		||||
	talloc_reparent(ctx, ctx->ms, ctx->fi);
 | 
			
		||||
	ctx->fi = NULL;
 | 
			
		||||
 | 
			
		||||
	/* remove references from owning MS and free entire ctx */
 | 
			
		||||
	ctx->ms->nacc = NULL;
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nacc_fsm_timer_cb(struct osmo_fsm_inst *fi)
 | 
			
		||||
{
 | 
			
		||||
	switch (fi->T) {
 | 
			
		||||
	case PCU_TDEF_NEIGH_RESOLVE_TO:
 | 
			
		||||
	case PCU_TDEF_SI_RESOLVE_TO:
 | 
			
		||||
		nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct osmo_fsm_state nacc_fsm_states[] = {
 | 
			
		||||
	[NACC_ST_INITIAL] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(NACC_EV_RX_CELL_CHG_NOTIFICATION),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(NACC_ST_WAIT_RESOLVE_RAC_CI),
 | 
			
		||||
		.name = "INITIAL",
 | 
			
		||||
		.action = st_initial,
 | 
			
		||||
	},
 | 
			
		||||
	[NACC_ST_WAIT_RESOLVE_RAC_CI] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
 | 
			
		||||
			X(NACC_EV_RX_RAC_CI),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
 | 
			
		||||
			X(NACC_ST_WAIT_REQUEST_SI) |
 | 
			
		||||
			X(NACC_ST_TX_CELL_CHG_CONTINUE),
 | 
			
		||||
		.name = "WAIT_RESOLVE_RAC_CI",
 | 
			
		||||
		.onenter = st_wait_resolve_rac_ci_on_enter,
 | 
			
		||||
		.action = st_wait_resolve_rac_ci,
 | 
			
		||||
	},
 | 
			
		||||
	[NACC_ST_WAIT_REQUEST_SI] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
 | 
			
		||||
			X(NACC_EV_RX_SI),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
 | 
			
		||||
			X(NACC_ST_TX_NEIGHBOUR_DATA) |
 | 
			
		||||
			X(NACC_ST_TX_CELL_CHG_CONTINUE),
 | 
			
		||||
		.name = "WAIT_REQUEST_SI",
 | 
			
		||||
		.onenter = st_wait_request_si_on_enter,
 | 
			
		||||
		.action = st_wait_request_si,
 | 
			
		||||
	},
 | 
			
		||||
	[NACC_ST_TX_NEIGHBOUR_DATA] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
 | 
			
		||||
			X(NACC_EV_CREATE_RLCMAC_MSG),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
 | 
			
		||||
			X(NACC_ST_TX_CELL_CHG_CONTINUE),
 | 
			
		||||
		.name = "TX_NEIGHBOUR_DATA",
 | 
			
		||||
		.onenter = st_tx_neighbour_data_on_enter,
 | 
			
		||||
		.action = st_tx_neighbour_data,
 | 
			
		||||
	},
 | 
			
		||||
	[NACC_ST_TX_CELL_CHG_CONTINUE] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
 | 
			
		||||
			X(NACC_EV_CREATE_RLCMAC_MSG),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
 | 
			
		||||
			X(NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK),
 | 
			
		||||
		.name = "TX_CELL_CHG_CONTINUE",
 | 
			
		||||
		.action = st_cell_chg_continue,
 | 
			
		||||
	},
 | 
			
		||||
	[NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK] = {
 | 
			
		||||
		.in_event_mask =
 | 
			
		||||
			X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
 | 
			
		||||
			X(NACC_EV_RX_CELL_CHG_CONTINUE_ACK) |
 | 
			
		||||
			X(NACC_EV_TIMEOUT_CELL_CHG_CONTINUE),
 | 
			
		||||
		.out_state_mask =
 | 
			
		||||
			X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
 | 
			
		||||
			X(NACC_ST_TX_CELL_CHG_CONTINUE) |
 | 
			
		||||
			X(NACC_ST_DONE),
 | 
			
		||||
		.name = "WAIT_CELL_CHG_CONTINUE_ACK",
 | 
			
		||||
		.action = st_wait_cell_chg_continue_ack,
 | 
			
		||||
	},
 | 
			
		||||
	[NACC_ST_DONE] = {
 | 
			
		||||
		.in_event_mask = 0,
 | 
			
		||||
		.out_state_mask = 0,
 | 
			
		||||
		.name = "DONE",
 | 
			
		||||
		.onenter = st_done_on_enter,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct osmo_fsm nacc_fsm = {
 | 
			
		||||
	.name = "NACC",
 | 
			
		||||
	.states = nacc_fsm_states,
 | 
			
		||||
	.num_states = ARRAY_SIZE(nacc_fsm_states),
 | 
			
		||||
	.timer_cb = nacc_fsm_timer_cb,
 | 
			
		||||
	.cleanup = nacc_fsm_cleanup,
 | 
			
		||||
	.log_subsys = DNACC,
 | 
			
		||||
	.event_names = nacc_fsm_event_names,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static __attribute__((constructor)) void nacc_fsm_init(void)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(osmo_fsm_register(&nacc_fsm) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void nacc_fsm_ctrl_reply_cb(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)data;
 | 
			
		||||
	char *tmp = NULL, *tok, *saveptr;
 | 
			
		||||
	unsigned int exp_id;
 | 
			
		||||
 | 
			
		||||
	LOGPFSML(ctx->fi, LOGL_NOTICE, "Received CTRL message: type=%d %s %s: %s\n",
 | 
			
		||||
		 cmd->type, cmd->variable, cmd->id, osmo_escape_str(cmd->reply, -1));
 | 
			
		||||
 | 
			
		||||
	if (cmd->type != CTRL_TYPE_GET_REPLY || !cmd->reply) {
 | 
			
		||||
		nacc_fsm_state_chg(ctx->fi, NACC_ST_TX_CELL_CHG_CONTINUE);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Validate it's the seqnum from our last GET cmd, and not from older
 | 
			
		||||
	 * one we may have requested in case MS decided to resend Pkt Cell
 | 
			
		||||
	 * Change Notify with a different tgt cell:
 | 
			
		||||
	 */
 | 
			
		||||
	exp_id = arfcn_bsic_2_ctrl_id(ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic);
 | 
			
		||||
	if ((unsigned int)atoi(cmd->id) != exp_id) {
 | 
			
		||||
		LOGPFSML(ctx->fi, LOGL_INFO,
 | 
			
		||||
			 "Received CTRL message with id=%s doesn't match our expected last id=%d, ignoring\n",
 | 
			
		||||
			 cmd->id, exp_id);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* TODO: Potentially validate cmd->variable contains same params as we
 | 
			
		||||
	   sent, and that cmd->id matches the original set. We may want to keep
 | 
			
		||||
	   the original cmd around by setting cmd->defer=1 when sending it. */
 | 
			
		||||
 | 
			
		||||
	tmp = talloc_strdup(cmd, cmd->reply);
 | 
			
		||||
	if (!tmp)
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
 | 
			
		||||
	if (!(tok = strtok_r(tmp, "-", &saveptr)))
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	ctx->cgi_ps.rai.lac.plmn.mcc = atoi(tok);
 | 
			
		||||
 | 
			
		||||
	if (!(tok = strtok_r(NULL, "-", &saveptr)))
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	ctx->cgi_ps.rai.lac.plmn.mnc = atoi(tok);
 | 
			
		||||
 | 
			
		||||
	if (!(tok = strtok_r(NULL, "-", &saveptr)))
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	ctx->cgi_ps.rai.lac.lac = atoi(tok);
 | 
			
		||||
 | 
			
		||||
	if (!(tok = strtok_r(NULL, "-", &saveptr)))
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	ctx->cgi_ps.rai.rac = atoi(tok);
 | 
			
		||||
 | 
			
		||||
	if (!(tok = strtok_r(NULL, "\0", &saveptr)))
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	ctx->cgi_ps.cell_identity = atoi(tok);
 | 
			
		||||
 | 
			
		||||
	/* Cache the cgi_ps so we can avoid requesting again same resolution for a while */
 | 
			
		||||
	neigh_cache_add(ctx->ms->bts->pcu->neigh_cache, &ctx->neigh_key, &ctx->cgi_ps);
 | 
			
		||||
 | 
			
		||||
	osmo_fsm_inst_dispatch(ctx->fi, NACC_EV_RX_RAC_CI, NULL);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
free_ret:
 | 
			
		||||
	talloc_free(tmp);
 | 
			
		||||
	nacc_fsm_state_chg(ctx->fi, NACC_ST_TX_CELL_CHG_CONTINUE);
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int nacc_fsm_ctx_talloc_destructor(struct nacc_fsm_ctx *ctx)
 | 
			
		||||
{
 | 
			
		||||
	if (ctx->fi) {
 | 
			
		||||
		osmo_fsm_inst_free(ctx->fi);
 | 
			
		||||
		ctx->fi = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ctx->neigh_ctrl_conn) {
 | 
			
		||||
		if (ctx->neigh_ctrl_conn->write_queue.bfd.fd != -1) {
 | 
			
		||||
			osmo_wqueue_clear(&ctx->neigh_ctrl_conn->write_queue);
 | 
			
		||||
			osmo_fd_unregister(&ctx->neigh_ctrl_conn->write_queue.bfd);
 | 
			
		||||
			close(ctx->neigh_ctrl_conn->write_queue.bfd.fd);
 | 
			
		||||
			ctx->neigh_ctrl_conn->write_queue.bfd.fd = -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct nacc_fsm_ctx *nacc_fsm_alloc(struct GprsMs* ms)
 | 
			
		||||
{
 | 
			
		||||
	struct nacc_fsm_ctx *ctx = talloc_zero(ms, struct nacc_fsm_ctx);
 | 
			
		||||
	char buf[64];
 | 
			
		||||
 | 
			
		||||
	talloc_set_destructor(ctx, nacc_fsm_ctx_talloc_destructor);
 | 
			
		||||
 | 
			
		||||
	ctx->ms = ms;
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof(buf), "TLLI-0x%08x", ms_tlli(ms));
 | 
			
		||||
	ctx->fi = osmo_fsm_inst_alloc(&nacc_fsm, ctx, ctx, LOGL_INFO, buf);
 | 
			
		||||
	if (!ctx->fi)
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
 | 
			
		||||
	ctx->neigh_ctrl = ctrl_handle_alloc(ctx, ctx, NULL);
 | 
			
		||||
	ctx->neigh_ctrl->reply_cb = nacc_fsm_ctrl_reply_cb;
 | 
			
		||||
	ctx->neigh_ctrl_conn = osmo_ctrl_conn_alloc(ctx, ctx->neigh_ctrl);
 | 
			
		||||
	if (!ctx->neigh_ctrl_conn)
 | 
			
		||||
		goto free_ret;
 | 
			
		||||
	/* Older versions of osmo_ctrl_conn_alloc didn't properly initialize fd to -1,
 | 
			
		||||
	 * so make sure to do it here otherwise fd may be valid fd 0 and cause trouble */
 | 
			
		||||
	ctx->neigh_ctrl_conn->write_queue.bfd.fd = -1;
 | 
			
		||||
	llist_add(&ctx->neigh_ctrl_conn->list_entry, &ctx->neigh_ctrl->ccon_list);
 | 
			
		||||
 | 
			
		||||
	return ctx;
 | 
			
		||||
free_ret:
 | 
			
		||||
	talloc_free(ctx);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								src/nacc_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/nacc_fsm.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
/* nacc_fsm.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/fsm.h>
 | 
			
		||||
#include <osmocom/gsm/gsm23003.h>
 | 
			
		||||
 | 
			
		||||
#include <neigh_cache.h>
 | 
			
		||||
 | 
			
		||||
struct GprsMs;
 | 
			
		||||
struct gprs_rlcmac_tbf;
 | 
			
		||||
 | 
			
		||||
enum nacc_fsm_event {
 | 
			
		||||
	NACC_EV_RX_CELL_CHG_NOTIFICATION, /* data: Packet_Cell_Change_Notification_t* */
 | 
			
		||||
	NACC_EV_RX_RAC_CI, /* no data passed, RAC_CI became available in neigh_cache */
 | 
			
		||||
	NACC_EV_RX_SI, /* data: struct si_cache_entry* */
 | 
			
		||||
	NACC_EV_CREATE_RLCMAC_MSG, /* data: struct nacc_ev_create_rlcmac_msg_ctx* */
 | 
			
		||||
	NACC_EV_RX_CELL_CHG_CONTINUE_ACK,
 | 
			
		||||
	NACC_EV_TIMEOUT_CELL_CHG_CONTINUE, /* Poll Timeout */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum nacc_fsm_states {
 | 
			
		||||
	NACC_ST_INITIAL,
 | 
			
		||||
	NACC_ST_WAIT_RESOLVE_RAC_CI,
 | 
			
		||||
	NACC_ST_WAIT_REQUEST_SI,
 | 
			
		||||
	NACC_ST_TX_NEIGHBOUR_DATA,
 | 
			
		||||
	NACC_ST_TX_CELL_CHG_CONTINUE,
 | 
			
		||||
	NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK,
 | 
			
		||||
	NACC_ST_DONE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct nacc_fsm_ctx {
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct GprsMs* ms; /* back pointer */
 | 
			
		||||
	struct ctrl_handle *neigh_ctrl;
 | 
			
		||||
	struct ctrl_connection *neigh_ctrl_conn;
 | 
			
		||||
	struct neigh_cache_entry_key neigh_key; /* target cell info from MS */
 | 
			
		||||
	struct osmo_cell_global_id_ps cgi_ps; /* target cell info resolved from req_{arfcn+bsic} */
 | 
			
		||||
	struct si_cache_value si_info; /* SI info resolved from SGSN, to be sent to MS */
 | 
			
		||||
	size_t si_info_bytes_sent; /* How many bytes out of si_info->si_len were already sent to MS */
 | 
			
		||||
	size_t container_idx; /* Next container_idx to assign when sending Packet Neighbor Data message */
 | 
			
		||||
	uint32_t continue_poll_fn; /* Scheduled poll FN to CTRL ACK the Pkt Cell Chg Continue */
 | 
			
		||||
	uint8_t continue_poll_ts; /* Scheduled poll TS to CTRL ACK the Pkt Cell Chg Continue */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* passed as data in NACC_EV_CREATE_RLCMAC_MSG */
 | 
			
		||||
struct nacc_ev_create_rlcmac_msg_ctx {
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf; /* target tbf to create messages for */
 | 
			
		||||
	uint32_t fn; /* FN where the created DL ctrl block is to be sent */
 | 
			
		||||
	uint8_t ts; /* TS where the created DL ctrl block is to be sent */
 | 
			
		||||
	struct msgb *msg; /* to be filled by FSM during event processing */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct nacc_fsm_ctx *nacc_fsm_alloc(struct GprsMs* ms);
 | 
			
		||||
							
								
								
									
										289
									
								
								src/neigh_cache.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								src/neigh_cache.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,289 @@
 | 
			
		||||
/* si_cache.c
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <talloc.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include <neigh_cache.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
 | 
			
		||||
static void neigh_cache_schedule_cleanup(struct neigh_cache *cache);
 | 
			
		||||
static void neigh_cache_cleanup_cb(void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct timespec now, threshold;
 | 
			
		||||
	struct neigh_cache *cache = (struct neigh_cache *)data;
 | 
			
		||||
	struct neigh_cache_entry *it, *tmp;
 | 
			
		||||
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &now);
 | 
			
		||||
 | 
			
		||||
	/* Instead of adding keep_time_intval to each, substract it from now once */
 | 
			
		||||
	timespecsub(&now, &cache->keep_time_intval, &threshold);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry_safe(it, tmp, &cache->list, list) {
 | 
			
		||||
		if (timespeccmp(&threshold, &it->update_ts, <))
 | 
			
		||||
			break;
 | 
			
		||||
		LOGP(DNACC, LOGL_DEBUG,
 | 
			
		||||
		     "neigh_cache: Removing entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
 | 
			
		||||
		     NEIGH_CACHE_ENTRY_KEY_ARGS(&it->key), osmo_cgi_ps_name(&it->value));
 | 
			
		||||
		llist_del(&it->list);
 | 
			
		||||
		talloc_free(it);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	neigh_cache_schedule_cleanup(cache);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void neigh_cache_schedule_cleanup(struct neigh_cache *cache)
 | 
			
		||||
{
 | 
			
		||||
	struct neigh_cache_entry *it;
 | 
			
		||||
	struct timespec now, threshold, result;
 | 
			
		||||
 | 
			
		||||
	/* First item is the one with oldest update_ts */
 | 
			
		||||
	it = llist_first_entry_or_null(&cache->list, struct neigh_cache_entry, list);
 | 
			
		||||
	if (!it)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &now);
 | 
			
		||||
 | 
			
		||||
	timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
 | 
			
		||||
 | 
			
		||||
	if (timespeccmp(&now, &threshold, >=)) {
 | 
			
		||||
		/* Too late, let's flush asynchonously so newly added isn't
 | 
			
		||||
		 * immediatelly freed before return. */
 | 
			
		||||
		result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
 | 
			
		||||
	} else {
 | 
			
		||||
		timespecsub(&threshold, &now, &result);
 | 
			
		||||
	}
 | 
			
		||||
	osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec)
 | 
			
		||||
{
 | 
			
		||||
	struct neigh_cache *cache = talloc_zero(ctx, struct neigh_cache);
 | 
			
		||||
	OSMO_ASSERT(cache);
 | 
			
		||||
	INIT_LLIST_HEAD(&cache->list);
 | 
			
		||||
	osmo_timer_setup(&cache->cleanup_timer, neigh_cache_cleanup_cb, cache);
 | 
			
		||||
	cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
 | 
			
		||||
	return cache;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec)
 | 
			
		||||
{
 | 
			
		||||
	cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
 | 
			
		||||
	neigh_cache_schedule_cleanup(cache);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
 | 
			
		||||
					  const struct neigh_cache_entry_key *key,
 | 
			
		||||
					  const struct osmo_cell_global_id_ps *value)
 | 
			
		||||
{
 | 
			
		||||
	struct neigh_cache_entry *it;
 | 
			
		||||
 | 
			
		||||
	/* First check if it already exists. If so, simply update timer+value */
 | 
			
		||||
	it = neigh_cache_lookup_entry(cache, key);
 | 
			
		||||
	if (!it) {
 | 
			
		||||
		LOGP(DNACC, LOGL_DEBUG,
 | 
			
		||||
		     "neigh_cache: Inserting new entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
 | 
			
		||||
		     NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(value));
 | 
			
		||||
		it = talloc_zero(cache, struct neigh_cache_entry);
 | 
			
		||||
		OSMO_ASSERT(it);
 | 
			
		||||
		memcpy(&it->key, key, sizeof(it->key));
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGP(DNACC, LOGL_DEBUG,
 | 
			
		||||
		     "neigh_cache: Updating entry " NEIGH_CACHE_ENTRY_KEY_FMT " => (%s -> %s)\n",
 | 
			
		||||
		     NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(&it->value), osmo_cgi_ps_name2(value));
 | 
			
		||||
		/* remove item, we'll add it to the end to have them sorted by last update */
 | 
			
		||||
		llist_del(&it->list);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(&it->value, value, sizeof(it->value));
 | 
			
		||||
	OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
 | 
			
		||||
	llist_add_tail(&it->list, &cache->list);
 | 
			
		||||
	neigh_cache_schedule_cleanup(cache);
 | 
			
		||||
	return it;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
 | 
			
		||||
						   const struct neigh_cache_entry_key *key)
 | 
			
		||||
{
 | 
			
		||||
	struct neigh_cache_entry *tmp;
 | 
			
		||||
	llist_for_each_entry(tmp, &cache->list, list) {
 | 
			
		||||
		if (neigh_cache_entry_key_eq(&tmp->key, key))
 | 
			
		||||
			return tmp;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
 | 
			
		||||
							      const struct neigh_cache_entry_key *key)
 | 
			
		||||
{
 | 
			
		||||
	struct neigh_cache_entry *it = neigh_cache_lookup_entry(cache, key);
 | 
			
		||||
	if (it)
 | 
			
		||||
		return &it->value;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void neigh_cache_free(struct neigh_cache *cache)
 | 
			
		||||
{
 | 
			
		||||
	struct neigh_cache_entry *it, *tmp;
 | 
			
		||||
	if (!cache)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry_safe(it, tmp, &cache->list, list) {
 | 
			
		||||
		llist_del(&it->list);
 | 
			
		||||
		talloc_free(it);
 | 
			
		||||
	}
 | 
			
		||||
	osmo_timer_del(&cache->cleanup_timer);
 | 
			
		||||
	talloc_free(cache);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
////////////////////
 | 
			
		||||
// SI CACHE
 | 
			
		||||
///////////////////
 | 
			
		||||
 | 
			
		||||
static void si_cache_schedule_cleanup(struct si_cache *cache);
 | 
			
		||||
static void si_cache_cleanup_cb(void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct timespec now, threshold;
 | 
			
		||||
	struct si_cache *cache = (struct si_cache *)data;
 | 
			
		||||
	struct si_cache_entry *it, *tmp;
 | 
			
		||||
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &now);
 | 
			
		||||
 | 
			
		||||
	/* Instead of adding keep_time_intval to each, substract it from now once */
 | 
			
		||||
	timespecsub(&now, &cache->keep_time_intval, &threshold);
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry_safe(it, tmp, &cache->list, list) {
 | 
			
		||||
		if (timespeccmp(&threshold, &it->update_ts, <))
 | 
			
		||||
			break;
 | 
			
		||||
		LOGP(DNACC, LOGL_DEBUG, "si_cache: Removing entry %s\n",
 | 
			
		||||
		     osmo_cgi_ps_name(&it->key));
 | 
			
		||||
		llist_del(&it->list);
 | 
			
		||||
		talloc_free(it);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	si_cache_schedule_cleanup(cache);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void si_cache_schedule_cleanup(struct si_cache *cache)
 | 
			
		||||
{
 | 
			
		||||
	struct si_cache_entry *it;
 | 
			
		||||
	struct timespec now, threshold, result;
 | 
			
		||||
 | 
			
		||||
	/* First item is the one with oldest update_ts */
 | 
			
		||||
	it = llist_first_entry_or_null(&cache->list, struct si_cache_entry, list);
 | 
			
		||||
	if (!it)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	osmo_clock_gettime(CLOCK_MONOTONIC, &now);
 | 
			
		||||
 | 
			
		||||
	timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
 | 
			
		||||
 | 
			
		||||
	if (timespeccmp(&now, &threshold, >=)) {
 | 
			
		||||
		/* Too late, let's flush asynchonously so newly added isn't
 | 
			
		||||
		 * immediatelly freed before return. */
 | 
			
		||||
		result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
 | 
			
		||||
	} else {
 | 
			
		||||
		timespecsub(&threshold, &now, &result);
 | 
			
		||||
	}
 | 
			
		||||
	osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec)
 | 
			
		||||
{
 | 
			
		||||
	struct si_cache *cache = talloc_zero(ctx, struct si_cache);
 | 
			
		||||
	OSMO_ASSERT(cache);
 | 
			
		||||
	INIT_LLIST_HEAD(&cache->list);
 | 
			
		||||
	osmo_timer_setup(&cache->cleanup_timer, si_cache_cleanup_cb, cache);
 | 
			
		||||
	cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
 | 
			
		||||
	return cache;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec)
 | 
			
		||||
{
 | 
			
		||||
	cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
 | 
			
		||||
	si_cache_schedule_cleanup(cache);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct si_cache_entry *si_cache_add(struct si_cache *cache,
 | 
			
		||||
				    const struct osmo_cell_global_id_ps *key,
 | 
			
		||||
				    const struct si_cache_value *value)
 | 
			
		||||
{
 | 
			
		||||
	struct si_cache_entry *it;
 | 
			
		||||
 | 
			
		||||
	/* First check if it already exists. If so, simply update timer+value */
 | 
			
		||||
	it = si_cache_lookup_entry(cache, key);
 | 
			
		||||
	if (!it) {
 | 
			
		||||
		LOGP(DNACC, LOGL_DEBUG, "si_cache: Inserting new entry %s\n",
 | 
			
		||||
		     osmo_cgi_ps_name(key));
 | 
			
		||||
		it = talloc_zero(cache, struct si_cache_entry);
 | 
			
		||||
		OSMO_ASSERT(it);
 | 
			
		||||
		memcpy(&it->key, key, sizeof(it->key));
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGP(DNACC, LOGL_DEBUG, "si_cache: Updating entry %s\n",
 | 
			
		||||
		     osmo_cgi_ps_name(&it->key));
 | 
			
		||||
		/* remove item, we'll add it to the end to have them sorted by last update */
 | 
			
		||||
		llist_del(&it->list);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(&it->value, value, sizeof(it->value));
 | 
			
		||||
	OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
 | 
			
		||||
	llist_add_tail(&it->list, &cache->list);
 | 
			
		||||
	si_cache_schedule_cleanup(cache);
 | 
			
		||||
	return it;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
 | 
			
		||||
					     const struct osmo_cell_global_id_ps *key)
 | 
			
		||||
{
 | 
			
		||||
	struct si_cache_entry *tmp;
 | 
			
		||||
	llist_for_each_entry(tmp, &cache->list, list) {
 | 
			
		||||
		if (osmo_cgi_ps_cmp(&tmp->key, key) == 0)
 | 
			
		||||
			return tmp;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
 | 
			
		||||
						   const struct osmo_cell_global_id_ps *key)
 | 
			
		||||
{
 | 
			
		||||
	struct si_cache_entry *it = si_cache_lookup_entry(cache, key);
 | 
			
		||||
	if (it)
 | 
			
		||||
		return &it->value;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void si_cache_free(struct si_cache *cache)
 | 
			
		||||
{
 | 
			
		||||
	struct si_cache_entry *it, *tmp;
 | 
			
		||||
	if (!cache)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	llist_for_each_entry_safe(it, tmp, &cache->list, list) {
 | 
			
		||||
		llist_del(&it->list);
 | 
			
		||||
		talloc_free(it);
 | 
			
		||||
	}
 | 
			
		||||
	osmo_timer_del(&cache->cleanup_timer);
 | 
			
		||||
	talloc_free(cache);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										112
									
								
								src/neigh_cache.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/neigh_cache.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
/* neigh_cache.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/gsm23003.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp_rim.h>
 | 
			
		||||
 | 
			
		||||
////////////////////
 | 
			
		||||
// NEIGH CACHE
 | 
			
		||||
///////////////////
 | 
			
		||||
 | 
			
		||||
/* ARFC+BSIC -> CGI PS cache */
 | 
			
		||||
struct neigh_cache {
 | 
			
		||||
	struct llist_head list; /* list of neigh_cache_entry items */
 | 
			
		||||
	struct osmo_timer_list cleanup_timer; /* Timer removing too-old entries */
 | 
			
		||||
	struct timespec keep_time_intval;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct neigh_cache_entry_key {
 | 
			
		||||
	uint16_t local_lac;
 | 
			
		||||
	uint16_t local_ci;
 | 
			
		||||
	uint16_t tgt_arfcn;
 | 
			
		||||
	uint8_t tgt_bsic;
 | 
			
		||||
};
 | 
			
		||||
#define NEIGH_CACHE_ENTRY_KEY_FMT "%" PRIu16 "-%" PRIu16 "-%" PRIu16 "-%" PRIu8
 | 
			
		||||
#define NEIGH_CACHE_ENTRY_KEY_ARGS(key) (key)->local_lac, (key)->local_ci, (key)->tgt_arfcn, (key)->tgt_bsic
 | 
			
		||||
 | 
			
		||||
static inline bool neigh_cache_entry_key_eq(const struct neigh_cache_entry_key *a,
 | 
			
		||||
					    const struct neigh_cache_entry_key *b)
 | 
			
		||||
{
 | 
			
		||||
	return a->local_lac == b->local_lac &&
 | 
			
		||||
	       a->local_ci == b->local_ci &&
 | 
			
		||||
	       a->tgt_arfcn == b->tgt_arfcn &&
 | 
			
		||||
	       a->tgt_bsic == b->tgt_bsic;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct neigh_cache_entry {
 | 
			
		||||
	struct llist_head list; /* to be included in neigh_cache->list */
 | 
			
		||||
	struct timespec update_ts;
 | 
			
		||||
	struct neigh_cache_entry_key key;
 | 
			
		||||
	struct osmo_cell_global_id_ps value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec);
 | 
			
		||||
void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec);
 | 
			
		||||
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
 | 
			
		||||
					  const struct neigh_cache_entry_key *key,
 | 
			
		||||
					  const struct osmo_cell_global_id_ps *value);
 | 
			
		||||
struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
 | 
			
		||||
						   const struct neigh_cache_entry_key *key);
 | 
			
		||||
const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
 | 
			
		||||
							      const struct neigh_cache_entry_key *key);
 | 
			
		||||
void neigh_cache_free(struct neigh_cache *cache);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
////////////////////
 | 
			
		||||
// SI CACHE
 | 
			
		||||
///////////////////
 | 
			
		||||
 | 
			
		||||
/* CGI-PS-> SI cache */
 | 
			
		||||
struct si_cache {
 | 
			
		||||
	struct llist_head list; /* list of si_cache_entry items */
 | 
			
		||||
	struct osmo_timer_list cleanup_timer; /* Timer removing too-old entries */
 | 
			
		||||
	struct timespec keep_time_intval;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct si_cache_value {
 | 
			
		||||
	uint8_t si_buf[BSSGP_RIM_PSI_LEN * 127]; /* 3GPP TS 48.018 11.3.63.2.1 */
 | 
			
		||||
	size_t si_len;
 | 
			
		||||
	bool type_psi;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct si_cache_entry {
 | 
			
		||||
	struct llist_head list; /* to be included in si_cache->list */
 | 
			
		||||
	struct timespec update_ts;
 | 
			
		||||
	struct osmo_cell_global_id_ps key;
 | 
			
		||||
	struct si_cache_value value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec);
 | 
			
		||||
void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec);
 | 
			
		||||
struct si_cache_entry *si_cache_add(struct si_cache *cache,
 | 
			
		||||
				    const struct osmo_cell_global_id_ps *key,
 | 
			
		||||
				    const struct si_cache_value *value);
 | 
			
		||||
struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
 | 
			
		||||
					     const struct osmo_cell_global_id_ps *key);
 | 
			
		||||
const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
 | 
			
		||||
						   const struct osmo_cell_global_id_ps *key);
 | 
			
		||||
void si_cache_free(struct si_cache *cache);
 | 
			
		||||
@@ -1,185 +0,0 @@
 | 
			
		||||
/* openbts_sock.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gprs_bssgp_pcu.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <bitvector.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
#include <pcuif_proto.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
struct femtol1_hdl {
 | 
			
		||||
	struct gsm_time gsm_time;
 | 
			
		||||
	uint32_t hLayer1;			/* handle to the L1 instance in the DSP */
 | 
			
		||||
	uint32_t dsp_trace_f;
 | 
			
		||||
	uint16_t clk_cal;
 | 
			
		||||
	struct llist_head wlc_list;
 | 
			
		||||
 | 
			
		||||
	void *priv;			/* user reference */
 | 
			
		||||
 | 
			
		||||
	struct osmo_timer_list alive_timer;
 | 
			
		||||
	unsigned int alive_prim_cnt;
 | 
			
		||||
 | 
			
		||||
	struct osmo_fd read_ofd;	/* osmo file descriptors */
 | 
			
		||||
	struct osmo_wqueue write_q;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		uint16_t arfcn;
 | 
			
		||||
		uint8_t tn;
 | 
			
		||||
		uint8_t tsc;
 | 
			
		||||
		uint16_t ta;
 | 
			
		||||
	} channel_info;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct l1fwd_hdl {
 | 
			
		||||
	struct sockaddr_storage remote_sa;
 | 
			
		||||
	socklen_t remote_sa_len;
 | 
			
		||||
 | 
			
		||||
	struct osmo_wqueue udp_wq;
 | 
			
		||||
 | 
			
		||||
	struct femtol1_hdl *fl1h;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
 | 
			
		||||
 | 
			
		||||
// TODO: We should move this parameters to config file.
 | 
			
		||||
#define PCU_L1_IF_PORT 5944
 | 
			
		||||
 | 
			
		||||
/* OpenBTS socket functions */
 | 
			
		||||
 | 
			
		||||
int pcu_sock_send(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* data has arrived on the udp socket */
 | 
			
		||||
static int udp_read_cb(struct osmo_fd *ofd)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
 | 
			
		||||
	struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
 | 
			
		||||
	struct femtol1_hdl *fl1h = l1fh->fl1h;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	msg->l1h = msg->data;
 | 
			
		||||
 | 
			
		||||
	l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
 | 
			
		||||
	rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
 | 
			
		||||
			(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		perror("read from udp");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return rc;
 | 
			
		||||
	} else if (rc == 0) {
 | 
			
		||||
		perror("len=0 read from udp");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
	msgb_put(msg, rc);
 | 
			
		||||
 | 
			
		||||
	struct gsm_pcu_if *pcu_prim = (gsm_pcu_if *)(msg->l1h);
 | 
			
		||||
	rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* callback when we can write to the UDP socket */
 | 
			
		||||
static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
 | 
			
		||||
 | 
			
		||||
	//LOGP(DPCU, LOGL_ERROR, "UDP: Writing %u bytes for MQ_L1_WRITE queue\n", msgb_length(msg));
 | 
			
		||||
 | 
			
		||||
	rc = sendto(ofd->fd, msgb_data(msg), msgb_length(msg), 0,
 | 
			
		||||
			(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DPCU, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
 | 
			
		||||
			strerror(errno));
 | 
			
		||||
		return rc;
 | 
			
		||||
	} else if (rc < (int)msgb_length(msg)) {
 | 
			
		||||
		LOGP(DPCU, LOGL_ERROR, "short write to L1 msg_queue: "
 | 
			
		||||
			"%u < %u\n", rc, msgb_length(msg));
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int pcu_l1if_open()
 | 
			
		||||
{
 | 
			
		||||
	struct femtol1_hdl *fl1h;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* allocate new femtol1_handle */
 | 
			
		||||
	fl1h = talloc_zero(tall_pcu_ctx, struct femtol1_hdl);
 | 
			
		||||
	INIT_LLIST_HEAD(&fl1h->wlc_list);
 | 
			
		||||
 | 
			
		||||
	l1fh->fl1h = fl1h;
 | 
			
		||||
	fl1h->priv = l1fh;
 | 
			
		||||
 | 
			
		||||
	struct osmo_wqueue * queue = &((l1fh->fl1h)->write_q);
 | 
			
		||||
	osmo_wqueue_init(queue, 10);
 | 
			
		||||
	queue->bfd.when |= BSC_FD_READ;
 | 
			
		||||
	queue->bfd.data = l1fh;
 | 
			
		||||
	queue->bfd.priv_nr = 0;
 | 
			
		||||
 | 
			
		||||
	/* Open UDP */
 | 
			
		||||
	struct osmo_wqueue *wq = &l1fh->udp_wq;
 | 
			
		||||
 | 
			
		||||
	osmo_wqueue_init(wq, 10);
 | 
			
		||||
	wq->write_cb = udp_write_cb;
 | 
			
		||||
	wq->read_cb = udp_read_cb;
 | 
			
		||||
	wq->bfd.when |= BSC_FD_READ;
 | 
			
		||||
	wq->bfd.data = l1fh;
 | 
			
		||||
	wq->bfd.priv_nr = 0;
 | 
			
		||||
	rc = osmo_sock_init_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM,
 | 
			
		||||
				IPPROTO_UDP, NULL, PCU_L1_IF_PORT,
 | 
			
		||||
				OSMO_SOCK_F_BIND);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		perror("sock_init");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pcu_l1if_close(void)
 | 
			
		||||
{
 | 
			
		||||
	gprs_bssgp_destroy();
 | 
			
		||||
 | 
			
		||||
	/* FIXME: cleanup l1if */
 | 
			
		||||
	talloc_free(l1fh->fl1h);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										206
									
								
								src/osmo-bts-litecell15/lc15_l1_hw.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								src/osmo-bts-litecell15/lc15_l1_hw.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,206 @@
 | 
			
		||||
/* Interface handler for Nuran Wireless Litecell 1.5 L1 (real hardware) */
 | 
			
		||||
 | 
			
		||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
 | 
			
		||||
 * based on:
 | 
			
		||||
 *     femto_l1_hw.c
 | 
			
		||||
 *     (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 *
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
 | 
			
		||||
#include <nrw/litecell15/litecell15.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1prim.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1const.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1types.h>
 | 
			
		||||
 | 
			
		||||
#include "gprs_debug.h"
 | 
			
		||||
#include "lc15bts.h"
 | 
			
		||||
#include "lc15_l1_if.h"
 | 
			
		||||
 | 
			
		||||
#define DEV_SYS_DSP2ARM_NAME    "/dev/msgq/litecell15_dsp2arm_trx"
 | 
			
		||||
#define DEV_SYS_ARM2DSP_NAME    "/dev/msgq/litecell15_arm2dsp_trx"
 | 
			
		||||
#define DEV_L1_DSP2ARM_NAME     "/dev/msgq/gsml1_sig_dsp2arm_trx"
 | 
			
		||||
#define DEV_L1_ARM2DSP_NAME     "/dev/msgq/gsml1_sig_arm2dsp_trx"
 | 
			
		||||
 | 
			
		||||
#define DEV_TCH_DSP2ARM_NAME    "/dev/msgq/gsml1_tch_dsp2arm_trx"
 | 
			
		||||
#define DEV_TCH_ARM2DSP_NAME    "/dev/msgq/gsml1_tch_arm2dsp_trx"
 | 
			
		||||
#define DEV_PDTCH_DSP2ARM_NAME  "/dev/msgq/gsml1_pdtch_dsp2arm_trx"
 | 
			
		||||
#define DEV_PDTCH_ARM2DSP_NAME  "/dev/msgq/gsml1_pdtch_arm2dsp_trx"
 | 
			
		||||
 | 
			
		||||
static const char *rd_devnames[] = {
 | 
			
		||||
        [MQ_SYS_READ]   = DEV_SYS_DSP2ARM_NAME,
 | 
			
		||||
        [MQ_L1_READ]    = DEV_L1_DSP2ARM_NAME,
 | 
			
		||||
        [MQ_TCH_READ]   = DEV_TCH_DSP2ARM_NAME,
 | 
			
		||||
        [MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *wr_devnames[] = {
 | 
			
		||||
        [MQ_SYS_WRITE]  = DEV_SYS_ARM2DSP_NAME,
 | 
			
		||||
        [MQ_L1_WRITE]   = DEV_L1_ARM2DSP_NAME,
 | 
			
		||||
        [MQ_TCH_WRITE]  = DEV_TCH_ARM2DSP_NAME,
 | 
			
		||||
        [MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* callback when there's something to read from the l1 msg_queue */
 | 
			
		||||
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	//struct msgb *msg = l1p_msgb_alloc();
 | 
			
		||||
	struct msgb *msg = msgb_alloc_headroom(sizeof(Litecell15_Prim_t) + 128,
 | 
			
		||||
		128, "1l_fd");
 | 
			
		||||
	struct lc15l1_hdl *fl1h = ofd->data;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	msg->l1h = msg->data;
 | 
			
		||||
	rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		if (rc != -1)
 | 
			
		||||
			LOGP(DL1IF, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
 | 
			
		||||
				strerror(errno));
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
	msgb_put(msg, rc);
 | 
			
		||||
 | 
			
		||||
	switch (ofd->priv_nr) {
 | 
			
		||||
	case MQ_SYS_WRITE:
 | 
			
		||||
		if (rc != sizeof(Litecell15_Prim_t))
 | 
			
		||||
			LOGP(DL1IF, LOGL_NOTICE, "%u != "
 | 
			
		||||
			     "sizeof(Litecell15_Prim_t)\n", rc);
 | 
			
		||||
		return l1if_handle_sysprim(fl1h, msg);
 | 
			
		||||
	case MQ_L1_WRITE:
 | 
			
		||||
	case MQ_TCH_WRITE:
 | 
			
		||||
	case MQ_PDTCH_WRITE:
 | 
			
		||||
		if (rc != sizeof(GsmL1_Prim_t))
 | 
			
		||||
			LOGP(DL1IF, LOGL_NOTICE, "%u != "
 | 
			
		||||
			     "sizeof(GsmL1_Prim_t)\n", rc);
 | 
			
		||||
		return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
 | 
			
		||||
	default:
 | 
			
		||||
		/* The compiler can't know that priv_nr is an enum. Assist. */
 | 
			
		||||
		LOGP(DL1IF, LOGL_FATAL, "writing on a wrong queue: %d\n",
 | 
			
		||||
			ofd->priv_nr);
 | 
			
		||||
		exit(0);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* callback when we can write to one of the l1 msg_queue devices */
 | 
			
		||||
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = write(ofd->fd, msg->l1h, msgb_l1len(msg));
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
 | 
			
		||||
			strerror(errno));
 | 
			
		||||
		return rc;
 | 
			
		||||
	} else if (rc < msg->len) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "short write to L1 msg_queue: "
 | 
			
		||||
			"%u < %u\n", rc, msg->len);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int l1if_transport_open(int q, struct lc15l1_hdl *hdl)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
        char buf[PATH_MAX];
 | 
			
		||||
 | 
			
		||||
	/* Step 1: Open all msg_queue file descriptors */
 | 
			
		||||
	struct osmo_fd *read_ofd = &hdl->read_ofd[q];
 | 
			
		||||
	struct osmo_wqueue *wq = &hdl->write_q[q];
 | 
			
		||||
	struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
 | 
			
		||||
 | 
			
		||||
        snprintf(buf, sizeof(buf)-1, "%s%d", rd_devnames[q], hdl->hw_info.trx_nr);
 | 
			
		||||
        buf[sizeof(buf)-1] = '\0';
 | 
			
		||||
 | 
			
		||||
	rc = open(buf, O_RDONLY);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
 | 
			
		||||
			buf, strerror(errno));
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
	osmo_fd_setup(read_ofd, rc, OSMO_FD_READ, l1if_fd_cb, hdl, q);
 | 
			
		||||
	rc = osmo_fd_register(read_ofd);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		close(read_ofd->fd);
 | 
			
		||||
		read_ofd->fd = -1;
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
        snprintf(buf, sizeof(buf)-1, "%s%d", wr_devnames[q], hdl->hw_info.trx_nr);
 | 
			
		||||
        buf[sizeof(buf)-1] = '\0';
 | 
			
		||||
 | 
			
		||||
	rc = open(buf, O_WRONLY);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
 | 
			
		||||
			buf, strerror(errno));
 | 
			
		||||
		goto out_read;
 | 
			
		||||
	}
 | 
			
		||||
	osmo_wqueue_init(wq, 10);
 | 
			
		||||
	wq->write_cb = l1fd_write_cb;
 | 
			
		||||
	osmo_fd_setup(write_ofd, rc, OSMO_FD_WRITE, osmo_wqueue_bfd_cb, hdl, q);
 | 
			
		||||
	rc = osmo_fd_register(write_ofd);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		close(write_ofd->fd);
 | 
			
		||||
		write_ofd->fd = -1;
 | 
			
		||||
		goto out_read;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
out_read:
 | 
			
		||||
	close(hdl->read_ofd[q].fd);
 | 
			
		||||
	osmo_fd_unregister(&hdl->read_ofd[q]);
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int l1if_transport_close(int q, struct lc15l1_hdl *hdl)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_fd *read_ofd = &hdl->read_ofd[q];
 | 
			
		||||
	struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
 | 
			
		||||
 | 
			
		||||
	osmo_fd_unregister(read_ofd);
 | 
			
		||||
	close(read_ofd->fd);
 | 
			
		||||
	read_ofd->fd = -1;
 | 
			
		||||
 | 
			
		||||
	osmo_fd_unregister(write_ofd);
 | 
			
		||||
	close(write_ofd->fd);
 | 
			
		||||
	write_ofd->fd = -1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user