mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-pcu.git
				synced 2025-11-04 06:03:30 +00:00 
			
		
		
		
	Compare commits
	
		
			2300 Commits
		
	
	
		
			jolly/outd
			...
			1.3.1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					9658f02214 | ||
| 
						 | 
					09dc6b21e2 | ||
| 
						 | 
					3ef173b980 | ||
| 
						 | 
					35a7824658 | ||
| 
						 | 
					b52e843136 | ||
| 
						 | 
					06b50c57a1 | ||
| 
						 | 
					0e47642e96 | ||
| 
						 | 
					3a6e19ce2b | ||
| 
						 | 
					245d340006 | ||
| 
						 | 
					60664a4df1 | ||
| 
						 | 
					02ea46eff9 | ||
| 
						 | 
					fa96a767d1 | ||
| 
						 | 
					e98b315d12 | ||
| 
						 | 
					90e33bffd6 | ||
| 
						 | 
					6691f51990 | ||
| 
						 | 
					c1278288b5 | ||
| 
						 | 
					ff33597e4b | ||
| 
						 | 
					61f64c39a0 | ||
| 
						 | 
					a1e54e7e27 | ||
| 
						 | 
					0cde91de1f | ||
| 
						 | 
					b8f79e85cb | ||
| 
						 | 
					eca828b8f9 | ||
| 
						 | 
					d67542588c | ||
| 
						 | 
					a4e755e563 | ||
| 
						 | 
					68e3d787f0 | ||
| 
						 | 
					c46b6f29f9 | ||
| 
						 | 
					e4e53adc9c | ||
| 
						 | 
					091fba7cd9 | ||
| 
						 | 
					37c2f84d53 | ||
| 
						 | 
					11627ffaae | ||
| 
						 | 
					747761f924 | ||
| 
						 | 
					6a12271b2e | ||
| 
						 | 
					5f3177eb41 | ||
| 
						 | 
					061fbca2a9 | ||
| 
						 | 
					28ccf7a284 | ||
| 
						 | 
					40a297f3b0 | ||
| 
						 | 
					8c52bef6c6 | ||
| 
						 | 
					556dc18109 | ||
| 
						 | 
					95ec50c9e4 | ||
| 
						 | 
					ed94d4cbfa | ||
| 
						 | 
					ac1495e7be | ||
| 
						 | 
					e8ac8da62a | ||
| 
						 | 
					dca3c906b0 | ||
| 
						 | 
					46db3c1db1 | ||
| 
						 | 
					e5ba83d3f7 | ||
| 
						 | 
					93b16ae02b | ||
| 
						 | 
					9c46591682 | ||
| 
						 | 
					83ff58d978 | ||
| 
						 | 
					6d7167658e | ||
| 
						 | 
					bb34ffaaf7 | ||
| 
						 | 
					40b8dd55f1 | ||
| 
						 | 
					d4075bd1b8 | ||
| 
						 | 
					c41fb6eb7c | ||
| 
						 | 
					84011ef86a | ||
| 
						 | 
					e656c210a1 | ||
| 
						 | 
					7a4428c3ad | ||
| 
						 | 
					906e7f4357 | ||
| 
						 | 
					d8ad8ffbe5 | ||
| 
						 | 
					365728d579 | ||
| 
						 | 
					89fbf87abf | ||
| 
						 | 
					d8c38777a1 | ||
| 
						 | 
					74f6dd7a00 | ||
| 
						 | 
					637bcdf80e | ||
| 
						 | 
					49b144847f | ||
| 
						 | 
					08b6a00afd | ||
| 
						 | 
					2549c1e696 | ||
| 
						 | 
					ba8918aae8 | ||
| 
						 | 
					ab571af3f2 | ||
| 
						 | 
					d0fd37c449 | ||
| 
						 | 
					53eaf74ffa | ||
| 
						 | 
					fe8f4a4c87 | ||
| 
						 | 
					bd461e62b5 | ||
| 
						 | 
					636d7fdb72 | ||
| 
						 | 
					f09056de46 | ||
| 
						 | 
					33cb3d6175 | ||
| 
						 | 
					96b7a3ec72 | ||
| 
						 | 
					a9b844d346 | ||
| 
						 | 
					2fc67e252c | ||
| 
						 | 
					98e5910ed8 | ||
| 
						 | 
					5fd0022b62 | ||
| 
						 | 
					3d462354e5 | ||
| 
						 | 
					8984afa5d0 | ||
| 
						 | 
					6ee8d136d3 | ||
| 
						 | 
					5e024342c4 | ||
| 
						 | 
					e1a8915936 | ||
| 
						 | 
					7a491b9d5f | ||
| 
						 | 
					5faedf3432 | ||
| 
						 | 
					96a3e7f48c | ||
| 
						 | 
					c564ce8965 | ||
| 
						 | 
					4d3639144d | ||
| 
						 | 
					d6c555ebd6 | ||
| 
						 | 
					a42b174bec | ||
| 
						 | 
					a6a972e11e | ||
| 
						 | 
					51c903e1bb | ||
| 
						 | 
					4673eb0b76 | ||
| 
						 | 
					f086fede93 | ||
| 
						 | 
					b53230acec | ||
| 
						 | 
					c5104b7f30 | ||
| 
						 | 
					4fdf79e0b3 | ||
| 
						 | 
					fe4d2f7dca | ||
| 
						 | 
					8456a3642b | ||
| 
						 | 
					eae9147424 | ||
| 
						 | 
					84b0ebc902 | ||
| 
						 | 
					759724ca74 | ||
| 
						 | 
					e0e4251a5b | ||
| 
						 | 
					e1c84579cc | ||
| 
						 | 
					f9783c58f7 | ||
| 
						 | 
					ac4d4a6d41 | ||
| 
						 | 
					df6684fe50 | ||
| 
						 | 
					6efa381248 | ||
| 
						 | 
					3ea467d104 | ||
| 
						 | 
					403e048ac0 | ||
| 
						 | 
					9da0686371 | ||
| 
						 | 
					fe7aee9302 | ||
| 
						 | 
					eb0a0527e0 | ||
| 
						 | 
					cde18c5632 | ||
| 
						 | 
					bfc9756c2b | ||
| 
						 | 
					6d8315f724 | ||
| 
						 | 
					f80cc5b9c8 | ||
| 
						 | 
					14379ef901 | ||
| 
						 | 
					cb947e0a0f | ||
| 
						 | 
					1083c01488 | ||
| 
						 | 
					52f882e8c4 | ||
| 
						 | 
					1bdb132b43 | ||
| 
						 | 
					1a5eb18e26 | ||
| 
						 | 
					2342d605d9 | ||
| 
						 | 
					f963a3d46a | ||
| 
						 | 
					b3b52c106b | ||
| 
						 | 
					e4666a3fd5 | ||
| 
						 | 
					07e06d38fb | ||
| 
						 | 
					efae56dc52 | ||
| 
						 | 
					41e38475ed | ||
| 
						 | 
					4bac398923 | ||
| 
						 | 
					b5a8a67dfe | ||
| 
						 | 
					93d7ece833 | ||
| 
						 | 
					db5ac8ddd5 | ||
| 
						 | 
					cc95424df9 | ||
| 
						 | 
					02d4b2a724 | ||
| 
						 | 
					8bb176fdcb | ||
| 
						 | 
					2353ed403f | ||
| 
						 | 
					c82c948e99 | ||
| 
						 | 
					6f41762717 | ||
| 
						 | 
					916e9d3454 | ||
| 
						 | 
					72ed33303a | ||
| 
						 | 
					af8d49a99d | ||
| 
						 | 
					0cb3b2aa16 | ||
| 
						 | 
					7f5393ca6e | ||
| 
						 | 
					83ee452ddb | ||
| 
						 | 
					84ece23c09 | ||
| 
						 | 
					80828f0514 | ||
| 
						 | 
					eaf5da306c | ||
| 
						 | 
					7c3fd98c90 | ||
| 
						 | 
					d8cea3c618 | ||
| 
						 | 
					0ec7724d10 | ||
| 
						 | 
					7932bc8d32 | ||
| 
						 | 
					40db4c330e | ||
| 
						 | 
					3b66bbf813 | ||
| 
						 | 
					08523c2286 | ||
| 
						 | 
					8b571ee2e7 | ||
| 
						 | 
					76ef66698c | ||
| 
						 | 
					9e732ae4b1 | ||
| 
						 | 
					ea6d534369 | ||
| 
						 | 
					e0a8488773 | ||
| 
						 | 
					1111aa16e6 | ||
| 
						 | 
					0e435fa959 | ||
| 
						 | 
					6a5b1b1f3e | ||
| 
						 | 
					a42521dfbb | ||
| 
						 | 
					39f5e27412 | ||
| 
						 | 
					d1058b9445 | ||
| 
						 | 
					0aacd21658 | ||
| 
						 | 
					0d957d8364 | ||
| 
						 | 
					5b7eeecac2 | ||
| 
						 | 
					fd74e79ab3 | ||
| 
						 | 
					d29a1435ad | ||
| 
						 | 
					ef1b9847f1 | ||
| 
						 | 
					e2ed40d02b | ||
| 
						 | 
					02aeba47df | ||
| 
						 | 
					23ae0e3d23 | ||
| 
						 | 
					ff7c581011 | ||
| 
						 | 
					9935d0d21d | ||
| 
						 | 
					478c150a09 | ||
| 
						 | 
					a621d59ea8 | ||
| 
						 | 
					57b2589041 | ||
| 
						 | 
					4a09054dc0 | ||
| 
						 | 
					b7a2b59c68 | ||
| 
						 | 
					140c97c318 | ||
| 
						 | 
					345d9ad1b3 | ||
| 
						 | 
					44347efacd | ||
| 
						 | 
					5ba3ef9a2b | ||
| 
						 | 
					106f2a02c5 | ||
| 
						 | 
					83a0892e61 | ||
| 
						 | 
					cdc762a591 | ||
| 
						 | 
					cefddbc913 | ||
| 
						 | 
					7e1fec20c0 | ||
| 
						 | 
					e87576984e | ||
| 
						 | 
					b5fa1aad7c | ||
| 
						 | 
					586ddda9bc | ||
| 
						 | 
					4f29fe7d1f | ||
| 
						 | 
					343c0ee8d9 | ||
| 
						 | 
					c6e4aeff95 | ||
| 
						 | 
					1e1275faee | ||
| 
						 | 
					ac9ee9aa8d | ||
| 
						 | 
					f197f15b2d | ||
| 
						 | 
					06abd3ea19 | ||
| 
						 | 
					dc6a860d0a | ||
| 
						 | 
					36342f4181 | ||
| 
						 | 
					477178dc63 | ||
| 
						 | 
					1002c4330d | ||
| 
						 | 
					f94562a622 | ||
| 
						 | 
					3cf9d536ed | ||
| 
						 | 
					38a9a25a70 | ||
| 
						 | 
					94f8258de7 | ||
| 
						 | 
					57843c571a | ||
| 
						 | 
					01dfe04c37 | ||
| 
						 | 
					f38a47ee01 | ||
| 
						 | 
					677bebbe5c | ||
| 
						 | 
					338a5ae770 | ||
| 
						 | 
					8abe13cf75 | ||
| 
						 | 
					bda7bb7667 | ||
| 
						 | 
					b8ff74b37a | ||
| 
						 | 
					32416fdcde | ||
| 
						 | 
					76363e4334 | ||
| 
						 | 
					9438613792 | ||
| 
						 | 
					3f33929119 | ||
| 
						 | 
					0f85956991 | ||
| 
						 | 
					091642a8f5 | ||
| 
						 | 
					8fa3e063f5 | ||
| 
						 | 
					ee35008037 | ||
| 
						 | 
					22b26d8a1c | ||
| 
						 | 
					3a7af57d03 | ||
| 
						 | 
					5c516fdbd2 | ||
| 
						 | 
					1e81b2e0a4 | ||
| 
						 | 
					36ada89373 | ||
| 
						 | 
					1e16e1dc92 | ||
| 
						 | 
					24dd3bcefc | ||
| 
						 | 
					cc30b052db | ||
| 
						 | 
					bd1f01fd27 | ||
| 
						 | 
					29b9f94cac | ||
| 
						 | 
					2d222a130d | ||
| 
						 | 
					2e490582fe | ||
| 
						 | 
					8757384aec | ||
| 
						 | 
					bf2842e64b | ||
| 
						 | 
					14beef6cfe | ||
| 
						 | 
					d6bb6190d3 | ||
| 
						 | 
					153cb7f47d | ||
| 
						 | 
					3547d64061 | ||
| 
						 | 
					7f360e74b7 | ||
| 
						 | 
					0b5997e8f7 | ||
| 
						 | 
					de8eb7bb33 | ||
| 
						 | 
					940e5953ed | ||
| 
						 | 
					bee29f46ae | ||
| 
						 | 
					f510f5bc1f | ||
| 
						 | 
					e9cbee917d | ||
| 
						 | 
					2cfc8e2c9a | ||
| 
						 | 
					c2eb4b7405 | ||
| 
						 | 
					f5dde9fe54 | ||
| 
						 | 
					1ad2d3f811 | ||
| 
						 | 
					8c88219ecc | ||
| 
						 | 
					e33c236db0 | ||
| 
						 | 
					8942a2c72f | ||
| 
						 | 
					559c9c219c | ||
| 
						 | 
					a1f1128be5 | ||
| 
						 | 
					19319ce487 | ||
| 
						 | 
					5e0fbdcb9e | ||
| 
						 | 
					cc2da7ba2c | ||
| 
						 | 
					3094bb218a | ||
| 
						 | 
					97eb2f9446 | ||
| 
						 | 
					2f2935eadd | ||
| 
						 | 
					0eaa3d3798 | ||
| 
						 | 
					263c75c912 | ||
| 
						 | 
					1a1e8307db | ||
| 
						 | 
					7ebdfc29da | ||
| 
						 | 
					b896b8e258 | ||
| 
						 | 
					cd18a5003d | ||
| 
						 | 
					52e2c08f66 | ||
| 
						 | 
					bf5e3cb3a4 | ||
| 
						 | 
					adcf27955d | ||
| 
						 | 
					9184e855a1 | ||
| 
						 | 
					f3561b59e4 | ||
| 
						 | 
					c7802d9fc1 | ||
| 
						 | 
					0bdad9f388 | ||
| 
						 | 
					2f94b77d8e | ||
| 
						 | 
					6b1e9515c9 | ||
| 
						 | 
					9c2512a638 | ||
| 
						 | 
					5c598c76cd | ||
| 
						 | 
					1cf38fcbe8 | ||
| 
						 | 
					b498746a49 | ||
| 
						 | 
					de4dc29c12 | ||
| 
						 | 
					ee1cb75127 | ||
| 
						 | 
					4f8384bfbb | ||
| 
						 | 
					1463383505 | ||
| 
						 | 
					2d92e3937f | ||
| 
						 | 
					8d03c50024 | ||
| 
						 | 
					11f72dfbcb | ||
| 
						 | 
					56b7c64298 | ||
| 
						 | 
					683ce64039 | ||
| 
						 | 
					e30153ea10 | ||
| 
						 | 
					0dcbc07682 | ||
| 
						 | 
					5deac1404d | ||
| 
						 | 
					13866f02a2 | ||
| 
						 | 
					afd9393a2d | ||
| 
						 | 
					17bfbedbc7 | ||
| 
						 | 
					bd1b90f141 | ||
| 
						 | 
					84f2b51a37 | ||
| 
						 | 
					dbdf84eaff | ||
| 
						 | 
					7eb9e69506 | ||
| 
						 | 
					715aeb4ebc | ||
| 
						 | 
					ace3b1bdc1 | ||
| 
						 | 
					54a126f6d4 | ||
| 
						 | 
					853cdf85eb | ||
| 
						 | 
					978071bce9 | ||
| 
						 | 
					d9066272ec | ||
| 
						 | 
					fb904fbbd9 | ||
| 
						 | 
					304b10a8b5 | ||
| 
						 | 
					20dfa54508 | ||
| 
						 | 
					ef8a730f6d | ||
| 
						 | 
					19b3392166 | ||
| 
						 | 
					13961c9b7a | ||
| 
						 | 
					f8a93cb882 | ||
| 
						 | 
					ba5683194a | ||
| 
						 | 
					cc77eed9d9 | ||
| 
						 | 
					10b29153b7 | ||
| 
						 | 
					f61acd75c4 | ||
| 
						 | 
					dbd3b78a9b | ||
| 
						 | 
					dff399fa42 | ||
| 
						 | 
					a02f945479 | ||
| 
						 | 
					d3d46de278 | ||
| 
						 | 
					de0eeafd2e | ||
| 
						 | 
					063296883f | ||
| 
						 | 
					92cbe4aee0 | ||
| 
						 | 
					b6babc39dc | ||
| 
						 | 
					48df600bfa | ||
| 
						 | 
					812a7d3fa3 | ||
| 
						 | 
					769e28114f | ||
| 
						 | 
					bf129c1437 | ||
| 
						 | 
					14015124ed | ||
| 
						 | 
					43fc4a8690 | ||
| 
						 | 
					7ce56d7c64 | ||
| 
						 | 
					858f038c1c | ||
| 
						 | 
					7e8d5ab4c4 | ||
| 
						 | 
					dd28f82747 | ||
| 
						 | 
					9ecdc11eb6 | ||
| 
						 | 
					1859ec38cc | ||
| 
						 | 
					ebdc0d8c17 | ||
| 
						 | 
					089d734cd1 | ||
| 
						 | 
					c90e6f8de1 | ||
| 
						 | 
					fef3da24ae | ||
| 
						 | 
					9f43c65c99 | ||
| 
						 | 
					affd6a7f7a | ||
| 
						 | 
					e50b5f1e3f | ||
| 
						 | 
					8a9eec345e | ||
| 
						 | 
					89a995a439 | ||
| 
						 | 
					a2ef802dba | ||
| 
						 | 
					77e2ff32d9 | ||
| 
						 | 
					9c84c88259 | ||
| 
						 | 
					f5cb4acb14 | ||
| 
						 | 
					9b9f5efb09 | ||
| 
						 | 
					196f36b75a | ||
| 
						 | 
					592239630b | ||
| 
						 | 
					b0aba59143 | ||
| 
						 | 
					b3291bc0e3 | ||
| 
						 | 
					880cbd3a8f | ||
| 
						 | 
					b9fede74ef | ||
| 
						 | 
					ffa2918bc5 | ||
| 
						 | 
					32744c8916 | ||
| 
						 | 
					ffe998ce9d | ||
| 
						 | 
					38a9c873bc | ||
| 
						 | 
					85aa87b61f | ||
| 
						 | 
					27a4e7371c | ||
| 
						 | 
					78ddfbc413 | ||
| 
						 | 
					4a1c561ce8 | ||
| 
						 | 
					0043005afb | ||
| 
						 | 
					7a2b65ed2c | ||
| 
						 | 
					201dbe04f7 | ||
| 
						 | 
					8318d9c25d | ||
| 
						 | 
					0dbf6f2a54 | ||
| 
						 | 
					e080808cfa | ||
| 
						 | 
					b5bad20731 | ||
| 
						 | 
					abed2e326d | ||
| 
						 | 
					2282b50e5c | ||
| 
						 | 
					cee6b122c2 | ||
| 
						 | 
					e9db5c7387 | ||
| 
						 | 
					a27fb3fce3 | ||
| 
						 | 
					c8d2166b86 | ||
| 
						 | 
					422636d752 | ||
| 
						 | 
					d72f46f020 | ||
| 
						 | 
					c276e4996d | ||
| 
						 | 
					864a41496c | ||
| 
						 | 
					ea7cb48c9c | ||
| 
						 | 
					fce67bc7fa | ||
| 
						 | 
					eeae776345 | ||
| 
						 | 
					0e3083daf5 | ||
| 
						 | 
					402451b308 | ||
| 
						 | 
					6c81adda45 | ||
| 
						 | 
					5557c0af80 | ||
| 
						 | 
					35d51ca4e3 | ||
| 
						 | 
					1d663d9277 | ||
| 
						 | 
					bf7bde1cbb | ||
| 
						 | 
					25d60cafc4 | ||
| 
						 | 
					0f88bcdebf | ||
| 
						 | 
					767144f7b7 | ||
| 
						 | 
					e376fd57cf | ||
| 
						 | 
					3bbb3cc1f2 | ||
| 
						 | 
					a89a23881f | ||
| 
						 | 
					c4fe1f97b4 | ||
| 
						 | 
					d06ec27856 | ||
| 
						 | 
					f48de627f4 | ||
| 
						 | 
					a161bf48bd | ||
| 
						 | 
					ea8dbddab1 | ||
| 
						 | 
					3e48cfd9f3 | ||
| 
						 | 
					405d2d10ec | ||
| 
						 | 
					3225290d77 | ||
| 
						 | 
					907f037339 | ||
| 
						 | 
					628d881247 | ||
| 
						 | 
					fbc1baa139 | ||
| 
						 | 
					afe189e802 | ||
| 
						 | 
					9d67e72e85 | ||
| 
						 | 
					c65c9e56e1 | ||
| 
						 | 
					5bc6560efc | ||
| 
						 | 
					49a2f404e8 | ||
| 
						 | 
					432d4f3b89 | ||
| 
						 | 
					ab8fba3a20 | ||
| 
						 | 
					6ad11a6990 | ||
| 
						 | 
					b0ead922a1 | ||
| 
						 | 
					284711d627 | ||
| 
						 | 
					131deb059f | ||
| 
						 | 
					9dacf0b35b | ||
| 
						 | 
					8c4f978483 | ||
| 
						 | 
					62e06f92e9 | ||
| 
						 | 
					f45ede640b | ||
| 
						 | 
					cfb61d9536 | ||
| 
						 | 
					55f600b702 | ||
| 
						 | 
					efcb046ce1 | ||
| 
						 | 
					c32c4a3648 | ||
| 
						 | 
					65bba93afa | ||
| 
						 | 
					720e19e7f3 | ||
| 
						 | 
					33e8007100 | ||
| 
						 | 
					88f34812df | ||
| 
						 | 
					b5fece959f | ||
| 
						 | 
					bc139a4af4 | ||
| 
						 | 
					e54f148ce9 | ||
| 
						 | 
					3f79470453 | ||
| 
						 | 
					3bd6488889 | ||
| 
						 | 
					d8e8ea9c8f | ||
| 
						 | 
					112c63e9b4 | ||
| 
						 | 
					4163361906 | ||
| 
						 | 
					d3c7591304 | ||
| 
						 | 
					4df959d305 | ||
| 
						 | 
					978396732b | ||
| 
						 | 
					3f561bfbfe | ||
| 
						 | 
					945be91032 | ||
| 
						 | 
					81db7334da | ||
| 
						 | 
					890de986ce | ||
| 
						 | 
					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 | 
							
								
								
									
										53
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										53
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,9 +1,11 @@
 | 
				
			|||||||
*.o
 | 
					*.o
 | 
				
			||||||
*.lo
 | 
					*.lo
 | 
				
			||||||
*.a
 | 
					*.a
 | 
				
			||||||
 | 
					*.sw?
 | 
				
			||||||
Makefile.in
 | 
					Makefile.in
 | 
				
			||||||
Makefile
 | 
					Makefile
 | 
				
			||||||
.deps
 | 
					.deps
 | 
				
			||||||
 | 
					src/cscope*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
aclocal.m4
 | 
					aclocal.m4
 | 
				
			||||||
autom4te.cache
 | 
					autom4te.cache
 | 
				
			||||||
@@ -12,11 +14,60 @@ config.guess
 | 
				
			|||||||
config.sub
 | 
					config.sub
 | 
				
			||||||
config.status
 | 
					config.status
 | 
				
			||||||
configure
 | 
					configure
 | 
				
			||||||
 | 
					compile
 | 
				
			||||||
depcomp
 | 
					depcomp
 | 
				
			||||||
install-sh
 | 
					install-sh
 | 
				
			||||||
missing
 | 
					missing
 | 
				
			||||||
libtool
 | 
					*libtool
 | 
				
			||||||
ltmain.sh
 | 
					ltmain.sh
 | 
				
			||||||
 | 
					*~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
core
 | 
					core
 | 
				
			||||||
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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# git-version-gen magic
 | 
				
			||||||
 | 
					.tarball-version
 | 
				
			||||||
 | 
					.version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ignore debian files
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
							
								
								
									
										22
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								Makefile.am
									
									
									
									
									
								
							@@ -1,3 +1,23 @@
 | 
				
			|||||||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
 | 
					AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SUBDIRS = src
 | 
					SUBDIRS = include src doc tests contrib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BUILT_SOURCES = $(top_srcdir)/.version
 | 
				
			||||||
 | 
					EXTRA_DIST = \
 | 
				
			||||||
 | 
						     .version \
 | 
				
			||||||
 | 
						     README.md \
 | 
				
			||||||
 | 
						     contrib/osmo-pcu.spec.in \
 | 
				
			||||||
 | 
						     debian \
 | 
				
			||||||
 | 
						     git-version-gen \
 | 
				
			||||||
 | 
						     osmoappdesc.py \
 | 
				
			||||||
 | 
						     $(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
				
			||||||
 | 
						--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@RELMAKE@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$(top_srcdir)/.version:
 | 
				
			||||||
 | 
						echo $(VERSION) > $@-t && mv $@-t $@
 | 
				
			||||||
 | 
					dist-hook:
 | 
				
			||||||
 | 
						echo $(VERSION) > $(distdir)/.tarball-version
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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 https://gitea.osmocom.org/cellular-infrastructure/osmo-pcu
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/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
 | 
				
			||||||
							
								
								
									
										9
									
								
								TODO-RELEASE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								TODO-RELEASE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					# 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
 | 
				
			||||||
							
								
								
									
										260
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										260
									
								
								configure.ac
									
									
									
									
									
								
							@@ -1,13 +1,24 @@
 | 
				
			|||||||
dnl Process this file with autoconf to produce a configure script
 | 
					dnl Process this file with autoconf to produce a configure script
 | 
				
			||||||
AC_INIT([osmo-pcu],
 | 
					AC_INIT([osmo-pcu],
 | 
				
			||||||
	m4_esyscmd([./git-version-gen .tarball-version]),
 | 
						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])
 | 
					AM_INIT_AUTOMAKE([dist-bzip2])
 | 
				
			||||||
 | 
					AC_CONFIG_TESTDIR(tests)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CXXFLAGS="$CXXFLAGS -std=gnu++03"
 | 
				
			||||||
 | 
					CFLAGS="$CFLAGS -std=gnu11"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dnl kernel style compile messages
 | 
					dnl kernel style compile messages
 | 
				
			||||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 | 
					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
 | 
					dnl checks for programs
 | 
				
			||||||
AC_PROG_MAKE_SET
 | 
					AC_PROG_MAKE_SET
 | 
				
			||||||
AC_PROG_CC
 | 
					AC_PROG_CC
 | 
				
			||||||
@@ -15,25 +26,248 @@ AC_PROG_CXX
 | 
				
			|||||||
AC_PROG_INSTALL
 | 
					AC_PROG_INSTALL
 | 
				
			||||||
LT_INIT
 | 
					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
 | 
					dnl checks for header files
 | 
				
			||||||
AC_HEADER_STDC
 | 
					AC_HEADER_STDC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dnl Checks for typedefs, structures and compiler characteristics
 | 
					dnl Checks for typedefs, structures and compiler characteristics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dnl checks for libraries
 | 
					AC_ARG_ENABLE(sanitize,
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore  >= 0.3.9)
 | 
							[AS_HELP_STRING([--enable-sanitize], [Compile with address sanitizer enabled], )],
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
 | 
							[sanitize=$enableval], [sanitize="no"])
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
 | 
					if test x"$sanitize" = x"yes"
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.1.4)
 | 
					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(werror,
 | 
				
			||||||
AC_ARG_ENABLE(sysmocom-bts,
 | 
						[AS_HELP_STRING(
 | 
				
			||||||
		AC_HELP_STRING([--enable-sysmocom-bts],
 | 
							[--enable-werror],
 | 
				
			||||||
				[enable code for sysmocom femto-bts [default=no]]),
 | 
							[Turn all compiler warnings into errors, with exceptions:
 | 
				
			||||||
		[enable_sysmocom_bts="yes"],[enable_sysmocom_bts="no"])
 | 
							 a) deprecation (allow upstream to mark deprecation without breaking builds);
 | 
				
			||||||
AC_MSG_RESULT([$enable_sysmocom_bts])
 | 
							 b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
 | 
				
			||||||
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
 | 
							]
 | 
				
			||||||
 | 
						)],
 | 
				
			||||||
 | 
						[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.9.0)
 | 
				
			||||||
 | 
					PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
 | 
				
			||||||
 | 
					PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
 | 
				
			||||||
 | 
					PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
 | 
				
			||||||
 | 
					PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 1.9.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_MSG_CHECKING([whether to enable direct E1 CCU access for PDCH of Ericsson RBS])
 | 
				
			||||||
 | 
					AC_ARG_ENABLE(er-e1-ccu,
 | 
				
			||||||
 | 
					                AC_HELP_STRING([--enable-er-e1-ccu],
 | 
				
			||||||
 | 
					                                [enable code for Ericsson RBS E1 CCU [default=no]]),
 | 
				
			||||||
 | 
					                [enable_er_e1_ccu="$enableval"],[enable_er_e1_ccu="no"])
 | 
				
			||||||
 | 
					AC_MSG_RESULT([$enable_er_e1_ccu])
 | 
				
			||||||
 | 
					AM_CONDITIONAL(ENABLE_ER_E1_CCU, test "x$enable_er_e1_ccu" = "xyes")
 | 
				
			||||||
 | 
					AS_IF([test "x$enable_er_e1_ccu" = "xyes"], [
 | 
				
			||||||
 | 
						PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0)
 | 
				
			||||||
 | 
						PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.5.0)
 | 
				
			||||||
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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(
 | 
					AC_OUTPUT(
 | 
				
			||||||
 | 
					    include/Makefile
 | 
				
			||||||
    src/Makefile
 | 
					    src/Makefile
 | 
				
			||||||
 | 
					    doc/Makefile
 | 
				
			||||||
 | 
					    doc/examples/Makefile
 | 
				
			||||||
 | 
					    tests/Makefile
 | 
				
			||||||
 | 
					    doc/manuals/Makefile
 | 
				
			||||||
 | 
					    contrib/Makefile
 | 
				
			||||||
 | 
					    contrib/systemd/Makefile
 | 
				
			||||||
 | 
					    contrib/osmo-pcu.spec
 | 
				
			||||||
    Makefile)
 | 
					    Makefile)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								contrib/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								contrib/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					SUBDIRS = systemd
 | 
				
			||||||
							
								
								
									
										109
									
								
								contrib/jenkins.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										109
									
								
								contrib/jenkins.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,109 @@
 | 
				
			|||||||
 | 
					#!/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 "Ericsson E1 CCU support enabled, sanitizer enabled"
 | 
				
			||||||
 | 
					  PCU_CONFIG="$PCU_CONFIG --enable-werror --enable-sanitize --enable-er-e1-ccu"
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					osmo-build-dep.sh libosmo-abis "" --disable-dahdi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
							
								
								
									
										86
									
								
								contrib/osmo-pcu.spec.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								contrib/osmo-pcu.spec.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					#
 | 
				
			||||||
 | 
					# 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.9.0
 | 
				
			||||||
 | 
					BuildRequires:  pkgconfig(libosmogb) >= 1.9.0
 | 
				
			||||||
 | 
					BuildRequires:  pkgconfig(libosmogsm) >= 1.9.0
 | 
				
			||||||
 | 
					BuildRequires:  pkgconfig(libosmovty) >= 1.9.0
 | 
				
			||||||
 | 
					BuildRequires:  pkgconfig(libosmoctrl) >= 1.9.0
 | 
				
			||||||
 | 
					BuildRequires:  pkgconfig(libosmoabis) >= 1.5.0
 | 
				
			||||||
 | 
					BuildRequires:  pkgconfig(libosmotrau) >= 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} \
 | 
				
			||||||
 | 
					  --enable-er-e1-ccu
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
							
								
								
									
										21
									
								
								contrib/systemd/osmo-pcu.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								contrib/systemd/osmo-pcu.service
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					[Unit]
 | 
				
			||||||
 | 
					Description=Osmocom osmo-pcu
 | 
				
			||||||
 | 
					After=network-online.target
 | 
				
			||||||
 | 
					Wants=network-online.target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Service]
 | 
				
			||||||
 | 
					Type=simple
 | 
				
			||||||
 | 
					ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
 | 
				
			||||||
 | 
					StateDirectory=osmocom
 | 
				
			||||||
 | 
					WorkingDirectory=%S/osmocom
 | 
				
			||||||
 | 
					Restart=always
 | 
				
			||||||
 | 
					RestartSec=2
 | 
				
			||||||
 | 
					RestartPreventExitStatus=1
 | 
				
			||||||
 | 
					# CPU scheduling policy:
 | 
				
			||||||
 | 
					CPUSchedulingPolicy=rr
 | 
				
			||||||
 | 
					# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
				
			||||||
 | 
					CPUSchedulingPriority=11
 | 
				
			||||||
 | 
					# See sched(7) for further details on real-time policies and priorities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[Install]
 | 
				
			||||||
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
							
								
								
									
										1516
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1516
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					10
 | 
				
			||||||
							
								
								
									
										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 (>= 10),
 | 
				
			||||||
 | 
					               dh-autoreconf,
 | 
				
			||||||
 | 
					               autotools-dev,
 | 
				
			||||||
 | 
					               pkg-config,
 | 
				
			||||||
 | 
						       libosmo-abis-dev (>= 1.5.0),
 | 
				
			||||||
 | 
					               libosmocore-dev (>= 1.9.0),
 | 
				
			||||||
 | 
					               osmo-gsm-manuals-dev (>= 1.5.0)
 | 
				
			||||||
 | 
					Standards-Version: 3.9.8
 | 
				
			||||||
 | 
					Homepage: http://osmocom.org/projects/osmopcu
 | 
				
			||||||
 | 
					Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-pcu
 | 
				
			||||||
 | 
					Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/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.
 | 
				
			||||||
							
								
								
									
										134
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 | 
				
			||||||
 | 
					Upstream-Name: osmo-pcu
 | 
				
			||||||
 | 
					Source: https://gitea.osmocom.org/cellular-infrastructure/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.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					           2015 by Yves Godin <support@nuranwireless.com>
 | 
				
			||||||
 | 
					License:   AGPL-3.0+
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Files:     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.f.m.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.c
 | 
				
			||||||
 | 
					           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.c
 | 
				
			||||||
 | 
					           src/encoding.cpp
 | 
				
			||||||
 | 
					           src/encoding.h
 | 
				
			||||||
 | 
					           src/gprs_rlcmac.c
 | 
				
			||||||
 | 
					           src/ts_alloc.cpp
 | 
				
			||||||
 | 
					Copyright: 2012 Ivan Klyuchnikov
 | 
				
			||||||
 | 
					           2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
				
			||||||
 | 
					           2013 by Holger Hans Peter Freyther
 | 
				
			||||||
 | 
					           2022 by by Sysmocom s.f.m.c. GmbH
 | 
				
			||||||
 | 
					License:   GPL-2.0+
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Files:     src/gprs_rlcmac.h
 | 
				
			||||||
 | 
					           src/gprs_bssgp_pcu.cpp
 | 
				
			||||||
 | 
					           src/gprs_bssgp_pcu.h
 | 
				
			||||||
 | 
					           src/ts_alloc.h
 | 
				
			||||||
 | 
					           src/bts.h
 | 
				
			||||||
 | 
					Copyright: 2012 Ivan Klyuchnikov
 | 
				
			||||||
 | 
					           2013 by Holger Hans Peter Freyther
 | 
				
			||||||
 | 
					           2022 by by Sysmocom s.f.m.c. GmbH
 | 
				
			||||||
 | 
					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.c
 | 
				
			||||||
 | 
					           src/csn1_dec.c
 | 
				
			||||||
 | 
					           src/csn1_enc.c
 | 
				
			||||||
 | 
					           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 --enable-er-e1-ccu
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 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
 | 
				
			||||||
							
								
								
									
										9
									
								
								doc/examples/osmo-pcu.e1_ccu.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								doc/examples/osmo-pcu.e1_ccu.cfg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					pcu
 | 
				
			||||||
 | 
					 flow-control-interval 10
 | 
				
			||||||
 | 
					 cs 2
 | 
				
			||||||
 | 
					 alloc-algorithm dynamic
 | 
				
			||||||
 | 
					 gamma 0
 | 
				
			||||||
 | 
					e1_input
 | 
				
			||||||
 | 
					 e1_line 0 driver dahdi
 | 
				
			||||||
 | 
					 e1_line 0 port 2
 | 
				
			||||||
 | 
					 no e1_line 0 keepalive
 | 
				
			||||||
							
								
								
									
										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
 | 
				
			||||||
							
								
								
									
										511
									
								
								doc/manuals/chapters/configuration.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										511
									
								
								doc/manuals/chapters/configuration.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,511 @@
 | 
				
			|||||||
 | 
					== 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 Service. This service is nowadays provided by means
 | 
				
			||||||
 | 
					of PCUIF container messages, which are transparently forwarded in both directions
 | 
				
			||||||
 | 
					by the BTS using the IPA multiplex of the OML connection against the BSC. No
 | 
				
			||||||
 | 
					specific configuration is required in any of the involved nodes, they should
 | 
				
			||||||
 | 
					behave properly out of the box.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					These neighbor address resolutions (<ARFCN + BSIC> => <RAI + CI>) are by default
 | 
				
			||||||
 | 
					cached for a while, in order to avoid querying the BSC frequently. As a result,
 | 
				
			||||||
 | 
					the resolution time is also optimized.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.Example: Configure Neighbor Resolution cache and timeouts
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					pcu
 | 
				
			||||||
 | 
					 timer X1 500 <1>
 | 
				
			||||||
 | 
					 timer X0 60 <2>
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					<1> Time out if the BSC doesn't answer our resolution request after 500 ms
 | 
				
			||||||
 | 
					<2> Keep resolved neighbor addresses cached for 60 seconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					===== OsmoBSC CTRL interface (deprecated)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CAUTION: This interface is nowadays considered deprecated and should not be used
 | 
				
			||||||
 | 
					anymore. Any related VTY options should be dropped from configuration files, to
 | 
				
			||||||
 | 
					let OsmoPCU use the new interface instead. This section is kept here for a while
 | 
				
			||||||
 | 
					as a reference for old deployments using old versions of the programs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This Neighbor Address Resolution Service was initially implemented by means of a
 | 
				
			||||||
 | 
					separate CTRL interface (see OsmoBSC User Manual), where OsmoPCU would create a
 | 
				
			||||||
 | 
					CTRL connection to the BSC each time an address resolution was required.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Older versions of OsmoBSC may not support the current Neighbor Address
 | 
				
			||||||
 | 
					Resolution Service over the IPA multiplex (see above). For those cases, OsmoPCU
 | 
				
			||||||
 | 
					can be configured to use the old deprecated CTRL interface.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.Example: Configure Neighbor Resolution CTRL interface against OsmoBSC
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					pcu
 | 
				
			||||||
 | 
					 neighbor resolution 172.18.13.10 4248 <1>
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					<1> Port 4248 is the default and hence could be omitted in this case
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					==== 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[cfg_e1_line]]
 | 
				
			||||||
 | 
					=== Configuring E1 line for CCU access
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Depending on the configuration the PCU may require direct access to a BTS CCU
 | 
				
			||||||
 | 
					(channel coding unit) via an E1 line. This is in particular the case when
 | 
				
			||||||
 | 
					OsmoPCU runs in co-location with OsmoBSC.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The exact timeslot configuration is passed to the PCU via the pcu_sock
 | 
				
			||||||
 | 
					interface. Only basic E1 line settings are required. However, it is important
 | 
				
			||||||
 | 
					that the E1 line number is the same as the E1 line number that is used in the
 | 
				
			||||||
 | 
					timeslot configuration of OsmoBSC.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.Example: Configure an E1 line
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					e1_input
 | 
				
			||||||
 | 
					 e1_line 0 driver dahdi
 | 
				
			||||||
 | 
					 e1_line 0 port 2
 | 
				
			||||||
 | 
					 no e1_line 0 keepalive
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					=== 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[gsmtap]]
 | 
				
			||||||
 | 
					=== Configuring GSMTAP tracing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In addition to being able to obtain pcap protocol traces of the NS/BSSGP
 | 
				
			||||||
 | 
					communication and the text-based logging from the OsmoPCU software, there is
 | 
				
			||||||
 | 
					also the capability of tracing all communication on the radio interface related
 | 
				
			||||||
 | 
					to PS. To do so, OsmoPCU can encapsulate MAC blocks (23-155 byte messages at the
 | 
				
			||||||
 | 
					L2-L1 interface depending on coding scheme) into _GSMTAP_ and send them via
 | 
				
			||||||
 | 
					UDP/IP. At that point, they can be captured with utilities like *tcpdump* or
 | 
				
			||||||
 | 
					*tshark* for further analysis by the *wireshark* protocol analyzer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to activate this feature, you first need to make sure to specify
 | 
				
			||||||
 | 
					the remote address of _GSMTAP_ host in the configuration file.  In most
 | 
				
			||||||
 | 
					cases, using 127.0.0.1 for passing the messages over the loopback (`lo`)
 | 
				
			||||||
 | 
					device will be sufficient:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.Example: Enabling GSMTAP Um-frame logging to localhost
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					pcu
 | 
				
			||||||
 | 
					 gsmtap-remote-host 127.0.0.1 <1>
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					<1> Destination address for _GSMTAP_ Um-frames
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NOTE: Changing this parameter at run-time will not affect the existing
 | 
				
			||||||
 | 
					_GSMTAP_ connection, full program restart is required.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NOTE: Command line parameters `-i` and `--gsmtap-ip` have been deprecated.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoPCU can selectively trace such messages based on different categories, for
 | 
				
			||||||
 | 
					both Ul and Dl. For a complete list of cateogry values, please refer to the
 | 
				
			||||||
 | 
					_OsmoPCU VTY reference manual_ <<vty-ref-osmopcu>>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For example, to enable GSMTAP tracing for all DL EGPRS rlcmac data blocks, you
 | 
				
			||||||
 | 
					can use the `gsmtap-category dl-data-egprs` command at the `pcu` node of the
 | 
				
			||||||
 | 
					OsmoPCU VTY.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.Example: Enabling  GSMTAP for for all DL EGPRS rlcmac data blocks
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					OsmoPCU> enable
 | 
				
			||||||
 | 
					OsmoPCU# configure terminal
 | 
				
			||||||
 | 
					OsmoPCU(config)# pcu
 | 
				
			||||||
 | 
					OsmoPCU(pcu)# gsmtap-category dl-data-egprs
 | 
				
			||||||
 | 
					OsmoPCU(trx)# write <1>
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					<1> the `write` command will make the configuration persistent in the
 | 
				
			||||||
 | 
					configuration file.  This is not required if you wish to enable GSMTAP
 | 
				
			||||||
 | 
					only in the current session of OsmoPCU.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					De-activation can be performed similarly by using the `no gsmtap-category
 | 
				
			||||||
 | 
					dl-data-egprs` command at the `pcu` node of the OsmoPCU VTY.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It may be useful to enable all categories with a few exceptions, or vice versa
 | 
				
			||||||
 | 
					disable everything using one command.  For this purpose, the VTY provides
 | 
				
			||||||
 | 
					`gsmtap-category enable-all` and `gsmtap-category disable-all` commands.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.Example: Enabling all categoriess except _dl-dummy_
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					pcu
 | 
				
			||||||
 | 
					 gsmtap-category enable-all <1>
 | 
				
			||||||
 | 
					 no gsmtap-category dl-dummy <2>
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					<1> Enable all available SAPIs
 | 
				
			||||||
 | 
					<2> Exclude DL RLCMAC blocks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					From the moment they are enabled via VTY, GSMTAP messages will be
 | 
				
			||||||
 | 
					generated and sent in UDP encapsulation to the IANA-registered UDP port
 | 
				
			||||||
 | 
					for GSMTAP (4729) of the specified remote address.
 | 
				
			||||||
							
								
								
									
										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[]
 | 
				
			||||||
							
								
								
									
										208
									
								
								doc/manuals/chapters/counters_generated.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								doc/manuals/chapters/counters_generated.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,208 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					// autogenerated by show asciidoc counters
 | 
				
			||||||
 | 
					These counters and their description are based on OsmoPCU 0.9.0.244-de96 (OsmoPCU).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					=== Rate Counters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// generating tables for rate_ctr_group
 | 
				
			||||||
 | 
					// 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)
 | 
				
			||||||
 | 
					| packets:out:drop | <<ns:nsvc_packets:out:drop>> | Dropped Packets      (Out)
 | 
				
			||||||
 | 
					| bytes:in | <<ns:nsvc_bytes:in>> | Bytes at NS Level    ( In)
 | 
				
			||||||
 | 
					| bytes:out | <<ns:nsvc_bytes:out>> | Bytes at NS Level    (Out)
 | 
				
			||||||
 | 
					| bytes:out:drop | <<ns:nsvc_bytes:out:drop>> | Dropped Bytes        (Out)
 | 
				
			||||||
 | 
					| blocked | <<ns:nsvc_blocked>> | NS-VC Block count
 | 
				
			||||||
 | 
					| unblocked | <<ns:nsvc_unblocked>> | NS-VC Unblock 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 NSE Peer Statistics
 | 
				
			||||||
 | 
					.ns:nse - NSE Peer Statistics
 | 
				
			||||||
 | 
					[options="header"]
 | 
				
			||||||
 | 
					|===
 | 
				
			||||||
 | 
					| Name | Reference | Description
 | 
				
			||||||
 | 
					| packets:in | <<ns:nse_packets:in>> | Packets at NS Level  ( In)
 | 
				
			||||||
 | 
					| packets:out | <<ns:nse_packets:out>> | Packets at NS Level  (Out)
 | 
				
			||||||
 | 
					| packets:out:drop | <<ns:nse_packets:out:drop>> | Dropped Packets      (Out)
 | 
				
			||||||
 | 
					| bytes:in | <<ns:nse_bytes:in>> | Bytes at NS Level    ( In)
 | 
				
			||||||
 | 
					| bytes:out | <<ns:nse_bytes:out>> | Bytes at NS Level    (Out)
 | 
				
			||||||
 | 
					| bytes:out:drop | <<ns:nse_bytes:out:drop>> | Dropped Bytes        (Out)
 | 
				
			||||||
 | 
					| blocked | <<ns:nse_blocked>> | NS-VC Block count
 | 
				
			||||||
 | 
					| unblocked | <<ns:nse_unblocked>> | NS-VC Unblock count
 | 
				
			||||||
 | 
					| dead | <<ns:nse_dead>> | NS-VC gone dead count
 | 
				
			||||||
 | 
					| replaced | <<ns:nse_replaced>> | NS-VC replaced other count
 | 
				
			||||||
 | 
					| nsei-chg | <<ns:nse_nsei-chg>> | NS-VC changed NSEI count
 | 
				
			||||||
 | 
					| inv-nsvci | <<ns:nse_inv-nsvci>> | NS-VCI was invalid count
 | 
				
			||||||
 | 
					| inv-nsei | <<ns:nse_inv-nsei>> | NSEI was invalid count
 | 
				
			||||||
 | 
					| lost:alive | <<ns:nse_lost:alive>> | ALIVE ACK missing count
 | 
				
			||||||
 | 
					| lost:reset | <<ns:nse_lost:reset>> | RESET ACK missing count
 | 
				
			||||||
 | 
					|===
 | 
				
			||||||
 | 
					// rate_ctr_group table SGSN Statistics
 | 
				
			||||||
 | 
					.pcu:sgsn - SGSN Statistics
 | 
				
			||||||
 | 
					[options="header"]
 | 
				
			||||||
 | 
					|===
 | 
				
			||||||
 | 
					| Name | Reference | Description
 | 
				
			||||||
 | 
					| rx_paging_cs | <<pcu:sgsn_rx_paging_cs>> | Amount of paging CS requests received
 | 
				
			||||||
 | 
					| rx_paging_ps | <<pcu:sgsn_rx_paging_ps>> | Amount of paging PS requests received
 | 
				
			||||||
 | 
					|===
 | 
				
			||||||
 | 
					// 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 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:alloc:failed | <<bts_tbf:alloc:failed>> | TBF Alloc Failure (any reason)
 | 
				
			||||||
 | 
					| tbf:alloc:failed:no_tfi | <<bts_tbf:alloc:failed:no_tfi>> | TBF Alloc Failure (TFIs exhausted)
 | 
				
			||||||
 | 
					| tbf:alloc:failed:no_usf | <<bts_tbf:alloc:failed:no_usf>> | TBF Alloc Failure (USFs exhausted)
 | 
				
			||||||
 | 
					| tbf:alloc:failed:no_slot_combi | <<bts_tbf:alloc:failed:no_slot_combi>> | TBF Alloc Failure (No valid UL/DL slot combination found)
 | 
				
			||||||
 | 
					| tbf:alloc:failed:no_slot_avail | <<bts_tbf:alloc:failed:no_slot_avail>> | TBF Alloc Failure (No slot available)
 | 
				
			||||||
 | 
					| 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
 | 
				
			||||||
 | 
					| pch:requests | <<bts_pch:requests>> | PCH requests sent
 | 
				
			||||||
 | 
					| pch:requests:timeout | <<bts_pch:requests:timeout>> | PCH requests timeout
 | 
				
			||||||
 | 
					| rach:requests | <<bts_rach:requests>> | RACH requests received
 | 
				
			||||||
 | 
					| rach:requests:11bit | <<bts_rach:requests:11bit>> | 11BIT_RACH requests received
 | 
				
			||||||
 | 
					| rach:requests:one_phase | <<bts_rach:requests:one_phase>> | One phase packet access with request for single TS UL
 | 
				
			||||||
 | 
					| rach:requests:two_phase | <<bts_rach:requests:two_phase>> | Single block packet request for two phase packet access
 | 
				
			||||||
 | 
					| rach:requests:unexpected | <<bts_rach:requests:unexpected>> | RACH Request with unexpected content received
 | 
				
			||||||
 | 
					| 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_ul:one_phase | <<bts_immediate:assignment_ul:one_phase>> | Immediate Assign UL (one phase packet access)
 | 
				
			||||||
 | 
					| immediate:assignment_ul:two_phase | <<bts_immediate:assignment_ul:two_phase>> | Immediate Assign UL (two phase packet access)
 | 
				
			||||||
 | 
					| immediate:assignment_ul:contention_resolution_success | <<bts_immediate:assignment_ul:contention_resolution_success>> | First RLC Block (PDU) on the PDTCH from the MS received
 | 
				
			||||||
 | 
					| 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
 | 
				
			||||||
 | 
					| pkt:cell_chg_notification | <<bts_pkt:cell_chg_notification>> | Packet Cell Change Notification
 | 
				
			||||||
 | 
					| pkt:cell_chg_continue | <<bts_pkt:cell_chg_continue>> | Packet Cell Change Continue
 | 
				
			||||||
 | 
					| pkt:neigh_cell_data | <<bts_pkt:neigh_cell_data>> | Packet Neighbour Cell Data
 | 
				
			||||||
 | 
					| 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
 | 
				
			||||||
 | 
					|===
 | 
				
			||||||
 | 
					=== Osmo Stat Items
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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
 | 
				
			||||||
 | 
					|===
 | 
				
			||||||
 | 
					NS Bind Statistics
 | 
				
			||||||
 | 
					// osmo_stat_item_group table NS Bind Statistics
 | 
				
			||||||
 | 
					.ns.bind - NS Bind Statistics
 | 
				
			||||||
 | 
					[options="header"]
 | 
				
			||||||
 | 
					|===
 | 
				
			||||||
 | 
					| Name | Reference | Description | Unit
 | 
				
			||||||
 | 
					| tx_backlog_length | <<ns.bind_tx_backlog_length>> | Transmit backlog length | packets
 | 
				
			||||||
 | 
					|===
 | 
				
			||||||
 | 
					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            |
 | 
				
			||||||
 | 
					| pdch.available | <<bts_pdch.available>> | PDCH available        |
 | 
				
			||||||
 | 
					| pdch.occupied | <<bts_pdch.occupied>> | PDCH occupied (all)   |
 | 
				
			||||||
 | 
					| pdch.occupied.gprs | <<bts_pdch.occupied.gprs>> | PDCH occupied (GPRS)  |
 | 
				
			||||||
 | 
					| pdch.occupied.egprs | <<bts_pdch.occupied.egprs>> | PDCH occupied (EGPRS) |
 | 
				
			||||||
 | 
					|===
 | 
				
			||||||
 | 
					// there are no ungrouped osmo_counters
 | 
				
			||||||
							
								
								
									
										123
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
				
			|||||||
 | 
					== Overview
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					=== About OsmoPCU
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoPCU is the Osmocom implementation of the GPRS PCU (Packet Control Unit)
 | 
				
			||||||
 | 
					element inside the GPRS network. Depending on the BTS type the PCU will be
 | 
				
			||||||
 | 
					co-located within the BTS or run in co-location with the BSC.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[pcu_co_located_with_bts]]
 | 
				
			||||||
 | 
					==== OsmoPCU in co-location with OsmoBTS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In most OsmoPCU-supported base stations, the PCU is co-located with the BTS.
 | 
				
			||||||
 | 
					In this scenario OsmoPCU and OsmoBTS run on the same host system. Both are
 | 
				
			||||||
 | 
					interconnected using a unix domain socket based interface.
 | 
				
			||||||
 | 
					(see also: <<pcu_sock_iface>>)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[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]
 | 
				
			||||||
 | 
						{ rank=same BTS PCU }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NOTE: Depending on the hardware architecture, OsmoPCU may also have direct
 | 
				
			||||||
 | 
					access on the PHY interface to exchange PDCH traffic efficiently. The socket
 | 
				
			||||||
 | 
					interface is then only used for signalling.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					==== OsmoPCU in co-location with OsmoBSC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Classic E1 based BTSs usually do not include a PCU. Instead those base stations
 | 
				
			||||||
 | 
					typically rely on an external PCU that is co-located with the BSC. The
 | 
				
			||||||
 | 
					signalling traffic (paging, channel assignments ect.) is then exchanged with the
 | 
				
			||||||
 | 
					BTS via RSL, while the PDCH traffic is handled by the PCU through a dedicated
 | 
				
			||||||
 | 
					TRAU frame based E1 connection.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoPCU supports this scenario as well. Due to the dedicated E1 connection, the
 | 
				
			||||||
 | 
					implementation is complex and strongly hardware specific. As of now (March 2023)
 | 
				
			||||||
 | 
					OsmoPCU supports Ericsson RBS2000/RBS6000 only. This implementation has been
 | 
				
			||||||
 | 
					made possible through funding by the NLnet Foundation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[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]
 | 
				
			||||||
 | 
						{ rank=same BTS PCU }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When OsmoPCU runs in co-location to OsmoBSC, both are connected through the
 | 
				
			||||||
 | 
					same unix domain socket interface as mentioned above.
 | 
				
			||||||
 | 
					(see also: <<pcu_co_located_with_bts>>) The socket is used to pass signalling
 | 
				
			||||||
 | 
					traffic between PCU and BSC while the PCU controls the PDCH by directly talking
 | 
				
			||||||
 | 
					to the BTS CCU (channel coding unit) through a dedicated E1 connection. The
 | 
				
			||||||
 | 
					E1 line interface uses TRAU frames and is vastly comparable to the interface that
 | 
				
			||||||
 | 
					is used when speech is transferred.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Since the PCU is mainly set up by OsmoBSC (or OsmoBTS) via the PCU socket, the
 | 
				
			||||||
 | 
					configuration in the BSC co-located scenario is no different from the BTS
 | 
				
			||||||
 | 
					co-located scenario. However, since the PCU requires a direct E1 connection to
 | 
				
			||||||
 | 
					the BTS an E1 line must be set up. (See also: <<cfg_e1_line>>)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					=== Software Components
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OsmoPCU consists of a variety of components, including
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Gb interface (NS/BSSGP protocol)
 | 
				
			||||||
 | 
					* `pcu_sock` interface towards OsmoBTS (or OsmoBSC)
 | 
				
			||||||
 | 
					* 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_iface]]
 | 
				
			||||||
 | 
					==== `pcu_sock` Interface to OsmoBTS/OsmoBSC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The interface towards OsmoBTS/OsmoBSC 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 path can be freely configured to a different file/path name,
 | 
				
			||||||
 | 
					primarily to permit running multiple independent BTS+PCU (or BSC+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/OsmoBSC 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.
 | 
				
			||||||
							
								
								
									
										27
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					== 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.
 | 
				
			||||||
 | 
					*-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
 | 
				
			||||||
							
								
								
									
										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.
 | 
					    Queue of next frames to be transmitted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
States:
 | 
					States:
 | 
				
			||||||
  GPRS_RLCMAC_ASSIGN
 | 
					  TBF_ST_ASSIGN
 | 
				
			||||||
    After a downlink TBF is created, it resides in this state until the
 | 
					    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
 | 
					    block flow can start. This is required to give the mobile time to listen
 | 
				
			||||||
    to connect to downlink PDCH.
 | 
					    to connect to downlink PDCH.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   GPRS_RLCMAC_FLOW,
 | 
					   TBF_ST_FLOW,
 | 
				
			||||||
    During packet flow, this state indicates downlink and uplink TBF block
 | 
					    During packet flow, this state indicates downlink and uplink TBF block
 | 
				
			||||||
    flow.
 | 
					    flow.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   GPRS_RLCMAC_FINISHED,
 | 
					   TBF_ST_FINISHED,
 | 
				
			||||||
    Uplink TBF:
 | 
					    Uplink TBF:
 | 
				
			||||||
    After final block is received AND all other blocks are completely
 | 
					    After final block is received AND all other blocks are completely
 | 
				
			||||||
    received, the state is entered. The PACKET CONTROL ACK is still not
 | 
					    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
 | 
					    downlink blocks are acknowledged yet. (Counter N3015 is counted on each
 | 
				
			||||||
    poll request.)
 | 
					    poll request.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   GPRS_RLCMAC_WAIT_RELEASE,
 | 
					   TBF_ST_WAIT_RELEASE,
 | 
				
			||||||
    The all blocks on downlink TBF have been acked by mobile. The penalty
 | 
					    The all blocks on downlink TBF have been acked by mobile. The penalty
 | 
				
			||||||
    timer T3192 is running on mobile.
 | 
					    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
 | 
					    Wait for TFI/USF to be re-used. This state is entered when a counter
 | 
				
			||||||
    reaches it's maximum and T3169 is running.
 | 
					    reaches it's maximum and T3169 is running.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -52,7 +52,7 @@ When downlink LLC PDU is received:
 | 
				
			|||||||
      Attach PDU to LLC Frame of TBF.
 | 
					      Attach PDU to LLC Frame of TBF.
 | 
				
			||||||
      Put TBF back into FLOW state.
 | 
					      Put TBF back into FLOW state.
 | 
				
			||||||
      Done.
 | 
					      Done.
 | 
				
			||||||
  If dowlink TBF does not exists for given TLLI, or in RELEASING state:
 | 
					  If downlink TBF does not exists for given TLLI, or in RELEASING state:
 | 
				
			||||||
    Create new downlink TBF.
 | 
					    Create new downlink TBF.
 | 
				
			||||||
    Attach PDU to LLC Frame of TBF.
 | 
					    Attach PDU to LLC Frame of TBF.
 | 
				
			||||||
    If uplink TBF exists for given TLLI, but not in RELEASING state:
 | 
					    If uplink TBF exists for given TLLI, but not in RELEASING state:
 | 
				
			||||||
@@ -117,13 +117,17 @@ Control TS:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Polling:
 | 
					Polling:
 | 
				
			||||||
  In order to poll uplink control block from MS, a special poll state and
 | 
					  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
 | 
					  frame number is stored at PDCH UL Controller. The scheduler reads that value
 | 
				
			||||||
  assign uplink ressource for other TBFs at that frame number.
 | 
					  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
 | 
					  On receipt of an Uplink RLCMAC block, it's the duty of each specific message
 | 
				
			||||||
  indicated by layer 1 interface. There are two ways of checking timeout:
 | 
					  handler to release the expectancies previously stored in the PDCH UL
 | 
				
			||||||
   - The received frame is bad (BFI).
 | 
					  Controller. After the specific handler returns, the generic return path will
 | 
				
			||||||
   - The GSM indicates that the block should have been already received.
 | 
					  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
 | 
					  Because polling requires uplink response from MS, the polling must be
 | 
				
			||||||
  performed at control TS.
 | 
					  performed at control TS.
 | 
				
			||||||
@@ -131,13 +135,21 @@ Polling:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Data structures of TBFs and PDCHs:
 | 
					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.
 | 
					  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
 | 
					   - uplink TBFs
 | 
				
			||||||
   - downlink TBFs
 | 
					   - downlink TBFs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -147,7 +159,7 @@ Data structures of TBFs and PDCHs:
 | 
				
			|||||||
   - an array of 8 PDCH structures, one for each assigned timeslot
 | 
					   - 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
 | 
					   - 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 uplink TBFs that are assigned to this PDCH
 | 
				
			||||||
   - an array of 32 downlink 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
 | 
					  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.
 | 
					  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
 | 
				
			||||||
							
								
								
									
										318
									
								
								include/osmocom/pcu/pcuif_proto.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								include/osmocom/pcu/pcuif_proto.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,318 @@
 | 
				
			|||||||
 | 
					#ifndef _PCUIF_PROTO_H
 | 
				
			||||||
 | 
					#define _PCUIF_PROTO_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/gsm/l1sap.h>
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/protocol/gsm_23_003.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PCU_SOCK_DEFAULT	"/tmp/pcu_bts"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PCU_IF_VERSION		0x0b
 | 
				
			||||||
 | 
					#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_2	0x11	/* confirm (using message id) */
 | 
				
			||||||
 | 
					#define PCU_IF_MSG_RACH_IND	0x22	/* receive RACH */
 | 
				
			||||||
 | 
					#define PCU_IF_MSG_INFO_IND	0x32	/* retrieve BTS info */
 | 
				
			||||||
 | 
					#define PCU_IF_MSG_E1_CCU_IND	0x33	/* retrieve E1 CCU comm. parameters */
 | 
				
			||||||
 | 
					#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_NEIGH_ADDR_REQ	0x81	/* Neighbor Address Resolution Request */
 | 
				
			||||||
 | 
					#define PCU_IF_MSG_NEIGH_ADDR_CNF	0x82	/* Neighbor Address Resolution Confirmation */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 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_PCH_2	0x08	/* assignment on PCH (confirmed using message id) */
 | 
				
			||||||
 | 
					#define PCU_IF_SAPI_AGCH_2	0x09	/* assignment on AGCH (confirmed using message id) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 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
 | 
				
			||||||
 | 
					#define PCU_IF_NUM_TRX 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 message id (instead of raw mac block) */
 | 
				
			||||||
 | 
					struct gsm_pcu_if_data_cnf {
 | 
				
			||||||
 | 
						uint8_t		sapi;
 | 
				
			||||||
 | 
						uint32_t	msg_id;
 | 
				
			||||||
 | 
					} __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_trx_ts {
 | 
				
			||||||
 | 
						uint8_t		tsc;
 | 
				
			||||||
 | 
						uint8_t		hopping;
 | 
				
			||||||
 | 
						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_trx_ts ts[8];
 | 
				
			||||||
 | 
					} __attribute__ ((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gsm_pcu_if_info_ind {
 | 
				
			||||||
 | 
						uint32_t	version;
 | 
				
			||||||
 | 
						uint32_t	flags;
 | 
				
			||||||
 | 
						struct gsm_pcu_if_info_trx trx[PCU_IF_NUM_TRX];	/* 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));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* E1 CCU connection parameters */
 | 
				
			||||||
 | 
					struct gsm_pcu_if_e1_ccu_ind {
 | 
				
			||||||
 | 
						/* GSM/GPRS air interface */
 | 
				
			||||||
 | 
						uint8_t trx_nr;
 | 
				
			||||||
 | 
						uint8_t ts_nr;
 | 
				
			||||||
 | 
						/* E1 line interface */
 | 
				
			||||||
 | 
						uint8_t e1_nr;
 | 
				
			||||||
 | 
						uint8_t e1_ts;
 | 
				
			||||||
 | 
						uint8_t e1_ts_ss;
 | 
				
			||||||
 | 
					} __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: NOTE: values must be network byte order here! ***/
 | 
				
			||||||
 | 
					/* Neighbor Address Resolution Request */
 | 
				
			||||||
 | 
					struct gsm_pcu_if_neigh_addr_req {
 | 
				
			||||||
 | 
						uint16_t	local_lac;
 | 
				
			||||||
 | 
						uint16_t	local_ci;
 | 
				
			||||||
 | 
						uint16_t	tgt_arfcn;
 | 
				
			||||||
 | 
						uint8_t		tgt_bsic;
 | 
				
			||||||
 | 
					} __attribute__ ((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Neighbor Address Resolution Confirmation */
 | 
				
			||||||
 | 
					struct gsm_pcu_if_neigh_addr_cnf {
 | 
				
			||||||
 | 
						struct gsm_pcu_if_neigh_addr_req orig_req;
 | 
				
			||||||
 | 
						uint8_t		err_code; /* 0 success, !0 failed & below unset */
 | 
				
			||||||
 | 
						/* RAI + CI (CGI-PS): */
 | 
				
			||||||
 | 
						struct __attribute__ ((packed)) {
 | 
				
			||||||
 | 
							uint16_t	mcc;
 | 
				
			||||||
 | 
							uint16_t	mnc;
 | 
				
			||||||
 | 
							uint8_t		mnc_3_digits;
 | 
				
			||||||
 | 
							uint16_t	lac;
 | 
				
			||||||
 | 
							uint8_t		rac;
 | 
				
			||||||
 | 
							uint16_t	cell_identity;
 | 
				
			||||||
 | 
						} cgi_ps;
 | 
				
			||||||
 | 
					} __attribute__ ((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Struct to send a (confirmed) IMMEDIATE ASSIGNMENT message via PCH. The struct is sent as a data request
 | 
				
			||||||
 | 
					 * (data_req) under SAPI PCU_IF_SAPI_PCH_2. */
 | 
				
			||||||
 | 
					struct gsm_pcu_if_pch {
 | 
				
			||||||
 | 
						/* message id as reference for confirmation */
 | 
				
			||||||
 | 
						uint32_t msg_id;
 | 
				
			||||||
 | 
						/* IMSI (to derive paging group) */
 | 
				
			||||||
 | 
						char imsi[OSMO_IMSI_BUF_SIZE];
 | 
				
			||||||
 | 
						/* GSM mac-block (with immediate assignment message) */
 | 
				
			||||||
 | 
						uint8_t data[GSM_MACBLOCK_LEN];
 | 
				
			||||||
 | 
						/* Set to true in case the receiving end must send a confirmation
 | 
				
			||||||
 | 
						 * when the MAC block (data) has been sent. */
 | 
				
			||||||
 | 
						bool confirm;
 | 
				
			||||||
 | 
					} __attribute__((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Struct to send a (confirmed) IMMEDIATE ASSIGNMENT message via AGCH. The struct is sent as a data request
 | 
				
			||||||
 | 
					 * (data_req) under SAPI PCU_IF_SAPI_AGCH_2. */
 | 
				
			||||||
 | 
					struct gsm_pcu_if_agch {
 | 
				
			||||||
 | 
						/* message id as reference for confirmation */
 | 
				
			||||||
 | 
						uint32_t msg_id;
 | 
				
			||||||
 | 
						/* GSM mac-block (with immediate assignment message) */
 | 
				
			||||||
 | 
						uint8_t data[GSM_MACBLOCK_LEN];
 | 
				
			||||||
 | 
						/* Set to true in case the receiving end must send a confirmation
 | 
				
			||||||
 | 
						 * when the MAC block (data) has been sent. */
 | 
				
			||||||
 | 
						bool confirm;
 | 
				
			||||||
 | 
					} __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	data_cnf2;
 | 
				
			||||||
 | 
							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_e1_ccu_ind	e1_ccu_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]
 | 
				
			||||||
							
								
								
									
										208
									
								
								src/Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								src/Makefile.am
									
									
									
									
									
								
							@@ -18,62 +18,204 @@
 | 
				
			|||||||
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					# 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_CXXFLAGS = -Wall -ldl -pthread
 | 
					AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOTRAU_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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if ENABLE_ER_E1_CCU
 | 
				
			||||||
 | 
					AM_CPPFLAGS += -DENABLE_DIRECT_PHY
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AM_CXXFLAGS = -Wall
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_LTLIBRARIES = libgprs.la
 | 
					noinst_LTLIBRARIES = libgprs.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
libgprs_la_SOURCES = \
 | 
					libgprs_la_SOURCES = \
 | 
				
			||||||
	gprs_debug.cpp \
 | 
						alloc_algo.cpp \
 | 
				
			||||||
	csn1.cpp \
 | 
						gprs_debug.c \
 | 
				
			||||||
	gsm_rlcmac.cpp \
 | 
						csn1.c \
 | 
				
			||||||
	gprs_bssgp_pcu.cpp \
 | 
						csn1_dec.c \
 | 
				
			||||||
	gprs_rlcmac.cpp \
 | 
						csn1_enc.c \
 | 
				
			||||||
	gprs_rlcmac_data.cpp \
 | 
						gsm_rlcmac.c \
 | 
				
			||||||
 | 
						gprs_bssgp_pcu.c \
 | 
				
			||||||
 | 
						gprs_bssgp_rim.c \
 | 
				
			||||||
 | 
						gprs_rlcmac.c \
 | 
				
			||||||
	gprs_rlcmac_sched.cpp \
 | 
						gprs_rlcmac_sched.cpp \
 | 
				
			||||||
	gsm_timer.cpp \
 | 
						gprs_rlcmac_meas.cpp \
 | 
				
			||||||
	bitvector.cpp \
 | 
						gprs_ms.c \
 | 
				
			||||||
 | 
						gprs_pcu.c \
 | 
				
			||||||
	pcu_l1_if.cpp \
 | 
						pcu_l1_if.cpp \
 | 
				
			||||||
	pcu_vty.c
 | 
						pcu_vty.c \
 | 
				
			||||||
 | 
						pcu_vty_functions.cpp \
 | 
				
			||||||
if ENABLE_SYSMOBTS
 | 
						mslot_class.c \
 | 
				
			||||||
libgprs_la_SOURCES += \
 | 
						nacc_fsm.c \
 | 
				
			||||||
	sysmo_sock.cpp
 | 
						neigh_cache.c \
 | 
				
			||||||
else
 | 
						tbf.cpp \
 | 
				
			||||||
libgprs_la_SOURCES += \
 | 
						tbf_fsm.c \
 | 
				
			||||||
	openbts_sock.cpp
 | 
						tbf_ul.cpp \
 | 
				
			||||||
endif
 | 
						tbf_ul_fsm.c \
 | 
				
			||||||
 | 
						tbf_ul_ack_fsm.c \
 | 
				
			||||||
noinst_PROGRAMS = \
 | 
						tbf_ul_ass_fsm.c \
 | 
				
			||||||
	RLCMACTest
 | 
						tbf_dl.cpp \
 | 
				
			||||||
 | 
						tbf_dl_fsm.c \
 | 
				
			||||||
 | 
						tbf_dl_ass_fsm.c \
 | 
				
			||||||
 | 
						bts.cpp \
 | 
				
			||||||
 | 
						bts_pch_timer.c \
 | 
				
			||||||
 | 
						pdch.cpp \
 | 
				
			||||||
 | 
						pdch_ul_controller.c \
 | 
				
			||||||
 | 
						encoding.cpp \
 | 
				
			||||||
 | 
						sba.c \
 | 
				
			||||||
 | 
						decoding.cpp \
 | 
				
			||||||
 | 
						llc.c \
 | 
				
			||||||
 | 
						rlc.cpp \
 | 
				
			||||||
 | 
						rlc_window.cpp \
 | 
				
			||||||
 | 
						rlc_window_dl.cpp \
 | 
				
			||||||
 | 
						rlc_window_ul.cpp \
 | 
				
			||||||
 | 
						pcuif_sock.c \
 | 
				
			||||||
 | 
						gprs_codel.c \
 | 
				
			||||||
 | 
						coding_scheme.c \
 | 
				
			||||||
 | 
						egprs_rlc_compression.cpp \
 | 
				
			||||||
 | 
						gprs_rlcmac_sched.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bin_PROGRAMS = \
 | 
					bin_PROGRAMS = \
 | 
				
			||||||
	osmo-pcu
 | 
						osmo-pcu
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					noinst_PROGRAMS =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_HEADERS = \
 | 
					noinst_HEADERS = \
 | 
				
			||||||
 | 
						alloc_algo.h \
 | 
				
			||||||
	gprs_debug.h \
 | 
						gprs_debug.h \
 | 
				
			||||||
	csn1.h \
 | 
						csn1.h \
 | 
				
			||||||
	gsm_rlcmac.h \
 | 
						gsm_rlcmac.h \
 | 
				
			||||||
	gprs_bssgp_pcu.h \
 | 
						gprs_bssgp_pcu.h \
 | 
				
			||||||
 | 
						gprs_bssgp_rim.h \
 | 
				
			||||||
	gprs_rlcmac.h \
 | 
						gprs_rlcmac.h \
 | 
				
			||||||
	pcuif_proto.h \
 | 
						gprs_ms.h \
 | 
				
			||||||
 | 
						gprs_pcu.h \
 | 
				
			||||||
	pcu_l1_if.h \
 | 
						pcu_l1_if.h \
 | 
				
			||||||
	gsm_timer.h \
 | 
						pcu_l1_if_phy.h \
 | 
				
			||||||
	bitvector.h \
 | 
						pcu_vty.h \
 | 
				
			||||||
	pcu_vty.h
 | 
						pcu_vty_functions.h \
 | 
				
			||||||
 | 
						mslot_class.h \
 | 
				
			||||||
RLCMACTest_SOURCES = RLCMACTest.cpp
 | 
						nacc_fsm.h \
 | 
				
			||||||
RLCMACTest_LDADD = \
 | 
						neigh_cache.h \
 | 
				
			||||||
	libgprs.la \
 | 
						tbf.h \
 | 
				
			||||||
	$(LIBOSMOCORE_LIBS) \
 | 
						tbf_fsm.h \
 | 
				
			||||||
	$(COMMON_LA)
 | 
						tbf_ul.h \
 | 
				
			||||||
 | 
						tbf_ul_ack_fsm.h \
 | 
				
			||||||
 | 
						tbf_ul_ass_fsm.h \
 | 
				
			||||||
 | 
						tbf_dl.h \
 | 
				
			||||||
 | 
						tbf_dl_ass_fsm.h \
 | 
				
			||||||
 | 
						bts.h \
 | 
				
			||||||
 | 
						bts_pch_timer.h \
 | 
				
			||||||
 | 
						pdch.h \
 | 
				
			||||||
 | 
						pdch_ul_controller.h \
 | 
				
			||||||
 | 
						encoding.h \
 | 
				
			||||||
 | 
						sba.h \
 | 
				
			||||||
 | 
						rlc.h \
 | 
				
			||||||
 | 
						rlc_window.h \
 | 
				
			||||||
 | 
						rlc_window_dl.h \
 | 
				
			||||||
 | 
						rlc_window_ul.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
 | 
					osmo_pcu_SOURCES = pcu_main.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if ENABLE_SYSMODSP
 | 
				
			||||||
 | 
					AM_CPPFLAGS += -I$(srcdir)/osmo-bts-sysmo -I$(SYSMOBTS_INCDIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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) \
 | 
				
			||||||
 | 
						-lrt \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if ENABLE_LC15BTS_PHY
 | 
				
			||||||
 | 
					AM_CPPFLAGS += $(LITECELL15_CFLAGS) -I$(srcdir)/osmo-bts-litecell15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if ENABLE_ER_E1_CCU
 | 
				
			||||||
 | 
					AM_CPPFLAGS += -I$(srcdir)/ericsson-rbs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					noinst_HEADERS += \
 | 
				
			||||||
 | 
						ericsson-rbs/er_ccu_if.h \
 | 
				
			||||||
 | 
						ericsson-rbs/er_ccu_descr.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					osmo_pcu_SOURCES += \
 | 
				
			||||||
 | 
						ericsson-rbs/er_ccu_l1_if.c \
 | 
				
			||||||
 | 
						ericsson-rbs/er_ccu_if.c
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
osmo_pcu_LDADD = \
 | 
					osmo_pcu_LDADD = \
 | 
				
			||||||
	libgprs.la \
 | 
						libgprs.la \
 | 
				
			||||||
	$(LIBOSMOGB_LIBS) \
 | 
						$(LIBOSMOGB_LIBS) \
 | 
				
			||||||
	$(LIBOSMOCORE_LIBS) \
 | 
						$(LIBOSMOCORE_LIBS) \
 | 
				
			||||||
 | 
						$(LIBOSMOCTRL_LIBS) \
 | 
				
			||||||
	$(LIBOSMOGSM_LIBS) \
 | 
						$(LIBOSMOGSM_LIBS) \
 | 
				
			||||||
	$(COMMON_LA)
 | 
						$(LIBOSMOABIS_LIBS) \
 | 
				
			||||||
 | 
						$(LIBOSMOTRAU_LIBS) \
 | 
				
			||||||
 | 
						-lrt \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#MOSTLYCLEANFILES += testSource testDestination
 | 
					#MOSTLYCLEANFILES += testSource testDestination
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										904
									
								
								src/alloc_algo.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										904
									
								
								src/alloc_algo.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,904 @@
 | 
				
			|||||||
 | 
					/* alloc_algo.cpp
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
				
			||||||
 | 
					 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
				
			||||||
 | 
					 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
				
			||||||
 | 
					 * Copyright (C) 2022 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <gprs_rlcmac.h>
 | 
				
			||||||
 | 
					#include <gprs_debug.h>
 | 
				
			||||||
 | 
					#include <bts.h>
 | 
				
			||||||
 | 
					#include <tbf.h>
 | 
				
			||||||
 | 
					#include <tbf_ul.h>
 | 
				
			||||||
 | 
					#include <pdch.h>
 | 
				
			||||||
 | 
					#include <gprs_ms.h>
 | 
				
			||||||
 | 
					#include <pcu_utils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <values.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#include "mslot_class.h"
 | 
				
			||||||
 | 
					#include "alloc_algo.h"
 | 
				
			||||||
 | 
					#include <osmocom/core/linuxlist.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Consider a PDCH as idle if has at most this number of TBFs assigned to it */
 | 
				
			||||||
 | 
					#define PDCH_IDLE_TBF_THRESH	1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOGPSL(req, level, fmt, args...) LOGP(DRLCMAC, level, "[%s] " fmt, \
 | 
				
			||||||
 | 
										      (req->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL", ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOGPAL(req, kind, level, fmt, args...) LOGPSL(req, level, \
 | 
				
			||||||
 | 
													     "algo %s <%s> (suggested TRX: %d): " fmt, \
 | 
				
			||||||
 | 
													     kind, req->single ? "single" : "multi", req->use_trx, ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static char *set_flag_chars(char *buf, uint8_t val, char set_char, char unset_char = 0)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < 8; i += 1, val = val >> 1) {
 | 
				
			||||||
 | 
							if (val & 1)
 | 
				
			||||||
 | 
								buf[i] = set_char;
 | 
				
			||||||
 | 
							else if (unset_char)
 | 
				
			||||||
 | 
								buf[i] = unset_char;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint8_t find_possible_pdchs(const struct gprs_rlcmac_trx *trx, uint8_t max_slots, uint8_t mask,
 | 
				
			||||||
 | 
									   const char *mask_reason = NULL)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned ts;
 | 
				
			||||||
 | 
						uint8_t valid_ts_set = 0;
 | 
				
			||||||
 | 
						int8_t last_tsc = -1; /* must be signed */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
 | 
				
			||||||
 | 
							const struct gprs_rlcmac_pdch *pdch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pdch = &trx->pdch[ts];
 | 
				
			||||||
 | 
							if (!pdch->is_enabled()) {
 | 
				
			||||||
 | 
								LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, because "
 | 
				
			||||||
 | 
									"not enabled\n", ts);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (((1 << ts) & mask) == 0) {
 | 
				
			||||||
 | 
								if (mask_reason)
 | 
				
			||||||
 | 
									LOGP(DRLCMAC, LOGL_DEBUG,
 | 
				
			||||||
 | 
										"- Skipping TS %d, because %s\n",
 | 
				
			||||||
 | 
										ts, mask_reason);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (max_slots > 1) {
 | 
				
			||||||
 | 
								/* check if TSC changes, see TS 45.002, 6.4.2 */
 | 
				
			||||||
 | 
								if (last_tsc < 0)
 | 
				
			||||||
 | 
									last_tsc = pdch->tsc;
 | 
				
			||||||
 | 
								else if (last_tsc != pdch->tsc) {
 | 
				
			||||||
 | 
									LOGP(DRLCMAC, LOGL_ERROR,
 | 
				
			||||||
 | 
										"Skipping TS %d of TRX=%d, because it "
 | 
				
			||||||
 | 
										"has different TSC than lower TS of TRX. "
 | 
				
			||||||
 | 
										"In order to allow multislot, all "
 | 
				
			||||||
 | 
										"slots must be configured with the same "
 | 
				
			||||||
 | 
										"TSC!\n", ts, trx->trx_no);
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							valid_ts_set |= 1 << ts;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return valid_ts_set;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int compute_usage_by_num_tbfs(const struct gprs_rlcmac_pdch *pdch, enum gprs_rlcmac_tbf_direction dir)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return pdch->num_tbfs(dir);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int compute_usage_by_reservation(const struct gprs_rlcmac_pdch *pdch, enum gprs_rlcmac_tbf_direction)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
							pdch->num_reserved(GPRS_RLCMAC_DL_TBF) +
 | 
				
			||||||
 | 
							pdch->num_reserved(GPRS_RLCMAC_UL_TBF);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int compute_usage_for_algo_a(const struct gprs_rlcmac_pdch *pdch, enum gprs_rlcmac_tbf_direction dir)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int usage =
 | 
				
			||||||
 | 
							pdch->num_tbfs(GPRS_RLCMAC_DL_TBF) +
 | 
				
			||||||
 | 
							pdch->num_tbfs(GPRS_RLCMAC_UL_TBF) +
 | 
				
			||||||
 | 
							compute_usage_by_reservation(pdch, dir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pdch->assigned_tfi(reverse(dir)) == NO_FREE_TFI)
 | 
				
			||||||
 | 
							/* No TFI in the opposite direction, avoid it */
 | 
				
			||||||
 | 
							usage += 32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return usage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Return the TS which corresponds to least busy PDCH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] trx Pointer to TRX object
 | 
				
			||||||
 | 
					 *  \param[in] dir TBF direction
 | 
				
			||||||
 | 
					 *  \param[in] mask set of available timeslots
 | 
				
			||||||
 | 
					 *  \param[in] fn Function pointer to function which computes number of associated TBFs
 | 
				
			||||||
 | 
					 *  \param[out] free_tfi Free TFI
 | 
				
			||||||
 | 
					 *  \param[out] free_usf Free USF
 | 
				
			||||||
 | 
					 *  \returns TS number or -1 if unable to find
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int find_least_busy_pdch(const struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t mask,
 | 
				
			||||||
 | 
									int (*fn)(const struct gprs_rlcmac_pdch *, enum gprs_rlcmac_tbf_direction dir),
 | 
				
			||||||
 | 
									int *free_tfi = NULL, int *free_usf = NULL)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned ts;
 | 
				
			||||||
 | 
						int min_used = INT_MAX;
 | 
				
			||||||
 | 
						int min_ts = -1;
 | 
				
			||||||
 | 
						int min_tfi = -1;
 | 
				
			||||||
 | 
						int min_usf = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
 | 
				
			||||||
 | 
							const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
 | 
				
			||||||
 | 
							int num_tbfs;
 | 
				
			||||||
 | 
							int usf = -1; /* must be signed */
 | 
				
			||||||
 | 
							int tfi = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (((1 << ts) & mask) == 0)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							num_tbfs = fn(pdch, dir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (num_tbfs < min_used) {
 | 
				
			||||||
 | 
								/* We have found a candidate */
 | 
				
			||||||
 | 
								/* Make sure that a TFI is available */
 | 
				
			||||||
 | 
								if (free_tfi) {
 | 
				
			||||||
 | 
									tfi = find_free_tfi(pdch->assigned_tfi(dir));
 | 
				
			||||||
 | 
									if (tfi < 0) {
 | 
				
			||||||
 | 
										LOGP(DRLCMAC, LOGL_DEBUG,
 | 
				
			||||||
 | 
											"- Skipping TS %d, because "
 | 
				
			||||||
 | 
											"no TFI available\n", ts);
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								/* Make sure that an USF is available */
 | 
				
			||||||
 | 
								if (dir == GPRS_RLCMAC_UL_TBF) {
 | 
				
			||||||
 | 
									usf = find_free_usf(pdch->assigned_usf());
 | 
				
			||||||
 | 
									if (usf < 0) {
 | 
				
			||||||
 | 
										LOGP(DRLCMAC, LOGL_DEBUG,
 | 
				
			||||||
 | 
											"- Skipping TS %d, because "
 | 
				
			||||||
 | 
											"no USF available\n", ts);
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (min_ts >= 0)
 | 
				
			||||||
 | 
									LOGP(DRLCMAC, LOGL_DEBUG,
 | 
				
			||||||
 | 
										"- Skipping TS %d, because "
 | 
				
			||||||
 | 
										"num TBFs %d > %d\n",
 | 
				
			||||||
 | 
										min_ts, min_used, num_tbfs);
 | 
				
			||||||
 | 
								min_used = num_tbfs;
 | 
				
			||||||
 | 
								min_ts = ts;
 | 
				
			||||||
 | 
								min_tfi = tfi;
 | 
				
			||||||
 | 
								min_usf = usf;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								LOGP(DRLCMAC, LOGL_DEBUG,
 | 
				
			||||||
 | 
									"- Skipping TS %d, because "
 | 
				
			||||||
 | 
									"num TBFs %d >= %d\n",
 | 
				
			||||||
 | 
									ts, num_tbfs, min_used);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (min_ts < 0)
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (free_tfi)
 | 
				
			||||||
 | 
							*free_tfi = min_tfi;
 | 
				
			||||||
 | 
						if (free_usf)
 | 
				
			||||||
 | 
							*free_usf = min_usf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return min_ts;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int find_trx(const struct alloc_resources_req *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned trx_no;
 | 
				
			||||||
 | 
						unsigned ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We must use the TRX currently actively used by an MS */
 | 
				
			||||||
 | 
						if (req->ms && ms_current_trx(req->ms))
 | 
				
			||||||
 | 
							return ms_current_trx(req->ms)->trx_no;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (req->use_trx >= 0 && req->use_trx < 8)
 | 
				
			||||||
 | 
							return req->use_trx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Find the first TRX that has a PDCH with a free UL and DL TFI */
 | 
				
			||||||
 | 
						for (trx_no = 0; trx_no < ARRAY_SIZE(req->bts->trx); trx_no += 1) {
 | 
				
			||||||
 | 
							const struct gprs_rlcmac_trx *trx = &req->bts->trx[trx_no];
 | 
				
			||||||
 | 
							for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
 | 
				
			||||||
 | 
								const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
 | 
				
			||||||
 | 
								if (!pdch->is_enabled())
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (pdch->assigned_tfi(GPRS_RLCMAC_UL_TBF) == NO_FREE_TFI)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (pdch->assigned_tfi(GPRS_RLCMAC_DL_TBF) == NO_FREE_TFI)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return trx_no;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return -EBUSY;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool idle_pdch_avail(const struct gprs_rlcmac_bts *bts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned trx_no;
 | 
				
			||||||
 | 
						unsigned ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Find the first PDCH with an unused DL TS */
 | 
				
			||||||
 | 
						for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); trx_no += 1) {
 | 
				
			||||||
 | 
							const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
 | 
				
			||||||
 | 
							for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
 | 
				
			||||||
 | 
								const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
 | 
				
			||||||
 | 
								if (!pdch->is_enabled())
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (pdch->num_tbfs(GPRS_RLCMAC_DL_TBF) > PDCH_IDLE_TBF_THRESH)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Return free TFI
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] req Contains all the requested params
 | 
				
			||||||
 | 
					 *  \param[out] trx_no_ TRX number on which TFI was found
 | 
				
			||||||
 | 
					 *  \returns negative error code or 0 on success
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int tfi_find_free(const struct alloc_resources_req *req, uint8_t *trx_no_)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct gprs_rlcmac_trx *trx;
 | 
				
			||||||
 | 
						int tfi;
 | 
				
			||||||
 | 
						int8_t use_trx = req->use_trx;
 | 
				
			||||||
 | 
						uint8_t trx_no;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If MS is already doing stuff on a TRX, set use_trx to it: */
 | 
				
			||||||
 | 
						if ((trx = ms_current_trx(req->ms))) {
 | 
				
			||||||
 | 
							if (use_trx >= 0 && use_trx != trx->trx_no) {
 | 
				
			||||||
 | 
								LOGP(DRLCMAC, LOGL_ERROR, "- Requested incompatible TRX %d (current is %d)\n",
 | 
				
			||||||
 | 
								     req->use_trx, trx->trx_no);
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							use_trx = trx->trx_no;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tfi = bts_tfi_find_free(req->bts, req->direction, &trx_no, use_trx);
 | 
				
			||||||
 | 
						if (tfi < 0)
 | 
				
			||||||
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (trx_no_)
 | 
				
			||||||
 | 
							*trx_no_ = trx_no;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return tfi;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Slot Allocation: Algorithm A
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Assign single slot for uplink and downlink
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] req Contains all the requested params
 | 
				
			||||||
 | 
					 *  \param[out] res The resolution/response for the allocation request
 | 
				
			||||||
 | 
					 *  \returns negative error code or 0 on success
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int alloc_algorithm_a(const struct alloc_resources_req *req,
 | 
				
			||||||
 | 
							      struct alloc_resources_res *res)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ts = -1;
 | 
				
			||||||
 | 
						uint8_t ul_slots, dl_slots;
 | 
				
			||||||
 | 
						int trx_no;
 | 
				
			||||||
 | 
						int tfi = -1;
 | 
				
			||||||
 | 
						int usf = -1;
 | 
				
			||||||
 | 
						uint8_t mask = 0xff;
 | 
				
			||||||
 | 
						const char *mask_reason = NULL;
 | 
				
			||||||
 | 
						gprs_rlcmac_trx *trx = ms_current_trx(req->ms);
 | 
				
			||||||
 | 
						struct gprs_rlcmac_pdch *first_common_ts = ms_first_common_ts(req->ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPAL(req, "A", LOGL_DEBUG, "Alloc start\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trx_no = find_trx(req);
 | 
				
			||||||
 | 
						if (trx_no < 0) {
 | 
				
			||||||
 | 
							LOGPAL(req, "A", LOGL_NOTICE,
 | 
				
			||||||
 | 
							       "failed to find a usable TRX (TFI exhausted)\n");
 | 
				
			||||||
 | 
							return trx_no;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!trx)
 | 
				
			||||||
 | 
							trx = &req->bts->trx[trx_no];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dl_slots = ms_reserved_dl_slots(req->ms);
 | 
				
			||||||
 | 
						ul_slots = ms_reserved_ul_slots(req->ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (first_common_ts) {
 | 
				
			||||||
 | 
							mask_reason = "need to reuse TS";
 | 
				
			||||||
 | 
							mask = 1 << first_common_ts->ts_no;
 | 
				
			||||||
 | 
						} else if (dl_slots || ul_slots) {
 | 
				
			||||||
 | 
							mask_reason = "need to use a reserved common TS";
 | 
				
			||||||
 | 
							mask = dl_slots & ul_slots;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mask = find_possible_pdchs(trx, 1, mask, mask_reason);
 | 
				
			||||||
 | 
						if (!mask)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ts = find_least_busy_pdch(trx, req->direction, mask,
 | 
				
			||||||
 | 
							compute_usage_for_algo_a,
 | 
				
			||||||
 | 
							&tfi, &usf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (req->direction == GPRS_RLCMAC_UL_TBF && usf < 0) {
 | 
				
			||||||
 | 
							LOGPAL(req, "A", LOGL_NOTICE,
 | 
				
			||||||
 | 
							       "failed to allocate a TS, no USF available\n");
 | 
				
			||||||
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ts < 0) {
 | 
				
			||||||
 | 
							LOGPAL(req, "A", LOGL_NOTICE,
 | 
				
			||||||
 | 
							       "failed to allocate a TS, no TFI available\n");
 | 
				
			||||||
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res->trx = trx;
 | 
				
			||||||
 | 
						res->first_common_ts = &trx->pdch[ts];
 | 
				
			||||||
 | 
						res->reserved_ul_slots = 1 << ts;
 | 
				
			||||||
 | 
						res->reserved_dl_slots = 1 << ts;
 | 
				
			||||||
 | 
						res->ass_slots_mask = 1 << ts;
 | 
				
			||||||
 | 
						res->upgrade_to_multislot = false;
 | 
				
			||||||
 | 
						res->tfi = tfi;
 | 
				
			||||||
 | 
						res->usf[ts] = usf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bts_do_rate_ctr_inc(req->bts, CTR_TBF_ALLOC_ALGO_A);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Compute capacity of a given TRX
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] trx Pointer to TRX object
 | 
				
			||||||
 | 
					 *  \param[in] rx_window Receive window
 | 
				
			||||||
 | 
					 *  \param[in] tx_window Transmit window
 | 
				
			||||||
 | 
					 *  \returns non-negative capacity
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline unsigned compute_capacity(const struct gprs_rlcmac_trx *trx, int rx_window, int tx_window)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct gprs_rlcmac_pdch *pdch;
 | 
				
			||||||
 | 
						unsigned ts, capacity = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
 | 
				
			||||||
 | 
							pdch = &trx->pdch[ts];
 | 
				
			||||||
 | 
							if (rx_window & (1 << ts))
 | 
				
			||||||
 | 
								capacity += OSMO_MAX(32 - pdch->num_reserved(GPRS_RLCMAC_DL_TBF), 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Only consider common slots for UL */
 | 
				
			||||||
 | 
							if (tx_window & rx_window & (1 << ts)) {
 | 
				
			||||||
 | 
								if (find_free_usf(pdch->assigned_usf()) >= 0)
 | 
				
			||||||
 | 
									capacity += OSMO_MAX(32 - pdch->num_reserved(GPRS_RLCMAC_UL_TBF), 1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return capacity;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Decide if a given slot should be skipped by multislot allocator
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] ms_class Pointer to MS Class object
 | 
				
			||||||
 | 
					 *  \param[in] check_tr Flag indicating whether we should check for Tra or Tta parameters for a given MS class
 | 
				
			||||||
 | 
					 *  \param[in] rx_window Receive window
 | 
				
			||||||
 | 
					 *  \param[in] tx_window Transmit window
 | 
				
			||||||
 | 
					 *  \param[in,out] checked_rx array with already checked RX timeslots
 | 
				
			||||||
 | 
					 *  \returns true if the slot should be skipped, false otherwise
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static bool skip_slot(uint8_t mslot_class, bool check_tr,
 | 
				
			||||||
 | 
							      int16_t rx_window, int16_t tx_window,
 | 
				
			||||||
 | 
							      uint32_t *checked_rx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint8_t common_slot_count, req_common_slots,
 | 
				
			||||||
 | 
							rx_slot_count = pcu_bitcount(rx_window),
 | 
				
			||||||
 | 
							tx_slot_count = pcu_bitcount(tx_window);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check compliance with TS 45.002, table 6.4.2.2.1 */
 | 
				
			||||||
 | 
						/* Whether to skip this round doesn not only depend on the bit
 | 
				
			||||||
 | 
						 * sets but also on check_tr. Therefore this check must be done
 | 
				
			||||||
 | 
						 * before doing the mslot_test_and_set_bit shortcut. */
 | 
				
			||||||
 | 
						if (mslot_class_get_type(mslot_class) == 1) {
 | 
				
			||||||
 | 
							uint16_t slot_sum = rx_slot_count + tx_slot_count;
 | 
				
			||||||
 | 
							/* Assume down + up / dynamic.
 | 
				
			||||||
 | 
							 * TODO: For ext-dynamic, down only, up only add more cases.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (slot_sum <= 6 && tx_slot_count < 3) {
 | 
				
			||||||
 | 
								if (!check_tr)
 | 
				
			||||||
 | 
									return true; /* Skip Tta */
 | 
				
			||||||
 | 
							} else if (slot_sum > 6 && tx_slot_count < 3) {
 | 
				
			||||||
 | 
								if (check_tr)
 | 
				
			||||||
 | 
									return true; /* Skip Tra */
 | 
				
			||||||
 | 
							} else
 | 
				
			||||||
 | 
								return true; /* No supported row in TS 45.002, table 6.4.2.2.1. */
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Avoid repeated RX combination check */
 | 
				
			||||||
 | 
						if (mslot_test_and_set_bit(checked_rx, rx_window))
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check number of common slots according to TS 45.002, §6.4.2.2 */
 | 
				
			||||||
 | 
						common_slot_count = pcu_bitcount(tx_window & rx_window);
 | 
				
			||||||
 | 
						req_common_slots = OSMO_MIN(tx_slot_count, rx_slot_count);
 | 
				
			||||||
 | 
						if (mslot_class_get_type(mslot_class) == 1)
 | 
				
			||||||
 | 
							req_common_slots = OSMO_MIN(req_common_slots, 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (req_common_slots != common_slot_count)
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Find set of slots available for allocation while taking MS class into account
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] trx Pointer to TRX object
 | 
				
			||||||
 | 
					 *  \param[in] mslot_class The multislot class
 | 
				
			||||||
 | 
					 *  \param[in,out] ul_slots set of UL timeslots
 | 
				
			||||||
 | 
					 *  \param[in,out] dl_slots set of DL timeslots
 | 
				
			||||||
 | 
					 *  \returns negative error code or 0 on success
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const uint8_t Rx = mslot_class_get_rx(mslot_class),   /* Max number of Rx slots */
 | 
				
			||||||
 | 
							      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);
 | 
				
			||||||
 | 
						uint8_t max_slots, num_rx, num_tx, mask_sel, pdch_slots, ul_ts, dl_ts;
 | 
				
			||||||
 | 
						int16_t rx_window, tx_window;
 | 
				
			||||||
 | 
						char slot_info[9] = {0};
 | 
				
			||||||
 | 
						int max_capacity = -1;
 | 
				
			||||||
 | 
						uint8_t max_ul_slots = 0, max_dl_slots = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mslot_class)
 | 
				
			||||||
 | 
							LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm B) for class %d\n",
 | 
				
			||||||
 | 
							     mslot_class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (Tx == MS_NA) {
 | 
				
			||||||
 | 
							LOGP(DRLCMAC, LOGL_NOTICE, "Multislot class %d not applicable.\n",
 | 
				
			||||||
 | 
							     mslot_class);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						max_slots = OSMO_MAX(Rx, Tx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (*dl_slots == 0)
 | 
				
			||||||
 | 
							*dl_slots = 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (*ul_slots == 0)
 | 
				
			||||||
 | 
							*ul_slots = 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pdch_slots = find_possible_pdchs(trx, max_slots, 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*dl_slots &= pdch_slots;
 | 
				
			||||||
 | 
						*ul_slots &= pdch_slots;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DRLCMAC, LOGL_DEBUG, "- Possible DL/UL slots: (TS=0)\"%s\"(TS=7)\n",
 | 
				
			||||||
 | 
							set_flag_chars(set_flag_chars(set_flag_chars(slot_info,
 | 
				
			||||||
 | 
									*dl_slots, 'D', '.'),
 | 
				
			||||||
 | 
									*ul_slots, 'U'),
 | 
				
			||||||
 | 
									*ul_slots & *dl_slots, 'C'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check for each UL (TX) slot */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Iterate through possible numbers of TX slots */
 | 
				
			||||||
 | 
						for (num_tx = 1; num_tx <= Tx; num_tx += 1) {
 | 
				
			||||||
 | 
							uint16_t tx_valid_win = (1 << num_tx) - 1;
 | 
				
			||||||
 | 
							uint8_t rx_mask[MASK_TR + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mslot_fill_rx_mask(mslot_class, num_tx, rx_mask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Rotate group of TX slots: UUU-----, -UUU----, ..., UU-----U */
 | 
				
			||||||
 | 
							for (ul_ts = 0; ul_ts < 8; ul_ts += 1, tx_valid_win <<= 1) {
 | 
				
			||||||
 | 
								uint16_t rx_valid_win;
 | 
				
			||||||
 | 
								uint32_t checked_rx[256/32] = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Wrap valid window */
 | 
				
			||||||
 | 
								tx_valid_win = mslot_wrap_window(tx_valid_win);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* for multislot type 1: don't split the window to wrap around.
 | 
				
			||||||
 | 
								 * E.g. 'UU-----U' is invalid for a 4 TN window. Except 8 TN window.
 | 
				
			||||||
 | 
								 * See 45.002 B.1 */
 | 
				
			||||||
 | 
								if (Type == 1 && num_tx < 8 &&
 | 
				
			||||||
 | 
										tx_valid_win & (1 << 0) && tx_valid_win & (1 << 7))
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								tx_window = tx_valid_win;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Filter out unavailable slots */
 | 
				
			||||||
 | 
								tx_window &= *ul_slots;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Skip if the the first TS (ul_ts) is not in the set */
 | 
				
			||||||
 | 
								if ((tx_window & (1 << ul_ts)) == 0)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Skip if the the last TS (ul_ts+num_tx-1) is not in the set */
 | 
				
			||||||
 | 
								if ((tx_window & (1 << ((ul_ts+num_tx-1) % 8))) == 0)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								num_rx = OSMO_MIN(Rx, Sum - num_tx);
 | 
				
			||||||
 | 
								rx_valid_win = (1 << num_rx) - 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Rotate group of RX slots: DDD-----, -DDD----, ..., DD-----D */
 | 
				
			||||||
 | 
								for (dl_ts = 0; dl_ts < 8; dl_ts += 1, rx_valid_win <<= 1) {
 | 
				
			||||||
 | 
									/* Wrap valid window */
 | 
				
			||||||
 | 
									rx_valid_win = (rx_valid_win | rx_valid_win >> 8) & 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/* for multislot type 1: don't split the window to wrap around.
 | 
				
			||||||
 | 
									 * E.g. 'DD-----D' is invalid for a 4 TN window. Except 8 TN window.
 | 
				
			||||||
 | 
									 * See 45.002 B.1 */
 | 
				
			||||||
 | 
									if (Type == 1 && num_rx < 8 &&
 | 
				
			||||||
 | 
											(rx_valid_win & (1 << 0)) && (rx_valid_win & (1 << 7)))
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/* Validate with both Tta/Ttb/Trb and Ttb/Tra/Trb */
 | 
				
			||||||
 | 
									for (mask_sel = MASK_TT; mask_sel <= MASK_TR; mask_sel += 1) {
 | 
				
			||||||
 | 
										int capacity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										rx_window = mslot_filter_bad(rx_mask[mask_sel], ul_ts, *dl_slots, rx_valid_win);
 | 
				
			||||||
 | 
										if (rx_window < 0)
 | 
				
			||||||
 | 
											continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (skip_slot(mslot_class, mask_sel != MASK_TT, rx_window, tx_window, checked_rx))
 | 
				
			||||||
 | 
											continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										/* Compute capacity */
 | 
				
			||||||
 | 
										capacity = compute_capacity(trx, rx_window, tx_window);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ENABLE_TS_ALLOC_DEBUG
 | 
				
			||||||
 | 
										LOGP(DRLCMAC, LOGL_DEBUG,
 | 
				
			||||||
 | 
											"- Considering DL/UL slots: (TS=0)\"%s\"(TS=7), "
 | 
				
			||||||
 | 
											"capacity = %d\n",
 | 
				
			||||||
 | 
											set_flag_chars(set_flag_chars(set_flag_chars(set_flag_chars(
 | 
				
			||||||
 | 
													slot_info,
 | 
				
			||||||
 | 
													rx_bad, 'x', '.'),
 | 
				
			||||||
 | 
													rx_window, 'D'),
 | 
				
			||||||
 | 
													tx_window, 'U'),
 | 
				
			||||||
 | 
													rx_window & tx_window, 'C'),
 | 
				
			||||||
 | 
											capacity);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (capacity <= max_capacity)
 | 
				
			||||||
 | 
											continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										max_capacity = capacity;
 | 
				
			||||||
 | 
										max_ul_slots = tx_window;
 | 
				
			||||||
 | 
										max_dl_slots = rx_window;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!max_ul_slots || !max_dl_slots) {
 | 
				
			||||||
 | 
							LOGP(DRLCMAC, LOGL_NOTICE,
 | 
				
			||||||
 | 
								"No valid UL/DL slot combination found\n");
 | 
				
			||||||
 | 
							bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_SLOT_COMBI);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*ul_slots = max_ul_slots;
 | 
				
			||||||
 | 
						*dl_slots = max_dl_slots;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Count used bits in slots and reserved_slots bitmasks
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] slots Timeslots in use
 | 
				
			||||||
 | 
					 *  \param[in] reserved_slots Reserved timeslots
 | 
				
			||||||
 | 
					 *  \param[out] slotcount Number of TS in use
 | 
				
			||||||
 | 
					 *  \param[out] reserve_count Number of reserved TS
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void count_slots(uint8_t slots, uint8_t reserved_slots, uint8_t *slotcount, uint8_t *reserve_count)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(*slotcount) = pcu_bitcount(slots);
 | 
				
			||||||
 | 
						(*reserve_count) = pcu_bitcount(reserved_slots);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Return slot mask with single TS from a given UL/DL set according to TBF's direction, ts pointer is set to that TS
 | 
				
			||||||
 | 
					 * number or to negative value on error
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] trx Pointer to TRX object
 | 
				
			||||||
 | 
					 *  \param[in] direction Direction of the TBF to allocate
 | 
				
			||||||
 | 
					 *  \param[in] dl_slots set of DL timeslots
 | 
				
			||||||
 | 
					 *  \param[in] ul_slots set of UL timeslots
 | 
				
			||||||
 | 
					 *  \param[in] ts corresponding TS or -1 for autoselection
 | 
				
			||||||
 | 
					 *  \returns slot mask with single UL or DL timeslot number if possible
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static uint8_t get_single_ts(const gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction direction, uint8_t dl_slots, uint8_t ul_slots,
 | 
				
			||||||
 | 
								     int ts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint8_t ret = dl_slots & ul_slots; /* Make sure to consider the first common slot only */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ts < 0)
 | 
				
			||||||
 | 
							ts = find_least_busy_pdch(trx, direction, ret, compute_usage_by_num_tbfs, NULL, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ts < 0)
 | 
				
			||||||
 | 
							return ffs(ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret & (1 << ts);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Find set of timeslots available for allocation
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] req Contains all the requested params
 | 
				
			||||||
 | 
					 *  \param[in] trx Pointer to TRX object
 | 
				
			||||||
 | 
					 *  \param[in] ul_slots set of UL timeslots
 | 
				
			||||||
 | 
					 *  \param[in] dl_slots set of DL timeslots
 | 
				
			||||||
 | 
					 *  \param[in] reserved_ul_slots set of reserved UL timeslots
 | 
				
			||||||
 | 
					 *  \param[in] reserved_dl_slots set of reserved DL timeslots
 | 
				
			||||||
 | 
					 *  \param[in] first_common_ts First TS common for both UL and DL or -1 if unknown
 | 
				
			||||||
 | 
					 *  \returns negative error code or selected TS on success
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int tbf_select_slot_set(const struct alloc_resources_req *req, const gprs_rlcmac_trx *trx,
 | 
				
			||||||
 | 
								       uint8_t ul_slots, uint8_t dl_slots,
 | 
				
			||||||
 | 
								       uint8_t reserved_ul_slots, uint8_t reserved_dl_slots,
 | 
				
			||||||
 | 
								       int8_t first_common_ts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						bool is_ul = req->direction == GPRS_RLCMAC_UL_TBF;
 | 
				
			||||||
 | 
						uint8_t sl = is_ul ? ul_slots : dl_slots;
 | 
				
			||||||
 | 
						char slot_info[9] = { 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (req->single)
 | 
				
			||||||
 | 
							sl = get_single_ts(trx, req->direction, dl_slots, ul_slots, first_common_ts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!sl) {
 | 
				
			||||||
 | 
							LOGP(DRLCMAC, LOGL_NOTICE, "No %s slots available\n",
 | 
				
			||||||
 | 
							     is_ul ? "uplink" : "downlink");
 | 
				
			||||||
 | 
							bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_SLOT_AVAIL);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (is_ul) {
 | 
				
			||||||
 | 
							snprintf(slot_info, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(reserved_ul_slots, 'u'));
 | 
				
			||||||
 | 
							masked_override_with(slot_info, sl, 'U');
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							snprintf(slot_info, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(reserved_dl_slots, 'd'));
 | 
				
			||||||
 | 
							masked_override_with(slot_info, sl, 'D');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPC(DRLCMAC, LOGL_DEBUG, "Selected %s slots: (TS=0)\"%s\"(TS=7), %s\n",
 | 
				
			||||||
 | 
						      is_ul ? "UL" : "DL",
 | 
				
			||||||
 | 
						      slot_info, req->single ? "single" : "multi");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return sl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Allocate USF according to a given UL TS mapping
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] trx Pointer to TRX object
 | 
				
			||||||
 | 
					 *  \param[in] selected_ul_slots set of UL timeslots selected for allocation
 | 
				
			||||||
 | 
					 *  \param[in] dl_slots set of DL timeslots
 | 
				
			||||||
 | 
					 *  \param[out] usf array for allocated USF
 | 
				
			||||||
 | 
					 *  \returns updated UL TS mask or negative on error
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int allocate_usf(const gprs_rlcmac_trx *trx, uint8_t selected_ul_slots, uint8_t dl_slots,
 | 
				
			||||||
 | 
								int *usf_list)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint8_t ul_slots = selected_ul_slots & dl_slots;
 | 
				
			||||||
 | 
						unsigned int ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
 | 
				
			||||||
 | 
							const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
 | 
				
			||||||
 | 
							int8_t free_usf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (((1 << ts) & ul_slots) == 0)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							free_usf = find_free_usf(pdch->assigned_usf());
 | 
				
			||||||
 | 
							if (free_usf < 0) {
 | 
				
			||||||
 | 
								LOGP(DRLCMAC, LOGL_DEBUG,
 | 
				
			||||||
 | 
									"- Skipping TS %d, because "
 | 
				
			||||||
 | 
									"no USF available\n", ts);
 | 
				
			||||||
 | 
								ul_slots &= (~(1 << ts)) & 0xff;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							usf_list[ts] = free_usf;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ul_slots) {
 | 
				
			||||||
 | 
							LOGP(DRLCMAC, LOGL_NOTICE, "No USF available\n");
 | 
				
			||||||
 | 
							bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_USF);
 | 
				
			||||||
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ul_slots;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Slot Allocation: Algorithm B
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Assign as many downlink slots as possible.
 | 
				
			||||||
 | 
					 * Assign one uplink slot. (With free USF)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] req Contains all the requested params
 | 
				
			||||||
 | 
					 *  \param[out] res The resolution/response for the allocation request
 | 
				
			||||||
 | 
					 *  \returns negative error code or 0 on success
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int alloc_algorithm_b(const struct alloc_resources_req *req,
 | 
				
			||||||
 | 
							      struct alloc_resources_res *res)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint8_t dl_slots;
 | 
				
			||||||
 | 
						uint8_t ul_slots;
 | 
				
			||||||
 | 
						uint8_t reserved_dl_slots;
 | 
				
			||||||
 | 
						uint8_t reserved_ul_slots;
 | 
				
			||||||
 | 
						int8_t first_common_tn;
 | 
				
			||||||
 | 
						uint8_t slotcount = 0;
 | 
				
			||||||
 | 
						uint8_t reserve_count = 0, trx_no;
 | 
				
			||||||
 | 
						int first_ts;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						int tfi;
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
						gprs_rlcmac_trx *trx;
 | 
				
			||||||
 | 
						char slot_info[9] = { 0 };
 | 
				
			||||||
 | 
						struct gprs_rlcmac_pdch *first_common_ts = ms_first_common_ts(req->ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPAL(req, "B", LOGL_DEBUG, "Alloc start\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ARRAY_SIZE(res->usf); i++)
 | 
				
			||||||
 | 
							res->usf[i] = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Step 1: Get current state from the MS object */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reserved_dl_slots = ms_reserved_dl_slots(req->ms);
 | 
				
			||||||
 | 
						reserved_ul_slots = ms_reserved_ul_slots(req->ms);
 | 
				
			||||||
 | 
						first_common_tn = first_common_ts ? first_common_ts->ts_no : -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Step 2a: Find usable TRX and TFI */
 | 
				
			||||||
 | 
						tfi = tfi_find_free(req, &trx_no);
 | 
				
			||||||
 | 
						if (tfi < 0) {
 | 
				
			||||||
 | 
							LOGPAL(req, "B", LOGL_NOTICE, "failed to allocate a TFI\n");
 | 
				
			||||||
 | 
							return tfi;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Step 2b: Reserve slots on the TRX for the MS */
 | 
				
			||||||
 | 
						trx = &req->bts->trx[trx_no];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!reserved_dl_slots || !reserved_ul_slots) {
 | 
				
			||||||
 | 
							rc = find_multi_slots(trx, ms_ms_class(req->ms), &reserved_ul_slots, &reserved_dl_slots);
 | 
				
			||||||
 | 
							if (rc < 0)
 | 
				
			||||||
 | 
								return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dl_slots = reserved_dl_slots;
 | 
				
			||||||
 | 
						ul_slots = reserved_ul_slots;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Step 3a: Derive the slot set for the current TBF */
 | 
				
			||||||
 | 
						rc = tbf_select_slot_set(req, trx, ul_slots, dl_slots, reserved_ul_slots, reserved_dl_slots,
 | 
				
			||||||
 | 
									 first_common_tn);
 | 
				
			||||||
 | 
						if (rc < 0)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Step 3b: Derive the slot set for a given direction */
 | 
				
			||||||
 | 
						if (req->direction == GPRS_RLCMAC_DL_TBF) {
 | 
				
			||||||
 | 
							dl_slots = rc;
 | 
				
			||||||
 | 
							count_slots(dl_slots, reserved_dl_slots, &slotcount, &reserve_count);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							rc = allocate_usf(trx, rc, dl_slots, &res->usf[0]);
 | 
				
			||||||
 | 
							if (rc < 0)
 | 
				
			||||||
 | 
								return rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ul_slots = rc;
 | 
				
			||||||
 | 
							reserved_ul_slots = ul_slots;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							count_slots(ul_slots, reserved_ul_slots, &slotcount, &reserve_count);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						first_ts = ffs(rc) - 1;
 | 
				
			||||||
 | 
						if (first_ts < 0) {
 | 
				
			||||||
 | 
							LOGPAL(req, "B", LOGL_NOTICE, "first slot unavailable\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						first_common_tn = ffs(dl_slots & ul_slots) - 1;
 | 
				
			||||||
 | 
						if (first_common_tn < 0) {
 | 
				
			||||||
 | 
							LOGPAL(req, "B", LOGL_NOTICE, "first common slot unavailable\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						first_common_ts = &trx->pdch[first_common_tn];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res->trx = trx;
 | 
				
			||||||
 | 
						res->first_common_ts = first_common_ts;
 | 
				
			||||||
 | 
						res->reserved_ul_slots = reserved_ul_slots;
 | 
				
			||||||
 | 
						res->reserved_dl_slots = reserved_dl_slots;
 | 
				
			||||||
 | 
						res->tfi = tfi;
 | 
				
			||||||
 | 
						/* res->usf is already filled in above */
 | 
				
			||||||
 | 
						if (req->single && slotcount) {
 | 
				
			||||||
 | 
							res->upgrade_to_multislot = (reserve_count > slotcount);
 | 
				
			||||||
 | 
							LOGPAL(req, "B", LOGL_INFO, "using single slot at TS %d\n", first_ts);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							res->upgrade_to_multislot = false;
 | 
				
			||||||
 | 
							LOGPAL(req, "B", LOGL_INFO, "using %d slots\n", slotcount);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ts_format(slot_info, dl_slots, ul_slots);
 | 
				
			||||||
 | 
						LOGP(DRLCMAC, LOGL_DEBUG, "- Available DL/UL slots: (TS=0)\"%s\"(TS=7)\n", slot_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (req->direction == GPRS_RLCMAC_DL_TBF)
 | 
				
			||||||
 | 
							res->ass_slots_mask = dl_slots;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							res->ass_slots_mask = ul_slots;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bts_do_rate_ctr_inc(req->bts, CTR_TBF_ALLOC_ALGO_B);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*! Slot Allocation: Algorithm dynamic
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This meta algorithm automatically selects on of the other algorithms based
 | 
				
			||||||
 | 
					 * on the current system state.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The goal is to support as many MS and TBF as possible. On low usage, the
 | 
				
			||||||
 | 
					 * goal is to provide the highest possible bandwidth per MS.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  \param[in] req Contains all the requested params
 | 
				
			||||||
 | 
					 *  \param[out] res The resolution/response for the allocation request
 | 
				
			||||||
 | 
					 *  \returns negative error code or 0 on success
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int alloc_algorithm_dynamic(const struct alloc_resources_req *req,
 | 
				
			||||||
 | 
								    struct alloc_resources_res *res)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Reset load_is_high if there is at least one idle PDCH */
 | 
				
			||||||
 | 
						if (req->bts->multislot_disabled) {
 | 
				
			||||||
 | 
							req->bts->multislot_disabled = !idle_pdch_avail(req->bts);
 | 
				
			||||||
 | 
							if (!req->bts->multislot_disabled)
 | 
				
			||||||
 | 
								LOGP(DRLCMAC, LOGL_DEBUG, "Enabling algorithm B\n");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!req->bts->multislot_disabled) {
 | 
				
			||||||
 | 
							rc = alloc_algorithm_b(req, res);
 | 
				
			||||||
 | 
							if (rc >= 0)
 | 
				
			||||||
 | 
								return rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!req->bts->multislot_disabled)
 | 
				
			||||||
 | 
								LOGP(DRLCMAC, LOGL_DEBUG, "Disabling algorithm B\n");
 | 
				
			||||||
 | 
							req->bts->multislot_disabled = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return alloc_algorithm_a(req, res);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rx = mslot_class_get_rx(ms_class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (rx == MS_NA)
 | 
				
			||||||
 | 
							rx = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (the_pcu->alloc_algorithm == alloc_algorithm_a)
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bts->multislot_disabled)
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rx;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										67
									
								
								src/alloc_algo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/alloc_algo.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					/* alloc_algo.h
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2022 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "tbf.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gprs_rlcmac_bts;
 | 
				
			||||||
 | 
					struct GprsMs;
 | 
				
			||||||
 | 
					struct gprs_rlcmac_tbf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct alloc_resources_req {
 | 
				
			||||||
 | 
						/* BTS where to allocate resources */
 | 
				
			||||||
 | 
						struct gprs_rlcmac_bts *bts;
 | 
				
			||||||
 | 
						/* MS for which to allocate resources */
 | 
				
			||||||
 | 
						const struct GprsMs *ms;
 | 
				
			||||||
 | 
						/* Direction of the TBF for which we are allocating resources */
 | 
				
			||||||
 | 
						enum gprs_rlcmac_tbf_direction direction;
 | 
				
			||||||
 | 
						/* Whether to allocate only a single (1) TS */
 | 
				
			||||||
 | 
						bool single;
 | 
				
			||||||
 | 
						/* Whether to allocate on a specific TRX (>=0) or not (-1) */
 | 
				
			||||||
 | 
						int8_t use_trx;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct alloc_resources_res {
 | 
				
			||||||
 | 
						struct gprs_rlcmac_trx *trx;
 | 
				
			||||||
 | 
						struct gprs_rlcmac_pdch *first_common_ts;
 | 
				
			||||||
 | 
						uint8_t reserved_ul_slots;
 | 
				
			||||||
 | 
						uint8_t reserved_dl_slots;
 | 
				
			||||||
 | 
						uint8_t ass_slots_mask;
 | 
				
			||||||
 | 
						bool upgrade_to_multislot;
 | 
				
			||||||
 | 
						uint8_t tfi;
 | 
				
			||||||
 | 
						int usf[8];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int alloc_algorithm_a(const struct alloc_resources_req *req,
 | 
				
			||||||
 | 
							      struct alloc_resources_res *res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int alloc_algorithm_b(const struct alloc_resources_req *req,
 | 
				
			||||||
 | 
							      struct alloc_resources_res *res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int alloc_algorithm_dynamic(const struct alloc_resources_req *req,
 | 
				
			||||||
 | 
								    struct alloc_resources_res *res);
 | 
				
			||||||
 | 
					int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@@ -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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,45 +0,0 @@
 | 
				
			|||||||
/* bitvector.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.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef BITVECTOR_H
 | 
					 | 
				
			||||||
#define BITVECTOR_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! \defgroup bitvector Bit vectors
 | 
					 | 
				
			||||||
 *  @{
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! \file bitvector.h
 | 
					 | 
				
			||||||
 *  \brief Additional functions for Osmocom bit vector abstraction.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern "C" {
 | 
					 | 
				
			||||||
#include <osmocom/core/bitvec.h>
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct bitvec *bitvec_alloc(unsigned size);
 | 
					 | 
				
			||||||
void bitvec_free(struct bitvec *bv);
 | 
					 | 
				
			||||||
int bitvec_unhex(struct bitvec *bv, const char* src);
 | 
					 | 
				
			||||||
int bitvec_pack(struct bitvec *bv, uint8_t *buffer);
 | 
					 | 
				
			||||||
int bitvec_unpack(struct bitvec *bv, uint8_t *buffer);
 | 
					 | 
				
			||||||
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len);
 | 
					 | 
				
			||||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*! }@ */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif // BITVECTOR_H
 | 
					 | 
				
			||||||
							
								
								
									
										1491
									
								
								src/bts.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1491
									
								
								src/bts.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										378
									
								
								src/bts.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								src/bts.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,378 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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/core/time_cc.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"
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "tbf.h"
 | 
				
			||||||
 | 
					#include "coding_scheme.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
						CTR_PDCH_ALL_ALLOCATED,
 | 
				
			||||||
 | 
						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_PCH_REQUESTS,
 | 
				
			||||||
 | 
						CTR_PCH_REQUESTS_ALREADY,
 | 
				
			||||||
 | 
						CTR_PCH_REQUESTS_TIMEDOUT,
 | 
				
			||||||
 | 
						CTR_RACH_REQUESTS,
 | 
				
			||||||
 | 
						CTR_RACH_REQUESTS_11BIT,
 | 
				
			||||||
 | 
						CTR_RACH_REQUESTS_ONE_PHASE,
 | 
				
			||||||
 | 
						CTR_RACH_REQUESTS_TWO_PHASE,
 | 
				
			||||||
 | 
						CTR_RACH_REQUESTS_UNEXPECTED,
 | 
				
			||||||
 | 
						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_UL_TBF_ONE_PHASE,
 | 
				
			||||||
 | 
						CTR_IMMEDIATE_ASSIGN_UL_TBF_TWO_PHASE,
 | 
				
			||||||
 | 
						CTR_IMMEDIATE_ASSIGN_UL_TBF_CONTENTION_RESOLUTION_SUCCESS,
 | 
				
			||||||
 | 
						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_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,
 | 
				
			||||||
 | 
						STAT_PDCH_AVAILABLE,
 | 
				
			||||||
 | 
						STAT_PDCH_OCCUPIED,
 | 
				
			||||||
 | 
						STAT_PDCH_OCCUPIED_GPRS,
 | 
				
			||||||
 | 
						STAT_PDCH_OCCUPIED_EGPRS,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 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;
 | 
				
			||||||
 | 
						uint16_t rfn;
 | 
				
			||||||
 | 
						uint32_t fn;
 | 
				
			||||||
 | 
						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 llist_head ms_list; /* list of struct GprsMs */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* List of struct bts_pch_timer for active PCH pagings */
 | 
				
			||||||
 | 
						struct llist_head pch_timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct osmo_time_cc all_allocated_pdch;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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, uint32_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, const struct gprs_rlcmac_dl_tbf *tbf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 tlli);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 GprsMs *bts_get_ms(const struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli,
 | 
				
			||||||
 | 
								  const char *imsi);
 | 
				
			||||||
 | 
					struct GprsMs *bts_get_ms_by_tlli(const struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli);
 | 
				
			||||||
 | 
					struct GprsMs *bts_get_ms_by_imsi(const struct gprs_rlcmac_bts *bts, const char *imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define bts_stat_item_inc(bts, stat_id) bts_stat_item_add(bts, stat_id, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define bts_stat_item_dec(bts, stat_id) bts_stat_item_add(bts, stat_id, -1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					uint8_t bts_get_ms_pwr_alpha(const struct gprs_rlcmac_bts *bts);
 | 
				
			||||||
 | 
					bool bts_all_pdch_allocated(const struct gprs_rlcmac_bts *bts);
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										121
									
								
								src/bts_pch_timer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/bts_pch_timer.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * Author: Oliver Smith
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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 <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/talloc.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/tdef.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <gprs_debug.h>
 | 
				
			||||||
 | 
					#include <gprs_pcu.h>
 | 
				
			||||||
 | 
					#include <bts_pch_timer.h>
 | 
				
			||||||
 | 
					#include <gprs_ms.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct bts_pch_timer *bts_pch_timer_get_by_ptmsi(struct gprs_rlcmac_bts *bts, uint32_t ptmsi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bts_pch_timer *p;
 | 
				
			||||||
 | 
						OSMO_ASSERT(ptmsi != GSM_RESERVED_TMSI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry(p, &bts->pch_timer, entry) {
 | 
				
			||||||
 | 
							if (p->ptmsi != GSM_RESERVED_TMSI && p->ptmsi == ptmsi)
 | 
				
			||||||
 | 
								return p;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct bts_pch_timer *bts_pch_timer_get_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bts_pch_timer *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry(p, &bts->pch_timer, entry) {
 | 
				
			||||||
 | 
							if (strcmp(p->imsi, imsi) == 0)
 | 
				
			||||||
 | 
								return p;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bts_pch_timer_remove(struct bts_pch_timer *p)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						osmo_timer_del(&p->T3113);
 | 
				
			||||||
 | 
						llist_del(&p->entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DPCU, LOGL_DEBUG, "PCH paging timer stopped for IMSI=%s\n", p->imsi);
 | 
				
			||||||
 | 
						talloc_free(p);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void T3113_callback(void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bts_pch_timer *p = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DPCU, LOGL_INFO, "PCH paging timeout for IMSI=%s\n", p->imsi);
 | 
				
			||||||
 | 
						bts_do_rate_ctr_inc(p->bts, CTR_PCH_REQUESTS_TIMEDOUT);
 | 
				
			||||||
 | 
						bts_pch_timer_remove(p);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bts_pch_timer_start(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi_paging,
 | 
				
			||||||
 | 
								 const char *imsi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bts_pch_timer *p;
 | 
				
			||||||
 | 
						struct osmo_tdef *tdef;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p = talloc_zero(bts, struct bts_pch_timer);
 | 
				
			||||||
 | 
						llist_add_tail(&p->entry, &bts->pch_timer);
 | 
				
			||||||
 | 
						p->bts = bts;
 | 
				
			||||||
 | 
						OSMO_STRLCPY_ARRAY(p->imsi, imsi);
 | 
				
			||||||
 | 
						p->ptmsi = (mi_paging->type == GSM_MI_TYPE_TMSI) ? mi_paging->tmsi : GSM_RESERVED_TMSI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tdef = osmo_tdef_get_entry(the_pcu->T_defs, 3113);
 | 
				
			||||||
 | 
						OSMO_ASSERT(tdef);
 | 
				
			||||||
 | 
						osmo_timer_setup(&p->T3113, T3113_callback, p);
 | 
				
			||||||
 | 
						osmo_timer_schedule(&p->T3113, tdef->val, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (log_check_level(DPCU, LOGL_DEBUG)) {
 | 
				
			||||||
 | 
							char str[64];
 | 
				
			||||||
 | 
							osmo_mobile_identity_to_str_buf(str, sizeof(str), mi_paging);
 | 
				
			||||||
 | 
							LOGP(DPCU, LOGL_DEBUG, "PCH paging timer started for MI=%s IMSI=%s\n", str, p->imsi);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bts_pch_timer_stop(struct gprs_rlcmac_bts *bts, const struct GprsMs *ms)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bts_pch_timer *p = NULL;
 | 
				
			||||||
 | 
						uint32_t tlli = ms_tlli(ms);
 | 
				
			||||||
 | 
						const char *imsi = ms_imsi(ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* First try matching by TMSI if available in MS */
 | 
				
			||||||
 | 
						if (tlli != GSM_RESERVED_TMSI)
 | 
				
			||||||
 | 
							p = bts_pch_timer_get_by_ptmsi(bts, tlli);
 | 
				
			||||||
 | 
						/* Otherwise try matching by IMSI if available in MS */
 | 
				
			||||||
 | 
						if (!p && imsi[0] != '\0')
 | 
				
			||||||
 | 
							p = bts_pch_timer_get_by_imsi(bts, imsi);
 | 
				
			||||||
 | 
						if (p)
 | 
				
			||||||
 | 
							bts_pch_timer_remove(p);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bts_pch_timer_stop_all(struct gprs_rlcmac_bts *bts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bts_pch_timer *p, *n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry_safe(p, n, &bts->pch_timer, entry) {
 | 
				
			||||||
 | 
							bts_pch_timer_remove(p);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										49
									
								
								src/bts_pch_timer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/bts_pch_timer.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * Author: Oliver Smith
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
 | 
					 * modify it under the terms of the GNU General Public License
 | 
				
			||||||
 | 
					 * as published by the Free Software Foundation; either version 2
 | 
				
			||||||
 | 
					 * of the License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/linuxlist.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/timer.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/protocol/gsm_23_003.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <bts.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct bts_pch_timer {
 | 
				
			||||||
 | 
						struct llist_head entry;
 | 
				
			||||||
 | 
						struct gprs_rlcmac_bts *bts;
 | 
				
			||||||
 | 
						struct osmo_timer_list T3113;
 | 
				
			||||||
 | 
						uint32_t ptmsi; /* GSM_RESERVED_TMSI if not available */
 | 
				
			||||||
 | 
						char imsi[OSMO_IMSI_BUF_SIZE];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct GprsMs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void bts_pch_timer_start(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi_paging,
 | 
				
			||||||
 | 
								 const char *imsi);
 | 
				
			||||||
 | 
					void bts_pch_timer_stop(struct gprs_rlcmac_bts *bts, const struct GprsMs *ms);
 | 
				
			||||||
 | 
					void bts_pch_timer_stop_all(struct gprs_rlcmac_bts *bts);
 | 
				
			||||||
 | 
					struct bts_pch_timer *bts_pch_timer_get_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										404
									
								
								src/coding_scheme.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								src/coding_scheme.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,404 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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)];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										95
									
								
								src/coding_scheme.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								src/coding_scheme.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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);
 | 
				
			||||||
							
								
								
									
										103
									
								
								src/csn1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/csn1.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					/* csn1_enc.c
 | 
				
			||||||
 | 
					 * Routines for CSN1 dissection in wireshark.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2011 Ivan Klyuchnikov
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * By Vincent Helfre, based on original code by Jari Sassi
 | 
				
			||||||
 | 
					 * with the gracious authorization of STE
 | 
				
			||||||
 | 
					 * Copyright (c) 2011 ST-Ericsson
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * $Id: packet-csn1.c 39140 2011-09-25 22:01:50Z wmeier $
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Wireshark - Network traffic analyzer
 | 
				
			||||||
 | 
					 * By Gerald Combs <gerald@wireshark.org>
 | 
				
			||||||
 | 
					 * Copyright 1998 Gerald Combs
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
 | 
					 * modify it under the terms of the GNU General Public License
 | 
				
			||||||
 | 
					 * as published by the Free Software Foundation; either version 2
 | 
				
			||||||
 | 
					 * of the License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#define __STDC_FORMAT_MACROS
 | 
				
			||||||
 | 
					#include <inttypes.h>
 | 
				
			||||||
 | 
					#include "csn1.h"
 | 
				
			||||||
 | 
					#include <gprs_debug.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const unsigned char ixBitsTab[] = {0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Returns no_of_bits (up to 8) masked with 0x2B */
 | 
				
			||||||
 | 
					guint8
 | 
				
			||||||
 | 
					get_masked_bits8(struct bitvec *vector, unsigned *readIndex, gint bit_offset,  const gint no_of_bits)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  static const guint8 maskBits[] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
 | 
				
			||||||
 | 
					  //gint byte_offset = bit_offset >> 3;          /* divide by 8 */
 | 
				
			||||||
 | 
					  gint relative_bit_offset = bit_offset & 0x07;  /* modulo 8 */
 | 
				
			||||||
 | 
					  guint8 result;
 | 
				
			||||||
 | 
					  gint bit_shift = 8 - relative_bit_offset - (gint) no_of_bits;
 | 
				
			||||||
 | 
					  *readIndex -= relative_bit_offset;
 | 
				
			||||||
 | 
					  if (bit_shift >= 0)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    result = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) >> bit_shift;
 | 
				
			||||||
 | 
					    *readIndex-= bit_shift;
 | 
				
			||||||
 | 
					    result &= maskBits[no_of_bits];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    guint8 hight_part = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) & maskBits[8 - relative_bit_offset];
 | 
				
			||||||
 | 
					    hight_part = (guint8) (hight_part << (-bit_shift));
 | 
				
			||||||
 | 
					    result =  (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) >> (8 + bit_shift);
 | 
				
			||||||
 | 
					    *readIndex = *readIndex - (8 - (-bit_shift));
 | 
				
			||||||
 | 
					    result |= hight_part;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * ================================================================================================
 | 
				
			||||||
 | 
					 * set initial/start values in help data structure used for packing/unpacking operation
 | 
				
			||||||
 | 
					 * ================================================================================================
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					csnStreamInit(csnStream_t* ar, gint bit_offset, gint remaining_bits_len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  ar->remaining_bits_len  = remaining_bits_len;
 | 
				
			||||||
 | 
					  ar->bit_offset          = bit_offset;
 | 
				
			||||||
 | 
					  ar->direction           = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct value_string csn1_error_names[] = {
 | 
				
			||||||
 | 
					  { CSN_OK,                               "General 0" },
 | 
				
			||||||
 | 
					  { CSN_ERROR_GENERAL,                    "General -1"  },
 | 
				
			||||||
 | 
					  { CSN_ERROR_DATA_NOT_VALID,             "DATA_NOT VALID" },
 | 
				
			||||||
 | 
					  { CSN_ERROR_IN_SCRIPT,                  "IN SCRIPT" },
 | 
				
			||||||
 | 
					  { CSN_ERROR_INVALID_UNION_INDEX,        "INVALID UNION INDEX" },
 | 
				
			||||||
 | 
					  { CSN_ERROR_NEED_MORE_BITS_TO_UNPACK,   "NEED_MORE BITS TO UNPACK" },
 | 
				
			||||||
 | 
					  { CSN_ERROR_ILLEGAL_BIT_VALUE,          "ILLEGAL BIT VALUE" },
 | 
				
			||||||
 | 
					  { CSN_ERROR_INTERNAL,                   "INTERNAL" },
 | 
				
			||||||
 | 
					  { CSN_ERROR_STREAM_NOT_SUPPORTED,       "STREAM_NOT_SUPPORTED" },
 | 
				
			||||||
 | 
					  { CSN_ERROR_MESSAGE_TOO_LONG,           "MESSAGE_TOO_LONG" },
 | 
				
			||||||
 | 
					  { 0, NULL }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gint16 ProcessError_impl(const char *file, int line, unsigned *readIndex,
 | 
				
			||||||
 | 
					                                const char* sz, gint16 err, const CSN_DESCR* pDescr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  /* Don't add trailing newline, top caller is responsible for appending it */
 | 
				
			||||||
 | 
					  if (err != CSN_OK)
 | 
				
			||||||
 | 
					    LOGPSRC(DCSN1, LOGL_ERROR, file, line, "%s: error %s (%d) at %s (idx %u)",
 | 
				
			||||||
 | 
					            sz, get_value_string(csn1_error_names, err), err,
 | 
				
			||||||
 | 
					            pDescr ? pDescr->sz : "-", *readIndex);
 | 
				
			||||||
 | 
					  return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2603
									
								
								src/csn1.cpp
									
									
									
									
									
								
							
							
						
						
									
										2603
									
								
								src/csn1.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										253
									
								
								src/csn1.h
									
									
									
									
									
								
							
							
						
						
									
										253
									
								
								src/csn1.h
									
									
									
									
									
								
							@@ -19,21 +19,13 @@
 | 
				
			|||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 * GNU General Public License for more details.
 | 
					 * 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>
 | 
					 | 
				
			||||||
#ifndef _PACKET_CSN1_H_
 | 
					#ifndef _PACKET_CSN1_H_
 | 
				
			||||||
#define _PACKET_CSN1_H_
 | 
					#define _PACKET_CSN1_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/bitvec.h>
 | 
				
			||||||
#define MIN(a,b) (((a)<(b))?(a):(b))
 | 
					#include "wireshark_compat.h"
 | 
				
			||||||
//#define max(a,b) (((a)>(b))?(a):(b))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Error codes */
 | 
					/* Error codes */
 | 
				
			||||||
#define  CSN_OK                               0
 | 
					#define  CSN_OK                               0
 | 
				
			||||||
@@ -48,15 +40,6 @@
 | 
				
			|||||||
#define  CSN_ERROR_MESSAGE_TOO_LONG          -9
 | 
					#define  CSN_ERROR_MESSAGE_TOO_LONG          -9
 | 
				
			||||||
#define  CSN_ERROR_                         -10
 | 
					#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 */
 | 
					/* CallBack return status */
 | 
				
			||||||
typedef gint16 CSN_CallBackStatus_t;
 | 
					typedef gint16 CSN_CallBackStatus_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -73,6 +56,7 @@ typedef gint16 CSN_CallBackStatus_t;
 | 
				
			|||||||
#ifndef ElementsOf
 | 
					#ifndef ElementsOf
 | 
				
			||||||
#define ElementsOf(array) (sizeof(array) / sizeof(array[0]))
 | 
					#define ElementsOf(array) (sizeof(array) / sizeof(array[0]))
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					typedef void(*void_fn_t)(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Context holding CSN1 parameters */
 | 
					/* Context holding CSN1 parameters */
 | 
				
			||||||
typedef struct
 | 
					typedef struct
 | 
				
			||||||
@@ -82,7 +66,9 @@ typedef struct
 | 
				
			|||||||
  gint  direction;           /* 0 - decode; 1 - encode */
 | 
					  gint  direction;           /* 0 - decode; 1 - encode */
 | 
				
			||||||
} csnStream_t;
 | 
					} 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
 | 
					typedef enum
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  CSN_END = 0,
 | 
					  CSN_END = 0,
 | 
				
			||||||
@@ -99,6 +85,7 @@ typedef enum
 | 
				
			|||||||
  CSN_VARIABLE_BITMAP_1,     /* <bitmap: bit**> i.e. to the end of message (R99) */
 | 
					  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,  /* 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_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_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,       /* 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) */
 | 
					  CSN_VARIABLE_TARRAY_OFFSET,/* As above but with offset. The offset is stored as third parameter of CSN_DESCR (descr.value) */
 | 
				
			||||||
@@ -127,7 +114,7 @@ typedef enum
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * i:
 | 
					 * i:
 | 
				
			||||||
 *       Depending on the contents of the type parameter,  the parameter "i" may have following meaning:
 | 
					 *       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
 | 
					 *       - the offset for an array size by which the size is incremented
 | 
				
			||||||
 *          for the CSN_VAR_ARRAY type
 | 
					 *          for the CSN_VAR_ARRAY type
 | 
				
			||||||
 *       - the length of each element of an array for the CSN_REC_ARRAY type
 | 
					 *       - the length of each element of an array for the CSN_REC_ARRAY type
 | 
				
			||||||
@@ -142,6 +129,7 @@ typedef enum
 | 
				
			|||||||
 *          CSN_VAR_BITMAP, CSN_LEFT_VAR_BMP, and CSN_LEFT_BMP_1 types
 | 
					 *          CSN_VAR_BITMAP, CSN_LEFT_VAR_BMP, and CSN_LEFT_BMP_1 types
 | 
				
			||||||
 *       - the offset to param1 for the CSN_CALLBACK type
 | 
					 *       - the offset to param1 for the CSN_CALLBACK type
 | 
				
			||||||
 *       - ERRORCODE  used by the CSN_ERROR type
 | 
					 *       - ERRORCODE  used by the CSN_ERROR type
 | 
				
			||||||
 | 
					 *       - the bit-length of the LENGTH field in a CSN_SERIALISE type
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * descr
 | 
					 * descr
 | 
				
			||||||
 *       This parameter has different meaning depending on the value of the type parameter:
 | 
					 *       This parameter has different meaning depending on the value of the type parameter:
 | 
				
			||||||
@@ -167,6 +155,10 @@ typedef enum
 | 
				
			|||||||
 *         - an offset to the variable Exist for CSN_NEXT_EXIST and CSN_NEXT_EXIST_LH types
 | 
					 *         - 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
 | 
					 *         - 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
 | 
					 * sz
 | 
				
			||||||
 *    - is the name of the parameter within the descr where their unpacked or packed value shall be stored or fetched.
 | 
					 *    - 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.
 | 
					 *      This paramater is pointed out by the offset parameter in the same CSN_DESCR variable as the sz.
 | 
				
			||||||
@@ -184,23 +176,21 @@ typedef struct
 | 
				
			|||||||
  gint16      i;
 | 
					  gint16      i;
 | 
				
			||||||
  union
 | 
					  union
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    void*     ptr;
 | 
					    const void*     ptr;
 | 
				
			||||||
    guint32   value;
 | 
					    guint32   value;
 | 
				
			||||||
  } descr;
 | 
					  } descr;
 | 
				
			||||||
  unsigned      offset;
 | 
					  unsigned      offset;
 | 
				
			||||||
 | 
					  gboolean    may_be_null;
 | 
				
			||||||
  const char* sz;
 | 
					  const char* sz;
 | 
				
			||||||
  union
 | 
					  guint32     value;
 | 
				
			||||||
  {
 | 
					  void_fn_t   aux_fn;
 | 
				
			||||||
    StreamSerializeFcn_t  fcn;
 | 
					 | 
				
			||||||
    guint32               value;
 | 
					 | 
				
			||||||
    int*                  hf_ptr;
 | 
					 | 
				
			||||||
  } serialize;
 | 
					 | 
				
			||||||
} CSN_DESCR;
 | 
					} CSN_DESCR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct
 | 
					typedef struct
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  guint8     bits;
 | 
					  guint8     bits;
 | 
				
			||||||
  guint8     value;
 | 
					  guint8     value;
 | 
				
			||||||
 | 
					  gboolean   keep_bits;
 | 
				
			||||||
  CSN_DESCR descr;
 | 
					  CSN_DESCR descr;
 | 
				
			||||||
} CSN_ChoiceElement_t;
 | 
					} CSN_ChoiceElement_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -223,16 +213,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
 | 
					* 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 */
 | 
					/* CSN struct macro's */
 | 
				
			||||||
#define  CSN_DESCR_BEGIN(_STRUCT)\
 | 
					#define  CSN_DESCR_BEGIN(_STRUCT)\
 | 
				
			||||||
        CSN_DESCR CSNDESCR_##_STRUCT[] = {
 | 
					        CSN_DESCR CSNDESCR_##_STRUCT[] = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define  CSN_DESCR_END(_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)
 | 
					 * CSN_ERROR(Par1, Par2, Par3)
 | 
				
			||||||
@@ -243,7 +233,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 *      Par3: Error code
 | 
					 *      Par3: Error code
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define CSN_ERROR(_STRUCT, _Text, _ERRCODE)\
 | 
					#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)
 | 
					 * M_BIT(Par1, Par2)
 | 
				
			||||||
@@ -252,11 +242,21 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 *      Par2: C structure element name
 | 
					 *      Par2: C structure element name
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_BIT(_STRUCT, _MEMBER)\
 | 
					#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)
 | 
					 * 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.
 | 
					 * structure is present or not.
 | 
				
			||||||
 *      Par1: C structure name
 | 
					 *      Par1: C structure name
 | 
				
			||||||
 *      Par2: C structure element name
 | 
					 *      Par2: C structure element name
 | 
				
			||||||
@@ -264,37 +264,37 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 *            element(s) does not exist
 | 
					 *            element(s) does not exist
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_NEXT_EXIST(_STRUCT, _MEMBER, _NoOfExisting)\
 | 
					#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)
 | 
					 * M_NEXT_EXIST_LH(Par1, Par2, Par3)
 | 
				
			||||||
 * similar to the M_NEXT_EXIST except that instead of bit 0/1 which is fetched
 | 
					 * 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
 | 
					 * 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.
 | 
					 * pattern 0x2B is performed on the read bit before the decision is made.
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_NEXT_EXIST_LH(_STRUCT, _MEMBER, _NoOfExisting)\
 | 
					#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)
 | 
					 * 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
 | 
					 * 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.
 | 
					 * message.
 | 
				
			||||||
 * Covers the case {null | 0 | 1 < IE >} 
 | 
					 * Covers the case {null | 0 | 1 < IE >}
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_NEXT_EXIST_OR_NULL(_STRUCT, _MEMBER, _NoOfExisting)\
 | 
					#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)
 | 
					 * 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
 | 
					 * 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
 | 
					 * end of the message may be encountered when looking for the next element in
 | 
				
			||||||
 * the message.
 | 
					 * 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)\
 | 
					#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)
 | 
					 * M_UINT(Par1, Par2, Par3)
 | 
				
			||||||
@@ -304,17 +304,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)
 | 
					 *      Par3: number of bits used to code the element (between 1 and 32)
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_UINT(_STRUCT, _MEMBER, _BITS)\
 | 
					#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)
 | 
					 * M_UINT(Par1, Par2, Par3)
 | 
				
			||||||
 * This macro has the same functionality as M_UINT except that  in addition the
 | 
					 * This macro has the same functionality as M_UINT except that  in addition the
 | 
				
			||||||
 * logical "exclusive or" operation with the background value "0x2B" is 
 | 
					 * logical "exclusive or" operation with the background value "0x2B" is
 | 
				
			||||||
 * performed before the final value of the integer number is delivered from the 
 | 
					 * performed before the final value of the integer number is delivered from the
 | 
				
			||||||
 * received CSN.1 message
 | 
					 * received CSN.1 message
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_UINT_LH(_STRUCT, _MEMBER, _BITS)\
 | 
					#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)
 | 
					 * M_UINT_OFFSET(Par1, Par2, Par3, Par4)
 | 
				
			||||||
@@ -325,7 +335,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 *      Par4: value added to the returned integer (offset)
 | 
					 *      Par4: value added to the returned integer (offset)
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_UINT_OFFSET(_STRUCT, _MEMBER, _BITS, _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)
 | 
					 * M_UINT_ARRAY(Par1, Par2, Par3, Par4)
 | 
				
			||||||
@@ -336,7 +346,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 *      Par4: number of elements in the array (fixed integer value)
 | 
					 *      Par4: number of elements in the array (fixed integer value)
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_UINT_ARRAY(_STRUCT, _MEMBER, _BITS, _ElementCount)\
 | 
					#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)
 | 
					 * M_VAR_UINT_ARRAY(Par1, Par2, Par3, Par4)
 | 
				
			||||||
@@ -348,7 +358,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 *            structure member holding the length value
 | 
					 *            structure member holding the length value
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_VAR_UINT_ARRAY(_STRUCT, _MEMBER, _BITS, _ElementCountField)\
 | 
					#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)
 | 
					 * M_VAR_ARRAY(Par1, Par2, Par3, Par4)
 | 
				
			||||||
@@ -359,7 +369,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
 | 
					 *      Par4: offset that is added to the Par3 to get the actual size of the array
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_VAR_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
 | 
					#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)
 | 
					 * M_VAR_TARRAY(Par1, Par2, Par3, Par4)
 | 
				
			||||||
@@ -370,32 +380,32 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 *      Par4: name of the structure member holding the size of the array
 | 
					 *      Par4: name of the structure member holding the size of the array
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_VAR_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
 | 
					#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)
 | 
					 * M_VAR_TARRAY_OFFSET(Par1, Par2, Par3, Par4)
 | 
				
			||||||
 * Same as M_VAR_TARRAY with offset
 | 
					 * Same as M_VAR_TARRAY with offset
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_VAR_TARRAY_OFFSET(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
 | 
					#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)
 | 
					 * 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
 | 
					 * 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. 
 | 
					 * 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 
 | 
					 * A zero element terminates the array. The CSN.1 syntax describes it
 | 
				
			||||||
 * recursively as:
 | 
					 * recursively as:
 | 
				
			||||||
 * <array> ::={1 <element> <array>| 0} 
 | 
					 * <array> ::={1 <element> <array>| 0}
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *      Par1: C structure name
 | 
					 *      Par1: C structure name
 | 
				
			||||||
 *      Par2: C structure element 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
 | 
					 *            array will be stored
 | 
				
			||||||
 *      Par4: length of each element in bits
 | 
					 *      Par4: length of each element in bits
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_REC_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _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)
 | 
					 * M_VAR_TYPE_ARRAY(Par1, Par2, Par3, Par4)
 | 
				
			||||||
@@ -406,7 +416,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 *      Par4: number of elements in the array (fixed integer value)
 | 
					 *      Par4: number of elements in the array (fixed integer value)
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_TYPE_ARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCount)\
 | 
					#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)
 | 
					 * M_REC_TARRAY(Par1, Par2, Par3, Par4)
 | 
				
			||||||
@@ -418,7 +428,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 *      Par4: will hold the number of element in the array after unpacking
 | 
					 *      Par4: will hold the number of element in the array after unpacking
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_REC_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
 | 
					#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)
 | 
					 * M_REC_TARRAY1(Par1, Par2, Par3, Par4)
 | 
				
			||||||
@@ -426,7 +436,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 * <list> ::= <type> {1 <type>} ** 0 ;
 | 
					 * <list> ::= <type> {1 <type>} ** 0 ;
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_REC_TARRAY_1(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
 | 
					#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)
 | 
					 * M_REC_TARRAY2(Par1, Par2, Par3, Par4)
 | 
				
			||||||
@@ -434,7 +444,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 * <lists> ::= <type> { 0 <type> } ** 1 ;
 | 
					 * <lists> ::= <type> { 0 <type> } ** 1 ;
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_REC_TARRAY_2(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
 | 
					#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)
 | 
					 * M_TYPE(Par1, Par2, Par3)
 | 
				
			||||||
@@ -445,130 +455,161 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
				
			|||||||
 *      Par3: type of member
 | 
					 *      Par3: type of member
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_TYPE(_STRUCT, _MEMBER, _MEMBER_TYPE)\
 | 
					#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_TYPE_OR_NULL(Par1, Par2, Par3)
 | 
				
			||||||
 | 
					 * Similar to the M_TYPE 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_TYPE_OR_NULL(_STRUCT, _MEMBER, _MEMBER_TYPE)\
 | 
				
			||||||
 | 
					        {CSN_TYPE, 0, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/******************************************************************************
 | 
					/******************************************************************************
 | 
				
			||||||
 * M_UNION(Par1, Par2)
 | 
					 * M_UNION(Par1, Par2)
 | 
				
			||||||
 * Informs the CSN.1 library that a union follows and how many possible choices
 | 
					 * 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
 | 
					 * 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 
 | 
					 * 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 
 | 
					 * called UnionType. The elements of the union have to be listed directly after
 | 
				
			||||||
 * the M_UNION statement.
 | 
					 * the M_UNION statement.
 | 
				
			||||||
 *      Par1: C structure name
 | 
					 *      Par1: C structure name
 | 
				
			||||||
 *      Par2: number of possible choice in the union
 | 
					 *      Par2: number of possible choice in the union
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_UNION(_STRUCT, _COUNT)\
 | 
					#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)
 | 
					 * M_UNION_LH(Par1, Par2)
 | 
				
			||||||
 * Same as M_UNION but masked with background value 0x2B
 | 
					 * Same as M_UNION but masked with background value 0x2B
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_UNION_LH(_STRUCT, _COUNT)\
 | 
					#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)
 | 
					 * M_CHOICE(Par1, Par2, Par3, Par4)
 | 
				
			||||||
 * Similar to the M_UNION. In the M_UNION the selected element of all possible 
 | 
					 * 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 
 | 
					 * 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 
 | 
					 * 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
 | 
					 * 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 
 | 
					 * 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 
 | 
					 * apply. There is free but predefined mapping of the element of the union and
 | 
				
			||||||
 * the value which addresses this element.
 | 
					 * 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
 | 
					 * 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
 | 
					 *      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
 | 
					 *      Par3: address of an array of type CSN_ChoiceElement_t where all possible
 | 
				
			||||||
 *            values of the selector are provided, together with the selector 
 | 
					 *            values of the selector are provided, together with the selector
 | 
				
			||||||
 *            length expressed in bits and the address of  the CSN_DESCR type 
 | 
					 *            length expressed in bits and the address of  the CSN_DESCR type
 | 
				
			||||||
 *            where the element is defined. For every element in the union 
 | 
					 *            where the element is defined. For every element in the union
 | 
				
			||||||
 *            there is one line in the Choice variable. These lines have to 
 | 
					 *            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 
 | 
					 *            appear in the _CHOICE in the same order as the elements in the
 | 
				
			||||||
 *            union. The element of the union selected in the message through 
 | 
					 *            union. The element of the union selected in the message through
 | 
				
			||||||
 *            the _CHOICE parameter is after unpacking translated to the 
 | 
					 *            the _CHOICE parameter is after unpacking translated to the
 | 
				
			||||||
 *            corresponding sequential number of this element and stored in 
 | 
					 *            corresponding sequential number of this element and stored in
 | 
				
			||||||
 *            the variable pointed out by the _MEMBER
 | 
					 *            the variable pointed out by the _MEMBER
 | 
				
			||||||
 *      Par4: number of possible choices in the union
 | 
					 *      Par4: number of possible choices in the union
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_CHOICE(_STRUCT, _MEMBER, _CHOICE, _ElementCount)\
 | 
					#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)
 | 
					 * 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
 | 
					 * in  the message
 | 
				
			||||||
 *      Par1: C structure name
 | 
					 *      Par1: C structure name
 | 
				
			||||||
 *      Par2: gives the length of the fixed number in bits.
 | 
					 *      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
 | 
					*             the message the unpacking procedure is aborted
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_FIXED(_STRUCT, _BITS, _BITVALUE)\
 | 
					#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)
 | 
					 * M_SERIALIZE(Par1, Par2, Par3)
 | 
				
			||||||
 * Allows using a complete free format of data being encoded or decoded. 
 | 
					 * 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 
 | 
					 * When the M_SERIALIZE is encounted during encoding or decoding of a message
 | 
				
			||||||
 * the CSNstream program passes the control over to the specified function 
 | 
					 * the CSNstream program passes the control over to the specified function
 | 
				
			||||||
 * together with all necessary parameters about the current position within 
 | 
					 * together with all necessary parameters about the current position within
 | 
				
			||||||
 * the message being unpacked or packed.  When transferring of "serialized" 
 | 
					 * the message being unpacked or packed.  When transferring of "serialized"
 | 
				
			||||||
 * data to or from the message is finished by the function the CSNstream gets 
 | 
					 * 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.
 | 
					 * back control over the data stream and continues to work with the message.
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_SERIALIZE(_STRUCT, _MEMBER, _SERIALIZEFCN)\
 | 
					#define M_SERIALIZE(_STRUCT, _MEMBER, _LENGTH_LEN, _SERIALIZEFCN)\
 | 
				
			||||||
        {CSN_SERIALIZE, 1, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {_SERIALIZEFCN}}
 | 
					        {CSN_SERIALIZE, _LENGTH_LEN, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, (void_fn_t)_SERIALIZEFCN}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define M_CALLBACK(_STRUCT, _CSNCALLBACKFCN, _PARAM1, _PARAM2)\
 | 
					#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)
 | 
					 * 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
 | 
					 * is fixed and provided by the parameter Par3
 | 
				
			||||||
 *      Par1: C structure name
 | 
					 *      Par1: C structure name
 | 
				
			||||||
 *      Par2: C structure element name
 | 
					 *      Par2: C structure element name
 | 
				
			||||||
 *      Par3: length of the bitmap expressed in bits
 | 
					 *      Par3: length of the bitmap expressed in bits
 | 
				
			||||||
 *****************************************************************************/
 | 
					 *****************************************************************************/
 | 
				
			||||||
#define M_BITMAP(_STRUCT, _MEMBER, _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 */
 | 
					/* variable length, right aligned bitmap i.e. _ElementCountField = 11 => 00000111 11111111 */
 | 
				
			||||||
#define M_VAR_BITMAP(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
 | 
					#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
 | 
					/* variable length, right aligned bitmap filling the rest of message
 | 
				
			||||||
 * - when unpacking the _ElementCountField will be set in runtime
 | 
					 * - when unpacking the _ElementCountField will be set in runtime
 | 
				
			||||||
 * - when packing _ElementCountField contains the size of bitmap
 | 
					 * - when packing _ElementCountField contains the size of bitmap
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#define M_VAR_BITMAP_1(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
 | 
					#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 */
 | 
					/* variable length, left aligned bitmap i.e. _ElementCountField = 11 => 11111111 11100000 */
 | 
				
			||||||
#define M_LEFT_VAR_BMP(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
 | 
					#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
 | 
					/* variable length, left aligned bitmap filling the rest of message
 | 
				
			||||||
 *- when unpacking the _ElementCountField will be set in runtime
 | 
					 *- when unpacking the _ElementCountField will be set in runtime
 | 
				
			||||||
 * - when packing _ElementCountField contains the size of bitmap
 | 
					 * - when packing _ElementCountField contains the size of bitmap
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#define M_LEFT_VAR_BMP_1(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
 | 
					#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)\
 | 
					/* todo: dissect padding bits looking for unexpected extensions */
 | 
				
			||||||
        {CSN_NULL, 0, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
 | 
					#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)\
 | 
					#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)\
 | 
					#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 */
 | 
					/* return value 0 if ok else discontionue the unpacking */
 | 
				
			||||||
typedef gint16 (*CsnCallBackFcn_t)(void* pv ,...);
 | 
					typedef gint16 (*CsnCallBackFcn_t)(void* pv ,...);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define CSNDESCR(_FuncType) CSNDESCR_##_FuncType
 | 
					#define CSNDESCR(_FuncType) CSNDESCR_##_FuncType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define pvDATA(_pv, _offset) ((void*) ((unsigned char*)_pv + _offset))
 | 
				
			||||||
 | 
					#define pui8DATA(_pv, _offset) ((guint8*) pvDATA(_pv, _offset))
 | 
				
			||||||
 | 
					#define pui16DATA(_pv, _offset) ((guint16*) pvDATA(_pv, _offset))
 | 
				
			||||||
 | 
					#define pui32DATA(_pv, _offset) ((guint32*) pvDATA(_pv, _offset))
 | 
				
			||||||
 | 
					#define pui64DATA(_pv, _offset) ((guint64*) pvDATA(_pv, _offset))
 | 
				
			||||||
 | 
					/* used to tag existence of next element in variable length lists */
 | 
				
			||||||
 | 
					#define STANDARD_TAG 1
 | 
				
			||||||
 | 
					#define REVERSED_TAG 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gint16 ProcessError_impl(const char *file, int line, unsigned *readIndex,
 | 
				
			||||||
 | 
					                                const char* sz, gint16 err, const CSN_DESCR* pDescr);
 | 
				
			||||||
 | 
					#define ProcessError(readIndex, sz, err, pDescr) \
 | 
				
			||||||
 | 
					        ProcessError_impl(__FILE__, __LINE__, readIndex, sz, err, pDescr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /*_PACKET_CSN1_H_*/
 | 
					#endif /*_PACKET_CSN1_H_*/
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1424
									
								
								src/csn1_dec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1424
									
								
								src/csn1_dec.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1301
									
								
								src/csn1_enc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1301
									
								
								src/csn1_enc.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										129
									
								
								src/cxx_linuxlist.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/cxx_linuxlist.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										784
									
								
								src/decoding.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										784
									
								
								src/decoding.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,784 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <decoding.h>
 | 
				
			||||||
 | 
					#include <rlc.h>
 | 
				
			||||||
 | 
					#include <rlc_window.h>
 | 
				
			||||||
 | 
					#include <gprs_debug.h>
 | 
				
			||||||
 | 
					#include <egprs_rlc_compression.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/bitcomp.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/protocol/gsm_44_060.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 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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										99
									
								
								src/decoding.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/decoding.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "rlc_window_dl.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 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();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										1804
									
								
								src/encoding.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1804
									
								
								src/encoding.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										128
									
								
								src/encoding.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/encoding.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#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,
 | 
				
			||||||
 | 
								const struct gprs_rlcmac_tbf *tbf,
 | 
				
			||||||
 | 
								bitvec * dest, bool downlink, uint16_t ra,
 | 
				
			||||||
 | 
								uint16_t ref_rfn, uint8_t ta,
 | 
				
			||||||
 | 
								uint8_t usf, bool polling,
 | 
				
			||||||
 | 
								uint16_t rfn, 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,
 | 
				
			||||||
 | 
								uint16_t ref_rfn,
 | 
				
			||||||
 | 
								enum ph_burst_type burst_type,
 | 
				
			||||||
 | 
								uint8_t t3142
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static void encode_rbb(const char *show_rbb, bitvec *rbb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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_access_reject(struct bitvec *dest, uint32_t tlli, unsigned long t3172_ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int write_paging_request(struct bitvec *dest, const struct osmo_mobile_identity *mi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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,
 | 
				
			||||||
 | 
									      uint8_t control_ack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void write_packet_uplink_ack(struct bitvec *dest, struct gprs_rlcmac_ul_tbf *tbf,
 | 
				
			||||||
 | 
								     bool is_final, uint8_t rrbp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										53
									
								
								src/ericsson-rbs/er_ccu_descr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/ericsson-rbs/er_ccu_descr.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <osmocom/abis/e1_input.h>
 | 
				
			||||||
 | 
					#include <osmocom/trau/trau_pcu_ericsson.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct er_ccu_descr;
 | 
				
			||||||
 | 
					struct e1_conn_pars;
 | 
				
			||||||
 | 
					typedef void (er_ccu_empty) (struct er_ccu_descr *ccu_descr);
 | 
				
			||||||
 | 
					typedef void (er_ccu_rx) (struct er_ccu_descr *ccu_descr, const ubit_t *bits, unsigned int num_bits);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct er_ccu_descr {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* E1-line and timeslot (filled in by user) */
 | 
				
			||||||
 | 
						struct e1_conn_pars *e1_conn_pars;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Callback functions (provided by user) */
 | 
				
			||||||
 | 
						er_ccu_empty *er_ccu_empty_cb;
 | 
				
			||||||
 | 
						er_ccu_rx *er_ccu_rx_cb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* I.460 Subslot */
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
							struct osmo_i460_schan_desc scd;
 | 
				
			||||||
 | 
							struct osmo_i460_subchan *schan;
 | 
				
			||||||
 | 
							struct osmo_fsm_inst *trau_sync_fi;
 | 
				
			||||||
 | 
							bool ccu_connected;
 | 
				
			||||||
 | 
						} link;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* TRAU Sync state */
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
							uint32_t pseq_ccu; /* CCU sequence counter (remote) */
 | 
				
			||||||
 | 
							uint32_t pseq_pcu; /* PCU sequence counter (local) */
 | 
				
			||||||
 | 
							uint32_t last_afn_ul; /* Adjusted frame number, uplink */
 | 
				
			||||||
 | 
							uint32_t last_afn_dl; /* Adjusted frame number, downlink */
 | 
				
			||||||
 | 
							enum time_adj_val tav; /* Last time adjustment value */
 | 
				
			||||||
 | 
							bool ul_frame_err; /* True when last uplink TRAU frame was bad */
 | 
				
			||||||
 | 
							bool ccu_synced; /* True when PCU is in sync with CCU */
 | 
				
			||||||
 | 
						} sync;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* PCU related context */
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
							uint8_t trx_no;
 | 
				
			||||||
 | 
							uint8_t bts_nr;
 | 
				
			||||||
 | 
							uint8_t ts;
 | 
				
			||||||
 | 
						} pcu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct er_trx_descr {
 | 
				
			||||||
 | 
					       struct er_ccu_descr ts_ccu_descr[8];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										416
									
								
								src/ericsson-rbs/er_ccu_if.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								src/ericsson-rbs/er_ccu_if.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,416 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Philipp Maier
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <er_ccu_if.h>
 | 
				
			||||||
 | 
					#include <er_ccu_descr.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/abis/e1_input.h>
 | 
				
			||||||
 | 
					#include <osmocom/abis/abis.h>
 | 
				
			||||||
 | 
					#include <osmocom/trau/trau_sync.h>
 | 
				
			||||||
 | 
					#include <osmocom/trau/trau_pcu_ericsson.h>
 | 
				
			||||||
 | 
					#include <bts.h>
 | 
				
			||||||
 | 
					#include <gprs_debug.h>
 | 
				
			||||||
 | 
					#include <pcu_l1_if.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define E1_TS_BYTES 160
 | 
				
			||||||
 | 
					#define DEBUG_BITS_MAX 1280
 | 
				
			||||||
 | 
					#define DEBUG_BYTES_MAX 40
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOGPCCU(ccu_descr, level, tag, fmt, args...) \
 | 
				
			||||||
 | 
						LOGP(DE1, level, "E1TS(%u:%u:%u) %s:" fmt, \
 | 
				
			||||||
 | 
						     ccu_descr->e1_conn_pars->e1_nr, ccu_descr->e1_conn_pars->e1_ts, \
 | 
				
			||||||
 | 
						     ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL ? 0 : ccu_descr->e1_conn_pars->e1_ts_ss, tag, \
 | 
				
			||||||
 | 
						     ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct e1_ts_descr {
 | 
				
			||||||
 | 
						uint8_t usecount;
 | 
				
			||||||
 | 
						bool i460_ts_initialized;
 | 
				
			||||||
 | 
						struct osmo_i460_timeslot i460_ts;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct e1_line_descr {
 | 
				
			||||||
 | 
						struct e1_ts_descr e1_ts[NUM_E1_TS - 1];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct e1_line_descr e1_lines[32];
 | 
				
			||||||
 | 
					static void *tall_ccu_ctx = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct e1inp_line_ops dummy_e1_line_ops = {
 | 
				
			||||||
 | 
						.sign_link_up = NULL,
 | 
				
			||||||
 | 
						.sign_link_down = NULL,
 | 
				
			||||||
 | 
						.sign_link = NULL,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* called by trau frame synchronizer: feed received MAC blocks into PCU */
 | 
				
			||||||
 | 
					static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct er_ccu_descr *ccu_descr = user_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!bits || num_bits == 0)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPCCU(ccu_descr, LOGL_DEBUG, "I.460-RX", "receiving %u TRAU frame bits from subslot (synchronized): %s...\n",
 | 
				
			||||||
 | 
							num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ccu_descr->er_ccu_rx_cb(ccu_descr, bits, num_bits);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* called by I.460 de-multiplexer: feed output of I.460 demux into TRAU frame sync */
 | 
				
			||||||
 | 
					static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
 | 
				
			||||||
 | 
									  unsigned int num_bits)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct er_ccu_descr *ccu_descr = user_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPCCU(ccu_descr, LOGL_DEBUG, "I.460-RX", "receiving %u TRAU frame bits from subslot: %s...\n", num_bits,
 | 
				
			||||||
 | 
							osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_ASSERT(ccu_descr->link.trau_sync_fi);
 | 
				
			||||||
 | 
						osmo_trau_sync_rx_ubits(ccu_descr->link.trau_sync_fi, bits, num_bits);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* called by I.460 de-multiplexer: ensure that sync indications are sent when mux buffer runs empty */
 | 
				
			||||||
 | 
					static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan2, void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct er_ccu_descr *ccu_descr = user_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPCCU(ccu_descr, LOGL_DEBUG, "I.460-TX", "demux buffer empty\n");
 | 
				
			||||||
 | 
						ccu_descr->er_ccu_empty_cb(ccu_descr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* handle outgoing E1 traffic */
 | 
				
			||||||
 | 
					static void e1_send_ts_frame(struct e1inp_ts *ts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						void *ctx = tall_ccu_ctx;
 | 
				
			||||||
 | 
						struct e1_ts_descr *ts_descr;
 | 
				
			||||||
 | 
						struct msgb *msg;
 | 
				
			||||||
 | 
						uint8_t *ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* The line number and ts number that arrives here should be clean. */
 | 
				
			||||||
 | 
						OSMO_ASSERT(ts->line->num < ARRAY_SIZE(e1_lines));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ts_descr = &e1_lines[ts->line->num].e1_ts[ts->num];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Do not send anything in case the E1 timeslot is not ready. */
 | 
				
			||||||
 | 
						if (ts_descr->usecount == 0)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Get E1 frame from I.460 multiplexer */
 | 
				
			||||||
 | 
						msg = msgb_alloc_c(ctx, E1_TS_BYTES, "E1-TX-timeslot-bytes");
 | 
				
			||||||
 | 
						ptr = msgb_put(msg, E1_TS_BYTES);
 | 
				
			||||||
 | 
						osmo_i460_mux_out(&ts_descr->i460_ts, ptr, E1_TS_BYTES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPITS(ts, DE1, LOGL_DEBUG, "E1-TX: sending %u bytes: %s...\n",
 | 
				
			||||||
 | 
						     msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
 | 
				
			||||||
 | 
											  msgb_length(msg) >
 | 
				
			||||||
 | 
											  DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Hand data over to the E1 stack */
 | 
				
			||||||
 | 
						msgb_enqueue(&ts->raw.tx_queue, msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Callback function to handle incoming E1 traffic */
 | 
				
			||||||
 | 
					static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct e1_ts_descr *ts_descr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (msg->len != E1_TS_BYTES) {
 | 
				
			||||||
 | 
							LOGPITS(ts, DE1, LOGL_ERROR,
 | 
				
			||||||
 | 
							     "E1-RX: receiving bad, expected length is %u, actual length is %u!\n",
 | 
				
			||||||
 | 
							     E1_TS_BYTES, msg->len);
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPITS(ts, DE1, LOGL_DEBUG, "E1-RX: receiving %u bytes: %s ...\n",
 | 
				
			||||||
 | 
							msg->len, osmo_hexdump_nospc(msg->data, msg->len));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Note: The line number and ts number that arrives here should be clean. */
 | 
				
			||||||
 | 
						OSMO_ASSERT(ts->line->num < ARRAY_SIZE(e1_lines));
 | 
				
			||||||
 | 
						ts_descr = &e1_lines[ts->line->num].e1_ts[ts->num];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Hand data over to the I640 demultiplexer. */
 | 
				
			||||||
 | 
						osmo_i460_demux_in(&ts_descr->i460_ts, msg->data, msg->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Trigger sending of pending E1 traffic */
 | 
				
			||||||
 | 
						e1_send_ts_frame(ts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* e1inp_rx_ts(), the caller of this callback does not free() msgb. */
 | 
				
			||||||
 | 
						msgb_free(msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct e1_ts_descr *ts_descr_from_ccu_descr(struct er_ccu_descr *ccu_descr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Make sure E1 line number is valid */
 | 
				
			||||||
 | 
						if (ccu_descr->e1_conn_pars->e1_nr >= ARRAY_SIZE(e1_lines)) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "Invalid E1 line number!\n");
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Make sure E1 timeslot number is valid */
 | 
				
			||||||
 | 
						if (ccu_descr->e1_conn_pars->e1_ts < 1 || ccu_descr->e1_conn_pars->e1_ts > NUM_E1_TS - 1) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "Invalid E1 timeslot number!\n");
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Timeslots are only initialized once and will stay open after that. */
 | 
				
			||||||
 | 
						return &e1_lines[ccu_descr->e1_conn_pars->e1_nr].e1_ts[ccu_descr->e1_conn_pars->e1_ts];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Configure an I.460 subslot and add it to the CCU descriptor */
 | 
				
			||||||
 | 
					static int add_i460_subslot(void *ctx, struct er_ccu_descr *ccu_descr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct e1_ts_descr *ts_descr;
 | 
				
			||||||
 | 
						enum osmo_tray_sync_pat_id sync_pattern;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ccu_descr->link.schan) {
 | 
				
			||||||
 | 
							/* NOTE: This is a serious error: subslots should be removed when l1if_close_trx() is called by the
 | 
				
			||||||
 | 
							 * PCU. This log line points towards a problem with the PDCH management inside the PCU! */
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "I.460 subslot is already configured -- will not touch it!\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ts_descr = ts_descr_from_ccu_descr(ccu_descr);
 | 
				
			||||||
 | 
						if (!ts_descr)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						if (ts_descr->usecount == 0) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "E1 timeslot not ready!\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Set up I.460 subchannel and connect it to the MUX on the E1 timeslot */
 | 
				
			||||||
 | 
						if (ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_INFO, "SETUP", "using 64k subslots\n");
 | 
				
			||||||
 | 
							ccu_descr->link.scd.rate = OSMO_I460_RATE_64k;
 | 
				
			||||||
 | 
							ccu_descr->link.scd.demux.num_bits = E1_TS_BYTES * 8;
 | 
				
			||||||
 | 
							ccu_descr->link.scd.bit_offset = 0;
 | 
				
			||||||
 | 
							sync_pattern = OSMO_TRAU_SYNCP_64_ER_CCU;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_INFO, "SETUP", "using 16k subslots\n");
 | 
				
			||||||
 | 
							if (ccu_descr->e1_conn_pars->e1_ts_ss > 3) {
 | 
				
			||||||
 | 
								LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "Invalid I.460 subslot number!\n");
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ccu_descr->link.scd.rate = OSMO_I460_RATE_16k;
 | 
				
			||||||
 | 
							ccu_descr->link.scd.demux.num_bits = E1_TS_BYTES / 4 * 8;
 | 
				
			||||||
 | 
							ccu_descr->link.scd.bit_offset = ccu_descr->e1_conn_pars->e1_ts_ss * 2;
 | 
				
			||||||
 | 
							sync_pattern = OSMO_TRAU_SYNCP_16_ER_CCU;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ccu_descr->link.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
 | 
				
			||||||
 | 
						ccu_descr->link.scd.demux.out_cb_bytes = NULL;
 | 
				
			||||||
 | 
						ccu_descr->link.scd.demux.user_data = ccu_descr;
 | 
				
			||||||
 | 
						ccu_descr->link.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
 | 
				
			||||||
 | 
						ccu_descr->link.scd.mux.user_data = ccu_descr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPCCU(ccu_descr, LOGL_INFO, "SETUP", "adding I.460 subchannel: bit_offset=%u, num_bits=%zu\n",
 | 
				
			||||||
 | 
							ccu_descr->link.scd.bit_offset, ccu_descr->link.scd.demux.num_bits);
 | 
				
			||||||
 | 
						ccu_descr->link.schan = osmo_i460_subchan_add(ctx, &ts_descr->i460_ts, &ccu_descr->link.scd);
 | 
				
			||||||
 | 
						if (!ccu_descr->link.schan) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "adding I.460 subchannel: failed!\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Configure TRAU synchronizer */
 | 
				
			||||||
 | 
						ccu_descr->link.trau_sync_fi = osmo_trau_sync_alloc(tall_ccu_ctx, "trau-sync", sync_frame_out_cb, sync_pattern, ccu_descr);
 | 
				
			||||||
 | 
						if (!ccu_descr->link.trau_sync_fi) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "adding I.460 TRAU frame sync: failed!\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Ericsson uses a different synchronization pattern for MCS9 TRAU frames */
 | 
				
			||||||
 | 
						if (sync_pattern == OSMO_TRAU_SYNCP_64_ER_CCU)
 | 
				
			||||||
 | 
							osmo_trau_sync_set_secondary_pat(ccu_descr->link.trau_sync_fi, OSMO_TRAU_SYNCP_64_ER_CCU_MCS9, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Remove an I.460 subslot from the CCU descriptor */
 | 
				
			||||||
 | 
					static void del_i460_subslot(struct er_ccu_descr *ccu_descr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (ccu_descr->link.schan)
 | 
				
			||||||
 | 
							osmo_i460_subchan_del(ccu_descr->link.schan);
 | 
				
			||||||
 | 
						ccu_descr->link.schan = NULL;
 | 
				
			||||||
 | 
						if (ccu_descr->link.trau_sync_fi)
 | 
				
			||||||
 | 
							osmo_fsm_inst_term(ccu_descr->link.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
 | 
				
			||||||
 | 
						ccu_descr->link.trau_sync_fi = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(&ccu_descr->link.scd, 0, sizeof(ccu_descr->link.scd));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Configure an E1 timeslot according to the description in the ccu_descr */
 | 
				
			||||||
 | 
					static int open_e1_timeslot(struct er_ccu_descr *ccu_descr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct e1inp_line *e1_line;
 | 
				
			||||||
 | 
						struct e1_ts_descr *ts_descr;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Find timeslot descriptor and check if the timeslot is already open. */
 | 
				
			||||||
 | 
						ts_descr = ts_descr_from_ccu_descr(ccu_descr);
 | 
				
			||||||
 | 
						if (!ts_descr)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						if (ts_descr->usecount > 0) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_DEBUG, "SETUP", "E1 timeslot already open -- using it as it is!\n");
 | 
				
			||||||
 | 
							ts_descr->usecount++;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Find and set up E1 line */
 | 
				
			||||||
 | 
						e1_line = e1inp_line_find(ccu_descr->e1_conn_pars->e1_nr);
 | 
				
			||||||
 | 
						if (!e1_line) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "no such E1 line!\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Set up E1 timeslot */
 | 
				
			||||||
 | 
						rc = e1inp_ts_config_raw(&e1_line->ts[ccu_descr->e1_conn_pars->e1_ts - 1], e1_line, e1_recv_cb);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "configuration of timeslot failed!\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rc = e1inp_line_update(e1_line);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "line update failed!\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Make sure the i460 mux is ready */
 | 
				
			||||||
 | 
						if (!ts_descr->i460_ts_initialized) {
 | 
				
			||||||
 | 
							osmo_i460_ts_init(&ts_descr->i460_ts);
 | 
				
			||||||
 | 
							ts_descr->i460_ts_initialized = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ts_descr->usecount++;
 | 
				
			||||||
 | 
						OSMO_ASSERT(ts_descr->usecount == 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Configure an E1 timeslot according to the description in the ccu_descr */
 | 
				
			||||||
 | 
					static int close_e1_timeslot(struct er_ccu_descr *ccu_descr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct e1inp_line *e1_line;
 | 
				
			||||||
 | 
						struct e1_ts_descr *ts_descr;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Find timeslot descriptor and check if the timeslot is still used by another subslot. */
 | 
				
			||||||
 | 
						ts_descr = ts_descr_from_ccu_descr(ccu_descr);
 | 
				
			||||||
 | 
						if (!ts_descr)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						if (ts_descr->usecount > 1) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_DEBUG, "SETUP",
 | 
				
			||||||
 | 
								"E1 timeslot still in used by another subslot, leaving it open!\n");
 | 
				
			||||||
 | 
							ts_descr->usecount--;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						} else if (ts_descr->usecount == 0) {
 | 
				
			||||||
 | 
							/* This should not be as it means we close the timeslot too often. */
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "E1 timeslot already closed, leaving it as it is...\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Find E1 line */
 | 
				
			||||||
 | 
						e1_line = e1inp_line_find(ccu_descr->e1_conn_pars->e1_nr);
 | 
				
			||||||
 | 
						if (!e1_line) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "no such E1 line!\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Release E1 timeslot */
 | 
				
			||||||
 | 
						rc = e1inp_ts_config_none(&e1_line->ts[ccu_descr->e1_conn_pars->e1_ts - 1], e1_line);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "failed to disable E1 timeslot!\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rc = e1inp_line_update(e1_line);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "failed to update E1 line!\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ts_descr->usecount--;
 | 
				
			||||||
 | 
						OSMO_ASSERT(ts_descr->usecount == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int er_ccu_if_open(struct er_ccu_descr *ccu_descr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (ccu_descr->link.ccu_connected) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP",
 | 
				
			||||||
 | 
								"cannot connect CCU since it is already connected -- ignored!\n");
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (open_e1_timeslot(ccu_descr) < 0)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (add_i460_subslot(tall_ccu_ctx, ccu_descr) < 0)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ccu_descr->link.ccu_connected = true;
 | 
				
			||||||
 | 
						LOGPCCU(ccu_descr, LOGL_DEBUG, "SETUP", "CCU connected.\n");
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void er_ccu_if_close(struct er_ccu_descr *ccu_descr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!ccu_descr->link.ccu_connected) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP",
 | 
				
			||||||
 | 
								"cannot disconnect CCU since it is already disconnected -- ignored!\n");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						del_i460_subslot(ccu_descr);
 | 
				
			||||||
 | 
						close_e1_timeslot(ccu_descr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ccu_descr->link.ccu_connected = false;
 | 
				
			||||||
 | 
						LOGPCCU(ccu_descr, LOGL_DEBUG, "SETUP", "CCU disconnected.\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void er_ccu_if_tx(struct er_ccu_descr *ccu_descr, const ubit_t *bits, unsigned int num_bits)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *msg;
 | 
				
			||||||
 | 
						uint8_t *ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ccu_descr->link.ccu_connected) {
 | 
				
			||||||
 | 
							LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "cannot TX block, CCU is disconnected -- ignored!\n");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg = msgb_alloc_c(tall_ccu_ctx, num_bits, "E1-I.460-PCU-IND-frame");
 | 
				
			||||||
 | 
						ptr = msgb_put(msg, num_bits);
 | 
				
			||||||
 | 
						memcpy(ptr, bits, num_bits);
 | 
				
			||||||
 | 
						LOGPCCU(ccu_descr, LOGL_DEBUG, "I.460-TX", "sending %u bits: %s...\n", msgb_length(msg),
 | 
				
			||||||
 | 
							osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
 | 
				
			||||||
 | 
						osmo_i460_mux_enqueue(ccu_descr->link.schan, msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void er_ccu_if_init(void *ctx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						libosmo_abis_init(ctx);
 | 
				
			||||||
 | 
						e1inp_vty_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tall_ccu_ctx = talloc_new(ctx);
 | 
				
			||||||
 | 
						memset(e1_lines, 0, sizeof(e1_lines));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								src/ericsson-rbs/er_ccu_if.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/ericsson-rbs/er_ccu_if.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <osmocom/abis/e1_input.h>
 | 
				
			||||||
 | 
					#include "er_ccu_descr.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int er_ccu_if_open(struct er_ccu_descr *ccu_descr);
 | 
				
			||||||
 | 
					void er_ccu_if_close(struct er_ccu_descr *ccu_descr);
 | 
				
			||||||
 | 
					void er_ccu_if_tx(struct er_ccu_descr *ccu_descr, const ubit_t *bits, unsigned int num_bits);
 | 
				
			||||||
 | 
					void er_ccu_if_init(void *ctx);
 | 
				
			||||||
							
								
								
									
										543
									
								
								src/ericsson-rbs/er_ccu_l1_if.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										543
									
								
								src/ericsson-rbs/er_ccu_l1_if.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,543 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Philipp Maier
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <er_ccu_descr.h>
 | 
				
			||||||
 | 
					#include <er_ccu_if.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/pcu/pcuif_proto.h>
 | 
				
			||||||
 | 
					#include <osmocom/abis/e1_input.h>
 | 
				
			||||||
 | 
					#include <osmocom/abis/abis.h>
 | 
				
			||||||
 | 
					#include <osmocom/abis/e1_input.h>
 | 
				
			||||||
 | 
					#include <osmocom/trau/trau_sync.h>
 | 
				
			||||||
 | 
					#include <osmocom/trau/trau_pcu_ericsson.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm0502.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/talloc.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <bts.h>
 | 
				
			||||||
 | 
					#include <pcu_l1_if.h>
 | 
				
			||||||
 | 
					#include <pcu_l1_if_phy.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern void *tall_pcu_ctx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uint8_t fn_inc_table[4] = { 4, 4, 5, 0 };
 | 
				
			||||||
 | 
					const uint8_t blk_nr_table[4] = { 4, 4, 5, 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SYNC_CHECK_INTERVAL GSM_TDMA_SUPERFRAME * 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Subtrahend to convert Ericsson adjusted (block ending) fn to regular fn (uplink only) */
 | 
				
			||||||
 | 
					#define AFN_SUBTRAHEND 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOGPL1IF(ccu_descr, level, tag, fmt, args...)	       \
 | 
				
			||||||
 | 
						LOGP(DL1IF, level, "%s: PDCH(trx=%u,ts=%u) E1-line(line=%u,ts=%u,ss=%u) " fmt, \
 | 
				
			||||||
 | 
						     tag, ccu_descr->pcu.trx_no, ccu_descr->pcu.ts, \
 | 
				
			||||||
 | 
						     ccu_descr->e1_conn_pars->e1_nr, ccu_descr->e1_conn_pars->e1_ts, \
 | 
				
			||||||
 | 
						     ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL ? 0 : ccu_descr->e1_conn_pars->e1_ts_ss, \
 | 
				
			||||||
 | 
						     ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Calculate GPRS block number from frame number */
 | 
				
			||||||
 | 
					static uint8_t fn_to_block_nr(uint32_t fn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Note: See also 3GPP TS 03.64 6.5.7.2.1,
 | 
				
			||||||
 | 
						 * Mapping on the multiframe structure */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint8_t rel_fn;
 | 
				
			||||||
 | 
						uint8_t super_block;
 | 
				
			||||||
 | 
						uint8_t local_block;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rel_fn = fn % 52;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Warn in case of frames that do not belong to a block */
 | 
				
			||||||
 | 
						if (rel_fn == 12 || rel_fn == 25 || rel_fn == 38 || rel_fn == 51)
 | 
				
			||||||
 | 
							LOGP(DL1IF, LOGL_ERROR, "Frame number is referencing invalid block!\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						super_block = (rel_fn / 13);
 | 
				
			||||||
 | 
						local_block = rel_fn % 13 / 4;
 | 
				
			||||||
 | 
						return super_block * 3 + local_block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint32_t fn_dl_advance(uint32_t fn, uint32_t n_blocks)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint32_t i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint8_t inc_fn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < n_blocks; i++) {
 | 
				
			||||||
 | 
							inc_fn = fn_inc_table[(fn % 13) / 4];
 | 
				
			||||||
 | 
							fn = GSM_TDMA_FN_SUM(fn, inc_fn);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return fn;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool mac_block_is_noise(struct er_gprs_trau_frame *trau_frame)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (trau_frame->u.ccu_data_ind.cs_hdr) {
 | 
				
			||||||
 | 
						case CS_OR_HDR_CS1:
 | 
				
			||||||
 | 
						case CS_OR_HDR_CS2:
 | 
				
			||||||
 | 
						case CS_OR_HDR_CS3:
 | 
				
			||||||
 | 
						case CS_OR_HDR_CS4:
 | 
				
			||||||
 | 
							if (!trau_frame->u.ccu_data_ind.u.gprs.parity_ok)
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CS_OR_HDR_HDR1:
 | 
				
			||||||
 | 
						case CS_OR_HDR_HDR2:
 | 
				
			||||||
 | 
						case CS_OR_HDR_HDR3:
 | 
				
			||||||
 | 
							if (!trau_frame->u.ccu_data_ind.u.egprs.hdr_good)
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							if (!trau_frame->u.ccu_data_ind.u.egprs.data_good[0]
 | 
				
			||||||
 | 
							    && !trau_frame->u.ccu_data_ind.u.egprs.data_good[1])
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CS_OR_HDR_AB:
 | 
				
			||||||
 | 
							/* We are not interested in receiving access bursts. */
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* No noise, this block is interesting for us. */
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void log_data_ind(struct er_ccu_descr *ccu_descr, struct er_gprs_trau_frame *trau_frame, uint32_t afn_ul_comp,
 | 
				
			||||||
 | 
								 uint32_t afn_dl_comp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (trau_frame->u.ccu_data_ind.cs_hdr) {
 | 
				
			||||||
 | 
						case CS_OR_HDR_CS1:
 | 
				
			||||||
 | 
						case CS_OR_HDR_CS2:
 | 
				
			||||||
 | 
						case CS_OR_HDR_CS3:
 | 
				
			||||||
 | 
						case CS_OR_HDR_CS4:
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-DATA-IND",
 | 
				
			||||||
 | 
								 "tav=%u, dbe=%u, cs_hdr=%u, rx_lev=%u, est_acc_del_dev=%u,"
 | 
				
			||||||
 | 
								 "block_qual=%u, parity_ok=%u, data=%s<==, afn_ul_comp=%u/%u\n", trau_frame->u.ccu_data_ind.tav,
 | 
				
			||||||
 | 
								 trau_frame->u.ccu_data_ind.dbe, trau_frame->u.ccu_data_ind.cs_hdr,
 | 
				
			||||||
 | 
								 trau_frame->u.ccu_data_ind.rx_lev, trau_frame->u.ccu_data_ind.est_acc_del_dev,
 | 
				
			||||||
 | 
								 trau_frame->u.ccu_data_ind.u.gprs.block_qual, trau_frame->u.ccu_data_ind.u.gprs.parity_ok,
 | 
				
			||||||
 | 
								 osmo_hexdump_nospc(trau_frame->u.ccu_data_ind.data, trau_frame->u.ccu_data_ind.data_len),
 | 
				
			||||||
 | 
								 afn_ul_comp, afn_ul_comp % 52);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CS_OR_HDR_HDR1:
 | 
				
			||||||
 | 
						case CS_OR_HDR_HDR2:
 | 
				
			||||||
 | 
						case CS_OR_HDR_HDR3:
 | 
				
			||||||
 | 
						case CS_OR_HDR_AB:
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-DATA-IND",
 | 
				
			||||||
 | 
								 "tav=%u, dbe=%u, cs_hdr=%u, rx_lev=%u, est_acc_del_dev=%u,"
 | 
				
			||||||
 | 
								 "mean_bep=%u, cv_bep=%u, hdr_good=%u, data_good[0]=%u, data_good[1]=%u, data=%s<==, afn_ul_comp=%u/%u\n",
 | 
				
			||||||
 | 
								 trau_frame->u.ccu_data_ind.tav, trau_frame->u.ccu_data_ind.dbe,
 | 
				
			||||||
 | 
								 trau_frame->u.ccu_data_ind.cs_hdr, trau_frame->u.ccu_data_ind.rx_lev,
 | 
				
			||||||
 | 
								 trau_frame->u.ccu_data_ind.est_acc_del_dev, trau_frame->u.ccu_data_ind.u.egprs.mean_bep,
 | 
				
			||||||
 | 
								 trau_frame->u.ccu_data_ind.u.egprs.cv_bep, trau_frame->u.ccu_data_ind.u.egprs.hdr_good,
 | 
				
			||||||
 | 
								 trau_frame->u.ccu_data_ind.u.egprs.data_good[0],
 | 
				
			||||||
 | 
								 trau_frame->u.ccu_data_ind.u.egprs.data_good[1],
 | 
				
			||||||
 | 
								 osmo_hexdump_nospc(trau_frame->u.ccu_data_ind.data, trau_frame->u.ccu_data_ind.data_len),
 | 
				
			||||||
 | 
								 afn_ul_comp, afn_ul_comp % 52);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Receive block from CCU */
 | 
				
			||||||
 | 
					static void er_ccu_rx_cb(struct er_ccu_descr *ccu_descr, const ubit_t *bits, unsigned int num_bits)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						struct er_gprs_trau_frame trau_frame;
 | 
				
			||||||
 | 
						uint8_t inc_ul;
 | 
				
			||||||
 | 
						uint8_t inc_dl;
 | 
				
			||||||
 | 
						uint32_t afn_ul;
 | 
				
			||||||
 | 
						uint32_t afn_dl;
 | 
				
			||||||
 | 
						uint32_t afn_ul_comp;
 | 
				
			||||||
 | 
						uint32_t afn_dl_comp;
 | 
				
			||||||
 | 
						struct pcu_l1_meas meas = { 0 };
 | 
				
			||||||
 | 
						struct gprs_rlcmac_bts *bts;
 | 
				
			||||||
 | 
						struct gprs_rlcmac_pdch *pdch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Compute the current frame numbers from the last frame number */
 | 
				
			||||||
 | 
						inc_ul = fn_inc_table[(ccu_descr->sync.last_afn_ul % 13) / 4];
 | 
				
			||||||
 | 
						inc_dl = fn_inc_table[(ccu_descr->sync.last_afn_dl % 13) / 4];
 | 
				
			||||||
 | 
						afn_ul = GSM_TDMA_FN_SUM(ccu_descr->sync.last_afn_ul, inc_ul);
 | 
				
			||||||
 | 
						afn_dl = GSM_TDMA_FN_SUM(ccu_descr->sync.last_afn_dl, inc_dl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Compute compensated frame numbers. This will be the framenumbers we
 | 
				
			||||||
 | 
						 * will use to exchange blocks with the PCU code. The following applies:
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * 1. The uplink related frame numbers sent by the ericsson CCU refer to the end of a block. This is
 | 
				
			||||||
 | 
						 *    compensated by subtracting three frames.
 | 
				
			||||||
 | 
						 * 2. The CCU downlink frame number runs one block past the uplink frame number. This needs to be
 | 
				
			||||||
 | 
						 *    compesated as well (+1).
 | 
				
			||||||
 | 
						 * 3. The difference between the local (PCU) and the returned (CCU) pseq counter value is the number of blocks
 | 
				
			||||||
 | 
						 *    that the PCU must
 | 
				
			||||||
 | 
						 *    shift its downlink alignment in order to compensate the link latency between PCU and CCU. */
 | 
				
			||||||
 | 
						afn_ul_comp = GSM_TDMA_FN_SUB(afn_ul, AFN_SUBTRAHEND);
 | 
				
			||||||
 | 
						afn_dl_comp = afn_dl;
 | 
				
			||||||
 | 
						afn_dl_comp = fn_dl_advance(afn_dl_comp, GSM_TDMA_FN_DIFF(ccu_descr->sync.pseq_pcu, ccu_descr->sync.pseq_ccu) + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-SYNC",
 | 
				
			||||||
 | 
							 "afn_ul=%u/%u, afn_dl=%u/%u, afn_diff=%u => afn_ul_comp=%u/%u, afn_dl_comp=%u/%u, afn_diff_comp=%u\n",
 | 
				
			||||||
 | 
							 afn_ul, afn_ul % 52, afn_dl, afn_dl % 52, GSM_TDMA_FN_DIFF(afn_ul, afn_dl), afn_ul_comp,
 | 
				
			||||||
 | 
							 afn_ul_comp % 52, afn_dl_comp, afn_dl_comp % 52, GSM_TDMA_FN_DIFF(afn_ul_comp, afn_dl_comp));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-SYNC", "pseq_pcu=%u, pseq_ccu=%u, pseq_diff=%u\n",
 | 
				
			||||||
 | 
							 ccu_descr->sync.pseq_pcu, ccu_descr->sync.pseq_ccu, GSM_TDMA_FN_DIFF(ccu_descr->sync.pseq_pcu, ccu_descr->sync.pseq_ccu));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Decode indication from CCU */
 | 
				
			||||||
 | 
						if (ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL)
 | 
				
			||||||
 | 
							rc = er_gprs_trau_frame_decode_64k(&trau_frame, bits);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							rc = er_gprs_trau_frame_decode_16k(&trau_frame, bits);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_ERROR, "CCU-XXXX-IND",
 | 
				
			||||||
 | 
								 "unable to decode uplink TRAU frame, afn_ul_comp=%u/%u\n", afn_ul_comp, afn_ul_comp % 52);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Report to the CCU that there is an issue with uplink TRAU frames, the CCU will then send
 | 
				
			||||||
 | 
							 * a CCU-SYNC-IND within the next TRAU frame, so we can check if we are still in sync and trigger
 | 
				
			||||||
 | 
							 * synchronization procedure if necessary. */
 | 
				
			||||||
 | 
							ccu_descr->sync.ul_frame_err = true;
 | 
				
			||||||
 | 
							goto skip;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (trau_frame.type) {
 | 
				
			||||||
 | 
						case ER_GPRS_TRAU_FT_SYNC:
 | 
				
			||||||
 | 
							if (trau_frame.u.ccu_sync_ind.pseq != 0x3FFFFF) {
 | 
				
			||||||
 | 
								LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-SYNC-IND",
 | 
				
			||||||
 | 
									 "tav=%u, dbe=%u, dfe=%u, pseq=%u, afn_ul=%u, afn_dl=%u\n",
 | 
				
			||||||
 | 
									 trau_frame.u.ccu_sync_ind.tav, trau_frame.u.ccu_sync_ind.dbe,
 | 
				
			||||||
 | 
									 trau_frame.u.ccu_sync_ind.dfe, trau_frame.u.ccu_sync_ind.pseq,
 | 
				
			||||||
 | 
									 trau_frame.u.ccu_sync_ind.afn_ul, trau_frame.u.ccu_sync_ind.afn_dl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Synchronize the current CCU PSEQ state */
 | 
				
			||||||
 | 
								ccu_descr->sync.pseq_ccu = trau_frame.u.ccu_sync_ind.pseq;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-SYNC-IND",
 | 
				
			||||||
 | 
									 "tav=%u, dbe=%u, dfe=%u, pseq=(none), afn_ul=%u, afn_dl=%u\n",
 | 
				
			||||||
 | 
									 trau_frame.u.ccu_sync_ind.tav, trau_frame.u.ccu_sync_ind.dbe,
 | 
				
			||||||
 | 
									 trau_frame.u.ccu_sync_ind.dfe, trau_frame.u.ccu_sync_ind.afn_ul,
 | 
				
			||||||
 | 
									 trau_frame.u.ccu_sync_ind.afn_dl);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ccu_descr->sync.tav = trau_frame.u.ccu_sync_ind.tav;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Check if we are in sync with the CCU, if not trigger synchronization procedure */
 | 
				
			||||||
 | 
							if (afn_ul != trau_frame.u.ccu_sync_ind.afn_ul || afn_dl != trau_frame.u.ccu_sync_ind.afn_dl) {
 | 
				
			||||||
 | 
								if (afn_ul != trau_frame.u.ccu_sync_ind.afn_ul)
 | 
				
			||||||
 | 
									LOGPL1IF(ccu_descr, LOGL_NOTICE, "CCU-SYNC-IND",
 | 
				
			||||||
 | 
										 "afn_ul=%u (computed) != afn_ul=%u (sync-ind) => delta=%u\n", afn_ul,
 | 
				
			||||||
 | 
										 trau_frame.u.ccu_sync_ind.afn_ul,
 | 
				
			||||||
 | 
										 GSM_TDMA_FN_DIFF(afn_ul, trau_frame.u.ccu_sync_ind.afn_ul));
 | 
				
			||||||
 | 
								if (afn_dl != trau_frame.u.ccu_sync_ind.afn_dl)
 | 
				
			||||||
 | 
									LOGPL1IF(ccu_descr, LOGL_NOTICE, "CCU-SYNC-IND",
 | 
				
			||||||
 | 
										 "afn_dl=%u (computed) != afn_dl=%u (sync-ind) => delta=%u\n", afn_dl,
 | 
				
			||||||
 | 
										 trau_frame.u.ccu_sync_ind.afn_dl,
 | 
				
			||||||
 | 
										 GSM_TDMA_FN_DIFF(afn_dl, trau_frame.u.ccu_sync_ind.afn_dl));
 | 
				
			||||||
 | 
								LOGPL1IF(ccu_descr, LOGL_NOTICE, "CCU-SYNC-IND",
 | 
				
			||||||
 | 
									 "FN jump detected, lost sync with CCU -- (re)synchronizing...\n");
 | 
				
			||||||
 | 
								ccu_descr->sync.ccu_synced = false;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								LOGPL1IF(ccu_descr, LOGL_NOTICE, "CCU-SYNC-IND", "in sync with CCU\n");
 | 
				
			||||||
 | 
								ccu_descr->sync.ccu_synced = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Overwrite calculated afn_ul and afn_dl with the actual values from the SYNC indication */
 | 
				
			||||||
 | 
							afn_ul = trau_frame.u.ccu_sync_ind.afn_ul;
 | 
				
			||||||
 | 
							afn_dl = trau_frame.u.ccu_sync_ind.afn_dl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case ER_GPRS_TRAU_FT_DATA:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ccu_descr->sync.tav = trau_frame.u.ccu_data_ind.tav;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Ignore all data indications that contain only noise */
 | 
				
			||||||
 | 
							if (mac_block_is_noise(&trau_frame))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							log_data_ind(ccu_descr, &trau_frame, afn_ul_comp, afn_dl_comp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Hand received MAC block into PCU */
 | 
				
			||||||
 | 
							bts = gprs_pcu_get_bts_by_nr(the_pcu, ccu_descr->pcu.bts_nr);
 | 
				
			||||||
 | 
							if (!bts)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							meas.have_rssi = 1;
 | 
				
			||||||
 | 
							meas.rssi = rxlev2dbm(trau_frame.u.ccu_data_ind.rx_lev);
 | 
				
			||||||
 | 
							meas.have_link_qual = 1;
 | 
				
			||||||
 | 
							meas.link_qual = trau_frame.u.ccu_data_ind.u.gprs.block_qual;
 | 
				
			||||||
 | 
							pdch = &bts->trx[ccu_descr->pcu.trx_no].pdch[ccu_descr->pcu.ts];
 | 
				
			||||||
 | 
							rc = pcu_rx_data_ind_pdtch(bts, pdch, trau_frame.u.ccu_data_ind.data,
 | 
				
			||||||
 | 
										   trau_frame.u.ccu_data_ind.data_len, afn_ul_comp, &meas);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_ERROR, "CCU-XXXX-IND", "unhandled CCU indication!\n");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					skip:
 | 
				
			||||||
 | 
						if (ccu_descr->sync.ccu_synced) {
 | 
				
			||||||
 | 
							bts = gprs_pcu_get_bts_by_nr(the_pcu, ccu_descr->pcu.bts_nr);
 | 
				
			||||||
 | 
							if (bts) {
 | 
				
			||||||
 | 
								/* The PCU timing is locked to the uplink fame number. The downlink frame number is advanced
 | 
				
			||||||
 | 
								 * into the future so that the line latency is compensated and the frame arrives at the right
 | 
				
			||||||
 | 
								 * point in time. */
 | 
				
			||||||
 | 
								pdch = &bts->trx[ccu_descr->pcu.trx_no].pdch[ccu_descr->pcu.ts];
 | 
				
			||||||
 | 
								pcu_rx_block_time(bts, pdch->trx->arfcn, afn_ul_comp, ccu_descr->pcu.ts);
 | 
				
			||||||
 | 
								rc = pcu_rx_rts_req_pdtch(bts, ccu_descr->pcu.trx_no, ccu_descr->pcu.ts, afn_dl_comp,
 | 
				
			||||||
 | 
											  fn_to_block_nr(afn_dl_comp));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* We do not receive sync indications in every cycle. When traffic is transferred we won't get frame numbers
 | 
				
			||||||
 | 
						 * from the CCU. In this case we must update the last_afn_ul/dl values from the computed frame numbers
 | 
				
			||||||
 | 
						 * (see above) */
 | 
				
			||||||
 | 
						ccu_descr->sync.last_afn_ul = afn_ul;
 | 
				
			||||||
 | 
						ccu_descr->sync.last_afn_dl = afn_dl;
 | 
				
			||||||
 | 
						ccu_descr->sync.pseq_pcu++;
 | 
				
			||||||
 | 
						ccu_descr->sync.pseq_ccu++;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void er_ccu_empty_cb(struct er_ccu_descr *ccu_descr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct er_gprs_trau_frame trau_frame;
 | 
				
			||||||
 | 
						ubit_t trau_frame_encoded[ER_GPRS_TRAU_FRAME_LEN_64K];
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(&trau_frame, 0, sizeof(trau_frame));
 | 
				
			||||||
 | 
						trau_frame.u.pcu_sync_ind.pseq = ccu_descr->sync.pseq_pcu;
 | 
				
			||||||
 | 
						trau_frame.u.pcu_sync_ind.tav = ccu_descr->sync.tav;
 | 
				
			||||||
 | 
						trau_frame.u.pcu_sync_ind.fn_ul = 0x3FFFFF;
 | 
				
			||||||
 | 
						trau_frame.u.pcu_sync_ind.fn_dl = 0x3FFFFF;
 | 
				
			||||||
 | 
						trau_frame.u.pcu_sync_ind.fn_ss = 0x3FFFFF;
 | 
				
			||||||
 | 
						trau_frame.u.pcu_sync_ind.ls = 0x3FFFFF;
 | 
				
			||||||
 | 
						trau_frame.u.pcu_sync_ind.ss = 0x3FFFFF;
 | 
				
			||||||
 | 
						trau_frame.type = ER_GPRS_TRAU_FT_SYNC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL)
 | 
				
			||||||
 | 
							rc = er_gprs_trau_frame_encode_64k(trau_frame_encoded, &trau_frame);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							rc = er_gprs_trau_frame_encode_16k(trau_frame_encoded, &trau_frame);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_ERROR, "PCU-SYNC-IND", "unable to encode TRAU frame\n");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						LOGPL1IF(ccu_descr, LOGL_DEBUG, "PCU-SYNC-IND", "pseq=%u, tav=%u\n",
 | 
				
			||||||
 | 
							 trau_frame.u.pcu_sync_ind.pseq, trau_frame.u.pcu_sync_ind.tav);
 | 
				
			||||||
 | 
						er_ccu_if_tx(ccu_descr, trau_frame_encoded, rc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Make sure timing adjustment value is reset after use */
 | 
				
			||||||
 | 
						ccu_descr->sync.tav = TIME_ADJ_NONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* use the length of the block to determine the coding scheme */
 | 
				
			||||||
 | 
					static int cs_hdr_from_len(uint8_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (len) {
 | 
				
			||||||
 | 
						case 23:
 | 
				
			||||||
 | 
							return CS_OR_HDR_CS1;
 | 
				
			||||||
 | 
						case 34:
 | 
				
			||||||
 | 
							return CS_OR_HDR_CS2;
 | 
				
			||||||
 | 
						case 40:
 | 
				
			||||||
 | 
							return CS_OR_HDR_CS3;
 | 
				
			||||||
 | 
						case 54:
 | 
				
			||||||
 | 
							return CS_OR_HDR_CS4;
 | 
				
			||||||
 | 
						case 27:
 | 
				
			||||||
 | 
						case 33:
 | 
				
			||||||
 | 
						case 42:
 | 
				
			||||||
 | 
						case 49:
 | 
				
			||||||
 | 
							return CS_OR_HDR_HDR3;
 | 
				
			||||||
 | 
						case 60:
 | 
				
			||||||
 | 
						case 78:
 | 
				
			||||||
 | 
							return CS_OR_HDR_HDR2;
 | 
				
			||||||
 | 
						case 118:
 | 
				
			||||||
 | 
						case 142:
 | 
				
			||||||
 | 
						case 154:
 | 
				
			||||||
 | 
							return CS_OR_HDR_HDR1;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* send packet data request to L1 */
 | 
				
			||||||
 | 
					int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
 | 
				
			||||||
 | 
							  uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct er_trx_descr *trx_descr = obj;
 | 
				
			||||||
 | 
						struct er_ccu_descr *ccu_descr;
 | 
				
			||||||
 | 
						struct er_gprs_trau_frame trau_frame;
 | 
				
			||||||
 | 
						ubit_t trau_frame_encoded[ER_GPRS_TRAU_FRAME_LEN_64K];
 | 
				
			||||||
 | 
						struct gprs_rlcmac_bts *bts;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Make sure that the CCU is synchronized and connected. */
 | 
				
			||||||
 | 
						if (!trx_descr) {
 | 
				
			||||||
 | 
							LOGP(DL1IF, LOGL_ERROR, "PCU-DATA-IND: PDCH(ts=%u, arfcn=%u) no TRX context, tossing MAC block...\n",
 | 
				
			||||||
 | 
							     ts, arfcn);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ccu_descr = &trx_descr->ts_ccu_descr[ts];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ccu_descr->link.ccu_connected) {
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_NOTICE, "PCU-DATA-IND", "CCU not connected, tossing MAC block...\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!ccu_descr->sync.ccu_synced) {
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_NOTICE, "PCU-DATA-IND", "CCU not synchronized, tossing MAC block...\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Hand received MAC block into PCU */
 | 
				
			||||||
 | 
						bts = gprs_pcu_get_bts_by_nr(the_pcu, ccu_descr->pcu.bts_nr);
 | 
				
			||||||
 | 
						if (!bts) {
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_NOTICE, "PCU-DATA-IND", "no BTS, tossing MAC block...\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(&trau_frame, 0, sizeof(trau_frame));
 | 
				
			||||||
 | 
						trau_frame.type = ER_GPRS_TRAU_FT_DATA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = cs_hdr_from_len(len);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_ERROR, "PCU-DATA-IND",
 | 
				
			||||||
 | 
								 "unable to encode TRAU frame, invalid CS or MCS value set\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						trau_frame.u.pcu_data_ind.cs_hdr = (enum er_cs_or_hdr)rc;
 | 
				
			||||||
 | 
						trau_frame.u.pcu_data_ind.tav = ccu_descr->sync.tav;
 | 
				
			||||||
 | 
						trau_frame.u.pcu_data_ind.ul_frame_err = ccu_descr->sync.ul_frame_err;
 | 
				
			||||||
 | 
						if (bts->mcs_mask)
 | 
				
			||||||
 | 
							trau_frame.u.pcu_data_ind.ul_chan_mode = ER_UL_CHMOD_NB_UNKN;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							trau_frame.u.pcu_data_ind.ul_chan_mode = ER_UL_CHMOD_NB_GMSK;
 | 
				
			||||||
 | 
						OSMO_ASSERT(len < sizeof(trau_frame.u.pcu_data_ind.data));
 | 
				
			||||||
 | 
						memcpy(trau_frame.u.pcu_data_ind.data, data, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Regulary ignore one MAC block in uplink. The CCU will then send one CCU-SYNC-IND instead. We use this
 | 
				
			||||||
 | 
						 * indication to check whether we are still in sync with the CCU. */
 | 
				
			||||||
 | 
						if (fn % SYNC_CHECK_INTERVAL == 0)
 | 
				
			||||||
 | 
							trau_frame.u.pcu_data_ind.ul_chan_mode = ER_UL_CHMOD_VOID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL)
 | 
				
			||||||
 | 
							rc = er_gprs_trau_frame_encode_64k(trau_frame_encoded, &trau_frame);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							rc = er_gprs_trau_frame_encode_16k(trau_frame_encoded, &trau_frame);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_ERROR, "PCU-DATA-IND", "unable to encode TRAU frame\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						LOGPL1IF(ccu_descr, LOGL_DEBUG, "PCU-DATA-IND",
 | 
				
			||||||
 | 
							 "tav=%u, ul_frame_err=%u, cs_hdr=%u, ul_chan_mode=%u, atten_db=%u, timing_offset=%u,"
 | 
				
			||||||
 | 
							 " data=%s==>, fn=%u/%u (comp)\n", trau_frame.u.pcu_data_ind.tav,
 | 
				
			||||||
 | 
							 trau_frame.u.pcu_data_ind.ul_frame_err, trau_frame.u.pcu_data_ind.cs_hdr,
 | 
				
			||||||
 | 
							 trau_frame.u.pcu_data_ind.ul_chan_mode, trau_frame.u.pcu_data_ind.atten_db,
 | 
				
			||||||
 | 
							 trau_frame.u.pcu_data_ind.timing_offset, osmo_hexdump_nospc(trau_frame.u.pcu_data_ind.data, len), fn,
 | 
				
			||||||
 | 
							 fn % 52);
 | 
				
			||||||
 | 
						er_ccu_if_tx(ccu_descr, trau_frame_encoded, rc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Make sure timing adjustment value is reset after use */
 | 
				
			||||||
 | 
						ccu_descr->sync.tav = TIME_ADJ_NONE;
 | 
				
			||||||
 | 
						ccu_descr->sync.ul_frame_err = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void *l1if_open_trx(uint8_t bts_nr, uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct er_trx_descr *trx_descr;
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Note: We do not have enough information to really open anything at
 | 
				
			||||||
 | 
						 * this point. We will just create the TRX context and fill it wit basic
 | 
				
			||||||
 | 
						 * CCU context (one for each TS) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trx_descr = talloc_zero(tall_pcu_ctx, struct er_trx_descr);
 | 
				
			||||||
 | 
						OSMO_ASSERT(trx_descr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ARRAY_SIZE(trx_descr->ts_ccu_descr); i++) {
 | 
				
			||||||
 | 
							trx_descr->ts_ccu_descr[i].er_ccu_rx_cb = er_ccu_rx_cb;
 | 
				
			||||||
 | 
							trx_descr->ts_ccu_descr[i].er_ccu_empty_cb = er_ccu_empty_cb;
 | 
				
			||||||
 | 
							trx_descr->ts_ccu_descr[i].pcu.trx_no = trx_no;
 | 
				
			||||||
 | 
							trx_descr->ts_ccu_descr[i].pcu.bts_nr = bts_nr;
 | 
				
			||||||
 | 
							trx_descr->ts_ccu_descr[i].pcu.ts = i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return trx_descr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int l1if_close_trx(void *obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct er_trx_descr *trx_descr = obj;
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!trx_descr) {
 | 
				
			||||||
 | 
							LOGP(DL1IF, LOGL_ERROR, "PCU-DATA-IND: no TRX context, cannot close unknown TRX...\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ARRAY_SIZE(trx_descr->ts_ccu_descr); i++)
 | 
				
			||||||
 | 
							er_ccu_if_close(&trx_descr->ts_ccu_descr[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						talloc_free(trx_descr);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int l1if_connect_pdch(void *obj, uint8_t ts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct er_trx_descr *trx_descr = obj;
 | 
				
			||||||
 | 
						struct er_ccu_descr *ccu_descr;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!trx_descr) {
 | 
				
			||||||
 | 
							LOGP(DL1IF, LOGL_ERROR, "SETUP: PDCH(ts=%u) no CCU context, TRX never opened before?\n", ts);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ccu_descr = &trx_descr->ts_ccu_descr[ts];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = pcu_l1if_get_e1_ccu_conn_pars(&ccu_descr->e1_conn_pars, ccu_descr->pcu.bts_nr, ccu_descr->pcu.trx_no,
 | 
				
			||||||
 | 
										   ccu_descr->pcu.ts);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGPL1IF(ccu_descr, LOGL_ERROR, "SETUP", "cannot find E1 connection parameters for CCU\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = er_ccu_if_open(ccu_descr);
 | 
				
			||||||
 | 
						if (rc < 0)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int l1if_disconnect_pdch(void *obj, uint8_t ts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct er_trx_descr *trx_descr = obj;
 | 
				
			||||||
 | 
						struct er_ccu_descr *ccu_descr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!trx_descr) {
 | 
				
			||||||
 | 
							LOGP(DL1IF, LOGL_ERROR, "SETUP: PDCH(ts=%u) no TRX context, TRX never opened before?\n", ts);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ccu_descr = &trx_descr->ts_ccu_descr[ts];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						er_ccu_if_close(ccu_descr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int l1if_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						er_ccu_if_init(tall_pcu_ctx);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1353
									
								
								src/gprs_bssgp_pcu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1353
									
								
								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
 | 
					/* gprs_bssgp_pcu.h
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
					 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
				
			||||||
 | 
					 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
 * modify it under the terms of the GNU General Public License
 | 
					 * modify it under the terms of the GNU General Public License
 | 
				
			||||||
@@ -11,53 +12,100 @@
 | 
				
			|||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 * GNU General Public License for more details.
 | 
					 * 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.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef GPRS_BSSGP_PCU_H
 | 
					#ifndef GPRS_BSSGP_PCU_H
 | 
				
			||||||
#define GPRS_BSSGP_PCU_H
 | 
					#define GPRS_BSSGP_PCU_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#include <osmocom/core/talloc.h>
 | 
					#include <osmocom/core/talloc.h>
 | 
				
			||||||
#include <osmocom/core/rate_ctr.h>
 | 
					#include <osmocom/core/rate_ctr.h>
 | 
				
			||||||
#include <osmocom/core/logging.h>
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
#include <osmocom/core/signal.h>
 | 
					#include <osmocom/core/signal.h>
 | 
				
			||||||
#include <osmocom/core/application.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.h>
 | 
				
			||||||
 | 
					#include <osmocom/gprs/gprs_bssgp_bss.h>
 | 
				
			||||||
#include <osmocom/gprs/gprs_msgb.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);
 | 
					struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
 | 
				
			||||||
}
 | 
					
 | 
				
			||||||
#include <gprs_debug.h>
 | 
					#include <gprs_debug.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define QOS_PROFILE 0
 | 
					#include <time.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define QOS_PROFILE 4
 | 
				
			||||||
#define BSSGP_HDR_LEN 53
 | 
					#define BSSGP_HDR_LEN 53
 | 
				
			||||||
#define NS_HDR_LEN 4
 | 
					#define NS_HDR_LEN 4
 | 
				
			||||||
#define IE_LLC_PDU 14
 | 
					#define IE_LLC_PDU 14
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern struct bssgp_bvc_ctx *bctx;
 | 
					enum sgsn_counter_id {
 | 
				
			||||||
 | 
						SGSN_CTR_RX_PAGING_CS,
 | 
				
			||||||
 | 
						SGSN_CTR_RX_PAGING_PS,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp);
 | 
					struct gprs_bssgp_pcu {
 | 
				
			||||||
 | 
						struct bssgp_bvc_ctx *bctx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
 | 
						struct gprs_rlcmac_bts *bts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
 | 
						struct osmo_timer_list bvc_timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int gprs_bssgp_pcu_rcvmsg(struct msgb *msg);
 | 
						struct rate_ctr_group *ctrs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
 | 
						/* state: is the NSVC unblocked? */
 | 
				
			||||||
        uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
 | 
						int nsvc_unblocked;
 | 
				
			||||||
	        uint16_t rac, uint16_t cell_id);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
void gprs_bssgp_destroy(void);
 | 
						/* 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** 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
 | 
					#endif // GPRS_BSSGP_PCU_H
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										285
									
								
								src/gprs_bssgp_rim.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								src/gprs_bssgp_rim.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,285 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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) {
 | 
				
			||||||
 | 
							struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
 | 
				
			||||||
 | 
							if (ms->nacc && nacc_fsm_is_waiting_si_resolution(ms->nacc, &nacc->reprt_cell))
 | 
				
			||||||
 | 
								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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/gprs_bssgp_rim.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/gprs_bssgp_rim.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct osmo_bssgp_prim;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int handle_rim(struct osmo_bssgp_prim *bp);
 | 
				
			||||||
							
								
								
									
										176
									
								
								src/gprs_codel.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/gprs_codel.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										104
									
								
								src/gprs_codel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/gprs_codel.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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
 | 
				
			||||||
							
								
								
									
										170
									
								
								src/gprs_debug.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/gprs_debug.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,170 @@
 | 
				
			|||||||
 | 
					/* gprs_debug.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 Harald Welte <laforge@gnumonks.org>
 | 
				
			||||||
 | 
					 * Copyright (C) 2022 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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] = {
 | 
				
			||||||
 | 
							.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,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						[DMS] = {
 | 
				
			||||||
 | 
							.name = "DMS",
 | 
				
			||||||
 | 
							.color = "\033[1;34m",
 | 
				
			||||||
 | 
							.description = "Mobile Station (MS)",
 | 
				
			||||||
 | 
							.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,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						[DE1] = {
 | 
				
			||||||
 | 
							.name = "DE1",
 | 
				
			||||||
 | 
							.color = "\033[1;31m",
 | 
				
			||||||
 | 
							.description = "E1 line handling",
 | 
				
			||||||
 | 
							.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[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 << 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 << LOG_FLT_GB_BVC)) != 0
 | 
				
			||||||
 | 
						    && bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC]))
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const struct log_info gprs_log_info = {
 | 
				
			||||||
 | 
						filter_fn,
 | 
				
			||||||
 | 
						(struct log_info_cat*)default_categories,
 | 
				
			||||||
 | 
						ARRAY_SIZE(default_categories),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,78 +0,0 @@
 | 
				
			|||||||
/* gprs_debug.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 <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>
 | 
					 | 
				
			||||||
#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,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Filter on the NS Virtual Connection */
 | 
					 | 
				
			||||||
	if ((tar->filter_map & (1 << FLT_NSVC)) != 0
 | 
					 | 
				
			||||||
	    && nsvc && (nsvc == tar->filter_data[FLT_NSVC]))
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Filter on the BVC */
 | 
					 | 
				
			||||||
	if ((tar->filter_map & (1 << FLT_BVC)) != 0
 | 
					 | 
				
			||||||
	    && bvc && (bvc == tar->filter_data[FLT_BVC]))
 | 
					 | 
				
			||||||
		return 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const struct log_info gprs_log_info = {
 | 
					 | 
				
			||||||
	filter_fn,
 | 
					 | 
				
			||||||
	(struct log_info_cat*)default_categories,
 | 
					 | 
				
			||||||
	ARRAY_SIZE(default_categories),
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@@ -11,20 +11,19 @@
 | 
				
			|||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 * GNU General Public License for more details.
 | 
					 * 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.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 
 | 
					 | 
				
			||||||
#ifndef GPRS_DEBUG_H
 | 
					 | 
				
			||||||
#define GPRS_DEBUG_H
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdio.h>
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
#include <osmocom/core/linuxlist.h>
 | 
					#endif
 | 
				
			||||||
#include <osmocom/core/logging.h>
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
};
 | 
					
 | 
				
			||||||
 | 
					/* 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 */
 | 
					/* Debug Areas of the code */
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
	DCSN1,
 | 
						DCSN1,
 | 
				
			||||||
@@ -34,39 +33,21 @@ enum {
 | 
				
			|||||||
	DRLCMACDL,
 | 
						DRLCMACDL,
 | 
				
			||||||
	DRLCMACUL,
 | 
						DRLCMACUL,
 | 
				
			||||||
	DRLCMACSCHED,
 | 
						DRLCMACSCHED,
 | 
				
			||||||
	DRLCMACBW,
 | 
						DRLCMACMEAS,
 | 
				
			||||||
	DBSSGP,
 | 
						DMS,
 | 
				
			||||||
 | 
						DTBF,
 | 
				
			||||||
 | 
						DTBFDL,
 | 
				
			||||||
 | 
						DTBFUL,
 | 
				
			||||||
 | 
						DNS,
 | 
				
			||||||
	DPCU,
 | 
						DPCU,
 | 
				
			||||||
 | 
						DNACC,
 | 
				
			||||||
 | 
						DRIM,
 | 
				
			||||||
 | 
						DE1,
 | 
				
			||||||
	aDebug_LastEntry
 | 
						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;
 | 
					extern const struct log_info gprs_log_info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
#endif // GPRS_DEBUG_H
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1410
									
								
								src/gprs_ms.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1410
									
								
								src/gprs_ms.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										270
									
								
								src/gprs_ms.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								src/gprs_ms.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,270 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#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/core/use_count.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/gsm/protocol/gsm_23_003.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm48.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "coding_scheme.h"
 | 
				
			||||||
 | 
					#include <gsm_rlcmac.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 GprsMs {
 | 
				
			||||||
 | 
						struct llist_head list; /* list of all GprsMs */
 | 
				
			||||||
 | 
						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 */
 | 
				
			||||||
 | 
						/* First common timeslot used both in UL and DL, or NULL if not set: */
 | 
				
			||||||
 | 
						struct gprs_rlcmac_pdch *first_common_ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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;
 | 
				
			||||||
 | 
						struct osmo_timer_list llc_timer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct osmo_use_count use_count;
 | 
				
			||||||
 | 
						struct osmo_timer_list release_timer;
 | 
				
			||||||
 | 
						/* Time at which MS became idle and waiting to be released by release_timer: */
 | 
				
			||||||
 | 
						struct timeval tv_idle_start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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;
 | 
				
			||||||
 | 
						enum mcs_kind mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct rate_ctr_group *ctrs;
 | 
				
			||||||
 | 
						struct nacc_fsm_ctx *nacc;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, const char *use_ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms);
 | 
				
			||||||
 | 
					void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch);
 | 
				
			||||||
 | 
					void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
 | 
				
			||||||
 | 
								   uint8_t ul_slots, uint8_t dl_slots);
 | 
				
			||||||
 | 
					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_update_announced_tlli(struct GprsMs *ms, uint32_t tlli);
 | 
				
			||||||
 | 
					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;}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *ms_name(const struct GprsMs *ms);
 | 
				
			||||||
 | 
					char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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,
 | 
				
			||||||
 | 
									       const struct gprs_rlcmac_pdch *pdch, uint32_t fn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx);
 | 
				
			||||||
 | 
					struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms);
 | 
				
			||||||
 | 
					struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch);
 | 
				
			||||||
 | 
					int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
 | 
				
			||||||
 | 
					int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms);
 | 
				
			||||||
 | 
					int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool ms_is_idle(const struct GprsMs *ms)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return !ms->ul_tbf && !ms->dl_tbf &&
 | 
				
			||||||
 | 
							llist_empty(&ms->old_tbfs);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct gprs_llc_queue *ms_llc_queue(struct GprsMs *ms)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return &ms->llc_queue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Function used in code where a ul_tbf related event occurs and hence its state
 | 
				
			||||||
 | 
					 * changes, which in turn may change the entire MS state (eg becoming reachable
 | 
				
			||||||
 | 
					 * over PCH or PACCH) and the caller may want to know if it is a good time to
 | 
				
			||||||
 | 
					 * assign a DL TBF (and whether we have DL data to send) */
 | 
				
			||||||
 | 
					static inline bool ms_need_dl_tbf(struct GprsMs *ms)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
 | 
				
			||||||
 | 
						if (dl_tbf) {
 | 
				
			||||||
 | 
							switch (tbf_state(dl_tbf_as_tbf(dl_tbf))) {
 | 
				
			||||||
 | 
							case TBF_ST_NEW:
 | 
				
			||||||
 | 
							case TBF_ST_ASSIGN:
 | 
				
			||||||
 | 
							case TBF_ST_FLOW:
 | 
				
			||||||
 | 
							case TBF_ST_FINISHED:
 | 
				
			||||||
 | 
							case TBF_ST_WAIT_RELEASE:
 | 
				
			||||||
 | 
								return false; /* TBF in use, hence no need for new DL TBF */
 | 
				
			||||||
 | 
							case TBF_ST_WAIT_REUSE_TFI:
 | 
				
			||||||
 | 
							case TBF_ST_RELEASING:
 | 
				
			||||||
 | 
								/* TBF cannot be used to send further data, a new one
 | 
				
			||||||
 | 
								 * may be needed, check below */
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								OSMO_ASSERT(0);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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_is_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 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 MS_USE_TBF "tbf"
 | 
				
			||||||
 | 
					#define ms_ref(ms, use) \
 | 
				
			||||||
 | 
						OSMO_ASSERT(osmo_use_count_get_put(&(ms)->use_count, use, 1) == 0)
 | 
				
			||||||
 | 
					#define ms_unref(ms, use) \
 | 
				
			||||||
 | 
						OSMO_ASSERT(osmo_use_count_get_put(&(ms)->use_count, use, -1) == 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOGPMS(ms, category, level, fmt, args...) \
 | 
				
			||||||
 | 
						LOGP(category, level, "%s " fmt, ms_name(ms), ## args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										214
									
								
								src/gprs_pcu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/gprs_pcu.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,214 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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=3113,  .default_val=7, .unit=OSMO_TDEF_S, .desc="Timeout for paging", .val=0 },
 | 
				
			||||||
 | 
						{ .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=3172,  .default_val=5000, .unit=OSMO_TDEF_MS, .desc="Wait Indication used in Imm Ass Reject during TBF Establishment (PACCH)", .val=0, .min_val = 0, .max_val = 255000 }, /* TS 44.060 7.1.3.2.1 */
 | 
				
			||||||
 | 
						{ .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=-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=0,   .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="DL TBF 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 void _update_stats_timer_cb(void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct gprs_pcu *pcu = (struct gprs_pcu *)data;
 | 
				
			||||||
 | 
						struct gprs_rlcmac_bts *bts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry(bts, &pcu->bts_list, list)
 | 
				
			||||||
 | 
							osmo_time_cc_set_flag(&bts->all_allocated_pdch, bts_all_pdch_allocated(bts));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_timer_schedule(&pcu->update_stats_timer, 1, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprs_pcu_talloc_destructor(struct gprs_pcu *pcu)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct gprs_rlcmac_bts *bts;
 | 
				
			||||||
 | 
						while ((bts = llist_first_entry_or_null(&pcu->bts_list, struct gprs_rlcmac_bts, list)))
 | 
				
			||||||
 | 
							talloc_free(bts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_timer_del(&pcu->update_stats_timer);
 | 
				
			||||||
 | 
						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.msclass_default = PCU_DEFAULT_MSLOT_CLASS;
 | 
				
			||||||
 | 
						pcu->vty.llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
 | 
				
			||||||
 | 
						pcu->vty.llc_idle_ack_csec = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_timer_setup(&pcu->update_stats_timer, _update_stats_timer_cb, pcu);
 | 
				
			||||||
 | 
						osmo_timer_schedule(&pcu->update_stats_timer, 1, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										154
									
								
								src/gprs_pcu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/gprs_pcu.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 <osmocom/core/timer.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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* If Multislot Class is not known during TS allocation, assume ms_class=12: Rx=4 Tx=4 Sum=5 */
 | 
				
			||||||
 | 
					#define PCU_DEFAULT_MSLOT_CLASS 12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 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;
 | 
				
			||||||
 | 
					struct alloc_resources_req;
 | 
				
			||||||
 | 
					struct alloc_resources_res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef int (*alloc_algorithm_func_t)(const struct alloc_resources_req *req,
 | 
				
			||||||
 | 
									      struct alloc_resources_res *res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 */
 | 
				
			||||||
 | 
							uint8_t msclass_default; /* Assume this MSCLASS if unknown when creating TBF */
 | 
				
			||||||
 | 
							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 */
 | 
				
			||||||
 | 
						} vty;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct gsmtap_inst *gsmtap;
 | 
				
			||||||
 | 
						uint32_t gsmtap_categ_mask;
 | 
				
			||||||
 | 
						char *gsmtap_remote_host;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct llist_head bts_list; /* list of gprs_rlcmac_bts */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct osmo_timer_list update_stats_timer; /* Used to update some time_cc stats periodically */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint8_t pcu_if_version;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
							
								
								
									
										78
									
								
								src/gprs_rlcmac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/gprs_rlcmac.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					/* gprs_rlcmac.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
				
			||||||
 | 
					 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
				
			||||||
 | 
					 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm48.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pcu_l1_if.h>
 | 
				
			||||||
 | 
					#include <gprs_rlcmac.h>
 | 
				
			||||||
 | 
					#include <bts.h>
 | 
				
			||||||
 | 
					#include <encoding.h>
 | 
				
			||||||
 | 
					#include <tbf.h>
 | 
				
			||||||
 | 
					#include <gprs_debug.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern void *tall_pcu_ctx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int gprs_rlcmac_paging_request(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi,
 | 
				
			||||||
 | 
								       const char *imsi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (log_check_level(DRLCMAC, LOGL_NOTICE)) {
 | 
				
			||||||
 | 
							char str[64];
 | 
				
			||||||
 | 
							osmo_mobile_identity_to_str_buf(str, sizeof(str), mi);
 | 
				
			||||||
 | 
							LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH) MI=%s\n", str);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						struct bitvec *paging_request = bitvec_alloc(22, tall_pcu_ctx);
 | 
				
			||||||
 | 
						bitvec_unhex(paging_request, DUMMY_VEC);
 | 
				
			||||||
 | 
						int plen = write_paging_request(paging_request, mi);
 | 
				
			||||||
 | 
						if (plen <= 0) {
 | 
				
			||||||
 | 
							LOGP(DRLCMAC, LOGL_ERROR, "TX: [PCU -> BTS] Failed to encode Paging Request\n");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bts_do_rate_ctr_inc(bts, CTR_PCH_REQUESTS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (the_pcu->pcu_if_version >= 0x0b)
 | 
				
			||||||
 | 
							pcu_l1if_tx_pch2(bts, paging_request, plen, false, imsi, GSM_RESERVED_TMSI);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							pcu_l1if_tx_pch(bts, paging_request, plen, imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bitvec_free(paging_request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Encode Application Information Request to Packet Application Information (3GPP TS 44.060 11.2.47) */
 | 
				
			||||||
 | 
					struct msgb *gprs_rlcmac_app_info_msg(const struct gsm_pcu_if_app_info_req *req) {
 | 
				
			||||||
 | 
						struct msgb *msg;
 | 
				
			||||||
 | 
						uint16_t msgb_len = req->len + 1;
 | 
				
			||||||
 | 
						struct bitvec bv = {0, msgb_len, NULL};
 | 
				
			||||||
 | 
						const enum bit_value page_mode[] = {ZERO, ZERO}; /* Normal Paging (3GPP TS 44.060 12.20) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!req->len) {
 | 
				
			||||||
 | 
							LOGP(DRLCMAC, LOGL_ERROR, "Application Information Request with zero length received!\n");
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg = msgb_alloc(msgb_len, "app_info_msg");
 | 
				
			||||||
 | 
						if (!msg)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bv.data = msgb_put(msg, msgb_len);
 | 
				
			||||||
 | 
						bitvec_set_bits(&bv, page_mode, 2);
 | 
				
			||||||
 | 
						bitvec_set_uint(&bv, req->application_type, 4);
 | 
				
			||||||
 | 
						bitvec_set_bytes(&bv, req->data, req->len);
 | 
				
			||||||
 | 
						return msg;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1488
									
								
								src/gprs_rlcmac.cpp
									
									
									
									
									
								
							
							
						
						
									
										1488
									
								
								src/gprs_rlcmac.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,6 +1,8 @@
 | 
				
			|||||||
/* gprs_rlcmac.h
 | 
					/* gprs_rlcmac.h
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
					 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
				
			||||||
 | 
					 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
				
			||||||
 | 
					 * Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or
 | 
					 * This program is free software; you can redistribute it and/or
 | 
				
			||||||
 * modify it under the terms of the GNU General Public License
 | 
					 * modify it under the terms of the GNU General Public License
 | 
				
			||||||
@@ -11,23 +13,25 @@
 | 
				
			|||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 * GNU General Public License for more details.
 | 
					 * 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.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 
 | 
					
 | 
				
			||||||
#ifndef GPRS_RLCMAC_H
 | 
					#pragma once
 | 
				
			||||||
#define GPRS_RLCMAC_H
 | 
					
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pcu_l1_if.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
#include <bitvector.h>
 | 
					 | 
				
			||||||
#include <gsm_rlcmac.h>
 | 
					 | 
				
			||||||
#include <gsm_timer.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#include <osmocom/core/linuxlist.h>
 | 
					#include <osmocom/core/linuxlist.h>
 | 
				
			||||||
#include <osmocom/core/timer.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
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -37,177 +41,13 @@ extern "C" {
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
//#define DEBUG_DL_ASS_IDLE
 | 
					//#define DEBUG_DL_ASS_IDLE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					#define DUMMY_VEC "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
 | 
				
			||||||
 * PDCH instanc
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct gprs_rlcmac_tbf;
 | 
					struct gprs_rlcmac_tbf;
 | 
				
			||||||
 | 
					struct gprs_rlcmac_bts;
 | 
				
			||||||
struct gprs_rlcmac_pdch {
 | 
					struct GprsMs;
 | 
				
			||||||
	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;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#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
 | 
					 * paging entry
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -217,120 +57,49 @@ struct gprs_rlcmac_paging {
 | 
				
			|||||||
	uint8_t identity_lv[9];
 | 
						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,
 | 
					/* TS allocation internal functions */
 | 
				
			||||||
	enum gprs_rlcmac_tbf_direction dir, uint8_t tfi, uint8_t trx,
 | 
					int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots);
 | 
				
			||||||
	uint8_t first_ts, uint8_t ms_class, uint8_t single_slot);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts,
 | 
					int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
 | 
				
			||||||
        enum gprs_rlcmac_tbf_direction dir);
 | 
						uint16_t lost);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
 | 
					int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
 | 
				
			||||||
	enum gprs_rlcmac_tbf_direction dir);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
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);
 | 
					int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets);
 | 
				
			||||||
 | 
					 | 
				
			||||||
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);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
 | 
					/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
 | 
				
			||||||
enum gprs_rlcmac_block_type {
 | 
					enum gprs_rlcmac_block_type {
 | 
				
			||||||
	GPRS_RLCMAC_DATA_BLOCK = 0x0,
 | 
						GPRS_RLCMAC_DATA_BLOCK = 0x0,
 | 
				
			||||||
	GPRS_RLCMAC_CONTROL_BLOCK = 0x1, 
 | 
						GPRS_RLCMAC_CONTROL_BLOCK = 0x1,
 | 
				
			||||||
	GPRS_RLCMAC_CONTROL_BLOCK_OPT = 0x2,
 | 
						GPRS_RLCMAC_CONTROL_BLOCK_OPT = 0x2,
 | 
				
			||||||
	GPRS_RLCMAC_RESERVED = 0x3
 | 
						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);
 | 
					int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void tbf_timer_cb(void *_tbf);
 | 
					int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
 | 
				
			||||||
 | 
						uint8_t trx, uint8_t ts,
 | 
				
			||||||
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, 
 | 
					 | 
				
			||||||
        uint32_t fn, uint8_t block_nr);
 | 
					        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" {
 | 
					extern "C" {
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
int alloc_algorithm_a(struct gprs_rlcmac_tbf *old_tbf,
 | 
					int gprs_rlcmac_paging_request(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi, const char *imsi);
 | 
				
			||||||
	struct gprs_rlcmac_tbf *tbf, uint32_t cust);
 | 
					struct msgb *gprs_rlcmac_app_info_msg(const struct gsm_pcu_if_app_info_req *req);
 | 
				
			||||||
 | 
					 | 
				
			||||||
int alloc_algorithm_b(struct gprs_rlcmac_tbf *old_tbf,
 | 
					 | 
				
			||||||
	struct gprs_rlcmac_tbf *tbf, uint32_t cust);
 | 
					 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif // GPRS_RLCMAC_H
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										189
									
								
								src/gprs_rlcmac_meas.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/gprs_rlcmac_meas.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,189 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 <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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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, "%s DL Loss: Received: %4d Lost: %4d  Sum: %4d\n",
 | 
				
			||||||
 | 
						     tbf_name(dl_tbf_as_tbf_const(tbf)), 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, "%s DL packet loss: %d%%\n",
 | 
				
			||||||
 | 
						     tbf_name(dl_tbf_as_tbf_const(tbf)), 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, "%s DL Bandwitdh: %d KBits/s\n",
 | 
				
			||||||
 | 
						     tbf_name(dl_tbf_as_tbf_const(tbf)), 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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -11,175 +11,335 @@
 | 
				
			|||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 * GNU General Public License for more details.
 | 
					 * 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_bssgp_pcu.h>
 | 
					#include <gprs_bssgp_pcu.h>
 | 
				
			||||||
#include <gprs_rlcmac.h>
 | 
					#include <gprs_rlcmac.h>
 | 
				
			||||||
#include <pcu_l1_if.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,
 | 
					extern "C" {
 | 
				
			||||||
		    struct gprs_rlcmac_tbf **poll_tbf,
 | 
						#include <osmocom/core/gsmtap.h>
 | 
				
			||||||
		    struct gprs_rlcmac_tbf **ul_ass_tbf,
 | 
						#include "nacc_fsm.h"
 | 
				
			||||||
		    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;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
 | 
					struct tbf_sched_candidates {
 | 
				
			||||||
	uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
 | 
						struct gprs_rlcmac_tbf *ul_ass;
 | 
				
			||||||
 | 
						struct gprs_rlcmac_tbf *dl_ass;
 | 
				
			||||||
 | 
						struct gprs_rlcmac_tbf *nacc;
 | 
				
			||||||
 | 
						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;
 | 
						struct gprs_rlcmac_ul_tbf *ul_tbf;
 | 
				
			||||||
	uint8_t usf = 0x07;
 | 
						struct gprs_rlcmac_dl_tbf *dl_tbf;
 | 
				
			||||||
 | 
						struct llist_item *pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_for_each_entry(pos, &pdch->trx->ul_tbfs, list) {
 | 
				
			||||||
 | 
							ul_tbf = tbf_as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
 | 
				
			||||||
 | 
							OSMO_ASSERT(ul_tbf);
 | 
				
			||||||
 | 
							if (tbf_ul_ack_rts(ul_tbf, pdch))
 | 
				
			||||||
 | 
								tbf_cand->ul_ack = ul_tbf;
 | 
				
			||||||
 | 
							if (tbf_dl_ass_rts(ul_tbf, pdch))
 | 
				
			||||||
 | 
								tbf_cand->dl_ass = ul_tbf;
 | 
				
			||||||
 | 
							if (tbf_ul_ass_rts(ul_tbf, pdch))
 | 
				
			||||||
 | 
								tbf_cand->ul_ass = ul_tbf;
 | 
				
			||||||
 | 
							if (tbf_nacc_rts(ul_tbf, pdch))
 | 
				
			||||||
 | 
								tbf_cand->nacc = ul_tbf;
 | 
				
			||||||
 | 
					/* 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 = tbf_as_dl_tbf((struct gprs_rlcmac_tbf *)pos->entry);
 | 
				
			||||||
 | 
							OSMO_ASSERT(dl_tbf);
 | 
				
			||||||
 | 
							if (tbf_dl_ass_rts(dl_tbf, pdch))
 | 
				
			||||||
 | 
								tbf_cand->dl_ass = dl_tbf;
 | 
				
			||||||
 | 
							if (tbf_ul_ass_rts(dl_tbf, pdch))
 | 
				
			||||||
 | 
								tbf_cand->ul_ass = dl_tbf;
 | 
				
			||||||
 | 
							if (tbf_nacc_rts(dl_tbf, pdch))
 | 
				
			||||||
 | 
								tbf_cand->nacc = 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;
 | 
						uint8_t i, tfi;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* select uplink ressource */
 | 
						/* select uplink resource */
 | 
				
			||||||
	for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
 | 
						for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
 | 
				
			||||||
	     i++, tfi = (tfi + 1) & 31) {
 | 
						     i++, tfi = (tfi + 1) & 31) {
 | 
				
			||||||
		tbf = pdch->ul_tbf[tfi];
 | 
							tbf = pdch->ul_tbf_by_tfi(tfi);
 | 
				
			||||||
		/* no TBF for this tfi, go next */
 | 
							/* no TBF for this tfi, go next */
 | 
				
			||||||
		if (!tbf)
 | 
							if (!tbf)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		/* no UL ressources needed, go next */
 | 
							/* no UL resources needed, go next */
 | 
				
			||||||
		/* we don't need to give ressources in FINISHED state,
 | 
							/* we don't need to give resources in FINISHED state,
 | 
				
			||||||
		 * because we have received all blocks and only poll
 | 
							 * because we have received all blocks and only poll
 | 
				
			||||||
		 * for packet control ack. */
 | 
							 * for packet control ack. */
 | 
				
			||||||
		if (tbf->state != GPRS_RLCMAC_FLOW)
 | 
							if (tbf->state_is_not(TBF_ST_FLOW))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* use this USF */
 | 
							if (require_gprs_only && tbf->is_egprs_enabled())
 | 
				
			||||||
		usf = tbf->dir.ul.usf[ts];
 | 
								continue;
 | 
				
			||||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
 | 
					
 | 
				
			||||||
			"TS=%d FN=%d block_nr=%d scheduling USF=%d for "
 | 
							/* use this USF (tbf) */
 | 
				
			||||||
			"required uplink ressource of UL TBF=%d\n", trx, ts, fn,
 | 
							/* next TBF to handle resource is the next one */
 | 
				
			||||||
			block_nr, usf, tfi);
 | 
					 | 
				
			||||||
		/* next TBF to handle ressource is the next one */
 | 
					 | 
				
			||||||
		pdch->next_ul_tfi = (tfi + 1) & 31;
 | 
							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,
 | 
					struct msgb *sched_app_info(struct gprs_rlcmac_tbf *tbf) {
 | 
				
			||||||
		    uint8_t block_nr, struct gprs_rlcmac_pdch *pdch,
 | 
						struct gprs_rlcmac_bts *bts;
 | 
				
			||||||
		    struct gprs_rlcmac_tbf *ul_ass_tbf,
 | 
					 | 
				
			||||||
		    struct gprs_rlcmac_tbf *dl_ass_tbf,
 | 
					 | 
				
			||||||
		    struct gprs_rlcmac_tbf *ul_ack_tbf)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct msgb *msg = NULL;
 | 
						struct msgb *msg = NULL;
 | 
				
			||||||
	struct gprs_rlcmac_tbf *tbf = NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* schedule PACKET UPLINK ASSIGNMENT (1st priority) */
 | 
						if (!tbf || !tbf->ms()->app_info_pending)
 | 
				
			||||||
	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))
 | 
					 | 
				
			||||||
		return NULL;
 | 
							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;
 | 
						return msg;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn,
 | 
					static struct msgb *sched_select_ctrl_msg(struct gprs_rlcmac_pdch *pdch, uint32_t fn,
 | 
				
			||||||
		    uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
 | 
										  uint8_t block_nr, struct tbf_sched_candidates *tbfs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct msgb *msg = NULL;
 | 
						struct msgb *msg = NULL;
 | 
				
			||||||
	struct gprs_rlcmac_tbf *tbf = 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 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* 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(TBF_UL_ASS_SEND_ASS_REJ))
 | 
				
			||||||
 | 
									msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, pdch, fn);
 | 
				
			||||||
 | 
								else if (tbf == tbfs->ul_ass && tbf->direction == GPRS_RLCMAC_DL_TBF)
 | 
				
			||||||
 | 
									msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, pdch, fn);
 | 
				
			||||||
 | 
								else if (tbf == tbfs->dl_ass && tbf->direction == GPRS_RLCMAC_UL_TBF)
 | 
				
			||||||
 | 
									msg = tbf_dl_ass_create_rlcmac_msg(tbfs->dl_ass, pdch, fn);
 | 
				
			||||||
 | 
								else if (tbf == tbfs->ul_ack)
 | 
				
			||||||
 | 
									msg = tbf_ul_ack_create_rlcmac_msg(tbfs->ul_ack, pdch, fn);
 | 
				
			||||||
 | 
								else if (tbf == tbfs->nacc)
 | 
				
			||||||
 | 
									msg = ms_nacc_create_rlcmac_msg(tbf->ms(), tbf, pdch, fn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								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 = tbf_dl_ass_create_rlcmac_msg(tbfs->dl_ass, pdch, fn);
 | 
				
			||||||
 | 
							} else if (tbfs->ul_ass) {
 | 
				
			||||||
 | 
								tbf = tbfs->ul_ass;
 | 
				
			||||||
 | 
								msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, pdch, fn);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* 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,
 | 
				
			||||||
 | 
											    const struct gprs_rlcmac_pdch *pdch, 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));
 | 
				
			||||||
 | 
						const bool is_control_ts = tbf_is_control_ts(tbf, pdch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (is_control_ts && tbf->need_poll_for_dl_ack_nack())
 | 
				
			||||||
 | 
							return DL_PRIO_CONTROL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (is_control_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 (is_control_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_dl_data_msg(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 i, tfi, prio_tfi;
 | 
				
			||||||
 | 
						int age;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* select downlink resource */
 | 
				
			||||||
	for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
 | 
						for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
 | 
				
			||||||
	     i++, tfi = (tfi + 1) & 31) {
 | 
						     i++, tfi = (tfi + 1) & 31) {
 | 
				
			||||||
		tbf = pdch->dl_tbf[tfi];
 | 
							tbf = pdch->dl_tbf_by_tfi(tfi);
 | 
				
			||||||
		/* no TBF for this tfi, go next */
 | 
							/* no TBF for this tfi, go next */
 | 
				
			||||||
		if (!tbf)
 | 
							if (!tbf)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		/* no DL TBF, go next */
 | 
							/* no DL TBF, go next */
 | 
				
			||||||
		if (tbf->direction != GPRS_RLCMAC_DL_TBF)
 | 
							if (tbf->direction != GPRS_RLCMAC_DL_TBF)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		/* no DL ressources needed, go next */
 | 
							/* no DL resources needed, go next */
 | 
				
			||||||
		if (tbf->state != GPRS_RLCMAC_FLOW
 | 
							if (tbf->state_is_not(TBF_ST_FLOW)
 | 
				
			||||||
		 && tbf->state != GPRS_RLCMAC_FINISHED)
 | 
							 && tbf->state_is_not(TBF_ST_FINISHED))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
 | 
							/* If a GPRS (CS1-4) Dl block is required, skip EGPRS(_GSMK) tbfs: */
 | 
				
			||||||
			"RTS for DL TBF=%d (TRX=%d, TS=%d)\n", tfi, trx, ts);
 | 
							if (req_mcs_kind == GPRS && tbf->is_egprs_enabled())
 | 
				
			||||||
		/* next TBF to handle ressource is the next one */
 | 
								continue;
 | 
				
			||||||
		pdch->next_dl_tfi = (tfi + 1) & 31;
 | 
					
 | 
				
			||||||
 | 
							age = tbf->frames_since_last_poll(fn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* compute priority */
 | 
				
			||||||
 | 
							prio = tbf_compute_priority(bts, tbf, pdch, 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 */
 | 
							/* generate DL data block */
 | 
				
			||||||
		msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn,
 | 
							msg = prio_tbf->create_dl_acked_block(fn, pdch, req_mcs_kind);
 | 
				
			||||||
			ts);
 | 
							*is_egprs = prio_tbf->is_egprs_enabled();
 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return msg;
 | 
						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 */
 | 
						0x47, /* control without optional header octets, no polling, USF=111 */
 | 
				
			||||||
	0x94, /* dummy downlink control message, paging mode 00 */
 | 
						0x94, /* dummy downlink control message, paging mode 00 */
 | 
				
			||||||
	0x2b, /* no persistance level, 7 bits spare pattern */
 | 
						0x2b, /* no persistance level, 7 bits spare pattern */
 | 
				
			||||||
@@ -187,7 +347,7 @@ static uint8_t rlcmac_dl_idle[23] = {
 | 
				
			|||||||
	0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
 | 
						0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct msgb *sched_dummy(void)
 | 
					static struct msgb *sched_dummy(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct msgb *msg;
 | 
						struct msgb *msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -199,66 +359,171 @@ struct msgb *sched_dummy(void)
 | 
				
			|||||||
	return msg;
 | 
						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)
 | 
					        uint32_t fn, uint8_t block_nr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 | 
					 | 
				
			||||||
	struct gprs_rlcmac_pdch *pdch;
 | 
						struct gprs_rlcmac_pdch *pdch;
 | 
				
			||||||
	struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
 | 
						struct tbf_sched_candidates tbf_cand = {0};
 | 
				
			||||||
		*ul_ass_tbf = NULL, *ul_ack_tbf = NULL;
 | 
						struct gprs_rlcmac_tbf *poll_tbf;
 | 
				
			||||||
	uint8_t usf = 0x7;
 | 
						struct gprs_rlcmac_ul_tbf *usf_tbf = NULL;
 | 
				
			||||||
 | 
						struct gprs_rlcmac_sba *sba;
 | 
				
			||||||
 | 
						uint8_t usf;
 | 
				
			||||||
	struct msgb *msg = NULL;
 | 
						struct msgb *msg = NULL;
 | 
				
			||||||
	uint32_t poll_fn;
 | 
						uint32_t poll_fn;
 | 
				
			||||||
 | 
						enum pcu_gsmtap_category gsmtap_cat = PCU_GSMTAP_C_DL_DUMMY; /* init: make gcc happy */
 | 
				
			||||||
 | 
						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)
 | 
						if (trx >= 8 || ts >= 8)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	pdch = &bts->trx[trx].pdch[ts];
 | 
						pdch = &bts->trx[trx].pdch[ts];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!pdch->enable) {
 | 
						if (!pdch->is_enabled()) {
 | 
				
			||||||
		LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
 | 
							LOGPDCH(pdch, DRLCMACSCHED, LOGL_INFO, "Received RTS on disabled TS\n");
 | 
				
			||||||
			"TRX=%d TS=%d\n", trx, ts);
 | 
					 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* store last frame number of RTS */
 | 
						/* store last frame number of RTS */
 | 
				
			||||||
	pdch->last_rts_fn = fn;
 | 
						pdch->last_rts_fn = fn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	poll_fn = sched_poll(trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
 | 
						/* require_gprs_only: Prioritize USF for GPRS-only MS here,
 | 
				
			||||||
		&dl_ass_tbf, &ul_ack_tbf);
 | 
						 * since anyway we'll need to tx a Dl block with CS1-4 due to
 | 
				
			||||||
	/* check uplink ressource for polling */
 | 
						 * synchronization requirements. See 3GPP TS 03.64 version
 | 
				
			||||||
	if (poll_tbf)
 | 
						 * 8.12.0
 | 
				
			||||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
 | 
						 */
 | 
				
			||||||
			"TS=%d FN=%d block_nr=%d scheduling free USF for "
 | 
						require_gprs_only = (pdch->fn_without_cs14 == MS_RESYNC_NUM_FRAMES - 1);
 | 
				
			||||||
			"polling at FN=%d of %s TFI=%d\n", trx, ts, fn,
 | 
						if (require_gprs_only) {
 | 
				
			||||||
			block_nr, poll_fn,
 | 
							LOGP(DRLCMACSCHED, LOGL_DEBUG, "TRX=%d TS=%d FN=%d "
 | 
				
			||||||
			(poll_tbf->direction == GPRS_RLCMAC_UL_TBF)
 | 
							     "synchronization frame (every 18 frames), only CS1-4 allowed",
 | 
				
			||||||
				? "UL" : "DL", poll_tbf->tfi);
 | 
							     trx, ts, fn);
 | 
				
			||||||
		/* use free USF */
 | 
							req_mcs_kind = GPRS; /* only GPRS CS1-4 allowed, all MS need to be able to decode it */
 | 
				
			||||||
	/* else, we search for uplink ressource */
 | 
						} else {
 | 
				
			||||||
	else
 | 
							req_mcs_kind = EGPRS; /* all kinds are fine */
 | 
				
			||||||
		usf = sched_select_uplink(trx, ts, fn, block_nr, pdch);
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* 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 && tbf_as_ul_tbf(poll_tbf)->m_usf[ts] != USF_INVALID)
 | 
				
			||||||
 | 
								usf_tbf = 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 */
 | 
						/* Prio 1: select control message */
 | 
				
			||||||
	msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
 | 
						if ((msg = sched_select_ctrl_msg(pdch, fn, block_nr, &tbf_cand))) {
 | 
				
			||||||
		dl_ass_tbf, ul_ack_tbf);
 | 
							gsmtap_cat = PCU_GSMTAP_C_DL_CTRL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	/* Prio 2: select data message for downlink */
 | 
						/* Prio 2: select data message for downlink */
 | 
				
			||||||
	if (!msg)
 | 
						else if((msg = sched_select_dl_data_msg(bts, pdch, fn, block_nr, req_mcs_kind, &tx_is_egprs))) {
 | 
				
			||||||
		msg = sched_select_downlink(trx, ts, fn, block_nr, pdch);
 | 
							gsmtap_cat = tx_is_egprs ? PCU_GSMTAP_C_DL_DATA_EGPRS :
 | 
				
			||||||
 | 
										   PCU_GSMTAP_C_DL_DATA_GPRS;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* Prio 3: send dummy control message if need to poll or USF */
 | 
				
			||||||
 | 
						else {
 | 
				
			||||||
 | 
							/* If there's no TBF attached to this PDCH, we can submit an empty
 | 
				
			||||||
 | 
							 * data_req since there's nothing to transmit nor to poll/USF. This
 | 
				
			||||||
 | 
							 * way we help BTS energy saving (on TRX!=C0) by sending nothing
 | 
				
			||||||
 | 
							 * instead of a dummy block. The early return is done here and
 | 
				
			||||||
 | 
							 * not at the start of the function because the condition below
 | 
				
			||||||
 | 
							 * (num_tbfs==0) may not be enough, because temporary dummy TBFs
 | 
				
			||||||
 | 
							 * created to send Imm Ass Rej (see handle_tbf_reject()) don't
 | 
				
			||||||
 | 
							 * have a TFI assigned and hence are not attached to the PDCH
 | 
				
			||||||
 | 
							 * TS, so they don't show up in the count below.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							const unsigned num_tbfs = pdch->num_tbfs(GPRS_RLCMAC_DL_TBF)
 | 
				
			||||||
 | 
										+ pdch->num_tbfs(GPRS_RLCMAC_UL_TBF);
 | 
				
			||||||
 | 
							bool skip_idle = (num_tbfs == 0);
 | 
				
			||||||
 | 
					#ifdef ENABLE_DIRECT_PHY
 | 
				
			||||||
 | 
							/* In DIRECT_PHY mode we want to always submit something to L1 in
 | 
				
			||||||
 | 
							 * TRX0, since BTS is not preparing dummy bursts on idle TS for us */
 | 
				
			||||||
 | 
							skip_idle = skip_idle && trx != 0;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							if (!skip_idle && (msg = sched_dummy())) {
 | 
				
			||||||
 | 
								/* increase counter */
 | 
				
			||||||
 | 
								gsmtap_cat = PCU_GSMTAP_C_DL_DUMMY;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								msg = NULL; /* submit empty frame */
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Prio 3: send dummy contol message */
 | 
						if (tx_is_egprs && pdch->has_gprs_only_tbf_attached()) {
 | 
				
			||||||
	if (!msg)
 | 
							pdch->fn_without_cs14 += 1;
 | 
				
			||||||
		msg = sched_dummy();
 | 
						} else {
 | 
				
			||||||
 | 
							pdch->fn_without_cs14 = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!msg)
 | 
						/* Used to measure the leak rate, count all blocks */
 | 
				
			||||||
		return -ENOMEM;
 | 
						gprs_bssgp_update_frames_sent();
 | 
				
			||||||
	/* msg is now available */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* set USF */
 | 
						if (msg) {
 | 
				
			||||||
	msg->data[0] = (msg->data[0] & 0xf8) | usf;
 | 
							/* msg is now available */
 | 
				
			||||||
 | 
							bts_do_rate_ctr_add(bts, CTR_RLC_DL_BYTES, msgb_length(msg));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* send PDTCH/PACCH to L1 */
 | 
							/* set USF */
 | 
				
			||||||
	pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
 | 
							OSMO_ASSERT(msgb_length(msg) > 0);
 | 
				
			||||||
 | 
							usf = usf_tbf ? usf_tbf->m_usf[ts] : USF_UNUSED;
 | 
				
			||||||
 | 
							msg->data[0] = (msg->data[0] & 0xf8) | usf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Send to GSMTAP */
 | 
				
			||||||
 | 
							tap_n_acc(msg, bts, trx, ts, fn, gsmtap_cat);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* send PDTCH/PACCH to L1. msg=NULL accepted too (idle block) */
 | 
				
			||||||
 | 
						pcu_l1if_tx_pdtch(msg, bts, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										549
									
								
								src/gsm_rlcmac.h
									
									
									
									
									
								
							
							
						
						
									
										549
									
								
								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
 | 
					 | 
				
			||||||
							
								
								
									
										404
									
								
								src/llc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								src/llc.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,404 @@
 | 
				
			|||||||
 | 
					/* 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/msgb.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "bts.h"
 | 
				
			||||||
 | 
					#include "gprs_ms.h"
 | 
				
			||||||
 | 
					#include "pcu_utils.h"
 | 
				
			||||||
 | 
					#include "llc.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void llc_init(struct gprs_llc *llc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						llc_reset(llc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* reset LLC frame */
 | 
				
			||||||
 | 
					void llc_reset(struct gprs_llc *llc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						llc->index = 0;
 | 
				
			||||||
 | 
						llc->length = 0;
 | 
				
			||||||
 | 
						llc->prio = 0;
 | 
				
			||||||
 | 
						llc->meta_info = (struct MetaInfo){0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(llc->frame, 0x42, sizeof(llc->frame));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void llc_reset_frame_space(struct gprs_llc *llc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						llc->index = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Put an Unconfirmed Information (UI) Dummy command, see GSM 44.064, 6.4.2.2 */
 | 
				
			||||||
 | 
					void llc_put_dummy_frame(struct gprs_llc *llc, 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llc_put_frame(llc, 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 (llc->length < req_len) {
 | 
				
			||||||
 | 
							memset(&llc->frame[llc->length], 0x2b, req_len - llc->length);
 | 
				
			||||||
 | 
							llc->length = req_len;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void llc_put_frame(struct gprs_llc *llc, const uint8_t *data, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* only put frames when we are empty */
 | 
				
			||||||
 | 
						OSMO_ASSERT(llc->index == 0 && llc->length == 0);
 | 
				
			||||||
 | 
						llc_append_frame(llc, data, len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void llc_append_frame(struct gprs_llc *llc, const uint8_t *data, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* TODO: bounds check */
 | 
				
			||||||
 | 
						memcpy(llc->frame + llc->length, data, len);
 | 
				
			||||||
 | 
						llc->length += len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool llc_pdu_can_be_discarded(const uint8_t *data, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const unsigned keep_small_thresh = 60;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Is the frame small, perhaps only a TCP ACK? */
 | 
				
			||||||
 | 
						if (len <= keep_small_thresh)
 | 
				
			||||||
 | 
							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, struct GprsMs *ms)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q->ms = ms;
 | 
				
			||||||
 | 
						q->queue_size = 0;
 | 
				
			||||||
 | 
						q->queue_octets = 0;
 | 
				
			||||||
 | 
						q->avg_queue_delay = 0;
 | 
				
			||||||
 | 
						for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
 | 
				
			||||||
 | 
							INIT_LLIST_HEAD(&q->pq[i].queue);
 | 
				
			||||||
 | 
							gprs_codel_init(&q->pq[i].codel_state);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* interval=0 -> don't use codel in the LLC queue */
 | 
				
			||||||
 | 
					void llc_queue_set_codel_interval(struct gprs_llc_queue *q, unsigned int interval)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
						if (interval == LLC_CODEL_DISABLE) {
 | 
				
			||||||
 | 
							q->use_codel = false;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						q->use_codel = true;
 | 
				
			||||||
 | 
						for (i = 0; i < ARRAY_SIZE(q->pq); i++)
 | 
				
			||||||
 | 
							gprs_codel_set_interval(&q->pq[i].codel_state, interval);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static enum gprs_llc_queue_prio llc_sapi2prio(uint8_t sapi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (sapi) {
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
							return LLC_QUEUE_PRIO_GMM;
 | 
				
			||||||
 | 
						case 2:
 | 
				
			||||||
 | 
						case 7:
 | 
				
			||||||
 | 
						case 8:
 | 
				
			||||||
 | 
							return LLC_QUEUE_PRIO_TOM_SMS;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return LLC_QUEUE_PRIO_OTHER;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void llc_queue_enqueue(struct gprs_llc_queue *q, struct msgb *llc_msg, const struct timespec *expire_time)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct MetaInfo *meta_storage;
 | 
				
			||||||
 | 
						struct gprs_llc_hdr *llc_hdr = (struct gprs_llc_hdr *)msgb_data(llc_msg);
 | 
				
			||||||
 | 
						enum gprs_llc_queue_prio prio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_static_assert(sizeof(*meta_storage) <= sizeof(llc_msg->cb), info_does_not_fit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prio = llc_sapi2prio(llc_hdr->sapi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q->queue_size += 1;
 | 
				
			||||||
 | 
						q->queue_octets += msgb_length(llc_msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						meta_storage = (struct MetaInfo *)&llc_msg->cb[0];
 | 
				
			||||||
 | 
						osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time);
 | 
				
			||||||
 | 
						meta_storage->expire_time = *expire_time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msgb_enqueue(&q->pq[prio].queue, llc_msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *msg;
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
 | 
				
			||||||
 | 
							while ((msg = msgb_dequeue(&q->pq[i].queue))) {
 | 
				
			||||||
 | 
								if (bts)
 | 
				
			||||||
 | 
									bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
 | 
				
			||||||
 | 
								msgb_free(msg);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q->queue_size = 0;
 | 
				
			||||||
 | 
						q->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;
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
						size_t queue_size = 0;
 | 
				
			||||||
 | 
						size_t queue_octets = 0;
 | 
				
			||||||
 | 
						INIT_LLIST_HEAD(&new_queue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
 | 
				
			||||||
 | 
							while (1) {
 | 
				
			||||||
 | 
								if (msg1 == NULL)
 | 
				
			||||||
 | 
									msg1 = msgb_dequeue(&q->pq[i].queue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (msg2 == NULL)
 | 
				
			||||||
 | 
									msg2 = msgb_dequeue(&o->pq[i].queue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (msg1 == NULL && msg2 == NULL)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (msg1 == NULL) {
 | 
				
			||||||
 | 
									msg = msg2;
 | 
				
			||||||
 | 
									msg2 = NULL;
 | 
				
			||||||
 | 
								} else if (msg2 == NULL) {
 | 
				
			||||||
 | 
									msg = msg1;
 | 
				
			||||||
 | 
									msg1 = NULL;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									const struct MetaInfo *mi1 = (struct MetaInfo *)&msg1->cb[0];
 | 
				
			||||||
 | 
									const struct MetaInfo *mi2 = (struct 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->pq[i].queue));
 | 
				
			||||||
 | 
							OSMO_ASSERT(llist_empty(&o->pq[i].queue));
 | 
				
			||||||
 | 
							llist_splice_init(&new_queue, &q->pq[i].queue);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						o->queue_size = 0;
 | 
				
			||||||
 | 
						o->queue_octets = 0;
 | 
				
			||||||
 | 
						q->queue_size = queue_size;
 | 
				
			||||||
 | 
						q->queue_octets = queue_octets;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Prepend / Put back a previously dequeued LLC frame (llc_queue_dequeue()) */
 | 
				
			||||||
 | 
					void llc_queue_merge_prepend(struct gprs_llc_queue *q, struct gprs_llc *llc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct MetaInfo *meta_storage;
 | 
				
			||||||
 | 
						unsigned int len = llc_frame_length(llc);
 | 
				
			||||||
 | 
						struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OSMO_ASSERT(llc_msg);
 | 
				
			||||||
 | 
						memcpy(msgb_put(llc_msg, len), llc->frame, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q->queue_size += 1;
 | 
				
			||||||
 | 
						q->queue_octets += msgb_length(llc_msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						meta_storage = (struct MetaInfo *)&llc_msg->cb[0];
 | 
				
			||||||
 | 
						memcpy(meta_storage, &llc->meta_info, sizeof(struct MetaInfo));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Prepend: */
 | 
				
			||||||
 | 
						llist_add(&llc_msg->list, &q->pq[llc->prio].queue);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ALPHA 0.5f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct msgb *llc_queue_pick_msg(struct gprs_llc_queue *q, enum gprs_llc_queue_prio *prio)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *msg;
 | 
				
			||||||
 | 
						struct timespec tv_now, tv_result;
 | 
				
			||||||
 | 
						uint32_t lifetime;
 | 
				
			||||||
 | 
						unsigned int i;
 | 
				
			||||||
 | 
						const struct MetaInfo *meta_storage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
 | 
				
			||||||
 | 
							if ((msg = msgb_dequeue(&q->pq[i].queue))) {
 | 
				
			||||||
 | 
								*prio = (enum gprs_llc_queue_prio)i;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!msg)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						meta_storage = (struct MetaInfo *)&msg->cb[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						q->queue_size -= 1;
 | 
				
			||||||
 | 
						q->queue_octets -= msgb_length(msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* take the second time */
 | 
				
			||||||
 | 
						osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
 | 
				
			||||||
 | 
						timespecsub(&tv_now, &meta_storage->recv_time, &tv_result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lifetime = tv_result.tv_sec*1000 + tv_result.tv_nsec/1000000;
 | 
				
			||||||
 | 
						q->avg_queue_delay = q->avg_queue_delay * ALPHA + lifetime * (1-ALPHA);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return msg;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct msgb *llc_queue_dequeue(struct gprs_llc_queue *q, enum gprs_llc_queue_prio *out_prio, struct MetaInfo *out_info)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct msgb *msg;
 | 
				
			||||||
 | 
						struct timespec tv_now, tv_now2;
 | 
				
			||||||
 | 
						uint32_t octets = 0, frames = 0;
 | 
				
			||||||
 | 
						struct gprs_rlcmac_bts *bts = q->ms->bts;
 | 
				
			||||||
 | 
						struct gprs_pcu *pcu = bts->pcu;
 | 
				
			||||||
 | 
						struct timespec hyst_delta = {0, 0};
 | 
				
			||||||
 | 
						enum gprs_llc_queue_prio prio;
 | 
				
			||||||
 | 
						const struct MetaInfo *info = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pcu->vty.llc_discard_csec)
 | 
				
			||||||
 | 
							csecs_to_timespec(pcu->vty.llc_discard_csec, &hyst_delta);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
 | 
				
			||||||
 | 
						timespecadd(&tv_now, &hyst_delta, &tv_now2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while ((msg = llc_queue_pick_msg(q, &prio))) {
 | 
				
			||||||
 | 
							info = (const struct MetaInfo *)&msg->cb[0];
 | 
				
			||||||
 | 
							const struct timespec *tv_disc = &info->expire_time;
 | 
				
			||||||
 | 
							const struct timespec *tv_recv = &info->recv_time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							gprs_bssgp_update_queue_delay(tv_recv, &tv_now);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (q->use_codel) {
 | 
				
			||||||
 | 
								int bytes = llc_queue_octets(q);
 | 
				
			||||||
 | 
								if (gprs_codel_control(&q->pq[prio].codel_state, tv_recv, &tv_now, bytes))
 | 
				
			||||||
 | 
									goto drop_frame;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Is the age below the low water mark? */
 | 
				
			||||||
 | 
							if (!llc_queue_is_frame_expired(&tv_now2, tv_disc))
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Is the age below the high water mark */
 | 
				
			||||||
 | 
							if (!llc_queue_is_frame_expired(&tv_now, tv_disc)) {
 | 
				
			||||||
 | 
								/* Has the previous message not been dropped? */
 | 
				
			||||||
 | 
								if (frames == 0)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Hysteresis mode, try to discard LLC messages until
 | 
				
			||||||
 | 
								 * the low water mark has been reached */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Check whether to abort the hysteresis mode:
 | 
				
			||||||
 | 
								 * Can the PDU be discarded according to its type? */
 | 
				
			||||||
 | 
								if (!llc_pdu_can_be_discarded(msg->data, msg->len))
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_TIMEDOUT);
 | 
				
			||||||
 | 
					drop_frame:
 | 
				
			||||||
 | 
							frames++;
 | 
				
			||||||
 | 
							octets += msg->len;
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
 | 
							bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
 | 
				
			||||||
 | 
							continue;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (frames) {
 | 
				
			||||||
 | 
							LOGPMS(q->ms, DTBFDL, LOGL_NOTICE, "Discarding LLC PDU "
 | 
				
			||||||
 | 
								"because lifetime limit reached, "
 | 
				
			||||||
 | 
								"count=%u new_queue_size=%zu\n",
 | 
				
			||||||
 | 
								  frames, llc_queue_size(q));
 | 
				
			||||||
 | 
							if (frames > 0xff)
 | 
				
			||||||
 | 
								frames = 0xff;
 | 
				
			||||||
 | 
							if (octets > 0xffffff)
 | 
				
			||||||
 | 
								octets = 0xffffff;
 | 
				
			||||||
 | 
							if (pcu->bssgp.bctx)
 | 
				
			||||||
 | 
								bssgp_tx_llc_discarded(pcu->bssgp.bctx, ms_tlli(q->ms), frames, octets);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!msg)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (out_prio)
 | 
				
			||||||
 | 
							*out_prio = prio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (out_info) {
 | 
				
			||||||
 | 
							OSMO_ASSERT(info);
 | 
				
			||||||
 | 
							*out_info = *info;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return msg;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void 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 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, >);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										157
									
								
								src/llc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/llc.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,157 @@
 | 
				
			|||||||
 | 
					/* 3GPP TS 44.064
 | 
				
			||||||
 | 
					 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
				
			||||||
 | 
					 * Copyright (C) 2022 by 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <time.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/endian.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/linuxlist.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/msgb.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/endian.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "gprs_codel.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LLC_MAX_LEN 1543
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gprs_rlcmac_bts;
 | 
				
			||||||
 | 
					struct GprsMs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct gprs_llc_hdr {
 | 
				
			||||||
 | 
					#if OSMO_IS_LITTLE_ENDIAN
 | 
				
			||||||
 | 
						union { /* 5.2, 6.2.0 */
 | 
				
			||||||
 | 
							uint8_t address;
 | 
				
			||||||
 | 
							uint8_t sapi:4, unused:2, c_r:1, pd:1;
 | 
				
			||||||
 | 
					#elif OSMO_IS_BIG_ENDIAN
 | 
				
			||||||
 | 
					/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							uint8_t address;
 | 
				
			||||||
 | 
							uint8_t pd:1, c_r:1, unused:2, sapi:4;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						uint8_t control[0];
 | 
				
			||||||
 | 
					} __attribute__ ((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct MetaInfo {
 | 
				
			||||||
 | 
						struct timespec recv_time;
 | 
				
			||||||
 | 
						struct timespec expire_time;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					enum gprs_llc_queue_prio { /* lowest value has highest prio */
 | 
				
			||||||
 | 
						LLC_QUEUE_PRIO_GMM = 0, /* SAPI 1 */
 | 
				
			||||||
 | 
						LLC_QUEUE_PRIO_TOM_SMS, /* SAPI 2,7,8 */
 | 
				
			||||||
 | 
						LLC_QUEUE_PRIO_OTHER, /* Other SAPIs */
 | 
				
			||||||
 | 
						_LLC_QUEUE_PRIO_SIZE /* used to calculate size of enum */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * I represent the LLC data to a MS
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct gprs_llc {
 | 
				
			||||||
 | 
						uint8_t frame[LLC_MAX_LEN]; /* current DL or UL frame */
 | 
				
			||||||
 | 
						uint16_t index; /* current write/read position of frame */
 | 
				
			||||||
 | 
						uint16_t length; /* len of current DL LLC_frame, 0 == no frame */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Saved when dequeue from llc_queue; allows re-enqueing in the queue if Tx fails */
 | 
				
			||||||
 | 
						enum gprs_llc_queue_prio prio;
 | 
				
			||||||
 | 
						struct MetaInfo meta_info;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void llc_init(struct gprs_llc *llc);
 | 
				
			||||||
 | 
					void llc_reset(struct gprs_llc *llc);
 | 
				
			||||||
 | 
					void llc_reset_frame_space(struct gprs_llc *llc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void llc_put_frame(struct gprs_llc *llc, const uint8_t *data, size_t len);
 | 
				
			||||||
 | 
					void llc_put_dummy_frame(struct gprs_llc *llc, size_t req_len);
 | 
				
			||||||
 | 
					void llc_append_frame(struct gprs_llc *llc, const uint8_t *data, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline uint16_t llc_chunk_size(const struct gprs_llc *llc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return llc->length - llc->index;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline uint16_t llc_remaining_space(const struct gprs_llc *llc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return LLC_MAX_LEN - llc->length;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline uint16_t llc_frame_length(const struct gprs_llc *llc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return llc->length;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void llc_consume(struct gprs_llc *llc, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						llc->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->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->length + chunk_size <= LLC_MAX_LEN;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * I store the LLC frames that come from the SGSN.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct gprs_llc_prio_queue {
 | 
				
			||||||
 | 
						struct gprs_codel codel_state;
 | 
				
			||||||
 | 
						struct llist_head queue; /* queued LLC DL data. See enum gprs_llc_queue_prio. */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					struct gprs_llc_queue {
 | 
				
			||||||
 | 
						struct GprsMs *ms; /* backpointer */
 | 
				
			||||||
 | 
						uint32_t avg_queue_delay; /* Average delay of data going through the queue */
 | 
				
			||||||
 | 
						size_t queue_size;
 | 
				
			||||||
 | 
						size_t queue_octets;
 | 
				
			||||||
 | 
						bool use_codel;
 | 
				
			||||||
 | 
						struct gprs_llc_prio_queue pq[_LLC_QUEUE_PRIO_SIZE]; /* queued LLC DL data. See enum gprs_llc_queue_prio. */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void llc_queue_calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec,
 | 
				
			||||||
 | 
							struct timespec *tv);
 | 
				
			||||||
 | 
					bool llc_queue_is_frame_expired(const struct timespec *tv_now, const struct timespec *tv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void llc_queue_init(struct gprs_llc_queue *q, struct GprsMs *ms);
 | 
				
			||||||
 | 
					void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts);
 | 
				
			||||||
 | 
					void llc_queue_set_codel_interval(struct gprs_llc_queue *q, unsigned int interval);
 | 
				
			||||||
 | 
					void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o);
 | 
				
			||||||
 | 
					void llc_queue_merge_prepend(struct gprs_llc_queue *q, struct gprs_llc *llc);
 | 
				
			||||||
 | 
					void llc_queue_enqueue(struct gprs_llc_queue *q, struct msgb *llc_msg, const struct timespec *expire_time);
 | 
				
			||||||
 | 
					struct msgb *llc_queue_dequeue(struct gprs_llc_queue *q, enum gprs_llc_queue_prio *out_prio, struct MetaInfo *out_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline size_t llc_queue_size(const struct gprs_llc_queue *q)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return q->queue_size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline size_t llc_queue_octets(const struct gprs_llc_queue *q)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return q->queue_octets;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user