mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-pcu.git
				synced 2025-11-03 21:53:32 +00:00 
			
		
		
		
	Compare commits
	
		
			517 Commits
		
	
	
		
			jerlbeck/w
			...
			0.5.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | 
							
								
								
									
										27
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -5,6 +5,7 @@
 | 
			
		||||
Makefile.in
 | 
			
		||||
Makefile
 | 
			
		||||
.deps
 | 
			
		||||
src/cscope*
 | 
			
		||||
 | 
			
		||||
aclocal.m4
 | 
			
		||||
autom4te.cache
 | 
			
		||||
@@ -13,10 +14,11 @@ config.guess
 | 
			
		||||
config.sub
 | 
			
		||||
config.status
 | 
			
		||||
configure
 | 
			
		||||
compile
 | 
			
		||||
depcomp
 | 
			
		||||
install-sh
 | 
			
		||||
missing
 | 
			
		||||
libtool
 | 
			
		||||
*libtool
 | 
			
		||||
ltmain.sh
 | 
			
		||||
 | 
			
		||||
core
 | 
			
		||||
@@ -32,13 +34,22 @@ src/osmo-pcu-remote
 | 
			
		||||
.dirstamp
 | 
			
		||||
tests/atconfig
 | 
			
		||||
tests/package.m4
 | 
			
		||||
tests/alloc/AllocTest
 | 
			
		||||
tests/rlcmac/RLCMACTest
 | 
			
		||||
tests/tbf/TbfTest
 | 
			
		||||
tests/types/TypesTest
 | 
			
		||||
tests/ms/MsTest
 | 
			
		||||
tests/llist/LListTest
 | 
			
		||||
tests/codel/codel_test
 | 
			
		||||
tests/*/*Test
 | 
			
		||||
tests/*/*_test
 | 
			
		||||
tests/emu/pcu_emu
 | 
			
		||||
tests/testsuite
 | 
			
		||||
tests/testsuite.log
 | 
			
		||||
 | 
			
		||||
# ignore debian files
 | 
			
		||||
.tarball-version
 | 
			
		||||
debian/autoreconf.after
 | 
			
		||||
debian/autoreconf.before
 | 
			
		||||
debian/files
 | 
			
		||||
debian/*.debhelper*
 | 
			
		||||
debian/*.substvars
 | 
			
		||||
debian/osmo-pcu-dbg/
 | 
			
		||||
debian/osmo-pcu.substvars
 | 
			
		||||
debian/osmo-pcu/
 | 
			
		||||
debian/tmp/
 | 
			
		||||
 | 
			
		||||
osmo-pcu.pc
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
[gerrit]
 | 
			
		||||
host=gerrit.osmocom.org
 | 
			
		||||
project=osmo-pcu
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
 | 
			
		||||
 | 
			
		||||
SUBDIRS = src examples tests
 | 
			
		||||
EXTRA_DIST = osmoappdesc.py
 | 
			
		||||
SUBDIRS = include src examples tests
 | 
			
		||||
EXTRA_DIST = osmoappdesc.py README.md
 | 
			
		||||
 | 
			
		||||
@RELMAKE@
 | 
			
		||||
 | 
			
		||||
pkgconfigdir = $(libdir)/pkgconfig
 | 
			
		||||
pkgconfig_DATA = osmo-pcu.pc
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								README
									
									
									
									
									
								
							@@ -1,20 +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 (only semi-duplex)
 | 
			
		||||
 * No handover support
 | 
			
		||||
 * No measurement support
 | 
			
		||||
 * No TA loop
 | 
			
		||||
 * No power loop
 | 
			
		||||
 * No CS loop
 | 
			
		||||
 * No EGPRS
 | 
			
		||||
							
								
								
									
										84
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
osmo-pcu - Osmocom Packet Control Unit
 | 
			
		||||
======================================
 | 
			
		||||
 | 
			
		||||
This repository contains a C/C++-language implementation of a GPRS
 | 
			
		||||
Packet Control Unit, as specified by ETSI/3GPP.  It is part of the
 | 
			
		||||
[Osmocom](https://osmocom.org/) Open Source Mobile Communications
 | 
			
		||||
project.
 | 
			
		||||
 | 
			
		||||
The Packet Control Unit is terminating the Layer 2 (RLC/MAC) of the GPRS
 | 
			
		||||
radio interface and adapting it to the Gb Interface (BSSGP+NS Protocol)
 | 
			
		||||
towards the SGSN.
 | 
			
		||||
 | 
			
		||||
The PCU interfaces with the physical layer of the radio interface.
 | 
			
		||||
OsmoPCU is typically used co-located with the BTS, specifically
 | 
			
		||||
[OsmoBTS](https://osmocom.org/projects/osmobts/wiki).
 | 
			
		||||
For legacy BTSs that run proprietary sotware without an interface to
 | 
			
		||||
OsmoPCU, you may also co-locate it with the BSC, specifically
 | 
			
		||||
[OsmoBSC](https://osmocom.org/projects/openbsc/wiki/Osmo-bsc)
 | 
			
		||||
 | 
			
		||||
Homepage
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
The official homepage of the project is
 | 
			
		||||
https://osmocom.org/projects/osmopcu/wiki/OsmoPCU
 | 
			
		||||
 | 
			
		||||
GIT Repository
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
You can clone from the official osmo-pcu.git repository using
 | 
			
		||||
 | 
			
		||||
	git clone git://git.osmocom.org/osmo-pcu.git
 | 
			
		||||
 | 
			
		||||
There is a cgit interface at http://git.osmocom.org/osmo-pcu/
 | 
			
		||||
 | 
			
		||||
Documentation
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
We provide a
 | 
			
		||||
[user manual](http://ftp.osmocom.org/docs/latest/osmopcu-usermanual.pdf)
 | 
			
		||||
as well as a
 | 
			
		||||
[vty reference manual](http://ftp.osmocom.org/docs/latest/osmopcu-vty-reference.pdf)
 | 
			
		||||
 | 
			
		||||
Please note that a lot of the PCU configuration actually happens inside
 | 
			
		||||
the BSC, which passes this configuration via A-bis OML to the BTS, which
 | 
			
		||||
then in turn passes it via the PCU socket into OsmoPCU.
 | 
			
		||||
 | 
			
		||||
Mailing List
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Discussions related to osmo-pcu are happening on the
 | 
			
		||||
osmocom-net-gprs@lists.osmocom.org mailing list, please see
 | 
			
		||||
https://lists.osmocom.org/mailman/listinfo/osmocom-net-gprs for
 | 
			
		||||
subscription options and the list archive.
 | 
			
		||||
 | 
			
		||||
Please observe the [Osmocom Mailing List
 | 
			
		||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
 | 
			
		||||
when posting.
 | 
			
		||||
 | 
			
		||||
Contributing
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Our coding standards are described at
 | 
			
		||||
https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards
 | 
			
		||||
 | 
			
		||||
We us a gerrit based patch submission/review process for managing
 | 
			
		||||
contributions.  Please see
 | 
			
		||||
https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
 | 
			
		||||
more details
 | 
			
		||||
 | 
			
		||||
The current patch queue for osmo-pcu can be seen at
 | 
			
		||||
https://gerrit.osmocom.org/#/q/project:osmo-pcu+status:open
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Current limitations
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
 * No PFC support
 | 
			
		||||
 * No fixed allocation support (was removed from 3GPP Rel >= 5 anyway)
 | 
			
		||||
 * No extended dynamic allocation support
 | 
			
		||||
 * No unacknowledged mode operation
 | 
			
		||||
 * Only single slot assignment on uplink direction
 | 
			
		||||
 * No half-duplex class support (only semi-duplex)
 | 
			
		||||
 * No TA loop
 | 
			
		||||
 * No power loop
 | 
			
		||||
							
								
								
									
										135
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								configure.ac
									
									
									
									
									
								
							@@ -3,12 +3,19 @@ AC_INIT([osmo-pcu],
 | 
			
		||||
	m4_esyscmd([./git-version-gen .tarball-version]),
 | 
			
		||||
	[osmocom-net-gprs@lists.osmocom.org])
 | 
			
		||||
 | 
			
		||||
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
 | 
			
		||||
AC_CONFIG_AUX_DIR([.])
 | 
			
		||||
 | 
			
		||||
AM_INIT_AUTOMAKE([dist-bzip2])
 | 
			
		||||
AC_CONFIG_TESTDIR(tests)
 | 
			
		||||
 | 
			
		||||
dnl kernel style compile messages
 | 
			
		||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 | 
			
		||||
 | 
			
		||||
dnl include release helper
 | 
			
		||||
RELMAKE='-include osmo-release.mk'
 | 
			
		||||
AC_SUBST([RELMAKE])
 | 
			
		||||
 | 
			
		||||
dnl checks for programs
 | 
			
		||||
AC_PROG_MAKE_SET
 | 
			
		||||
AC_PROG_CC
 | 
			
		||||
@@ -16,32 +23,120 @@ AC_PROG_CXX
 | 
			
		||||
AC_PROG_INSTALL
 | 
			
		||||
LT_INIT
 | 
			
		||||
 | 
			
		||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
 | 
			
		||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
 | 
			
		||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
 | 
			
		||||
        AC_MSG_WARN([You need to install pkg-config])
 | 
			
		||||
fi
 | 
			
		||||
PKG_PROG_PKG_CONFIG([0.20])
 | 
			
		||||
 | 
			
		||||
dnl checks for header files
 | 
			
		||||
AC_HEADER_STDC
 | 
			
		||||
 | 
			
		||||
dnl Checks for typedefs, structures and compiler characteristics
 | 
			
		||||
 | 
			
		||||
dnl checks for libraries
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore  >= 0.3.9)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.1.4)
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
		[AS_HELP_STRING([--enable-sanitize], [Compile with address sanitizer enabled], )],
 | 
			
		||||
		[sanitize=$enableval], [sanitize="no"])
 | 
			
		||||
if test x"$sanitize" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
 | 
			
		||||
	LDFLAGS="$LDFLAGS -fsanitize=address -fsanitize=undefined"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
 | 
			
		||||
AC_ARG_ENABLE(sysmocom-bts,
 | 
			
		||||
		AC_HELP_STRING([--enable-sysmocom-bts],
 | 
			
		||||
				[enable code for sysmocom femto-bts [default=no]]),
 | 
			
		||||
		[enable_sysmocom_bts="$enableval"],[enable_sysmocom_bts="no"])
 | 
			
		||||
AC_MSG_RESULT([$enable_sysmocom_bts])
 | 
			
		||||
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
 | 
			
		||||
AC_ARG_ENABLE(werror,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
		[--enable-werror],
 | 
			
		||||
		[Turn all compiler warnings into errors, with exceptions:
 | 
			
		||||
		 a) deprecation (allow upstream to mark deprecation without breaking builds);
 | 
			
		||||
		 b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
 | 
			
		||||
		]
 | 
			
		||||
	)],
 | 
			
		||||
	[werror=$enableval], [werror="no"])
 | 
			
		||||
if test x"$werror" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	WERROR_FLAGS="-Werror"
 | 
			
		||||
	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
 | 
			
		||||
	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
 | 
			
		||||
	CFLAGS="$CFLAGS $WERROR_FLAGS"
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(profile,
 | 
			
		||||
		[AS_HELP_STRING([--enable-profile], [Compile with profiling support enabled], )],
 | 
			
		||||
		[profile=$enableval], [profile="no"])
 | 
			
		||||
if test x"$profile" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	CFLAGS="$CFLAGS -pg"
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS -pg"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
dnl checks for libraries
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.11.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 sysmocom DSP [default=no]]),
 | 
			
		||||
		[enable_sysmocom_dsp="$enableval"],[enable_sysmocom_dsp="no"])
 | 
			
		||||
AC_MSG_RESULT([$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_ARG_ENABLE([vty_tests],
 | 
			
		||||
		AC_HELP_STRING([--enable-vty-tests],
 | 
			
		||||
@@ -58,7 +153,15 @@ 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)
 | 
			
		||||
 | 
			
		||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
 | 
			
		||||
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
 | 
			
		||||
 | 
			
		||||
AC_OUTPUT(
 | 
			
		||||
    osmo-pcu.pc
 | 
			
		||||
    include/Makefile
 | 
			
		||||
    src/Makefile
 | 
			
		||||
    examples/Makefile
 | 
			
		||||
    tests/Makefile
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										84
									
								
								contrib/jenkins.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										84
									
								
								contrib/jenkins.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
# jenkins build helper script for osmo-pcu.  This is how we build on jenkins.osmocom.org
 | 
			
		||||
 | 
			
		||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
 | 
			
		||||
	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
 | 
			
		||||
	exit 2
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
set -ex
 | 
			
		||||
 | 
			
		||||
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-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 [ -z "$with_dsp" -o "$with_dsp" = none ]; then
 | 
			
		||||
  echo "Direct DSP access disabled, sanitizer enabled"
 | 
			
		||||
  PCU_CONFIG="$PCU_CONFIG --enable-sanitize"
 | 
			
		||||
else
 | 
			
		||||
  echo 'Invalid $with_dsp value:' $with_dsp
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$with_vty" = "True" ]; then
 | 
			
		||||
  PCU_CONFIG="$PCU_CONFIG --enable-vty-tests"
 | 
			
		||||
elif [ -z "$with_vty" -o "$with_vty" = "False" ]; then
 | 
			
		||||
  echo "VTY tests disabled"
 | 
			
		||||
else
 | 
			
		||||
  echo 'Invalid $with_vty value:' $with_vty
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
 | 
			
		||||
 | 
			
		||||
# Build deps
 | 
			
		||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
 | 
			
		||||
 | 
			
		||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
 | 
			
		||||
export LD_LIBRARY_PATH="$inst/lib"
 | 
			
		||||
 | 
			
		||||
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" AM_DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" \
 | 
			
		||||
  $MAKE distcheck \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
							
								
								
									
										17
									
								
								contrib/osmo-pcu.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								contrib/osmo-pcu.service
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=sysmocom sysmoPCU
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
 | 
			
		||||
Restart=always
 | 
			
		||||
RestartSec=2
 | 
			
		||||
RestartPreventExitStatus=1
 | 
			
		||||
 | 
			
		||||
# The msg queues must be read fast enough
 | 
			
		||||
CPUSchedulingPolicy=rr
 | 
			
		||||
CPUSchedulingPriority=1
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
Alias=sysmopcu.service
 | 
			
		||||
@@ -3,7 +3,7 @@ Description=sysmocom sysmoPCU
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg -e
 | 
			
		||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
 | 
			
		||||
Restart=always
 | 
			
		||||
RestartSec=2
 | 
			
		||||
RestartPreventExitStatus=1
 | 
			
		||||
@@ -14,3 +14,4 @@ CPUSchedulingPriority=1
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
Alias=osmo-pcu.service
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										168
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
			
		||||
osmo-pcu (0.5.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Neels Hofmeyr ]
 | 
			
		||||
  * jenkins: use osmo-clean-workspace.sh before and after build
 | 
			
		||||
  * vty: skip installing cmds now always installed by default
 | 
			
		||||
  * implement support for 3-digit MNC with leading zeros
 | 
			
		||||
  * configure: add --enable-werror
 | 
			
		||||
  * mslot_class: find_free_tfi(): use uint32_t to shift 1 << 31
 | 
			
		||||
  * mslot_class: two more: use uint32_t to shift 1 << 31
 | 
			
		||||
  * Revert "Use Timing Advance Index in UL assignments"
 | 
			
		||||
  * Revert "Rewrite Packet Uplink Assignment"
 | 
			
		||||
  * Revert "Rewrite Packet Downlink Assignment"
 | 
			
		||||
  * configure: fix --enable-sysmocom-dsp and --with-sysmobts flags
 | 
			
		||||
  * configure: properly quote CFLAGS in lc15 check
 | 
			
		||||
  * Revert "Rewrite EGPRS Packet Uplink Assignment"
 | 
			
		||||
  * use osmo_init_logging2() with proper talloc ctx
 | 
			
		||||
 | 
			
		||||
  [ Minh-Quang Nguyen ]
 | 
			
		||||
  * PCU: Fix TA adjustment
 | 
			
		||||
  * PCU: display TA information in TBF stats
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
  * Remove unused parameter
 | 
			
		||||
  * Move multislot table to separate file
 | 
			
		||||
  * Replace '.' in counter names with ':'
 | 
			
		||||
  * Fix compiler warning
 | 
			
		||||
  * TBF: log timer override
 | 
			
		||||
  * TBF: fix compiler warning in test
 | 
			
		||||
  * TBF: expand timer logging
 | 
			
		||||
  * vty: print class and TBFs for each MS
 | 
			
		||||
  * DL window: constify resend_needed() function
 | 
			
		||||
  * TBF: move EGPRS enablement into (U|D)L-TBF
 | 
			
		||||
  * TBF-DL: fix misleading idle time check
 | 
			
		||||
  * TBF: remove unused variable
 | 
			
		||||
  * Remove unused includes and forward declarations
 | 
			
		||||
  * Fix tests after rate_ctr change
 | 
			
		||||
  * Introduce LOGTBF* for consistent logging
 | 
			
		||||
  * TBF: implement independent T31xx timers
 | 
			
		||||
  * TBF: add N3101 counter
 | 
			
		||||
  * Fix warnings
 | 
			
		||||
  * Add function to get max supported MS class
 | 
			
		||||
  * Add --enable-sanitize configure option
 | 
			
		||||
  * Enable sanitize for CI test
 | 
			
		||||
  * Add tests for pcu_lsb()
 | 
			
		||||
  * Add optional profiling support
 | 
			
		||||
  * TBF: unify timer handling
 | 
			
		||||
  * TBF: log timer invocation source
 | 
			
		||||
  * TBF: bail out for unknown timers
 | 
			
		||||
  * Fix llc_queue_size() type
 | 
			
		||||
  * TBF-DL: mark rcvd_dl_ack() parameters as boolean
 | 
			
		||||
  * window: move encoding into functions
 | 
			
		||||
  * cosmetic: clarify coding scheme and puncturing
 | 
			
		||||
  * Make TBF state private
 | 
			
		||||
  * TBF: cleanup state flag handling
 | 
			
		||||
  * Clarify RACH-related interfaces
 | 
			
		||||
  * TBF-UL: add simpler test helper
 | 
			
		||||
  * Avoid code duplication in TBF test
 | 
			
		||||
  * TBF: move window parameters to UL/DL level
 | 
			
		||||
  * TBF-DL: move priority computation into function
 | 
			
		||||
  * TBF: unify EGPRS window calculation
 | 
			
		||||
  * Don't access TBF internals in vty functions
 | 
			
		||||
  * Fix jenkins.sh to match jenkins job axis filter
 | 
			
		||||
  * Allocate global context for TypesTest
 | 
			
		||||
  * Fix sanitizer build
 | 
			
		||||
  * Rewrite EGPRS Packet Uplink Assignment
 | 
			
		||||
  * Rewrite Packet Downlink Assignment
 | 
			
		||||
  * Rewrite Packet Uplink Assignment
 | 
			
		||||
  * Use Timing Advance Index in UL assignments
 | 
			
		||||
  * Allow specifying sysmocom headers explicitly
 | 
			
		||||
  * TBF: log source of state transitions
 | 
			
		||||
  * jenkins.sh: Disable building doxygen for deps
 | 
			
		||||
  * Set V_N and V_B to known initial state
 | 
			
		||||
  * TBF: add dedicated log categories
 | 
			
		||||
  * TBF: make UL/DL state internal
 | 
			
		||||
  * TBF: make UL ack state internal
 | 
			
		||||
  * TBF: make poll state internal
 | 
			
		||||
  * TBF: adjust test log levels
 | 
			
		||||
  * Add tests for find_multi_slots()
 | 
			
		||||
  * AllocTest: adjust test_alloc_b()
 | 
			
		||||
  * AllocTest: expand test output
 | 
			
		||||
  * AllocTest: remove assumption on max MS class
 | 
			
		||||
  * Add multislot classes from latest spec
 | 
			
		||||
  * cosmetic: fix whitespace issue with include files
 | 
			
		||||
  * TBF: decrease L1 logging verbosity in test
 | 
			
		||||
  * TBF: override send function via linker option
 | 
			
		||||
  * Simplify TS alloc: adjust allocator signatures
 | 
			
		||||
  * Simplify TS alloc: fix allocation calls
 | 
			
		||||
  * Simplify TS alloc: avoid TS reassignment
 | 
			
		||||
  * Simplify TS alloc: use defines for constants
 | 
			
		||||
  * Simplify TS alloc: adjust function signatures
 | 
			
		||||
  * TS alloc: print suggested TRX on allocation errors
 | 
			
		||||
  * Simplify TS alloc: internalize TRX check
 | 
			
		||||
  * TBF: decrease logging verbosity for traffic
 | 
			
		||||
  * TBF: add helpers for assignment type handling
 | 
			
		||||
  * TBF: show assignment kind in vty
 | 
			
		||||
  * vty: drop unused function
 | 
			
		||||
  * RACH: improve single block detection
 | 
			
		||||
  * TBF: move common test code into functions
 | 
			
		||||
  * emu: use libosmocore definitions
 | 
			
		||||
  * Use explicit type for pcu_lsb()
 | 
			
		||||
  * Move paging generation into PDCH
 | 
			
		||||
  * Move include guard to the top
 | 
			
		||||
  * Update header includes
 | 
			
		||||
  * Simplify TS alloc: split off RX mask computation
 | 
			
		||||
  * Simplify TS alloc: separate capacity computation
 | 
			
		||||
  * Simplify TS alloc: split allocation
 | 
			
		||||
  * Simplify TS alloc: split USF/UL allocation
 | 
			
		||||
  * Move PDCH-related functions into separate files
 | 
			
		||||
  * Simplify TS alloc: don't use PDCH for free TFI
 | 
			
		||||
  * Simplify TS alloc: constify max dl slot func
 | 
			
		||||
  * TBF: make network counters internal
 | 
			
		||||
  * Simplify TS alloc: move slot assignment
 | 
			
		||||
  * Simplify TS alloc: move slot check into functions
 | 
			
		||||
 | 
			
		||||
  [ Pau Espin Pedrol ]
 | 
			
		||||
  * Print error cause of pcu socket connect failure
 | 
			
		||||
  * gprs_bssgp_pcu.cpp: Comment unused function parse_ra_cap
 | 
			
		||||
 | 
			
		||||
  [ Stefan Sperling ]
 | 
			
		||||
  * Make osmo-pcu wait for BTS to become available at start-up time.
 | 
			
		||||
  * improve documentation of Encoding::write_paging_request()
 | 
			
		||||
 | 
			
		||||
  [ Alexander Couzens ]
 | 
			
		||||
  * pcuif_proto.h: fix whitespaces and indention
 | 
			
		||||
  * pcuif_proto: add version 8 features
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * cosmetic: remove runaway semicolon
 | 
			
		||||
  * pcu_l1_if: add frame number to log output
 | 
			
		||||
  * tbf: add frame number to log output
 | 
			
		||||
 | 
			
		||||
 -- Pau Espin Pedrol <pespin@sysmocom.de>  Thu, 03 May 2018 16:20:00 +0200
 | 
			
		||||
 | 
			
		||||
osmo-pcu (0.4.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  [ Holger Hans Peter Freyther ]
 | 
			
		||||
  * Initial release.
 | 
			
		||||
 | 
			
		||||
  [ Max ]
 | 
			
		||||
  * Use value string check from osmo-ci
 | 
			
		||||
  * cosmetic: tighten direct-phy related code
 | 
			
		||||
  * Support receiving SI13 from BTS
 | 
			
		||||
  * Move gsmtap and accounting into separate function
 | 
			
		||||
  * cosmetic: convert explicit warnings to fixme/todo
 | 
			
		||||
  * Assert valid CS
 | 
			
		||||
  * TBF-DL: extend index check for RLC block copy
 | 
			
		||||
  * TS alloc: properly count UL slots
 | 
			
		||||
  * cosmetic: reformat multislot classes table
 | 
			
		||||
 | 
			
		||||
  [ Philipp Maier ]
 | 
			
		||||
  * gb: allow only packets from a specific SGSN
 | 
			
		||||
 | 
			
		||||
  [ Harald Welte ]
 | 
			
		||||
  * tests: Don't use private version of log_info but global gprs_log_info
 | 
			
		||||
  * Call osmo_init_logging() before static BTS constructor
 | 
			
		||||
  * Forward GPRS SUSPEND REQ from BTS to SGSN using BSSGP
 | 
			
		||||
  * Debian: Cosmetic changes to control file; add better Description
 | 
			
		||||
  * Debian: print test results in case of failure + clean-up autotest
 | 
			
		||||
  * Debian: migrate from DEB_BUILD_HARDENING to DEB_BUILD_MAINT_OPTIONS
 | 
			
		||||
  * Debian: upgrade to debhelper 9 / Standards 3.9.8
 | 
			
		||||
 | 
			
		||||
 -- Harald Welte <laforge@gnumonks.org>  Sun, 29 Oct 2017 12:03:05 +0100
 | 
			
		||||
 | 
			
		||||
osmo-pcu (0.3.0) UNRELEASED; urgency=medium
 | 
			
		||||
 | 
			
		||||
  * Initial release.
 | 
			
		||||
 | 
			
		||||
 -- Holger Hans Peter Freyther <holger@moiji-mobile.com>  Fri, 01 Apr 2016 18:59:00 +0200
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
9
 | 
			
		||||
							
								
								
									
										37
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
Source: osmo-pcu
 | 
			
		||||
Section: net
 | 
			
		||||
Priority: optional
 | 
			
		||||
Maintainer: Holger Hans Peter Freyther <holger@moiji-mobile.com>
 | 
			
		||||
Build-Depends: debhelper (>= 9),
 | 
			
		||||
               dh-autoreconf,
 | 
			
		||||
               dh-systemd (>= 1.5),
 | 
			
		||||
               autotools-dev,
 | 
			
		||||
               pkg-config,
 | 
			
		||||
               libosmocore-dev (>= 0.10.1)
 | 
			
		||||
Standards-Version: 3.9.8
 | 
			
		||||
Homepage: http://osmocom.org/projects/osmopcu
 | 
			
		||||
Vcs-Git: git://git.osmocom.org/osmo-pcu
 | 
			
		||||
Vcs-Browser: http://git.osmocom.org/osmo-pcu/
 | 
			
		||||
 | 
			
		||||
Package: osmo-pcu
 | 
			
		||||
Architecture: any
 | 
			
		||||
Depends: ${shlibs:Depends}, ${misc:Depends}
 | 
			
		||||
Description: Osmocom GPRS/EDGE Packet Control Unit (PCU)
 | 
			
		||||
 The GPRS Packet Control Unit is co-located with the GSM BTS or GSM BSC
 | 
			
		||||
 in order to provide packet-switched services for 2G (2.5G, 2.75G)
 | 
			
		||||
 networks.  OsmoPCU is the Osmcoom 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 Osmcoom 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.
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
usr/bin/osmo-pcu
 | 
			
		||||
usr/include/osmocom/pcu/pcuif_proto.h
 | 
			
		||||
usr/lib/*/pkgconfig/osmo-pcu.pc
 | 
			
		||||
							
								
								
									
										15
									
								
								debian/osmo-pcu.service
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								debian/osmo-pcu.service
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom osmo-pcu
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
 | 
			
		||||
Restart=always
 | 
			
		||||
RestartSec=2
 | 
			
		||||
 | 
			
		||||
# Read quickly enough
 | 
			
		||||
CPUSchedulingPolicy=rr
 | 
			
		||||
CPUSchedulingPriority=1
 | 
			
		||||
 | 
			
		||||
[Install]
 | 
			
		||||
WantedBy=multi-user.target
 | 
			
		||||
							
								
								
									
										28
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										28
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
#!/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_autoreconf:
 | 
			
		||||
	echo $(VERSION) > .tarball-version
 | 
			
		||||
	dh_autoreconf
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
3.0 (native)
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
pcu
 | 
			
		||||
 flow-control-interval 10
 | 
			
		||||
 cs 4
 | 
			
		||||
 alloc-algorithm b
 | 
			
		||||
 cs 2
 | 
			
		||||
 alloc-algorithm dynamic
 | 
			
		||||
 alpha 0
 | 
			
		||||
 gamma 0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								include/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								include/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
nobase_include_HEADERS = \
 | 
			
		||||
	osmocom/pcu/pcuif_proto.h
 | 
			
		||||
@@ -1,18 +1,25 @@
 | 
			
		||||
#ifndef _PCUIF_PROTO_H
 | 
			
		||||
#define _PCUIF_PROTO_H
 | 
			
		||||
 | 
			
		||||
#define PCU_IF_VERSION		0x05
 | 
			
		||||
#include <osmocom/gsm/l1sap.h>
 | 
			
		||||
 | 
			
		||||
#define PCU_SOCK_DEFAULT	"/tmp/pcu_bts"
 | 
			
		||||
 | 
			
		||||
#define PCU_IF_VERSION		0x09
 | 
			
		||||
#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_DATA_IND	0x02	/* receive data from given channel */
 | 
			
		||||
#define PCU_IF_MSG_RTS_REQ	0x10	/* ready to send request */
 | 
			
		||||
#define PCU_IF_MSG_DATA_CNF_DT	0x11	/* confirm (with direct tlli) */
 | 
			
		||||
#define PCU_IF_MSG_RACH_IND	0x22	/* receive RACH */
 | 
			
		||||
#define PCU_IF_MSG_INFO_IND	0x32	/* retrieve BTS info */
 | 
			
		||||
#define PCU_IF_MSG_ACT_REQ	0x40	/* activate/deactivate PDCH */
 | 
			
		||||
#define PCU_IF_MSG_TIME_IND	0x52	/* GSM time indication */
 | 
			
		||||
#define PCU_IF_MSG_PAG_REQ	0x60	/* paging request */
 | 
			
		||||
#define PCU_IF_MSG_TXT_IND	0x70	/* Text indication for BTS */
 | 
			
		||||
 | 
			
		||||
/* sapi */
 | 
			
		||||
#define PCU_IF_SAPI_RACH	0x01	/* channel request on CCCH */
 | 
			
		||||
@@ -22,6 +29,7 @@
 | 
			
		||||
#define PCU_IF_SAPI_PDTCH	0x05	/* packet data/control/ccch block */
 | 
			
		||||
#define PCU_IF_SAPI_PRACH	0x06	/* packet random access channel */
 | 
			
		||||
#define PCU_IF_SAPI_PTCCH	0x07	/* packet TA control channel */
 | 
			
		||||
#define PCU_IF_SAPI_AGCH_DT	0x08	/* assignment on AGCH but with additional TLLI */
 | 
			
		||||
 | 
			
		||||
/* flags */
 | 
			
		||||
#define PCU_IF_FLAG_ACTIVE	(1 << 0)/* BTS is active */
 | 
			
		||||
@@ -40,6 +48,16 @@
 | 
			
		||||
#define PCU_IF_FLAG_MCS8	(1 << 27)
 | 
			
		||||
#define PCU_IF_FLAG_MCS9	(1 << 28)
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
@@ -50,6 +68,24 @@ struct gsm_pcu_if_data {
 | 
			
		||||
	uint8_t		ts_nr;
 | 
			
		||||
	uint8_t		block_nr;
 | 
			
		||||
	int8_t		rssi;
 | 
			
		||||
	uint16_t	ber10k;		/* !< \brief BER in units of 0.01% */
 | 
			
		||||
	int16_t		ta_offs_qbits;	/* !< \brief Burst TA Offset in quarter bits */
 | 
			
		||||
	int16_t		lqual_cb;	/* !< \brief Link quality in centiBel */
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
/* data confirmation with direct tlli (instead of raw mac block with tlli) */
 | 
			
		||||
struct gsm_pcu_if_data_cnf_dt {
 | 
			
		||||
	uint8_t		sapi;
 | 
			
		||||
	uint32_t	tlli;
 | 
			
		||||
	uint32_t	fn;
 | 
			
		||||
	uint16_t	arfcn;
 | 
			
		||||
	uint8_t		trx_nr;
 | 
			
		||||
	uint8_t		ts_nr;
 | 
			
		||||
	uint8_t		block_nr;
 | 
			
		||||
	int8_t		rssi;
 | 
			
		||||
	uint16_t	ber10k;		/* !< \brief BER in units of 0.01% */
 | 
			
		||||
	int16_t		ta_offs_qbits;	/* !< \brief Burst TA Offset in quarter bits */
 | 
			
		||||
	int16_t		lqual_cb;	/* !< \brief Link quality in centiBel */
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct gsm_pcu_if_rts_req {
 | 
			
		||||
@@ -64,10 +100,12 @@ struct gsm_pcu_if_rts_req {
 | 
			
		||||
 | 
			
		||||
struct gsm_pcu_if_rach_ind {
 | 
			
		||||
	uint8_t		sapi;
 | 
			
		||||
	uint8_t		ra;
 | 
			
		||||
	uint16_t	ra;
 | 
			
		||||
	int16_t		qta;
 | 
			
		||||
	uint32_t	fn;
 | 
			
		||||
	uint16_t	arfcn;
 | 
			
		||||
	uint8_t		is_11bit;
 | 
			
		||||
	uint8_t		burst_type;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct gsm_pcu_if_info_trx {
 | 
			
		||||
@@ -84,12 +122,14 @@ struct gsm_pcu_if_info_ind {
 | 
			
		||||
	struct gsm_pcu_if_info_trx trx[8];	/* TRX infos per BTS */
 | 
			
		||||
	uint8_t		bsic;
 | 
			
		||||
	/* RAI */
 | 
			
		||||
	uint16_t	mcc, mnc, lac, rac;
 | 
			
		||||
	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  */
 | 
			
		||||
	/* cell */
 | 
			
		||||
	uint16_t	cell_id;
 | 
			
		||||
	uint16_t	repeat_time;
 | 
			
		||||
	uint8_t		repeat_count;
 | 
			
		||||
@@ -140,9 +180,11 @@ struct gsm_pcu_if {
 | 
			
		||||
	union {
 | 
			
		||||
		struct gsm_pcu_if_data		data_req;
 | 
			
		||||
		struct gsm_pcu_if_data		data_cnf;
 | 
			
		||||
		struct gsm_pcu_if_data_cnf_dt	data_cnf_dt;
 | 
			
		||||
		struct gsm_pcu_if_data		data_ind;
 | 
			
		||||
		struct gsm_pcu_if_rts_req	rts_req;
 | 
			
		||||
		struct gsm_pcu_if_rach_ind	rach_ind;
 | 
			
		||||
		struct gsm_pcu_if_txt_ind	txt_ind;
 | 
			
		||||
		struct gsm_pcu_if_info_ind	info_ind;
 | 
			
		||||
		struct gsm_pcu_if_act_req	act_req;
 | 
			
		||||
		struct gsm_pcu_if_time_ind	time_ind;
 | 
			
		||||
							
								
								
									
										10
									
								
								osmo-pcu.pc.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								osmo-pcu.pc.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
prefix=@prefix@
 | 
			
		||||
exec_prefix=@exec_prefix@
 | 
			
		||||
libdir=@libdir@
 | 
			
		||||
includedir=@includedir@/
 | 
			
		||||
 | 
			
		||||
Name: OsmoPCU
 | 
			
		||||
Description: Osmocom PCU implementation
 | 
			
		||||
Requires:
 | 
			
		||||
Version: @VERSION@
 | 
			
		||||
Cflags: -I${includedir}
 | 
			
		||||
							
								
								
									
										111
									
								
								src/Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								src/Makefile.am
									
									
									
									
									
								
							@@ -18,10 +18,15 @@
 | 
			
		||||
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)
 | 
			
		||||
AUTOMAKE_OPTIONS = subdir-objects
 | 
			
		||||
AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)
 | 
			
		||||
 | 
			
		||||
if ENABLE_SYSMODSP
 | 
			
		||||
AM_CPPFLAGS += -DENABLE_SYSMODSP
 | 
			
		||||
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if ENABLE_LC15BTS_PHY
 | 
			
		||||
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
AM_CXXFLAGS = -Wall -ldl -pthread
 | 
			
		||||
@@ -41,40 +46,31 @@ libgprs_la_SOURCES = \
 | 
			
		||||
	gprs_ms.cpp \
 | 
			
		||||
	gprs_ms_storage.cpp \
 | 
			
		||||
	gsm_timer.cpp \
 | 
			
		||||
	bitvector.cpp \
 | 
			
		||||
	pcu_l1_if.cpp \
 | 
			
		||||
	pcu_vty.c \
 | 
			
		||||
	pcu_vty_functions.cpp \
 | 
			
		||||
	mslot_class.c \
 | 
			
		||||
	tbf.cpp \
 | 
			
		||||
	tbf_ul.cpp \
 | 
			
		||||
	tbf_dl.cpp \
 | 
			
		||||
	bts.cpp \
 | 
			
		||||
	pdch.cpp \
 | 
			
		||||
	poll_controller.cpp \
 | 
			
		||||
	encoding.cpp \
 | 
			
		||||
	sba.cpp \
 | 
			
		||||
	decoding.cpp \
 | 
			
		||||
	llc.cpp \
 | 
			
		||||
	rlc.cpp \
 | 
			
		||||
	gprs_codel.c
 | 
			
		||||
 | 
			
		||||
if ENABLE_SYSMOBTS
 | 
			
		||||
libgprs_la_SOURCES += \
 | 
			
		||||
	sysmo_sock.cpp
 | 
			
		||||
else
 | 
			
		||||
libgprs_la_SOURCES += \
 | 
			
		||||
	openbts_sock.cpp
 | 
			
		||||
endif
 | 
			
		||||
	osmobts_sock.cpp \
 | 
			
		||||
	gprs_codel.c \
 | 
			
		||||
	gprs_coding_scheme.cpp \
 | 
			
		||||
	egprs_rlc_compression.cpp
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS = \
 | 
			
		||||
	osmo-pcu
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS =
 | 
			
		||||
 | 
			
		||||
if ENABLE_SYSMODSP
 | 
			
		||||
noinst_PROGRAMS += \
 | 
			
		||||
	osmo-pcu-remote
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	gprs_debug.h \
 | 
			
		||||
	csn1.h \
 | 
			
		||||
@@ -83,16 +79,14 @@ noinst_HEADERS = \
 | 
			
		||||
	gprs_rlcmac.h \
 | 
			
		||||
	gprs_ms.h \
 | 
			
		||||
	gprs_ms_storage.h \
 | 
			
		||||
	pcuif_proto.h \
 | 
			
		||||
	pcu_l1_if.h \
 | 
			
		||||
	gsm_timer.h \
 | 
			
		||||
	bitvector.h \
 | 
			
		||||
	pcu_vty.h \
 | 
			
		||||
	pcu_vty_functions.h \
 | 
			
		||||
	sysmo_l1_if.h \
 | 
			
		||||
	femtobts.h \
 | 
			
		||||
	mslot_class.h \
 | 
			
		||||
	tbf.h \
 | 
			
		||||
	bts.h \
 | 
			
		||||
	pdch.h \
 | 
			
		||||
	poll_controller.h \
 | 
			
		||||
	encoding.h \
 | 
			
		||||
	sba.h \
 | 
			
		||||
@@ -101,19 +95,69 @@ noinst_HEADERS = \
 | 
			
		||||
	llc.h \
 | 
			
		||||
	pcu_utils.h \
 | 
			
		||||
	cxx_linuxlist.h \
 | 
			
		||||
	gprs_codel.h
 | 
			
		||||
	gprs_codel.h \
 | 
			
		||||
	gprs_coding_scheme.h \
 | 
			
		||||
	egprs_rlc_compression.h
 | 
			
		||||
 | 
			
		||||
nobase_include_HEADERS =
 | 
			
		||||
	osmocom/pcu/pcuif_proto.h
 | 
			
		||||
 | 
			
		||||
osmo_pcu_SOURCES = pcu_main.cpp
 | 
			
		||||
 | 
			
		||||
if ENABLE_SYSMODSP
 | 
			
		||||
osmo_pcu_SOURCES += sysmo_l1_if.c \
 | 
			
		||||
	sysmo_l1_hw.c \
 | 
			
		||||
	femtobts.c
 | 
			
		||||
AM_CPPFLAGS += -I$(srcdir)/osmo-bts-sysmo -I$(SYSMOBTS_INCDIR)
 | 
			
		||||
 | 
			
		||||
osmo_pcu_remote_SOURCES = pcu_main.cpp \
 | 
			
		||||
	sysmo_l1_if.c \
 | 
			
		||||
	sysmo_l1_fwd.c \
 | 
			
		||||
	femtobts.c
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	osmo-bts-sysmo/sysmo_l1_if.c \
 | 
			
		||||
        osmo-bts-sysmo/sysmo_l1_if.h \
 | 
			
		||||
        osmo-bts-sysmo/sysmo_l1_hw.c \
 | 
			
		||||
        osmo-bts-sysmo/femtobts.c \
 | 
			
		||||
        osmo-bts-sysmo/femtobts.h
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS += \
 | 
			
		||||
        osmo-bts-sysmo/sysmo_l1_if.h \
 | 
			
		||||
        osmo-bts-sysmo/femtobts.h
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS += \
 | 
			
		||||
	osmo-pcu-remote
 | 
			
		||||
 | 
			
		||||
osmo_pcu_SOURCES += \
 | 
			
		||||
	osmo-bts-sysmo/sysmo_l1_if.c \
 | 
			
		||||
	osmo-bts-sysmo/sysmo_l1_hw.c \
 | 
			
		||||
	osmo-bts-sysmo/femtobts.c
 | 
			
		||||
 | 
			
		||||
osmo_pcu_remote_SOURCES = \
 | 
			
		||||
	pcu_main.cpp \
 | 
			
		||||
	osmo-bts-sysmo/sysmo_l1_if.c \
 | 
			
		||||
	osmo-bts-sysmo/sysmo_l1_fwd.c \
 | 
			
		||||
	osmo-bts-sysmo/femtobts.c
 | 
			
		||||
 | 
			
		||||
osmo_pcu_remote_LDADD = \
 | 
			
		||||
	libgprs.la \
 | 
			
		||||
	$(LIBOSMOGB_LIBS) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if ENABLE_LC15BTS_PHY
 | 
			
		||||
AM_CPPFLAGS += $(LITECELL15_CFLAGS) -I$(srcdir)/osmo-bts-litecell15
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	osmo-bts-litecell15/lc15_l1_if.c \
 | 
			
		||||
        osmo-bts-litecell15/lc15_l1_if.h \
 | 
			
		||||
        osmo-bts-litecell15/lc15_l1_hw.c \
 | 
			
		||||
        osmo-bts-litecell15/lc15bts.c \
 | 
			
		||||
        osmo-bts-litecell15/lc15bts.h
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS += \
 | 
			
		||||
        osmo-bts-litecell15/lc15_l1_if.h \
 | 
			
		||||
        osmo-bts-litecell15/lc15bts.h
 | 
			
		||||
 | 
			
		||||
osmo_pcu_SOURCES += \
 | 
			
		||||
	osmo-bts-litecell15/lc15_l1_if.c \
 | 
			
		||||
	osmo-bts-litecell15/lc15_l1_hw.c \
 | 
			
		||||
	osmo-bts-litecell15/lc15bts.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
osmo_pcu_LDADD = \
 | 
			
		||||
@@ -123,13 +167,4 @@ osmo_pcu_LDADD = \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
if ENABLE_SYSMODSP
 | 
			
		||||
osmo_pcu_remote_LDADD = \
 | 
			
		||||
	libgprs.la \
 | 
			
		||||
	$(LIBOSMOGB_LIBS) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
#MOSTLYCLEANFILES += testSource testDestination
 | 
			
		||||
 
 | 
			
		||||
@@ -1,119 +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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int bitvec_pack(struct bitvec *bv, uint8_t *buffer)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i = 0;
 | 
			
		||||
	for (i = 0; i < bv->data_len; i++)
 | 
			
		||||
	{
 | 
			
		||||
		buffer[i] = bv->data[i];
 | 
			
		||||
	}
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int bitvec_unpack(struct bitvec *bv, uint8_t *buffer)
 | 
			
		||||
{
 | 
			
		||||
	unsigned 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)
 | 
			
		||||
{
 | 
			
		||||
	unsigned 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)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	int 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);
 | 
			
		||||
unsigned int bitvec_pack(struct bitvec *bv, uint8_t *buffer);
 | 
			
		||||
unsigned int bitvec_unpack(struct bitvec *bv, uint8_t *buffer);
 | 
			
		||||
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len);
 | 
			
		||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
 | 
			
		||||
 | 
			
		||||
/*! }@ */
 | 
			
		||||
 | 
			
		||||
#endif // BITVECTOR_H
 | 
			
		||||
							
								
								
									
										1351
									
								
								src/bts.cpp
									
									
									
									
									
								
							
							
						
						
									
										1351
									
								
								src/bts.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										420
									
								
								src/bts.h
									
									
									
									
									
								
							
							
						
						
									
										420
									
								
								src/bts.h
									
									
									
									
									
								
							@@ -25,99 +25,47 @@
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/core/stat_item.h>
 | 
			
		||||
#include <osmocom/gsm/l1sap.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
	#include <mslot_class.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <gsm_rlcmac.h>
 | 
			
		||||
#include "poll_controller.h"
 | 
			
		||||
#include "sba.h"
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
#include "gprs_ms_storage.h"
 | 
			
		||||
#include "gprs_coding_scheme.h"
 | 
			
		||||
#include <cxx_linuxlist.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#define LLC_CODEL_DISABLE 0
 | 
			
		||||
#define LLC_CODEL_USE_DEFAULT (-1)
 | 
			
		||||
#define MAX_GPRS_CS 9
 | 
			
		||||
 | 
			
		||||
/* 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_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 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct BTS;
 | 
			
		||||
struct GprsMs;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * PDCH instance
 | 
			
		||||
 */
 | 
			
		||||
struct gprs_rlcmac_pdch {
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
	struct gprs_rlcmac_paging *dequeue_paging();
 | 
			
		||||
	struct msgb *packet_paging_request();
 | 
			
		||||
 | 
			
		||||
	void add_paging(struct gprs_rlcmac_paging *pag);
 | 
			
		||||
 | 
			
		||||
	void free_resources();
 | 
			
		||||
 | 
			
		||||
	bool is_enabled() const;
 | 
			
		||||
 | 
			
		||||
	void enable();
 | 
			
		||||
	void disable();
 | 
			
		||||
 | 
			
		||||
	/* dispatching of messages */
 | 
			
		||||
	int rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
 | 
			
		||||
		struct pcu_l1_meas *meas);
 | 
			
		||||
 | 
			
		||||
	gprs_rlcmac_bts *bts_data() const;
 | 
			
		||||
	BTS *bts() const;
 | 
			
		||||
	uint8_t trx_no() const;
 | 
			
		||||
 | 
			
		||||
	struct gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi);
 | 
			
		||||
	struct gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi);
 | 
			
		||||
 | 
			
		||||
	void attach_tbf(gprs_rlcmac_tbf *tbf);
 | 
			
		||||
	void detach_tbf(gprs_rlcmac_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
	unsigned num_tbfs(enum gprs_rlcmac_tbf_direction dir) const;
 | 
			
		||||
 | 
			
		||||
	void reserve(enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
	void unreserve(enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
	unsigned num_reserved(enum gprs_rlcmac_tbf_direction dir) const;
 | 
			
		||||
 | 
			
		||||
	uint8_t assigned_usf() const;
 | 
			
		||||
	uint32_t assigned_tfi(enum gprs_rlcmac_tbf_direction dir) const;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	uint8_t m_is_enabled; /* TS is enabled */
 | 
			
		||||
	uint8_t tsc; /* TSC of this slot */
 | 
			
		||||
	uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
 | 
			
		||||
	uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
 | 
			
		||||
	uint8_t next_ctrl_prio; /* next kind of ctrl message to schedule */
 | 
			
		||||
	struct llist_head paging_list; /* list of paging messages */
 | 
			
		||||
	uint32_t last_rts_fn; /* store last frame number of RTS */
 | 
			
		||||
 | 
			
		||||
	/* back pointers */
 | 
			
		||||
	struct gprs_rlcmac_trx *trx;
 | 
			
		||||
	uint8_t ts_no;
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
private:
 | 
			
		||||
	int rcv_data_block_acknowledged(uint8_t *data, uint8_t len,
 | 
			
		||||
		struct pcu_l1_meas *meas);
 | 
			
		||||
	int rcv_control_block(bitvec *rlc_block, uint32_t fn);
 | 
			
		||||
 | 
			
		||||
	void rcv_control_ack(Packet_Control_Acknowledgement_t *, uint32_t fn);
 | 
			
		||||
	void rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *, uint32_t fn);
 | 
			
		||||
	void rcv_resource_request(Packet_Resource_Request_t *t, uint32_t fn);
 | 
			
		||||
	void rcv_measurement_report(Packet_Measurement_Report_t *t, uint32_t fn);
 | 
			
		||||
	gprs_rlcmac_tbf *tbf_from_list_by_tfi(struct llist_head *tbf_list, uint8_t tfi,
 | 
			
		||||
		enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
	gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi,
 | 
			
		||||
		enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	uint8_t m_num_tbfs[2];
 | 
			
		||||
	uint8_t m_num_reserved[2];
 | 
			
		||||
	uint8_t m_assigned_usf; /* bit set */
 | 
			
		||||
	uint32_t m_assigned_tfi[2]; /* bit set */
 | 
			
		||||
	struct gprs_rlcmac_tbf *m_tbfs[2][32];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_trx {
 | 
			
		||||
	void *fl1h;
 | 
			
		||||
	uint16_t arfcn;
 | 
			
		||||
@@ -133,11 +81,20 @@ struct gprs_rlcmac_trx {
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
void bts_update_tbf_ta(const char *p, uint32_t fn, uint8_t trx_no, uint8_t ts, int8_t ta, bool is_rach);
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This is the data from C. As soon as our minimal compiler is gcc 4.7
 | 
			
		||||
 * we can start to compile pcu_vty.c with c++ and remove the split.
 | 
			
		||||
 */
 | 
			
		||||
struct gprs_rlcmac_bts {
 | 
			
		||||
	bool active;
 | 
			
		||||
	uint8_t bsic;
 | 
			
		||||
	uint8_t fc_interval;
 | 
			
		||||
	uint16_t fc_bucket_time;
 | 
			
		||||
@@ -150,7 +107,9 @@ struct gprs_rlcmac_bts {
 | 
			
		||||
	uint8_t cs3;
 | 
			
		||||
	uint8_t cs4;
 | 
			
		||||
	uint8_t initial_cs_dl, initial_cs_ul;
 | 
			
		||||
	uint8_t initial_mcs_dl, initial_mcs_ul;
 | 
			
		||||
	uint8_t max_cs_dl, max_cs_ul;
 | 
			
		||||
	uint8_t max_mcs_dl, max_mcs_ul;
 | 
			
		||||
	uint8_t force_cs;	/* 0=use from BTS 1=use from VTY */
 | 
			
		||||
	uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
 | 
			
		||||
	uint32_t llc_discard_csec;
 | 
			
		||||
@@ -164,27 +123,30 @@ struct gprs_rlcmac_bts {
 | 
			
		||||
	uint8_t n3101;
 | 
			
		||||
	uint8_t n3103;
 | 
			
		||||
	uint8_t n3105;
 | 
			
		||||
	struct gsmtap_inst *gsmtap;
 | 
			
		||||
	uint32_t gsmtap_categ_mask;
 | 
			
		||||
	struct gprs_rlcmac_trx trx[8];
 | 
			
		||||
	int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
		struct GprsMs *ms,
 | 
			
		||||
		struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
 | 
			
		||||
		int use_tbf);
 | 
			
		||||
	uint32_t alloc_algorithm_curst; /* options to customize algorithm */
 | 
			
		||||
	int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
 | 
			
		||||
			       bool single, int8_t use_tbf);
 | 
			
		||||
 | 
			
		||||
	uint8_t force_two_phase;
 | 
			
		||||
	uint8_t alpha, gamma;
 | 
			
		||||
	uint8_t egprs_enabled;
 | 
			
		||||
	uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
 | 
			
		||||
	uint8_t si13[GSM_MACBLOCK_LEN];
 | 
			
		||||
	bool si13_is_set;
 | 
			
		||||
	/* 0 to support resegmentation in DL, 1 for no reseg */
 | 
			
		||||
	uint8_t dl_arq_type;
 | 
			
		||||
 | 
			
		||||
	uint32_t ms_idle_sec;
 | 
			
		||||
	uint8_t cs_adj_enabled;
 | 
			
		||||
	uint8_t cs_adj_upper_limit;
 | 
			
		||||
	uint8_t cs_adj_lower_limit;
 | 
			
		||||
	struct {int16_t low; int16_t high;} cs_lqual_ranges[4];
 | 
			
		||||
	struct {int16_t low; int16_t high; } cs_lqual_ranges[MAX_GPRS_CS];
 | 
			
		||||
	struct {int16_t low; int16_t high; } mcs_lqual_ranges[MAX_GPRS_CS];
 | 
			
		||||
	uint16_t cs_downgrade_threshold; /* downgrade if less packets left (DL) */
 | 
			
		||||
 | 
			
		||||
	/* TBF handling, make private or move into TBFController */
 | 
			
		||||
	/* list of uplink TBFs */
 | 
			
		||||
	struct llist_head ul_tbfs;
 | 
			
		||||
	/* list of downlink TBFs */
 | 
			
		||||
	struct llist_head dl_tbfs;
 | 
			
		||||
	uint16_t ws_base;
 | 
			
		||||
	uint16_t ws_pdch; /* increase WS by this value per PDCH */
 | 
			
		||||
 | 
			
		||||
	/* State for dynamic algorithm selection */
 | 
			
		||||
	int multislot_disabled;
 | 
			
		||||
@@ -194,6 +156,9 @@ struct gprs_rlcmac_bts {
 | 
			
		||||
	 * period.
 | 
			
		||||
	 */
 | 
			
		||||
	struct BTS *bts;
 | 
			
		||||
 | 
			
		||||
	/* Path to be used for the pcu-bts socket */
 | 
			
		||||
	char *pcu_sock_path;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
@@ -207,19 +172,32 @@ public:
 | 
			
		||||
	enum {
 | 
			
		||||
		CTR_TBF_DL_ALLOCATED,
 | 
			
		||||
		CTR_TBF_DL_FREED,
 | 
			
		||||
		CTR_TBF_DL_ABORTED,
 | 
			
		||||
		CTR_TBF_UL_ALLOCATED,
 | 
			
		||||
		CTR_TBF_UL_FREED,
 | 
			
		||||
		CTR_TBF_UL_ABORTED,
 | 
			
		||||
		CTR_TBF_REUSED,
 | 
			
		||||
		CTR_TBF_ALLOC_ALGO_A,
 | 
			
		||||
		CTR_TBF_ALLOC_ALGO_B,
 | 
			
		||||
		CTR_TBF_FAILED_EGPRS_ONLY,
 | 
			
		||||
		CTR_RLC_SENT,
 | 
			
		||||
		CTR_RLC_RESENT,
 | 
			
		||||
		CTR_RLC_RESTARTED,
 | 
			
		||||
		CTR_RLC_STALLED,
 | 
			
		||||
		CTR_RLC_NACKED,
 | 
			
		||||
		CTR_RLC_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,
 | 
			
		||||
@@ -227,7 +205,60 @@ public:
 | 
			
		||||
		CTR_LLC_FRAME_TIMEDOUT,
 | 
			
		||||
		CTR_LLC_FRAME_DROPPED,
 | 
			
		||||
		CTR_LLC_FRAME_SCHED,
 | 
			
		||||
		CTR_LLC_DL_BYTES,
 | 
			
		||||
		CTR_LLC_UL_BYTES,
 | 
			
		||||
		CTR_RACH_REQUESTS,
 | 
			
		||||
		CTR_11BIT_RACH_REQUESTS,
 | 
			
		||||
		CTR_SPB_UL_FIRST_SEGMENT,
 | 
			
		||||
		CTR_SPB_UL_SECOND_SEGMENT,
 | 
			
		||||
		CTR_SPB_DL_FIRST_SEGMENT,
 | 
			
		||||
		CTR_SPB_DL_SECOND_SEGMENT,
 | 
			
		||||
		CTR_IMMEDIATE_ASSIGN_UL_TBF,
 | 
			
		||||
		CTR_IMMEDIATE_ASSIGN_REJ,
 | 
			
		||||
		CTR_IMMEDIATE_ASSIGN_DL_TBF,
 | 
			
		||||
		CTR_CHANNEL_REQUEST_DESCRIPTION,
 | 
			
		||||
		CTR_PKT_UL_ASSIGNMENT,
 | 
			
		||||
		CTR_PKT_ACCESS_REJ,
 | 
			
		||||
		CTR_PKT_DL_ASSIGNMENT,
 | 
			
		||||
		CTR_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,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum {
 | 
			
		||||
@@ -244,6 +275,7 @@ public:
 | 
			
		||||
 | 
			
		||||
	/** TODO: change the number to unsigned */
 | 
			
		||||
	void set_current_frame_number(int frame_number);
 | 
			
		||||
	void set_current_block_frame_number(int frame_number, unsigned max_delay);
 | 
			
		||||
	int current_frame_number() const;
 | 
			
		||||
 | 
			
		||||
	/** add paging to paging queue(s) */
 | 
			
		||||
@@ -254,36 +286,56 @@ public:
 | 
			
		||||
	gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
 | 
			
		||||
	gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
 | 
			
		||||
 | 
			
		||||
	int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx);
 | 
			
		||||
	int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx) const;
 | 
			
		||||
 | 
			
		||||
	int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
 | 
			
		||||
	int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
 | 
			
		||||
 | 
			
		||||
	void trigger_dl_ass(gprs_rlcmac_dl_tbf *tbf, gprs_rlcmac_tbf *old_tbf);
 | 
			
		||||
	uint32_t rfn_to_fn(int32_t rfn);
 | 
			
		||||
	int rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, bool is_11bit,
 | 
			
		||||
		enum ph_burst_type burst_type);
 | 
			
		||||
 | 
			
		||||
	void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
 | 
			
		||||
 | 
			
		||||
	GprsMsStorage &ms_store();
 | 
			
		||||
	GprsMs *ms_by_tlli(uint32_t tlli, uint32_t old_tlli = 0);
 | 
			
		||||
	GprsMs *ms_alloc(uint8_t ms_class);
 | 
			
		||||
	GprsMs *ms_by_imsi(const char *imsi);
 | 
			
		||||
	GprsMs *ms_alloc(uint8_t ms_class, uint8_t egprs_ms_class = 0);
 | 
			
		||||
 | 
			
		||||
	void send_gsmtap(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);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Statistics
 | 
			
		||||
	 */
 | 
			
		||||
	void tbf_dl_created();
 | 
			
		||||
	void tbf_dl_freed();
 | 
			
		||||
	void tbf_dl_aborted();
 | 
			
		||||
	void tbf_ul_created();
 | 
			
		||||
	void tbf_ul_freed();
 | 
			
		||||
	void tbf_ul_aborted();
 | 
			
		||||
	void tbf_reused();
 | 
			
		||||
	void tbf_alloc_algo_a();
 | 
			
		||||
	void tbf_alloc_algo_b();
 | 
			
		||||
	void tbf_failed_egprs_only();
 | 
			
		||||
	void rlc_sent();
 | 
			
		||||
	void rlc_resent();
 | 
			
		||||
	void rlc_restarted();
 | 
			
		||||
	void rlc_stalled();
 | 
			
		||||
	void rlc_nacked();
 | 
			
		||||
	void rlc_final_block_resent();
 | 
			
		||||
	void rlc_ass_timedout();
 | 
			
		||||
	void rlc_ass_failed();
 | 
			
		||||
	void rlc_ack_timedout();
 | 
			
		||||
	void rlc_ack_failed();
 | 
			
		||||
	void rlc_rel_timedout();
 | 
			
		||||
	void rlc_late_block();
 | 
			
		||||
	void rlc_sent_dummy();
 | 
			
		||||
	void rlc_sent_control();
 | 
			
		||||
	void rlc_dl_bytes(int bytes);
 | 
			
		||||
	void rlc_dl_payload_bytes(int bytes);
 | 
			
		||||
	void rlc_ul_bytes(int bytes);
 | 
			
		||||
	void rlc_ul_payload_bytes(int bytes);
 | 
			
		||||
	void decode_error();
 | 
			
		||||
	void sba_allocated();
 | 
			
		||||
	void sba_freed();
 | 
			
		||||
@@ -291,23 +343,84 @@ public:
 | 
			
		||||
	void llc_timedout_frame();
 | 
			
		||||
	void llc_dropped_frame();
 | 
			
		||||
	void llc_frame_sched();
 | 
			
		||||
	void llc_dl_bytes(int bytes);
 | 
			
		||||
	void llc_ul_bytes(int bytes);
 | 
			
		||||
	void rach_frame();
 | 
			
		||||
	void rach_frame_11bit();
 | 
			
		||||
	void spb_uplink_first_segment();
 | 
			
		||||
	void spb_uplink_second_segment();
 | 
			
		||||
	void spb_downlink_first_segment();
 | 
			
		||||
	void spb_downlink_second_segment();
 | 
			
		||||
	void immediate_assignment_ul_tbf();
 | 
			
		||||
	void immediate_assignment_reject();
 | 
			
		||||
	void immediate_assignment_dl_tbf();
 | 
			
		||||
	void channel_request_description();
 | 
			
		||||
	void pkt_ul_assignment();
 | 
			
		||||
	void pkt_access_reject();
 | 
			
		||||
	void pkt_dl_assignemnt();
 | 
			
		||||
	void rlc_rcvd_control();
 | 
			
		||||
	void pua_poll_timedout();
 | 
			
		||||
	void pua_poll_failed();
 | 
			
		||||
	void pda_poll_timedout();
 | 
			
		||||
	void pda_poll_failed();
 | 
			
		||||
	void pkt_ul_ack_nack_poll_timedout();
 | 
			
		||||
	void pkt_ul_ack_nack_poll_failed();
 | 
			
		||||
	void pkt_dl_ack_nack_poll_timedout();
 | 
			
		||||
	void pkt_dl_ack_nack_poll_failed();
 | 
			
		||||
	void gprs_dl_cs1();
 | 
			
		||||
	void gprs_dl_cs2();
 | 
			
		||||
	void gprs_dl_cs3();
 | 
			
		||||
	void gprs_dl_cs4();
 | 
			
		||||
	void egprs_dl_mcs1();
 | 
			
		||||
	void egprs_dl_mcs2();
 | 
			
		||||
	void egprs_dl_mcs3();
 | 
			
		||||
	void egprs_dl_mcs4();
 | 
			
		||||
	void egprs_dl_mcs5();
 | 
			
		||||
	void egprs_dl_mcs6();
 | 
			
		||||
	void egprs_dl_mcs7();
 | 
			
		||||
	void egprs_dl_mcs8();
 | 
			
		||||
	void egprs_dl_mcs9();
 | 
			
		||||
	void gprs_ul_cs1();
 | 
			
		||||
	void gprs_ul_cs2();
 | 
			
		||||
	void gprs_ul_cs3();
 | 
			
		||||
	void gprs_ul_cs4();
 | 
			
		||||
	void egprs_ul_mcs1();
 | 
			
		||||
	void egprs_ul_mcs2();
 | 
			
		||||
	void egprs_ul_mcs3();
 | 
			
		||||
	void egprs_ul_mcs4();
 | 
			
		||||
	void egprs_ul_mcs5();
 | 
			
		||||
	void egprs_ul_mcs6();
 | 
			
		||||
	void egprs_ul_mcs7();
 | 
			
		||||
	void egprs_ul_mcs8();
 | 
			
		||||
	void egprs_ul_mcs9();
 | 
			
		||||
 | 
			
		||||
	void ms_present(int32_t n);
 | 
			
		||||
	int32_t ms_present_get();
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Below for C interface for the VTY
 | 
			
		||||
	 */
 | 
			
		||||
	struct rate_ctr_group *rate_counters() const;
 | 
			
		||||
	struct osmo_stat_item_group *stat_items() const;
 | 
			
		||||
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf>& ul_tbfs();
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf>& dl_tbfs();
 | 
			
		||||
private:
 | 
			
		||||
	int m_cur_fn;
 | 
			
		||||
	int m_cur_blk_fn;
 | 
			
		||||
	struct gprs_rlcmac_bts m_bts;
 | 
			
		||||
	PollController m_pollController;
 | 
			
		||||
	SBAController m_sba;
 | 
			
		||||
	struct rate_ctr_group *m_ratectrs;
 | 
			
		||||
	struct osmo_stat_item_group *m_statg;
 | 
			
		||||
 | 
			
		||||
	GprsMsStorage m_ms_store;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	/* list of uplink TBFs */
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> m_ul_tbfs;
 | 
			
		||||
	/* list of downlink TBFs */
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> m_dl_tbfs;
 | 
			
		||||
 | 
			
		||||
	/* disable copying to avoid slicing */
 | 
			
		||||
	BTS(const BTS&);
 | 
			
		||||
	BTS& operator=(const BTS&);
 | 
			
		||||
@@ -333,31 +446,19 @@ inline GprsMs *BTS::ms_by_tlli(uint32_t tlli, uint32_t old_tlli)
 | 
			
		||||
	return ms_store().get_ms(tlli, old_tlli);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline BTS *gprs_rlcmac_pdch::bts() const
 | 
			
		||||
inline GprsMs *BTS::ms_by_imsi(const char *imsi)
 | 
			
		||||
{
 | 
			
		||||
	return trx->bts;
 | 
			
		||||
	return ms_store().get_ms(0, 0, imsi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline unsigned gprs_rlcmac_pdch::num_tbfs(enum gprs_rlcmac_tbf_direction dir) const
 | 
			
		||||
inline LListHead<gprs_rlcmac_tbf>& BTS::ul_tbfs()
 | 
			
		||||
{
 | 
			
		||||
	return m_num_tbfs[dir];
 | 
			
		||||
	return m_ul_tbfs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline unsigned gprs_rlcmac_pdch::num_reserved(
 | 
			
		||||
	enum gprs_rlcmac_tbf_direction dir) const
 | 
			
		||||
inline LListHead<gprs_rlcmac_tbf>& BTS::dl_tbfs()
 | 
			
		||||
{
 | 
			
		||||
	return gprs_rlcmac_pdch::m_num_reserved[dir];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint8_t gprs_rlcmac_pdch::assigned_usf() const
 | 
			
		||||
{
 | 
			
		||||
	return m_assigned_usf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint32_t gprs_rlcmac_pdch::assigned_tfi(
 | 
			
		||||
	enum gprs_rlcmac_tbf_direction dir) const
 | 
			
		||||
{
 | 
			
		||||
	return m_assigned_tfi[dir];
 | 
			
		||||
	return m_dl_tbfs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline struct rate_ctr_group *BTS::rate_counters() const
 | 
			
		||||
@@ -365,6 +466,16 @@ inline struct rate_ctr_group *BTS::rate_counters() const
 | 
			
		||||
	return m_ratectrs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline struct osmo_stat_item_group *BTS::stat_items() const
 | 
			
		||||
{
 | 
			
		||||
	return m_statg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CREATE_COUNT_ADD_INLINE(func_name, ctr_name) \
 | 
			
		||||
	inline void BTS::func_name(int inc) {\
 | 
			
		||||
		rate_ctr_add(&m_ratectrs->ctr[ctr_name], inc); \
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#define CREATE_COUNT_INLINE(func_name, ctr_name) \
 | 
			
		||||
	inline void BTS::func_name() {\
 | 
			
		||||
		rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \
 | 
			
		||||
@@ -372,19 +483,32 @@ inline struct rate_ctr_group *BTS::rate_counters() const
 | 
			
		||||
 | 
			
		||||
CREATE_COUNT_INLINE(tbf_dl_created, CTR_TBF_DL_ALLOCATED)
 | 
			
		||||
CREATE_COUNT_INLINE(tbf_dl_freed, CTR_TBF_DL_FREED)
 | 
			
		||||
CREATE_COUNT_INLINE(tbf_dl_aborted, CTR_TBF_DL_ABORTED)
 | 
			
		||||
CREATE_COUNT_INLINE(tbf_ul_created, CTR_TBF_UL_ALLOCATED)
 | 
			
		||||
CREATE_COUNT_INLINE(tbf_ul_freed, CTR_TBF_UL_FREED)
 | 
			
		||||
CREATE_COUNT_INLINE(tbf_ul_aborted, CTR_TBF_UL_ABORTED)
 | 
			
		||||
CREATE_COUNT_INLINE(tbf_reused, CTR_TBF_REUSED)
 | 
			
		||||
CREATE_COUNT_INLINE(tbf_alloc_algo_a, CTR_TBF_ALLOC_ALGO_A)
 | 
			
		||||
CREATE_COUNT_INLINE(tbf_alloc_algo_b, CTR_TBF_ALLOC_ALGO_B)
 | 
			
		||||
CREATE_COUNT_INLINE(tbf_failed_egprs_only, CTR_TBF_FAILED_EGPRS_ONLY)
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_sent, CTR_RLC_SENT)
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_resent, CTR_RLC_RESENT)
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_restarted, CTR_RLC_RESTARTED)
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_stalled, CTR_RLC_STALLED)
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_nacked, CTR_RLC_NACKED)
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_final_block_resent, CTR_RLC_FINAL_BLOCK_RESENT);
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_ass_timedout, CTR_RLC_ASS_TIMEDOUT);
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_ass_failed, CTR_RLC_ASS_FAILED);
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_ack_timedout, CTR_RLC_ACK_TIMEDOUT);
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_ack_failed, CTR_RLC_ACK_FAILED);
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_rel_timedout, CTR_RLC_REL_TIMEDOUT);
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_late_block, CTR_RLC_LATE_BLOCK);
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_sent_dummy, CTR_RLC_SENT_DUMMY);
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_sent_control, CTR_RLC_SENT_CONTROL);
 | 
			
		||||
CREATE_COUNT_ADD_INLINE(rlc_dl_bytes, CTR_RLC_DL_BYTES);
 | 
			
		||||
CREATE_COUNT_ADD_INLINE(rlc_dl_payload_bytes, CTR_RLC_DL_PAYLOAD_BYTES);
 | 
			
		||||
CREATE_COUNT_ADD_INLINE(rlc_ul_bytes, CTR_RLC_UL_BYTES);
 | 
			
		||||
CREATE_COUNT_ADD_INLINE(rlc_ul_payload_bytes, CTR_RLC_UL_PAYLOAD_BYTES);
 | 
			
		||||
CREATE_COUNT_INLINE(decode_error, CTR_DECODE_ERRORS)
 | 
			
		||||
CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED)
 | 
			
		||||
CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED)
 | 
			
		||||
@@ -392,20 +516,71 @@ CREATE_COUNT_INLINE(sba_timedout, CTR_SBA_TIMEDOUT)
 | 
			
		||||
CREATE_COUNT_INLINE(llc_timedout_frame, CTR_LLC_FRAME_TIMEDOUT);
 | 
			
		||||
CREATE_COUNT_INLINE(llc_dropped_frame, CTR_LLC_FRAME_DROPPED);
 | 
			
		||||
CREATE_COUNT_INLINE(llc_frame_sched, CTR_LLC_FRAME_SCHED);
 | 
			
		||||
CREATE_COUNT_ADD_INLINE(llc_dl_bytes, CTR_LLC_DL_BYTES);
 | 
			
		||||
CREATE_COUNT_ADD_INLINE(llc_ul_bytes, CTR_LLC_UL_BYTES);
 | 
			
		||||
CREATE_COUNT_INLINE(rach_frame, CTR_RACH_REQUESTS);
 | 
			
		||||
CREATE_COUNT_INLINE(rach_frame_11bit, CTR_11BIT_RACH_REQUESTS);
 | 
			
		||||
CREATE_COUNT_INLINE(spb_uplink_first_segment, CTR_SPB_UL_FIRST_SEGMENT);
 | 
			
		||||
CREATE_COUNT_INLINE(spb_uplink_second_segment, CTR_SPB_UL_SECOND_SEGMENT);
 | 
			
		||||
CREATE_COUNT_INLINE(spb_downlink_first_segment, CTR_SPB_DL_FIRST_SEGMENT);
 | 
			
		||||
CREATE_COUNT_INLINE(spb_downlink_second_segment, CTR_SPB_DL_SECOND_SEGMENT);
 | 
			
		||||
CREATE_COUNT_INLINE(immediate_assignment_ul_tbf, CTR_IMMEDIATE_ASSIGN_UL_TBF);
 | 
			
		||||
CREATE_COUNT_INLINE(immediate_assignment_reject, CTR_IMMEDIATE_ASSIGN_REJ);
 | 
			
		||||
CREATE_COUNT_INLINE(immediate_assignment_dl_tbf, CTR_IMMEDIATE_ASSIGN_DL_TBF);
 | 
			
		||||
CREATE_COUNT_INLINE(channel_request_description, CTR_CHANNEL_REQUEST_DESCRIPTION);
 | 
			
		||||
CREATE_COUNT_INLINE(pkt_ul_assignment, CTR_PKT_UL_ASSIGNMENT);
 | 
			
		||||
CREATE_COUNT_INLINE(pkt_access_reject, CTR_PKT_ACCESS_REJ);
 | 
			
		||||
CREATE_COUNT_INLINE(pkt_dl_assignemnt, CTR_PKT_DL_ASSIGNMENT);
 | 
			
		||||
CREATE_COUNT_INLINE(rlc_rcvd_control, CTR_RLC_RECV_CONTROL);
 | 
			
		||||
CREATE_COUNT_INLINE(pua_poll_timedout, CTR_PUA_POLL_TIMEDOUT);
 | 
			
		||||
CREATE_COUNT_INLINE(pua_poll_failed, CTR_PUA_POLL_FAILED);
 | 
			
		||||
CREATE_COUNT_INLINE(pda_poll_timedout, CTR_PDA_POLL_TIMEDOUT);
 | 
			
		||||
CREATE_COUNT_INLINE(pda_poll_failed, CTR_PDA_POLL_FAILED);
 | 
			
		||||
CREATE_COUNT_INLINE(pkt_ul_ack_nack_poll_timedout, CTR_PUAN_POLL_TIMEDOUT);
 | 
			
		||||
CREATE_COUNT_INLINE(pkt_ul_ack_nack_poll_failed, CTR_PUAN_POLL_FAILED);
 | 
			
		||||
CREATE_COUNT_INLINE(pkt_dl_ack_nack_poll_timedout, CTR_PDAN_POLL_TIMEDOUT);
 | 
			
		||||
CREATE_COUNT_INLINE(pkt_dl_ack_nack_poll_failed, CTR_PDAN_POLL_FAILED);
 | 
			
		||||
CREATE_COUNT_INLINE(gprs_dl_cs1, CTR_GPRS_DL_CS1);
 | 
			
		||||
CREATE_COUNT_INLINE(gprs_dl_cs2, CTR_GPRS_DL_CS2);
 | 
			
		||||
CREATE_COUNT_INLINE(gprs_dl_cs3, CTR_GPRS_DL_CS3);
 | 
			
		||||
CREATE_COUNT_INLINE(gprs_dl_cs4, CTR_GPRS_DL_CS4);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_dl_mcs1, CTR_EGPRS_DL_MCS1);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_dl_mcs2, CTR_EGPRS_DL_MCS2);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_dl_mcs3, CTR_EGPRS_DL_MCS3);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_dl_mcs4, CTR_EGPRS_DL_MCS4);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_dl_mcs5, CTR_EGPRS_DL_MCS5);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_dl_mcs6, CTR_EGPRS_DL_MCS6);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_dl_mcs7, CTR_EGPRS_DL_MCS7);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_dl_mcs8, CTR_EGPRS_DL_MCS8);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_dl_mcs9, CTR_EGPRS_DL_MCS9);
 | 
			
		||||
CREATE_COUNT_INLINE(gprs_ul_cs1, CTR_GPRS_UL_CS1);
 | 
			
		||||
CREATE_COUNT_INLINE(gprs_ul_cs2, CTR_GPRS_UL_CS2);
 | 
			
		||||
CREATE_COUNT_INLINE(gprs_ul_cs3, CTR_GPRS_UL_CS3);
 | 
			
		||||
CREATE_COUNT_INLINE(gprs_ul_cs4, CTR_GPRS_UL_CS4);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_ul_mcs1, CTR_EGPRS_UL_MCS1);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_ul_mcs2, CTR_EGPRS_UL_MCS2);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_ul_mcs3, CTR_EGPRS_UL_MCS3);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_ul_mcs4, CTR_EGPRS_UL_MCS4);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_ul_mcs5, CTR_EGPRS_UL_MCS5);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_ul_mcs6, CTR_EGPRS_UL_MCS6);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_ul_mcs7, CTR_EGPRS_UL_MCS7);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_ul_mcs8, CTR_EGPRS_UL_MCS8);
 | 
			
		||||
CREATE_COUNT_INLINE(egprs_ul_mcs9, CTR_EGPRS_UL_MCS9);
 | 
			
		||||
 | 
			
		||||
#undef CREATE_COUNT_INLINE
 | 
			
		||||
 | 
			
		||||
#define CREATE_STAT_INLINE(func_name, func_name_get, stat_name) \
 | 
			
		||||
	inline void BTS::func_name(int32_t val) {\
 | 
			
		||||
		osmo_stat_item_set(m_statg->items[stat_name], val); \
 | 
			
		||||
	} \
 | 
			
		||||
	inline int32_t BTS::func_name_get() {\
 | 
			
		||||
		return osmo_stat_item_get_last(m_statg->items[stat_name]); \
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
inline gprs_rlcmac_bts *gprs_rlcmac_pdch::bts_data() const
 | 
			
		||||
{
 | 
			
		||||
	return trx->bts->bts_data();
 | 
			
		||||
}
 | 
			
		||||
CREATE_STAT_INLINE(ms_present, ms_present_get, STAT_MS_PRESENT);
 | 
			
		||||
 | 
			
		||||
#undef CREATE_STAT_INLINE
 | 
			
		||||
 | 
			
		||||
inline uint8_t gprs_rlcmac_pdch::trx_no() const
 | 
			
		||||
{
 | 
			
		||||
	return trx->trx_no;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
@@ -413,11 +588,8 @@ extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
	struct gprs_rlcmac_bts *bts_main_data();
 | 
			
		||||
	struct rate_ctr_group *bts_main_data_stats();
 | 
			
		||||
	struct osmo_stat_item_group *bts_main_data_stat_items();
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlcmac_pdch::is_enabled() const
 | 
			
		||||
{
 | 
			
		||||
	return m_is_enabled;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										228
									
								
								src/csn1.cpp
									
									
									
									
									
								
							
							
						
						
									
										228
									
								
								src/csn1.cpp
									
									
									
									
									
								
							@@ -64,15 +64,15 @@ get_masked_bits8( bitvec *vector, unsigned& readIndex, gint bit_offset,  const g
 | 
			
		||||
  readIndex -= relative_bit_offset;
 | 
			
		||||
  if (bit_shift >= 0)
 | 
			
		||||
  {
 | 
			
		||||
    result = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) >> bit_shift;
 | 
			
		||||
    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];
 | 
			
		||||
    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);
 | 
			
		||||
    result =  (0x2B ^ ((guint8)bitvec_read_field(vector, &readIndex, 8))) >> (8 + bit_shift);
 | 
			
		||||
    readIndex = readIndex - (8 - (-bit_shift));
 | 
			
		||||
    result |= hight_part;
 | 
			
		||||
  }
 | 
			
		||||
@@ -133,7 +133,7 @@ ProcessError( unsigned readIndex, const char* sz, gint16 err, const CSN_DESCR* p
 | 
			
		||||
static gboolean
 | 
			
		||||
existNextElement(bitvec *vector, unsigned& readIndex, guint8 Tag)
 | 
			
		||||
{
 | 
			
		||||
  guint8 res = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
  guint8 res = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
  if (Tag == STANDARD_TAG)
 | 
			
		||||
  {
 | 
			
		||||
    return (res > 0);
 | 
			
		||||
@@ -167,7 +167,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
        if (remaining_bits_len > 0)
 | 
			
		||||
        {
 | 
			
		||||
          pui8  = pui8DATA(data, pDescr->offset);
 | 
			
		||||
          *pui8 = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
	  *pui8 = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
          /* end add the bit value to protocol tree */
 | 
			
		||||
        }
 | 
			
		||||
@@ -202,21 +202,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
        {
 | 
			
		||||
          if (no_of_bits <= 8)
 | 
			
		||||
          {
 | 
			
		||||
            guint8 ui8 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
	    guint8 ui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
            pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            *pui8     = ui8;
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
          }
 | 
			
		||||
          else if (no_of_bits <= 16)
 | 
			
		||||
          {
 | 
			
		||||
            guint16 ui16 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
	    guint16 ui16 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
            pui16       = pui16DATA(data, pDescr->offset);
 | 
			
		||||
            *pui16      = ui16;
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
 | 
			
		||||
          }
 | 
			
		||||
          else if (no_of_bits <= 32)
 | 
			
		||||
          {
 | 
			
		||||
            guint32 ui32 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
	    guint32 ui32 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
            pui32       = pui32DATA(data, pDescr->offset);
 | 
			
		||||
            *pui32      = ui32;
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = 0x%08x | ", pDescr->sz , *pui32);
 | 
			
		||||
@@ -264,21 +264,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
        {
 | 
			
		||||
          if (no_of_bits <= 8)
 | 
			
		||||
          {
 | 
			
		||||
            guint8 ui8 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
	    guint8 ui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
            pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            *pui8     = ui8 + (guint8)pDescr->descr.value;
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
          }
 | 
			
		||||
          else if (no_of_bits <= 16)
 | 
			
		||||
          {
 | 
			
		||||
            guint16 ui16 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
	    guint16 ui16 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
            pui16       = pui16DATA(data, pDescr->offset);
 | 
			
		||||
            *pui16      = ui16 + (guint16)pDescr->descr.value;
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
 | 
			
		||||
          }
 | 
			
		||||
          else if (no_of_bits <= 32)
 | 
			
		||||
          {
 | 
			
		||||
            guint32 ui32 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
	    guint32 ui32 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
            pui32       = pui32DATA(data, pDescr->offset);
 | 
			
		||||
            *pui32      = ui32 + (guint16)pDescr->descr.value;
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
 | 
			
		||||
@@ -308,7 +308,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
          remaining_bits_len -= no_of_bits;
 | 
			
		||||
          if (no_of_bits <= 8)
 | 
			
		||||
          {
 | 
			
		||||
            guint8 ui8 = get_masked_bits8(vector, readIndex, bit_offset, no_of_bits);
 | 
			
		||||
	    guint8 ui8 = get_masked_bits8(vector, readIndex, bit_offset, no_of_bits);
 | 
			
		||||
            pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            *pui8     = ui8;
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
@@ -347,7 +347,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
            pui8 = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            do
 | 
			
		||||
            {
 | 
			
		||||
              *pui8 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
	      *pui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              pui8++;
 | 
			
		||||
              bit_offset += no_of_bits;
 | 
			
		||||
@@ -401,7 +401,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s | ", pDescr->sz);
 | 
			
		||||
          csnStreamInit(&arT, bit_offset, remaining_bits_len);
 | 
			
		||||
          Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
 | 
			
		||||
	  Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
 | 
			
		||||
          if (Status >= 0)
 | 
			
		||||
          {
 | 
			
		||||
            pui8    += nSize;
 | 
			
		||||
@@ -430,7 +430,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
          {
 | 
			
		||||
            for(unsigned ib = 0; ib < 4; ib++)
 | 
			
		||||
            {
 | 
			
		||||
              guint8 ui8 = bitvec_read_field(vector, readIndex, 8);
 | 
			
		||||
	      guint8 ui8 = bitvec_read_field(vector, &readIndex, 8);
 | 
			
		||||
              pui8      = pui8DATA(data, pDescr->offset+ib);
 | 
			
		||||
              *pui8      = ui8;
 | 
			
		||||
               LOGPC(DCSN1, LOGL_NOTICE, "%s[%u] = %u | ", pDescr->sz , ib, (unsigned)*pui8);
 | 
			
		||||
@@ -440,7 +440,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
          {
 | 
			
		||||
            for(unsigned ib = 0; ib < 8; ib++)
 | 
			
		||||
            {
 | 
			
		||||
              guint8 ui8 = bitvec_read_field(vector, readIndex, 8);
 | 
			
		||||
	      guint8 ui8 = bitvec_read_field(vector, &readIndex, 8);
 | 
			
		||||
              pui8      = pui8DATA(data, pDescr->offset+ib);
 | 
			
		||||
              *pui8      = ui8;
 | 
			
		||||
               LOGPC(DCSN1, LOGL_NOTICE, "%s[%u] = %u | ", pDescr->sz , ib, (unsigned)*pui8);
 | 
			
		||||
@@ -467,7 +467,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
        csnStream_t arT = *ar;
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, " : %s | ", pDescr->sz);
 | 
			
		||||
        csnStreamInit(&arT, bit_offset, remaining_bits_len);
 | 
			
		||||
        Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
 | 
			
		||||
	Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, ": End %s | ", pDescr->sz);
 | 
			
		||||
        if (Status >= 0)
 | 
			
		||||
        {
 | 
			
		||||
@@ -493,7 +493,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
        while (count > 0)
 | 
			
		||||
        {
 | 
			
		||||
          guint8 no_of_bits = pChoice->bits;
 | 
			
		||||
          guint8 value = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
	  guint8 value = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
          if (value == pChoice->value)
 | 
			
		||||
          {
 | 
			
		||||
            CSN_DESCR   descr[2];
 | 
			
		||||
@@ -510,7 +510,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
            remaining_bits_len -= no_of_bits;
 | 
			
		||||
 | 
			
		||||
            csnStreamInit(&arT, bit_offset, remaining_bits_len);
 | 
			
		||||
            Status = csnStreamDecoder(&arT, descr, vector, readIndex, data);
 | 
			
		||||
	    Status = csnStreamDecoder(&arT, descr, vector, readIndex, data);
 | 
			
		||||
 | 
			
		||||
            if (Status >= 0)
 | 
			
		||||
            {
 | 
			
		||||
@@ -541,7 +541,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
        guint8 length_len              = pDescr->i;
 | 
			
		||||
        gint16               Status    = -1;
 | 
			
		||||
 | 
			
		||||
        guint8 length = bitvec_read_field(vector, readIndex, length_len);
 | 
			
		||||
	guint8 length = bitvec_read_field(vector, &readIndex, length_len);
 | 
			
		||||
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "%s length = %d | ", pDescr->sz , (int)length);
 | 
			
		||||
        bit_offset += length_len;
 | 
			
		||||
@@ -549,7 +549,8 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
        csnStreamInit(&arT, bit_offset, length);
 | 
			
		||||
        arT.direction = 1;
 | 
			
		||||
        Status = serialize(&arT, vector, readIndex, pvDATA(data, pDescr->offset));
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "ptr = %p | offset = %d | ", (void *)data, (int)pDescr->offset);
 | 
			
		||||
	Status = serialize(&arT, vector, readIndex, pvDATA(data, pDescr->offset));
 | 
			
		||||
 | 
			
		||||
        if (Status >= 0)
 | 
			
		||||
        {
 | 
			
		||||
@@ -594,7 +595,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
          }
 | 
			
		||||
          else
 | 
			
		||||
          {
 | 
			
		||||
            index |= bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
	    index |= bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
          }
 | 
			
		||||
          remaining_bits_len--;
 | 
			
		||||
          bit_offset++;
 | 
			
		||||
@@ -617,7 +618,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
          {
 | 
			
		||||
            pui8  = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            *pui8 = 0x00;
 | 
			
		||||
            if (bitvec_read_field(vector, readIndex, 1) > 0)
 | 
			
		||||
	    if (bitvec_read_field(vector, &readIndex, 1) > 0)
 | 
			
		||||
            {
 | 
			
		||||
              *pui8 = 0x01;
 | 
			
		||||
            }
 | 
			
		||||
@@ -643,21 +644,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
              if (no_of_bits <= 8)
 | 
			
		||||
              {
 | 
			
		||||
                guint8 ui8 = bitvec_read_field(vector, readIndex,  no_of_bits);
 | 
			
		||||
		guint8 ui8 = bitvec_read_field(vector, &readIndex,  no_of_bits);
 | 
			
		||||
                pui8       = pui8DATA(data, pDescr->offset);
 | 
			
		||||
                *pui8      = ui8;
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              }
 | 
			
		||||
              else if (no_of_bits <= 16)
 | 
			
		||||
              {
 | 
			
		||||
                guint16 ui16 = bitvec_read_field(vector, readIndex,  no_of_bits);
 | 
			
		||||
		guint16 ui16 = bitvec_read_field(vector, &readIndex,  no_of_bits);
 | 
			
		||||
                pui16        = pui16DATA(data, pDescr->offset);
 | 
			
		||||
                *pui16       = ui16;
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
 | 
			
		||||
              }
 | 
			
		||||
              else if (no_of_bits <= 32)
 | 
			
		||||
              {
 | 
			
		||||
                guint32 ui32 = bitvec_read_field(vector, readIndex,  no_of_bits);
 | 
			
		||||
		guint32 ui32 = bitvec_read_field(vector, &readIndex,  no_of_bits);
 | 
			
		||||
                pui32       = pui32DATA(data, pDescr->offset);
 | 
			
		||||
                *pui32      = ui32;
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
 | 
			
		||||
@@ -685,21 +686,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
            {
 | 
			
		||||
              if (no_of_bits <= 8)
 | 
			
		||||
              {
 | 
			
		||||
                guint8 ui8 = bitvec_read_field(vector, readIndex,  no_of_bits);
 | 
			
		||||
		guint8 ui8 = bitvec_read_field(vector, &readIndex,  no_of_bits);
 | 
			
		||||
                pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
                *pui8     = ui8 + (guint8)pDescr->descr.value;
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              }
 | 
			
		||||
              else if (no_of_bits <= 16)
 | 
			
		||||
              {
 | 
			
		||||
                guint16 ui16 = bitvec_read_field(vector, readIndex,  no_of_bits);
 | 
			
		||||
		guint16 ui16 = bitvec_read_field(vector, &readIndex,  no_of_bits);
 | 
			
		||||
                pui16       = pui16DATA(data, pDescr->offset);
 | 
			
		||||
                *pui16      = ui16 + (guint16)pDescr->descr.value;
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
 | 
			
		||||
              }
 | 
			
		||||
              else if (no_of_bits <= 32)
 | 
			
		||||
              {
 | 
			
		||||
                guint32 ui32 = bitvec_read_field(vector, readIndex,  no_of_bits);
 | 
			
		||||
		guint32 ui32 = bitvec_read_field(vector, &readIndex,  no_of_bits);
 | 
			
		||||
                pui32       = pui32DATA(data, pDescr->offset);
 | 
			
		||||
                *pui32      = ui32 + (guint16)pDescr->descr.value;
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
 | 
			
		||||
@@ -727,7 +728,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
            {
 | 
			
		||||
              if (no_of_bits <= 8)
 | 
			
		||||
              {
 | 
			
		||||
                guint8 ui8 = get_masked_bits8(vector, readIndex, bit_offset, no_of_bits);
 | 
			
		||||
		guint8 ui8 = get_masked_bits8(vector, readIndex, bit_offset, no_of_bits);
 | 
			
		||||
                pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
                *pui8     = ui8;
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
@@ -766,7 +767,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
                while (nCount > 0)
 | 
			
		||||
                {
 | 
			
		||||
                  *pui8 = bitvec_read_field(vector, readIndex,  no_of_bits);
 | 
			
		||||
		  *pui8 = bitvec_read_field(vector, &readIndex,  no_of_bits);
 | 
			
		||||
                  LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
                  pui8++;
 | 
			
		||||
                  bit_offset += no_of_bits;
 | 
			
		||||
@@ -779,7 +780,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
                while (nCount > 0)
 | 
			
		||||
                {
 | 
			
		||||
                 *pui16 = bitvec_read_field(vector, readIndex,  no_of_bits);
 | 
			
		||||
		 *pui16 = bitvec_read_field(vector, &readIndex,  no_of_bits);
 | 
			
		||||
                  LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , *pui16);
 | 
			
		||||
                  pui16++;
 | 
			
		||||
                  bit_offset += no_of_bits;
 | 
			
		||||
@@ -829,7 +830,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
            {
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s | ", pDescr->sz);
 | 
			
		||||
              csnStreamInit(&arT, bit_offset, remaining_bits_len);
 | 
			
		||||
              Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
 | 
			
		||||
	      Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
 | 
			
		||||
              if (Status >= 0)
 | 
			
		||||
              {
 | 
			
		||||
                pui8    += nSize;
 | 
			
		||||
@@ -855,13 +856,13 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
              if (no_of_bits <= 32)
 | 
			
		||||
              {
 | 
			
		||||
                guint32 ui32 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
		guint32 ui32 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
                pui32       = pui32DATA(data, pDescr->offset);
 | 
			
		||||
                *pui32      = ui32;
 | 
			
		||||
              }
 | 
			
		||||
              else if (no_of_bits <= 64)
 | 
			
		||||
              {
 | 
			
		||||
                guint64 ui64 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
		guint64 ui64 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
                pui64       = pui64DATA(data, pDescr->offset);
 | 
			
		||||
                *pui64      = ui64;
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %lu | ", pDescr->sz , *pui64);
 | 
			
		||||
@@ -887,7 +888,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
            csnStream_t arT = *ar;
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, " : %s | ", pDescr->sz);
 | 
			
		||||
            csnStreamInit(&arT, bit_offset, remaining_bits_len);
 | 
			
		||||
            Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
 | 
			
		||||
	    Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, " : End %s | ", pDescr->sz);
 | 
			
		||||
            if (Status >= 0)
 | 
			
		||||
            {
 | 
			
		||||
@@ -922,11 +923,11 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
        if (CSN_EXIST_LH == pDescr->type)
 | 
			
		||||
        {
 | 
			
		||||
          fExist = get_masked_bits8(vector, readIndex, bit_offset, 1);
 | 
			
		||||
	  fExist = get_masked_bits8(vector, readIndex, bit_offset, 1);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
          fExist = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
	  fExist = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        *pui8 = fExist;
 | 
			
		||||
@@ -965,7 +966,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
        /* the "regular" M_NEXT_EXIST description element */
 | 
			
		||||
 | 
			
		||||
        fExist = 0x00;
 | 
			
		||||
        if (bitvec_read_field(vector, readIndex, 1))
 | 
			
		||||
	if (bitvec_read_field(vector, &readIndex, 1))
 | 
			
		||||
        {
 | 
			
		||||
          fExist = 0x01;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1058,7 +1059,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
            if (nB1 > 0)
 | 
			
		||||
            { /* take care of the first byte - it will be right aligned */
 | 
			
		||||
              *pui8 = bitvec_read_field(vector, readIndex, nB1);
 | 
			
		||||
	      *pui8 = bitvec_read_field(vector, &readIndex, nB1);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              pui8++;
 | 
			
		||||
              no_of_bits  -= nB1;
 | 
			
		||||
@@ -1068,7 +1069,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
            /* remaining no_of_bits is a multiple of 8 or 0 */
 | 
			
		||||
            while (no_of_bits > 0)
 | 
			
		||||
            {
 | 
			
		||||
              *pui8 = bitvec_read_field(vector, readIndex, 8);
 | 
			
		||||
	      *pui8 = bitvec_read_field(vector, &readIndex, 8);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              pui8++;
 | 
			
		||||
              no_of_bits -= 8;
 | 
			
		||||
@@ -1109,22 +1110,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
          { /* extract bits */
 | 
			
		||||
            guint8* pui8 = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            gint16 nB1  = no_of_bits & 0x07;/* no_of_bits Mod 8 */
 | 
			
		||||
 | 
			
		||||
            while (no_of_bits > 0)
 | 
			
		||||
            while (no_of_bits >= 8)
 | 
			
		||||
            {
 | 
			
		||||
              *pui8 = bitvec_read_field(vector, readIndex, 8);
 | 
			
		||||
	      *pui8 = bitvec_read_field(vector, &readIndex, 8);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              pui8++;
 | 
			
		||||
              no_of_bits -= 8;
 | 
			
		||||
            }
 | 
			
		||||
            if (nB1 > 0)
 | 
			
		||||
            if (no_of_bits > 0)
 | 
			
		||||
            { 
 | 
			
		||||
              *pui8 = bitvec_read_field(vector, readIndex, nB1);
 | 
			
		||||
	      *pui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              pui8++;
 | 
			
		||||
              no_of_bits  -= nB1;
 | 
			
		||||
              bit_offset += nB1; /* (nB1 is no_of_bits Mod 8) */
 | 
			
		||||
              bit_offset += no_of_bits;
 | 
			
		||||
              no_of_bits = 0;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
@@ -1144,13 +1144,13 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
            guint8 bits_to_handle = remaining_bits_len%8;
 | 
			
		||||
            if (bits_to_handle > 0)
 | 
			
		||||
            {
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, readIndex, bits_to_handle));
 | 
			
		||||
	      LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, &readIndex, bits_to_handle));
 | 
			
		||||
              remaining_bits_len -= bits_to_handle;
 | 
			
		||||
              bit_offset += bits_to_handle;
 | 
			
		||||
            }
 | 
			
		||||
            else if (bits_to_handle == 0)
 | 
			
		||||
            {
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, readIndex, 8));
 | 
			
		||||
	      LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, &readIndex, 8));
 | 
			
		||||
              remaining_bits_len -= 8;
 | 
			
		||||
              bit_offset += 8;
 | 
			
		||||
            }
 | 
			
		||||
@@ -1191,7 +1191,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
          while (count > 0)
 | 
			
		||||
          {
 | 
			
		||||
            readIndex -= 8;
 | 
			
		||||
            *pui8 = bitvec_read_field(vector, readIndex, 8);
 | 
			
		||||
	    *pui8 = bitvec_read_field(vector, &readIndex, 8);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
            pui8++;
 | 
			
		||||
            bit_offset += 8;
 | 
			
		||||
@@ -1217,14 +1217,14 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
        pui8  = pui8DATA(data, pDescr->offset);
 | 
			
		||||
 | 
			
		||||
        while (existNextElement(vector, readIndex, Tag))
 | 
			
		||||
	while (existNextElement(vector, readIndex, Tag))
 | 
			
		||||
        { /* tag control shows existence of next list elements */
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s = Exist | ", pDescr->sz);
 | 
			
		||||
          bit_offset++;
 | 
			
		||||
          remaining_bits_len--;
 | 
			
		||||
 | 
			
		||||
          /* extract and store no_of_bits long element from bitstream */
 | 
			
		||||
          *pui8 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
	  *pui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
          pui8++;
 | 
			
		||||
          remaining_bits_len -= no_of_bits;
 | 
			
		||||
@@ -1238,7 +1238,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
          bit_offset += no_of_bits;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)bitvec_read_field(vector, readIndex, 1));
 | 
			
		||||
	LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)bitvec_read_field(vector, &readIndex, 1));
 | 
			
		||||
        /* existNextElement() returned FALSE, 1 bit consumed */
 | 
			
		||||
        bit_offset++;
 | 
			
		||||
 | 
			
		||||
@@ -1258,7 +1258,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
        guint8  ElementCount = 0;
 | 
			
		||||
        pui8  = pui8DATA(data, pDescr->offset);
 | 
			
		||||
 | 
			
		||||
        while (existNextElement(vector, readIndex, Tag))
 | 
			
		||||
	while (existNextElement(vector, readIndex, Tag))
 | 
			
		||||
        { /* tag control shows existence of next list elements */
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s = Exist | ", pDescr->sz);
 | 
			
		||||
          /* existNextElement() returned TRUE, 1 bit consumed */
 | 
			
		||||
@@ -1270,7 +1270,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
            csnStream_t arT = *ar;
 | 
			
		||||
            gint16      Status;
 | 
			
		||||
            csnStreamInit(&arT, bit_offset, remaining_bits_len);
 | 
			
		||||
            Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
 | 
			
		||||
	    Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
 | 
			
		||||
 | 
			
		||||
            if (Status >= 0)
 | 
			
		||||
            { /* successful completion */
 | 
			
		||||
@@ -1290,7 +1290,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)bitvec_read_field(vector, readIndex, 1));
 | 
			
		||||
	LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)bitvec_read_field(vector, &readIndex, 1));
 | 
			
		||||
 | 
			
		||||
        /* existNextElement() returned FALSE, 1 bit consumed */
 | 
			
		||||
        bit_offset++;
 | 
			
		||||
@@ -1331,7 +1331,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s { | ", pDescr->sz);
 | 
			
		||||
          
 | 
			
		||||
          csnStreamInit(&arT, bit_offset, remaining_bits_len);
 | 
			
		||||
          Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
 | 
			
		||||
	  Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
 | 
			
		||||
 | 
			
		||||
          if (Status >= 0)
 | 
			
		||||
          { /* successful completion */
 | 
			
		||||
@@ -1351,7 +1351,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
          /* control of next element's tag */
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s } | ", pDescr->sz);
 | 
			
		||||
          EndOfList         = !(existNextElement(vector, readIndex, Tag));
 | 
			
		||||
	  EndOfList         = !(existNextElement(vector, readIndex, Tag));
 | 
			
		||||
 | 
			
		||||
          bit_offset++;
 | 
			
		||||
          remaining_bits_len--; /* 1 bit consumed (tag) */
 | 
			
		||||
@@ -1372,7 +1372,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
 | 
			
		||||
 | 
			
		||||
        if (no_of_bits <= 32)
 | 
			
		||||
        {
 | 
			
		||||
          ui32 = bitvec_read_field(vector, readIndex, no_of_bits);
 | 
			
		||||
	  ui32 = bitvec_read_field(vector, &readIndex, no_of_bits);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
@@ -1449,7 +1449,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
        if (remaining_bits_len > 0)
 | 
			
		||||
        {
 | 
			
		||||
          pui8  = pui8DATA(data, pDescr->offset);
 | 
			
		||||
          bitvec_write_field(vector, writeIndex, *pui8, 1);
 | 
			
		||||
	  bitvec_write_field(vector, &writeIndex, *pui8, 1);
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
          /* end add the bit value to protocol tree */
 | 
			
		||||
        }
 | 
			
		||||
@@ -1483,19 +1483,19 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
          if (no_of_bits <= 8)
 | 
			
		||||
          {
 | 
			
		||||
            pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
          }
 | 
			
		||||
          else if (no_of_bits <= 16)
 | 
			
		||||
          {
 | 
			
		||||
            pui16       = pui16DATA(data, pDescr->offset);
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, *pui16, no_of_bits);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, *pui16, no_of_bits);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
 | 
			
		||||
          }
 | 
			
		||||
          else if (no_of_bits <= 32)
 | 
			
		||||
          {
 | 
			
		||||
            pui32       = pui32DATA(data, pDescr->offset);
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, *pui32, no_of_bits);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, *pui32, no_of_bits);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
 | 
			
		||||
          }
 | 
			
		||||
          else
 | 
			
		||||
@@ -1528,19 +1528,19 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
          if (no_of_bits <= 8)
 | 
			
		||||
          {
 | 
			
		||||
            pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, *pui8 - (guint8)pDescr->descr.value, no_of_bits);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, *pui8 - (guint8)pDescr->descr.value, no_of_bits);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(*pui8 - (guint8)pDescr->descr.value));
 | 
			
		||||
          }
 | 
			
		||||
          else if (no_of_bits <= 16)
 | 
			
		||||
          {
 | 
			
		||||
            pui16       = pui16DATA(data, pDescr->offset);
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, *pui16 - (guint16)pDescr->descr.value, no_of_bits);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, *pui16 - (guint16)pDescr->descr.value, no_of_bits);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , (unsigned short)(*pui16 - (guint16)pDescr->descr.value));
 | 
			
		||||
          }
 | 
			
		||||
          else if (no_of_bits <= 32)
 | 
			
		||||
          {
 | 
			
		||||
            pui32       = pui32DATA(data, pDescr->offset);
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, *pui32 - (guint16)pDescr->descr.value, no_of_bits);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, *pui32 - (guint16)pDescr->descr.value, no_of_bits);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , (unsigned int)(*pui32 - (guint16)pDescr->descr.value));
 | 
			
		||||
          }
 | 
			
		||||
          else
 | 
			
		||||
@@ -1569,12 +1569,12 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
          if (no_of_bits <= 8)
 | 
			
		||||
          {
 | 
			
		||||
            pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
 | 
			
		||||
            // TODO : Change get_masked_bits8()
 | 
			
		||||
            writeIndex -= no_of_bits;
 | 
			
		||||
            guint8 ui8 = get_masked_bits8(vector, writeIndex, bit_offset, no_of_bits);
 | 
			
		||||
            writeIndex -= no_of_bits;
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, ui8, no_of_bits);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, ui8, no_of_bits);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
 | 
			
		||||
          }
 | 
			
		||||
@@ -1612,7 +1612,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
            pui8 = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            do
 | 
			
		||||
            {
 | 
			
		||||
              bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
 | 
			
		||||
	      bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              pui8++;
 | 
			
		||||
              bit_offset += no_of_bits;
 | 
			
		||||
@@ -1697,7 +1697,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
            for(unsigned ib = 0; ib < 4; ib++)
 | 
			
		||||
            {
 | 
			
		||||
              pui8      = pui8DATA(data, pDescr->offset+ib);
 | 
			
		||||
              bitvec_write_field(vector, writeIndex, *pui8, 8);
 | 
			
		||||
	      bitvec_write_field(vector, &writeIndex, *pui8, 8);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s[%u] = %u | ", pDescr->sz , ib, (unsigned)*pui8);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
@@ -1706,7 +1706,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
            for(unsigned ib = 0; ib < 8; ib++)
 | 
			
		||||
            {
 | 
			
		||||
              pui8      = pui8DATA(data, pDescr->offset+ib);
 | 
			
		||||
              bitvec_write_field(vector, writeIndex, *pui8, 8);
 | 
			
		||||
	      bitvec_write_field(vector, &writeIndex, *pui8, 8);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s[%u] = %u | ", pDescr->sz , ib, (unsigned)*pui8);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
@@ -1761,7 +1761,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
        guint8 no_of_bits = pChoice->bits;
 | 
			
		||||
        guint8 value = pChoice->value;
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pChoice->descr.sz , (unsigned)value);
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, value, no_of_bits);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, value, no_of_bits);
 | 
			
		||||
 | 
			
		||||
        CSN_DESCR   descr[2];
 | 
			
		||||
        gint16      Status;
 | 
			
		||||
@@ -1807,7 +1807,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
        csnStreamInit(&arT, bit_offset, remaining_bits_len);
 | 
			
		||||
        Status = serialize(&arT, vector, writeIndex, pvDATA(data, pDescr->offset));
 | 
			
		||||
 | 
			
		||||
        bitvec_write_field(vector, lengthIndex, writeIndex-lengthIndex-length_len, length_len);
 | 
			
		||||
	bitvec_write_field(vector, &lengthIndex, writeIndex-lengthIndex-length_len, length_len);
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "%s length = %u | ", pDescr->sz , (unsigned)(writeIndex-lengthIndex));
 | 
			
		||||
 | 
			
		||||
        if (Status >= 0)
 | 
			
		||||
@@ -1846,7 +1846,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
        /* Assign UnionType */
 | 
			
		||||
        pui8  = pui8DATA(data, pDescr->offset);
 | 
			
		||||
	//read index from data and write to vector
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, *pui8, Bits);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, *pui8, Bits);
 | 
			
		||||
 | 
			
		||||
	//decode index 
 | 
			
		||||
        writeIndex -= Bits;
 | 
			
		||||
@@ -1861,7 +1861,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
          }
 | 
			
		||||
          else
 | 
			
		||||
          {
 | 
			
		||||
            index |= bitvec_read_field(vector, writeIndex, 1);
 | 
			
		||||
	    index |= bitvec_read_field(vector, &writeIndex, 1);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          remaining_bits_len--;
 | 
			
		||||
@@ -1870,7 +1870,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        writeIndex -= Bits;
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, index, Bits);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, index, Bits);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /* script index to continue on, limited in case we do not have a power of 2 */
 | 
			
		||||
@@ -1883,7 +1883,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
          case CSN_BIT:
 | 
			
		||||
          {
 | 
			
		||||
            pui8  = pui8DATA(data, pDescr->offset);
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, *pui8, 1);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, *pui8, 1);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
            remaining_bits_len -= 1;
 | 
			
		||||
            bit_offset++;
 | 
			
		||||
@@ -1907,19 +1907,19 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
              if (no_of_bits <= 8)
 | 
			
		||||
              {
 | 
			
		||||
                pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
                bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
 | 
			
		||||
		bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              }
 | 
			
		||||
              else if (no_of_bits <= 16)
 | 
			
		||||
              {
 | 
			
		||||
                pui16       = pui16DATA(data, pDescr->offset);
 | 
			
		||||
                bitvec_write_field(vector, writeIndex, *pui16, no_of_bits);
 | 
			
		||||
		bitvec_write_field(vector, &writeIndex, *pui16, no_of_bits);
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
 | 
			
		||||
              }
 | 
			
		||||
              else if (no_of_bits <= 32)
 | 
			
		||||
              {
 | 
			
		||||
                pui32       = pui32DATA(data, pDescr->offset);
 | 
			
		||||
                bitvec_write_field(vector, writeIndex, *pui32, no_of_bits);
 | 
			
		||||
		bitvec_write_field(vector, &writeIndex, *pui32, no_of_bits);
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
 | 
			
		||||
              }
 | 
			
		||||
              else
 | 
			
		||||
@@ -1946,19 +1946,19 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
              if (no_of_bits <= 8)
 | 
			
		||||
              {
 | 
			
		||||
                pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
                bitvec_write_field(vector, writeIndex, *pui8 - (guint8)pDescr->descr.value, no_of_bits);
 | 
			
		||||
		bitvec_write_field(vector, &writeIndex, *pui8 - (guint8)pDescr->descr.value, no_of_bits);
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(*pui8 - (guint8)pDescr->descr.value));
 | 
			
		||||
              }
 | 
			
		||||
              else if (no_of_bits <= 16)
 | 
			
		||||
              {
 | 
			
		||||
                pui16       = pui16DATA(data, pDescr->offset);
 | 
			
		||||
                bitvec_write_field(vector, writeIndex, *pui16 - (guint16)pDescr->descr.value, no_of_bits);
 | 
			
		||||
		bitvec_write_field(vector, &writeIndex, *pui16 - (guint16)pDescr->descr.value, no_of_bits);
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , (unsigned short)(*pui16 - (guint16)pDescr->descr.value));
 | 
			
		||||
              }
 | 
			
		||||
              else if (no_of_bits <= 32)
 | 
			
		||||
              {
 | 
			
		||||
                pui32       = pui32DATA(data, pDescr->offset);
 | 
			
		||||
                bitvec_write_field(vector, writeIndex, *pui32 - (guint16)pDescr->descr.value, no_of_bits);
 | 
			
		||||
		bitvec_write_field(vector, &writeIndex, *pui32 - (guint16)pDescr->descr.value, no_of_bits);
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , (unsigned int)(*pui32 - (guint16)pDescr->descr.value));
 | 
			
		||||
              }
 | 
			
		||||
              else
 | 
			
		||||
@@ -1987,12 +1987,12 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
              if (no_of_bits <= 8)
 | 
			
		||||
              {
 | 
			
		||||
                pui8      = pui8DATA(data, pDescr->offset);
 | 
			
		||||
                bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
 | 
			
		||||
		bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
 | 
			
		||||
                // TODO : Change get_masked_bits8()
 | 
			
		||||
                writeIndex -= no_of_bits;
 | 
			
		||||
                guint8 ui8 = get_masked_bits8(vector, writeIndex, bit_offset, no_of_bits);
 | 
			
		||||
                writeIndex -= no_of_bits;
 | 
			
		||||
                bitvec_write_field(vector, writeIndex, ui8, no_of_bits);
 | 
			
		||||
		bitvec_write_field(vector, &writeIndex, ui8, no_of_bits);
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
 | 
			
		||||
              }
 | 
			
		||||
@@ -2030,7 +2030,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
                pui8 = pui8DATA(data, pDescr->offset);
 | 
			
		||||
                do
 | 
			
		||||
                {
 | 
			
		||||
                  bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
 | 
			
		||||
		  bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
 | 
			
		||||
                  LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
                  pui8++;
 | 
			
		||||
                  bit_offset += no_of_bits;
 | 
			
		||||
@@ -2112,13 +2112,13 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
              if (no_of_bits <= 32)
 | 
			
		||||
              {
 | 
			
		||||
                pui32 = pui32DATA(data, pDescr->offset);
 | 
			
		||||
                bitvec_write_field(vector, writeIndex, *pui32, no_of_bits);
 | 
			
		||||
		bitvec_write_field(vector, &writeIndex, *pui32, no_of_bits);
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
 | 
			
		||||
              }
 | 
			
		||||
              else if (no_of_bits <= 64)
 | 
			
		||||
              {
 | 
			
		||||
                pui64 = pui64DATA(data, pDescr->offset);
 | 
			
		||||
                bitvec_write_field(vector, writeIndex, *pui64, no_of_bits);
 | 
			
		||||
		bitvec_write_field(vector, &writeIndex, *pui64, no_of_bits);
 | 
			
		||||
                LOGPC(DCSN1, LOGL_NOTICE, "%s = %lu | ", pDescr->sz , *pui64);
 | 
			
		||||
              }
 | 
			
		||||
              else
 | 
			
		||||
@@ -2176,7 +2176,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
        unsigned exist = 0;
 | 
			
		||||
        pui8  = pui8DATA(data, pDescr->offset);
 | 
			
		||||
        exist = *pui8;
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, *pui8, 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, *pui8, 1);
 | 
			
		||||
        writeIndex--;
 | 
			
		||||
        if (CSN_EXIST_LH == pDescr->type)
 | 
			
		||||
        {
 | 
			
		||||
@@ -2184,10 +2184,10 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
          fExist = bitvec_read_field(vector, writeIndex, 1);
 | 
			
		||||
	  fExist = bitvec_read_field(vector, &writeIndex, 1);
 | 
			
		||||
        }
 | 
			
		||||
        writeIndex--;
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, fExist, 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, fExist, 1);
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz, (unsigned)fExist);
 | 
			
		||||
        pDescr++;
 | 
			
		||||
        remaining_bits_len -= 1;
 | 
			
		||||
@@ -2217,7 +2217,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, *pui8, 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, *pui8, 1);
 | 
			
		||||
        fExist = *pui8;
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
        remaining_bits_len -= 1;
 | 
			
		||||
@@ -2257,11 +2257,11 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* the "regular" M_NEXT_EXIST_LH description element */
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, *pui8, 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, *pui8, 1);
 | 
			
		||||
        writeIndex--;
 | 
			
		||||
        fExist = get_masked_bits8(vector,writeIndex, bit_offset, 1);
 | 
			
		||||
        writeIndex--;
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, fExist, 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, fExist, 1);
 | 
			
		||||
        pui8++;
 | 
			
		||||
        remaining_bits_len -= 1;
 | 
			
		||||
 | 
			
		||||
@@ -2310,7 +2310,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 | 
			
		||||
            if (nB1 > 0)
 | 
			
		||||
            { /* take care of the first byte - it will be right aligned */
 | 
			
		||||
              bitvec_write_field(vector, writeIndex, *pui8, nB1);
 | 
			
		||||
	      bitvec_write_field(vector, &writeIndex, *pui8, nB1);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              pui8++;
 | 
			
		||||
              no_of_bits  -= nB1;
 | 
			
		||||
@@ -2320,7 +2320,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
            /* remaining no_of_bits is a multiple of 8 or 0 */
 | 
			
		||||
            while (no_of_bits > 0)
 | 
			
		||||
            {
 | 
			
		||||
              bitvec_write_field(vector, writeIndex, *pui8, 8);
 | 
			
		||||
	      bitvec_write_field(vector, &writeIndex, *pui8, 8);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              pui8++;
 | 
			
		||||
              no_of_bits -= 8;
 | 
			
		||||
@@ -2366,14 +2366,14 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 | 
			
		||||
            while (no_of_bits > 0)
 | 
			
		||||
            {
 | 
			
		||||
              bitvec_write_field(vector, writeIndex, *pui8, 8);
 | 
			
		||||
	      bitvec_write_field(vector, &writeIndex, *pui8, 8);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              pui8++;
 | 
			
		||||
              no_of_bits -= 8;
 | 
			
		||||
            }
 | 
			
		||||
            if (nB1 > 0)
 | 
			
		||||
            {
 | 
			
		||||
              bitvec_write_field(vector, writeIndex, *pui8, nB1);
 | 
			
		||||
	      bitvec_write_field(vector, &writeIndex, *pui8, nB1);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
              pui8++;
 | 
			
		||||
              no_of_bits  -= nB1;
 | 
			
		||||
@@ -2399,15 +2399,20 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
            guint8 bits_to_handle = remaining_bits_len%8;
 | 
			
		||||
            if (bits_to_handle > 0)
 | 
			
		||||
            {
 | 
			
		||||
              guint8 fl = filler&(0xff>>(8-bits_to_handle));
 | 
			
		||||
              bitvec_write_field(vector, writeIndex, fl, bits_to_handle);
 | 
			
		||||
              /* section 11 of 44.060
 | 
			
		||||
               * The padding bits may be the 'null' string. Otherwise, the
 | 
			
		||||
               * padding bits starts with bit '0', followed by 'spare padding'
 | 
			
		||||
               * < padding bits > ::= { null | 0 < spare padding > ! < Ignore : 1 bit** = < no string > > } ;
 | 
			
		||||
              */
 | 
			
		||||
              guint8 fl = filler&(0xff>>(8-bits_to_handle + 1));
 | 
			
		||||
	      bitvec_write_field(vector, &writeIndex, fl, bits_to_handle);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%u|", fl);
 | 
			
		||||
              remaining_bits_len -= bits_to_handle;
 | 
			
		||||
              bit_offset += bits_to_handle;
 | 
			
		||||
            }
 | 
			
		||||
            else if (bits_to_handle == 0)
 | 
			
		||||
            {
 | 
			
		||||
              bitvec_write_field(vector, writeIndex, filler, 8);
 | 
			
		||||
	      bitvec_write_field(vector, &writeIndex, filler, 8);
 | 
			
		||||
              LOGPC(DCSN1, LOGL_NOTICE, "%u|", filler);
 | 
			
		||||
              remaining_bits_len -= 8;
 | 
			
		||||
              bit_offset += 8;
 | 
			
		||||
@@ -2448,7 +2453,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 | 
			
		||||
          while (count > 0)
 | 
			
		||||
          {
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, *pui8, 8);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, *pui8, 8);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
            pui8++;
 | 
			
		||||
            bit_offset += 8;
 | 
			
		||||
@@ -2475,13 +2480,13 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
        ElementCount = *pui8DATA(data, (gint16)pDescr->descr.value);
 | 
			
		||||
        while (ElementCount > 0)
 | 
			
		||||
        { /* tag control shows existence of next list elements */
 | 
			
		||||
          bitvec_write_field(vector, writeIndex, Tag, 1);
 | 
			
		||||
	  bitvec_write_field(vector, &writeIndex, Tag, 1);
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)Tag);
 | 
			
		||||
          bit_offset++;
 | 
			
		||||
          remaining_bits_len--;
 | 
			
		||||
 | 
			
		||||
          /* extract and store no_of_bits long element from bitstream */
 | 
			
		||||
          bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
 | 
			
		||||
	  bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
 | 
			
		||||
          pui8++;
 | 
			
		||||
          remaining_bits_len -= no_of_bits;
 | 
			
		||||
@@ -2495,9 +2500,10 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
          bit_offset += no_of_bits;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, !Tag, 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, !Tag, 1);
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(!Tag));
 | 
			
		||||
        bit_offset++;
 | 
			
		||||
        remaining_bits_len--;
 | 
			
		||||
 | 
			
		||||
        pDescr++;
 | 
			
		||||
        break;
 | 
			
		||||
@@ -2516,7 +2522,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
 | 
			
		||||
        while (ElementCount > 0)
 | 
			
		||||
        { /* tag control shows existence of next list elements */
 | 
			
		||||
          bitvec_write_field(vector, writeIndex, Tag, 1);
 | 
			
		||||
	  bitvec_write_field(vector, &writeIndex, Tag, 1);
 | 
			
		||||
          LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)Tag);
 | 
			
		||||
          bit_offset++;
 | 
			
		||||
 | 
			
		||||
@@ -2547,7 +2553,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, !Tag, 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, !Tag, 1);
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(!Tag));
 | 
			
		||||
        bit_offset++;
 | 
			
		||||
 | 
			
		||||
@@ -2585,7 +2591,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
        { /* get data element */
 | 
			
		||||
          if (ElementCount != ElementNum)
 | 
			
		||||
          {
 | 
			
		||||
            bitvec_write_field(vector, writeIndex, Tag, 1);
 | 
			
		||||
	    bitvec_write_field(vector, &writeIndex, Tag, 1);
 | 
			
		||||
            LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)Tag);
 | 
			
		||||
            bit_offset++;
 | 
			
		||||
            remaining_bits_len--;
 | 
			
		||||
@@ -2612,7 +2618,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, !Tag, 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, !Tag, 1);
 | 
			
		||||
        bit_offset++;
 | 
			
		||||
        remaining_bits_len--;
 | 
			
		||||
        Tag = STANDARD_TAG; /* in case it was set to "reversed" */
 | 
			
		||||
@@ -2623,7 +2629,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
 | 
			
		||||
      case CSN_FIXED:
 | 
			
		||||
      { /* Verify the fixed bits */
 | 
			
		||||
        guint8  no_of_bits = (guint8) pDescr->i;
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, pDescr->offset, no_of_bits);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, pDescr->offset, no_of_bits);
 | 
			
		||||
        LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)pDescr->offset);
 | 
			
		||||
        remaining_bits_len   -= no_of_bits;
 | 
			
		||||
        bit_offset += no_of_bits;
 | 
			
		||||
 
 | 
			
		||||
@@ -25,12 +25,15 @@
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <bitvector.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#ifndef _PACKET_CSN1_H_
 | 
			
		||||
#define _PACKET_CSN1_H_
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/bitvec.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
 | 
			
		||||
#define MIN(a,b) (((a)<(b))?(a):(b))
 | 
			
		||||
//#define max(a,b) (((a)>(b))?(a):(b))
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ struct LListHead {
 | 
			
		||||
		return *static_cast<llist_head *>(static_cast<void *>(this));
 | 
			
		||||
	}
 | 
			
		||||
	const llist_head &llist() const {
 | 
			
		||||
		return *static_cast<llist_head *>(static_cast<void *>(this));
 | 
			
		||||
		return *static_cast<const llist_head *>(static_cast<const void *>(this));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										760
									
								
								src/decoding.cpp
									
									
									
									
									
								
							
							
						
						
									
										760
									
								
								src/decoding.cpp
									
									
									
									
									
								
							@@ -20,52 +20,275 @@
 | 
			
		||||
#include <decoding.h>
 | 
			
		||||
#include <rlc.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <egprs_rlc_compression.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/bitcomp.h>
 | 
			
		||||
#include <osmocom/gprs/protocol/gsm_04_60.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int Decoding::tlli_from_ul_data(const uint8_t *data, uint8_t len,
 | 
			
		||||
					uint32_t *tlli)
 | 
			
		||||
#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)
 | 
			
		||||
{
 | 
			
		||||
	struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
 | 
			
		||||
	struct rlc_li_field *li;
 | 
			
		||||
	const struct rlc_li_field_egprs *li;
 | 
			
		||||
	uint8_t e;
 | 
			
		||||
	uint32_t _tlli;
 | 
			
		||||
	unsigned int num_chunks = 0;
 | 
			
		||||
 | 
			
		||||
	if (!rh->ti)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	
 | 
			
		||||
	data += 3;
 | 
			
		||||
	len -= 3;
 | 
			
		||||
	e = rh->e;
 | 
			
		||||
	/* if E is not set (LI follows) */
 | 
			
		||||
	e = 0;
 | 
			
		||||
	while (!e) {
 | 
			
		||||
		if (!len) {
 | 
			
		||||
		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 *)data;
 | 
			
		||||
		if (li->e == 0) /* if LI==0, E is interpreted as '1' */
 | 
			
		||||
			e = 1;
 | 
			
		||||
		else
 | 
			
		||||
			e = li->e;
 | 
			
		||||
		data++;
 | 
			
		||||
		len--;
 | 
			
		||||
	}
 | 
			
		||||
	if (len < 4) {
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of frame "
 | 
			
		||||
			"border\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	memcpy(&_tlli, data, 4);
 | 
			
		||||
	*tlli = ntohl(_tlli);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
		/* 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 extention 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 "
 | 
			
		||||
			"extention 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 "
 | 
			
		||||
			"extention 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, GprsCodingScheme cs,
 | 
			
		||||
	const uint8_t *data, RlcData *chunks, unsigned int chunks_size,
 | 
			
		||||
	uint32_t *tlli)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t e;
 | 
			
		||||
	unsigned int data_len = rdbi->data_len;
 | 
			
		||||
	int num_chunks = 0, i;
 | 
			
		||||
	unsigned int offs = 0;
 | 
			
		||||
	bool is_last_block = (rdbi->cv == 0);
 | 
			
		||||
 | 
			
		||||
	if (!chunks)
 | 
			
		||||
		chunks_size = 0;
 | 
			
		||||
 | 
			
		||||
	e = rdbi->e;
 | 
			
		||||
	if (e) {
 | 
			
		||||
		if (chunks_size > 0) {
 | 
			
		||||
			/* 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 (cs.isEgprs()) {
 | 
			
		||||
		/* if E is not set (LI follows), EGPRS */
 | 
			
		||||
		num_chunks = parse_extensions_egprs(data, data_len, &offs,
 | 
			
		||||
			is_last_block,
 | 
			
		||||
			chunks, chunks_size);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* if E is not set (LI follows), GPRS */
 | 
			
		||||
		num_chunks = parse_extensions_gprs(data, data_len, &offs,
 | 
			
		||||
			is_last_block,
 | 
			
		||||
			chunks, chunks_size);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (num_chunks < 0)
 | 
			
		||||
		return num_chunks;
 | 
			
		||||
 | 
			
		||||
	/* TLLI */
 | 
			
		||||
	if (rdbi->ti) {
 | 
			
		||||
		uint32_t tlli_enc;
 | 
			
		||||
		if (offs + 4 > data_len) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of block "
 | 
			
		||||
				"border\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		memcpy(&tlli_enc, data + offs, sizeof(tlli_enc));
 | 
			
		||||
		if (cs.isGprs())
 | 
			
		||||
			/* The TLLI is encoded in big endian for GPRS (see
 | 
			
		||||
			 * TS 44.060, figure 10.2.2.1, note) */
 | 
			
		||||
			*tlli = be32toh(tlli_enc);
 | 
			
		||||
		else
 | 
			
		||||
			/* The TLLI is encoded in little endian for EGPRS (see
 | 
			
		||||
			 * TS 44.060, figure 10.3a.2.1, note 2) */
 | 
			
		||||
			*tlli = le32toh(tlli_enc);
 | 
			
		||||
 | 
			
		||||
		offs += sizeof(tlli_enc);
 | 
			
		||||
	} else {
 | 
			
		||||
		*tlli = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* PFI */
 | 
			
		||||
	if (rdbi->pi) {
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
 | 
			
		||||
			"please disable in SYSTEM INFORMATION\n");
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
 | 
			
		||||
		/* TODO: Skip all extensions with E=0 (see TS 44.060, 10.4.11 */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (chunks_size == 0)
 | 
			
		||||
		return num_chunks;
 | 
			
		||||
 | 
			
		||||
	/* LLC */
 | 
			
		||||
	for (i = 0; i < num_chunks; i++) {
 | 
			
		||||
		chunks[i].offset = offs;
 | 
			
		||||
		if (chunks[i].length == LENGTH_TO_END) {
 | 
			
		||||
			if (offs == data_len) {
 | 
			
		||||
				/* There is no place for an additional chunk,
 | 
			
		||||
				 * so drop it (this may happen with EGPRS since
 | 
			
		||||
				 * there is no M flag. */
 | 
			
		||||
				num_chunks -= 1;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			chunks[i].length = data_len - offs;
 | 
			
		||||
		}
 | 
			
		||||
		offs += chunks[i].length;
 | 
			
		||||
		if (offs > data_len) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA out of block "
 | 
			
		||||
				"border, chunk idx: %d, size: %d\n",
 | 
			
		||||
				i, chunks[i].length);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return num_chunks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
 | 
			
		||||
@@ -83,6 +306,21 @@ uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t Decoding::get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
 | 
			
		||||
		if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_EGPRS_multislot_class)
 | 
			
		||||
			continue;
 | 
			
		||||
		return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.EGPRS_multislot_class;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * show_rbb needs to be an array with 65 elements
 | 
			
		||||
 * The index of the array is the bit position in the rbb
 | 
			
		||||
@@ -99,3 +337,463 @@ void Decoding::extract_rbb(const uint8_t *rbb, char *show_rbb)
 | 
			
		||||
 | 
			
		||||
	show_rbb[64] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	for (i = 0; i < rbb->cur_bit; i++) {
 | 
			
		||||
		uint8_t bit;
 | 
			
		||||
		bit = bitvec_get_bit_pos(rbb, i);
 | 
			
		||||
		show_rbb[i] = bit == 1 ? 'R' : 'I';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	show_rbb[i] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	const uint8_t *data, GprsCodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int cur_bit = 0;
 | 
			
		||||
	switch(cs.headerTypeData()) {
 | 
			
		||||
	case GprsCodingScheme::HEADER_GPRS_DATA :
 | 
			
		||||
		cur_bit = rlc_parse_ul_data_header_gprs(rlc, data, cs);
 | 
			
		||||
		break;
 | 
			
		||||
	case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3 :
 | 
			
		||||
		cur_bit = rlc_parse_ul_data_header_egprs_type_3(rlc, data, cs);
 | 
			
		||||
		break;
 | 
			
		||||
	case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2 :
 | 
			
		||||
		cur_bit = rlc_parse_ul_data_header_egprs_type_2(rlc, data, cs);
 | 
			
		||||
		break;
 | 
			
		||||
	case GprsCodingScheme::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",
 | 
			
		||||
			cs.name());
 | 
			
		||||
		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 GprsCodingScheme &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 += cs.maxDataBlockBytes() * 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 GprsCodingScheme &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 += cs.maxDataBlockBytes() * 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 GprsCodingScheme &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 += cs.maxDataBlockBytes() * 8;
 | 
			
		||||
 | 
			
		||||
	return cur_bit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Decoding::rlc_parse_ul_data_header_gprs(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	const uint8_t *data, const GprsCodingScheme &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 += cs.maxDataBlockBytes() * 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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,14 +20,62 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <gsm_rlcmac.h>
 | 
			
		||||
#include "rlc.h"
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
struct bitvec;
 | 
			
		||||
 | 
			
		||||
class Decoding {
 | 
			
		||||
public:
 | 
			
		||||
	static int tlli_from_ul_data(const uint8_t *data, uint8_t len,
 | 
			
		||||
					uint32_t *tlli);
 | 
			
		||||
	/* 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,
 | 
			
		||||
		GprsCodingScheme cs, const uint8_t *data, RlcData *chunks,
 | 
			
		||||
		unsigned int chunks_size, uint32_t *tlli);
 | 
			
		||||
	static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
 | 
			
		||||
	static uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
 | 
			
		||||
 | 
			
		||||
	static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
 | 
			
		||||
	static void extract_rbb(const struct bitvec *rbb, char *show_rbb);
 | 
			
		||||
	static int rlc_parse_ul_data_header_egprs_type_3(
 | 
			
		||||
		struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		const uint8_t *data,
 | 
			
		||||
		const GprsCodingScheme &cs);
 | 
			
		||||
	static int rlc_parse_ul_data_header_egprs_type_2(
 | 
			
		||||
		struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		const uint8_t *data,
 | 
			
		||||
		const GprsCodingScheme &cs);
 | 
			
		||||
	static int rlc_parse_ul_data_header_egprs_type_1(
 | 
			
		||||
		struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		const uint8_t *data,
 | 
			
		||||
		const GprsCodingScheme &cs);
 | 
			
		||||
	static int rlc_parse_ul_data_header_gprs(
 | 
			
		||||
		struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		const uint8_t *data,
 | 
			
		||||
		const GprsCodingScheme &cs);
 | 
			
		||||
	static int rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		const uint8_t *data, GprsCodingScheme cs);
 | 
			
		||||
	static unsigned int rlc_copy_to_aligned_buffer(
 | 
			
		||||
		const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		unsigned int data_block_idx,
 | 
			
		||||
		const uint8_t *src, uint8_t *buffer);
 | 
			
		||||
	static const uint8_t *rlc_get_data_aligned(
 | 
			
		||||
		const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		unsigned int data_block_idx,
 | 
			
		||||
		const uint8_t *src, uint8_t *buffer);
 | 
			
		||||
	static int decode_egprs_acknack_bits(
 | 
			
		||||
		const EGPRS_AckNack_Desc_t *desc,
 | 
			
		||||
		struct bitvec *bits, int *bsn_begin, int *bsn_end,
 | 
			
		||||
		struct gprs_rlc_dl_window *window);
 | 
			
		||||
	static int decode_gprs_acknack_bits(
 | 
			
		||||
		const Ack_Nack_Description_t *desc,
 | 
			
		||||
		bitvec *bits, int *bsn_begin, int *bsn_end,
 | 
			
		||||
		gprs_rlc_dl_window *window);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										691
									
								
								src/egprs_rlc_compression.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										691
									
								
								src/egprs_rlc_compression.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,691 @@
 | 
			
		||||
/* 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 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 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;
 | 
			
		||||
		/* 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)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	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 (compress_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,
 | 
			
		||||
				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;
 | 
			
		||||
		compress_bmap_len = compress_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;
 | 
			
		||||
	else
 | 
			
		||||
		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();
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										1531
									
								
								src/encoding.cpp
									
									
									
									
									
								
							
							
						
						
									
										1531
									
								
								src/encoding.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -22,10 +22,16 @@
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <gsm_rlcmac.h>
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/gsm/l1sap.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
struct gprs_rlcmac_tbf;
 | 
			
		||||
struct bitvec;
 | 
			
		||||
struct gprs_llc;
 | 
			
		||||
struct gprs_rlc_data_block_info;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * I help with encoding data into CSN1 messages.
 | 
			
		||||
@@ -36,28 +42,45 @@ struct bitvec;
 | 
			
		||||
class Encoding {
 | 
			
		||||
public:
 | 
			
		||||
	static int write_immediate_assignment(
 | 
			
		||||
			struct gprs_rlcmac_bts *bts,
 | 
			
		||||
			bitvec * dest, uint8_t downlink, uint8_t ra, 
 | 
			
		||||
		        uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc, 
 | 
			
		||||
		        uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
 | 
			
		||||
			uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma,
 | 
			
		||||
			int8_t ta_idx);
 | 
			
		||||
			struct gprs_rlcmac_tbf *tbf,
 | 
			
		||||
			bitvec * dest, uint8_t downlink, uint16_t ra,
 | 
			
		||||
			uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts,
 | 
			
		||||
			uint8_t tsc, uint8_t usf, uint8_t polling,
 | 
			
		||||
			uint32_t fn, uint8_t alpha, uint8_t gamma,
 | 
			
		||||
			int8_t ta_idx,
 | 
			
		||||
			enum ph_burst_type burst_type =
 | 
			
		||||
				GSM_L1_BURST_TYPE_ACCESS_0,
 | 
			
		||||
			uint8_t sb = 1);
 | 
			
		||||
 | 
			
		||||
	static int write_immediate_assignment_reject(
 | 
			
		||||
			bitvec *dest, uint16_t ra,
 | 
			
		||||
			uint32_t ref_fn,
 | 
			
		||||
			enum ph_burst_type burst_type
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
	static void write_packet_uplink_assignment(
 | 
			
		||||
			struct gprs_rlcmac_bts *bts,
 | 
			
		||||
			bitvec * dest, uint8_t old_tfi,
 | 
			
		||||
			uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, 
 | 
			
		||||
			struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t alpha,
 | 
			
		||||
			uint8_t gamma, int8_t ta_idx);
 | 
			
		||||
			uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
 | 
			
		||||
			struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t rrbp,
 | 
			
		||||
			uint8_t alpha, uint8_t gamma, int8_t ta_idx,
 | 
			
		||||
			int8_t use_egprs);
 | 
			
		||||
 | 
			
		||||
	static void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
 | 
			
		||||
			uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll,
 | 
			
		||||
			uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts);
 | 
			
		||||
	static void write_packet_downlink_assignment(RlcMacDownlink_t * block,
 | 
			
		||||
			bool old_tfi_is_valid, uint8_t old_tfi, uint8_t old_downlink,
 | 
			
		||||
			struct gprs_rlcmac_dl_tbf *tbf, uint8_t poll, uint8_t rrbp,
 | 
			
		||||
			uint8_t alpha, uint8_t gamma,
 | 
			
		||||
			int8_t ta_idx, uint8_t ta_ts, bool use_egprs);
 | 
			
		||||
 | 
			
		||||
	static void encode_rbb(const char *show_rbb, uint8_t *rbb);
 | 
			
		||||
 | 
			
		||||
	static void write_packet_uplink_ack(struct gprs_rlcmac_bts *bts, RlcMacDownlink_t * block, struct gprs_rlcmac_ul_tbf *tbf,
 | 
			
		||||
		        uint8_t final);
 | 
			
		||||
	static void write_packet_access_reject(
 | 
			
		||||
		bitvec * dest, uint32_t tlli);
 | 
			
		||||
 | 
			
		||||
	static void write_packet_uplink_ack(
 | 
			
		||||
			struct gprs_rlcmac_bts *bts, bitvec * dest,
 | 
			
		||||
			struct gprs_rlcmac_ul_tbf *tbf, bool is_final,
 | 
			
		||||
			uint8_t rrbp);
 | 
			
		||||
 | 
			
		||||
	static int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len);
 | 
			
		||||
 | 
			
		||||
@@ -65,4 +88,23 @@ public:
 | 
			
		||||
			uint8_t *identity, uint8_t chan_needed);
 | 
			
		||||
 | 
			
		||||
	static unsigned write_packet_paging_request(bitvec * dest);
 | 
			
		||||
 | 
			
		||||
	static int rlc_write_dl_data_header(
 | 
			
		||||
			const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
			uint8_t *data);
 | 
			
		||||
	static unsigned int rlc_copy_from_aligned_buffer(
 | 
			
		||||
			const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
			unsigned int data_block_idx,
 | 
			
		||||
			uint8_t *dst, const uint8_t *buffer);
 | 
			
		||||
 | 
			
		||||
	enum AppendResult {
 | 
			
		||||
		AR_NEED_MORE_BLOCKS,
 | 
			
		||||
		AR_COMPLETED_SPACE_LEFT,
 | 
			
		||||
		AR_COMPLETED_BLOCK_FILLED,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static AppendResult rlc_data_to_dl_append(
 | 
			
		||||
		struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
 | 
			
		||||
		gprs_llc *llc, int *offset, int *num_chunks,
 | 
			
		||||
		uint8_t *data, bool is_final, int *count_payload);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,11 @@
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gprs_bssgp_pcu.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
 | 
			
		||||
#define BSSGP_TIMER_T1	30	/* Guards the (un)blocking procedures */
 | 
			
		||||
#define BSSGP_TIMER_T2	30	/* Guards the reset procedure */
 | 
			
		||||
@@ -41,6 +44,7 @@ static struct gprs_bssgp_pcu the_pcu = { 0, };
 | 
			
		||||
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
extern uint16_t spoof_mcc, spoof_mnc;
 | 
			
		||||
extern bool spoof_mnc_3_digits;
 | 
			
		||||
 | 
			
		||||
static void bvc_timeout(void *_priv);
 | 
			
		||||
 | 
			
		||||
@@ -73,45 +77,33 @@ static int parse_imsi(struct tlv_parsed *tp, char *imsi)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_ra_cap_ms_class(struct tlv_parsed *tp)
 | 
			
		||||
#if 0
 | 
			
		||||
static int parse_ra_cap(struct tlv_parsed *tp, MS_Radio_Access_capability_t *rac)
 | 
			
		||||
{
 | 
			
		||||
	bitvec *block;
 | 
			
		||||
	unsigned rp = 0;
 | 
			
		||||
	uint8_t ms_class = 0;
 | 
			
		||||
	uint8_t cap_len;
 | 
			
		||||
	uint8_t *cap;
 | 
			
		||||
 | 
			
		||||
	memset(rac, 0, sizeof(*rac));
 | 
			
		||||
 | 
			
		||||
	if (!TLVP_PRESENT(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP))
 | 
			
		||||
		return ms_class;
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	cap_len = TLVP_LEN(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
 | 
			
		||||
	cap = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
 | 
			
		||||
 | 
			
		||||
	block = bitvec_alloc(cap_len);
 | 
			
		||||
	LOGP(DBSSGP, LOGL_DEBUG, "Got BSSGP RA Capability of size %d\n", cap_len);
 | 
			
		||||
 | 
			
		||||
	block = bitvec_alloc(cap_len, tall_pcu_ctx);
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* TS 24.008, 10.5.5.12a */
 | 
			
		||||
	decode_gsm_ra_cap(block, rac);
 | 
			
		||||
 | 
			
		||||
	bitvec_free(block);
 | 
			
		||||
	return ms_class;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
 | 
			
		||||
{
 | 
			
		||||
@@ -122,6 +114,11 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
 | 
			
		||||
	uint8_t *data;
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
	char imsi[16] = "000";
 | 
			
		||||
	uint8_t ms_class = 0;
 | 
			
		||||
	uint8_t egprs_ms_class = 0;
 | 
			
		||||
#if 0
 | 
			
		||||
	MS_Radio_Access_capability_t rac;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
 | 
			
		||||
	tlli = ntohl(budh->tlli);
 | 
			
		||||
@@ -146,17 +143,25 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
 | 
			
		||||
	 * will listen to all paging blocks. */
 | 
			
		||||
	parse_imsi(tp, imsi);
 | 
			
		||||
 | 
			
		||||
#if 0 /* Do not rely on this IE. TODO: make this configurable */
 | 
			
		||||
	/* parse ms radio access capability */
 | 
			
		||||
	uint8_t ms_class = parse_ra_cap_ms_class(tp);
 | 
			
		||||
	if (parse_ra_cap(tp, &rac) >= 0) {
 | 
			
		||||
		/* Get the EGPRS class from the RA capability */
 | 
			
		||||
		ms_class = Decoding::get_ms_class_by_capability(&rac);
 | 
			
		||||
		egprs_ms_class =
 | 
			
		||||
			Decoding::get_egprs_ms_class_by_capability(&rac);
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "Got downlink MS class %d/%d\n",
 | 
			
		||||
			ms_class, egprs_ms_class);
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* 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);
 | 
			
		||||
			delay_csec = tlvp_val16be(tp, BSSGP_IE_PDU_LIFETIME);
 | 
			
		||||
		else
 | 
			
		||||
			LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of "
 | 
			
		||||
				"PDU_LIFETIME IE\n");
 | 
			
		||||
@@ -168,9 +173,8 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
 | 
			
		||||
	if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t tlli_len = TLVP_LEN(tp, BSSGP_IE_PDU_LIFETIME);
 | 
			
		||||
		uint16_t *e_tlli_old = (uint16_t *) TLVP_VAL(tp, BSSGP_IE_TLLI);
 | 
			
		||||
		if (tlli_len == 2)
 | 
			
		||||
			tlli_old = ntohs(*e_tlli_old);
 | 
			
		||||
			tlli_old = tlvp_val16be(tp, BSSGP_IE_TLLI);
 | 
			
		||||
		else
 | 
			
		||||
			LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of "
 | 
			
		||||
				"TLLI (old) IE\n");
 | 
			
		||||
@@ -179,7 +183,7 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
 | 
			
		||||
	LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
 | 
			
		||||
 | 
			
		||||
	return gprs_rlcmac_dl_tbf::handle(the_pcu.bts, tlli, tlli_old, imsi,
 | 
			
		||||
			ms_class, delay_csec, data, len);
 | 
			
		||||
			ms_class, egprs_ms_class, delay_csec, data, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
 | 
			
		||||
@@ -208,7 +212,8 @@ int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
 | 
			
		||||
static 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;
 | 
			
		||||
	enum bssgp_pdu_type pdu_type = (enum bssgp_pdu_type) bgph->pdu_type;
 | 
			
		||||
	int bvci = bctx ? bctx->bvci : -1;
 | 
			
		||||
	unsigned rc = 0;
 | 
			
		||||
 | 
			
		||||
	if (!bctx)
 | 
			
		||||
@@ -225,29 +230,52 @@ static int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (pdu_type) {
 | 
			
		||||
	case BSSGP_PDUT_STATUS:
 | 
			
		||||
		/* already handled in libosmogb */
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
		break;
 | 
			
		||||
	case BSSGP_PDUT_DL_UNITDATA:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "Rx BSSGP BVCI=%d (PTP) DL_UNITDATA\n", bvci);
 | 
			
		||||
		if (the_pcu.on_dl_unit_data)
 | 
			
		||||
			the_pcu.on_dl_unit_data(&the_pcu, msg, tp);
 | 
			
		||||
		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");
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "Rx BSSGP BVCI=%d (PTP) %s\n",
 | 
			
		||||
		     bvci, bssgp_pdu_str(pdu_type));
 | 
			
		||||
		break;
 | 
			
		||||
	case BSSGP_PDUT_PAGING_PS:
 | 
			
		||||
	case BSSGP_PDUT_PAGING_CS:
 | 
			
		||||
	case BSSGP_PDUT_RA_CAPABILITY:
 | 
			
		||||
	case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_INFO, "Rx BSSGP BVCI=%d (PTP) PDU type %s not implemented\n",
 | 
			
		||||
		     bvci, bssgp_pdu_str(pdu_type));
 | 
			
		||||
		break;
 | 
			
		||||
	/* See TS 08.18 5.4.1 */
 | 
			
		||||
	case BSSGP_PDUT_SUSPEND:
 | 
			
		||||
	case BSSGP_PDUT_SUSPEND_ACK:
 | 
			
		||||
	case BSSGP_PDUT_SUSPEND_NACK:
 | 
			
		||||
	case BSSGP_PDUT_RESUME:
 | 
			
		||||
	case BSSGP_PDUT_RESUME_ACK:
 | 
			
		||||
	case BSSGP_PDUT_RESUME_NACK:
 | 
			
		||||
	case BSSGP_PDUT_FLUSH_LL:
 | 
			
		||||
	case BSSGP_PDUT_FLUSH_LL_ACK:
 | 
			
		||||
	case BSSGP_PDUT_LLC_DISCARD:
 | 
			
		||||
	case BSSGP_PDUT_BVC_BLOCK:
 | 
			
		||||
	case BSSGP_PDUT_BVC_BLOCK_ACK:
 | 
			
		||||
	case BSSGP_PDUT_BVC_UNBLOCK:
 | 
			
		||||
	case BSSGP_PDUT_BVC_UNBLOCK_ACK:
 | 
			
		||||
	case BSSGP_PDUT_BVC_RESET:
 | 
			
		||||
	case BSSGP_PDUT_BVC_RESET_ACK:
 | 
			
		||||
	case BSSGP_PDUT_SGSN_INVOKE_TRACE:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%u (PTP) PDU type %s unexpected at PTP\n",
 | 
			
		||||
			bctx->bvci, bssgp_pdu_str(pdu_type));
 | 
			
		||||
		rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type);
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%u (PTP) PDU type %s unknown\n",
 | 
			
		||||
			bctx->bvci, bssgp_pdu_str(pdu_type));
 | 
			
		||||
		rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
@@ -258,22 +286,22 @@ static int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct
 | 
			
		||||
static 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);
 | 
			
		||||
	enum bssgp_pdu_type pdu_type = (enum bssgp_pdu_type) bgph->pdu_type;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int bvci = bctx ? bctx->bvci : -1;
 | 
			
		||||
	switch (bgph->pdu_type) {
 | 
			
		||||
	switch (pdu_type) {
 | 
			
		||||
	case BSSGP_PDUT_STATUS:
 | 
			
		||||
		/* Some exception has occurred */
 | 
			
		||||
		DEBUGP(DBSSGP, "BSSGP BVCI=%d Rx BVC STATUS\n", bvci);
 | 
			
		||||
		/* FIXME: send NM_STATUS.ind to NM */
 | 
			
		||||
		/* already handled in libosmogb */
 | 
			
		||||
		OSMO_ASSERT(0);
 | 
			
		||||
		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");
 | 
			
		||||
	case BSSGP_PDUT_RESUME_ACK:
 | 
			
		||||
	case BSSGP_PDUT_BVC_BLOCK_ACK:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "Rx BSSGP BVCI=%d (SIGN) %s\n",
 | 
			
		||||
		     bvci, bssgp_pdu_str(pdu_type));
 | 
			
		||||
		break;
 | 
			
		||||
	case BSSGP_PDUT_BVC_RESET_ACK:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%d (SIGN) BVC_RESET_ACK\n", bvci);
 | 
			
		||||
		if (!the_pcu.bvc_sig_reset)
 | 
			
		||||
			the_pcu.bvc_sig_reset = 1;
 | 
			
		||||
		else
 | 
			
		||||
@@ -281,37 +309,49 @@ static int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struc
 | 
			
		||||
		bvc_timeout(NULL);
 | 
			
		||||
		break;
 | 
			
		||||
	case BSSGP_PDUT_PAGING_PS:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "RX: [SGSN->PCU] BSSGP_PDUT_PAGING_PS\n");
 | 
			
		||||
		gprs_bssgp_pcu_rx_paging_ps(msg, tp);
 | 
			
		||||
		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");
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%d (SIGN) BVC_UNBLOCK_ACK\n", bvci);
 | 
			
		||||
		the_pcu.bvc_unblocked = 1;
 | 
			
		||||
		if (the_pcu.on_unblock_ack)
 | 
			
		||||
			the_pcu.on_unblock_ack(&the_pcu);
 | 
			
		||||
		bvc_timeout(NULL);
 | 
			
		||||
		break;
 | 
			
		||||
	case BSSGP_PDUT_SUSPEND_NACK:
 | 
			
		||||
	case BSSGP_PDUT_RESUME_NACK:
 | 
			
		||||
	case BSSGP_PDUT_PAGING_CS:
 | 
			
		||||
	case BSSGP_PDUT_FLUSH_LL:
 | 
			
		||||
	case BSSGP_PDUT_SGSN_INVOKE_TRACE:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
 | 
			
		||||
		LOGP(DBSSGP, LOGL_INFO, "Rx BSSGP BVCI=%d (SIGN) PDU type %s not implemented\n",
 | 
			
		||||
		     bvci, bssgp_pdu_str(pdu_type));
 | 
			
		||||
		break;
 | 
			
		||||
	/* See TS 08.18 5.4.1 */
 | 
			
		||||
	case BSSGP_PDUT_UL_UNITDATA:
 | 
			
		||||
	case BSSGP_PDUT_DL_UNITDATA:
 | 
			
		||||
	case BSSGP_PDUT_RA_CAPABILITY:
 | 
			
		||||
	case BSSGP_PDUT_PTM_UNITDATA:
 | 
			
		||||
	case BSSGP_PDUT_RA_CAPA_UDPATE:
 | 
			
		||||
	case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
 | 
			
		||||
	case BSSGP_PDUT_RADIO_STATUS:
 | 
			
		||||
	case BSSGP_PDUT_FLOW_CONTROL_BVC:
 | 
			
		||||
	case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
 | 
			
		||||
	case BSSGP_PDUT_FLOW_CONTROL_MS:
 | 
			
		||||
	case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
 | 
			
		||||
	case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
 | 
			
		||||
	case BSSGP_PDUT_CREATE_BSS_PFC:
 | 
			
		||||
	case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
 | 
			
		||||
	case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
 | 
			
		||||
	case BSSGP_PDUT_MODIFY_BSS_PFC:
 | 
			
		||||
	case BSSGP_PDUT_MODIFY_BSS_PFC_ACK:
 | 
			
		||||
	case BSSGP_PDUT_DELETE_BSS_PFC:
 | 
			
		||||
	case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%d (SIGN) PDU type %s unexpected at SIGN\n",
 | 
			
		||||
		     bvci, bssgp_pdu_str(pdu_type));
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%d Rx PDU type 0x%02x unknown\n",
 | 
			
		||||
			bvci, bgph->pdu_type);
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%d (SIGN) PDU type %s unknown\n",
 | 
			
		||||
		     bvci, bssgp_pdu_str(pdu_type));
 | 
			
		||||
		rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
@@ -323,15 +363,16 @@ static 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);
 | 
			
		||||
	enum bssgp_pdu_type pdu_type = (enum bssgp_pdu_type) bgph->pdu_type;
 | 
			
		||||
	enum gprs_bssgp_cause cause = BSSGP_CAUSE_OML_INTERV;
 | 
			
		||||
	uint16_t ns_bvci = msgb_bvci(msg), nsei = msgb_nsei(msg);
 | 
			
		||||
	int data_len;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	struct bssgp_bvc_ctx *bctx;
 | 
			
		||||
 | 
			
		||||
	if (pdu_type == BSSGP_PDUT_STATUS)
 | 
			
		||||
		/* Pass the message to the generic BSSGP parser, which handles
 | 
			
		||||
		 * STATUS message in either direction. */
 | 
			
		||||
		 * STATUS and RESET messages in either direction. */
 | 
			
		||||
		return bssgp_rcvmsg(msg);
 | 
			
		||||
 | 
			
		||||
	/* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
 | 
			
		||||
@@ -348,6 +389,23 @@ static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
 | 
			
		||||
		rc = bssgp_tlv_parse(&tp, budh->data, data_len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (pdu_type == BSSGP_PDUT_BVC_RESET) {
 | 
			
		||||
		rc = bssgp_rcvmsg(msg);
 | 
			
		||||
		if (ns_bvci != BVCI_SIGNALLING)
 | 
			
		||||
			return rc;
 | 
			
		||||
 | 
			
		||||
		if (TLVP_PRES_LEN(&tp, BSSGP_IE_CAUSE, 1))
 | 
			
		||||
			cause = (enum gprs_bssgp_cause)*TLVP_VAL(&tp, BSSGP_IE_CAUSE);
 | 
			
		||||
		else
 | 
			
		||||
			LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u BVC RESET without cause?!\n", nsei);
 | 
			
		||||
 | 
			
		||||
		rc = bssgp_tx_bvc_ptp_reset(nsei, cause);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u BVC PTP reset procedure failed: %d\n", nsei, rc);
 | 
			
		||||
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* look-up or create the BTS context for this BVC */
 | 
			
		||||
	bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
 | 
			
		||||
 | 
			
		||||
@@ -356,15 +414,14 @@ static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
 | 
			
		||||
	 && pdu_type != BSSGP_PDUT_BVC_UNBLOCK_ACK
 | 
			
		||||
	 && pdu_type != BSSGP_PDUT_PAGING_PS)
 | 
			
		||||
	{
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
 | 
			
		||||
			"type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
 | 
			
		||||
			pdu_type);
 | 
			
		||||
		LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU type %s for unknown BVCI\n",
 | 
			
		||||
		     nsei, ns_bvci, bssgp_pdu_str(pdu_type));
 | 
			
		||||
		return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bctx)
 | 
			
		||||
	{
 | 
			
		||||
		log_set_context(BSC_CTX_BVC, bctx);
 | 
			
		||||
		log_set_context(LOG_CTX_GB_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));
 | 
			
		||||
	}
 | 
			
		||||
@@ -503,6 +560,11 @@ static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
 | 
			
		||||
			LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n");
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case S_NS_ALIVE_EXP:
 | 
			
		||||
		LOGP(DPCU, LOGL_NOTICE, "Tns alive expired too often, "
 | 
			
		||||
			"re-starting RESET procedure\n");
 | 
			
		||||
		gprs_ns_reconnect(nssd->nsvc);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -527,19 +589,12 @@ static unsigned count_pdch(const struct gprs_rlcmac_bts *bts)
 | 
			
		||||
	return num_pdch;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t gprs_bssgp_max_leak_rate(unsigned cs, int num_pdch)
 | 
			
		||||
static uint32_t gprs_bssgp_max_leak_rate(GprsCodingScheme cs, int num_pdch)
 | 
			
		||||
{
 | 
			
		||||
	static const uint32_t max_lr_per_ts[4] = {
 | 
			
		||||
		20 * (1000 / 20), /* CS-1: 20 byte payload per 20ms */
 | 
			
		||||
		30 * (1000 / 20), /* CS-2: 30 byte payload per 20ms */
 | 
			
		||||
		36 * (1000 / 20), /* CS-3: 36 byte payload per 20ms */
 | 
			
		||||
		50 * (1000 / 20), /* CS-4: 50 byte payload per 20ms */
 | 
			
		||||
	};
 | 
			
		||||
	int bytes_per_rlc_block = cs.maxDataBlockBytes() * cs.numDataBlocks();
 | 
			
		||||
 | 
			
		||||
	if (cs > ARRAY_SIZE(max_lr_per_ts))
 | 
			
		||||
		cs = 1;
 | 
			
		||||
 | 
			
		||||
	return max_lr_per_ts[cs-1] * num_pdch;
 | 
			
		||||
	/* n byte payload per 20ms */
 | 
			
		||||
	return bytes_per_rlc_block * (1000 / 20) * num_pdch;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t compute_bucket_size(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
@@ -586,6 +641,71 @@ static uint32_t get_and_reset_avg_queue_delay(void)
 | 
			
		||||
	return avg_delay_ms;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_and_reset_measured_leak_rate(int *usage_by_1000, unsigned num_pdch)
 | 
			
		||||
{
 | 
			
		||||
	int rate; /* byte per second */
 | 
			
		||||
 | 
			
		||||
	if (the_pcu.queue_frames_sent == 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (the_pcu.queue_frames_recv == 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	*usage_by_1000 = the_pcu.queue_frames_recv * 1000 /
 | 
			
		||||
		the_pcu.queue_frames_sent;
 | 
			
		||||
 | 
			
		||||
	/* 20ms/num_pdch is the average RLC block duration, so the rate is
 | 
			
		||||
	 * calculated as:
 | 
			
		||||
	 * rate = bytes_recv / (block_dur * block_count) */
 | 
			
		||||
	rate = the_pcu.queue_bytes_recv * 1000 * num_pdch /
 | 
			
		||||
		(20 * the_pcu.queue_frames_recv);
 | 
			
		||||
 | 
			
		||||
	the_pcu.queue_frames_sent = 0;
 | 
			
		||||
	the_pcu.queue_bytes_recv = 0;
 | 
			
		||||
	the_pcu.queue_frames_recv = 0;
 | 
			
		||||
 | 
			
		||||
	return rate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GprsCodingScheme max_coding_scheme_dl(struct gprs_rlcmac_bts *bts)
 | 
			
		||||
{
 | 
			
		||||
	int num;
 | 
			
		||||
 | 
			
		||||
	if (bts->egprs_enabled) {
 | 
			
		||||
		if (!bts->cs_adj_enabled) {
 | 
			
		||||
			if (bts->initial_mcs_dl)
 | 
			
		||||
				num = bts->initial_mcs_dl;
 | 
			
		||||
			else
 | 
			
		||||
				num = 1;
 | 
			
		||||
		} else if (bts->max_mcs_dl) {
 | 
			
		||||
			num = bts->max_mcs_dl;
 | 
			
		||||
		} else {
 | 
			
		||||
			num = 9;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return GprsCodingScheme::getEgprsByNum(num);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!bts->cs_adj_enabled) {
 | 
			
		||||
		if (bts->initial_cs_dl)
 | 
			
		||||
			num = bts->initial_cs_dl;
 | 
			
		||||
		else if (bts->cs4)
 | 
			
		||||
			num = 4;
 | 
			
		||||
		else if (bts->cs3)
 | 
			
		||||
			num = 3;
 | 
			
		||||
		else if (bts->cs2)
 | 
			
		||||
			num = 2;
 | 
			
		||||
		else
 | 
			
		||||
			num = 1;
 | 
			
		||||
	} else if (bts->max_cs_dl) {
 | 
			
		||||
		num = bts->max_cs_dl;
 | 
			
		||||
	} else {
 | 
			
		||||
		num = 4;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return GprsCodingScheme::getGprsByNum(num);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_bssgp_tx_fc_bvc(void)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
@@ -595,7 +715,7 @@ int gprs_bssgp_tx_fc_bvc(void)
 | 
			
		||||
	uint32_t ms_leak_rate; /* oct/s */
 | 
			
		||||
	uint32_t avg_delay_ms;
 | 
			
		||||
	int num_pdch = -1;
 | 
			
		||||
	int max_cs_dl;
 | 
			
		||||
	GprsCodingScheme max_cs_dl;
 | 
			
		||||
 | 
			
		||||
	if (!the_pcu.bctx) {
 | 
			
		||||
		LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
 | 
			
		||||
@@ -603,27 +723,33 @@ int gprs_bssgp_tx_fc_bvc(void)
 | 
			
		||||
	}
 | 
			
		||||
	bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	if (bts->cs_adj_enabled) {
 | 
			
		||||
		max_cs_dl = bts->max_cs_dl;
 | 
			
		||||
		if (!max_cs_dl) {
 | 
			
		||||
			if (bts->cs4)
 | 
			
		||||
				max_cs_dl = 4;
 | 
			
		||||
			else if (bts->cs3)
 | 
			
		||||
				max_cs_dl = 3;
 | 
			
		||||
			else if (bts->cs2)
 | 
			
		||||
				max_cs_dl = 2;
 | 
			
		||||
			else
 | 
			
		||||
				max_cs_dl = 1;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		max_cs_dl = bts->initial_cs_dl;
 | 
			
		||||
	}
 | 
			
		||||
	max_cs_dl = max_coding_scheme_dl(bts);
 | 
			
		||||
 | 
			
		||||
	bucket_size = bts->fc_bvc_bucket_size;
 | 
			
		||||
	leak_rate = bts->fc_bvc_leak_rate;
 | 
			
		||||
	ms_bucket_size = bts->fc_ms_bucket_size;
 | 
			
		||||
	ms_leak_rate = bts->fc_ms_leak_rate;
 | 
			
		||||
 | 
			
		||||
	if (leak_rate == 0) {
 | 
			
		||||
		int meas_rate;
 | 
			
		||||
		int usage; /* in 0..1000 */
 | 
			
		||||
 | 
			
		||||
		if (num_pdch < 0)
 | 
			
		||||
			num_pdch = count_pdch(bts);
 | 
			
		||||
 | 
			
		||||
		meas_rate = get_and_reset_measured_leak_rate(&usage, num_pdch);
 | 
			
		||||
		if (meas_rate > 0) {
 | 
			
		||||
			leak_rate = gprs_bssgp_max_leak_rate(max_cs_dl, num_pdch);
 | 
			
		||||
			leak_rate =
 | 
			
		||||
				(meas_rate * usage + leak_rate * (1000 - usage)) /
 | 
			
		||||
				1000;
 | 
			
		||||
			LOGP(DBSSGP, LOGL_DEBUG,
 | 
			
		||||
				"Estimated BVC leak rate = %d "
 | 
			
		||||
				"(measured %d, usage %d%%)\n",
 | 
			
		||||
				leak_rate, meas_rate, usage/10);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (leak_rate == 0) {
 | 
			
		||||
		if (num_pdch < 0)
 | 
			
		||||
			num_pdch = count_pdch(bts);
 | 
			
		||||
@@ -631,8 +757,8 @@ int gprs_bssgp_tx_fc_bvc(void)
 | 
			
		||||
		leak_rate = gprs_bssgp_max_leak_rate(max_cs_dl, num_pdch);
 | 
			
		||||
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG,
 | 
			
		||||
			"Computed BVC leak rate = %d, num_pdch = %d, cs = %d\n",
 | 
			
		||||
			leak_rate, num_pdch, max_cs_dl);
 | 
			
		||||
			"Computed BVC leak rate = %d, num_pdch = %d, cs = %s\n",
 | 
			
		||||
			leak_rate, num_pdch, max_cs_dl.name());
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (ms_leak_rate == 0) {
 | 
			
		||||
@@ -654,8 +780,9 @@ int gprs_bssgp_tx_fc_bvc(void)
 | 
			
		||||
		 * should be derived from the max number of PDCH TS per TRX.
 | 
			
		||||
		 */
 | 
			
		||||
		LOGP(DBSSGP, LOGL_DEBUG,
 | 
			
		||||
			"Computed MS default leak rate = %d, ms_num_pdch = %d, cs = %d\n",
 | 
			
		||||
			ms_leak_rate, ms_num_pdch, max_cs_dl);
 | 
			
		||||
			"Computed MS default leak rate = %d, ms_num_pdch = %d, "
 | 
			
		||||
			"cs = %s\n",
 | 
			
		||||
			ms_leak_rate, ms_num_pdch, max_cs_dl.name());
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* TODO: Force leak_rate to 0 on buffer bloat */
 | 
			
		||||
@@ -723,20 +850,41 @@ static void bvc_timeout(void *_priv)
 | 
			
		||||
	osmo_timer_schedule(&the_pcu.bvc_timer, the_pcu.bts->fc_interval, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_ns_reconnect(struct gprs_nsvc *nsvc)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_nsvc *nsvc2;
 | 
			
		||||
 | 
			
		||||
	if (!bssgp_nsi) {
 | 
			
		||||
		LOGP(DBSSGP, LOGL_ERROR, "NS instance does not exist\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (nsvc != the_pcu.nsvc) {
 | 
			
		||||
		LOGP(DBSSGP, LOGL_ERROR, "NSVC is invalid\n");
 | 
			
		||||
		return -EBADF;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nsvc2 = gprs_ns_nsip_connect(bssgp_nsi, &nsvc->ip.bts_addr,
 | 
			
		||||
		nsvc->nsei, nsvc->nsvci);
 | 
			
		||||
 | 
			
		||||
	if (!nsvc2) {
 | 
			
		||||
		LOGP(DBSSGP, LOGL_ERROR, "Failed to reconnect NSVC\n");
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* create BSSGP/NS layer instances */
 | 
			
		||||
struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	uint16_t local_port, uint32_t sgsn_ip,
 | 
			
		||||
	uint16_t sgsn_port, uint16_t nsei, uint16_t nsvci, uint16_t bvci,
 | 
			
		||||
	uint16_t mcc, uint16_t mnc, uint16_t lac, uint16_t rac,
 | 
			
		||||
	uint16_t mcc, uint16_t mnc, bool mnc_3_digits, uint16_t lac, uint16_t rac,
 | 
			
		||||
	uint16_t cell_id)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in dest;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	mcc = ((mcc & 0xf00) >> 8) * 100 + ((mcc & 0x0f0) >> 4) * 10 + (mcc & 0x00f);
 | 
			
		||||
	mnc = ((mnc & 0xf00) >> 8) * 100 + ((mnc & 0x0f0) >> 4) * 10 + (mnc & 0x00f);
 | 
			
		||||
	cell_id = ntohs(cell_id);
 | 
			
		||||
 | 
			
		||||
	/* if already created... return the current address */
 | 
			
		||||
	if (the_pcu.bctx)
 | 
			
		||||
		return &the_pcu;
 | 
			
		||||
@@ -749,6 +897,8 @@ struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	gprs_ns_vty_init(bssgp_nsi);
 | 
			
		||||
	bssgp_nsi->nsip.remote_port = sgsn_port;
 | 
			
		||||
	bssgp_nsi->nsip.remote_ip = sgsn_ip;
 | 
			
		||||
	bssgp_nsi->nsip.local_port = local_port;
 | 
			
		||||
	rc = gprs_ns_nsip_listen(bssgp_nsi);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
@@ -779,7 +929,13 @@ struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	the_pcu.bctx->ra_id.mcc = spoof_mcc ? : mcc;
 | 
			
		||||
	the_pcu.bctx->ra_id.mnc = spoof_mnc ? : mnc;
 | 
			
		||||
	if (spoof_mnc) {
 | 
			
		||||
		the_pcu.bctx->ra_id.mnc = spoof_mnc;
 | 
			
		||||
		the_pcu.bctx->ra_id.mnc_3_digits = spoof_mnc_3_digits;
 | 
			
		||||
	} else {
 | 
			
		||||
		the_pcu.bctx->ra_id.mnc = mnc;
 | 
			
		||||
		the_pcu.bctx->ra_id.mnc_3_digits = mnc_3_digits;
 | 
			
		||||
	}
 | 
			
		||||
	the_pcu.bctx->ra_id.lac = lac;
 | 
			
		||||
	the_pcu.bctx->ra_id.rac = rac;
 | 
			
		||||
	the_pcu.bctx->cell_id = cell_id;
 | 
			
		||||
@@ -794,28 +950,33 @@ struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts
 | 
			
		||||
 | 
			
		||||
void gprs_bssgp_destroy(void)
 | 
			
		||||
{
 | 
			
		||||
	if (!bssgp_nsi)
 | 
			
		||||
	struct gprs_ns_inst *nsi = bssgp_nsi;
 | 
			
		||||
	if (!nsi)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	bssgp_nsi = NULL;
 | 
			
		||||
 | 
			
		||||
	osmo_timer_del(&the_pcu.bvc_timer);
 | 
			
		||||
 | 
			
		||||
	osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL);
 | 
			
		||||
 | 
			
		||||
	the_pcu.nsvc = NULL;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: move this to libgb: btsctx_free() */
 | 
			
		||||
	llist_del(&the_pcu.bctx->list);
 | 
			
		||||
	talloc_free(the_pcu.bctx);
 | 
			
		||||
	the_pcu.bctx = NULL;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: blocking... */
 | 
			
		||||
	the_pcu.nsvc_unblocked = 0;
 | 
			
		||||
	the_pcu.bvc_sig_reset = 0;
 | 
			
		||||
	the_pcu.bvc_reset = 0;
 | 
			
		||||
	the_pcu.bvc_unblocked = 0;	
 | 
			
		||||
 | 
			
		||||
	gprs_ns_destroy(bssgp_nsi);
 | 
			
		||||
	bssgp_nsi = NULL;
 | 
			
		||||
	gprs_ns_destroy(nsi);
 | 
			
		||||
 | 
			
		||||
	/* FIXME: move this to libgb: btsctx_free() */
 | 
			
		||||
	llist_del(&the_pcu.bctx->list);
 | 
			
		||||
#warning "This causes ASAN to complain. It is not critical for normal operation but should be fixed nevertheless"
 | 
			
		||||
#if 0
 | 
			
		||||
	talloc_free(the_pcu.bctx);
 | 
			
		||||
#endif
 | 
			
		||||
	the_pcu.bctx = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void)
 | 
			
		||||
@@ -823,6 +984,17 @@ struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void)
 | 
			
		||||
	return the_pcu.bctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_bssgp_update_frames_sent()
 | 
			
		||||
{
 | 
			
		||||
	the_pcu.queue_frames_sent += 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_bssgp_update_bytes_received(unsigned bytes_recv, unsigned frames_recv)
 | 
			
		||||
{
 | 
			
		||||
	the_pcu.queue_bytes_recv += bytes_recv;
 | 
			
		||||
	the_pcu.queue_frames_recv += frames_recv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_bssgp_update_queue_delay(const struct timeval *tv_recv,
 | 
			
		||||
	const struct timeval *tv_now)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -42,8 +42,6 @@ struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
 | 
			
		||||
#define NS_HDR_LEN 4
 | 
			
		||||
#define IE_LLC_PDU 14
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
 | 
			
		||||
struct gprs_bssgp_pcu {
 | 
			
		||||
	struct gprs_nsvc *nsvc;
 | 
			
		||||
	struct bssgp_bvc_ctx *bctx;
 | 
			
		||||
@@ -62,6 +60,9 @@ struct gprs_bssgp_pcu {
 | 
			
		||||
	struct timeval queue_delay_sum;
 | 
			
		||||
	unsigned queue_delay_count;
 | 
			
		||||
	uint8_t fc_tag;
 | 
			
		||||
	unsigned queue_frames_sent;
 | 
			
		||||
	unsigned queue_bytes_recv;
 | 
			
		||||
	unsigned queue_frames_recv;
 | 
			
		||||
 | 
			
		||||
	/** callbacks below */
 | 
			
		||||
 | 
			
		||||
@@ -76,14 +77,17 @@ struct gprs_bssgp_pcu {
 | 
			
		||||
struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
		uint16_t local_port,
 | 
			
		||||
		uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
 | 
			
		||||
		uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc,
 | 
			
		||||
		uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, bool mnc_3_digits,
 | 
			
		||||
		uint16_t lac, uint16_t rac, uint16_t cell_id);
 | 
			
		||||
 | 
			
		||||
void gprs_bssgp_destroy(void);
 | 
			
		||||
int gprs_ns_reconnect(struct gprs_nsvc *nsvc);
 | 
			
		||||
 | 
			
		||||
struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void);
 | 
			
		||||
 | 
			
		||||
void gprs_bssgp_update_queue_delay(const struct timeval *tv_recv,
 | 
			
		||||
		const struct timeval *tv_now);
 | 
			
		||||
void gprs_bssgp_update_frames_sent();
 | 
			
		||||
void gprs_bssgp_update_bytes_received(unsigned bytes_recv, unsigned frames_recv);
 | 
			
		||||
 | 
			
		||||
#endif // GPRS_BSSGP_PCU_H
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										318
									
								
								src/gprs_coding_scheme.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								src/gprs_coding_scheme.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,318 @@
 | 
			
		||||
/* gprs_coding_scheme.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
 | 
			
		||||
 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "gprs_coding_scheme.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 44.060 Table 8.1.1.1 and Table 8.1.1.2
 | 
			
		||||
 * It has 3 level indexing. 0th level is ARQ type
 | 
			
		||||
 * 1st level is Original MCS( index 0 corresponds to MCS1 and so on)
 | 
			
		||||
 * 2nd level is MS MCS (index 0 corresponds to MCS1 and so on)
 | 
			
		||||
 */
 | 
			
		||||
enum GprsCodingScheme::Scheme GprsCodingScheme::egprs_mcs_retx_tbl[MAX_NUM_ARQ]
 | 
			
		||||
			[MAX_NUM_MCS][MAX_NUM_MCS] = {
 | 
			
		||||
		{
 | 
			
		||||
			{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}
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			{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}
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	const char *name;
 | 
			
		||||
	GprsCodingScheme::HeaderType data_hdr;
 | 
			
		||||
	GprsCodingScheme::Family family;
 | 
			
		||||
} mcs_info[GprsCodingScheme::NUM_SCHEMES] = {
 | 
			
		||||
	{{0, 0},   {0, 0},    0,  0, "UNKNOWN",
 | 
			
		||||
		GprsCodingScheme::HEADER_INVALID, GprsCodingScheme::FAMILY_INVALID},
 | 
			
		||||
	{{23, 0},  {23, 0},  20,  0, "CS-1",
 | 
			
		||||
		GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
 | 
			
		||||
	{{33, 7},  {33, 7},  30,  0, "CS-2",
 | 
			
		||||
		GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
 | 
			
		||||
	{{39, 3},  {39, 3},  36,  0, "CS-3",
 | 
			
		||||
		GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
 | 
			
		||||
	{{53, 7},  {53, 7},  50,  0, "CS-4",
 | 
			
		||||
		GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
 | 
			
		||||
 | 
			
		||||
	{{26, 1},  {26, 1},  22,  0, "MCS-1",
 | 
			
		||||
		GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_C},
 | 
			
		||||
	{{32, 1},  {32, 1},  28,  0, "MCS-2",
 | 
			
		||||
		GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_B},
 | 
			
		||||
	{{41, 1},  {41, 1},  37, 48, "MCS-3",
 | 
			
		||||
		GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_A},
 | 
			
		||||
	{{48, 1},  {48, 1},  44,  0, "MCS-4",
 | 
			
		||||
		GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_C},
 | 
			
		||||
 | 
			
		||||
	{{60, 7},  {59, 6},  56,  0, "MCS-5",
 | 
			
		||||
		GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2, GprsCodingScheme::FAMILY_B},
 | 
			
		||||
	{{78, 7},  {77, 6},  74, 48, "MCS-6",
 | 
			
		||||
		GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2, GprsCodingScheme::FAMILY_A},
 | 
			
		||||
	{{118, 2}, {117, 4}, 56,  0, "MCS-7",
 | 
			
		||||
		GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_B},
 | 
			
		||||
	{{142, 2}, {141, 4}, 68,  0, "MCS-8",
 | 
			
		||||
		GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_A},
 | 
			
		||||
	{{154, 2}, {153, 4}, 74,  0, "MCS-9",
 | 
			
		||||
		GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_A},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct {
 | 
			
		||||
	struct {
 | 
			
		||||
		uint8_t data_header_bits;
 | 
			
		||||
	} uplink, downlink;
 | 
			
		||||
	uint8_t data_block_header_bits;
 | 
			
		||||
	uint8_t num_blocks;
 | 
			
		||||
	const char *name;
 | 
			
		||||
} hdr_type_info[GprsCodingScheme::NUM_HEADER_TYPES] = {
 | 
			
		||||
	{{0},       {0},       0, 0, "INVALID"},
 | 
			
		||||
	{{1*8 + 0}, {1*8 + 0}, 0, 0, "CONTROL"},
 | 
			
		||||
	{{3*8 + 0}, {3*8 + 0}, 0, 1, "GPRS_DATA"},
 | 
			
		||||
	{{5*8 + 6}, {5*8 + 0}, 2, 2, "EGPRS_DATA_TYPE1"},
 | 
			
		||||
	{{4*8 + 5}, {3*8 + 4}, 2, 1, "EGPRS_DATA_TYPE2"},
 | 
			
		||||
	{{3*8 + 7}, {3*8 + 7}, 2, 1, "EGPRS_DATA_TYPE3"},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GprsCodingScheme GprsCodingScheme::getBySizeUL(unsigned size)
 | 
			
		||||
{
 | 
			
		||||
	switch (size) {
 | 
			
		||||
		case 23: return GprsCodingScheme(CS1);
 | 
			
		||||
		case 27: return GprsCodingScheme(MCS1);
 | 
			
		||||
		case 33: return GprsCodingScheme(MCS2);
 | 
			
		||||
		case 34: return GprsCodingScheme(CS2);
 | 
			
		||||
		case 40: return GprsCodingScheme(CS3);
 | 
			
		||||
		case 42: return GprsCodingScheme(MCS3);
 | 
			
		||||
		case 49: return GprsCodingScheme(MCS4);
 | 
			
		||||
		case 54: return GprsCodingScheme(CS4);
 | 
			
		||||
		case 61: return GprsCodingScheme(MCS5);
 | 
			
		||||
		case 79: return GprsCodingScheme(MCS6);
 | 
			
		||||
		case 119: return GprsCodingScheme(MCS7);
 | 
			
		||||
		case 143: return GprsCodingScheme(MCS8);
 | 
			
		||||
		case 155: return GprsCodingScheme(MCS9);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return GprsCodingScheme(UNKNOWN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::sizeUL() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].uplink.bytes + (spareBitsUL() ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::usedSizeUL() const
 | 
			
		||||
{
 | 
			
		||||
	if (mcs_info[m_scheme].data_hdr == HEADER_GPRS_DATA)
 | 
			
		||||
		return mcs_info[m_scheme].uplink.bytes;
 | 
			
		||||
	else
 | 
			
		||||
		return sizeUL();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::maxBytesUL() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].uplink.bytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::spareBitsUL() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].uplink.ext_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::sizeDL() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].downlink.bytes + (spareBitsDL() ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::usedSizeDL() const
 | 
			
		||||
{
 | 
			
		||||
	if (mcs_info[m_scheme].data_hdr == HEADER_GPRS_DATA)
 | 
			
		||||
		return mcs_info[m_scheme].downlink.bytes;
 | 
			
		||||
	else
 | 
			
		||||
		return sizeDL();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::maxBytesDL() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].downlink.bytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::spareBitsDL() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].downlink.ext_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::maxDataBlockBytes() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].data_bytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::optionalPaddingBits() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].optional_padding_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::numDataBlocks() const
 | 
			
		||||
{
 | 
			
		||||
	return hdr_type_info[headerTypeData()].num_blocks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::numDataHeaderBitsUL() const
 | 
			
		||||
{
 | 
			
		||||
	return hdr_type_info[headerTypeData()].uplink.data_header_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::numDataHeaderBitsDL() const
 | 
			
		||||
{
 | 
			
		||||
	return hdr_type_info[headerTypeData()].downlink.data_header_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsCodingScheme::numDataBlockHeaderBits() const
 | 
			
		||||
{
 | 
			
		||||
	return hdr_type_info[headerTypeData()].data_block_header_bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *GprsCodingScheme::name() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GprsCodingScheme::HeaderType GprsCodingScheme::headerTypeData() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].data_hdr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GprsCodingScheme::Family GprsCodingScheme::family() const
 | 
			
		||||
{
 | 
			
		||||
	return mcs_info[m_scheme].family;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsCodingScheme::inc(Mode mode)
 | 
			
		||||
{
 | 
			
		||||
	if (!isCompatible(mode))
 | 
			
		||||
		/* This should not happen. TODO: Use assert? */
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	Scheme new_cs(Scheme(m_scheme + 1));
 | 
			
		||||
	if (!GprsCodingScheme(new_cs).isCompatible(mode))
 | 
			
		||||
		/* Clipping, do not change the value */
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	m_scheme = new_cs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsCodingScheme::dec(Mode mode)
 | 
			
		||||
{
 | 
			
		||||
	if (!isCompatible(mode))
 | 
			
		||||
		/* This should not happen. TODO: Use assert? */
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	Scheme new_cs(Scheme(m_scheme - 1));
 | 
			
		||||
	if (!GprsCodingScheme(new_cs).isCompatible(mode))
 | 
			
		||||
		/* Clipping, do not change the value */
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	m_scheme = new_cs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsCodingScheme::inc()
 | 
			
		||||
{
 | 
			
		||||
	if (isGprs() && m_scheme == CS4)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (isEgprs() && m_scheme == MCS9)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!isValid())
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	m_scheme = Scheme(m_scheme + 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsCodingScheme::dec()
 | 
			
		||||
{
 | 
			
		||||
	if (isGprs() && m_scheme == CS1)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (isEgprs() && m_scheme == MCS1)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!isValid())
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	m_scheme = Scheme(m_scheme - 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *GprsCodingScheme::modeName(Mode mode)
 | 
			
		||||
{
 | 
			
		||||
	switch (mode) {
 | 
			
		||||
	case GPRS:       return "GPRS";
 | 
			
		||||
	case EGPRS_GMSK: return "EGPRS_GMSK-only";
 | 
			
		||||
	case EGPRS:      return "EGPRS";
 | 
			
		||||
	default:         return "???";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GprsCodingScheme::isFamilyCompatible(GprsCodingScheme o) const
 | 
			
		||||
{
 | 
			
		||||
	if (*this == o)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	if (family() == FAMILY_INVALID)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return family() == o.family();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GprsCodingScheme::isCombinable(GprsCodingScheme o) const
 | 
			
		||||
{
 | 
			
		||||
	return numDataBlocks() == o.numDataBlocks();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsCodingScheme::decToSingleBlock(bool *needStuffing)
 | 
			
		||||
{
 | 
			
		||||
	switch (m_scheme) {
 | 
			
		||||
	case MCS7: *needStuffing = false; m_scheme = MCS5; break;
 | 
			
		||||
	case MCS8: *needStuffing =  true; m_scheme = MCS6; break;
 | 
			
		||||
	case MCS9: *needStuffing = false; m_scheme = MCS6; break;
 | 
			
		||||
	default:   *needStuffing = false; break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										247
									
								
								src/gprs_coding_scheme.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								src/gprs_coding_scheme.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,247 @@
 | 
			
		||||
/* gprs_coding_scheme.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
 | 
			
		||||
 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
	#include <osmocom/core/utils.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class GprsCodingScheme {
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
#define MAX_NUM_ARQ           2      /* max. number of ARQ */
 | 
			
		||||
#define MAX_NUM_MCS           9     /* max. number of MCS */
 | 
			
		||||
#define EGPRS_ARQ1            0x0
 | 
			
		||||
#define EGPRS_ARQ2            0x1
 | 
			
		||||
 | 
			
		||||
	enum Scheme {
 | 
			
		||||
		UNKNOWN,
 | 
			
		||||
		CS1, CS2, CS3, CS4,
 | 
			
		||||
		MCS1, MCS2, MCS3, MCS4,
 | 
			
		||||
		MCS5, MCS6, MCS7, MCS8, MCS9,
 | 
			
		||||
		NUM_SCHEMES
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum Mode {
 | 
			
		||||
		GPRS,
 | 
			
		||||
		EGPRS_GMSK,
 | 
			
		||||
		EGPRS,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum HeaderType {
 | 
			
		||||
		HEADER_INVALID,
 | 
			
		||||
		HEADER_GPRS_CONTROL,
 | 
			
		||||
		HEADER_GPRS_DATA,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_1,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_2,
 | 
			
		||||
		HEADER_EGPRS_DATA_TYPE_3,
 | 
			
		||||
		NUM_HEADER_TYPES
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum Family {
 | 
			
		||||
		FAMILY_INVALID,
 | 
			
		||||
		FAMILY_A,
 | 
			
		||||
		FAMILY_B,
 | 
			
		||||
		FAMILY_C,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	GprsCodingScheme(Scheme s = UNKNOWN);
 | 
			
		||||
 | 
			
		||||
	operator bool() const {return m_scheme != UNKNOWN;}
 | 
			
		||||
	operator Scheme() const {return m_scheme;}
 | 
			
		||||
	uint8_t to_num() const;
 | 
			
		||||
 | 
			
		||||
	GprsCodingScheme& operator =(Scheme s);
 | 
			
		||||
	bool operator == (Scheme s) const;
 | 
			
		||||
	GprsCodingScheme& operator =(GprsCodingScheme o);
 | 
			
		||||
 | 
			
		||||
	bool isValid()   const {return UNKNOWN <= m_scheme && m_scheme <= MCS9;}
 | 
			
		||||
	bool isGprs()   const {return CS1 <= m_scheme && m_scheme <= CS4;}
 | 
			
		||||
	bool isEgprs()  const {return m_scheme >= MCS1;}
 | 
			
		||||
	bool isEgprsGmsk()  const {return isEgprs() && m_scheme <= MCS4;}
 | 
			
		||||
	bool isCompatible(Mode mode) const;
 | 
			
		||||
	bool isCompatible(GprsCodingScheme o) const;
 | 
			
		||||
	bool isFamilyCompatible(GprsCodingScheme o) const;
 | 
			
		||||
	bool isCombinable(GprsCodingScheme o) const;
 | 
			
		||||
 | 
			
		||||
	void inc(Mode mode);
 | 
			
		||||
	void dec(Mode mode);
 | 
			
		||||
	void inc();
 | 
			
		||||
	void dec();
 | 
			
		||||
	void decToSingleBlock(bool *needStuffing);
 | 
			
		||||
 | 
			
		||||
	uint8_t sizeUL() const;
 | 
			
		||||
	uint8_t sizeDL() const;
 | 
			
		||||
	uint8_t usedSizeUL() const;
 | 
			
		||||
	uint8_t usedSizeDL() const;
 | 
			
		||||
	uint8_t maxBytesUL() const;
 | 
			
		||||
	uint8_t maxBytesDL() const;
 | 
			
		||||
	uint8_t spareBitsUL() const;
 | 
			
		||||
	uint8_t spareBitsDL() const;
 | 
			
		||||
	uint8_t maxDataBlockBytes() const;
 | 
			
		||||
	uint8_t numDataBlocks() const;
 | 
			
		||||
	uint8_t numDataHeaderBitsUL() const;
 | 
			
		||||
	uint8_t numDataHeaderBitsDL() const;
 | 
			
		||||
	uint8_t numDataBlockHeaderBits() const;
 | 
			
		||||
	uint8_t optionalPaddingBits() const;
 | 
			
		||||
	const char *name() const;
 | 
			
		||||
	HeaderType headerTypeData() const;
 | 
			
		||||
	HeaderType headerTypeControl() const;
 | 
			
		||||
	Family family() const;
 | 
			
		||||
 | 
			
		||||
	static GprsCodingScheme getBySizeUL(unsigned size);
 | 
			
		||||
	static GprsCodingScheme getGprsByNum(unsigned num);
 | 
			
		||||
	static GprsCodingScheme getEgprsByNum(unsigned num);
 | 
			
		||||
 | 
			
		||||
	static const char *modeName(Mode mode);
 | 
			
		||||
	static Scheme get_retx_mcs(const GprsCodingScheme mcs,
 | 
			
		||||
				const GprsCodingScheme retx_mcs,
 | 
			
		||||
				const unsigned arq_type);
 | 
			
		||||
 | 
			
		||||
	static enum Scheme egprs_mcs_retx_tbl[MAX_NUM_ARQ]
 | 
			
		||||
			[MAX_NUM_MCS][MAX_NUM_MCS];
 | 
			
		||||
private:
 | 
			
		||||
	GprsCodingScheme(int s); /* fail on use */
 | 
			
		||||
	GprsCodingScheme& operator =(int s); /* fail on use */
 | 
			
		||||
	enum Scheme m_scheme;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
inline uint8_t GprsCodingScheme::to_num() const
 | 
			
		||||
{
 | 
			
		||||
	if (isGprs())
 | 
			
		||||
		return (m_scheme - CS1) + 1;
 | 
			
		||||
 | 
			
		||||
	if (isEgprs())
 | 
			
		||||
		return (m_scheme - MCS1) + 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool GprsCodingScheme::isCompatible(Mode mode) const
 | 
			
		||||
{
 | 
			
		||||
	switch (mode) {
 | 
			
		||||
	case GPRS: return isGprs();
 | 
			
		||||
	case EGPRS_GMSK: return isEgprsGmsk();
 | 
			
		||||
	case EGPRS: return isEgprs();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool GprsCodingScheme::isCompatible(GprsCodingScheme o) const
 | 
			
		||||
{
 | 
			
		||||
	return (isGprs() && o.isGprs()) || (isEgprs() && o.isEgprs());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline GprsCodingScheme::HeaderType GprsCodingScheme::headerTypeControl() const
 | 
			
		||||
{
 | 
			
		||||
	return HEADER_GPRS_CONTROL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline GprsCodingScheme::GprsCodingScheme(Scheme s)
 | 
			
		||||
	: m_scheme(s)
 | 
			
		||||
{
 | 
			
		||||
	if (!isValid())
 | 
			
		||||
		m_scheme = UNKNOWN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline GprsCodingScheme& GprsCodingScheme::operator =(Scheme s)
 | 
			
		||||
{
 | 
			
		||||
	m_scheme = s;
 | 
			
		||||
 | 
			
		||||
	if (!isValid())
 | 
			
		||||
		m_scheme = UNKNOWN;
 | 
			
		||||
 | 
			
		||||
	return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline GprsCodingScheme& GprsCodingScheme::operator =(GprsCodingScheme o)
 | 
			
		||||
{
 | 
			
		||||
	m_scheme = o.m_scheme;
 | 
			
		||||
	return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline GprsCodingScheme GprsCodingScheme::getGprsByNum(unsigned num)
 | 
			
		||||
{
 | 
			
		||||
	if (num < 1 || num > 4)
 | 
			
		||||
		return GprsCodingScheme();
 | 
			
		||||
 | 
			
		||||
	return GprsCodingScheme(Scheme(CS1 + (num - 1)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline GprsCodingScheme GprsCodingScheme::getEgprsByNum(unsigned num)
 | 
			
		||||
{
 | 
			
		||||
	if (num < 1 || num > 9)
 | 
			
		||||
		return GprsCodingScheme();
 | 
			
		||||
 | 
			
		||||
	return GprsCodingScheme(Scheme(MCS1 + (num - 1)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* The coding schemes form a partial ordering */
 | 
			
		||||
inline bool operator ==(GprsCodingScheme a, GprsCodingScheme b)
 | 
			
		||||
{
 | 
			
		||||
	return GprsCodingScheme::Scheme(a) == GprsCodingScheme::Scheme(b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool GprsCodingScheme::operator == (Scheme scheme) const
 | 
			
		||||
{
 | 
			
		||||
	return this->m_scheme == scheme;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator !=(GprsCodingScheme a, GprsCodingScheme b)
 | 
			
		||||
{
 | 
			
		||||
	return !(a == b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator <(GprsCodingScheme a, GprsCodingScheme b)
 | 
			
		||||
{
 | 
			
		||||
	return a.isCompatible(b) &&
 | 
			
		||||
		GprsCodingScheme::Scheme(a) < GprsCodingScheme::Scheme(b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator >(GprsCodingScheme a, GprsCodingScheme b)
 | 
			
		||||
{
 | 
			
		||||
	return b < a;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator <=(GprsCodingScheme a, GprsCodingScheme b)
 | 
			
		||||
{
 | 
			
		||||
	return a == b || a < b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator >=(GprsCodingScheme a, GprsCodingScheme b)
 | 
			
		||||
{
 | 
			
		||||
	return a == b || a > b;
 | 
			
		||||
}
 | 
			
		||||
inline GprsCodingScheme::Scheme GprsCodingScheme::get_retx_mcs(
 | 
			
		||||
				const GprsCodingScheme mcs,
 | 
			
		||||
				const GprsCodingScheme demanded_mcs,
 | 
			
		||||
				const unsigned arq_type)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(mcs.to_num() > 0);
 | 
			
		||||
	OSMO_ASSERT(demanded_mcs.to_num() > 0);
 | 
			
		||||
 | 
			
		||||
	return egprs_mcs_retx_tbl[arq_type][mcs.to_num() - 1]
 | 
			
		||||
			[demanded_mcs.to_num() - 1];
 | 
			
		||||
}
 | 
			
		||||
@@ -16,16 +16,7 @@
 | 
			
		||||
 * 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>
 | 
			
		||||
@@ -41,31 +32,28 @@ static const struct log_info_cat default_categories[] = {
 | 
			
		||||
	{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Uplink (RLCMAC)", LOGL_NOTICE, 1},
 | 
			
		||||
	{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Scheduling (RLCMAC)", LOGL_NOTICE, 1},
 | 
			
		||||
	{"DRLCMACMEAS", "\033[1;31m", "GPRS RLC/MAC layer Measurements (RLCMAC)", LOGL_INFO, 1},
 | 
			
		||||
	{"DTBF","\033[1;34m", "Temporary Block Flow (TBF)", LOGL_INFO , 1},
 | 
			
		||||
	{"DTBFDL","\033[1;34m", "Temporary Block Flow (TBF) Downlink", LOGL_INFO , 1},
 | 
			
		||||
	{"DTBFUL","\033[1;34m", "Temporary Block Flow (TBF) Uplink", LOGL_INFO , 1},
 | 
			
		||||
	{"DNS","\033[1;34m", "GPRS Network Service Protocol (NS)", LOGL_INFO , 1},
 | 
			
		||||
	{"DBSSGP","\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO , 1},
 | 
			
		||||
	{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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];
 | 
			
		||||
	const struct gprs_nsvc *nsvc = (const struct gprs_nsvc*)ctx->ctx[LOG_CTX_GB_NSVC];
 | 
			
		||||
	const struct gprs_nsvc *bvc = (const struct gprs_nsvc*)ctx->ctx[LOG_CTX_GB_BVC];
 | 
			
		||||
 | 
			
		||||
	/* Filter on the NS Virtual Connection */
 | 
			
		||||
	if ((tar->filter_map & (1 << FLT_NSVC)) != 0
 | 
			
		||||
	    && nsvc && (nsvc == tar->filter_data[FLT_NSVC]))
 | 
			
		||||
	if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0
 | 
			
		||||
	    && nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC]))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	/* Filter on the BVC */
 | 
			
		||||
	if ((tar->filter_map & (1 << FLT_BVC)) != 0
 | 
			
		||||
	    && bvc && (bvc == tar->filter_data[FLT_BVC]))
 | 
			
		||||
	if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0
 | 
			
		||||
	    && bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC]))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -16,19 +16,17 @@
 | 
			
		||||
 * 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" {
 | 
			
		||||
#endif
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Debug Areas of the code */
 | 
			
		||||
enum {
 | 
			
		||||
	DCSN1,
 | 
			
		||||
@@ -39,38 +37,13 @@ enum {
 | 
			
		||||
	DRLCMACUL,
 | 
			
		||||
	DRLCMACSCHED,
 | 
			
		||||
	DRLCMACMEAS,
 | 
			
		||||
	DTBF,
 | 
			
		||||
	DTBFDL,
 | 
			
		||||
	DTBFUL,
 | 
			
		||||
	DNS,
 | 
			
		||||
	DBSSGP,
 | 
			
		||||
	DPCU,
 | 
			
		||||
	aDebug_LastEntry
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* context */
 | 
			
		||||
#define BSC_CTX_SUBSCR	1
 | 
			
		||||
#define BSC_CTX_NSVC	4
 | 
			
		||||
#define BSC_CTX_BVC		5
 | 
			
		||||
 | 
			
		||||
/* target */
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	//DEBUG_FILTER_ALL = 1 << 0,
 | 
			
		||||
	LOG_FILTER_IMSI = 1 << 1,
 | 
			
		||||
	LOG_FILTER_NSVC = 1 << 2,
 | 
			
		||||
	LOG_FILTER_BVC  = 1 << 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* we don't need a header dependency for this... */
 | 
			
		||||
 | 
			
		||||
struct gprs_nsvc;
 | 
			
		||||
struct bssgp_bvc_ctx;
 | 
			
		||||
 | 
			
		||||
void log_set_imsi_filter(struct log_target *target, const char *imsi);
 | 
			
		||||
void log_set_nsvc_filter(struct log_target *target,
 | 
			
		||||
			 struct gprs_nsvc *nsvc);
 | 
			
		||||
void log_set_bvc_filter(struct log_target *target,
 | 
			
		||||
			struct bssgp_bvc_ctx *bctx);
 | 
			
		||||
 | 
			
		||||
extern const struct log_info gprs_log_info;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // GPRS_DEBUG_H
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										365
									
								
								src/gprs_ms.cpp
									
									
									
									
									
								
							
							
						
						
									
										365
									
								
								src/gprs_ms.cpp
									
									
									
									
									
								
							@@ -20,20 +20,23 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "gprs_ms.h"
 | 
			
		||||
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
#include "bts.h"
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
#include "gprs_debug.h"
 | 
			
		||||
#include "gprs_codel.h"
 | 
			
		||||
#include "pcu_utils.h"
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
	#include <osmocom/core/talloc.h>
 | 
			
		||||
	#include <osmocom/core/utils.h>
 | 
			
		||||
	#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
	#include <osmocom/core/logging.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GPRS_CODEL_SLOW_INTERVAL_MS 2000
 | 
			
		||||
#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
 | 
			
		||||
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
@@ -94,10 +97,9 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
 | 
			
		||||
	m_tlli(tlli),
 | 
			
		||||
	m_new_ul_tlli(0),
 | 
			
		||||
	m_new_dl_tlli(0),
 | 
			
		||||
	m_ta(0),
 | 
			
		||||
	m_ta(GSM48_TA_INVALID),
 | 
			
		||||
	m_ms_class(0),
 | 
			
		||||
	m_current_cs_ul(1),
 | 
			
		||||
	m_current_cs_dl(1),
 | 
			
		||||
	m_egprs_ms_class(0),
 | 
			
		||||
	m_is_idle(true),
 | 
			
		||||
	m_ref(0),
 | 
			
		||||
	m_list(this),
 | 
			
		||||
@@ -106,7 +108,9 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
 | 
			
		||||
	m_reserved_dl_slots(0),
 | 
			
		||||
	m_reserved_ul_slots(0),
 | 
			
		||||
	m_current_trx(NULL),
 | 
			
		||||
	m_codel_state(NULL)
 | 
			
		||||
	m_codel_state(NULL),
 | 
			
		||||
	m_mode(GprsCodingScheme::GPRS),
 | 
			
		||||
	m_dl_ctrl_msg(0)
 | 
			
		||||
{
 | 
			
		||||
	int codel_interval = LLC_CODEL_USE_DEFAULT;
 | 
			
		||||
 | 
			
		||||
@@ -116,17 +120,11 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
 | 
			
		||||
	memset(&m_timer, 0, sizeof(m_timer));
 | 
			
		||||
	m_timer.cb = GprsMs::timeout;
 | 
			
		||||
	m_llc_queue.init();
 | 
			
		||||
	if (m_bts) {
 | 
			
		||||
		m_current_cs_ul = m_bts->bts_data()->initial_cs_ul;
 | 
			
		||||
		if (m_current_cs_ul < 1)
 | 
			
		||||
			m_current_cs_ul = 1;
 | 
			
		||||
 | 
			
		||||
		m_current_cs_dl = m_bts->bts_data()->initial_cs_dl;
 | 
			
		||||
		if (m_current_cs_dl < 1)
 | 
			
		||||
			m_current_cs_dl = 1;
 | 
			
		||||
	set_mode(m_mode);
 | 
			
		||||
 | 
			
		||||
	if (m_bts)
 | 
			
		||||
		codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (codel_interval) {
 | 
			
		||||
		if (codel_interval == LLC_CODEL_USE_DEFAULT)
 | 
			
		||||
@@ -140,6 +138,8 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
 | 
			
		||||
 | 
			
		||||
GprsMs::~GprsMs()
 | 
			
		||||
{
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> *pos, *tmp;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
 | 
			
		||||
 | 
			
		||||
	set_reserved_slots(NULL, 0, 0);
 | 
			
		||||
@@ -156,6 +156,10 @@ GprsMs::~GprsMs()
 | 
			
		||||
		m_dl_tbf->set_ms(NULL);
 | 
			
		||||
		m_dl_tbf = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_for_each_safe(pos, tmp, &m_old_tbfs)
 | 
			
		||||
		pos->entry()->set_ms(NULL);
 | 
			
		||||
 | 
			
		||||
	m_llc_queue.clear(m_bts);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -208,12 +212,53 @@ void GprsMs::stop_timer()
 | 
			
		||||
	unref();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::set_mode(GprsCodingScheme::Mode mode)
 | 
			
		||||
{
 | 
			
		||||
	m_mode = mode;
 | 
			
		||||
 | 
			
		||||
	if (!m_bts)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	switch (m_mode) {
 | 
			
		||||
	case GprsCodingScheme::GPRS:
 | 
			
		||||
		if (!m_current_cs_ul.isGprs()) {
 | 
			
		||||
			m_current_cs_ul = GprsCodingScheme::getGprsByNum(
 | 
			
		||||
				m_bts->bts_data()->initial_cs_ul);
 | 
			
		||||
			if (!m_current_cs_ul.isValid())
 | 
			
		||||
				m_current_cs_ul = GprsCodingScheme::CS1;
 | 
			
		||||
		}
 | 
			
		||||
		if (!m_current_cs_dl.isGprs()) {
 | 
			
		||||
			m_current_cs_dl = GprsCodingScheme::getGprsByNum(
 | 
			
		||||
				m_bts->bts_data()->initial_cs_dl);
 | 
			
		||||
			if (!m_current_cs_dl.isValid())
 | 
			
		||||
				m_current_cs_dl = GprsCodingScheme::CS1;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case GprsCodingScheme::EGPRS_GMSK:
 | 
			
		||||
	case GprsCodingScheme::EGPRS:
 | 
			
		||||
		if (!m_current_cs_ul.isEgprs()) {
 | 
			
		||||
			m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
 | 
			
		||||
				m_bts->bts_data()->initial_mcs_ul);
 | 
			
		||||
			if (!m_current_cs_ul.isValid())
 | 
			
		||||
				m_current_cs_ul = GprsCodingScheme::MCS1;
 | 
			
		||||
		}
 | 
			
		||||
		if (!m_current_cs_dl.isEgprs()) {
 | 
			
		||||
			m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
 | 
			
		||||
				m_bts->bts_data()->initial_mcs_dl);
 | 
			
		||||
			if (!m_current_cs_dl.isValid())
 | 
			
		||||
				m_current_cs_dl = GprsCodingScheme::MCS1;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
 | 
			
		||||
{
 | 
			
		||||
	if (tbf->direction == GPRS_RLCMAC_DL_TBF)
 | 
			
		||||
		attach_dl_tbf(static_cast<gprs_rlcmac_dl_tbf *>(tbf));
 | 
			
		||||
		attach_dl_tbf(as_dl_tbf(tbf));
 | 
			
		||||
	else
 | 
			
		||||
		attach_ul_tbf(static_cast<gprs_rlcmac_ul_tbf *>(tbf));
 | 
			
		||||
		attach_ul_tbf(as_ul_tbf(tbf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
 | 
			
		||||
@@ -227,7 +272,7 @@ void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
 | 
			
		||||
	Guard guard(this);
 | 
			
		||||
 | 
			
		||||
	if (m_ul_tbf)
 | 
			
		||||
		detach_tbf(m_ul_tbf);
 | 
			
		||||
		llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
 | 
			
		||||
 | 
			
		||||
	m_ul_tbf = tbf;
 | 
			
		||||
 | 
			
		||||
@@ -246,7 +291,7 @@ void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
 | 
			
		||||
	Guard guard(this);
 | 
			
		||||
 | 
			
		||||
	if (m_dl_tbf)
 | 
			
		||||
		detach_tbf(m_dl_tbf);
 | 
			
		||||
		llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
 | 
			
		||||
 | 
			
		||||
	m_dl_tbf = tbf;
 | 
			
		||||
 | 
			
		||||
@@ -256,12 +301,26 @@ void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
 | 
			
		||||
 | 
			
		||||
void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
 | 
			
		||||
{
 | 
			
		||||
	if (m_ul_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf))
 | 
			
		||||
	if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
 | 
			
		||||
		m_ul_tbf = NULL;
 | 
			
		||||
	else if (m_dl_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf))
 | 
			
		||||
	} else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
 | 
			
		||||
		m_dl_tbf = NULL;
 | 
			
		||||
	else
 | 
			
		||||
		return;
 | 
			
		||||
	} else {
 | 
			
		||||
		bool found = false;
 | 
			
		||||
 | 
			
		||||
		LListHead<gprs_rlcmac_tbf> *pos, *tmp;
 | 
			
		||||
		llist_for_each_safe(pos, tmp, &m_old_tbfs) {
 | 
			
		||||
			if (pos->entry() == tbf) {
 | 
			
		||||
				llist_del(pos);
 | 
			
		||||
				found = true;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Protect against recursive calls via set_ms() */
 | 
			
		||||
		if (!found)
 | 
			
		||||
			return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
 | 
			
		||||
		tlli(), tbf->name());
 | 
			
		||||
@@ -297,6 +356,36 @@ void GprsMs::update_status()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::reset()
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_INFO,
 | 
			
		||||
		"Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
 | 
			
		||||
		tlli(), imsi());
 | 
			
		||||
 | 
			
		||||
	stop_timer();
 | 
			
		||||
 | 
			
		||||
	m_tlli = 0;
 | 
			
		||||
	m_new_dl_tlli = 0;
 | 
			
		||||
	m_new_ul_tlli = 0;
 | 
			
		||||
	m_imsi[0] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::merge_old_ms(GprsMs *old_ms)
 | 
			
		||||
{
 | 
			
		||||
	if (old_ms == this)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
 | 
			
		||||
		set_imsi(old_ms->imsi());
 | 
			
		||||
 | 
			
		||||
	if (!ms_class() && old_ms->ms_class())
 | 
			
		||||
		set_ms_class(old_ms->ms_class());
 | 
			
		||||
 | 
			
		||||
	m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
 | 
			
		||||
 | 
			
		||||
	old_ms->reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::set_tlli(uint32_t tlli)
 | 
			
		||||
{
 | 
			
		||||
	if (tlli == m_tlli || tlli == m_new_ul_tlli)
 | 
			
		||||
@@ -378,11 +467,15 @@ void GprsMs::set_ta(uint8_t ta_)
 | 
			
		||||
	if (ta_ == m_ta)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_INFO,
 | 
			
		||||
		"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
 | 
			
		||||
		tlli(), m_ta, ta_);
 | 
			
		||||
 | 
			
		||||
	m_ta = ta_;
 | 
			
		||||
	if (gsm48_ta_is_valid(ta_)) {
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_INFO,
 | 
			
		||||
		     "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
 | 
			
		||||
		     tlli(), m_ta, ta_);
 | 
			
		||||
		m_ta = ta_;
 | 
			
		||||
	} else
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_NOTICE,
 | 
			
		||||
		     "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
 | 
			
		||||
		     "value %d kept)\n", tlli(), ta_, m_ta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::set_ms_class(uint8_t ms_class_)
 | 
			
		||||
@@ -397,13 +490,25 @@ void GprsMs::set_ms_class(uint8_t ms_class_)
 | 
			
		||||
	m_ms_class = ms_class_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
 | 
			
		||||
{
 | 
			
		||||
	if (ms_class_ == m_egprs_ms_class)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_INFO,
 | 
			
		||||
		"Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
 | 
			
		||||
		tlli(), m_egprs_ms_class, ms_class_);
 | 
			
		||||
 | 
			
		||||
	m_egprs_ms_class = ms_class_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts_data;
 | 
			
		||||
	int64_t now;
 | 
			
		||||
	uint8_t max_cs_dl = 4;
 | 
			
		||||
	GprsCodingScheme max_cs_dl = this->max_cs_dl();
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(m_bts != NULL);
 | 
			
		||||
	OSMO_ASSERT(max_cs_dl);
 | 
			
		||||
	bts_data = m_bts->bts_data();
 | 
			
		||||
 | 
			
		||||
	if (error_rate < 0)
 | 
			
		||||
@@ -411,32 +516,30 @@ void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
 | 
			
		||||
 | 
			
		||||
	now = now_msec();
 | 
			
		||||
 | 
			
		||||
	if (bts_data->max_cs_dl)
 | 
			
		||||
		max_cs_dl = bts_data->max_cs_dl;
 | 
			
		||||
 | 
			
		||||
	/* TODO: Check for TBF direction */
 | 
			
		||||
	/* TODO: Support different CS values for UL and DL */
 | 
			
		||||
 | 
			
		||||
	m_nack_rate_dl = error_rate;
 | 
			
		||||
 | 
			
		||||
	if (error_rate > bts_data->cs_adj_upper_limit) {
 | 
			
		||||
		if (m_current_cs_dl > 1) {
 | 
			
		||||
			m_current_cs_dl -= 1;
 | 
			
		||||
		if (m_current_cs_dl.to_num() > 1) {
 | 
			
		||||
			m_current_cs_dl.dec(mode());
 | 
			
		||||
			LOGP(DRLCMACDL, LOGL_INFO,
 | 
			
		||||
				"MS (IMSI %s): High error rate %d%%, "
 | 
			
		||||
				"reducing CS level to %d\n",
 | 
			
		||||
				imsi(), error_rate, m_current_cs_dl);
 | 
			
		||||
				"reducing CS level to %s\n",
 | 
			
		||||
				imsi(), error_rate, m_current_cs_dl.name());
 | 
			
		||||
			m_last_cs_not_low = now;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (error_rate < bts_data->cs_adj_lower_limit) {
 | 
			
		||||
		if (m_current_cs_dl < max_cs_dl) {
 | 
			
		||||
		       if (now - m_last_cs_not_low > 1000) {
 | 
			
		||||
			       m_current_cs_dl += 1;
 | 
			
		||||
			       m_current_cs_dl.inc(mode());
 | 
			
		||||
 | 
			
		||||
			       LOGP(DRLCMACDL, LOGL_INFO,
 | 
			
		||||
				       "MS (IMSI %s): Low error rate %d%%, "
 | 
			
		||||
				       "increasing DL CS level to %d\n",
 | 
			
		||||
				       imsi(), error_rate, m_current_cs_dl);
 | 
			
		||||
				       "increasing DL CS level to %s\n",
 | 
			
		||||
				       imsi(), error_rate,
 | 
			
		||||
				       m_current_cs_dl.name());
 | 
			
		||||
			       m_last_cs_not_low = now;
 | 
			
		||||
		       } else {
 | 
			
		||||
			       LOGP(DRLCMACDL, LOGL_DEBUG,
 | 
			
		||||
@@ -453,46 +556,131 @@ void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
 | 
			
		||||
GprsCodingScheme GprsMs::max_cs_ul() const
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts_data;
 | 
			
		||||
	uint8_t max_cs_ul = 4;
 | 
			
		||||
	unsigned i;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(m_bts != NULL);
 | 
			
		||||
	bts_data = m_bts->bts_data();
 | 
			
		||||
 | 
			
		||||
	if (bts_data->max_cs_ul)
 | 
			
		||||
		max_cs_ul = bts_data->max_cs_ul;
 | 
			
		||||
	if (m_current_cs_ul.isGprs()) {
 | 
			
		||||
		if (!bts_data->max_cs_ul)
 | 
			
		||||
			return GprsCodingScheme(GprsCodingScheme::CS4);
 | 
			
		||||
 | 
			
		||||
	if (meas->have_link_qual) {
 | 
			
		||||
		int old_link_qual = meas->link_qual;
 | 
			
		||||
		int low  = bts_data->cs_lqual_ranges[current_cs_ul()-1].low;
 | 
			
		||||
		int high  = bts_data->cs_lqual_ranges[current_cs_ul()-1].high;
 | 
			
		||||
		uint8_t new_cs_ul = m_current_cs_ul;
 | 
			
		||||
 | 
			
		||||
		if (m_l1_meas.have_link_qual)
 | 
			
		||||
		       old_link_qual = m_l1_meas.link_qual;
 | 
			
		||||
 | 
			
		||||
		if (meas->link_qual < low &&  old_link_qual < low)
 | 
			
		||||
			new_cs_ul = m_current_cs_ul - 1;
 | 
			
		||||
		else if (meas->link_qual > high &&  old_link_qual > high &&
 | 
			
		||||
			m_current_cs_ul < max_cs_ul)
 | 
			
		||||
			new_cs_ul = m_current_cs_ul + 1;
 | 
			
		||||
 | 
			
		||||
		if (m_current_cs_ul != new_cs_ul) {
 | 
			
		||||
			LOGP(DRLCMACDL, LOGL_INFO,
 | 
			
		||||
				"MS (IMSI %s): "
 | 
			
		||||
				"Link quality %ddB (%ddB) left window [%d, %d], "
 | 
			
		||||
				"modifying uplink CS level: %d -> %d\n",
 | 
			
		||||
				imsi(), meas->link_qual, old_link_qual,
 | 
			
		||||
				low, high,
 | 
			
		||||
				m_current_cs_ul, new_cs_ul);
 | 
			
		||||
 | 
			
		||||
			m_current_cs_ul = new_cs_ul;
 | 
			
		||||
		}
 | 
			
		||||
		return GprsCodingScheme::getGprsByNum(bts_data->max_cs_ul);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!m_current_cs_ul.isEgprs())
 | 
			
		||||
		return GprsCodingScheme(); /* UNKNOWN */
 | 
			
		||||
 | 
			
		||||
	if (bts_data->max_mcs_ul)
 | 
			
		||||
		return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_ul);
 | 
			
		||||
	else if (bts_data->max_cs_ul)
 | 
			
		||||
		return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_ul);
 | 
			
		||||
 | 
			
		||||
	return GprsCodingScheme(GprsCodingScheme::MCS4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::set_current_cs_dl(GprsCodingScheme::Scheme scheme)
 | 
			
		||||
{
 | 
			
		||||
	m_current_cs_dl = scheme;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GprsCodingScheme GprsMs::max_cs_dl() const
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts_data;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(m_bts != NULL);
 | 
			
		||||
	bts_data = m_bts->bts_data();
 | 
			
		||||
 | 
			
		||||
	if (m_current_cs_dl.isGprs()) {
 | 
			
		||||
		if (!bts_data->max_cs_dl)
 | 
			
		||||
			return GprsCodingScheme(GprsCodingScheme::CS4);
 | 
			
		||||
 | 
			
		||||
		return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!m_current_cs_dl.isEgprs())
 | 
			
		||||
		return GprsCodingScheme(); /* UNKNOWN */
 | 
			
		||||
 | 
			
		||||
	if (bts_data->max_mcs_dl)
 | 
			
		||||
		return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
 | 
			
		||||
	else if (bts_data->max_cs_dl)
 | 
			
		||||
		return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
 | 
			
		||||
 | 
			
		||||
	return GprsCodingScheme(GprsCodingScheme::MCS4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts_data;
 | 
			
		||||
	GprsCodingScheme max_cs_ul = this->max_cs_ul();
 | 
			
		||||
 | 
			
		||||
	int old_link_qual;
 | 
			
		||||
	int low;
 | 
			
		||||
	int high;
 | 
			
		||||
	GprsCodingScheme new_cs_ul = m_current_cs_ul;
 | 
			
		||||
	unsigned current_cs_num = m_current_cs_ul.to_num();
 | 
			
		||||
 | 
			
		||||
	bts_data = m_bts->bts_data();
 | 
			
		||||
 | 
			
		||||
	if (!max_cs_ul) {
 | 
			
		||||
		LOGP(DRLCMACMEAS, LOGL_ERROR,
 | 
			
		||||
			"max_cs_ul cannot be derived (current UL CS: %s)\n",
 | 
			
		||||
			m_current_cs_ul.name());
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(current_cs_num > 0);
 | 
			
		||||
 | 
			
		||||
	if (!m_current_cs_ul)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!meas->have_link_qual)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	old_link_qual = meas->link_qual;
 | 
			
		||||
 | 
			
		||||
	if (m_current_cs_ul.isGprs()) {
 | 
			
		||||
		low  = bts_data->cs_lqual_ranges[current_cs_num-1].low;
 | 
			
		||||
		high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
 | 
			
		||||
	} else if (m_current_cs_ul.isEgprs()) {
 | 
			
		||||
		if (current_cs_num > MAX_GPRS_CS)
 | 
			
		||||
			current_cs_num = MAX_GPRS_CS;
 | 
			
		||||
		low  = bts_data->mcs_lqual_ranges[current_cs_num-1].low;
 | 
			
		||||
		high = bts_data->mcs_lqual_ranges[current_cs_num-1].high;
 | 
			
		||||
	} else {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (m_l1_meas.have_link_qual)
 | 
			
		||||
		old_link_qual = m_l1_meas.link_qual;
 | 
			
		||||
 | 
			
		||||
	if (meas->link_qual < low &&  old_link_qual < low)
 | 
			
		||||
		new_cs_ul.dec(mode());
 | 
			
		||||
	else if (meas->link_qual > high &&  old_link_qual > high &&
 | 
			
		||||
		m_current_cs_ul < max_cs_ul)
 | 
			
		||||
		new_cs_ul.inc(mode());
 | 
			
		||||
 | 
			
		||||
	if (m_current_cs_ul != new_cs_ul) {
 | 
			
		||||
		LOGP(DRLCMACMEAS, LOGL_INFO,
 | 
			
		||||
			"MS (IMSI %s): "
 | 
			
		||||
			"Link quality %ddB (%ddB) left window [%d, %d], "
 | 
			
		||||
			"modifying uplink CS level: %s -> %s\n",
 | 
			
		||||
			imsi(), meas->link_qual, old_link_qual,
 | 
			
		||||
			low, high,
 | 
			
		||||
			m_current_cs_ul.name(), new_cs_ul.name());
 | 
			
		||||
 | 
			
		||||
		m_current_cs_ul = new_cs_ul;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
 | 
			
		||||
{
 | 
			
		||||
	unsigned i;
 | 
			
		||||
 | 
			
		||||
	update_cs_ul(meas);
 | 
			
		||||
 | 
			
		||||
	if (meas->have_rssi)
 | 
			
		||||
		m_l1_meas.set_rssi(meas->rssi);
 | 
			
		||||
	if (meas->have_bto)
 | 
			
		||||
@@ -519,9 +707,9 @@ void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsMs::current_cs_dl() const
 | 
			
		||||
GprsCodingScheme GprsMs::current_cs_dl() const
 | 
			
		||||
{
 | 
			
		||||
	uint8_t cs = m_current_cs_dl;
 | 
			
		||||
	GprsCodingScheme cs = m_current_cs_dl;
 | 
			
		||||
	size_t unencoded_octets;
 | 
			
		||||
 | 
			
		||||
	if (!m_bts)
 | 
			
		||||
@@ -531,7 +719,7 @@ uint8_t GprsMs::current_cs_dl() const
 | 
			
		||||
 | 
			
		||||
	/* If the DL TBF is active, add number of unencoded chunk octets */
 | 
			
		||||
	if (m_dl_tbf)
 | 
			
		||||
		unencoded_octets = m_dl_tbf->m_llc.chunk_size();
 | 
			
		||||
		unencoded_octets += m_dl_tbf->m_llc.chunk_size();
 | 
			
		||||
 | 
			
		||||
	/* There are many unencoded octets, don't reduce */
 | 
			
		||||
	if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
 | 
			
		||||
@@ -542,11 +730,11 @@ uint8_t GprsMs::current_cs_dl() const
 | 
			
		||||
		return cs;
 | 
			
		||||
 | 
			
		||||
	/* The throughput would probably be better if the CS level was reduced */
 | 
			
		||||
	cs -= 1;
 | 
			
		||||
	cs.dec(mode());
 | 
			
		||||
 | 
			
		||||
	/* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
 | 
			
		||||
	if (cs == 2)
 | 
			
		||||
		cs -= 1;
 | 
			
		||||
	if (cs == GprsCodingScheme(GprsCodingScheme::CS2))
 | 
			
		||||
		cs.dec(mode());
 | 
			
		||||
 | 
			
		||||
	return cs;
 | 
			
		||||
}
 | 
			
		||||
@@ -588,6 +776,31 @@ uint8_t GprsMs::ul_slots() const
 | 
			
		||||
	return slots;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t GprsMs::current_pacch_slots() const
 | 
			
		||||
{
 | 
			
		||||
	uint8_t slots = 0;
 | 
			
		||||
 | 
			
		||||
	bool is_dl_active = m_dl_tbf && m_dl_tbf->is_tfi_assigned();
 | 
			
		||||
	bool is_ul_active = m_ul_tbf && m_ul_tbf->is_tfi_assigned();
 | 
			
		||||
 | 
			
		||||
	if (!is_dl_active && !is_ul_active)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* see TS 44.060, 8.1.1.2.2 */
 | 
			
		||||
	if (is_dl_active && !is_ul_active)
 | 
			
		||||
		slots =  m_dl_tbf->dl_slots();
 | 
			
		||||
	else if (!is_dl_active && is_ul_active)
 | 
			
		||||
		slots =  m_ul_tbf->ul_slots();
 | 
			
		||||
	else
 | 
			
		||||
		slots =  m_ul_tbf->ul_slots() & m_dl_tbf->dl_slots();
 | 
			
		||||
 | 
			
		||||
	/* Assume a multislot class 1 device */
 | 
			
		||||
	/* TODO: For class 2 devices, this could be removed */
 | 
			
		||||
	slots = pcu_lsb(slots);
 | 
			
		||||
 | 
			
		||||
	return slots;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
 | 
			
		||||
	uint8_t ul_slots, uint8_t dl_slots)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -20,18 +20,18 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf;
 | 
			
		||||
struct gprs_rlcmac_dl_tbf;
 | 
			
		||||
struct gprs_rlcmac_ul_tbf;
 | 
			
		||||
struct gprs_codel;
 | 
			
		||||
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
#include "cxx_linuxlist.h"
 | 
			
		||||
#include "llc.h"
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
#include "pcu_l1_if.h"
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
	#include <osmocom/core/timer.h>
 | 
			
		||||
	#include <osmocom/core/linuxlist.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
@@ -63,6 +63,8 @@ public:
 | 
			
		||||
 | 
			
		||||
	void set_callback(Callback *cb) {m_cb = cb;}
 | 
			
		||||
 | 
			
		||||
	void merge_old_ms(GprsMs *old_ms);
 | 
			
		||||
 | 
			
		||||
	gprs_rlcmac_ul_tbf *ul_tbf() const {return m_ul_tbf;}
 | 
			
		||||
	gprs_rlcmac_dl_tbf *dl_tbf() const {return m_dl_tbf;}
 | 
			
		||||
	gprs_rlcmac_tbf *tbf(enum gprs_rlcmac_tbf_direction dir) const;
 | 
			
		||||
@@ -71,22 +73,32 @@ public:
 | 
			
		||||
	bool confirm_tlli(uint32_t tlli);
 | 
			
		||||
	bool check_tlli(uint32_t tlli);
 | 
			
		||||
 | 
			
		||||
	void reset();
 | 
			
		||||
	GprsCodingScheme::Mode mode() const;
 | 
			
		||||
	void set_mode(GprsCodingScheme::Mode mode);
 | 
			
		||||
 | 
			
		||||
	const char *imsi() const;
 | 
			
		||||
	void set_imsi(const char *imsi);
 | 
			
		||||
 | 
			
		||||
	uint8_t ta() const;
 | 
			
		||||
	void set_ta(uint8_t ta);
 | 
			
		||||
	uint8_t ms_class() const;
 | 
			
		||||
	uint8_t egprs_ms_class() const;
 | 
			
		||||
	void set_ms_class(uint8_t ms_class);
 | 
			
		||||
	void set_egprs_ms_class(uint8_t ms_class);
 | 
			
		||||
	void set_current_cs_dl(GprsCodingScheme::Scheme scheme);
 | 
			
		||||
 | 
			
		||||
	uint8_t current_cs_ul() const;
 | 
			
		||||
	uint8_t current_cs_dl() const;
 | 
			
		||||
	GprsCodingScheme current_cs_ul() const;
 | 
			
		||||
	GprsCodingScheme current_cs_dl() const;
 | 
			
		||||
	GprsCodingScheme max_cs_ul() const;
 | 
			
		||||
	GprsCodingScheme max_cs_dl() const;
 | 
			
		||||
 | 
			
		||||
	int first_common_ts() const;
 | 
			
		||||
	uint8_t dl_slots() const;
 | 
			
		||||
	uint8_t ul_slots() const;
 | 
			
		||||
	uint8_t reserved_dl_slots() const;
 | 
			
		||||
	uint8_t reserved_ul_slots() const;
 | 
			
		||||
	uint8_t current_pacch_slots() const;
 | 
			
		||||
	gprs_rlcmac_trx *current_trx() const;
 | 
			
		||||
	void set_reserved_slots(gprs_rlcmac_trx *trx,
 | 
			
		||||
		uint8_t ul_slots, uint8_t dl_slots);
 | 
			
		||||
@@ -105,17 +117,21 @@ public:
 | 
			
		||||
 | 
			
		||||
	void update_error_rate(gprs_rlcmac_tbf *tbf, int percent);
 | 
			
		||||
 | 
			
		||||
	bool is_idle() const {return !m_ul_tbf && !m_dl_tbf && !m_ref;}
 | 
			
		||||
	bool is_idle() const;
 | 
			
		||||
	bool need_dl_tbf() const;
 | 
			
		||||
 | 
			
		||||
	void* operator new(size_t num);
 | 
			
		||||
	void operator delete(void* p);
 | 
			
		||||
 | 
			
		||||
	LListHead<GprsMs>& list() {return this->m_list;}
 | 
			
		||||
	const LListHead<GprsMs>& list() const {return this->m_list;}
 | 
			
		||||
	const LListHead<gprs_rlcmac_tbf>& old_tbfs() const {return m_old_tbfs;}
 | 
			
		||||
 | 
			
		||||
	void update_l1_meas(const pcu_l1_meas *meas);
 | 
			
		||||
	const pcu_l1_meas* l1_meas() const {return &m_l1_meas;};
 | 
			
		||||
	unsigned nack_rate_dl() const;
 | 
			
		||||
	unsigned dl_ctrl_msg() const;
 | 
			
		||||
	void update_dl_ctrl_msg();
 | 
			
		||||
 | 
			
		||||
	/* internal use */
 | 
			
		||||
	static void timeout(void *priv_);
 | 
			
		||||
@@ -126,12 +142,15 @@ protected:
 | 
			
		||||
	void unref();
 | 
			
		||||
	void start_timer();
 | 
			
		||||
	void stop_timer();
 | 
			
		||||
	void update_cs_ul(const pcu_l1_meas*);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	BTS *m_bts;
 | 
			
		||||
	Callback * m_cb;
 | 
			
		||||
	gprs_rlcmac_ul_tbf *m_ul_tbf;
 | 
			
		||||
	gprs_rlcmac_dl_tbf *m_dl_tbf;
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> m_old_tbfs;
 | 
			
		||||
 | 
			
		||||
	uint32_t m_tlli;
 | 
			
		||||
	uint32_t m_new_ul_tlli;
 | 
			
		||||
	uint32_t m_new_dl_tlli;
 | 
			
		||||
@@ -140,9 +159,10 @@ private:
 | 
			
		||||
	char m_imsi[16];
 | 
			
		||||
	uint8_t m_ta;
 | 
			
		||||
	uint8_t m_ms_class;
 | 
			
		||||
	uint8_t m_egprs_ms_class;
 | 
			
		||||
	/* current coding scheme */
 | 
			
		||||
	uint8_t m_current_cs_ul;
 | 
			
		||||
	uint8_t m_current_cs_dl;
 | 
			
		||||
	GprsCodingScheme m_current_cs_ul;
 | 
			
		||||
	GprsCodingScheme m_current_cs_dl;
 | 
			
		||||
 | 
			
		||||
	gprs_llc_queue m_llc_queue;
 | 
			
		||||
 | 
			
		||||
@@ -161,8 +181,24 @@ private:
 | 
			
		||||
	gprs_rlcmac_trx *m_current_trx;
 | 
			
		||||
 | 
			
		||||
	struct gprs_codel *m_codel_state;
 | 
			
		||||
	GprsCodingScheme::Mode m_mode;
 | 
			
		||||
 | 
			
		||||
	unsigned m_dl_ctrl_msg;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
inline bool GprsMs::is_idle() const
 | 
			
		||||
{
 | 
			
		||||
	return !m_ul_tbf && !m_dl_tbf && !m_ref && llist_empty(&m_old_tbfs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool GprsMs::need_dl_tbf() const
 | 
			
		||||
{
 | 
			
		||||
	if (dl_tbf() != NULL && dl_tbf()->state_is_not(GPRS_RLCMAC_WAIT_RELEASE))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return llc_queue()->size() > 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint32_t GprsMs::tlli() const
 | 
			
		||||
{
 | 
			
		||||
	return m_new_ul_tlli ? m_new_ul_tlli :
 | 
			
		||||
@@ -191,11 +227,21 @@ inline uint8_t GprsMs::ms_class() const
 | 
			
		||||
	return m_ms_class;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint8_t GprsMs::current_cs_ul() const
 | 
			
		||||
inline uint8_t GprsMs::egprs_ms_class() const
 | 
			
		||||
{
 | 
			
		||||
	return m_egprs_ms_class;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline GprsCodingScheme GprsMs::current_cs_ul() const
 | 
			
		||||
{
 | 
			
		||||
	return m_current_cs_ul;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline GprsCodingScheme::Mode GprsMs::mode() const
 | 
			
		||||
{
 | 
			
		||||
	return m_mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void GprsMs::set_timeout(unsigned secs)
 | 
			
		||||
{
 | 
			
		||||
	m_delay = secs;
 | 
			
		||||
@@ -221,6 +267,16 @@ inline unsigned GprsMs::nack_rate_dl() const
 | 
			
		||||
	return m_nack_rate_dl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline unsigned GprsMs::dl_ctrl_msg() const
 | 
			
		||||
{
 | 
			
		||||
	return m_dl_ctrl_msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void GprsMs::update_dl_ctrl_msg()
 | 
			
		||||
{
 | 
			
		||||
	m_dl_ctrl_msg++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint8_t GprsMs::reserved_dl_slots() const
 | 
			
		||||
{
 | 
			
		||||
	return m_reserved_dl_slots;
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,13 @@
 | 
			
		||||
#include "gprs_ms_storage.h"
 | 
			
		||||
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
#include "gprs_debug.h"
 | 
			
		||||
#include "bts.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
	#include <osmocom/core/linuxlist.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define GPRS_UNDEFINED_IMSI "000"
 | 
			
		||||
 | 
			
		||||
GprsMsStorage::GprsMsStorage(BTS *bts) :
 | 
			
		||||
	m_bts(bts)
 | 
			
		||||
@@ -30,6 +36,11 @@ GprsMsStorage::GprsMsStorage(BTS *bts) :
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GprsMsStorage::~GprsMsStorage()
 | 
			
		||||
{
 | 
			
		||||
	cleanup();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GprsMsStorage::cleanup()
 | 
			
		||||
{
 | 
			
		||||
	LListHead<GprsMs> *pos, *tmp;
 | 
			
		||||
 | 
			
		||||
@@ -43,6 +54,8 @@ GprsMsStorage::~GprsMsStorage()
 | 
			
		||||
void GprsMsStorage::ms_idle(class GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	llist_del(&ms->list());
 | 
			
		||||
	if (m_bts)
 | 
			
		||||
		m_bts->ms_present(m_bts->ms_present_get() - 1);
 | 
			
		||||
	if (ms->is_idle())
 | 
			
		||||
		delete ms;
 | 
			
		||||
}
 | 
			
		||||
@@ -69,7 +82,7 @@ GprsMs *GprsMsStorage::get_ms(uint32_t tlli, uint32_t old_tlli, const char *imsi
 | 
			
		||||
 | 
			
		||||
	/* not found by TLLI */
 | 
			
		||||
 | 
			
		||||
	if (imsi && imsi[0]) {
 | 
			
		||||
	if (imsi && imsi[0] && strcmp(imsi, GPRS_UNDEFINED_IMSI) != 0) {
 | 
			
		||||
		llist_for_each(pos, &m_list) {
 | 
			
		||||
			ms = pos->entry();
 | 
			
		||||
			if (strcmp(imsi, ms->imsi()) == 0)
 | 
			
		||||
@@ -88,6 +101,8 @@ GprsMs *GprsMsStorage::create_ms()
 | 
			
		||||
 | 
			
		||||
	ms->set_callback(this);
 | 
			
		||||
	llist_add(&ms->list(), &m_list);
 | 
			
		||||
	if (m_bts)
 | 
			
		||||
		m_bts->ms_present(m_bts->ms_present_get() + 1);
 | 
			
		||||
 | 
			
		||||
	return ms;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,8 @@ public:
 | 
			
		||||
	GprsMsStorage(BTS *bts);
 | 
			
		||||
	~GprsMsStorage();
 | 
			
		||||
 | 
			
		||||
	void cleanup();
 | 
			
		||||
 | 
			
		||||
	virtual void ms_idle(class GprsMs *);
 | 
			
		||||
	virtual void ms_active(class GprsMs *);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,13 +19,12 @@
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
#include <gprs_bssgp_pcu.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;
 | 
			
		||||
 | 
			
		||||
@@ -33,7 +32,7 @@ int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
 | 
			
		||||
	const char *imsi)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH)\n");
 | 
			
		||||
	bitvec *paging_request = bitvec_alloc(23);
 | 
			
		||||
	bitvec *paging_request = bitvec_alloc(23, tall_pcu_ctx);
 | 
			
		||||
	bitvec_unhex(paging_request, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
 | 
			
		||||
	int plen = Encoding::write_paging_request(paging_request, ptmsi, ptmsi_len);
 | 
			
		||||
	pcu_l1if_tx_pch(paging_request, plen, (char *)imsi);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,14 +21,16 @@
 | 
			
		||||
#ifndef GPRS_RLCMAC_H
 | 
			
		||||
#define GPRS_RLCMAC_H
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
#include <bitvector.h>
 | 
			
		||||
#include <gsm_rlcmac.h>
 | 
			
		||||
#include <gsm_timer.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/core/bitvec.h>
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +43,6 @@ extern "C" {
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf;
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
struct BTS;
 | 
			
		||||
struct GprsMs;
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
@@ -63,6 +64,9 @@ struct gprs_rlcmac_cs {
 | 
			
		||||
	uint8_t block_payload;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* TS allocation internal functions */
 | 
			
		||||
int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
 | 
			
		||||
	uint16_t lost);
 | 
			
		||||
 | 
			
		||||
@@ -90,28 +94,22 @@ int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
 | 
			
		||||
	const char *imsi);
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	uint8_t trx, uint8_t ts, uint16_t arfcn, 
 | 
			
		||||
	uint8_t trx, uint8_t ts,
 | 
			
		||||
        uint32_t fn, uint8_t block_nr);
 | 
			
		||||
 | 
			
		||||
int gprs_alloc_max_dl_slots_per_ms(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	uint8_t ms_class = 0);
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	struct GprsMs *ms,
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
 | 
			
		||||
	int use_trx);
 | 
			
		||||
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
 | 
			
		||||
		      int8_t use_trx);
 | 
			
		||||
 | 
			
		||||
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	struct GprsMs *ms,
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
 | 
			
		||||
	int use_trx);
 | 
			
		||||
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
 | 
			
		||||
		      int8_t use_trx);
 | 
			
		||||
 | 
			
		||||
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	struct GprsMs *ms,
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
 | 
			
		||||
	int use_trx);
 | 
			
		||||
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
 | 
			
		||||
			    int8_t use_trx);
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 * downlink measurement
 | 
			
		||||
 */
 | 
			
		||||
#warning "TODO: trigger the measurement report from the pollcontroller and use it for flow control"
 | 
			
		||||
/* TODO: trigger the measurement report from the pollcontroller and use it for flow control */
 | 
			
		||||
 | 
			
		||||
/* received Measurement Report */
 | 
			
		||||
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr)
 | 
			
		||||
@@ -179,6 +179,8 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
 | 
			
		||||
	if (elapsed < 128)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	tbf->m_bw.dl_throughput = (tbf->m_bw.dl_bw_octets/elapsed);
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
 | 
			
		||||
		"%d KBits/s\n", tbf->imsi(), tbf->tlli(),
 | 
			
		||||
		tbf->m_bw.dl_bw_octets / elapsed);
 | 
			
		||||
 
 | 
			
		||||
@@ -22,10 +22,18 @@
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <gprs_ms.h>
 | 
			
		||||
#include <rlc.h>
 | 
			
		||||
#include <sba.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
#include "pcu_utils.h"
 | 
			
		||||
 | 
			
		||||
static uint32_t sched_poll(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
extern "C" {
 | 
			
		||||
	#include <osmocom/core/gsmtap.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t sched_poll(BTS *bts,
 | 
			
		||||
		    uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
 | 
			
		||||
		    struct gprs_rlcmac_tbf **poll_tbf,
 | 
			
		||||
		    struct gprs_rlcmac_tbf **ul_ass_tbf,
 | 
			
		||||
@@ -34,41 +42,46 @@ static uint32_t sched_poll(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
	struct gprs_rlcmac_dl_tbf *dl_tbf;
 | 
			
		||||
	struct llist_pods *lpods;
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> *pos;
 | 
			
		||||
	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_pods_for_each_entry(ul_tbf, &bts->ul_tbfs, list, lpods) {
 | 
			
		||||
	poll_fn = poll_fn % GSM_MAX_FN;
 | 
			
		||||
	llist_for_each(pos, &bts->ul_tbfs()) {
 | 
			
		||||
		ul_tbf = as_ul_tbf(pos->entry());
 | 
			
		||||
		OSMO_ASSERT(ul_tbf);
 | 
			
		||||
		/* this trx, this ts */
 | 
			
		||||
		if (ul_tbf->trx->trx_no != trx || ul_tbf->control_ts != ts)
 | 
			
		||||
		if (ul_tbf->trx->trx_no != trx || !ul_tbf->is_control_ts(ts))
 | 
			
		||||
			continue;
 | 
			
		||||
		/* polling for next uplink block */
 | 
			
		||||
		if (ul_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
 | 
			
		||||
		 && ul_tbf->poll_fn == poll_fn)
 | 
			
		||||
		if (ul_tbf->poll_scheduled() && ul_tbf->poll_fn == poll_fn)
 | 
			
		||||
			*poll_tbf = ul_tbf;
 | 
			
		||||
		if (ul_tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
 | 
			
		||||
		if (ul_tbf->ul_ack_state_is(GPRS_RLCMAC_UL_ACK_SEND_ACK))
 | 
			
		||||
			*ul_ack_tbf = ul_tbf;
 | 
			
		||||
		if (ul_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
 | 
			
		||||
		if (ul_tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_SEND_ASS))
 | 
			
		||||
			*dl_ass_tbf = ul_tbf;
 | 
			
		||||
		if (ul_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
 | 
			
		||||
		if (ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
 | 
			
		||||
		    || ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
 | 
			
		||||
			*ul_ass_tbf = ul_tbf;
 | 
			
		||||
#warning "Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all states?"
 | 
			
		||||
/* 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_pods_for_each_entry(dl_tbf, &bts->dl_tbfs, list, lpods) {
 | 
			
		||||
	llist_for_each(pos, &bts->dl_tbfs()) {
 | 
			
		||||
		dl_tbf = as_dl_tbf(pos->entry());
 | 
			
		||||
		OSMO_ASSERT(dl_tbf);
 | 
			
		||||
		/* this trx, this ts */
 | 
			
		||||
		if (dl_tbf->trx->trx_no != trx || dl_tbf->control_ts != ts)
 | 
			
		||||
		if (dl_tbf->trx->trx_no != trx || !dl_tbf->is_control_ts(ts))
 | 
			
		||||
			continue;
 | 
			
		||||
		/* polling for next uplink block */
 | 
			
		||||
		if (dl_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
 | 
			
		||||
		 && dl_tbf->poll_fn == poll_fn)
 | 
			
		||||
		if (dl_tbf->poll_scheduled() && dl_tbf->poll_fn == poll_fn)
 | 
			
		||||
			*poll_tbf = dl_tbf;
 | 
			
		||||
		if (dl_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
 | 
			
		||||
		if (dl_tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_SEND_ASS))
 | 
			
		||||
			*dl_ass_tbf = dl_tbf;
 | 
			
		||||
		if (dl_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
 | 
			
		||||
		if (dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
 | 
			
		||||
		    || dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
 | 
			
		||||
			*ul_ass_tbf = dl_tbf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -126,34 +139,67 @@ static struct msgb *sched_select_ctrl_msg(
 | 
			
		||||
		if (!tbf)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (tbf == ul_ass_tbf)
 | 
			
		||||
			msg = ul_ass_tbf->create_ul_ass(fn);
 | 
			
		||||
		else if (tbf == dl_ass_tbf)
 | 
			
		||||
			msg = dl_ass_tbf->create_dl_ass(fn);
 | 
			
		||||
		/*
 | 
			
		||||
		 * 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 == ul_ass_tbf && tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
 | 
			
		||||
			msg = ul_ass_tbf->create_packet_access_reject();
 | 
			
		||||
		else if (tbf == ul_ass_tbf && tbf->direction ==
 | 
			
		||||
				GPRS_RLCMAC_DL_TBF)
 | 
			
		||||
			if (tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
 | 
			
		||||
				msg = ul_ass_tbf->create_packet_access_reject();
 | 
			
		||||
			else
 | 
			
		||||
				msg = ul_ass_tbf->create_ul_ass(fn, ts);
 | 
			
		||||
		else if (tbf == dl_ass_tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
 | 
			
		||||
			msg = dl_ass_tbf->create_dl_ass(fn, ts);
 | 
			
		||||
		else if (tbf == ul_ack_tbf)
 | 
			
		||||
			msg = ul_ack_tbf->create_ul_ack(fn);
 | 
			
		||||
		else
 | 
			
		||||
			abort();
 | 
			
		||||
			msg = ul_ack_tbf->create_ul_ack(fn, ts);
 | 
			
		||||
 | 
			
		||||
		if (!msg) {
 | 
			
		||||
			tbf = NULL;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pdch->next_ctrl_prio += i + 1;
 | 
			
		||||
		pdch->next_ctrl_prio += 1;
 | 
			
		||||
		pdch->next_ctrl_prio %= 3;
 | 
			
		||||
		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 (dl_ass_tbf) {
 | 
			
		||||
			tbf = dl_ass_tbf;
 | 
			
		||||
			msg = dl_ass_tbf->create_dl_ass(fn, ts);
 | 
			
		||||
		} else if (ul_ass_tbf) {
 | 
			
		||||
			tbf = ul_ass_tbf;
 | 
			
		||||
			msg = ul_ass_tbf->create_ul_ass(fn, ts);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* any message */
 | 
			
		||||
	if (msg) {
 | 
			
		||||
		if (!tbf) {
 | 
			
		||||
			LOGP(DRLCMACSCHED, LOGL_ERROR,
 | 
			
		||||
			     "Control message to be scheduled, but no TBF (TRX=%d, TS=%d)\n", trx, ts);
 | 
			
		||||
			msgb_free(msg);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
		tbf->rotate_in_list();
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
 | 
			
		||||
			"message at RTS for %s (TRX=%d, TS=%d)\n",
 | 
			
		||||
			tbf_name(tbf), trx, ts);
 | 
			
		||||
		/* Updates the dl ctrl msg counter for ms */
 | 
			
		||||
		tbf->ms()->update_dl_ctrl_msg();
 | 
			
		||||
		return msg;
 | 
			
		||||
	}
 | 
			
		||||
	/* schedule PACKET PAGING REQUEST */
 | 
			
		||||
 | 
			
		||||
	/* schedule PACKET PAGING REQUEST, if any are pending */
 | 
			
		||||
	msg = pdch->packet_paging_request();
 | 
			
		||||
	if (msg) {
 | 
			
		||||
		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request "
 | 
			
		||||
@@ -164,27 +210,41 @@ static struct msgb *sched_select_ctrl_msg(
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline enum tbf_dl_prio tbf_compute_priority(const struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_dl_tbf *tbf,
 | 
			
		||||
						    uint8_t ts, uint32_t fn, int age)
 | 
			
		||||
{
 | 
			
		||||
	const gprs_rlc_dl_window *w = tbf->window();
 | 
			
		||||
	int age_thresh1 = msecs_to_frames(200),
 | 
			
		||||
		age_thresh2 = msecs_to_frames(OSMO_MIN(BTS::TIMER_T3190_MSEC/2, bts->dl_tbf_idle_msec));
 | 
			
		||||
 | 
			
		||||
	if (tbf->is_control_ts(ts) && tbf->need_control_ts())
 | 
			
		||||
		return DL_PRIO_CONTROL;
 | 
			
		||||
 | 
			
		||||
	if (tbf->is_control_ts(ts) && age > age_thresh2 && age_thresh2 > 0)
 | 
			
		||||
		return DL_PRIO_HIGH_AGE;
 | 
			
		||||
 | 
			
		||||
	if ((tbf->state_is(GPRS_RLCMAC_FLOW) && tbf->have_data()) || w->resend_needed() >= 0)
 | 
			
		||||
		return DL_PRIO_NEW_DATA;
 | 
			
		||||
 | 
			
		||||
	if (tbf->is_control_ts(ts) && age > age_thresh1 && tbf->keep_open(fn))
 | 
			
		||||
		return DL_PRIO_LOW_AGE;
 | 
			
		||||
 | 
			
		||||
	if (!w->window_empty())
 | 
			
		||||
		return DL_PRIO_SENT_DATA;
 | 
			
		||||
 | 
			
		||||
	return DL_PRIO_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
		    uint8_t trx, uint8_t ts, uint32_t fn,
 | 
			
		||||
		    uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = NULL;
 | 
			
		||||
	struct gprs_rlcmac_dl_tbf *tbf, *prio_tbf = NULL;
 | 
			
		||||
	enum {
 | 
			
		||||
		DL_PRIO_NONE,
 | 
			
		||||
		DL_PRIO_SENT_DATA, /* the data has been sent and not (yet) nacked */
 | 
			
		||||
		DL_PRIO_LOW_AGE,   /* the age has reached the first threshold */
 | 
			
		||||
		DL_PRIO_NEW_DATA,  /* the data has not been sent yet or nacked */
 | 
			
		||||
		DL_PRIO_HIGH_AGE,  /* the age has reached the second threshold */
 | 
			
		||||
		DL_PRIO_CONTROL,   /* a control block needs to be sent */
 | 
			
		||||
	} prio, max_prio = DL_PRIO_NONE;
 | 
			
		||||
	enum tbf_dl_prio prio, max_prio = DL_PRIO_NONE;
 | 
			
		||||
 | 
			
		||||
	uint8_t i, tfi, prio_tfi;
 | 
			
		||||
	int age;
 | 
			
		||||
	const int age_thresh1 = msecs_to_frames(200);
 | 
			
		||||
	const int high_prio_msecs =
 | 
			
		||||
		OSMO_MIN(BTS::TIMER_T3190_MSEC/2, bts->dl_tbf_idle_msec);
 | 
			
		||||
	const int age_thresh2 = msecs_to_frames(high_prio_msecs);
 | 
			
		||||
 | 
			
		||||
	/* select downlink resource */
 | 
			
		||||
	for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
 | 
			
		||||
@@ -208,20 +268,8 @@ static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
		age = tbf->frames_since_last_poll(fn);
 | 
			
		||||
 | 
			
		||||
		/* compute priority */
 | 
			
		||||
		if (tbf->is_control_ts(ts) && tbf->need_control_ts())
 | 
			
		||||
			prio = DL_PRIO_CONTROL;
 | 
			
		||||
		else if (tbf->is_control_ts(ts) &&
 | 
			
		||||
			age > age_thresh2 && age_thresh2 > 0)
 | 
			
		||||
			prio = DL_PRIO_HIGH_AGE;
 | 
			
		||||
		else if ((tbf->state_is(GPRS_RLCMAC_FLOW) && tbf->have_data()) ||
 | 
			
		||||
			tbf->m_window.resend_needed() >= 0)
 | 
			
		||||
			prio = DL_PRIO_NEW_DATA;
 | 
			
		||||
		else if (tbf->is_control_ts(ts) &&
 | 
			
		||||
			age > age_thresh1 && tbf->keep_open(fn))
 | 
			
		||||
			prio = DL_PRIO_LOW_AGE;
 | 
			
		||||
		else if (!tbf->m_window.window_empty())
 | 
			
		||||
			prio = DL_PRIO_SENT_DATA;
 | 
			
		||||
		else
 | 
			
		||||
		prio = tbf_compute_priority(bts, tbf, ts, fn, age);
 | 
			
		||||
		if (prio == DL_PRIO_NONE)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* get the TBF with the highest priority */
 | 
			
		||||
@@ -265,8 +313,36 @@ static struct msgb *sched_dummy(void)
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void tap_n_acc(const struct msgb *msg, const 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->bts->rlc_sent_control();
 | 
			
		||||
		bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_CTRL, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
 | 
			
		||||
				      msg->len);
 | 
			
		||||
		break;
 | 
			
		||||
	case PCU_GSMTAP_C_DL_DATA_GPRS:
 | 
			
		||||
		bts->bts->rlc_sent();
 | 
			
		||||
		/* FIXME: distinguish between GPRS and EGPRS */
 | 
			
		||||
		bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_DATA_GPRS, false, trx, ts, GSMTAP_CHANNEL_PDTCH, fn, msg->data,
 | 
			
		||||
				      msg->len);
 | 
			
		||||
		break;
 | 
			
		||||
	case PCU_GSMTAP_C_DL_DUMMY:
 | 
			
		||||
		bts->bts->rlc_sent_dummy();
 | 
			
		||||
		bts->bts->send_gsmtap(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, uint16_t arfcn,
 | 
			
		||||
	uint8_t trx, uint8_t ts,
 | 
			
		||||
        uint32_t fn, uint8_t block_nr)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_pdch *pdch;
 | 
			
		||||
@@ -277,8 +353,6 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	struct msgb *msg = NULL;
 | 
			
		||||
	uint32_t poll_fn, sba_fn;
 | 
			
		||||
 | 
			
		||||
#warning "ARFCN... it is already in the TRX..... is it consistent with it?"
 | 
			
		||||
 | 
			
		||||
	if (trx >= 8 || ts >= 8)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	pdch = &bts->trx[trx].pdch[ts];
 | 
			
		||||
@@ -292,7 +366,7 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	/* store last frame number of RTS */
 | 
			
		||||
	pdch->last_rts_fn = fn;
 | 
			
		||||
 | 
			
		||||
	poll_fn = sched_poll(bts, trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
 | 
			
		||||
	poll_fn = sched_poll(bts->bts, trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
 | 
			
		||||
		&dl_ass_tbf, &ul_ack_tbf);
 | 
			
		||||
	/* check uplink resource for polling */
 | 
			
		||||
	if (poll_tbf)
 | 
			
		||||
@@ -316,24 +390,35 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	/* Prio 1: select control message */
 | 
			
		||||
	msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
 | 
			
		||||
		dl_ass_tbf, ul_ack_tbf);
 | 
			
		||||
	tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_CTRL);
 | 
			
		||||
 | 
			
		||||
	/* Prio 2: select data message for downlink */
 | 
			
		||||
	if (!msg)
 | 
			
		||||
	if (!msg) {
 | 
			
		||||
		msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch);
 | 
			
		||||
		tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_DATA_GPRS);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Prio 3: send dummy contol message */
 | 
			
		||||
	if (!msg)
 | 
			
		||||
	if (!msg) {
 | 
			
		||||
		/* increase counter */
 | 
			
		||||
		msg = sched_dummy();
 | 
			
		||||
		tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_DUMMY);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	/* msg is now available */
 | 
			
		||||
	bts->bts->rlc_dl_bytes(msg->data_len);
 | 
			
		||||
 | 
			
		||||
	/* set USF */
 | 
			
		||||
	OSMO_ASSERT(msgb_length(msg) > 0);
 | 
			
		||||
	msg->data[0] = (msg->data[0] & 0xf8) | usf;
 | 
			
		||||
 | 
			
		||||
	/* Used to measure the leak rate, count all blocks */
 | 
			
		||||
	gprs_bssgp_update_frames_sent();
 | 
			
		||||
 | 
			
		||||
	/* send PDTCH/PACCH to L1 */
 | 
			
		||||
	pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
 | 
			
		||||
	pcu_l1if_tx_pdtch(msg, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -4799,7 +4799,7 @@ void decode_gsm_rlcmac_uplink(bitvec * vector, RlcMacUplink_t * data)
 | 
			
		||||
{
 | 
			
		||||
  csnStream_t      ar;
 | 
			
		||||
  unsigned readIndex = 0;
 | 
			
		||||
  guint8 payload_type = bitvec_read_field(vector, readIndex, 2);
 | 
			
		||||
  guint8 payload_type = bitvec_read_field(vector, &readIndex, 2);
 | 
			
		||||
 | 
			
		||||
  if (payload_type == PAYLOAD_TYPE_DATA)
 | 
			
		||||
  {
 | 
			
		||||
@@ -4814,7 +4814,7 @@ void decode_gsm_rlcmac_uplink(bitvec * vector, RlcMacUplink_t * data)
 | 
			
		||||
  data->NrOfBits = 23 * 8;
 | 
			
		||||
  csnStreamInit(&ar, 0, data->NrOfBits);
 | 
			
		||||
  readIndex += 6;
 | 
			
		||||
  data->u.MESSAGE_TYPE = bitvec_read_field(vector, readIndex, 6);
 | 
			
		||||
  data->u.MESSAGE_TYPE = bitvec_read_field(vector, &readIndex, 6);
 | 
			
		||||
  readIndex = 0;
 | 
			
		||||
  switch (data->u.MESSAGE_TYPE)
 | 
			
		||||
  {
 | 
			
		||||
@@ -4907,10 +4907,10 @@ void decode_gsm_rlcmac_downlink(bitvec * vector, RlcMacDownlink_t * data)
 | 
			
		||||
  gint bit_offset = 0;
 | 
			
		||||
  gint bit_length;
 | 
			
		||||
  unsigned readIndex = 0;
 | 
			
		||||
  data->PAYLOAD_TYPE = bitvec_read_field(vector, readIndex, 2);
 | 
			
		||||
  data->RRBP = bitvec_read_field(vector, readIndex, 2);
 | 
			
		||||
  data->SP = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
  data->USF = bitvec_read_field(vector, readIndex, 3);
 | 
			
		||||
  data->PAYLOAD_TYPE = bitvec_read_field(vector, &readIndex, 2);
 | 
			
		||||
  data->RRBP = bitvec_read_field(vector, &readIndex, 2);
 | 
			
		||||
  data->SP = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
  data->USF = bitvec_read_field(vector, &readIndex, 3);
 | 
			
		||||
 | 
			
		||||
  if (data->PAYLOAD_TYPE == PAYLOAD_TYPE_DATA)
 | 
			
		||||
  {
 | 
			
		||||
@@ -4929,27 +4929,27 @@ void decode_gsm_rlcmac_downlink(bitvec * vector, RlcMacDownlink_t * data)
 | 
			
		||||
    bit_offset = 8;
 | 
			
		||||
    if (data->PAYLOAD_TYPE == PAYLOAD_TYPE_CTRL_OPT_OCTET)
 | 
			
		||||
    {
 | 
			
		||||
      data->RBSN = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
      data->RTI = bitvec_read_field(vector, readIndex, 5);
 | 
			
		||||
      data->FS = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
      data->AC = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
      data->RBSN = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
      data->RTI = bitvec_read_field(vector, &readIndex, 5);
 | 
			
		||||
      data->FS = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
      data->AC = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
      bit_offset += 8;
 | 
			
		||||
      if (data->AC == 1)
 | 
			
		||||
      {
 | 
			
		||||
        data->PR = bitvec_read_field(vector, readIndex, 2);
 | 
			
		||||
        data->TFI = bitvec_read_field(vector, readIndex, 5);
 | 
			
		||||
        data->D = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
	data->PR = bitvec_read_field(vector, &readIndex, 2);
 | 
			
		||||
	data->TFI = bitvec_read_field(vector, &readIndex, 5);
 | 
			
		||||
	data->D = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
        bit_offset += 8;
 | 
			
		||||
      }
 | 
			
		||||
      if ((data->RBSN == 1) && (data->FS == 0))
 | 
			
		||||
      {
 | 
			
		||||
        data->RBSNe = bitvec_read_field(vector, readIndex, 3);
 | 
			
		||||
        data->FSe = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
        data->spare = bitvec_read_field(vector, readIndex, 4);
 | 
			
		||||
	data->RBSNe = bitvec_read_field(vector, &readIndex, 3);
 | 
			
		||||
	data->FSe = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
	data->spare = bitvec_read_field(vector, &readIndex, 4);
 | 
			
		||||
        bit_offset += 8;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    data->u.MESSAGE_TYPE = bitvec_read_field(vector, readIndex, 6);
 | 
			
		||||
    data->u.MESSAGE_TYPE = bitvec_read_field(vector, &readIndex, 6);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Initialize the contexts */
 | 
			
		||||
@@ -5206,30 +5206,30 @@ void encode_gsm_rlcmac_downlink(bitvec * vector, RlcMacDownlink_t * data)
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    /* First print the message type and create a tree item */
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->PAYLOAD_TYPE, 2);
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->RRBP, 2);
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->SP, 1);
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->USF, 3);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->PAYLOAD_TYPE, 2);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->RRBP, 2);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->SP, 1);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->USF, 3);
 | 
			
		||||
    bit_offset = 8;
 | 
			
		||||
    if (data->PAYLOAD_TYPE == PAYLOAD_TYPE_CTRL_OPT_OCTET)
 | 
			
		||||
    {
 | 
			
		||||
      bitvec_write_field(vector, writeIndex, data->RBSN, 1);
 | 
			
		||||
      bitvec_write_field(vector, writeIndex, data->RTI, 5);
 | 
			
		||||
      bitvec_write_field(vector, writeIndex, data->FS, 1);
 | 
			
		||||
      bitvec_write_field(vector, writeIndex, data->AC, 1);
 | 
			
		||||
      bitvec_write_field(vector, &writeIndex, data->RBSN, 1);
 | 
			
		||||
      bitvec_write_field(vector, &writeIndex, data->RTI, 5);
 | 
			
		||||
      bitvec_write_field(vector, &writeIndex, data->FS, 1);
 | 
			
		||||
      bitvec_write_field(vector, &writeIndex, data->AC, 1);
 | 
			
		||||
      bit_offset += 8;
 | 
			
		||||
      if (data->AC == 1)
 | 
			
		||||
      {
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, data->PR, 2);
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, data->TFI, 5);
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, data->D, 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, data->PR, 2);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, data->TFI, 5);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, data->D, 1);
 | 
			
		||||
        bit_offset += 8;
 | 
			
		||||
      }
 | 
			
		||||
      if ((data->RBSN == 1) && (data->FS == 0))
 | 
			
		||||
      {
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, data->RBSNe, 3);
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, data->FSe, 1);
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, data->spare, 4);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, data->RBSNe, 3);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, data->FSe, 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, data->spare, 4);
 | 
			
		||||
        bit_offset += 8;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@@ -5378,32 +5378,32 @@ void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * da
 | 
			
		||||
{
 | 
			
		||||
  unsigned readIndex = 0;
 | 
			
		||||
  //unsigned dataLen = 0;
 | 
			
		||||
  guint8 payload_type = bitvec_read_field(vector, readIndex, 2);
 | 
			
		||||
  guint8 payload_type = bitvec_read_field(vector, &readIndex, 2);
 | 
			
		||||
  if (payload_type == PAYLOAD_TYPE_DATA)
 | 
			
		||||
  {
 | 
			
		||||
    readIndex = 0;
 | 
			
		||||
    // MAC header
 | 
			
		||||
    data->PAYLOAD_TYPE = bitvec_read_field(vector, readIndex, 2);
 | 
			
		||||
    data->CV = bitvec_read_field(vector, readIndex, 4);
 | 
			
		||||
    data->SI = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
    data->R = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
    data->PAYLOAD_TYPE = bitvec_read_field(vector, &readIndex, 2);
 | 
			
		||||
    data->CV = bitvec_read_field(vector, &readIndex, 4);
 | 
			
		||||
    data->SI = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
    data->R = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "PAYLOAD_TYPE = %u ", (unsigned)(data->PAYLOAD_TYPE));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "CV = %u ", (unsigned)(data->CV));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "SI = %u ", (unsigned)(data->SI));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "R = %u ", (unsigned)(data->R));
 | 
			
		||||
    // Octet 1
 | 
			
		||||
    data->spare = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
    data->PI = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
    data->TFI = bitvec_read_field(vector, readIndex, 5);
 | 
			
		||||
    data->TI = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
    data->spare = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
    data->PI = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
    data->TFI = bitvec_read_field(vector, &readIndex, 5);
 | 
			
		||||
    data->TI = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "spare = %u ", (unsigned)(data->spare));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "PI = %u ", (unsigned)(data->PI));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "TFI = %u ", (unsigned)(data->TFI));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "TI = %u ", (unsigned)(data->TI));
 | 
			
		||||
 | 
			
		||||
    // Octet 2
 | 
			
		||||
    data->BSN = bitvec_read_field(vector, readIndex, 7);
 | 
			
		||||
    data->E_1 = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
    data->BSN = bitvec_read_field(vector, &readIndex, 7);
 | 
			
		||||
    data->E_1 = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "BSN = %u ", (unsigned)(data->BSN));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "E_1 = %u ", (unsigned)(data->E_1));
 | 
			
		||||
 | 
			
		||||
@@ -5414,9 +5414,9 @@ void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * da
 | 
			
		||||
      unsigned i = 0;
 | 
			
		||||
      do
 | 
			
		||||
      {
 | 
			
		||||
        data->LENGTH_INDICATOR[i] = bitvec_read_field(vector, readIndex, 6);
 | 
			
		||||
        data->M[i] = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
        data->E[i] = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
	data->LENGTH_INDICATOR[i] = bitvec_read_field(vector, &readIndex, 6);
 | 
			
		||||
	data->M[i] = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
	data->E[i] = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
        LOGPC(DRLCMACDATA, LOGL_NOTICE, "LENGTH_INDICATOR[%u] = %u ", i, (unsigned)(data->LENGTH_INDICATOR[i]));
 | 
			
		||||
        LOGPC(DRLCMACDATA, LOGL_NOTICE, "M[%u] = %u ", i, (unsigned)(data->M[i]));
 | 
			
		||||
        LOGPC(DRLCMACDATA, LOGL_NOTICE, "E[%u] = %u ", i, (unsigned)(data->E[i]));
 | 
			
		||||
@@ -5425,12 +5425,12 @@ void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * da
 | 
			
		||||
    }
 | 
			
		||||
    if(data->TI == 1) // TLLI field is present
 | 
			
		||||
    {
 | 
			
		||||
      data->TLLI = bitvec_read_field(vector, readIndex, 32);
 | 
			
		||||
      data->TLLI = bitvec_read_field(vector, &readIndex, 32);
 | 
			
		||||
      LOGPC(DRLCMACDATA, LOGL_NOTICE, "TLLI = %08x ", data->TLLI);
 | 
			
		||||
      if (data->PI == 1) // PFI is present if TI field indicates presence of TLLI
 | 
			
		||||
      {
 | 
			
		||||
        data->PFI = bitvec_read_field(vector, readIndex, 7);
 | 
			
		||||
        data->E_2 = bitvec_read_field(vector, readIndex, 1);
 | 
			
		||||
	data->PFI = bitvec_read_field(vector, &readIndex, 7);
 | 
			
		||||
	data->E_2 = bitvec_read_field(vector, &readIndex, 1);
 | 
			
		||||
        LOGPC(DRLCMACDATA, LOGL_NOTICE, "PFI = %u ", (unsigned)(data->PFI));
 | 
			
		||||
        LOGPC(DRLCMACDATA, LOGL_NOTICE, "E_2 = %u ", (unsigned)(data->E_2));
 | 
			
		||||
      }
 | 
			
		||||
@@ -5440,7 +5440,7 @@ void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * da
 | 
			
		||||
    assert(dataLen <= 20);
 | 
			
		||||
    for (unsigned i = 0; i < dataLen; i++)
 | 
			
		||||
    {
 | 
			
		||||
      data->RLC_DATA[i] = bitvec_read_field(vector, readIndex, 8);
 | 
			
		||||
      data->RLC_DATA[i] = bitvec_read_field(vector, &readIndex, 8);
 | 
			
		||||
      LOGPC(DRLCMACDATA, LOGL_NOTICE, "%02x", (unsigned)(data->RLC_DATA[i]));
 | 
			
		||||
    }
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "\n");
 | 
			
		||||
@@ -5459,26 +5459,26 @@ void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t
 | 
			
		||||
  if (data->PAYLOAD_TYPE == PAYLOAD_TYPE_DATA)
 | 
			
		||||
  {
 | 
			
		||||
    // MAC header
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->PAYLOAD_TYPE, 2);
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->RRBP, 2);
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->SP, 1);
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->USF, 3);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->PAYLOAD_TYPE, 2);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->RRBP, 2);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->SP, 1);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->USF, 3);
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "PAYLOAD_TYPE = %u ", (unsigned)(data->PAYLOAD_TYPE));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "RRBP = %u ", (unsigned)(data->RRBP));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "SP = %u ", (unsigned)(data->SP));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "USF = %u ", (unsigned)(data->USF));
 | 
			
		||||
    
 | 
			
		||||
    // Octet 1
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->PR, 2);
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->TFI, 5);
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->FBI, 1);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->PR, 2);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->TFI, 5);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->FBI, 1);
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "PR = %u ", (unsigned)(data->PR));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "TFI = %u ", (unsigned)(data->TFI));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "FBI = %u ", (unsigned)(data->FBI));
 | 
			
		||||
 | 
			
		||||
    // Octet 2
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->BSN, 7);
 | 
			
		||||
    bitvec_write_field(vector, writeIndex, data->E_1, 1);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->BSN, 7);
 | 
			
		||||
    bitvec_write_field(vector, &writeIndex, data->E_1, 1);
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "BSN = %u ", (unsigned)(data->BSN));
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "E_1 = %u ", (unsigned)(data->E_1));
 | 
			
		||||
 | 
			
		||||
@@ -5488,9 +5488,9 @@ void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t
 | 
			
		||||
      unsigned i = 0;
 | 
			
		||||
      do
 | 
			
		||||
      {
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, data->LENGTH_INDICATOR[i], 6);
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, data->M[i], 1);
 | 
			
		||||
        bitvec_write_field(vector, writeIndex, data->E[i], 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, data->LENGTH_INDICATOR[i], 6);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, data->M[i], 1);
 | 
			
		||||
	bitvec_write_field(vector, &writeIndex, data->E[i], 1);
 | 
			
		||||
        LOGPC(DRLCMACDATA, LOGL_NOTICE, "LENGTH_INDICATOR[%u] = %u ", i, (unsigned)(data->LENGTH_INDICATOR[i]));
 | 
			
		||||
        LOGPC(DRLCMACDATA, LOGL_NOTICE, "M[%u] = %u ", i, (unsigned)(data->M[i]));
 | 
			
		||||
        LOGPC(DRLCMACDATA, LOGL_NOTICE, "E[%u] = %u ", i, (unsigned)(data->E[i]));
 | 
			
		||||
@@ -5503,9 +5503,17 @@ void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t
 | 
			
		||||
    assert(dataNumOctets <= 20);
 | 
			
		||||
    for (unsigned i = 0; i < dataNumOctets; i++)
 | 
			
		||||
    {
 | 
			
		||||
      bitvec_write_field(vector, writeIndex, data->RLC_DATA[i], 8);
 | 
			
		||||
      bitvec_write_field(vector, &writeIndex, data->RLC_DATA[i], 8);
 | 
			
		||||
      LOGPC(DRLCMACDATA, LOGL_NOTICE, "%02x", (unsigned)(data->RLC_DATA[i]));
 | 
			
		||||
    }
 | 
			
		||||
    LOGPC(DRLCMACDATA, LOGL_NOTICE, "\n");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void decode_gsm_ra_cap(bitvec * vector, MS_Radio_Access_capability_t *data)
 | 
			
		||||
{
 | 
			
		||||
  csnStream_t      ar;
 | 
			
		||||
  unsigned readIndex = 0;
 | 
			
		||||
  csnStreamInit(&ar, 0, 8 * vector->data_len);
 | 
			
		||||
  /*ret =*/ csnStreamDecoder(&ar, CSNDESCR(MS_Radio_Access_capability_t), vector, readIndex, data);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,11 +29,12 @@
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __PACKET_GSM_RLCMAC_H__
 | 
			
		||||
#define __PACKET_GSM_RLCMAC_H__
 | 
			
		||||
 | 
			
		||||
#include "csn1.h"
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#ifndef __PACKET_GSM_RLCMAC_H__
 | 
			
		||||
#define __PACKET_GSM_RLCMAC_H__
 | 
			
		||||
 | 
			
		||||
#ifndef PRE_PACKED
 | 
			
		||||
#define PRE_PACKED
 | 
			
		||||
@@ -476,7 +477,7 @@ typedef struct
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  guint8   LENGTH;
 | 
			
		||||
  /* guint8   LENGTH; */
 | 
			
		||||
  EGPRS_AckNack_Desc_t Desc;
 | 
			
		||||
} EGPRS_AckNack_w_len_t;
 | 
			
		||||
 | 
			
		||||
@@ -5136,4 +5137,5 @@ typedef struct
 | 
			
		||||
 void encode_gsm_rlcmac_uplink(bitvec * vector, RlcMacUplink_t * data);
 | 
			
		||||
 void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * data);
 | 
			
		||||
 void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t * data);
 | 
			
		||||
 void decode_gsm_ra_cap(bitvec * vector, MS_Radio_Access_capability_t * data);
 | 
			
		||||
#endif /* __PACKET_GSM_RLCMAC_H__ */
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ static struct rb_root timer_root = RB_ROOT;
 | 
			
		||||
 * all time functions schedule based on the BTS they
 | 
			
		||||
 * are scheduled on.
 | 
			
		||||
 */
 | 
			
		||||
static int get_current_fn()
 | 
			
		||||
int get_current_fn()
 | 
			
		||||
{
 | 
			
		||||
	return BTS::main_bts()->current_frame_number();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -79,6 +79,12 @@ void osmo_gsm_timers_prepare(void);
 | 
			
		||||
int osmo_gsm_timers_update(void);
 | 
			
		||||
int osmo_gsm_timers_check(void);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Get Current Frame Number
 | 
			
		||||
 */
 | 
			
		||||
int get_current_fn();
 | 
			
		||||
 | 
			
		||||
/*! }@ */
 | 
			
		||||
 | 
			
		||||
#endif // GSM_TIMER_H
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								src/llc.cpp
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								src/llc.cpp
									
									
									
									
									
								
							@@ -19,7 +19,6 @@
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
@@ -134,6 +133,59 @@ void gprs_llc_queue::clear(BTS *bts)
 | 
			
		||||
	m_queue_octets = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
 | 
			
		||||
	struct llist_head new_queue;
 | 
			
		||||
	size_t queue_size = 0;
 | 
			
		||||
	size_t queue_octets = 0;
 | 
			
		||||
	INIT_LLIST_HEAD(&new_queue);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		if (msg1 == NULL)
 | 
			
		||||
			msg1 = msgb_dequeue(&m_queue);
 | 
			
		||||
 | 
			
		||||
		if (msg2 == NULL)
 | 
			
		||||
			msg2 = msgb_dequeue(&o->m_queue);
 | 
			
		||||
 | 
			
		||||
		if (msg1 == NULL && msg2 == NULL)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (msg1 == NULL) {
 | 
			
		||||
			msg = msg2;
 | 
			
		||||
			msg2 = NULL;
 | 
			
		||||
		} else if (msg2 == NULL) {
 | 
			
		||||
			msg = msg1;
 | 
			
		||||
			msg1 = NULL;
 | 
			
		||||
		} else {
 | 
			
		||||
			const MetaInfo *mi1 = (MetaInfo *)&msg1->cb[0];
 | 
			
		||||
			const MetaInfo *mi2 = (MetaInfo *)&msg2->cb[0];
 | 
			
		||||
 | 
			
		||||
			if (timercmp(&mi2->recv_time, &mi1->recv_time, >)) {
 | 
			
		||||
				msg = msg1;
 | 
			
		||||
				msg1 = NULL;
 | 
			
		||||
			} else {
 | 
			
		||||
				msg = msg2;
 | 
			
		||||
				msg2 = NULL;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		msgb_enqueue(&new_queue, msg);
 | 
			
		||||
		queue_size += 1;
 | 
			
		||||
		queue_octets += msgb_length(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(llist_empty(&m_queue));
 | 
			
		||||
	OSMO_ASSERT(llist_empty(&o->m_queue));
 | 
			
		||||
 | 
			
		||||
	o->m_queue_size = 0;
 | 
			
		||||
	o->m_queue_octets = 0;
 | 
			
		||||
 | 
			
		||||
	llist_splice_init(&new_queue, &m_queue);
 | 
			
		||||
	m_queue_size = queue_size;
 | 
			
		||||
	m_queue_octets = queue_octets;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define ALPHA 0.5f
 | 
			
		||||
 | 
			
		||||
struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,6 @@ extern "C" {
 | 
			
		||||
#define LLC_MAX_LEN 1543
 | 
			
		||||
 | 
			
		||||
struct BTS;
 | 
			
		||||
struct timeval;
 | 
			
		||||
struct msgb;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * I represent the LLC data to a MS
 | 
			
		||||
@@ -80,6 +78,7 @@ struct gprs_llc_queue {
 | 
			
		||||
	void enqueue(struct msgb *llc_msg, const MetaInfo *info = 0);
 | 
			
		||||
	struct msgb *dequeue(const MetaInfo **info = 0);
 | 
			
		||||
	void clear(BTS *bts);
 | 
			
		||||
	void move_and_merge(gprs_llc_queue *o);
 | 
			
		||||
	size_t size() const;
 | 
			
		||||
	size_t octets() const;
 | 
			
		||||
 | 
			
		||||
@@ -126,10 +125,10 @@ inline bool gprs_llc::fits_in_current_frame(uint8_t chunk_size) const
 | 
			
		||||
 | 
			
		||||
inline size_t gprs_llc_queue::size() const
 | 
			
		||||
{
 | 
			
		||||
	return this ? m_queue_size : 0;
 | 
			
		||||
	return m_queue_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline size_t gprs_llc_queue::octets() const
 | 
			
		||||
{
 | 
			
		||||
	return this ? m_queue_octets : 0;
 | 
			
		||||
	return m_queue_octets;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										297
									
								
								src/mslot_class.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								src/mslot_class.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,297 @@
 | 
			
		||||
/* mslot_class.c
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 * Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <mslot_class.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/bits.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
/* 3GPP TS 45.002 Annex B Table B.1 */
 | 
			
		||||
 | 
			
		||||
struct gprs_ms_multislot_class {
 | 
			
		||||
	uint8_t rx, tx, sum;	/* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */
 | 
			
		||||
	uint8_t ta, tb, ra, rb;	/* Minimum Number of Slots */
 | 
			
		||||
	uint8_t type; /* Type of Mobile */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct gprs_ms_multislot_class gprs_ms_multislot_class[] = {
 | 
			
		||||
	/* M-S Class |  Max # of slots |       Min # of slots      | Type */
 | 
			
		||||
	/*           | Rx     Tx   Sum |  Tta    Ttb    Tra    Trb |      */
 | 
			
		||||
	/* N/A */ { MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA },
 | 
			
		||||
	/*  1 */  {   1,     1,     2,     3,     2,     4,     2,     1 },
 | 
			
		||||
	/*  2 */  {   2,     1,     3,     3,     2,     3,     1,     1 },
 | 
			
		||||
	/*  3 */  {   2,     2,     3,     3,     2,     3,     1,     1 },
 | 
			
		||||
	/*  4 */  {   3,     1,     4,     3,     1,     3,     1,     1 },
 | 
			
		||||
	/*  5 */  {   2,     2,     4,     3,     1,     3,     1,     1 },
 | 
			
		||||
	/*  6 */  {   3,     2,     4,     3,     1,     3,     1,     1 },
 | 
			
		||||
	/*  7 */  {   3,     3,     4,     3,     1,     3,     1,     1 },
 | 
			
		||||
	/*  8 */  {   4,     1,     5,     3,     1,     2,     1,     1 },
 | 
			
		||||
	/*  9 */  {   3,     2,     5,     3,     1,     2,     1,     1 },
 | 
			
		||||
	/* 10 */  {   4,     2,     5,     3,     1,     2,     1,     1 },
 | 
			
		||||
	/* 11 */  {   4,     3,     5,     3,     1,     2,     1,     1 },
 | 
			
		||||
	/* 12 */  {   4,     4,     5,     2,     1,     2,     1,     1 },
 | 
			
		||||
	/* 13 */  {   3,     3,   MS_NA, MS_NA, MS_A,    3,   MS_A,    2 },
 | 
			
		||||
	/* 14 */  {   4,     4,   MS_NA, MS_NA, MS_A,    3,   MS_A,    2 },
 | 
			
		||||
	/* 15 */  {   5,     5,   MS_NA, MS_NA, MS_A,    3,   MS_A,    2 },
 | 
			
		||||
	/* 16 */  {   6,     6,   MS_NA, MS_NA, MS_A,    2,   MS_A,    2 },
 | 
			
		||||
	/* 17 */  {   7,     7,   MS_NA, MS_NA, MS_A,    1,     0,     2 },
 | 
			
		||||
	/* 18 */  {   8,     8,   MS_NA, MS_NA,   0,     0,     0,     2 },
 | 
			
		||||
	/* 19 */  {   6,     2,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 20 */  {   6,     3,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 21 */  {   6,     4,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 22 */  {   6,     4,   MS_NA,   2,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 23 */  {   6,     6,   MS_NA,   2,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 24 */  {   8,     2,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 25 */  {   8,     3,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 26 */  {   8,     4,   MS_NA,   3,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 27 */  {   8,     4,   MS_NA,   2,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 28 */  {   8,     6,   MS_NA,   2,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 29 */  {   8,     8,   MS_NA,   2,   MS_B,    2,   MS_C,    1 },
 | 
			
		||||
	/* 30 */  {   5,     1,     6,     2,     1,     1,     1,     1 },
 | 
			
		||||
	/* 31 */  {   5,     2,     6,     2,     1,     1,     1,     1 },
 | 
			
		||||
	/* 32 */  {   5,     3,     6,     2,     1,     1,     1,     1 },
 | 
			
		||||
	/* 33 */  {   5,     4,     6,     2,     1,     1,     1,     1 },
 | 
			
		||||
	/* 34 */  {   5,     5,     6,     2,     1,     1,     1,     1 },
 | 
			
		||||
	/* 35 */  {   5,     1,     6,     2,     1,   MS_TO,   1,     1 },
 | 
			
		||||
	/* 36 */  {   5,     2,     6,     2,     1,   MS_TO,   1,     1 },
 | 
			
		||||
	/* 37 */  {   5,     3,     6,     2,     1,   MS_TO,   1,     1 },
 | 
			
		||||
	/* 38 */  {   5,     4,     6,     2,     1,   MS_TO,   1,     1 },
 | 
			
		||||
	/* 39 */  {   5,     5,     6,     2,     1,   MS_TO,   1,     1 },
 | 
			
		||||
	/* 40 */  {   6,     1,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
	/* 41 */  {   6,     2,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
	/* 42 */  {   6,     3,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
	/* 43 */  {   6,     4,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
	/* 44 */  {   6,     5,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
	/* 45 */  {   6,     6,     7,     1,     1,     1,   MS_TO,   1 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline const struct gprs_ms_multislot_class *get_mslot_table(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t index = ms_cl ? ms_cl : DEFAULT_MSLOT_CLASS;
 | 
			
		||||
 | 
			
		||||
	if (ms_cl >= ARRAY_SIZE(gprs_ms_multislot_class))
 | 
			
		||||
		index = 0;
 | 
			
		||||
 | 
			
		||||
	return &gprs_ms_multislot_class[index];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_max()
 | 
			
		||||
{
 | 
			
		||||
	return ARRAY_SIZE(gprs_ms_multislot_class);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_ta(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	return get_mslot_table(ms_cl)->ta;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO: Set it to 1 if FH is implemented and enabled
 | 
			
		||||
 * MS_A and MS_B are 0 iff FH is disabled and there is no Tx/Rx change.
 | 
			
		||||
 * This is never the case with the current implementation, so 1 will always be used. */
 | 
			
		||||
uint8_t mslot_class_get_tb(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
 | 
			
		||||
 | 
			
		||||
	switch (t->tb) {
 | 
			
		||||
	case MS_A:
 | 
			
		||||
		return 0;
 | 
			
		||||
	case MS_B:
 | 
			
		||||
		return 1;
 | 
			
		||||
	default:
 | 
			
		||||
		return t->tb;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_ra(uint8_t ms_cl, uint8_t ta)
 | 
			
		||||
{
 | 
			
		||||
	const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
 | 
			
		||||
 | 
			
		||||
	switch (t->ra) {
 | 
			
		||||
	case MS_TO:
 | 
			
		||||
		return ta + 1;
 | 
			
		||||
	default:
 | 
			
		||||
		return t->ra;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_rb(uint8_t ms_cl, uint8_t ta)
 | 
			
		||||
{
 | 
			
		||||
	const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
 | 
			
		||||
 | 
			
		||||
	switch (t->rb) {
 | 
			
		||||
	case MS_A:
 | 
			
		||||
		return 0;
 | 
			
		||||
	case MS_C:
 | 
			
		||||
		return 1;
 | 
			
		||||
	case MS_TO:
 | 
			
		||||
		return ta;
 | 
			
		||||
	default:
 | 
			
		||||
		return t->rb;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_tx(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	return get_mslot_table(ms_cl)->tx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_rx(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	return get_mslot_table(ms_cl)->rx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_sum(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	return get_mslot_table(ms_cl)->sum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t mslot_class_get_type(uint8_t ms_cl)
 | 
			
		||||
{
 | 
			
		||||
	return get_mslot_table(ms_cl)->type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Fill in RX mask table for a given MS Class
 | 
			
		||||
 *
 | 
			
		||||
 *  \param[in] ms_cl MS Class pointer
 | 
			
		||||
 *  \param[in] num_tx Number of TX slots to consider
 | 
			
		||||
 *  \param[out] rx_mask RX mask table
 | 
			
		||||
 */
 | 
			
		||||
void mslot_fill_rx_mask(uint8_t mslot_class, uint8_t num_tx, uint8_t *rx_mask)
 | 
			
		||||
{
 | 
			
		||||
	static const char *digit[10] = { "0","1","2","3","4","5","6","7","8","9" };
 | 
			
		||||
	uint8_t Tx = mslot_class_get_tx(mslot_class),     /* Max number of Tx slots */
 | 
			
		||||
		Sum = mslot_class_get_sum(mslot_class),	  /* Max number of Tx + Rx slots */
 | 
			
		||||
		Type = mslot_class_get_type(mslot_class), /* Type of Mobile */
 | 
			
		||||
		Tta = mslot_class_get_ta(mslot_class),    /* Minimum number of slots */
 | 
			
		||||
		Ttb = mslot_class_get_tb(mslot_class),
 | 
			
		||||
		/* FIXME: use actual TA offset for computation - make sure to adjust "1 + MS_TO" accordingly
 | 
			
		||||
		   see also "Offset required" bit in 3GPP TS 24.008 §10.5.1.7 */
 | 
			
		||||
		Tra = mslot_class_get_ra(mslot_class, 0),
 | 
			
		||||
		Trb = mslot_class_get_rb(mslot_class, 0);
 | 
			
		||||
 | 
			
		||||
	if (num_tx == 1) /* it's enough to log this once per TX slot set iteration */
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_DEBUG,
 | 
			
		||||
		     "Rx=%d Tx=%d Sum Rx+Tx=%s, Tta=%s Ttb=%d, Tra=%d Trb=%d, Type=%d\n",
 | 
			
		||||
		     mslot_class_get_rx(mslot_class), Tx,
 | 
			
		||||
		     (Sum == MS_NA) ? "N/A" : digit[Sum],
 | 
			
		||||
		     (Tta == MS_NA) ? "N/A" : digit[Tta], Ttb, Tra, Trb, Type);
 | 
			
		||||
 | 
			
		||||
	if (Type == 1) {
 | 
			
		||||
		rx_mask[MASK_TT] = (0x100 >> OSMO_MAX(Ttb, Tta)) - 1;
 | 
			
		||||
		rx_mask[MASK_TT] &= ~((1 << (Trb + num_tx)) - 1);
 | 
			
		||||
		rx_mask[MASK_TR] = (0x100 >> Ttb) - 1;
 | 
			
		||||
		rx_mask[MASK_TR] &= ~((1 << (OSMO_MAX(Trb, Tra) + num_tx)) - 1);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Class type 2 MS have independant RX and TX */
 | 
			
		||||
		rx_mask[MASK_TT] = 0xff;
 | 
			
		||||
		rx_mask[MASK_TR] = 0xff;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rx_mask[MASK_TT] = (rx_mask[MASK_TT] << 3) | (rx_mask[MASK_TT] >> 5);
 | 
			
		||||
	rx_mask[MASK_TR] = (rx_mask[MASK_TR] << 3) | (rx_mask[MASK_TR] >> 5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* look for USF, don't use USF=7 */
 | 
			
		||||
int8_t find_free_usf(uint8_t usf_map)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t usf;
 | 
			
		||||
 | 
			
		||||
	if (usf_map == (1 << 7) - 1)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	for (usf = 0; usf < 7; usf++) {
 | 
			
		||||
		if (!(usf_map & (1 << usf)))
 | 
			
		||||
			return usf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* look for USF, don't use USF=7 */
 | 
			
		||||
int8_t find_free_tfi(uint32_t tfi_map)
 | 
			
		||||
{
 | 
			
		||||
	int8_t tfi;
 | 
			
		||||
 | 
			
		||||
	if (tfi_map == NO_FREE_TFI)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	for (tfi = 0; tfi < 32; tfi++) {
 | 
			
		||||
		if (!(tfi_map & (((uint32_t)1) << tfi)))
 | 
			
		||||
			return tfi;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void masked_override_with(char *buf, uint8_t mask, char set_char)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; mask; i++, mask >>= 1)
 | 
			
		||||
		if (mask & 1)
 | 
			
		||||
			buf[i] = set_char;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ts_format(char *buf, uint8_t dl_mask, uint8_t ul_mask)
 | 
			
		||||
{
 | 
			
		||||
	snprintf(buf, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(dl_mask, 'D'));
 | 
			
		||||
	masked_override_with(buf, ul_mask, 'U');
 | 
			
		||||
	masked_override_with(buf, ul_mask & dl_mask, 'C');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t mslot_wrap_window(uint16_t win)
 | 
			
		||||
{
 | 
			
		||||
	return (win | win >> 8) & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool mslot_test_and_set_bit(uint32_t *bits, size_t elem)
 | 
			
		||||
{
 | 
			
		||||
	bool was_set = bits[elem/32] & (((uint32_t)1) << (elem % 32));
 | 
			
		||||
	bits[elem/32] |= (((uint32_t)1) << (elem % 32));
 | 
			
		||||
 | 
			
		||||
	return was_set;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Filter out bad slots
 | 
			
		||||
 *
 | 
			
		||||
 *  \param[in] mask TS selection mask
 | 
			
		||||
 *  \param[in] ul_slots set of UL timeslots
 | 
			
		||||
 *  \param[in] dl_slots set of DL timeslots
 | 
			
		||||
 *  \param[in] rx_valid_win Mask for valid RX window value
 | 
			
		||||
 *  \returns negative error code or RX window on success
 | 
			
		||||
 */
 | 
			
		||||
int16_t mslot_filter_bad(uint8_t mask, uint8_t ul_slots, uint8_t dl_slots, uint16_t rx_valid_win)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t rx_good;
 | 
			
		||||
	uint16_t rx_bad = (uint16_t)(0xFF & ~mask) << ul_slots;
 | 
			
		||||
 | 
			
		||||
	/* TODO: CHECK this calculation -> separate function for unit testing */
 | 
			
		||||
	rx_bad = (rx_bad | (rx_bad >> 8)) & 0xFF;
 | 
			
		||||
	rx_good = dl_slots & ~rx_bad;
 | 
			
		||||
	if (!rx_good)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	return rx_good & rx_valid_win;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										62
									
								
								src/mslot_class.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/mslot_class.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
/* mslot_class.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 * Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
/* 3GPP TS 05.02 Annex B.1 */
 | 
			
		||||
 | 
			
		||||
#define MS_NA	255 /* N/A */
 | 
			
		||||
#define MS_A	254 /* 1 with hopping, 0 without */
 | 
			
		||||
#define MS_B	253 /* 1 with hopping, 0 without (change Rx to Tx)*/
 | 
			
		||||
#define MS_C	252 /* 1 with hopping, 0 without (change Tx to Rx)*/
 | 
			
		||||
#define MS_TO	251 /* 31 symbol periods (this can be provided by a TA offset, i.e. a minimum TA value) */
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_MSLOT_CLASS 12
 | 
			
		||||
 | 
			
		||||
#define NO_FREE_TFI 0xffffffff
 | 
			
		||||
 | 
			
		||||
enum { MASK_TT = 0, MASK_TR = 1 };
 | 
			
		||||
 | 
			
		||||
/* multislot class selection routines */
 | 
			
		||||
uint8_t mslot_class_get_ta(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_get_tb(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_get_ra(uint8_t ms_cl, uint8_t ta);
 | 
			
		||||
uint8_t mslot_class_get_rb(uint8_t ms_cl, uint8_t ta);
 | 
			
		||||
uint8_t mslot_class_get_tx(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_get_rx(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_get_sum(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_get_type(uint8_t ms_cl);
 | 
			
		||||
uint8_t mslot_class_max();
 | 
			
		||||
 | 
			
		||||
/* multislot allocation helper routines */
 | 
			
		||||
void mslot_fill_rx_mask(uint8_t mslot_class, uint8_t num_tx, uint8_t *rx_mask);
 | 
			
		||||
int8_t find_free_usf(uint8_t usf_map);
 | 
			
		||||
int8_t find_free_tfi(uint32_t tfi_map);
 | 
			
		||||
void masked_override_with(char *buf, uint8_t mask, char set_char);
 | 
			
		||||
void ts_format(char *buf, uint8_t dl_mask, uint8_t ul_mask);
 | 
			
		||||
uint16_t mslot_wrap_window(uint16_t win);
 | 
			
		||||
bool mslot_test_and_set_bit(uint32_t *bits, size_t elem);
 | 
			
		||||
int16_t mslot_filter_bad(uint8_t mask, uint8_t ul_slots, uint8_t dl_slots, uint16_t rx_valid_win);
 | 
			
		||||
@@ -1,191 +0,0 @@
 | 
			
		||||
/* openbts_sock.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gprs_bssgp_pcu.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <bitvector.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
#include <pcuif_proto.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
struct femtol1_hdl {
 | 
			
		||||
	struct gsm_time gsm_time;
 | 
			
		||||
	uint32_t hLayer1;			/* handle to the L1 instance in the DSP */
 | 
			
		||||
	uint32_t dsp_trace_f;
 | 
			
		||||
	uint16_t clk_cal;
 | 
			
		||||
	struct llist_head wlc_list;
 | 
			
		||||
 | 
			
		||||
	void *priv;			/* user reference */
 | 
			
		||||
 | 
			
		||||
	struct osmo_timer_list alive_timer;
 | 
			
		||||
	unsigned int alive_prim_cnt;
 | 
			
		||||
 | 
			
		||||
	struct osmo_fd read_ofd;	/* osmo file descriptors */
 | 
			
		||||
	struct osmo_wqueue write_q;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		uint16_t arfcn;
 | 
			
		||||
		uint8_t tn;
 | 
			
		||||
		uint8_t tsc;
 | 
			
		||||
		uint16_t ta;
 | 
			
		||||
	} channel_info;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct l1fwd_hdl {
 | 
			
		||||
	struct sockaddr_storage remote_sa;
 | 
			
		||||
	socklen_t remote_sa_len;
 | 
			
		||||
 | 
			
		||||
	struct osmo_wqueue udp_wq;
 | 
			
		||||
 | 
			
		||||
	struct femtol1_hdl *fl1h;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
 | 
			
		||||
 | 
			
		||||
// TODO: We should move this parameters to config file.
 | 
			
		||||
#define PCU_L1_IF_PORT 5944
 | 
			
		||||
 | 
			
		||||
/* OpenBTS socket functions */
 | 
			
		||||
 | 
			
		||||
int pcu_sock_send(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	if (osmo_wqueue_enqueue(&l1fh->udp_wq, msg) != 0) {
 | 
			
		||||
		LOGP(DPCU, LOGL_ERROR, "PCU write queue full. Dropping message.\n");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* data has arrived on the udp socket */
 | 
			
		||||
static int udp_read_cb(struct osmo_fd *ofd)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
 | 
			
		||||
	struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
 | 
			
		||||
	struct femtol1_hdl *fl1h = l1fh->fl1h;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	msg->l1h = msg->data;
 | 
			
		||||
 | 
			
		||||
	l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
 | 
			
		||||
	rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
 | 
			
		||||
			(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		perror("read from udp");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return rc;
 | 
			
		||||
	} else if (rc == 0) {
 | 
			
		||||
		perror("len=0 read from udp");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
	msgb_put(msg, rc);
 | 
			
		||||
 | 
			
		||||
	struct gsm_pcu_if *pcu_prim = (gsm_pcu_if *)(msg->l1h);
 | 
			
		||||
	rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* callback when we can write to the UDP socket */
 | 
			
		||||
static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
 | 
			
		||||
 | 
			
		||||
	//LOGP(DPCU, LOGL_ERROR, "UDP: Writing %u bytes for MQ_L1_WRITE queue\n", msgb_length(msg));
 | 
			
		||||
 | 
			
		||||
	rc = sendto(ofd->fd, msgb_data(msg), msgb_length(msg), 0,
 | 
			
		||||
			(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DPCU, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
 | 
			
		||||
			strerror(errno));
 | 
			
		||||
		return rc;
 | 
			
		||||
	} else if (rc < (int)msgb_length(msg)) {
 | 
			
		||||
		LOGP(DPCU, LOGL_ERROR, "short write to L1 msg_queue: "
 | 
			
		||||
			"%u < %u\n", rc, msgb_length(msg));
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int pcu_l1if_open()
 | 
			
		||||
{
 | 
			
		||||
	struct femtol1_hdl *fl1h;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* allocate new femtol1_handle */
 | 
			
		||||
	fl1h = talloc_zero(tall_pcu_ctx, struct femtol1_hdl);
 | 
			
		||||
	INIT_LLIST_HEAD(&fl1h->wlc_list);
 | 
			
		||||
 | 
			
		||||
	l1fh->fl1h = fl1h;
 | 
			
		||||
	fl1h->priv = l1fh;
 | 
			
		||||
 | 
			
		||||
	struct osmo_wqueue * queue = &((l1fh->fl1h)->write_q);
 | 
			
		||||
	osmo_wqueue_init(queue, 10);
 | 
			
		||||
	queue->bfd.when |= BSC_FD_READ;
 | 
			
		||||
	queue->bfd.data = l1fh;
 | 
			
		||||
	queue->bfd.priv_nr = 0;
 | 
			
		||||
 | 
			
		||||
	/* Open UDP */
 | 
			
		||||
	struct osmo_wqueue *wq = &l1fh->udp_wq;
 | 
			
		||||
 | 
			
		||||
	osmo_wqueue_init(wq, 10);
 | 
			
		||||
	wq->write_cb = udp_write_cb;
 | 
			
		||||
	wq->read_cb = udp_read_cb;
 | 
			
		||||
	wq->bfd.when |= BSC_FD_READ;
 | 
			
		||||
	wq->bfd.data = l1fh;
 | 
			
		||||
	wq->bfd.priv_nr = 0;
 | 
			
		||||
	rc = osmo_sock_init_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM,
 | 
			
		||||
				IPPROTO_UDP, NULL, PCU_L1_IF_PORT,
 | 
			
		||||
				OSMO_SOCK_F_BIND);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		perror("sock_init");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pcu_l1if_close(void)
 | 
			
		||||
{
 | 
			
		||||
	gprs_bssgp_destroy();
 | 
			
		||||
 | 
			
		||||
	/* FIXME: cleanup l1if */
 | 
			
		||||
	talloc_free(l1fh->fl1h);
 | 
			
		||||
 | 
			
		||||
	exit(0);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										213
									
								
								src/osmo-bts-litecell15/lc15_l1_hw.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								src/osmo-bts-litecell15/lc15_l1_hw.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,213 @@
 | 
			
		||||
/* Interface handler for Nuran Wireless Litecell 1.5 L1 (real hardware) */
 | 
			
		||||
 | 
			
		||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
 | 
			
		||||
 * based on:
 | 
			
		||||
 *     femto_l1_hw.c
 | 
			
		||||
 *     (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 *
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
 | 
			
		||||
#include <nrw/litecell15/litecell15.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1prim.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1const.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1types.h>
 | 
			
		||||
 | 
			
		||||
#include "gprs_debug.h"
 | 
			
		||||
#include "lc15bts.h"
 | 
			
		||||
#include "lc15_l1_if.h"
 | 
			
		||||
 | 
			
		||||
#define DEV_SYS_DSP2ARM_NAME    "/dev/msgq/litecell15_dsp2arm_trx"
 | 
			
		||||
#define DEV_SYS_ARM2DSP_NAME    "/dev/msgq/litecell15_arm2dsp_trx"
 | 
			
		||||
#define DEV_L1_DSP2ARM_NAME     "/dev/msgq/gsml1_sig_dsp2arm_trx"
 | 
			
		||||
#define DEV_L1_ARM2DSP_NAME     "/dev/msgq/gsml1_sig_arm2dsp_trx"
 | 
			
		||||
 | 
			
		||||
#define DEV_TCH_DSP2ARM_NAME    "/dev/msgq/gsml1_tch_dsp2arm_trx"
 | 
			
		||||
#define DEV_TCH_ARM2DSP_NAME    "/dev/msgq/gsml1_tch_arm2dsp_trx"
 | 
			
		||||
#define DEV_PDTCH_DSP2ARM_NAME  "/dev/msgq/gsml1_pdtch_dsp2arm_trx"
 | 
			
		||||
#define DEV_PDTCH_ARM2DSP_NAME  "/dev/msgq/gsml1_pdtch_arm2dsp_trx"
 | 
			
		||||
 | 
			
		||||
static const char *rd_devnames[] = {
 | 
			
		||||
        [MQ_SYS_READ]   = DEV_SYS_DSP2ARM_NAME,
 | 
			
		||||
        [MQ_L1_READ]    = DEV_L1_DSP2ARM_NAME,
 | 
			
		||||
        [MQ_TCH_READ]   = DEV_TCH_DSP2ARM_NAME,
 | 
			
		||||
        [MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *wr_devnames[] = {
 | 
			
		||||
        [MQ_SYS_WRITE]  = DEV_SYS_ARM2DSP_NAME,
 | 
			
		||||
        [MQ_L1_WRITE]   = DEV_L1_ARM2DSP_NAME,
 | 
			
		||||
        [MQ_TCH_WRITE]  = DEV_TCH_ARM2DSP_NAME,
 | 
			
		||||
        [MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* callback when there's something to read from the l1 msg_queue */
 | 
			
		||||
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	//struct msgb *msg = l1p_msgb_alloc();
 | 
			
		||||
	struct msgb *msg = msgb_alloc_headroom(sizeof(Litecell15_Prim_t) + 128,
 | 
			
		||||
		128, "1l_fd");
 | 
			
		||||
	struct lc15l1_hdl *fl1h = ofd->data;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	msg->l1h = msg->data;
 | 
			
		||||
	rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		if (rc != -1)
 | 
			
		||||
			LOGP(DL1IF, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
 | 
			
		||||
				strerror(errno));
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
	msgb_put(msg, rc);
 | 
			
		||||
 | 
			
		||||
	switch (ofd->priv_nr) {
 | 
			
		||||
	case MQ_SYS_WRITE:
 | 
			
		||||
		if (rc != sizeof(Litecell15_Prim_t))
 | 
			
		||||
			LOGP(DL1IF, LOGL_NOTICE, "%u != "
 | 
			
		||||
			     "sizeof(Litecell15_Prim_t)\n", rc);
 | 
			
		||||
		return l1if_handle_sysprim(fl1h, msg);
 | 
			
		||||
	case MQ_L1_WRITE:
 | 
			
		||||
	case MQ_TCH_WRITE:
 | 
			
		||||
	case MQ_PDTCH_WRITE:
 | 
			
		||||
		if (rc != sizeof(GsmL1_Prim_t))
 | 
			
		||||
			LOGP(DL1IF, LOGL_NOTICE, "%u != "
 | 
			
		||||
			     "sizeof(GsmL1_Prim_t)\n", rc);
 | 
			
		||||
		return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
 | 
			
		||||
	default:
 | 
			
		||||
		/* The compiler can't know that priv_nr is an enum. Assist. */
 | 
			
		||||
		LOGP(DL1IF, LOGL_FATAL, "writing on a wrong queue: %d\n",
 | 
			
		||||
			ofd->priv_nr);
 | 
			
		||||
		exit(0);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* callback when we can write to one of the l1 msg_queue devices */
 | 
			
		||||
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = write(ofd->fd, msg->l1h, msgb_l1len(msg));
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
 | 
			
		||||
			strerror(errno));
 | 
			
		||||
		return rc;
 | 
			
		||||
	} else if (rc < msg->len) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "short write to L1 msg_queue: "
 | 
			
		||||
			"%u < %u\n", rc, msg->len);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int l1if_transport_open(int q, struct lc15l1_hdl *hdl)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
        char buf[PATH_MAX];
 | 
			
		||||
 | 
			
		||||
	/* Step 1: Open all msg_queue file descriptors */
 | 
			
		||||
	struct osmo_fd *read_ofd = &hdl->read_ofd[q];
 | 
			
		||||
	struct osmo_wqueue *wq = &hdl->write_q[q];
 | 
			
		||||
	struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
 | 
			
		||||
 | 
			
		||||
        snprintf(buf, sizeof(buf)-1, "%s%d", rd_devnames[q], hdl->hw_info.trx_nr);
 | 
			
		||||
        buf[sizeof(buf)-1] = '\0';
 | 
			
		||||
 | 
			
		||||
	rc = open(buf, O_RDONLY);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
 | 
			
		||||
			buf, strerror(errno));
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
	read_ofd->fd = rc;
 | 
			
		||||
	read_ofd->priv_nr = q;
 | 
			
		||||
	read_ofd->data = hdl;
 | 
			
		||||
	read_ofd->cb = l1if_fd_cb;
 | 
			
		||||
	read_ofd->when = BSC_FD_READ;
 | 
			
		||||
	rc = osmo_fd_register(read_ofd);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		close(read_ofd->fd);
 | 
			
		||||
		read_ofd->fd = -1;
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
        snprintf(buf, sizeof(buf)-1, "%s%d", wr_devnames[q], hdl->hw_info.trx_nr);
 | 
			
		||||
        buf[sizeof(buf)-1] = '\0';
 | 
			
		||||
 | 
			
		||||
	rc = open(buf, O_WRONLY);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
 | 
			
		||||
			buf, strerror(errno));
 | 
			
		||||
		goto out_read;
 | 
			
		||||
	}
 | 
			
		||||
	osmo_wqueue_init(wq, 10);
 | 
			
		||||
	wq->write_cb = l1fd_write_cb;
 | 
			
		||||
	write_ofd->fd = rc;
 | 
			
		||||
	write_ofd->priv_nr = q;
 | 
			
		||||
	write_ofd->data = hdl;
 | 
			
		||||
	write_ofd->when = BSC_FD_WRITE;
 | 
			
		||||
	rc = osmo_fd_register(write_ofd);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		close(write_ofd->fd);
 | 
			
		||||
		write_ofd->fd = -1;
 | 
			
		||||
		goto out_read;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
out_read:
 | 
			
		||||
	close(hdl->read_ofd[q].fd);
 | 
			
		||||
	osmo_fd_unregister(&hdl->read_ofd[q]);
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int l1if_transport_close(int q, struct lc15l1_hdl *hdl)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_fd *read_ofd = &hdl->read_ofd[q];
 | 
			
		||||
	struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
 | 
			
		||||
 | 
			
		||||
	osmo_fd_unregister(read_ofd);
 | 
			
		||||
	close(read_ofd->fd);
 | 
			
		||||
	read_ofd->fd = -1;
 | 
			
		||||
 | 
			
		||||
	osmo_fd_unregister(write_ofd);
 | 
			
		||||
	close(write_ofd->fd);
 | 
			
		||||
	write_ofd->fd = -1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										377
									
								
								src/osmo-bts-litecell15/lc15_l1_if.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								src/osmo-bts-litecell15/lc15_l1_if.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,377 @@
 | 
			
		||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
 | 
			
		||||
 * based on:
 | 
			
		||||
 *     femto_l1_if.c
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <nrw/litecell15/litecell15.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1prim.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1const.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1types.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/gsmtap.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
 | 
			
		||||
#include <lc15_l1_if.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
uint32_t l1if_ts_to_hLayer2(uint8_t trx, uint8_t ts)
 | 
			
		||||
{
 | 
			
		||||
	return (ts << 16) | (trx << 24);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* allocate a msgb containing a GsmL1_Prim_t */
 | 
			
		||||
struct msgb *l1p_msgb_alloc(void)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = msgb_alloc(sizeof(GsmL1_Prim_t), "l1_prim");
 | 
			
		||||
 | 
			
		||||
	if (msg)
 | 
			
		||||
		msg->l1h = msgb_put(msg, sizeof(GsmL1_Prim_t));
 | 
			
		||||
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int l1if_req_pdch(struct lc15l1_hdl *fl1h, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_wqueue *wqueue = &fl1h->write_q[MQ_PDTCH_WRITE];
 | 
			
		||||
 | 
			
		||||
	if (osmo_wqueue_enqueue(wqueue, msg) != 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct lc15l1_hdl *gl1)
 | 
			
		||||
{
 | 
			
		||||
	prim->id = id;
 | 
			
		||||
 | 
			
		||||
	switch (id) {
 | 
			
		||||
	case GsmL1_PrimId_MphInitReq:
 | 
			
		||||
		//prim->u.mphInitReq.hLayer1 = (HANDLE)gl1->hLayer1;
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphCloseReq:
 | 
			
		||||
		prim->u.mphCloseReq.hLayer1 = (HANDLE)gl1->hLayer1;
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphConnectReq:
 | 
			
		||||
		prim->u.mphConnectReq.hLayer1 = (HANDLE)gl1->hLayer1;
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphDisconnectReq:
 | 
			
		||||
		prim->u.mphDisconnectReq.hLayer1 = (HANDLE)gl1->hLayer1;
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphActivateReq:
 | 
			
		||||
		prim->u.mphActivateReq.hLayer1 = (HANDLE)gl1->hLayer1;
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphDeactivateReq:
 | 
			
		||||
		prim->u.mphDeactivateReq.hLayer1 = (HANDLE)gl1->hLayer1;
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphConfigReq:
 | 
			
		||||
		prim->u.mphConfigReq.hLayer1 = (HANDLE)gl1->hLayer1;
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphMeasureReq:
 | 
			
		||||
		prim->u.mphMeasureReq.hLayer1 = (HANDLE)gl1->hLayer1;
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphInitCnf:
 | 
			
		||||
	case GsmL1_PrimId_MphCloseCnf:
 | 
			
		||||
	case GsmL1_PrimId_MphConnectCnf:
 | 
			
		||||
	case GsmL1_PrimId_MphDisconnectCnf:
 | 
			
		||||
	case GsmL1_PrimId_MphActivateCnf:
 | 
			
		||||
	case GsmL1_PrimId_MphDeactivateCnf:
 | 
			
		||||
	case GsmL1_PrimId_MphConfigCnf:
 | 
			
		||||
	case GsmL1_PrimId_MphMeasureCnf:
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphTimeInd:
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphSyncInd:
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_PhEmptyFrameReq:
 | 
			
		||||
		prim->u.phEmptyFrameReq.hLayer1 = (HANDLE)gl1->hLayer1;
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_PhDataReq:
 | 
			
		||||
		prim->u.phDataReq.hLayer1 = (HANDLE)gl1->hLayer1;
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_PhConnectInd:
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_PhReadyToSendInd:
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_PhDataInd:
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_PhRaInd:
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "unknown L1 primitive %u\n", id);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return &prim->u;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* connect PDTCH */
 | 
			
		||||
int l1if_connect_pdch(void *obj, uint8_t ts)
 | 
			
		||||
{
 | 
			
		||||
	struct lc15l1_hdl *fl1h = obj;
 | 
			
		||||
	struct msgb *msg = l1p_msgb_alloc();
 | 
			
		||||
	GsmL1_MphConnectReq_t *cr;
 | 
			
		||||
 | 
			
		||||
	cr = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConnectReq, fl1h);
 | 
			
		||||
	cr->u8Tn = ts;
 | 
			
		||||
	cr->logChComb = GsmL1_LogChComb_XIII;
 | 
			
		||||
 | 
			
		||||
	return l1if_req_pdch(fl1h, msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ph_readytosend_ind(struct lc15l1_hdl *fl1h,
 | 
			
		||||
				     GsmL1_PhReadyToSendInd_t *rts_ind)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_time g_time;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
 | 
			
		||||
 | 
			
		||||
	DEBUGP(DL1IF, "Rx PH-RTS.ind %02u/%02u/%02u SAPI=%s\n",
 | 
			
		||||
		g_time.t1, g_time.t2, g_time.t3,
 | 
			
		||||
		get_value_string(lc15bts_l1sapi_names, rts_ind->sapi));
 | 
			
		||||
 | 
			
		||||
	switch (rts_ind->sapi) {
 | 
			
		||||
	case GsmL1_Sapi_Pdtch:
 | 
			
		||||
	case GsmL1_Sapi_Pacch:
 | 
			
		||||
		rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
 | 
			
		||||
			rts_ind->u32Fn, rts_ind->u8BlockNbr);
 | 
			
		||||
	case GsmL1_Sapi_Ptcch:
 | 
			
		||||
		// FIXME
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_meas(struct pcu_l1_meas *meas, const GsmL1_MeasParam_t *l1_meas)
 | 
			
		||||
{
 | 
			
		||||
	meas->rssi = (int8_t) (l1_meas->fRssi);
 | 
			
		||||
	meas->have_rssi = 1;
 | 
			
		||||
	meas->ber  = (uint8_t) (l1_meas->fBer * 100);
 | 
			
		||||
	meas->have_ber = 1;
 | 
			
		||||
	meas->bto  = (int16_t) (l1_meas->i16BurstTiming);
 | 
			
		||||
	meas->have_bto = 1;
 | 
			
		||||
	meas->link_qual  = (int16_t) (l1_meas->fLinkQuality);
 | 
			
		||||
	meas->have_link_qual = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ph_data_ind(struct lc15l1_hdl *fl1h,
 | 
			
		||||
	GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	struct pcu_l1_meas meas = {0};
 | 
			
		||||
 | 
			
		||||
	DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s\n",
 | 
			
		||||
		get_value_string(lc15bts_l1sapi_names, data_ind->sapi),
 | 
			
		||||
		data_ind->hLayer2,
 | 
			
		||||
		osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
 | 
			
		||||
			     data_ind->msgUnitParam.u8Size));
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * TODO: Add proper bad frame handling here. This could be used
 | 
			
		||||
	 * to switch the used CS. Avoid a crash with the PCU right now
 | 
			
		||||
	 * feed "0 - 1" amount of data.
 | 
			
		||||
	 */
 | 
			
		||||
	if (data_ind->msgUnitParam.u8Size == 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	get_meas(&meas, &data_ind->measParam);
 | 
			
		||||
	bts_update_tbf_ta("PH-DATA", data_ind->u32Fn, fl1h->trx_no,
 | 
			
		||||
			  data_ind->u8Tn, sign_qta2ta(meas.bto), false);
 | 
			
		||||
 | 
			
		||||
	switch (data_ind->sapi) {
 | 
			
		||||
	case GsmL1_Sapi_Pdtch:
 | 
			
		||||
	case GsmL1_Sapi_Pacch:
 | 
			
		||||
		/* drop incomplete UL block */
 | 
			
		||||
		if (data_ind->msgUnitParam.u8Buffer[0]
 | 
			
		||||
			!= GsmL1_PdtchPlType_Full)
 | 
			
		||||
			break;
 | 
			
		||||
		/* PDTCH / PACCH frame handling */
 | 
			
		||||
		rc = pcu_rx_data_ind_pdtch(fl1h->trx_no, data_ind->u8Tn,
 | 
			
		||||
			data_ind->msgUnitParam.u8Buffer + 1,
 | 
			
		||||
			data_ind->msgUnitParam.u8Size - 1,
 | 
			
		||||
			data_ind->u32Fn,
 | 
			
		||||
			&meas);
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_Sapi_Ptcch:
 | 
			
		||||
		// FIXME
 | 
			
		||||
		rc = -1;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
 | 
			
		||||
			get_value_string(lc15bts_l1sapi_names, data_ind->sapi));
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
 | 
			
		||||
				data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
 | 
			
		||||
				data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1,
 | 
			
		||||
				data_ind->msgUnitParam.u8Size-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MIN_QUAL_RACH	5.0f
 | 
			
		||||
 | 
			
		||||
static int handle_ph_ra_ind(struct lc15l1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
 | 
			
		||||
{
 | 
			
		||||
	if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	DEBUGP(DL1IF, "Rx PH-RA.ind");
 | 
			
		||||
	bts_update_tbf_ta("PH-RA", ra_ind->u32Fn, fl1h->trx_no, ra_ind->u8Tn,
 | 
			
		||||
			qta2ta(ra_ind->measParam.i16BurstTiming), true);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* handle any random indication from the L1 */
 | 
			
		||||
int l1if_handle_l1prim(int wq, struct lc15l1_hdl *fl1h, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	GsmL1_Prim_t *l1p = msgb_l1prim(msg);
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, "Rx L1 prim %s on queue %d\n",
 | 
			
		||||
		get_value_string(lc15bts_l1prim_names, l1p->id), wq);
 | 
			
		||||
 | 
			
		||||
	switch (l1p->id) {
 | 
			
		||||
#if 0
 | 
			
		||||
	case GsmL1_PrimId_MphTimeInd:
 | 
			
		||||
		rc = handle_mph_time_ind(fl1h, &l1p->u.mphTimeInd);
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_MphSyncInd:
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_PhConnectInd:
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	case GsmL1_PrimId_PhReadyToSendInd:
 | 
			
		||||
		rc = handle_ph_readytosend_ind(fl1h, &l1p->u.phReadyToSendInd);
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_PhDataInd:
 | 
			
		||||
		rc = handle_ph_data_ind(fl1h, &l1p->u.phDataInd, msg);
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_PrimId_PhRaInd:
 | 
			
		||||
		rc = handle_ph_ra_ind(fl1h, &l1p->u.phRaInd);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int l1if_handle_sysprim(struct lc15l1_hdl *fl1h, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	return -ENOTSUP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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 lc15l1_hdl *fl1h = obj;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	GsmL1_Prim_t *l1p;
 | 
			
		||||
	GsmL1_PhDataReq_t *data_req;
 | 
			
		||||
	GsmL1_MsgUnitParam_t *msu_param;
 | 
			
		||||
	struct gsm_time g_time;
 | 
			
		||||
 | 
			
		||||
	gsm_fn2gsmtime(&g_time, fn);
 | 
			
		||||
 | 
			
		||||
	DEBUGP(DL1IF, "TX packet data %02u/%02u/%02u is_ptcch=%d ts=%d "
 | 
			
		||||
		"block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
 | 
			
		||||
		g_time.t3, is_ptcch, ts, block_nr, arfcn, len);
 | 
			
		||||
 | 
			
		||||
	msg = l1p_msgb_alloc();
 | 
			
		||||
	l1p = msgb_l1prim(msg);
 | 
			
		||||
	l1p->id = GsmL1_PrimId_PhDataReq;
 | 
			
		||||
	data_req = &l1p->u.phDataReq;
 | 
			
		||||
	data_req->hLayer1 = (HANDLE)fl1h->hLayer1;
 | 
			
		||||
	data_req->sapi = (is_ptcch) ? GsmL1_Sapi_Ptcch : GsmL1_Sapi_Pdtch;
 | 
			
		||||
	data_req->subCh = GsmL1_SubCh_NA;
 | 
			
		||||
	data_req->u8BlockNbr = block_nr;
 | 
			
		||||
	data_req->u8Tn = ts;
 | 
			
		||||
	data_req->u32Fn = fn;
 | 
			
		||||
	msu_param = &data_req->msgUnitParam;
 | 
			
		||||
	msu_param->u8Size = len;
 | 
			
		||||
	memcpy(msu_param->u8Buffer, data, len);
 | 
			
		||||
 | 
			
		||||
	/* transmit */
 | 
			
		||||
	if (osmo_wqueue_enqueue(&fl1h->write_q[MQ_PDTCH_WRITE], msg) != 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1)
 | 
			
		||||
{
 | 
			
		||||
	struct lc15l1_hdl *fl1h;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	fl1h = talloc_zero(tall_pcu_ctx, struct lc15l1_hdl);
 | 
			
		||||
	if (!fl1h)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	fl1h->hLayer1 = hlayer1;
 | 
			
		||||
	fl1h->trx_no = trx_no;
 | 
			
		||||
	/* hardware queues are numbered starting from 0 */
 | 
			
		||||
	fl1h->hw_info.trx_nr = trx_no;
 | 
			
		||||
 | 
			
		||||
	DEBUGP(DL1IF, "PCU: Using TRX HW#%u\n", fl1h->hw_info.trx_nr);
 | 
			
		||||
 | 
			
		||||
	rc = l1if_transport_open(MQ_PDTCH_WRITE, fl1h);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		talloc_free(fl1h);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
 | 
			
		||||
	if (fl1h->gsmtap)
 | 
			
		||||
		gsmtap_source_add_sink(fl1h->gsmtap);
 | 
			
		||||
 | 
			
		||||
	return fl1h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int l1if_close_pdch(void *obj)
 | 
			
		||||
{
 | 
			
		||||
	struct lc15l1_hdl *fl1h = obj;
 | 
			
		||||
	if (fl1h)
 | 
			
		||||
		l1if_transport_close(MQ_PDTCH_WRITE, fl1h);
 | 
			
		||||
	talloc_free(fl1h);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										104
									
								
								src/osmo-bts-litecell15/lc15_l1_if.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/osmo-bts-litecell15/lc15_l1_if.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
 | 
			
		||||
 * based on:
 | 
			
		||||
 *     femto_l1_if.h
 | 
			
		||||
 *
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _LC15_L1_IF_H
 | 
			
		||||
#define _LC15_L1_IF_H
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
#include <osmocom/core/gsmtap_util.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
#include "lc15bts.h"
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	MQ_SYS_READ,
 | 
			
		||||
	MQ_L1_READ,
 | 
			
		||||
	MQ_TCH_READ,
 | 
			
		||||
	MQ_PDTCH_READ,
 | 
			
		||||
	_NUM_MQ_READ
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	MQ_SYS_WRITE,
 | 
			
		||||
	MQ_L1_WRITE,
 | 
			
		||||
	MQ_TCH_WRITE,
 | 
			
		||||
	MQ_PDTCH_WRITE,
 | 
			
		||||
	_NUM_MQ_WRITE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct lc15l1_hdl {
 | 
			
		||||
	struct gsm_time gsm_time;
 | 
			
		||||
	uint32_t hLayer1;			/* handle to the L1 instance in the DSP */
 | 
			
		||||
	uint32_t dsp_trace_f;
 | 
			
		||||
	struct llist_head wlc_list;
 | 
			
		||||
 | 
			
		||||
	struct gsmtap_inst *gsmtap;
 | 
			
		||||
	uint32_t gsmtap_sapi_mask;
 | 
			
		||||
 | 
			
		||||
	uint8_t trx_no;
 | 
			
		||||
 | 
			
		||||
	struct osmo_timer_list alive_timer;
 | 
			
		||||
	unsigned int alive_prim_cnt;
 | 
			
		||||
 | 
			
		||||
	struct osmo_fd read_ofd[_NUM_MQ_READ];	/* osmo file descriptors */
 | 
			
		||||
	struct osmo_wqueue write_q[_NUM_MQ_WRITE];
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
		int trx_nr;	/* <1-2> */
 | 
			
		||||
	} hw_info;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define msgb_l1prim(msg)	((GsmL1_Prim_t *)(msg)->l1h)
 | 
			
		||||
#define msgb_sysprim(msg)	((Litecell15_Prim_t *)(msg)->l1h)
 | 
			
		||||
 | 
			
		||||
typedef int l1if_compl_cb(struct msgb *l1_msg, void *data);
 | 
			
		||||
 | 
			
		||||
/* send a request primitive to the L1 and schedule completion call-back */
 | 
			
		||||
int l1if_req_compl(struct lc15l1_hdl *fl1h, struct msgb *msg,
 | 
			
		||||
		   int is_system_prim, l1if_compl_cb *cb, void *data);
 | 
			
		||||
 | 
			
		||||
int l1if_reset(struct lc15l1_hdl *hdl);
 | 
			
		||||
int l1if_activate_rf(struct lc15l1_hdl *hdl, int on);
 | 
			
		||||
int l1if_set_trace_flags(struct lc15l1_hdl *hdl, uint32_t flags);
 | 
			
		||||
int l1if_set_txpower(struct lc15l1_hdl *fl1h, float tx_power);
 | 
			
		||||
 | 
			
		||||
struct msgb *l1p_msgb_alloc(void);
 | 
			
		||||
struct msgb *sysp_msgb_alloc(void);
 | 
			
		||||
 | 
			
		||||
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan);
 | 
			
		||||
struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2);
 | 
			
		||||
 | 
			
		||||
int l1if_handle_sysprim(struct lc15l1_hdl *fl1h, struct msgb *msg);
 | 
			
		||||
int l1if_handle_l1prim(int wq, struct lc15l1_hdl *fl1h, struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
/* tch.c */
 | 
			
		||||
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
 | 
			
		||||
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
 | 
			
		||||
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The implementation of these functions is selected by either compiling and
 | 
			
		||||
 * linking sysmo_l1_hw.c or sysmo_l1_fwd.c
 | 
			
		||||
 */
 | 
			
		||||
int l1if_transport_open(int q, struct lc15l1_hdl *hdl);
 | 
			
		||||
int l1if_transport_close(int q, struct lc15l1_hdl *hdl);
 | 
			
		||||
 | 
			
		||||
#endif /* _SYSMO_L1_IF_H */
 | 
			
		||||
							
								
								
									
										332
									
								
								src/osmo-bts-litecell15/lc15bts.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										332
									
								
								src/osmo-bts-litecell15/lc15bts.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,332 @@
 | 
			
		||||
/* NuRAN Wireless Litecell 1.5 L1 API related definitions */
 | 
			
		||||
 | 
			
		||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
 | 
			
		||||
 * based on:
 | 
			
		||||
 *     sysmobts.c
 | 
			
		||||
 *     (C) 2011 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
 *
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <nrw/litecell15/litecell15.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1const.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1dbg.h>
 | 
			
		||||
 | 
			
		||||
#include "lc15bts.h"
 | 
			
		||||
 | 
			
		||||
enum l1prim_type lc15bts_get_l1prim_type(GsmL1_PrimId_t id)
 | 
			
		||||
{
 | 
			
		||||
	switch (id) {
 | 
			
		||||
	case GsmL1_PrimId_MphInitReq:       return L1P_T_REQ;
 | 
			
		||||
	case GsmL1_PrimId_MphCloseReq:      return L1P_T_REQ;
 | 
			
		||||
	case GsmL1_PrimId_MphConnectReq:    return L1P_T_REQ;
 | 
			
		||||
	case GsmL1_PrimId_MphDisconnectReq: return L1P_T_REQ;
 | 
			
		||||
	case GsmL1_PrimId_MphActivateReq:   return L1P_T_REQ;
 | 
			
		||||
	case GsmL1_PrimId_MphDeactivateReq: return L1P_T_REQ;
 | 
			
		||||
	case GsmL1_PrimId_MphConfigReq:     return L1P_T_REQ;
 | 
			
		||||
	case GsmL1_PrimId_MphMeasureReq:    return L1P_T_REQ;
 | 
			
		||||
	case GsmL1_PrimId_MphInitCnf:       return L1P_T_CONF;
 | 
			
		||||
	case GsmL1_PrimId_MphCloseCnf:      return L1P_T_CONF;
 | 
			
		||||
	case GsmL1_PrimId_MphConnectCnf:    return L1P_T_CONF;
 | 
			
		||||
	case GsmL1_PrimId_MphDisconnectCnf: return L1P_T_CONF;
 | 
			
		||||
	case GsmL1_PrimId_MphActivateCnf:   return L1P_T_CONF;
 | 
			
		||||
	case GsmL1_PrimId_MphDeactivateCnf: return L1P_T_CONF;
 | 
			
		||||
	case GsmL1_PrimId_MphConfigCnf:     return L1P_T_CONF;
 | 
			
		||||
	case GsmL1_PrimId_MphMeasureCnf:    return L1P_T_CONF;
 | 
			
		||||
	case GsmL1_PrimId_PhEmptyFrameReq:  return L1P_T_REQ;
 | 
			
		||||
	case GsmL1_PrimId_PhDataReq:        return L1P_T_REQ;
 | 
			
		||||
	case GsmL1_PrimId_MphTimeInd:       return L1P_T_IND;
 | 
			
		||||
	case GsmL1_PrimId_MphSyncInd:       return L1P_T_IND;
 | 
			
		||||
	case GsmL1_PrimId_PhConnectInd:     return L1P_T_IND;
 | 
			
		||||
	case GsmL1_PrimId_PhReadyToSendInd: return L1P_T_IND;
 | 
			
		||||
	case GsmL1_PrimId_PhDataInd:        return L1P_T_IND;
 | 
			
		||||
	case GsmL1_PrimId_PhRaInd:          return L1P_T_IND;
 | 
			
		||||
	default:                            return L1P_T_INVALID;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_l1prim_names[GsmL1_PrimId_NUM+1] = {
 | 
			
		||||
	{ GsmL1_PrimId_MphInitReq,	"MPH-INIT.req" },
 | 
			
		||||
	{ GsmL1_PrimId_MphCloseReq,	"MPH-CLOSE.req" },
 | 
			
		||||
	{ GsmL1_PrimId_MphConnectReq,	"MPH-CONNECT.req" },
 | 
			
		||||
	{ GsmL1_PrimId_MphDisconnectReq,"MPH-DISCONNECT.req" },
 | 
			
		||||
	{ GsmL1_PrimId_MphActivateReq,	"MPH-ACTIVATE.req" },
 | 
			
		||||
	{ GsmL1_PrimId_MphDeactivateReq,"MPH-DEACTIVATE.req" },
 | 
			
		||||
	{ GsmL1_PrimId_MphConfigReq,	"MPH-CONFIG.req" },
 | 
			
		||||
	{ GsmL1_PrimId_MphMeasureReq,	"MPH-MEASURE.req" },
 | 
			
		||||
	{ GsmL1_PrimId_MphInitCnf,	"MPH-INIT.conf" },
 | 
			
		||||
	{ GsmL1_PrimId_MphCloseCnf,	"MPH-CLOSE.conf" },
 | 
			
		||||
	{ GsmL1_PrimId_MphConnectCnf,	"MPH-CONNECT.conf" },
 | 
			
		||||
	{ GsmL1_PrimId_MphDisconnectCnf,"MPH-DISCONNECT.conf" },
 | 
			
		||||
	{ GsmL1_PrimId_MphActivateCnf,	"MPH-ACTIVATE.conf" },
 | 
			
		||||
	{ GsmL1_PrimId_MphDeactivateCnf,"MPH-DEACTIVATE.conf" },
 | 
			
		||||
	{ GsmL1_PrimId_MphConfigCnf,	"MPH-CONFIG.conf" },
 | 
			
		||||
	{ GsmL1_PrimId_MphMeasureCnf,	"MPH-MEASURE.conf" },
 | 
			
		||||
	{ GsmL1_PrimId_MphTimeInd,	"MPH-TIME.ind" },
 | 
			
		||||
	{ GsmL1_PrimId_MphSyncInd,	"MPH-SYNC.ind" },
 | 
			
		||||
	{ GsmL1_PrimId_PhEmptyFrameReq,	"PH-EMPTY_FRAME.req" },
 | 
			
		||||
	{ GsmL1_PrimId_PhDataReq,	"PH-DATA.req" },
 | 
			
		||||
	{ GsmL1_PrimId_PhConnectInd,	"PH-CONNECT.ind" },
 | 
			
		||||
	{ GsmL1_PrimId_PhReadyToSendInd,"PH-READY_TO_SEND.ind" },
 | 
			
		||||
	{ GsmL1_PrimId_PhDataInd,	"PH-DATA.ind" },
 | 
			
		||||
	{ GsmL1_PrimId_PhRaInd,		"PH-RA.ind" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GsmL1_PrimId_t lc15bts_get_l1prim_conf(GsmL1_PrimId_t id)
 | 
			
		||||
{
 | 
			
		||||
        switch (id) {
 | 
			
		||||
        case GsmL1_PrimId_MphInitReq:       return GsmL1_PrimId_MphInitCnf;
 | 
			
		||||
        case GsmL1_PrimId_MphCloseReq:      return GsmL1_PrimId_MphCloseCnf;
 | 
			
		||||
        case GsmL1_PrimId_MphConnectReq:    return GsmL1_PrimId_MphConnectCnf;
 | 
			
		||||
        case GsmL1_PrimId_MphDisconnectReq: return GsmL1_PrimId_MphDisconnectCnf;
 | 
			
		||||
        case GsmL1_PrimId_MphActivateReq:   return GsmL1_PrimId_MphActivateCnf;
 | 
			
		||||
        case GsmL1_PrimId_MphDeactivateReq: return GsmL1_PrimId_MphDeactivateCnf;
 | 
			
		||||
        case GsmL1_PrimId_MphConfigReq:     return GsmL1_PrimId_MphConfigCnf;
 | 
			
		||||
        case GsmL1_PrimId_MphMeasureReq:    return GsmL1_PrimId_MphMeasureCnf;
 | 
			
		||||
        default:                            return -1;	// Weak
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum l1prim_type lc15bts_get_sysprim_type(Litecell15_PrimId_t id)
 | 
			
		||||
{
 | 
			
		||||
	switch (id) {
 | 
			
		||||
	case Litecell15_PrimId_SystemInfoReq:    return L1P_T_REQ;
 | 
			
		||||
	case Litecell15_PrimId_SystemInfoCnf:    return L1P_T_CONF;
 | 
			
		||||
	case Litecell15_PrimId_SystemFailureInd: return L1P_T_IND;
 | 
			
		||||
	case Litecell15_PrimId_ActivateRfReq:    return L1P_T_REQ;
 | 
			
		||||
	case Litecell15_PrimId_ActivateRfCnf:    return L1P_T_CONF;
 | 
			
		||||
	case Litecell15_PrimId_DeactivateRfReq:  return L1P_T_REQ;
 | 
			
		||||
	case Litecell15_PrimId_DeactivateRfCnf:  return L1P_T_CONF;
 | 
			
		||||
	case Litecell15_PrimId_SetTraceFlagsReq: return L1P_T_REQ;
 | 
			
		||||
	case Litecell15_PrimId_Layer1ResetReq:   return L1P_T_REQ;
 | 
			
		||||
	case Litecell15_PrimId_Layer1ResetCnf:   return L1P_T_CONF;
 | 
			
		||||
	case Litecell15_PrimId_SetCalibTblReq:   return L1P_T_REQ;
 | 
			
		||||
	case Litecell15_PrimId_SetCalibTblCnf:   return L1P_T_CONF;
 | 
			
		||||
	case Litecell15_PrimId_MuteRfReq:        return L1P_T_REQ;
 | 
			
		||||
	case Litecell15_PrimId_MuteRfCnf:        return L1P_T_CONF;
 | 
			
		||||
	case Litecell15_PrimId_SetRxAttenReq:    return L1P_T_REQ;
 | 
			
		||||
	case Litecell15_PrimId_SetRxAttenCnf:    return L1P_T_CONF;
 | 
			
		||||
	default:                                 return L1P_T_INVALID;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_sysprim_names[Litecell15_PrimId_NUM+1] = {
 | 
			
		||||
	{ Litecell15_PrimId_SystemInfoReq,	"SYSTEM-INFO.req" },
 | 
			
		||||
	{ Litecell15_PrimId_SystemInfoCnf,	"SYSTEM-INFO.conf" },
 | 
			
		||||
	{ Litecell15_PrimId_SystemFailureInd,	"SYSTEM-FAILURE.ind" },
 | 
			
		||||
	{ Litecell15_PrimId_ActivateRfReq,	"ACTIVATE-RF.req" },
 | 
			
		||||
	{ Litecell15_PrimId_ActivateRfCnf,	"ACTIVATE-RF.conf" },
 | 
			
		||||
	{ Litecell15_PrimId_DeactivateRfReq,	"DEACTIVATE-RF.req" },
 | 
			
		||||
	{ Litecell15_PrimId_DeactivateRfCnf,	"DEACTIVATE-RF.conf" },
 | 
			
		||||
	{ Litecell15_PrimId_SetTraceFlagsReq,	"SET-TRACE-FLAGS.req" },
 | 
			
		||||
	{ Litecell15_PrimId_Layer1ResetReq,	"LAYER1-RESET.req" },
 | 
			
		||||
	{ Litecell15_PrimId_Layer1ResetCnf,	"LAYER1-RESET.conf" },
 | 
			
		||||
	{ Litecell15_PrimId_SetCalibTblReq,	"SET-CALIB.req" },
 | 
			
		||||
	{ Litecell15_PrimId_SetCalibTblCnf,	"SET-CALIB.cnf" },
 | 
			
		||||
	{ Litecell15_PrimId_MuteRfReq,	        "MUTE-RF.req" },
 | 
			
		||||
	{ Litecell15_PrimId_MuteRfCnf,	        "MUTE-RF.cnf" },
 | 
			
		||||
	{ Litecell15_PrimId_SetRxAttenReq,	"SET-RX-ATTEN.req" },
 | 
			
		||||
	{ Litecell15_PrimId_SetRxAttenCnf,	"SET-RX-ATTEN-CNF.cnf" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Litecell15_PrimId_t lc15bts_get_sysprim_conf(Litecell15_PrimId_t id)
 | 
			
		||||
{
 | 
			
		||||
        switch (id) {
 | 
			
		||||
	case Litecell15_PrimId_SystemInfoReq:    return Litecell15_PrimId_SystemInfoCnf;
 | 
			
		||||
	case Litecell15_PrimId_ActivateRfReq:    return Litecell15_PrimId_ActivateRfCnf;
 | 
			
		||||
	case Litecell15_PrimId_DeactivateRfReq:  return Litecell15_PrimId_DeactivateRfCnf;
 | 
			
		||||
	case Litecell15_PrimId_Layer1ResetReq:   return Litecell15_PrimId_Layer1ResetCnf;
 | 
			
		||||
	case Litecell15_PrimId_SetCalibTblReq:   return Litecell15_PrimId_SetCalibTblCnf;
 | 
			
		||||
	case Litecell15_PrimId_MuteRfReq:        return Litecell15_PrimId_MuteRfCnf;
 | 
			
		||||
	case Litecell15_PrimId_SetRxAttenReq:    return Litecell15_PrimId_SetRxAttenCnf;
 | 
			
		||||
        default:                                 return -1;	// Weak
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
 | 
			
		||||
	{ GsmL1_Sapi_Idle,	"IDLE" },
 | 
			
		||||
	{ GsmL1_Sapi_Fcch,	"FCCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Sch,	"SCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Sacch,	"SACCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Sdcch,	"SDCCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Bcch,	"BCCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Pch,	"PCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Agch,	"AGCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Cbch,	"CBCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Rach,	"RACH" },
 | 
			
		||||
	{ GsmL1_Sapi_TchF,	"TCH/F" },
 | 
			
		||||
	{ GsmL1_Sapi_FacchF,	"FACCH/F" },
 | 
			
		||||
	{ GsmL1_Sapi_TchH,	"TCH/H" },
 | 
			
		||||
	{ GsmL1_Sapi_FacchH,	"FACCH/H" },
 | 
			
		||||
	{ GsmL1_Sapi_Nch,	"NCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Pdtch,	"PDTCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Pacch,	"PACCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Pbcch,	"PBCCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Pagch,	"PAGCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Ppch,	"PPCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Pnch,	"PNCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Ptcch,	"PTCCH" },
 | 
			
		||||
	{ GsmL1_Sapi_Prach,	"PRACH" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_l1status_names[GSML1_STATUS_NUM+1] = {
 | 
			
		||||
	{ GsmL1_Status_Success,		"Success" },
 | 
			
		||||
	{ GsmL1_Status_Generic,		"Generic error" },
 | 
			
		||||
	{ GsmL1_Status_NoMemory,	"Not enough memory" },
 | 
			
		||||
	{ GsmL1_Status_Timeout,		"Timeout" },
 | 
			
		||||
	{ GsmL1_Status_InvalidParam,	"Invalid parameter" },
 | 
			
		||||
	{ GsmL1_Status_Busy,		"Resource busy" },
 | 
			
		||||
	{ GsmL1_Status_NoRessource,	"No more resources" },
 | 
			
		||||
	{ GsmL1_Status_Uninitialized,	"Trying to use uninitialized resource" },
 | 
			
		||||
	{ GsmL1_Status_NullInterface,	"Trying to call a NULL interface" },
 | 
			
		||||
	{ GsmL1_Status_NullFctnPtr,	"Trying to call a NULL function ptr" },
 | 
			
		||||
	{ GsmL1_Status_BadCrc,		"Bad CRC" },
 | 
			
		||||
	{ GsmL1_Status_BadUsf,		"Bad USF" },
 | 
			
		||||
	{ GsmL1_Status_InvalidCPS,	"Invalid CPS field" },
 | 
			
		||||
	{ GsmL1_Status_UnexpectedBurst,	"Unexpected burst" },
 | 
			
		||||
	{ GsmL1_Status_UnavailCodec,	"AMR codec is unavailable" },
 | 
			
		||||
	{ GsmL1_Status_CriticalError,	"Critical error" },
 | 
			
		||||
	{ GsmL1_Status_OverheatError,	"Overheat error" },
 | 
			
		||||
	{ GsmL1_Status_DeviceError,	"Device error" },
 | 
			
		||||
	{ GsmL1_Status_FacchError,	"FACCH / TCH order error" },
 | 
			
		||||
	{ GsmL1_Status_AlreadyDeactivated, "Lchan already deactivated" },
 | 
			
		||||
	{ GsmL1_Status_TxBurstFifoOvrn,	"FIFO overrun" },
 | 
			
		||||
	{ GsmL1_Status_TxBurstFifoUndr,	"FIFO underrun" },
 | 
			
		||||
	{ GsmL1_Status_NotSynchronized,	"Not synchronized" },
 | 
			
		||||
	{ GsmL1_Status_Unsupported,	"Unsupported feature" },
 | 
			
		||||
	{ GsmL1_Status_ClockError,	"System clock error" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_tracef_names[29] = {
 | 
			
		||||
	{ DBG_DEBUG,			"DEBUG" },
 | 
			
		||||
	{ DBG_L1WARNING,		"L1_WARNING" },
 | 
			
		||||
	{ DBG_ERROR,			"ERROR" },
 | 
			
		||||
	{ DBG_L1RXMSG,			"L1_RX_MSG" },
 | 
			
		||||
	{ DBG_L1RXMSGBYTE,		"L1_RX_MSG_BYTE" },
 | 
			
		||||
	{ DBG_L1TXMSG,			"L1_TX_MSG" },
 | 
			
		||||
	{ DBG_L1TXMSGBYTE,		"L1_TX_MSG_BYTE" },
 | 
			
		||||
	{ DBG_MPHCNF,			"MPH_CNF" },
 | 
			
		||||
	{ DBG_MPHIND,			"MPH_IND" },
 | 
			
		||||
	{ DBG_MPHREQ,			"MPH_REQ" },
 | 
			
		||||
	{ DBG_PHIND,			"PH_IND" },
 | 
			
		||||
	{ DBG_PHREQ,			"PH_REQ" },
 | 
			
		||||
	{ DBG_PHYRF,			"PHY_RF" },
 | 
			
		||||
	{ DBG_PHYRFMSGBYTE,		"PHY_MSG_BYTE" },
 | 
			
		||||
	{ DBG_MODE,			"MODE" },
 | 
			
		||||
	{ DBG_TDMAINFO,			"TDMA_INFO" },
 | 
			
		||||
	{ DBG_BADCRC,			"BAD_CRC" },
 | 
			
		||||
	{ DBG_PHINDBYTE,		"PH_IND_BYTE" },
 | 
			
		||||
	{ DBG_PHREQBYTE,		"PH_REQ_BYTE" },
 | 
			
		||||
	{ DBG_DEVICEMSG,		"DEVICE_MSG" },
 | 
			
		||||
	{ DBG_RACHINFO,			"RACH_INFO" },
 | 
			
		||||
	{ DBG_LOGCHINFO,		"LOG_CH_INFO" },
 | 
			
		||||
	{ DBG_MEMORY,			"MEMORY" },
 | 
			
		||||
	{ DBG_PROFILING,		"PROFILING" },
 | 
			
		||||
	{ DBG_TESTCOMMENT,		"TEST_COMMENT" },
 | 
			
		||||
	{ DBG_TEST,			"TEST" },
 | 
			
		||||
	{ DBG_STATUS,			"STATUS" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_tracef_docs[29] = {
 | 
			
		||||
	{ DBG_DEBUG,			"Debug Region" },
 | 
			
		||||
	{ DBG_L1WARNING,		"L1 Warning Region" },
 | 
			
		||||
	{ DBG_ERROR,			"Error Region" },
 | 
			
		||||
	{ DBG_L1RXMSG,			"L1_RX_MSG Region" },
 | 
			
		||||
	{ DBG_L1RXMSGBYTE,		"L1_RX_MSG_BYTE Region" },
 | 
			
		||||
	{ DBG_L1TXMSG,			"L1_TX_MSG Region" },
 | 
			
		||||
	{ DBG_L1TXMSGBYTE,		"L1_TX_MSG_BYTE Region" },
 | 
			
		||||
	{ DBG_MPHCNF,			"MphConfirmation Region" },
 | 
			
		||||
	{ DBG_MPHIND,			"MphIndication Region" },
 | 
			
		||||
	{ DBG_MPHREQ,			"MphRequest Region" },
 | 
			
		||||
	{ DBG_PHIND,			"PhIndication Region" },
 | 
			
		||||
	{ DBG_PHREQ,			"PhRequest Region" },
 | 
			
		||||
	{ DBG_PHYRF,			"PhyRF Region" },
 | 
			
		||||
	{ DBG_PHYRFMSGBYTE,		"PhyRF Message Region" },
 | 
			
		||||
	{ DBG_MODE,			"Mode Region" },
 | 
			
		||||
	{ DBG_TDMAINFO,			"TDMA Info Region" },
 | 
			
		||||
	{ DBG_BADCRC,			"Bad CRC Region" },
 | 
			
		||||
	{ DBG_PHINDBYTE,		"PH_IND_BYTE" },
 | 
			
		||||
	{ DBG_PHREQBYTE,		"PH_REQ_BYTE" },
 | 
			
		||||
	{ DBG_DEVICEMSG,		"Device Message Region" },
 | 
			
		||||
	{ DBG_RACHINFO,			"RACH Info" },
 | 
			
		||||
	{ DBG_LOGCHINFO,		"LOG_CH_INFO" },
 | 
			
		||||
	{ DBG_MEMORY,			"Memory Region" },
 | 
			
		||||
	{ DBG_PROFILING,		"Profiling Region" },
 | 
			
		||||
	{ DBG_TESTCOMMENT,		"Test Comments" },
 | 
			
		||||
	{ DBG_TEST,			"Test Region" },
 | 
			
		||||
	{ DBG_STATUS,			"Status Region" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_tch_pl_names[] = {
 | 
			
		||||
	{ GsmL1_TchPlType_NA,			"N/A" },
 | 
			
		||||
	{ GsmL1_TchPlType_Fr,			"FR" },
 | 
			
		||||
	{ GsmL1_TchPlType_Hr,			"HR" },
 | 
			
		||||
	{ GsmL1_TchPlType_Efr,			"EFR" },
 | 
			
		||||
	{ GsmL1_TchPlType_Amr,			"AMR(IF2)" },
 | 
			
		||||
	{ GsmL1_TchPlType_Amr_SidBad,		"AMR(SID BAD)" },
 | 
			
		||||
	{ GsmL1_TchPlType_Amr_Onset,		"AMR(ONSET)" },
 | 
			
		||||
	{ GsmL1_TchPlType_Amr_Ratscch,		"AMR(RATSCCH)" },
 | 
			
		||||
	{ GsmL1_TchPlType_Amr_SidUpdateInH,	"AMR(SID_UPDATE INH)" },
 | 
			
		||||
	{ GsmL1_TchPlType_Amr_SidFirstP1,	"AMR(SID_FIRST P1)" },
 | 
			
		||||
	{ GsmL1_TchPlType_Amr_SidFirstP2,	"AMR(SID_FIRST P2)" },
 | 
			
		||||
	{ GsmL1_TchPlType_Amr_SidFirstInH,	"AMR(SID_FIRST INH)" },
 | 
			
		||||
	{ GsmL1_TchPlType_Amr_RatscchMarker,	"AMR(RATSCCH MARK)" },
 | 
			
		||||
	{ GsmL1_TchPlType_Amr_RatscchData,	"AMR(RATSCCH DATA)" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_dir_names[] = {
 | 
			
		||||
	{ GsmL1_Dir_TxDownlink,	"TxDL" },
 | 
			
		||||
	{ GsmL1_Dir_TxUplink,	"TxUL" },
 | 
			
		||||
	{ GsmL1_Dir_RxUplink,	"RxUL" },
 | 
			
		||||
	{ GsmL1_Dir_RxDownlink,	"RxDL" },
 | 
			
		||||
	{ GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink, "BOTH" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_chcomb_names[] = {
 | 
			
		||||
	{ GsmL1_LogChComb_0,	"dummy" },
 | 
			
		||||
	{ GsmL1_LogChComb_I,	"tch_f" },
 | 
			
		||||
	{ GsmL1_LogChComb_II,	"tch_h" },
 | 
			
		||||
	{ GsmL1_LogChComb_IV,	"ccch" },
 | 
			
		||||
	{ GsmL1_LogChComb_V,	"ccch_sdcch4" },
 | 
			
		||||
	{ GsmL1_LogChComb_VII,	"sdcch8" },
 | 
			
		||||
	{ GsmL1_LogChComb_XIII,	"pdtch" },
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const uint8_t pdch_msu_size[_NUM_PDCH_CS] = {
 | 
			
		||||
	[PDCH_CS_1]	= 23,
 | 
			
		||||
	[PDCH_CS_2]	= 34,
 | 
			
		||||
	[PDCH_CS_3]	= 40,
 | 
			
		||||
	[PDCH_CS_4]	= 54,
 | 
			
		||||
	[PDCH_MCS_1]	= 27,
 | 
			
		||||
	[PDCH_MCS_2]	= 33,
 | 
			
		||||
	[PDCH_MCS_3]	= 42,
 | 
			
		||||
	[PDCH_MCS_4]	= 49,
 | 
			
		||||
	[PDCH_MCS_5]	= 60,
 | 
			
		||||
	[PDCH_MCS_6]	= 78,
 | 
			
		||||
	[PDCH_MCS_7]	= 118,
 | 
			
		||||
	[PDCH_MCS_8]	= 142,
 | 
			
		||||
	[PDCH_MCS_9]	= 154
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										64
									
								
								src/osmo-bts-litecell15/lc15bts.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/osmo-bts-litecell15/lc15bts.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
#ifndef LC15BTS_H
 | 
			
		||||
#define LC15BTS_H
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include <nrw/litecell15/litecell15.h>
 | 
			
		||||
#include <nrw/litecell15/gsml1const.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Depending on the firmware version either GsmL1_Prim_t or Litecell15_Prim_t
 | 
			
		||||
 * is the bigger struct. For earlier firmware versions the GsmL1_Prim_t was the
 | 
			
		||||
 * bigger struct.
 | 
			
		||||
 */
 | 
			
		||||
#define LC15BTS_PRIM_SIZE \
 | 
			
		||||
	(OSMO_MAX(sizeof(Litecell15_Prim_t), sizeof(GsmL1_Prim_t)) + 128)
 | 
			
		||||
 | 
			
		||||
enum l1prim_type {
 | 
			
		||||
	L1P_T_INVALID, /* this must be 0 to detect uninitialized elements */
 | 
			
		||||
	L1P_T_REQ,
 | 
			
		||||
	L1P_T_CONF,
 | 
			
		||||
	L1P_T_IND,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum l1prim_type lc15bts_get_l1prim_type(GsmL1_PrimId_t id);
 | 
			
		||||
const struct value_string lc15bts_l1prim_names[GsmL1_PrimId_NUM+1];
 | 
			
		||||
GsmL1_PrimId_t lc15bts_get_l1prim_conf(GsmL1_PrimId_t id);
 | 
			
		||||
 | 
			
		||||
enum l1prim_type lc15bts_get_sysprim_type(Litecell15_PrimId_t id);
 | 
			
		||||
const struct value_string lc15bts_sysprim_names[Litecell15_PrimId_NUM+1];
 | 
			
		||||
Litecell15_PrimId_t lc15bts_get_sysprim_conf(Litecell15_PrimId_t id);
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_l1sapi_names[GsmL1_Sapi_NUM+1];
 | 
			
		||||
const struct value_string lc15bts_l1status_names[GSML1_STATUS_NUM+1];
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_tracef_names[29];
 | 
			
		||||
const struct value_string lc15bts_tracef_docs[29];
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_tch_pl_names[15];
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_clksrc_names[10];
 | 
			
		||||
 | 
			
		||||
const struct value_string lc15bts_dir_names[6];
 | 
			
		||||
 | 
			
		||||
enum pdch_cs {
 | 
			
		||||
	PDCH_CS_1,
 | 
			
		||||
	PDCH_CS_2,
 | 
			
		||||
	PDCH_CS_3,
 | 
			
		||||
	PDCH_CS_4,
 | 
			
		||||
	PDCH_MCS_1,
 | 
			
		||||
	PDCH_MCS_2,
 | 
			
		||||
	PDCH_MCS_3,
 | 
			
		||||
	PDCH_MCS_4,
 | 
			
		||||
	PDCH_MCS_5,
 | 
			
		||||
	PDCH_MCS_6,
 | 
			
		||||
	PDCH_MCS_7,
 | 
			
		||||
	PDCH_MCS_8,
 | 
			
		||||
	PDCH_MCS_9,
 | 
			
		||||
	_NUM_PDCH_CS
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const uint8_t pdch_msu_size[_NUM_PDCH_CS];
 | 
			
		||||
 | 
			
		||||
#endif /* LC15BTS_H */
 | 
			
		||||
@@ -10,9 +10,13 @@
 | 
			
		||||
#include <osmocom/core/gsmtap.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
 | 
			
		||||
#include <sysmo_l1_if.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
@@ -109,23 +113,6 @@ static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct femtol1_hdl
 | 
			
		||||
	return &prim->u;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct sapi_dir {
 | 
			
		||||
	GsmL1_Sapi_t sapi;
 | 
			
		||||
	GsmL1_Dir_t dir;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct sapi_dir pdtch_sapis[] = {
 | 
			
		||||
	{ GsmL1_Sapi_Pdtch,	GsmL1_Dir_TxDownlink },
 | 
			
		||||
	{ GsmL1_Sapi_Pdtch,	GsmL1_Dir_RxUplink },
 | 
			
		||||
	{ GsmL1_Sapi_Ptcch,	GsmL1_Dir_TxDownlink },
 | 
			
		||||
	{ GsmL1_Sapi_Prach,	GsmL1_Dir_RxUplink },
 | 
			
		||||
#if 0
 | 
			
		||||
	{ GsmL1_Sapi_Ptcch,	GsmL1_Dir_RxUplink },
 | 
			
		||||
	{ GsmL1_Sapi_Pacch,	GsmL1_Dir_TxDownlink },
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* connect PDTCH */
 | 
			
		||||
int l1if_connect_pdch(void *obj, uint8_t ts)
 | 
			
		||||
{
 | 
			
		||||
@@ -155,8 +142,8 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1h,
 | 
			
		||||
	switch (rts_ind->sapi) {
 | 
			
		||||
	case GsmL1_Sapi_Pdtch:
 | 
			
		||||
	case GsmL1_Sapi_Pacch:
 | 
			
		||||
		rc = pcu_rx_rts_req_pdtch((long)fl1h->priv, rts_ind->u8Tn,
 | 
			
		||||
			rts_ind->u16Arfcn, rts_ind->u32Fn, rts_ind->u8BlockNbr);
 | 
			
		||||
		rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
 | 
			
		||||
			rts_ind->u32Fn, rts_ind->u8BlockNbr);
 | 
			
		||||
	case GsmL1_Sapi_Ptcch:
 | 
			
		||||
		// FIXME
 | 
			
		||||
	default:
 | 
			
		||||
@@ -190,6 +177,8 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
 | 
			
		||||
		osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
 | 
			
		||||
			     data_ind->msgUnitParam.u8Size));
 | 
			
		||||
 | 
			
		||||
	pcu_rx_block_time(data_ind->u16Arfcn, data_ind->u32Fn, data_ind->u8Tn);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * TODO: Add proper bad frame handling here. This could be used
 | 
			
		||||
	 * to switch the used CS. Avoid a crash with the PCU right now
 | 
			
		||||
@@ -198,12 +187,9 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
 | 
			
		||||
	if (data_ind->msgUnitParam.u8Size == 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
 | 
			
		||||
			data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
 | 
			
		||||
			data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1,
 | 
			
		||||
			data_ind->msgUnitParam.u8Size-1);
 | 
			
		||||
 | 
			
		||||
	get_meas(&meas, &data_ind->measParam);
 | 
			
		||||
	bts_update_tbf_ta("PH-DATA", data_ind->u32Fn, fl1h->trx_no,
 | 
			
		||||
			  data_ind->u8Tn, sign_qta2ta(meas.bto), false);
 | 
			
		||||
 | 
			
		||||
	switch (data_ind->sapi) {
 | 
			
		||||
	case GsmL1_Sapi_Pdtch:
 | 
			
		||||
@@ -213,7 +199,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
 | 
			
		||||
			!= GsmL1_PdtchPlType_Full)
 | 
			
		||||
			break;
 | 
			
		||||
		/* PDTCH / PACCH frame handling */
 | 
			
		||||
		pcu_rx_data_ind_pdtch((long)fl1h->priv, data_ind->u8Tn,
 | 
			
		||||
		pcu_rx_data_ind_pdtch(fl1h->trx_no, data_ind->u8Tn,
 | 
			
		||||
			data_ind->msgUnitParam.u8Buffer + 1,
 | 
			
		||||
			data_ind->msgUnitParam.u8Size - 1,
 | 
			
		||||
			data_ind->u32Fn,
 | 
			
		||||
@@ -221,13 +207,23 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
 | 
			
		||||
		break;
 | 
			
		||||
	case GsmL1_Sapi_Ptcch:
 | 
			
		||||
		// FIXME
 | 
			
		||||
		rc = -1;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
 | 
			
		||||
			get_value_string(femtobts_l1sapi_names, data_ind->sapi));
 | 
			
		||||
		rc = -1;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
 | 
			
		||||
				data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
 | 
			
		||||
				data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1,
 | 
			
		||||
				data_ind->msgUnitParam.u8Size-1);
 | 
			
		||||
		//send_gsmtap(PCU_GSMTAP_C_UL_UNKNOWN, true, 0, date_ind->u8Tn, GSMTAP_CHANNEL_PACCH, data_ind->u32Fn, data_ind->msgUnitParam.u8Buffer+1, data_ind->msgUnitParam.u8Size-1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -235,31 +231,14 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
 | 
			
		||||
 | 
			
		||||
static int handle_ph_ra_ind(struct femtol1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t acc_delay;
 | 
			
		||||
	pcu_rx_ra_time(ra_ind->u16Arfcn, ra_ind->u32Fn, ra_ind->u8Tn);
 | 
			
		||||
 | 
			
		||||
	if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	DEBUGP(DL1IF, "Rx PH-RA.ind");
 | 
			
		||||
 | 
			
		||||
	/* check for under/overflow / sign */
 | 
			
		||||
	if (ra_ind->measParam.i16BurstTiming < 0)
 | 
			
		||||
		acc_delay = 0;
 | 
			
		||||
	else
 | 
			
		||||
		acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_NOTICE, "got (P)RACH request, TA = %u (ignored)\n",
 | 
			
		||||
		acc_delay);
 | 
			
		||||
 | 
			
		||||
#warning "The (P)RACH request is just dropped here"
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	if (acc_delay > bts->max_ta) {
 | 
			
		||||
		LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
 | 
			
		||||
		     acc_delay, btsb->max_ta);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
	bts_update_tbf_ta("PH-RA", ra_ind->u32Fn, fl1h->trx_no, ra_ind->u8Tn,
 | 
			
		||||
			qta2ta(ra_ind->measParam.i16BurstTiming), true);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -338,12 +317,6 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
 | 
			
		||||
	msu_param->u8Size = len;
 | 
			
		||||
	memcpy(msu_param->u8Buffer, data, len);
 | 
			
		||||
 | 
			
		||||
	gsmtap_send(fl1h->gsmtap, arfcn, data_req->u8Tn, GSMTAP_CHANNEL_PACCH,
 | 
			
		||||
			0, data_req->u32Fn, 0, 0,
 | 
			
		||||
			data_req->msgUnitParam.u8Buffer,
 | 
			
		||||
			data_req->msgUnitParam.u8Size);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* transmit */
 | 
			
		||||
	if (osmo_wqueue_enqueue(&fl1h->write_q[MQ_PDTCH_WRITE], msg) != 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
 | 
			
		||||
@@ -353,7 +326,7 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *l1if_open_pdch(void *priv, uint32_t hlayer1)
 | 
			
		||||
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
 | 
			
		||||
{
 | 
			
		||||
	struct femtol1_hdl *fl1h;
 | 
			
		||||
	int rc;
 | 
			
		||||
@@ -363,7 +336,7 @@ void *l1if_open_pdch(void *priv, uint32_t hlayer1)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	fl1h->hLayer1 = hlayer1;
 | 
			
		||||
	fl1h->priv = priv;
 | 
			
		||||
	fl1h->trx_no = trx_no;
 | 
			
		||||
	fl1h->clk_cal = 0;
 | 
			
		||||
	/* default clock source: OCXO */
 | 
			
		||||
	fl1h->clk_src = SuperFemto_ClkSrcId_Ocxo;
 | 
			
		||||
@@ -374,9 +347,7 @@ void *l1if_open_pdch(void *priv, uint32_t hlayer1)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
 | 
			
		||||
	if (fl1h->gsmtap)
 | 
			
		||||
		gsmtap_source_add_sink(fl1h->gsmtap);
 | 
			
		||||
	fl1h->gsmtap = gsmtap;
 | 
			
		||||
 | 
			
		||||
	return fl1h;
 | 
			
		||||
}
 | 
			
		||||
@@ -38,7 +38,7 @@ struct femtol1_hdl {
 | 
			
		||||
	struct gsmtap_inst *gsmtap;
 | 
			
		||||
	uint32_t gsmtap_sapi_mask;
 | 
			
		||||
 | 
			
		||||
	void *priv;			/* user reference */
 | 
			
		||||
	uint8_t trx_no;
 | 
			
		||||
 | 
			
		||||
	struct osmo_timer_list alive_timer;
 | 
			
		||||
	unsigned int alive_prim_cnt;
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
/* sysmo_sock.cpp
 | 
			
		||||
/* osmobts_sock.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
 | 
			
		||||
 *
 | 
			
		||||
@@ -29,15 +29,18 @@ extern "C" {
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <gprs_bssgp_pcu.h>
 | 
			
		||||
#include <pcuif_proto.h>
 | 
			
		||||
#include <osmocom/pcu/pcuif_proto.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
@@ -46,7 +49,7 @@ int l1if_close_pdch(void *obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * SYSMO-PCU socket functions
 | 
			
		||||
 * osmo-bts PCU socket functions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
struct pcu_sock_state {
 | 
			
		||||
@@ -55,7 +58,23 @@ struct pcu_sock_state {
 | 
			
		||||
	struct llist_head upqueue;	/* queue for sending messages */
 | 
			
		||||
} *pcu_sock_state = NULL;
 | 
			
		||||
 | 
			
		||||
static void pcu_sock_timeout(void *_priv);
 | 
			
		||||
static void pcu_sock_timeout(void *_priv)
 | 
			
		||||
{
 | 
			
		||||
	pcu_l1if_open();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pcu_tx_txt_retry(void *_priv)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
	struct pcu_sock_state *state = pcu_sock_state;
 | 
			
		||||
 | 
			
		||||
	if (bts->active)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_INFO, "Sending version %s to BTS.\n", PACKAGE_VERSION);
 | 
			
		||||
	pcu_tx_txt_ind(PCU_VERSION, "%s", PACKAGE_VERSION);
 | 
			
		||||
	osmo_timer_schedule(&state->timer, 5, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int pcu_sock_send(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
@@ -100,7 +119,7 @@ static void pcu_sock_close(struct pcu_sock_state *state, int lost)
 | 
			
		||||
 | 
			
		||||
	/* disable all slots, kick all TBFs */
 | 
			
		||||
	for (trx = 0; trx < 8; trx++) {
 | 
			
		||||
#ifdef ENABLE_SYSMODSP
 | 
			
		||||
#ifdef ENABLE_DIRECT_PHY
 | 
			
		||||
		if (bts->trx[trx].fl1h) {
 | 
			
		||||
			l1if_close_pdch(bts->trx[trx].fl1h);
 | 
			
		||||
			bts->trx[trx].fl1h = NULL;
 | 
			
		||||
@@ -108,7 +127,8 @@ static void pcu_sock_close(struct pcu_sock_state *state, int lost)
 | 
			
		||||
#endif
 | 
			
		||||
		for (ts = 0; ts < 8; ts++)
 | 
			
		||||
			bts->trx[trx].pdch[ts].disable();
 | 
			
		||||
#warning "NOT ALL RESOURCES are freed in this case... inconsistent with the other code. Share the code with pcu_l1if.c for the reset."
 | 
			
		||||
/* FIXME: NOT ALL RESOURCES are freed in this case... inconsistent with the other code. Share the code with pcu_l1if.c
 | 
			
		||||
for the reset. */
 | 
			
		||||
		gprs_rlcmac_tbf::free_all(&bts->trx[trx]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -223,6 +243,9 @@ int pcu_l1if_open(void)
 | 
			
		||||
	struct sockaddr_un local;
 | 
			
		||||
	unsigned int namelen;
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_INFO, "Opening OsmoPCU L1 interface to OsmoBTS\n");
 | 
			
		||||
 | 
			
		||||
	state = pcu_sock_state;
 | 
			
		||||
	if (!state) {
 | 
			
		||||
@@ -236,13 +259,13 @@ int pcu_l1if_open(void)
 | 
			
		||||
 | 
			
		||||
	bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
 | 
			
		||||
	if (bfd->fd < 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU-SYSMO socket.\n");
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU socket.\n");
 | 
			
		||||
		talloc_free(state);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	local.sun_family = AF_UNIX;
 | 
			
		||||
	strncpy(local.sun_path, "/tmp/pcu_bts", sizeof(local.sun_path));
 | 
			
		||||
	strncpy(local.sun_path, bts->pcu_sock_path, sizeof(local.sun_path));
 | 
			
		||||
	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
	/* we use the same magic that X11 uses in Xtranssock.c for
 | 
			
		||||
@@ -258,12 +281,13 @@ int pcu_l1if_open(void)
 | 
			
		||||
#endif
 | 
			
		||||
	rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
 | 
			
		||||
	if (rc != 0) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
 | 
			
		||||
			"socket, delaying... '%s'\n", local.sun_path);
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "Failed to connect to the osmo-bts"
 | 
			
		||||
			" PCU socket (%s), delaying... '%s'\n",
 | 
			
		||||
			strerror(errno), local.sun_path);
 | 
			
		||||
		pcu_sock_state = state;
 | 
			
		||||
		close(bfd->fd);
 | 
			
		||||
		bfd->fd = -1;
 | 
			
		||||
		state->timer.cb = pcu_sock_timeout;
 | 
			
		||||
		osmo_timer_setup(&state->timer, pcu_sock_timeout, NULL);
 | 
			
		||||
		osmo_timer_schedule(&state->timer, 5, 0);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
@@ -280,10 +304,18 @@ int pcu_l1if_open(void)
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_NOTICE, "PCU-SYSMO socket has been connected\n");
 | 
			
		||||
	LOGP(DL1IF, LOGL_NOTICE, "osmo-bts PCU socket %s has been connected\n",
 | 
			
		||||
	     local.sun_path);
 | 
			
		||||
 | 
			
		||||
	pcu_sock_state = state;
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_INFO, "Sending version %s to BTS.\n", PACKAGE_VERSION);
 | 
			
		||||
	pcu_tx_txt_ind(PCU_VERSION, "%s", PACKAGE_VERSION);
 | 
			
		||||
 | 
			
		||||
	/* Schedule a timer so we keep trying until the BTS becomes active. */
 | 
			
		||||
	osmo_timer_setup(&state->timer, pcu_tx_txt_retry, NULL);
 | 
			
		||||
	osmo_timer_schedule(&state->timer, 5, 0);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -303,8 +335,3 @@ void pcu_l1if_close(void)
 | 
			
		||||
	talloc_free(state);
 | 
			
		||||
	pcu_sock_state = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pcu_sock_timeout(void *_priv)
 | 
			
		||||
{
 | 
			
		||||
	pcu_l1if_open();
 | 
			
		||||
}
 | 
			
		||||
@@ -26,23 +26,32 @@
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/gsmtap_util.h>
 | 
			
		||||
#include <osmocom/core/gsmtap.h>
 | 
			
		||||
#include <osmocom/core/bitvec.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/gsm/l1sap.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <gprs_bssgp_pcu.h>
 | 
			
		||||
#include <pcuif_proto.h>
 | 
			
		||||
#include <osmocom/pcu/pcuif_proto.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
 | 
			
		||||
// FIXME: move this, when changed from c++ to c.
 | 
			
		||||
extern "C" {
 | 
			
		||||
void *l1if_open_pdch(void *priv, uint32_t hlayer1);
 | 
			
		||||
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1,
 | 
			
		||||
		     struct gsmtap_inst *gsmtap);
 | 
			
		||||
int l1if_connect_pdch(void *obj, uint8_t ts);
 | 
			
		||||
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
 | 
			
		||||
        uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
 | 
			
		||||
@@ -70,6 +79,42 @@ struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct value_string gsm_pcu_if_text_type_names[] = {
 | 
			
		||||
	OSMO_VALUE_STRING(PCU_VERSION),
 | 
			
		||||
	OSMO_VALUE_STRING(PCU_OML_ALERT),
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int pcu_tx_txt_ind(enum gsm_pcu_if_text_type t, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	struct gsm_pcu_if *pcu_prim;
 | 
			
		||||
	struct gsm_pcu_if_txt_ind *txt;
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	char *rep;
 | 
			
		||||
	struct msgb *msg = pcu_msgb_alloc(PCU_IF_MSG_TXT_IND, 0);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	pcu_prim = (struct gsm_pcu_if *) msg->data;
 | 
			
		||||
	txt = &pcu_prim->u.txt_ind;
 | 
			
		||||
	txt->type = t;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	rep = talloc_vasprintf(tall_pcu_ctx, fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	if (!rep)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	osmo_strlcpy(txt->text, rep, TXT_MAX_LEN);
 | 
			
		||||
	talloc_free(rep);
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_INFO, "Sending %s TXT as %s to BTS\n", txt->text,
 | 
			
		||||
	     get_value_string(gsm_pcu_if_text_type_names, t));
 | 
			
		||||
 | 
			
		||||
	return pcu_sock_send(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pcu_tx_act_req(uint8_t trx, uint8_t ts, uint8_t activate)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
@@ -98,9 +143,10 @@ static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct gsm_pcu_if *pcu_prim;
 | 
			
		||||
	struct gsm_pcu_if_data *data_req;
 | 
			
		||||
	int current_fn = get_current_fn();
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, "Sending data request: trx=%d ts=%d sapi=%d "
 | 
			
		||||
		"arfcn=%d fn=%d block=%d data=%s\n", trx, ts, sapi, arfcn, fn,
 | 
			
		||||
		"arfcn=%d fn=%d cur_fn=%d block=%d data=%s\n", trx, ts, sapi, arfcn, fn, current_fn,
 | 
			
		||||
		block_nr, osmo_hexdump(data, len));
 | 
			
		||||
 | 
			
		||||
	msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_REQ, 0);
 | 
			
		||||
@@ -124,15 +170,17 @@ static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
 | 
			
		||||
void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
 | 
			
		||||
	uint32_t fn, uint8_t block_nr)
 | 
			
		||||
{
 | 
			
		||||
#ifdef ENABLE_SYSMODSP
 | 
			
		||||
#ifdef ENABLE_DIRECT_PHY
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	if (bts->trx[trx].fl1h)
 | 
			
		||||
	if (bts->trx[trx].fl1h) {
 | 
			
		||||
		l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr,
 | 
			
		||||
			msg->data, msg->len);
 | 
			
		||||
	else
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
		pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
 | 
			
		||||
	pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
 | 
			
		||||
			msg->data, msg->len);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
@@ -140,15 +188,19 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
 | 
			
		||||
void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
 | 
			
		||||
	uint32_t fn, uint8_t block_nr)
 | 
			
		||||
{
 | 
			
		||||
#ifdef ENABLE_SYSMODSP
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	if (bts->trx[trx].fl1h)
 | 
			
		||||
	if (bts->gsmtap_categ_mask & (1 << PCU_GSMTAP_C_DL_PTCCH))
 | 
			
		||||
		gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PTCCH, 0, fn, 0, 0, msg->data, msg->len);
 | 
			
		||||
#ifdef ENABLE_DIRECT_PHY
 | 
			
		||||
	if (bts->trx[trx].fl1h) {
 | 
			
		||||
		l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr,
 | 
			
		||||
			msg->data, msg->len);
 | 
			
		||||
	else
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
		pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
 | 
			
		||||
	pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
 | 
			
		||||
			msg->data, msg->len);
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
}
 | 
			
		||||
@@ -180,6 +232,17 @@ void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi)
 | 
			
		||||
	pcu_tx_data_req(0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, 23+3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" void pcu_rx_block_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no)
 | 
			
		||||
{
 | 
			
		||||
	BTS::main_bts()->set_current_block_frame_number(fn, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" void pcu_rx_ra_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no)
 | 
			
		||||
{
 | 
			
		||||
	/* access bursts may arrive some bursts earlier */
 | 
			
		||||
	BTS::main_bts()->set_current_block_frame_number(fn, 5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx_no, uint8_t ts_no, uint8_t *data,
 | 
			
		||||
	uint8_t len, uint32_t fn, struct pcu_l1_meas *meas)
 | 
			
		||||
{
 | 
			
		||||
@@ -189,38 +252,78 @@ extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx_no, uint8_t ts_no, uint8_t *dat
 | 
			
		||||
	return pdch->rcv_block(data, len, fn, meas);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pcu_rx_data_ind_bcch(uint8_t *data, uint8_t len)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	if (len == 0) {
 | 
			
		||||
		bts->si13_is_set = false;
 | 
			
		||||
		LOGP(DL1IF, LOGL_INFO, "Received PCU data indication with empty SI13: cache cleaned\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (len != GSM_MACBLOCK_LEN) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with SI13 with unexpected length %u\n", len);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(bts->si13, data, GSM_MACBLOCK_LEN);
 | 
			
		||||
	bts->si13_is_set = true;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
	int rc;
 | 
			
		||||
	int current_fn = get_current_fn();
 | 
			
		||||
	pcu_l1_meas meas;
 | 
			
		||||
	meas.set_rssi(data_ind->rssi);
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
 | 
			
		||||
		"block=%d data=%s\n", data_ind->sapi,
 | 
			
		||||
		data_ind->arfcn, data_ind->block_nr,
 | 
			
		||||
		"fn=%d cur_fn=%d block=%d data=%s\n", data_ind->sapi,
 | 
			
		||||
		data_ind->arfcn, data_ind->fn, current_fn, data_ind->block_nr,
 | 
			
		||||
		osmo_hexdump(data_ind->data, data_ind->len));
 | 
			
		||||
 | 
			
		||||
	switch (data_ind->sapi) {
 | 
			
		||||
	case PCU_IF_SAPI_PDTCH:
 | 
			
		||||
		meas.set_rssi(data_ind->rssi);
 | 
			
		||||
#ifndef ENABLE_DIRECT_PHY
 | 
			
		||||
		/* convert BER to % value */
 | 
			
		||||
		meas.set_ber(data_ind->ber10k / 100);
 | 
			
		||||
		meas.set_bto(data_ind->ta_offs_qbits);
 | 
			
		||||
		meas.set_link_qual(data_ind->lqual_cb / 10);
 | 
			
		||||
		LOGP(DL1IF, LOGL_DEBUG, "Data indication with raw measurements received: BER10k = %d, BTO = %d, Q = %d\n",
 | 
			
		||||
		     data_ind->ber10k, data_ind->ta_offs_qbits, data_ind->lqual_cb);
 | 
			
		||||
#endif
 | 
			
		||||
		rc = pcu_rx_data_ind_pdtch(data_ind->trx_nr, data_ind->ts_nr,
 | 
			
		||||
			data_ind->data, data_ind->len, data_ind->fn,
 | 
			
		||||
			&meas);
 | 
			
		||||
		break;
 | 
			
		||||
	case PCU_IF_SAPI_BCCH:
 | 
			
		||||
		rc = pcu_rx_data_ind_bcch(data_ind->data, data_ind->len);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
 | 
			
		||||
			"unsupported sapi %d\n", data_ind->sapi);
 | 
			
		||||
		rc = -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (rc < 0 && (bts->gsmtap_categ_mask & (1 <<PCU_GSMTAP_C_UL_UNKNOWN))) {
 | 
			
		||||
		gsmtap_send(bts->gsmtap, data_ind->arfcn | GSMTAP_ARFCN_F_UPLINK, data_ind->ts_nr,
 | 
			
		||||
			    GSMTAP_CHANNEL_PACCH, 0, data_ind->fn, 0, 0, data_ind->data, data_ind->len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int current_fn = get_current_fn();
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d fn=%d\n",
 | 
			
		||||
		data_cnf->sapi, data_cnf->fn);
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d fn=%d cur_fn=%d\n",
 | 
			
		||||
		data_cnf->sapi, data_cnf->fn, current_fn);
 | 
			
		||||
 | 
			
		||||
	switch (data_cnf->sapi) {
 | 
			
		||||
	case PCU_IF_SAPI_PCH:
 | 
			
		||||
@@ -237,25 +340,26 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FIXME: remove this, when changed from c++ to c.
 | 
			
		||||
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
 | 
			
		||||
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts,
 | 
			
		||||
	uint32_t fn, uint8_t block_nr)
 | 
			
		||||
{
 | 
			
		||||
	return gprs_rlcmac_rcv_rts_block(bts_main_data(),
 | 
			
		||||
					trx, ts, arfcn, fn, block_nr);
 | 
			
		||||
					trx, ts, fn, block_nr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int current_fn = get_current_fn();
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, "RTS request received: trx=%d ts=%d sapi=%d "
 | 
			
		||||
		"arfcn=%d fn=%d block=%d\n", rts_req->trx_nr, rts_req->ts_nr,
 | 
			
		||||
		rts_req->sapi, rts_req->arfcn, rts_req->fn, rts_req->block_nr);
 | 
			
		||||
		"arfcn=%d fn=%d cur_fn=%d block=%d\n", rts_req->trx_nr, rts_req->ts_nr,
 | 
			
		||||
		rts_req->sapi, rts_req->arfcn, rts_req->fn, current_fn, rts_req->block_nr);
 | 
			
		||||
 | 
			
		||||
	switch (rts_req->sapi) {
 | 
			
		||||
	case PCU_IF_SAPI_PDTCH:
 | 
			
		||||
		pcu_rx_rts_req_pdtch(rts_req->trx_nr, rts_req->ts_nr,
 | 
			
		||||
			rts_req->arfcn, rts_req->fn, rts_req->block_nr);
 | 
			
		||||
			rts_req->fn, rts_req->block_nr);
 | 
			
		||||
		break;
 | 
			
		||||
	case PCU_IF_SAPI_PTCCH:
 | 
			
		||||
		/* FIXME */
 | 
			
		||||
@@ -278,16 +382,18 @@ static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
 | 
			
		||||
static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
 | 
			
		||||
{
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int current_fn = get_current_fn();
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_INFO, "RACH request received: sapi=%d "
 | 
			
		||||
		"qta=%d, ra=%d, fn=%d\n", rach_ind->sapi, rach_ind->qta,
 | 
			
		||||
		rach_ind->ra, rach_ind->fn);
 | 
			
		||||
		"qta=%d, ra=%d, fn=%d, cur_fn=%d, is_11bit=%d\n", rach_ind->sapi, rach_ind->qta,
 | 
			
		||||
		rach_ind->ra, rach_ind->fn, current_fn, rach_ind->is_11bit);
 | 
			
		||||
 | 
			
		||||
	switch (rach_ind->sapi) {
 | 
			
		||||
	case PCU_IF_SAPI_RACH:
 | 
			
		||||
		rc = BTS::main_bts()->rcv_rach(
 | 
			
		||||
			rach_ind->ra, rach_ind->fn,
 | 
			
		||||
			rach_ind->qta);
 | 
			
		||||
			rach_ind->qta, rach_ind->is_11bit,
 | 
			
		||||
			(ph_burst_type)rach_ind->burst_type);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
 | 
			
		||||
@@ -305,8 +411,9 @@ static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
 | 
			
		||||
	struct gprs_rlcmac_pdch *pdch;
 | 
			
		||||
	struct in_addr ia;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	int trx, ts;
 | 
			
		||||
	unsigned int trx, ts;
 | 
			
		||||
	int i;
 | 
			
		||||
	uint16_t cell_id = ntohs(info_ind->cell_id);
 | 
			
		||||
 | 
			
		||||
	if (info_ind->version != PCU_IF_VERSION) {
 | 
			
		||||
		fprintf(stderr, "PCU interface version number of BTS (%d) is "
 | 
			
		||||
@@ -319,22 +426,25 @@ static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
 | 
			
		||||
 | 
			
		||||
	if (!(info_ind->flags & PCU_IF_FLAG_ACTIVE)) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n");
 | 
			
		||||
		if (!bts->active)
 | 
			
		||||
			return -EAGAIN;
 | 
			
		||||
bssgp_failed:
 | 
			
		||||
		bts->active = false;
 | 
			
		||||
		/* free all TBF */
 | 
			
		||||
		for (trx = 0; trx < 8; trx++) {
 | 
			
		||||
		for (trx = 0; trx < ARRAY_SIZE(bts->trx); trx++) {
 | 
			
		||||
			bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
 | 
			
		||||
			for (ts = 0; ts < 8; ts++)
 | 
			
		||||
			for (ts = 0; ts < ARRAY_SIZE(bts->trx[0].pdch); ts++)
 | 
			
		||||
				bts->trx[trx].pdch[ts].free_resources();
 | 
			
		||||
		}
 | 
			
		||||
		gprs_bssgp_destroy();
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DL1IF, LOGL_INFO, "BTS available\n");
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " mcc=%x\n", info_ind->mcc);
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " mnc=%x\n", info_ind->mnc);
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " mcc=%03u\n", info_ind->mcc);
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " mnc=%0*u\n", info_ind->mnc_3_digits, info_ind->mnc);
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " lac=%d\n", info_ind->lac);
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " rac=%d\n", info_ind->rac);
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " cell_id=%d\n", ntohs(info_ind->cell_id));
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " cell_id=%d\n", cell_id);
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " bsic=%d\n", info_ind->bsic);
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " nsei=%d\n", info_ind->nsei);
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, " nse_timer=%d %d %d %d %d %d %d\n",
 | 
			
		||||
@@ -384,8 +494,8 @@ bssgp_failed:
 | 
			
		||||
	pcu = gprs_bssgp_create_and_connect(bts, info_ind->local_port[0],
 | 
			
		||||
		info_ind->remote_ip[0], info_ind->remote_port[0],
 | 
			
		||||
		info_ind->nsei, info_ind->nsvci[0], info_ind->bvci,
 | 
			
		||||
		info_ind->mcc, info_ind->mnc, info_ind->lac, info_ind->rac,
 | 
			
		||||
		info_ind->cell_id);
 | 
			
		||||
		info_ind->mcc, info_ind->mnc, info_ind->mnc_3_digits, info_ind->lac, info_ind->rac,
 | 
			
		||||
		cell_id);
 | 
			
		||||
	if (!pcu) {
 | 
			
		||||
		LOGP(DL1IF, LOGL_NOTICE, "SGSN not available\n");
 | 
			
		||||
		goto bssgp_failed;
 | 
			
		||||
@@ -415,17 +525,18 @@ bssgp_failed:
 | 
			
		||||
		bts->initial_cs_ul = bts->initial_cs_dl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (trx = 0; trx < 8; trx++) {
 | 
			
		||||
	for (trx = 0; trx < ARRAY_SIZE(bts->trx); trx++) {
 | 
			
		||||
		bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
 | 
			
		||||
		if ((info_ind->flags & PCU_IF_FLAG_SYSMO)
 | 
			
		||||
		 && info_ind->trx[trx].hlayer1) {
 | 
			
		||||
#ifdef ENABLE_SYSMODSP
 | 
			
		||||
#ifdef ENABLE_DIRECT_PHY
 | 
			
		||||
			LOGP(DL1IF, LOGL_DEBUG, " TRX %d hlayer1=%x\n", trx,
 | 
			
		||||
				info_ind->trx[trx].hlayer1);
 | 
			
		||||
				if (!bts->trx[trx].fl1h)
 | 
			
		||||
					bts->trx[trx].fl1h = l1if_open_pdch(
 | 
			
		||||
						(void *)trx,
 | 
			
		||||
						info_ind->trx[trx].hlayer1);
 | 
			
		||||
						trx,
 | 
			
		||||
						info_ind->trx[trx].hlayer1,
 | 
			
		||||
						bts->gsmtap);
 | 
			
		||||
			if (!bts->trx[trx].fl1h) {
 | 
			
		||||
				LOGP(DL1IF, LOGL_FATAL, "Failed to open direct "
 | 
			
		||||
					"DSP access for PDCH.\n");
 | 
			
		||||
@@ -439,12 +550,12 @@ bssgp_failed:
 | 
			
		||||
#endif
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (ts = 0; ts < 8; ts++) {
 | 
			
		||||
		for (ts = 0; ts < ARRAY_SIZE(bts->trx[0].pdch); ts++) {
 | 
			
		||||
			pdch = &bts->trx[trx].pdch[ts];
 | 
			
		||||
			if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
 | 
			
		||||
				/* FIXME: activate dynamically at RLCMAC */
 | 
			
		||||
				if (!pdch->is_enabled()) {
 | 
			
		||||
#ifdef ENABLE_SYSMODSP
 | 
			
		||||
#ifdef ENABLE_DIRECT_PHY
 | 
			
		||||
					if ((info_ind->flags &
 | 
			
		||||
							PCU_IF_FLAG_SYSMO))
 | 
			
		||||
						l1if_connect_pdch(
 | 
			
		||||
@@ -466,6 +577,7 @@ bssgp_failed:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bts->active = true;
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -476,9 +588,8 @@ static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
 | 
			
		||||
	/* omit frame numbers not starting at a MAC block */
 | 
			
		||||
	if (fn13 != 0 && fn13 != 4 && fn13 != 8)
 | 
			
		||||
		return 0;
 | 
			
		||||
#warning uncomment
 | 
			
		||||
//	LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n",
 | 
			
		||||
//		time_ind->fn % 52);
 | 
			
		||||
 | 
			
		||||
	LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n", time_ind->fn % 52);
 | 
			
		||||
 | 
			
		||||
	BTS::main_bts()->set_current_frame_number(time_ind->fn);
 | 
			
		||||
	return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -29,10 +29,48 @@ extern "C" {
 | 
			
		||||
#include <osmocom/core/timer.h>
 | 
			
		||||
#include <osmocom/core/bitvec.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
#include <osmocom/pcu/pcuif_proto.h>
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static inline uint8_t qta2ta(int16_t qta)
 | 
			
		||||
{
 | 
			
		||||
	if (qta < 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
	if (qta > 252)
 | 
			
		||||
		qta = 252;
 | 
			
		||||
	return qta >> 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int8_t sign_qta2ta(int16_t qta)
 | 
			
		||||
{
 | 
			
		||||
	int8_t ta_adj = 0;
 | 
			
		||||
 | 
			
		||||
	if (qta < -252)
 | 
			
		||||
		qta = -252;
 | 
			
		||||
 | 
			
		||||
	if (qta > 252)
 | 
			
		||||
		qta = 252;
 | 
			
		||||
 | 
			
		||||
	/* 1-bit TA adjustment  if TA error reported by L1 is outside +/- 2 qbits */
 | 
			
		||||
	if (qta > 2)
 | 
			
		||||
		ta_adj = 1;
 | 
			
		||||
	if (qta < -2)
 | 
			
		||||
		ta_adj = -1;
 | 
			
		||||
 | 
			
		||||
	return (qta >> 2) + ta_adj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint8_t ta_limit(int16_t ta)
 | 
			
		||||
{
 | 
			
		||||
	if (ta < 0)
 | 
			
		||||
		ta = 0;
 | 
			
		||||
	if (ta > 63)
 | 
			
		||||
		ta = 63;
 | 
			
		||||
	return ta;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * L1 Measurement values
 | 
			
		||||
 */
 | 
			
		||||
@@ -48,7 +86,8 @@ struct pcu_l1_meas_ts {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pcu_l1_meas_ts() :
 | 
			
		||||
		have_ms_i_level(0)
 | 
			
		||||
		have_ms_i_level(0),
 | 
			
		||||
		ms_i_level(0)
 | 
			
		||||
	{}
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
@@ -100,7 +139,14 @@ struct pcu_l1_meas {
 | 
			
		||||
		have_ms_rx_qual(0),
 | 
			
		||||
		have_ms_c_value(0),
 | 
			
		||||
		have_ms_sign_var(0),
 | 
			
		||||
		have_ms_i_level(0)
 | 
			
		||||
		have_ms_i_level(0),
 | 
			
		||||
		rssi(0),
 | 
			
		||||
		ber(0),
 | 
			
		||||
		bto(0),
 | 
			
		||||
		link_qual(0),
 | 
			
		||||
		ms_rx_qual(0),
 | 
			
		||||
		ms_c_value(0),
 | 
			
		||||
		ms_sign_var(0)
 | 
			
		||||
	{}
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
@@ -114,6 +160,8 @@ void pcu_l1if_tx_agch(bitvec * block, int len);
 | 
			
		||||
 | 
			
		||||
void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi);
 | 
			
		||||
 | 
			
		||||
int pcu_tx_txt_ind(enum gsm_pcu_if_text_type t, const char *fmt, ...);
 | 
			
		||||
 | 
			
		||||
int pcu_l1if_open(void);
 | 
			
		||||
void pcu_l1if_close(void);
 | 
			
		||||
 | 
			
		||||
@@ -122,15 +170,18 @@ int pcu_sock_send(struct msgb *msg);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C"
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
 | 
			
		||||
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts,
 | 
			
		||||
	uint32_t fn, uint8_t block_nr);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C"
 | 
			
		||||
#endif
 | 
			
		||||
int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
 | 
			
		||||
	uint8_t len, uint32_t fn, struct pcu_l1_meas *meas);
 | 
			
		||||
 | 
			
		||||
void pcu_rx_block_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no);
 | 
			
		||||
void pcu_rx_ra_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#endif // PCU_L1_IF_H
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										106
									
								
								src/pcu_main.cpp
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/pcu_main.cpp
									
									
									
									
									
								
							@@ -17,32 +17,50 @@
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <gprs_bssgp_pcu.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gsm_timer.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <sched.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
#include <osmocom/pcu/pcuif_proto.h>
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "pcu_vty.h"
 | 
			
		||||
#include <osmocom/gprs/gprs_bssgp.h>
 | 
			
		||||
#include <osmocom/gprs/gprs_ns.h>
 | 
			
		||||
#include <osmocom/vty/telnet_interface.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/vty.h>
 | 
			
		||||
#include <osmocom/vty/ports.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/stats.h>
 | 
			
		||||
#include <osmocom/core/gsmtap.h>
 | 
			
		||||
#include <osmocom/core/gsmtap_util.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern struct gprs_nsvc *nsvc;
 | 
			
		||||
uint16_t spoof_mcc = 0, spoof_mnc = 0;
 | 
			
		||||
bool spoof_mnc_3_digits = false;
 | 
			
		||||
static int config_given = 0;
 | 
			
		||||
static char *config_file = strdup("osmo-pcu.cfg");
 | 
			
		||||
extern struct vty_app_info pcu_vty_info;
 | 
			
		||||
void *tall_pcu_ctx;
 | 
			
		||||
void *tall_pcu_ctx = NULL;
 | 
			
		||||
extern void *bv_tall_ctx;
 | 
			
		||||
static int quit = 0;
 | 
			
		||||
static int rt_prio = -1;
 | 
			
		||||
static const char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
 | 
			
		||||
 | 
			
		||||
static void print_help()
 | 
			
		||||
{
 | 
			
		||||
@@ -54,8 +72,10 @@ static void print_help()
 | 
			
		||||
			"provided by BTS\n"
 | 
			
		||||
		"  -n	--mnc MNC	use given MNC instead of value "
 | 
			
		||||
			"provided by BTS\n"
 | 
			
		||||
		"  -V   --version	print version\n"
 | 
			
		||||
		"  -r   --realtime PRIO Use SCHED_RR with the specified "
 | 
			
		||||
			"priority\n"
 | 
			
		||||
		"  -i	--gsmtap-ip	The destination IP used for GSMTAP.\n"
 | 
			
		||||
		);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -72,10 +92,11 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
			{ "version", 0, 0, 'V' },
 | 
			
		||||
			{ "realtime", 1, 0, 'r' },
 | 
			
		||||
			{ "exit", 0, 0, 'e' },
 | 
			
		||||
			{ "gsmtap-ip", 1, 0, 'i' },
 | 
			
		||||
			{ 0, 0, 0, 0 }
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		c = getopt_long(argc, argv, "hc:m:n:Vr:e",
 | 
			
		||||
		c = getopt_long(argc, argv, "hc:m:n:Vr:e:i:",
 | 
			
		||||
				long_options, &option_idx);
 | 
			
		||||
		if (c == -1)
 | 
			
		||||
			break;
 | 
			
		||||
@@ -94,12 +115,18 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
			spoof_mcc = atoi(optarg);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'n':
 | 
			
		||||
			spoof_mnc = atoi(optarg);
 | 
			
		||||
			if (osmo_mnc_from_str(optarg, &spoof_mnc, &spoof_mnc_3_digits)) {
 | 
			
		||||
				fprintf(stderr, "Error decoding MNC '%s'\n", optarg);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 'V':
 | 
			
		||||
			print_version(1);
 | 
			
		||||
			exit(0);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'i':
 | 
			
		||||
			gsmtap_addr = optarg;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'r':
 | 
			
		||||
			rt_prio = atoi(optarg);
 | 
			
		||||
			break;
 | 
			
		||||
@@ -154,14 +181,18 @@ int main(int argc, char *argv[])
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	tall_pcu_ctx = talloc_named_const(NULL, 1, "Osmo-PCU context");
 | 
			
		||||
	if (!tall_pcu_ctx)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	bv_tall_ctx = tall_pcu_ctx;
 | 
			
		||||
	/* tall_pcu_ctx may already have been initialized in bts.cpp during early_init(). */
 | 
			
		||||
	if (!tall_pcu_ctx) {
 | 
			
		||||
		tall_pcu_ctx = talloc_named_const(NULL, 1, "Osmo-PCU context");
 | 
			
		||||
		if (!tall_pcu_ctx)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		osmo_init_logging2(tall_pcu_ctx, &gprs_log_info);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bts = bts_main_data();
 | 
			
		||||
	bts->fc_interval = 1;
 | 
			
		||||
	bts->initial_cs_dl = bts->initial_cs_ul = 1;
 | 
			
		||||
	bts->initial_mcs_dl = bts->initial_mcs_ul = 1;
 | 
			
		||||
	bts->cs1 = 1;
 | 
			
		||||
	bts->t3142 = 20;
 | 
			
		||||
	bts->t3169 = 5;
 | 
			
		||||
@@ -172,12 +203,15 @@ int main(int argc, char *argv[])
 | 
			
		||||
	bts->n3103 = 4;
 | 
			
		||||
	bts->n3105 = 8;
 | 
			
		||||
	bts->alpha = 0; /* a = 0.0 */
 | 
			
		||||
	bts->si13_is_set = false;
 | 
			
		||||
	bts->ms_idle_sec = 60; /* slightly above T3314 (default 44s, 24.008, 11.2.2) */
 | 
			
		||||
	bts->cs_adj_enabled = 1;
 | 
			
		||||
	bts->cs_adj_upper_limit = 33; /* Decrease CS if the error rate is above */
 | 
			
		||||
	bts->cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
 | 
			
		||||
	bts->max_cs_ul = 4;
 | 
			
		||||
	bts->max_cs_dl = 4;
 | 
			
		||||
	bts->max_mcs_ul = MAX_GPRS_CS;
 | 
			
		||||
	bts->max_mcs_dl = MAX_GPRS_CS;
 | 
			
		||||
	/* CS-1 to CS-4 */
 | 
			
		||||
	bts->cs_lqual_ranges[0].low = -256;
 | 
			
		||||
	bts->cs_lqual_ranges[0].high = 6;
 | 
			
		||||
@@ -187,13 +221,51 @@ int main(int argc, char *argv[])
 | 
			
		||||
	bts->cs_lqual_ranges[2].high = 13;
 | 
			
		||||
	bts->cs_lqual_ranges[3].low = 12;
 | 
			
		||||
	bts->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) */
 | 
			
		||||
	bts->mcs_lqual_ranges[0].low = -256;
 | 
			
		||||
	bts->mcs_lqual_ranges[0].high = 6;
 | 
			
		||||
	bts->mcs_lqual_ranges[1].low = 5;
 | 
			
		||||
	bts->mcs_lqual_ranges[1].high = 8;
 | 
			
		||||
	bts->mcs_lqual_ranges[2].low = 7;
 | 
			
		||||
	bts->mcs_lqual_ranges[2].high = 13;
 | 
			
		||||
	bts->mcs_lqual_ranges[3].low = 12;
 | 
			
		||||
	bts->mcs_lqual_ranges[3].high = 15;
 | 
			
		||||
	bts->mcs_lqual_ranges[4].low = 14;
 | 
			
		||||
	bts->mcs_lqual_ranges[4].high = 17;
 | 
			
		||||
	bts->mcs_lqual_ranges[5].low = 16;
 | 
			
		||||
	bts->mcs_lqual_ranges[5].high = 18;
 | 
			
		||||
	bts->mcs_lqual_ranges[6].low = 17;
 | 
			
		||||
	bts->mcs_lqual_ranges[6].high = 20;
 | 
			
		||||
	bts->mcs_lqual_ranges[7].low = 19;
 | 
			
		||||
	bts->mcs_lqual_ranges[7].high = 24;
 | 
			
		||||
	bts->mcs_lqual_ranges[8].low = 23;
 | 
			
		||||
	bts->mcs_lqual_ranges[8].high = 256;
 | 
			
		||||
	bts->cs_downgrade_threshold = 200;
 | 
			
		||||
 | 
			
		||||
	/* TODO: increase them when CRBB decoding is implemented */
 | 
			
		||||
	bts->ws_base = 64;
 | 
			
		||||
	bts->ws_pdch = 0;
 | 
			
		||||
 | 
			
		||||
	bts->llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
 | 
			
		||||
	bts->dl_tbf_idle_msec = 2000;
 | 
			
		||||
	bts->llc_idle_ack_csec = 10;
 | 
			
		||||
 | 
			
		||||
	msgb_set_talloc_ctx(tall_pcu_ctx);
 | 
			
		||||
	/*
 | 
			
		||||
	 * By default resegmentation is supported in DL
 | 
			
		||||
	 * can also be configured through VTY
 | 
			
		||||
	 */
 | 
			
		||||
	bts->dl_arq_type = EGPRS_ARQ1;
 | 
			
		||||
 | 
			
		||||
	osmo_init_logging(&gprs_log_info);
 | 
			
		||||
	bts->pcu_sock_path = talloc_strdup(tall_pcu_ctx, PCU_SOCK_DEFAULT);
 | 
			
		||||
 | 
			
		||||
	msgb_talloc_ctx_init(tall_pcu_ctx, 0);
 | 
			
		||||
 | 
			
		||||
	osmo_stats_init(tall_pcu_ctx);
 | 
			
		||||
	rate_ctr_init(tall_pcu_ctx);
 | 
			
		||||
	gprs_ns_set_log_ss(DNS);
 | 
			
		||||
	bssgp_set_log_ss(DBSSGP);
 | 
			
		||||
 | 
			
		||||
	vty_init(&pcu_vty_info);
 | 
			
		||||
@@ -206,6 +278,13 @@ int main(int argc, char *argv[])
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bts->gsmtap = gsmtap_source_init(gsmtap_addr, GSMTAP_UDP_PORT, 1);
 | 
			
		||||
 | 
			
		||||
	if (bts->gsmtap)
 | 
			
		||||
		gsmtap_source_add_sink(bts->gsmtap);
 | 
			
		||||
	else
 | 
			
		||||
		fprintf(stderr, "Failed to initialize GSMTAP for %s\n", gsmtap_addr);
 | 
			
		||||
 | 
			
		||||
	rc = vty_read_config_file(config_file, NULL);
 | 
			
		||||
	if (rc < 0 && config_given) {
 | 
			
		||||
		fprintf(stderr, "Failed to parse the config file: '%s'\n",
 | 
			
		||||
@@ -216,7 +295,8 @@ int main(int argc, char *argv[])
 | 
			
		||||
		fprintf(stderr, "No config file: '%s' Using default config.\n",
 | 
			
		||||
			config_file);
 | 
			
		||||
 | 
			
		||||
	rc = telnet_init(tall_pcu_ctx, NULL, 4240);
 | 
			
		||||
	rc = telnet_init_dynif(tall_pcu_ctx, NULL, vty_get_bind_addr(),
 | 
			
		||||
			       OSMO_VTY_PORT_PCU);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		fprintf(stderr, "Error initializing telnet\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
 
 | 
			
		||||
@@ -16,11 +16,35 @@
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int msecs_to_frames(int msecs) {
 | 
			
		||||
	return (msecs * (1024 * 1000 / 4615)) / 1024;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint32_t next_fn(uint32_t fn, uint32_t offset)
 | 
			
		||||
{
 | 
			
		||||
	return (fn + offset) % GSM_MAX_FN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void csecs_to_timeval(unsigned csecs, struct timeval *tv) {
 | 
			
		||||
	tv->tv_sec  = csecs / 100;
 | 
			
		||||
	tv->tv_usec = (csecs % 100) * 10000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline unsigned int pcu_bitcount(T x)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int count = 0;
 | 
			
		||||
	for (count = 0; x; count += 1)
 | 
			
		||||
		x &= x - 1;
 | 
			
		||||
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint8_t pcu_lsb(uint8_t x)
 | 
			
		||||
{
 | 
			
		||||
	return x & -x;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										381
									
								
								src/pcu_vty.c
									
									
									
									
									
								
							
							
						
						
									
										381
									
								
								src/pcu_vty.c
									
									
									
									
									
								
							@@ -3,18 +3,87 @@
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
#include <osmocom/vty/stats.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/pcu/pcuif_proto.h>
 | 
			
		||||
#include "pcu_vty.h"
 | 
			
		||||
#include "gprs_rlcmac.h"
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
#include "bts.h"
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
 | 
			
		||||
#include "pcu_vty_functions.h"
 | 
			
		||||
 | 
			
		||||
enum node_type pcu_vty_go_parent(struct vty *vty)
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
static const struct value_string pcu_gsmtap_categ_names[] = {
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_UNKNOWN,	"dl-unknown" },
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_DUMMY,	"dl-dummy" },
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_CTRL,		"dl-ctrl" },
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_DATA_GPRS,	"dl-data-gprs" },
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_DATA_EGPRS,	"dl-data-egprs" },
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_PTCCH,	"dl-ptcch" },
 | 
			
		||||
 | 
			
		||||
	{ PCU_GSMTAP_C_UL_UNKNOWN,	"ul-unknown" },
 | 
			
		||||
	{ PCU_GSMTAP_C_UL_DUMMY,	"ul-dummy" },
 | 
			
		||||
	{ PCU_GSMTAP_C_UL_CTRL,		"ul-ctrl" },
 | 
			
		||||
	{ PCU_GSMTAP_C_UL_DATA_GPRS,	"ul-data-gprs" },
 | 
			
		||||
	{ PCU_GSMTAP_C_UL_DATA_EGPRS,	"ul-data-egprs" },
 | 
			
		||||
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct value_string pcu_gsmtap_categ_help[] = {
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_UNKNOWN,	"Unknown / Unparseable / Erroneous Downlink Blocks" },
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_DUMMY,	"Downlink Dummy Blocks" },
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_CTRL,		"Downlink Control Blocks" },
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_DATA_GPRS,	"Downlink Data Blocks (GPRS)" },
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_DATA_EGPRS,	"Downlink Data Blocks (EGPRS)" },
 | 
			
		||||
	{ PCU_GSMTAP_C_DL_PTCCH,	"Downlink PTCCH Blocks" },
 | 
			
		||||
 | 
			
		||||
	{ PCU_GSMTAP_C_UL_UNKNOWN,	"Unknown / Unparseable / Erroneous Downlink Blocks" },
 | 
			
		||||
	{ PCU_GSMTAP_C_UL_DUMMY,	"Uplink Dummy Blocks" },
 | 
			
		||||
	{ PCU_GSMTAP_C_UL_CTRL,		"Uplink Control Blocks" },
 | 
			
		||||
	{ PCU_GSMTAP_C_UL_DATA_GPRS,	"Uplink Data Blocks (GPRS)" },
 | 
			
		||||
	{ PCU_GSMTAP_C_UL_DATA_EGPRS,	"Uplink Data Blocks (EGPRS)" },
 | 
			
		||||
 | 
			
		||||
	{ 0, NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_gsmtap_categ, cfg_pcu_gsmtap_categ_cmd, "HIDDEN", "HIDDEN")
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
	int categ;
 | 
			
		||||
 | 
			
		||||
	categ = get_string_value(pcu_gsmtap_categ_names, argv[0]);
 | 
			
		||||
	if (categ < 0)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	bts->gsmtap_categ_mask |= (1 << categ);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_no_gsmtap_categ, cfg_pcu_no_gsmtap_categ_cmd, "HIDDEN", "HIDDEN")
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
	int categ;
 | 
			
		||||
 | 
			
		||||
	categ = get_string_value(pcu_gsmtap_categ_names, argv[0]);
 | 
			
		||||
	if (categ < 0)
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	bts->gsmtap_categ_mask &= ~(1 << categ);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int pcu_vty_go_parent(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	switch (vty->node) {
 | 
			
		||||
#if 0
 | 
			
		||||
@@ -51,8 +120,12 @@ static struct cmd_node pcu_node = {
 | 
			
		||||
static int config_write_pcu(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "pcu%s", VTY_NEWLINE);
 | 
			
		||||
	if (bts->egprs_enabled)
 | 
			
		||||
		vty_out(vty, " egprs only%s", VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, " flow-control-interval %d%s", bts->fc_interval,
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	if (bts->fc_bvc_bucket_size)
 | 
			
		||||
@@ -105,6 +178,50 @@ static int config_write_pcu(struct vty *vty)
 | 
			
		||||
		bts->cs_lqual_ranges[3].low,
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, " mcs link-quality-ranges mcs1 %d mcs2 %d %d mcs3 %d %d mcs4 %d %d mcs5 %d %d mcs6 %d %d mcs7 %d %d mcs8 %d %d mcs9 %d%s",
 | 
			
		||||
		bts->mcs_lqual_ranges[0].high,
 | 
			
		||||
		bts->mcs_lqual_ranges[1].low,
 | 
			
		||||
		bts->mcs_lqual_ranges[1].high,
 | 
			
		||||
		bts->mcs_lqual_ranges[2].low,
 | 
			
		||||
		bts->mcs_lqual_ranges[2].high,
 | 
			
		||||
		bts->mcs_lqual_ranges[3].low,
 | 
			
		||||
		bts->mcs_lqual_ranges[3].high,
 | 
			
		||||
		bts->mcs_lqual_ranges[4].low,
 | 
			
		||||
		bts->mcs_lqual_ranges[4].high,
 | 
			
		||||
		bts->mcs_lqual_ranges[5].low,
 | 
			
		||||
		bts->mcs_lqual_ranges[5].high,
 | 
			
		||||
		bts->mcs_lqual_ranges[6].low,
 | 
			
		||||
		bts->mcs_lqual_ranges[6].high,
 | 
			
		||||
		bts->mcs_lqual_ranges[7].low,
 | 
			
		||||
		bts->mcs_lqual_ranges[7].high,
 | 
			
		||||
		bts->mcs_lqual_ranges[8].low,
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (bts->initial_mcs_dl != 1 && bts->initial_mcs_ul != 1) {
 | 
			
		||||
		if (bts->initial_mcs_ul == bts->initial_mcs_dl)
 | 
			
		||||
			vty_out(vty, " mcs %d%s", bts->initial_mcs_dl,
 | 
			
		||||
				VTY_NEWLINE);
 | 
			
		||||
		else
 | 
			
		||||
			vty_out(vty, " mcs %d %d%s", bts->initial_mcs_dl,
 | 
			
		||||
				bts->initial_mcs_ul, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bts->max_mcs_dl && bts->max_mcs_ul) {
 | 
			
		||||
		if (bts->max_mcs_ul == bts->max_mcs_dl)
 | 
			
		||||
			vty_out(vty, " mcs max %d%s", bts->max_mcs_dl,
 | 
			
		||||
				VTY_NEWLINE);
 | 
			
		||||
		else
 | 
			
		||||
			vty_out(vty, " mcs max %d %d%s", bts->max_mcs_dl,
 | 
			
		||||
				bts->max_mcs_ul, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, " window-size %d %d%s", bts->ws_base, bts->ws_pdch,
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (bts->dl_arq_type)
 | 
			
		||||
		vty_out(vty, " egprs dl arq-type arq2%s",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	if (bts->force_llc_lifetime == 0xffff)
 | 
			
		||||
		vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
 | 
			
		||||
	else if (bts->force_llc_lifetime)
 | 
			
		||||
@@ -137,8 +254,18 @@ static int config_write_pcu(struct vty *vty)
 | 
			
		||||
	if (bts->dl_tbf_idle_msec)
 | 
			
		||||
		vty_out(vty, " dl-tbf-idle-time %d%s", bts->dl_tbf_idle_msec,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
	if (strcmp(bts->pcu_sock_path, PCU_SOCK_DEFAULT))
 | 
			
		||||
		vty_out(vty, " pcu-socket %s%s", bts->pcu_sock_path, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return pcu_vty_config_write_pcu_ext(vty);
 | 
			
		||||
	for (i = 0; i < 32; i++) {
 | 
			
		||||
		unsigned int cs = (1 << i);
 | 
			
		||||
		if (bts->gsmtap_categ_mask & cs) {
 | 
			
		||||
			vty_out(vty, " gsmtap-category %s%s",
 | 
			
		||||
				get_value_string(pcu_gsmtap_categ_names, i), VTY_NEWLINE);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* per-BTS configuration */
 | 
			
		||||
@@ -152,6 +279,32 @@ DEFUN(cfg_pcu,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define EGPRS_STR "EGPRS configuration\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_egprs,
 | 
			
		||||
      cfg_pcu_egprs_cmd,
 | 
			
		||||
      "egprs only",
 | 
			
		||||
      EGPRS_STR "Use EGPRS and disable plain GPRS\n")
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	bts->egprs_enabled = 1;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_no_egprs,
 | 
			
		||||
      cfg_pcu_no_egprs_cmd,
 | 
			
		||||
      "no egprs",
 | 
			
		||||
      NO_STR EGPRS_STR)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	bts->egprs_enabled = 0;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_fc_interval,
 | 
			
		||||
      cfg_pcu_fc_interval_cmd,
 | 
			
		||||
      "flow-control-interval <1-10>",
 | 
			
		||||
@@ -323,11 +476,12 @@ DEFUN(cfg_pcu_no_cs,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CS_MAX_STR "Set maximum values for adaptive CS selection (overrides BTS config)\n"
 | 
			
		||||
DEFUN(cfg_pcu_cs_max,
 | 
			
		||||
      cfg_pcu_cs_max_cmd,
 | 
			
		||||
      "cs max <1-4> [<1-4>]",
 | 
			
		||||
      CS_STR
 | 
			
		||||
      "Set maximum values for adaptive CS selection (overrides BTS config)\n"
 | 
			
		||||
      CS_MAX_STR
 | 
			
		||||
      "Maximum CS value to be used\n"
 | 
			
		||||
      "Use a different maximum CS value for the uplink")
 | 
			
		||||
{
 | 
			
		||||
@@ -346,8 +500,7 @@ DEFUN(cfg_pcu_cs_max,
 | 
			
		||||
DEFUN(cfg_pcu_no_cs_max,
 | 
			
		||||
      cfg_pcu_no_cs_max_cmd,
 | 
			
		||||
      "no cs max",
 | 
			
		||||
      NO_STR CS_STR
 | 
			
		||||
      "Set maximum values for adaptive CS selection (overrides BTS config)\n")
 | 
			
		||||
      NO_STR CS_STR CS_MAX_STR)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
@@ -357,6 +510,112 @@ DEFUN(cfg_pcu_no_cs_max,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MCS_STR "Modulation and Coding Scheme configuration (EGPRS)\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_mcs,
 | 
			
		||||
      cfg_pcu_mcs_cmd,
 | 
			
		||||
      "mcs <1-9> [<1-9>]",
 | 
			
		||||
      MCS_STR
 | 
			
		||||
      "Initial MCS value to be used (default 1)\n"
 | 
			
		||||
      "Use a different initial MCS value for the uplink")
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
	uint8_t cs = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	bts->initial_mcs_dl = cs;
 | 
			
		||||
	if (argc > 1)
 | 
			
		||||
		bts->initial_mcs_ul = atoi(argv[1]);
 | 
			
		||||
	else
 | 
			
		||||
		bts->initial_mcs_ul = cs;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_no_mcs,
 | 
			
		||||
      cfg_pcu_no_mcs_cmd,
 | 
			
		||||
      "no mcs",
 | 
			
		||||
      NO_STR MCS_STR)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	bts->initial_mcs_dl = 1;
 | 
			
		||||
	bts->initial_mcs_ul = 1;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_mcs_max,
 | 
			
		||||
      cfg_pcu_mcs_max_cmd,
 | 
			
		||||
      "mcs max <1-9> [<1-9>]",
 | 
			
		||||
      MCS_STR
 | 
			
		||||
      CS_MAX_STR
 | 
			
		||||
      "Maximum MCS value to be used\n"
 | 
			
		||||
      "Use a different maximum MCS value for the uplink")
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
	uint8_t mcs = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	bts->max_mcs_dl = mcs;
 | 
			
		||||
	if (argc > 1)
 | 
			
		||||
		bts->max_mcs_ul = atoi(argv[1]);
 | 
			
		||||
	else
 | 
			
		||||
		bts->max_mcs_ul = mcs;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_no_mcs_max,
 | 
			
		||||
      cfg_pcu_no_mcs_max_cmd,
 | 
			
		||||
      "no mcs max",
 | 
			
		||||
      NO_STR MCS_STR CS_MAX_STR)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	bts->max_mcs_dl = 0;
 | 
			
		||||
	bts->max_mcs_ul = 0;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define DL_STR "downlink specific configuration\n"
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_dl_arq_type,
 | 
			
		||||
      cfg_pcu_dl_arq_cmd,
 | 
			
		||||
      "egprs dl arq-type (spb|arq2)",
 | 
			
		||||
      EGPRS_STR DL_STR "ARQ options\n"
 | 
			
		||||
      "enable SPB(ARQ1) support\n"
 | 
			
		||||
      "enable ARQ2 support")
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	if (!strcmp(argv[0], "arq2"))
 | 
			
		||||
		bts->dl_arq_type = 1;
 | 
			
		||||
	else
 | 
			
		||||
		bts->dl_arq_type = 0;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_window_size,
 | 
			
		||||
      cfg_pcu_window_size_cmd,
 | 
			
		||||
      "window-size <0-1024> [<0-256>]",
 | 
			
		||||
      "Window size configuration (b + N_PDCH * f)\n"
 | 
			
		||||
      "Base value (b)\n"
 | 
			
		||||
      "Factor for number of PDCH (f)")
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
	uint16_t b = atoi(argv[0]);
 | 
			
		||||
 | 
			
		||||
	bts->ws_base = b;
 | 
			
		||||
	if (argc > 1) {
 | 
			
		||||
		uint16_t f = atoi(argv[1]);
 | 
			
		||||
		bts->ws_pdch = f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define QUEUE_STR "Packet queue options\n"
 | 
			
		||||
#define LIFETIME_STR "Set lifetime limit of LLC frame in centi-seconds " \
 | 
			
		||||
	"(overrides the value given by SGSN)\n"
 | 
			
		||||
@@ -730,6 +989,75 @@ DEFUN(cfg_pcu_cs_lqual_ranges,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_mcs_lqual_ranges,
 | 
			
		||||
      cfg_pcu_mcs_lqual_ranges_cmd,
 | 
			
		||||
      "mcs link-quality-ranges mcs1 <0-35> mcs2 <0-35> <0-35> mcs3 <0-35> <0-35> mcs4 <0-35> <0-35> mcs5 <0-35> <0-35> mcs6 <0-35> <0-35> mcs7 <0-35> <0-35> mcs8 <0-35> <0-35> mcs9 <0-35>",
 | 
			
		||||
      CS_STR "Set link quality ranges\n"
 | 
			
		||||
      "Set quality range for MCS-1 (high value only)\n"
 | 
			
		||||
      "MCS-1 high (dB)\n"
 | 
			
		||||
      "Set quality range for MCS-2\n"
 | 
			
		||||
      "MCS-2 high (dB)\n"
 | 
			
		||||
      "MCS-2 low (dB)\n"
 | 
			
		||||
      "Set quality range for MCS-3\n"
 | 
			
		||||
      "MCS-3 high (dB)\n"
 | 
			
		||||
      "MCS-3 low (dB)\n"
 | 
			
		||||
      "Set quality range for MCS-4\n"
 | 
			
		||||
      "MCS-4 high (dB)\n"
 | 
			
		||||
      "MCS-4 low (dB)\n"
 | 
			
		||||
      "Set quality range for MCS-5\n"
 | 
			
		||||
      "MCS-5 high (dB)\n"
 | 
			
		||||
      "MCS-5 low (dB)\n"
 | 
			
		||||
      "Set quality range for MCS-6\n"
 | 
			
		||||
      "MCS-6 low (dB)\n"
 | 
			
		||||
      "MCS-6 high (dB)\n"
 | 
			
		||||
      "Set quality range for MCS-7\n"
 | 
			
		||||
      "MCS-7 low (dB)\n"
 | 
			
		||||
      "MCS-7 high (dB)\n"
 | 
			
		||||
      "Set quality range for MCS-8\n"
 | 
			
		||||
      "MCS-8 low (dB)\n"
 | 
			
		||||
      "MCS-8 high (dB)\n"
 | 
			
		||||
      "Set quality range for MCS-9 (low value only)\n"
 | 
			
		||||
      "MCS-9 low (dB)\n")
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	bts->mcs_lqual_ranges[0].high = atoi(argv[0]);
 | 
			
		||||
	bts->mcs_lqual_ranges[1].low  = atoi(argv[1]);
 | 
			
		||||
	bts->mcs_lqual_ranges[1].high = atoi(argv[2]);
 | 
			
		||||
	bts->mcs_lqual_ranges[2].low  = atoi(argv[3]);
 | 
			
		||||
	bts->mcs_lqual_ranges[2].high = atoi(argv[4]);
 | 
			
		||||
	bts->mcs_lqual_ranges[3].low  = atoi(argv[5]);
 | 
			
		||||
	bts->mcs_lqual_ranges[3].high  = atoi(argv[6]);
 | 
			
		||||
	bts->mcs_lqual_ranges[4].low  = atoi(argv[7]);
 | 
			
		||||
	bts->mcs_lqual_ranges[4].high  = atoi(argv[8]);
 | 
			
		||||
	bts->mcs_lqual_ranges[5].low  = atoi(argv[9]);
 | 
			
		||||
	bts->mcs_lqual_ranges[5].high  = atoi(argv[10]);
 | 
			
		||||
	bts->mcs_lqual_ranges[6].low  = atoi(argv[11]);
 | 
			
		||||
	bts->mcs_lqual_ranges[6].high  = atoi(argv[12]);
 | 
			
		||||
	bts->mcs_lqual_ranges[7].low  = atoi(argv[13]);
 | 
			
		||||
	bts->mcs_lqual_ranges[7].high  = atoi(argv[14]);
 | 
			
		||||
	bts->mcs_lqual_ranges[8].low  = atoi(argv[15]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_pcu_sock,
 | 
			
		||||
      cfg_pcu_sock_cmd,
 | 
			
		||||
      "pcu-socket PATH",
 | 
			
		||||
      "Configure the osmo-bts PCU socket file/path name\n"
 | 
			
		||||
      "Path of the socket to connect to\n")
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
 | 
			
		||||
	if (bts->pcu_sock_path) {
 | 
			
		||||
		/* FIXME: close the interface? */
 | 
			
		||||
		talloc_free(bts->pcu_sock_path);
 | 
			
		||||
	}
 | 
			
		||||
	bts->pcu_sock_path = talloc_strdup(tall_pcu_ctx, argv[0]);
 | 
			
		||||
	/* FIXME: re-open the interface? */
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(show_tbf,
 | 
			
		||||
      show_tbf_cmd,
 | 
			
		||||
@@ -737,19 +1065,7 @@ DEFUN(show_tbf,
 | 
			
		||||
      SHOW_STR "information about TBFs\n" "All TBFs\n")
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = bts_main_data();
 | 
			
		||||
	struct llist_head *tbf;
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "UL TBFs%s", VTY_NEWLINE);
 | 
			
		||||
	llist_for_each(tbf, &bts->ul_tbfs) {
 | 
			
		||||
		tbf_print_vty_info(vty, tbf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "%sDL TBFs%s", VTY_NEWLINE, VTY_NEWLINE);
 | 
			
		||||
	llist_for_each(tbf, &bts->dl_tbfs) {
 | 
			
		||||
		tbf_print_vty_info(vty, tbf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
	return pcu_vty_show_tbf_all(vty, bts);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(show_ms_all,
 | 
			
		||||
@@ -804,11 +1120,26 @@ int pcu_vty_init(const struct log_info *cat)
 | 
			
		||||
{
 | 
			
		||||
//	install_element_ve(&show_pcu_cmd);
 | 
			
		||||
 | 
			
		||||
	cfg_pcu_gsmtap_categ_cmd.string = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_names,
 | 
			
		||||
						"gsmtap-category (",
 | 
			
		||||
						"|",")", VTY_DO_LOWER);
 | 
			
		||||
	cfg_pcu_gsmtap_categ_cmd.doc = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_help,
 | 
			
		||||
						"GSMTAP Category\n",
 | 
			
		||||
						"\n", "", 0);
 | 
			
		||||
	cfg_pcu_no_gsmtap_categ_cmd.string = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_names,
 | 
			
		||||
						"no gsmtap-category (",
 | 
			
		||||
						"|",")", VTY_DO_LOWER);
 | 
			
		||||
	cfg_pcu_no_gsmtap_categ_cmd.doc = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_help,
 | 
			
		||||
						NO_STR "GSMTAP Category\n",
 | 
			
		||||
						"\n", "", 0);
 | 
			
		||||
 | 
			
		||||
	logging_vty_add_cmds(cat);
 | 
			
		||||
	osmo_stats_vty_add_cmds(cat);
 | 
			
		||||
 | 
			
		||||
	install_node(&pcu_node, config_write_pcu);
 | 
			
		||||
	install_element(CONFIG_NODE, &cfg_pcu_cmd);
 | 
			
		||||
	vty_install_default(PCU_NODE);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_egprs_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_no_egprs_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_no_two_phase_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_cs_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_no_cs_cmd);
 | 
			
		||||
@@ -819,6 +1150,13 @@ int pcu_vty_init(const struct log_info *cat)
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_cs_downgrade_thrsh_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_no_cs_downgrade_thrsh_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_cs_lqual_ranges_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_mcs_lqual_ranges_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_mcs_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_dl_arq_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_no_mcs_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_mcs_max_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_no_mcs_max_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_window_size_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_queue_lifetime_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_queue_lifetime_inf_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd);
 | 
			
		||||
@@ -848,6 +1186,9 @@ int pcu_vty_init(const struct log_info *cat)
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_no_dl_tbf_idle_time_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_ms_idle_time_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_no_ms_idle_time_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_gsmtap_categ_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_no_gsmtap_categ_cmd);
 | 
			
		||||
	install_element(PCU_NODE, &cfg_pcu_sock_cmd);
 | 
			
		||||
 | 
			
		||||
	install_element_ve(&show_bts_stats_cmd);
 | 
			
		||||
	install_element_ve(&show_tbf_cmd);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#ifndef _PCU_VTY_H
 | 
			
		||||
#define _PCU_VTY_H
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/vty.h>
 | 
			
		||||
 | 
			
		||||
@@ -8,7 +9,7 @@ enum pcu_vty_node {
 | 
			
		||||
	PCU_NODE = _LAST_OSMOVTY_NODE + 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum node_type pcu_vty_go_parent(struct vty *vty);
 | 
			
		||||
int pcu_vty_go_parent(struct vty *vty);
 | 
			
		||||
int pcu_vty_is_config_node(struct vty *vty, int node);
 | 
			
		||||
 | 
			
		||||
int pcu_vty_init(const struct log_info *cat);
 | 
			
		||||
 
 | 
			
		||||
@@ -27,15 +27,92 @@
 | 
			
		||||
#include "gprs_ms_storage.h"
 | 
			
		||||
#include "gprs_ms.h"
 | 
			
		||||
#include "cxx_linuxlist.h"
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
#include <llc.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <rlc.h>
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#  include <osmocom/vty/command.h>
 | 
			
		||||
#  include <osmocom/vty/logging.h>
 | 
			
		||||
#  include <osmocom/vty/misc.h>
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
	#include <osmocom/core/linuxlist.h>
 | 
			
		||||
	#include <osmocom/core/utils.h>
 | 
			
		||||
	#include <osmocom/vty/vty.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int pcu_vty_config_write_pcu_ext(struct vty *vty)
 | 
			
		||||
static void tbf_print_vty_info(struct vty *vty, gprs_rlcmac_tbf *tbf)
 | 
			
		||||
{
 | 
			
		||||
	gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
 | 
			
		||||
	gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "TBF: TFI=%d TLLI=0x%08x (%s) TA=%u DIR=%s IMSI=%s%s", tbf->tfi(),
 | 
			
		||||
			tbf->tlli(), tbf->is_tlli_valid() ? "valid" : "invalid",
 | 
			
		||||
			tbf->ta(),
 | 
			
		||||
			tbf->direction == GPRS_RLCMAC_UL_TBF ? "UL" : "DL",
 | 
			
		||||
			tbf->imsi(), VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " created=%lu state=%08x [CCCH:%u, PACCH:%u] 1st_TS=%d 1st_cTS=%d ctrl_TS=%d MS_CLASS=%d/%d%s",
 | 
			
		||||
		tbf->created_ts(), tbf->state_flags,
 | 
			
		||||
		tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH),
 | 
			
		||||
		tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH),
 | 
			
		||||
		tbf->first_ts,
 | 
			
		||||
			tbf->first_common_ts, tbf->control_ts,
 | 
			
		||||
			tbf->ms_class(),
 | 
			
		||||
			tbf->ms() ? tbf->ms()->egprs_ms_class() : -1,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, " TS_alloc=");
 | 
			
		||||
	for (int i = 0; i < 8; i++) {
 | 
			
		||||
		bool is_ctrl = tbf->is_control_ts(i);
 | 
			
		||||
		if (tbf->pdch[i])
 | 
			
		||||
			vty_out(vty, "%d%s ", i, is_ctrl ? "!" : "");
 | 
			
		||||
	}
 | 
			
		||||
	if (tbf->trx != NULL)
 | 
			
		||||
		vty_out(vty, " TRX_ID=%d", tbf->trx->trx_no);
 | 
			
		||||
	vty_out(vty, " CS=%s", tbf->current_cs().name());
 | 
			
		||||
 | 
			
		||||
	if (ul_tbf) {
 | 
			
		||||
		gprs_rlc_ul_window *win = ul_tbf->window();
 | 
			
		||||
		vty_out(vty, " WS=%u V(Q)=%d V(R)=%d",
 | 
			
		||||
			ul_tbf->window_size(), win->v_q(), win->v_r());
 | 
			
		||||
		vty_out(vty, "%s", VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, " TBF Statistics:%s", VTY_NEWLINE);
 | 
			
		||||
		if(GprsCodingScheme::GPRS == tbf->ms()->mode()) {
 | 
			
		||||
			vty_out_rate_ctr_group(vty, " ", ul_tbf->m_ul_gprs_ctrs);
 | 
			
		||||
		} else {
 | 
			
		||||
			vty_out_rate_ctr_group(vty, " ", ul_tbf->m_ul_egprs_ctrs);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (dl_tbf) {
 | 
			
		||||
		gprs_rlc_dl_window *win = dl_tbf->window();
 | 
			
		||||
		vty_out(vty, " WS=%u V(A)=%d V(S)=%d nBSN=%d%s",
 | 
			
		||||
			dl_tbf->window_size(), win->v_a(), win->v_s(), win->resend_needed(),
 | 
			
		||||
			win->window_stalled() ? " STALLED" : "");
 | 
			
		||||
		vty_out(vty, "%s", VTY_NEWLINE);
 | 
			
		||||
		vty_out_rate_ctr_group(vty, " ", tbf->m_ctrs);
 | 
			
		||||
		if(GprsCodingScheme::GPRS == tbf->ms()->mode()) {
 | 
			
		||||
			vty_out_rate_ctr_group(vty, " ", dl_tbf->m_dl_gprs_ctrs);
 | 
			
		||||
		} else {
 | 
			
		||||
			vty_out_rate_ctr_group(vty, " ", dl_tbf->m_dl_egprs_ctrs);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	vty_out(vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
 | 
			
		||||
{
 | 
			
		||||
	BTS *bts = bts_data->bts;
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> *ms_iter;
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "UL TBFs%s", VTY_NEWLINE);
 | 
			
		||||
	llist_for_each(ms_iter, &bts->ul_tbfs())
 | 
			
		||||
		tbf_print_vty_info(vty, ms_iter->entry());
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "%sDL TBFs%s", VTY_NEWLINE, VTY_NEWLINE);
 | 
			
		||||
	llist_for_each(ms_iter, &bts->dl_tbfs())
 | 
			
		||||
		tbf_print_vty_info(vty, ms_iter->entry());
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -47,9 +124,16 @@ int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
 | 
			
		||||
	llist_for_each(ms_iter, &bts->ms_store().ms_list()) {
 | 
			
		||||
		GprsMs *ms = ms_iter->entry();
 | 
			
		||||
 | 
			
		||||
		vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%d, CS-DL=%d, IMSI=%s%s",
 | 
			
		||||
		vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%s, CS-DL=%s, LLC=%zd, Cl=%d, E-Cl=%d,"
 | 
			
		||||
			" TBF-UL=%s, TBF-DL=%s, IMSI=%s%s",
 | 
			
		||||
			ms->tlli(),
 | 
			
		||||
			ms->ta(), ms->current_cs_ul(), ms->current_cs_dl(),
 | 
			
		||||
			ms->ta(), ms->current_cs_ul().name(),
 | 
			
		||||
			ms->current_cs_dl().name(),
 | 
			
		||||
			ms->llc_queue()->size(),
 | 
			
		||||
			ms->ms_class(),
 | 
			
		||||
			ms->egprs_ms_class(),
 | 
			
		||||
			ms->ul_tbf() ? ms->ul_tbf()->state_name() : "NA",
 | 
			
		||||
			ms->dl_tbf() ? ms->dl_tbf()->state_name() : "NA",
 | 
			
		||||
			ms->imsi(),
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
@@ -59,15 +143,28 @@ int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
 | 
			
		||||
static int show_ms(struct vty *vty, GprsMs *ms)
 | 
			
		||||
{
 | 
			
		||||
	unsigned i;
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> *i_tbf;
 | 
			
		||||
	uint8_t slots;
 | 
			
		||||
 | 
			
		||||
	vty_out(vty, "MS TLLI=%08x, IMSI=%s%s", ms->tlli(), ms->imsi(), VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  Timing advance (TA):    %d%s", ms->ta(), VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  Coding scheme uplink:   CS-%d%s", ms->current_cs_ul(),
 | 
			
		||||
	vty_out(vty, "  Coding scheme uplink:   %s%s", ms->current_cs_ul().name(),
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  Coding scheme downlink: CS-%d%s", ms->current_cs_dl(),
 | 
			
		||||
	vty_out(vty, "  Coding scheme downlink: %s%s", ms->current_cs_dl().name(),
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  Mode:                   %s%s",
 | 
			
		||||
		GprsCodingScheme::modeName(ms->mode()), VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  MS class:               %d%s", ms->ms_class(), VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  LLC queue length:       %d%s", ms->llc_queue()->size(),
 | 
			
		||||
	vty_out(vty, "  EGPRS MS class:         %d%s", ms->egprs_ms_class(), VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  PACCH:                  ");
 | 
			
		||||
	slots = ms->current_pacch_slots();
 | 
			
		||||
	for (int i = 0; i < 8; i++)
 | 
			
		||||
		if (slots & (1 << i))
 | 
			
		||||
			vty_out(vty, "%d ", i);
 | 
			
		||||
	vty_out(vty, "%s", VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  LLC queue length:       %zd%s", ms->llc_queue()->size(),
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	vty_out(vty, "  LLC queue octets:       %zd%s", ms->llc_queue()->octets(),
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	if (ms->l1_meas()->have_rssi)
 | 
			
		||||
		vty_out(vty, "  RSSI:                   %d dBm%s",
 | 
			
		||||
@@ -98,16 +195,30 @@ static int show_ms(struct vty *vty, GprsMs *ms)
 | 
			
		||||
			vty_out(vty, "  MS I level (slot %d):    %d dB%s",
 | 
			
		||||
				i, ms->l1_meas()->ts[i].ms_i_level, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
	vty_out(vty, "  RLC/MAC DL Control Msg: %d%s", ms->dl_ctrl_msg(),
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	if (ms->ul_tbf())
 | 
			
		||||
		vty_out(vty, "  Uplink TBF:             TFI=%d, state=%s%s",
 | 
			
		||||
			ms->ul_tbf()->tfi(),
 | 
			
		||||
			ms->ul_tbf()->state_name(),
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
	if (ms->dl_tbf())
 | 
			
		||||
	if (ms->dl_tbf()) {
 | 
			
		||||
		vty_out(vty, "  Downlink TBF:           TFI=%d, state=%s%s",
 | 
			
		||||
			ms->dl_tbf()->tfi(),
 | 
			
		||||
			ms->dl_tbf()->state_name(),
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		vty_out(vty, "  Current DL Throughput:  %d Kbps %s",
 | 
			
		||||
			ms->dl_tbf()->m_bw.dl_throughput,
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	llist_for_each(i_tbf, &ms->old_tbfs())
 | 
			
		||||
		vty_out(vty, "  Old %-19s TFI=%d, state=%s%s",
 | 
			
		||||
			i_tbf->entry()->direction == GPRS_RLCMAC_UL_TBF ?
 | 
			
		||||
			"Uplink TBF:" : "Downlink TBF:",
 | 
			
		||||
			i_tbf->entry()->tfi(),
 | 
			
		||||
			i_tbf->entry()->state_name(),
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ extern "C" {
 | 
			
		||||
struct vty;
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
 | 
			
		||||
int pcu_vty_config_write_pcu_ext(struct vty *vty);
 | 
			
		||||
int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data);
 | 
			
		||||
int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data);
 | 
			
		||||
int pcu_vty_show_ms_by_tlli(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
 | 
			
		||||
	uint32_t tlli);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										963
									
								
								src/pdch.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										963
									
								
								src/pdch.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,963 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 * Copyright (C) 2018 by sysmocom - s.f.m.c. GmbH <info@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 <bts.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
#include <decoding.h>
 | 
			
		||||
#include <encoding.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
#include <gprs_ms.h>
 | 
			
		||||
#include <gprs_ms_storage.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <rlc.h>
 | 
			
		||||
#include <sba.h>
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
#include <cxx_linuxlist.h>
 | 
			
		||||
#include <gsm_rlcmac.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
	#include <osmocom/core/talloc.h>
 | 
			
		||||
	#include <osmocom/core/msgb.h>
 | 
			
		||||
	#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
	#include <osmocom/core/bitvec.h>
 | 
			
		||||
	#include <osmocom/core/gsmtap.h>
 | 
			
		||||
	#include <osmocom/core/logging.h>
 | 
			
		||||
	#include <osmocom/core/utils.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
static void get_rx_qual_meas(struct pcu_l1_meas *meas, uint8_t rx_qual_enc)
 | 
			
		||||
{
 | 
			
		||||
	static const int16_t rx_qual_map[] = {
 | 
			
		||||
		0, /* 0,14 % */
 | 
			
		||||
		0, /* 0,28 % */
 | 
			
		||||
		1, /* 0,57 % */
 | 
			
		||||
		1, /* 1,13 % */
 | 
			
		||||
		2, /* 2,26 % */
 | 
			
		||||
		5, /* 4,53 % */
 | 
			
		||||
		9, /* 9,05 % */
 | 
			
		||||
		18, /* 18,10 % */
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	meas->set_ms_rx_qual(rx_qual_map[
 | 
			
		||||
		OSMO_MIN(rx_qual_enc, ARRAY_SIZE(rx_qual_map)-1)
 | 
			
		||||
		]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_meas(struct pcu_l1_meas *meas,
 | 
			
		||||
	const Packet_Resource_Request_t *qr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned i;
 | 
			
		||||
 | 
			
		||||
	meas->set_ms_c_value(qr->C_VALUE);
 | 
			
		||||
	if (qr->Exist_SIGN_VAR)
 | 
			
		||||
		meas->set_ms_sign_var((qr->SIGN_VAR + 2) / 4); /* SIGN_VAR * 0.25 dB */
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < OSMO_MIN(ARRAY_SIZE(qr->Slot), ARRAY_SIZE(meas->ts)); i++)
 | 
			
		||||
	{
 | 
			
		||||
		if (qr->Slot[i].Exist) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_INFO,
 | 
			
		||||
				"Packet resource request: i_level[%d] = %d\n",
 | 
			
		||||
				i, qr->Slot[i].I_LEVEL);
 | 
			
		||||
			meas->set_ms_i_level(i, -2 * qr->Slot[i].I_LEVEL);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_meas(struct pcu_l1_meas *meas,
 | 
			
		||||
	const Channel_Quality_Report_t *qr)
 | 
			
		||||
{
 | 
			
		||||
	unsigned i;
 | 
			
		||||
 | 
			
		||||
	get_rx_qual_meas(meas, qr->RXQUAL);
 | 
			
		||||
	meas->set_ms_c_value(qr->C_VALUE);
 | 
			
		||||
	meas->set_ms_sign_var((qr->SIGN_VAR + 2) / 4); /* SIGN_VAR * 0.25 dB */
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < OSMO_MIN(ARRAY_SIZE(qr->Slot), ARRAY_SIZE(meas->ts)); i++)
 | 
			
		||||
	{
 | 
			
		||||
		if (qr->Slot[i].Exist) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_DEBUG,
 | 
			
		||||
				"Channel quality report: i_level[%d] = %d\n",
 | 
			
		||||
				i, qr->Slot[i].I_LEVEL_TN);
 | 
			
		||||
			meas->set_ms_i_level(i, -2 * qr->Slot[i].I_LEVEL_TN);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void sched_ul_ass_or_rej(BTS *bts, gprs_rlcmac_bts *bts_data, struct gprs_rlcmac_dl_tbf *tbf)
 | 
			
		||||
{
 | 
			
		||||
	bts->channel_request_description();
 | 
			
		||||
 | 
			
		||||
	/* This call will register the new TBF with the MS on success */
 | 
			
		||||
	gprs_rlcmac_ul_tbf *ul_tbf = tbf_alloc_ul(bts_data, tbf->trx->trx_no, tbf->ms_class(),
 | 
			
		||||
						  tbf->ms()->egprs_ms_class(), tbf->tlli(), tbf->ta(), tbf->ms());
 | 
			
		||||
 | 
			
		||||
	/* schedule uplink assignment or reject */
 | 
			
		||||
	if (ul_tbf) {
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack message, so we provide one:\n");
 | 
			
		||||
		TBF_SET_ASS_STATE_UL(tbf, GPRS_RLCMAC_UL_ASS_SEND_ASS);
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack message, so we packet access reject:\n");
 | 
			
		||||
		TBF_SET_ASS_STATE_UL(tbf, GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::enable()
 | 
			
		||||
{
 | 
			
		||||
	/* TODO: Check if there are still allocated resources.. */
 | 
			
		||||
	INIT_LLIST_HEAD(&paging_list);
 | 
			
		||||
	m_is_enabled = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::disable()
 | 
			
		||||
{
 | 
			
		||||
	/* TODO.. kick free_resources once we know the TRX/TS we are on */
 | 
			
		||||
	m_is_enabled = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::free_resources()
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_paging *pag;
 | 
			
		||||
 | 
			
		||||
	/* we are not enabled. there should be no resources */
 | 
			
		||||
	if (!is_enabled())
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* kick all TBF on slot */
 | 
			
		||||
	gprs_rlcmac_tbf::free_all(this);
 | 
			
		||||
 | 
			
		||||
	/* flush all pending paging messages */
 | 
			
		||||
	while ((pag = dequeue_paging()))
 | 
			
		||||
		talloc_free(pag);
 | 
			
		||||
 | 
			
		||||
	trx->bts->sba()->free_resources(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_paging *gprs_rlcmac_pdch::dequeue_paging()
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_paging *pag;
 | 
			
		||||
 | 
			
		||||
	if (llist_empty(&paging_list))
 | 
			
		||||
		return NULL;
 | 
			
		||||
	pag = llist_entry(paging_list.next, struct gprs_rlcmac_paging, list);
 | 
			
		||||
	llist_del(&pag->list);
 | 
			
		||||
 | 
			
		||||
	return pag;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct msgb *gprs_rlcmac_pdch::packet_paging_request()
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_paging *pag;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	unsigned wp = 0, len;
 | 
			
		||||
 | 
			
		||||
	/* no paging, no message */
 | 
			
		||||
	pag = dequeue_paging();
 | 
			
		||||
	if (!pag)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_DEBUG, "Scheduling paging\n");
 | 
			
		||||
 | 
			
		||||
	/* alloc message */
 | 
			
		||||
	msg = msgb_alloc(23, "pag ctrl block");
 | 
			
		||||
	if (!msg) {
 | 
			
		||||
		talloc_free(pag);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	bitvec *pag_vec = bitvec_alloc(23, tall_pcu_ctx);
 | 
			
		||||
	if (!pag_vec) {
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		talloc_free(pag);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	wp = Encoding::write_packet_paging_request(pag_vec);
 | 
			
		||||
 | 
			
		||||
	/* loop until message is full */
 | 
			
		||||
	while (pag) {
 | 
			
		||||
		/* try to add paging */
 | 
			
		||||
		if ((pag->identity_lv[1] & 0x07) == 4) {
 | 
			
		||||
			/* TMSI */
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_DEBUG, "- TMSI=0x%08x\n",
 | 
			
		||||
				ntohl(*((uint32_t *)(pag->identity_lv + 1))));
 | 
			
		||||
			len = 1 + 1 + 1 + 32 + 2 + 1;
 | 
			
		||||
			if (pag->identity_lv[0] != 5) {
 | 
			
		||||
				LOGP(DRLCMAC, LOGL_ERROR, "TMSI paging with "
 | 
			
		||||
					"MI != 5 octets!\n");
 | 
			
		||||
				goto continue_next;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			/* MI */
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_DEBUG, "- MI=%s\n",
 | 
			
		||||
				osmo_hexdump(pag->identity_lv + 1,
 | 
			
		||||
					pag->identity_lv[0]));
 | 
			
		||||
			len = 1 + 1 + 1 + 4 + (pag->identity_lv[0]<<3) + 2 + 1;
 | 
			
		||||
			if (pag->identity_lv[0] > 8) {
 | 
			
		||||
				LOGP(DRLCMAC, LOGL_ERROR, "Paging with "
 | 
			
		||||
					"MI > 8 octets!\n");
 | 
			
		||||
				goto continue_next;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (wp + len > 184) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_DEBUG, "- Does not fit, so schedule "
 | 
			
		||||
				"next time\n");
 | 
			
		||||
			/* put back paging record, because does not fit */
 | 
			
		||||
			llist_add_tail(&pag->list, &paging_list);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		Encoding::write_repeated_page_info(pag_vec, wp, pag->identity_lv[0],
 | 
			
		||||
			pag->identity_lv + 1, pag->chan_needed);
 | 
			
		||||
 | 
			
		||||
continue_next:
 | 
			
		||||
		talloc_free(pag);
 | 
			
		||||
		pag = dequeue_paging();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bitvec_pack(pag_vec, msgb_put(msg, 23));
 | 
			
		||||
	RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Paging Request +++++++++++++++++++++++++\n");
 | 
			
		||||
	decode_gsm_rlcmac_downlink(pag_vec, mac_control_block);
 | 
			
		||||
	LOGPC(DCSN1, LOGL_NOTICE, "\n");
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Paging Request -------------------------\n");
 | 
			
		||||
	bitvec_free(pag_vec);
 | 
			
		||||
	talloc_free(mac_control_block);
 | 
			
		||||
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool gprs_rlcmac_pdch::add_paging(uint8_t chan_needed, uint8_t *identity_lv)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_paging *pag = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_paging);
 | 
			
		||||
	if (!pag)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	pag->chan_needed = chan_needed;
 | 
			
		||||
	memcpy(pag->identity_lv, identity_lv, identity_lv[0] + 1);
 | 
			
		||||
 | 
			
		||||
	llist_add(&pag->list, &paging_list);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet, uint32_t fn)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf, *new_tbf;
 | 
			
		||||
	uint32_t tlli = packet->TLLI;
 | 
			
		||||
	GprsMs *ms = bts()->ms_by_tlli(tlli);
 | 
			
		||||
	gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
 | 
			
		||||
	tbf = bts()->ul_tbf_by_poll_fn(fn, trx_no(), ts_no);
 | 
			
		||||
	if (!tbf)
 | 
			
		||||
		tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
 | 
			
		||||
 | 
			
		||||
	if (!tbf) {
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
 | 
			
		||||
			"unknown FN=%u TLLI=0x%08x (TRX %d TS %d)\n",
 | 
			
		||||
			fn, tlli, trx_no(), ts_no);
 | 
			
		||||
		if (ms)
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
 | 
			
		||||
			     "unknown TBF corresponds to MS with IMSI %s, TA %d, "
 | 
			
		||||
			     "uTBF (TFI=%d, state=%s), dTBF (TFI=%d, state=%s)\n",
 | 
			
		||||
			     ms->imsi(), ms->ta(),
 | 
			
		||||
			     ms->ul_tbf() ? ms->ul_tbf()->tfi() : 0,
 | 
			
		||||
			     ms->ul_tbf() ? ms->ul_tbf()->state_name() : "None",
 | 
			
		||||
			     ms->dl_tbf() ? ms->dl_tbf()->tfi() : 0,
 | 
			
		||||
			     ms->dl_tbf() ? ms->dl_tbf()->state_name() : "None");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Reset N3101 counter: */
 | 
			
		||||
	tbf->n_reset(N3101);
 | 
			
		||||
 | 
			
		||||
	tbf->update_ms(tlli, GPRS_RLCMAC_UL_TBF);
 | 
			
		||||
 | 
			
		||||
	LOGPTBF(tbf, LOGL_DEBUG, "RX: [PCU <- BTS] Packet Control Ack\n");
 | 
			
		||||
	TBF_POLL_SCHED_UNSET(tbf);
 | 
			
		||||
 | 
			
		||||
	/* check if this control ack belongs to packet uplink ack */
 | 
			
		||||
	ul_tbf = as_ul_tbf(tbf);
 | 
			
		||||
	if (ul_tbf && ul_tbf->handle_ctrl_ack()) {
 | 
			
		||||
		LOGPTBF(tbf, LOGL_DEBUG, "[UPLINK] END\n");
 | 
			
		||||
		if (ul_tbf->ctrl_ack_to_toggle())
 | 
			
		||||
			LOGPTBF(tbf, LOGL_NOTICE, "Recovered uplink ack for UL\n");
 | 
			
		||||
 | 
			
		||||
		tbf_free(tbf);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_WAIT_ACK)) {
 | 
			
		||||
		LOGPTBF(tbf, LOGL_DEBUG, "[UPLINK] DOWNLINK ASSIGNED\n");
 | 
			
		||||
		/* reset N3105 */
 | 
			
		||||
		tbf->n_reset(N3105);
 | 
			
		||||
		TBF_SET_ASS_STATE_DL(tbf, GPRS_RLCMAC_DL_ASS_NONE);
 | 
			
		||||
 | 
			
		||||
		new_tbf = tbf->ms() ? tbf->ms()->dl_tbf() : NULL;
 | 
			
		||||
		if (!new_tbf) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but DL "
 | 
			
		||||
				"TBF is gone TLLI=0x%08x\n", tlli);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE) &&
 | 
			
		||||
				tbf->direction == new_tbf->direction)
 | 
			
		||||
			tbf_free(tbf);
 | 
			
		||||
 | 
			
		||||
		if (new_tbf->check_n_clear(GPRS_RLCMAC_FLAG_CCCH)) {
 | 
			
		||||
			/* We now know that the PACCH really existed */
 | 
			
		||||
			LOGPTBF(new_tbf, LOGL_INFO,
 | 
			
		||||
				"The TBF has been confirmed on the PACCH, "
 | 
			
		||||
				"changed type from CCCH to PACCH\n");
 | 
			
		||||
			TBF_ASS_TYPE_SET(new_tbf, GPRS_RLCMAC_FLAG_PACCH);
 | 
			
		||||
		}
 | 
			
		||||
		TBF_SET_STATE(new_tbf, GPRS_RLCMAC_FLOW);
 | 
			
		||||
		/* stop pending assignment timer */
 | 
			
		||||
		new_tbf->t_stop(T0, "control acked (DL-TBF)");
 | 
			
		||||
		if (new_tbf->check_n_clear(GPRS_RLCMAC_FLAG_TO_DL_ASS))
 | 
			
		||||
			LOGPTBF(new_tbf, LOGL_NOTICE, "Recovered downlink assignment\n");
 | 
			
		||||
 | 
			
		||||
		tbf_assign_control_ts(new_tbf);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_WAIT_ACK)) {
 | 
			
		||||
		LOGPTBF(tbf, LOGL_DEBUG, "[DOWNLINK] UPLINK ASSIGNED\n");
 | 
			
		||||
		/* reset N3105 */
 | 
			
		||||
		tbf->n_reset(N3105);
 | 
			
		||||
		TBF_SET_ASS_STATE_UL(tbf, GPRS_RLCMAC_UL_ASS_NONE);
 | 
			
		||||
 | 
			
		||||
		new_tbf = tbf->ms() ? tbf->ms()->ul_tbf() : NULL;
 | 
			
		||||
		if (!new_tbf) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but UL "
 | 
			
		||||
				"TBF is gone TLLI=0x%08x\n", tlli);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE) &&
 | 
			
		||||
				tbf->direction == new_tbf->direction)
 | 
			
		||||
			tbf_free(tbf);
 | 
			
		||||
 | 
			
		||||
		TBF_SET_STATE(new_tbf, GPRS_RLCMAC_FLOW);
 | 
			
		||||
		if (new_tbf->check_n_clear(GPRS_RLCMAC_FLAG_TO_UL_ASS))
 | 
			
		||||
			LOGPTBF(new_tbf, LOGL_NOTICE, "Recovered uplink assignment for UL\n");
 | 
			
		||||
 | 
			
		||||
		tbf_assign_control_ts(new_tbf);
 | 
			
		||||
		/* there might be LLC packets waiting in the queue, but the DL
 | 
			
		||||
		 * TBF might have been released while the UL TBF has been
 | 
			
		||||
		 * established */
 | 
			
		||||
		if (new_tbf->ms()->need_dl_tbf())
 | 
			
		||||
			new_tbf->establish_dl_tbf_on_pacch();
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_ERROR, "Error: received PACET CONTROL ACK "
 | 
			
		||||
		"at no request\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_nack, uint32_t fn)
 | 
			
		||||
{
 | 
			
		||||
	int8_t tfi = 0; /* must be signed */
 | 
			
		||||
	struct gprs_rlcmac_dl_tbf *tbf;
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct pcu_l1_meas meas;
 | 
			
		||||
	int num_blocks;
 | 
			
		||||
	uint8_t bits_data[RLC_GPRS_WS/8];
 | 
			
		||||
	bitvec bits;
 | 
			
		||||
	int bsn_begin, bsn_end;
 | 
			
		||||
	char show_bits[RLC_GPRS_WS + 1];
 | 
			
		||||
 | 
			
		||||
	tfi = ack_nack->DOWNLINK_TFI;
 | 
			
		||||
	tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
 | 
			
		||||
	if (!tbf) {
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
 | 
			
		||||
			"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
 | 
			
		||||
			fn, tfi, trx_no(), ts_no);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (tbf->tfi() != tfi) {
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
 | 
			
		||||
			"wrong TFI=%d, ignoring!\n", tfi);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Reset N3101 counter: */
 | 
			
		||||
	tbf->n_reset(N3101);
 | 
			
		||||
 | 
			
		||||
	if (tbf->handle_ack_nack())
 | 
			
		||||
		LOGPTBF(tbf, LOGL_NOTICE, "Recovered downlink ack\n");
 | 
			
		||||
 | 
			
		||||
	LOGPTBF(tbf, LOGL_DEBUG, "RX: [PCU <- BTS] Packet Downlink Ack/Nack\n");
 | 
			
		||||
 | 
			
		||||
	bits.data = bits_data;
 | 
			
		||||
	bits.data_len = sizeof(bits_data);
 | 
			
		||||
	bits.cur_bit = 0;
 | 
			
		||||
 | 
			
		||||
	num_blocks = Decoding::decode_gprs_acknack_bits(
 | 
			
		||||
		&ack_nack->Ack_Nack_Description, &bits,
 | 
			
		||||
		&bsn_begin, &bsn_end, tbf->window());
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_DEBUG,
 | 
			
		||||
		"Got GPRS DL ACK bitmap: SSN: %d, BSN %d to %d - 1 (%d blocks), "
 | 
			
		||||
		"\"%s\"\n",
 | 
			
		||||
		ack_nack->Ack_Nack_Description.STARTING_SEQUENCE_NUMBER,
 | 
			
		||||
		bsn_begin, bsn_end, num_blocks,
 | 
			
		||||
		(Decoding::extract_rbb(&bits, show_bits), show_bits));
 | 
			
		||||
 | 
			
		||||
	rc = tbf->rcvd_dl_ack(
 | 
			
		||||
		ack_nack->Ack_Nack_Description.FINAL_ACK_INDICATION,
 | 
			
		||||
		bsn_begin, &bits);
 | 
			
		||||
	if (rc == 1) {
 | 
			
		||||
		tbf_free(tbf);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	/* check for channel request */
 | 
			
		||||
	if (ack_nack->Exist_Channel_Request_Description)
 | 
			
		||||
		sched_ul_ass_or_rej(bts(), bts_data(), tbf);
 | 
			
		||||
 | 
			
		||||
	/* get measurements */
 | 
			
		||||
	if (tbf->ms()) {
 | 
			
		||||
		get_meas(&meas, &ack_nack->Channel_Quality_Report);
 | 
			
		||||
		tbf->ms()->update_l1_meas(&meas);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *ack_nack, uint32_t fn)
 | 
			
		||||
{
 | 
			
		||||
	int8_t tfi = 0; /* must be signed */
 | 
			
		||||
	struct gprs_rlcmac_dl_tbf *tbf;
 | 
			
		||||
	struct pcu_l1_meas meas;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int num_blocks;
 | 
			
		||||
	uint8_t bits_data[RLC_EGPRS_MAX_WS/8];
 | 
			
		||||
	char show_bits[RLC_EGPRS_MAX_WS + 1];
 | 
			
		||||
	bitvec bits;
 | 
			
		||||
	int bsn_begin, bsn_end;
 | 
			
		||||
 | 
			
		||||
	tfi = ack_nack->DOWNLINK_TFI;
 | 
			
		||||
	tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
 | 
			
		||||
	if (!tbf) {
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_NOTICE, "EGPRS PACKET DOWNLINK ACK with "
 | 
			
		||||
			"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
 | 
			
		||||
			fn, tfi, trx_no(), ts_no);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (tbf->tfi() != tfi) {
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_NOTICE, "EGPRS PACKET DOWNLINK ACK with "
 | 
			
		||||
			"wrong TFI=%d, ignoring!\n", tfi);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Reset N3101 counter: */
 | 
			
		||||
	tbf->n_reset(N3101);
 | 
			
		||||
 | 
			
		||||
	if (tbf->handle_ack_nack())
 | 
			
		||||
		LOGPTBF(tbf, LOGL_NOTICE, "Recovered EGPRS downlink ack\n");
 | 
			
		||||
 | 
			
		||||
	LOGPTBF(tbf, LOGL_DEBUG,
 | 
			
		||||
		"RX: [PCU <- BTS] EGPRS Packet Downlink Ack/Nack\n");
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_DEBUG, "EGPRS ACK/NACK: "
 | 
			
		||||
		"ut: %d, final: %d, bow: %d, eow: %d, ssn: %d, have_crbb: %d, "
 | 
			
		||||
		"urbb_len:%d, %p, %p, %d, %d, win: %d-%d, urbb: %s\n",
 | 
			
		||||
		(int)ack_nack->EGPRS_AckNack.UnionType,
 | 
			
		||||
		(int)ack_nack->EGPRS_AckNack.Desc.FINAL_ACK_INDICATION,
 | 
			
		||||
		(int)ack_nack->EGPRS_AckNack.Desc.BEGINNING_OF_WINDOW,
 | 
			
		||||
		(int)ack_nack->EGPRS_AckNack.Desc.END_OF_WINDOW,
 | 
			
		||||
		(int)ack_nack->EGPRS_AckNack.Desc.STARTING_SEQUENCE_NUMBER,
 | 
			
		||||
		(int)ack_nack->EGPRS_AckNack.Desc.Exist_CRBB,
 | 
			
		||||
		(int)ack_nack->EGPRS_AckNack.Desc.URBB_LENGTH,
 | 
			
		||||
		(void *)&ack_nack->EGPRS_AckNack.UnionType,
 | 
			
		||||
		(void *)&ack_nack->EGPRS_AckNack.Desc,
 | 
			
		||||
		(int)offsetof(EGPRS_AckNack_t, Desc),
 | 
			
		||||
		(int)offsetof(EGPRS_AckNack_w_len_t, Desc),
 | 
			
		||||
		tbf->window()->v_a(),
 | 
			
		||||
		tbf->window()->v_s(),
 | 
			
		||||
		osmo_hexdump((const uint8_t *)&ack_nack->EGPRS_AckNack.Desc.URBB,
 | 
			
		||||
			sizeof(ack_nack->EGPRS_AckNack.Desc.URBB)));
 | 
			
		||||
 | 
			
		||||
	bits.data = bits_data;
 | 
			
		||||
	bits.data_len = sizeof(bits_data);
 | 
			
		||||
	bits.cur_bit = 0;
 | 
			
		||||
 | 
			
		||||
	num_blocks = Decoding::decode_egprs_acknack_bits(
 | 
			
		||||
		&ack_nack->EGPRS_AckNack.Desc, &bits,
 | 
			
		||||
		&bsn_begin, &bsn_end, tbf->window());
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_DEBUG,
 | 
			
		||||
		"Got EGPRS DL ACK bitmap: SSN: %d, BSN %d to %d - 1 (%d blocks), "
 | 
			
		||||
		"\"%s\"\n",
 | 
			
		||||
		ack_nack->EGPRS_AckNack.Desc.STARTING_SEQUENCE_NUMBER,
 | 
			
		||||
		bsn_begin, bsn_end, num_blocks,
 | 
			
		||||
		(Decoding::extract_rbb(&bits, show_bits), show_bits)
 | 
			
		||||
	    );
 | 
			
		||||
 | 
			
		||||
	rc = tbf->rcvd_dl_ack(
 | 
			
		||||
		ack_nack->EGPRS_AckNack.Desc.FINAL_ACK_INDICATION,
 | 
			
		||||
		bsn_begin, &bits);
 | 
			
		||||
	if (rc == 1) {
 | 
			
		||||
		tbf_free(tbf);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* check for channel request */
 | 
			
		||||
	if (ack_nack->Exist_ChannelRequestDescription)
 | 
			
		||||
		sched_ul_ass_or_rej(bts(), bts_data(), tbf);
 | 
			
		||||
 | 
			
		||||
	/* get measurements */
 | 
			
		||||
	if (tbf->ms()) {
 | 
			
		||||
		/* TODO: Implement Measurements parsing for EGPRS */
 | 
			
		||||
		/*
 | 
			
		||||
		get_meas(&meas, &ack_nack->Channel_Quality_Report);
 | 
			
		||||
		tbf->ms()->update_l1_meas(&meas);
 | 
			
		||||
		*/
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request, uint32_t fn)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_sba *sba;
 | 
			
		||||
 | 
			
		||||
	if (request->ID.UnionType) {
 | 
			
		||||
		struct gprs_rlcmac_ul_tbf *ul_tbf = NULL;
 | 
			
		||||
		struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
 | 
			
		||||
		uint32_t tlli = request->ID.u.TLLI;
 | 
			
		||||
		uint8_t ms_class = 0;
 | 
			
		||||
		uint8_t egprs_ms_class = 0;
 | 
			
		||||
		uint8_t ta = GSM48_TA_INVALID;
 | 
			
		||||
		struct pcu_l1_meas meas;
 | 
			
		||||
 | 
			
		||||
		GprsMs *ms = bts()->ms_by_tlli(tlli);
 | 
			
		||||
		/* Keep the ms, even if it gets idle temporarily */
 | 
			
		||||
		GprsMs::Guard guard(ms);
 | 
			
		||||
 | 
			
		||||
		if (ms) {
 | 
			
		||||
			ul_tbf = ms->ul_tbf();
 | 
			
		||||
			dl_tbf = ms->dl_tbf();
 | 
			
		||||
			ta = ms->ta();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* We got a RACH so the MS was in packet idle mode and thus
 | 
			
		||||
		 * didn't have any active TBFs */
 | 
			
		||||
		if (ul_tbf) {
 | 
			
		||||
			LOGPTBFUL(ul_tbf, LOGL_NOTICE,
 | 
			
		||||
				  "Got RACH from TLLI=0x%08x while TBF still exists. Killing pending UL TBF\n",
 | 
			
		||||
				  tlli);
 | 
			
		||||
			tbf_free(ul_tbf);
 | 
			
		||||
			ul_tbf = NULL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (dl_tbf) {
 | 
			
		||||
			LOGPTBFUL(dl_tbf, LOGL_NOTICE,
 | 
			
		||||
				  "Got RACH from TLLI=0x%08x while TBF still exists. Release pending DL TBF\n",
 | 
			
		||||
				  tlli);
 | 
			
		||||
			tbf_free(dl_tbf);
 | 
			
		||||
			dl_tbf = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF "
 | 
			
		||||
			"in packet resource request of single "
 | 
			
		||||
			"block, so we provide one:\n");
 | 
			
		||||
		sba = bts()->sba()->find(this, fn);
 | 
			
		||||
		if (!sba) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_NOTICE, "MS requests UL TBF "
 | 
			
		||||
				"in packet resource request of single "
 | 
			
		||||
				"block, but there is no resource request "
 | 
			
		||||
				"scheduled!\n");
 | 
			
		||||
		} else {
 | 
			
		||||
			ta = sba->ta;
 | 
			
		||||
			bts()->sba()->free_sba(sba);
 | 
			
		||||
		}
 | 
			
		||||
		if (request->Exist_MS_Radio_Access_capability) {
 | 
			
		||||
			ms_class = Decoding::get_ms_class_by_capability(
 | 
			
		||||
				&request->MS_Radio_Access_capability);
 | 
			
		||||
			egprs_ms_class =
 | 
			
		||||
				Decoding::get_egprs_ms_class_by_capability(
 | 
			
		||||
					&request->MS_Radio_Access_capability);
 | 
			
		||||
		}
 | 
			
		||||
		if (!ms_class)
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_NOTICE, "MS does not give us a class.\n");
 | 
			
		||||
		if (egprs_ms_class)
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_NOTICE,
 | 
			
		||||
				"MS supports EGPRS multislot class %d.\n",
 | 
			
		||||
				egprs_ms_class);
 | 
			
		||||
		ul_tbf = tbf_alloc_ul(bts_data(), trx_no(), ms_class,
 | 
			
		||||
			egprs_ms_class, tlli, ta, ms);
 | 
			
		||||
 | 
			
		||||
		if (!ul_tbf) {
 | 
			
		||||
			handle_tbf_reject(bts_data(), ms, tlli,
 | 
			
		||||
				trx_no(), ts_no);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* set control ts to current MS's TS, until assignment complete */
 | 
			
		||||
		LOGPTBF(ul_tbf, LOGL_DEBUG, "change control TS %d -> %d until assinment is complete.\n",
 | 
			
		||||
			ul_tbf->control_ts, ts_no);
 | 
			
		||||
 | 
			
		||||
		ul_tbf->control_ts = ts_no;
 | 
			
		||||
		/* schedule uplink assignment */
 | 
			
		||||
		TBF_SET_ASS_STATE_UL(ul_tbf, GPRS_RLCMAC_UL_ASS_SEND_ASS);
 | 
			
		||||
 | 
			
		||||
		/* get capabilities */
 | 
			
		||||
		if (ul_tbf->ms())
 | 
			
		||||
			ul_tbf->ms()->set_egprs_ms_class(egprs_ms_class);
 | 
			
		||||
 | 
			
		||||
		/* get measurements */
 | 
			
		||||
		if (ul_tbf->ms()) {
 | 
			
		||||
			get_meas(&meas, request);
 | 
			
		||||
			ul_tbf->ms()->update_l1_meas(&meas);
 | 
			
		||||
		}
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (request->ID.u.Global_TFI.UnionType) {
 | 
			
		||||
		struct gprs_rlcmac_dl_tbf *dl_tbf;
 | 
			
		||||
		int8_t tfi = request->ID.u.Global_TFI.u.DOWNLINK_TFI;
 | 
			
		||||
		dl_tbf = bts()->dl_tbf_by_tfi(tfi, trx_no(), ts_no);
 | 
			
		||||
		if (!dl_tbf) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown downlink TFI=%d\n", tfi);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		LOGPTBFDL(dl_tbf, LOGL_ERROR,
 | 
			
		||||
			"RX: [PCU <- BTS] FIXME: Packet resource request\n");
 | 
			
		||||
 | 
			
		||||
		/* Reset N3101 counter: */
 | 
			
		||||
		dl_tbf->n_reset(N3101);
 | 
			
		||||
	} else {
 | 
			
		||||
		struct gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
		int8_t tfi = request->ID.u.Global_TFI.u.UPLINK_TFI;
 | 
			
		||||
		ul_tbf = bts()->ul_tbf_by_tfi(tfi, trx_no(), ts_no);
 | 
			
		||||
		if (!ul_tbf) {
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown uplink TFI=%d\n", tfi);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		LOGPTBFUL(ul_tbf, LOGL_ERROR,
 | 
			
		||||
			"RX: [PCU <- BTS] FIXME: Packet resource request\n");
 | 
			
		||||
 | 
			
		||||
		/* Reset N3101 counter: */
 | 
			
		||||
		ul_tbf->n_reset(N3101);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::rcv_measurement_report(Packet_Measurement_Report_t *report, uint32_t fn)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_sba *sba;
 | 
			
		||||
 | 
			
		||||
	sba = bts()->sba()->find(this, fn);
 | 
			
		||||
	if (!sba) {
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_NOTICE, "MS send measurement "
 | 
			
		||||
			"in packet resource request of single "
 | 
			
		||||
			"block, but there is no resource request "
 | 
			
		||||
			"scheduled! TLLI=0x%08x\n", report->TLLI);
 | 
			
		||||
	} else {
 | 
			
		||||
		GprsMs *ms = bts()->ms_store().get_ms(report->TLLI);
 | 
			
		||||
		if (!ms)
 | 
			
		||||
			LOGP(DRLCMAC, LOGL_NOTICE, "MS send measurement "
 | 
			
		||||
				"but TLLI 0x%08x is unknown\n", report->TLLI);
 | 
			
		||||
		else
 | 
			
		||||
			ms->set_ta(sba->ta);
 | 
			
		||||
 | 
			
		||||
		bts()->sba()->free_sba(sba);
 | 
			
		||||
	}
 | 
			
		||||
	gprs_rlcmac_meas_rep(report);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Received Uplink RLC control block. */
 | 
			
		||||
int gprs_rlcmac_pdch::rcv_control_block(
 | 
			
		||||
	const uint8_t *data, uint8_t data_len, bitvec *rlc_block, uint32_t fn)
 | 
			
		||||
{
 | 
			
		||||
	RlcMacUplink_t * ul_control_block = (RlcMacUplink_t *)talloc_zero(tall_pcu_ctx, RlcMacUplink_t);
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ RX : Uplink Control Block +++++++++++++++++++++++++\n");
 | 
			
		||||
	decode_gsm_rlcmac_uplink(rlc_block, ul_control_block);
 | 
			
		||||
	LOGPC(DCSN1, LOGL_NOTICE, "\n");
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- RX : Uplink Control Block -------------------------\n");
 | 
			
		||||
 | 
			
		||||
	if (ul_control_block->u.MESSAGE_TYPE == MT_PACKET_UPLINK_DUMMY_CONTROL_BLOCK)
 | 
			
		||||
		bts()->send_gsmtap(PCU_GSMTAP_C_UL_DUMMY, true, trx_no(), ts_no, GSMTAP_CHANNEL_PACCH, fn, data, data_len);
 | 
			
		||||
	else
 | 
			
		||||
		bts()->send_gsmtap(PCU_GSMTAP_C_UL_CTRL, true, trx_no(), ts_no, GSMTAP_CHANNEL_PACCH, fn, data, data_len);
 | 
			
		||||
 | 
			
		||||
	bts()->rlc_rcvd_control();
 | 
			
		||||
	switch (ul_control_block->u.MESSAGE_TYPE) {
 | 
			
		||||
	case MT_PACKET_CONTROL_ACK:
 | 
			
		||||
		rcv_control_ack(&ul_control_block->u.Packet_Control_Acknowledgement, fn);
 | 
			
		||||
		break;
 | 
			
		||||
	case MT_PACKET_DOWNLINK_ACK_NACK:
 | 
			
		||||
		rcv_control_dl_ack_nack(&ul_control_block->u.Packet_Downlink_Ack_Nack, fn);
 | 
			
		||||
		break;
 | 
			
		||||
	case MT_EGPRS_PACKET_DOWNLINK_ACK_NACK:
 | 
			
		||||
		rcv_control_egprs_dl_ack_nack(&ul_control_block->u.Egprs_Packet_Downlink_Ack_Nack, fn);
 | 
			
		||||
		break;
 | 
			
		||||
	case MT_PACKET_RESOURCE_REQUEST:
 | 
			
		||||
		rcv_resource_request(&ul_control_block->u.Packet_Resource_Request, fn);
 | 
			
		||||
		break;
 | 
			
		||||
	case MT_PACKET_MEASUREMENT_REPORT:
 | 
			
		||||
		rcv_measurement_report(&ul_control_block->u.Packet_Measurement_Report, fn);
 | 
			
		||||
		break;
 | 
			
		||||
	case MT_PACKET_UPLINK_DUMMY_CONTROL_BLOCK:
 | 
			
		||||
		/* ignoring it. change the SI to not force sending these? */
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		bts()->decode_error();
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_NOTICE,
 | 
			
		||||
			"RX: [PCU <- BTS] unknown control block(%d) received\n",
 | 
			
		||||
			ul_control_block->u.MESSAGE_TYPE);
 | 
			
		||||
	}
 | 
			
		||||
	talloc_free(ul_control_block);
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* received RLC/MAC block from L1 */
 | 
			
		||||
int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
 | 
			
		||||
	struct pcu_l1_meas *meas)
 | 
			
		||||
{
 | 
			
		||||
	GprsCodingScheme cs = GprsCodingScheme::getBySizeUL(len);
 | 
			
		||||
	if (!cs) {
 | 
			
		||||
		bts()->decode_error();
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_ERROR, "Dropping data block with invalid"
 | 
			
		||||
			"length: %d)\n", len);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bts()->rlc_ul_bytes(len);
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMACUL, LOGL_DEBUG, "Got RLC block, coding scheme: %s, "
 | 
			
		||||
		"length: %d (%d))\n", cs.name(), len, cs.usedSizeUL());
 | 
			
		||||
 | 
			
		||||
	if (cs.isGprs())
 | 
			
		||||
		return rcv_block_gprs(data, len, fn, meas, cs);
 | 
			
		||||
 | 
			
		||||
	if (cs.isEgprs())
 | 
			
		||||
		return rcv_data_block(data, len, fn, meas, cs);
 | 
			
		||||
 | 
			
		||||
	bts()->decode_error();
 | 
			
		||||
	LOGP(DRLCMACUL, LOGL_ERROR, "Unsupported coding scheme %s\n",
 | 
			
		||||
		cs.name());
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! \brief process egprs and gprs data blocks */
 | 
			
		||||
int gprs_rlcmac_pdch::rcv_data_block(uint8_t *data, uint8_t data_len, uint32_t fn,
 | 
			
		||||
	struct pcu_l1_meas *meas, GprsCodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct gprs_rlc_data_info rlc_dec;
 | 
			
		||||
	struct gprs_rlcmac_ul_tbf *tbf;
 | 
			
		||||
	unsigned len = cs.sizeUL();
 | 
			
		||||
 | 
			
		||||
	/* These are always data blocks, since EGPRS still uses CS-1 for
 | 
			
		||||
	 * control blocks (see 44.060, section 10.3, 1st par.)
 | 
			
		||||
	 */
 | 
			
		||||
	if (cs.isEgprs()) {
 | 
			
		||||
		if (!bts()->bts_data()->egprs_enabled) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_ERROR,
 | 
			
		||||
				"Got %s RLC block but EGPRS is not enabled\n",
 | 
			
		||||
				cs.name());
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		bts()->send_gsmtap(PCU_GSMTAP_C_UL_DATA_EGPRS, true, trx_no(), ts_no, GSMTAP_CHANNEL_PDTCH, fn,
 | 
			
		||||
				   data, data_len);
 | 
			
		||||
	} else {
 | 
			
		||||
		bts()->send_gsmtap(PCU_GSMTAP_C_UL_DATA_GPRS, true, trx_no(), ts_no, GSMTAP_CHANNEL_PDTCH, fn,
 | 
			
		||||
				   data, data_len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMACUL, LOGL_DEBUG, "  UL data: %s\n", osmo_hexdump(data, len));
 | 
			
		||||
 | 
			
		||||
	rc = Decoding::rlc_parse_ul_data_header(&rlc_dec, data, cs);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_ERROR,
 | 
			
		||||
			"Got %s RLC block but header parsing has failed\n",
 | 
			
		||||
			cs.name());
 | 
			
		||||
		bts()->decode_error();
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMACUL, LOGL_INFO,
 | 
			
		||||
		"Got %s RLC block: "
 | 
			
		||||
		"R=%d, SI=%d, TFI=%d, CPS=%d, RSB=%d, "
 | 
			
		||||
		"rc=%d\n",
 | 
			
		||||
		cs.name(),
 | 
			
		||||
		rlc_dec.r, rlc_dec.si, rlc_dec.tfi, rlc_dec.cps, rlc_dec.rsb,
 | 
			
		||||
		rc);
 | 
			
		||||
 | 
			
		||||
	/* find TBF inst from given TFI */
 | 
			
		||||
	tbf = ul_tbf_by_tfi(rlc_dec.tfi);
 | 
			
		||||
	if (!tbf) {
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA unknown TFI=%d\n",
 | 
			
		||||
			rlc_dec.tfi);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Reset N3101 counter: */
 | 
			
		||||
	tbf->n_reset(N3101);
 | 
			
		||||
 | 
			
		||||
	return tbf->rcv_data_block_acknowledged(&rlc_dec, data, meas);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_pdch::rcv_block_gprs(uint8_t *data, uint8_t data_len, uint32_t fn,
 | 
			
		||||
	struct pcu_l1_meas *meas, GprsCodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	unsigned payload = data[0] >> 6;
 | 
			
		||||
	bitvec *block;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	unsigned len = cs.maxBytesUL();
 | 
			
		||||
 | 
			
		||||
	switch (payload) {
 | 
			
		||||
	case GPRS_RLCMAC_DATA_BLOCK:
 | 
			
		||||
		rc = rcv_data_block(data, data_len, fn, meas, cs);
 | 
			
		||||
		break;
 | 
			
		||||
	case GPRS_RLCMAC_CONTROL_BLOCK:
 | 
			
		||||
		block = bitvec_alloc(len, tall_pcu_ctx);
 | 
			
		||||
		if (!block)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		bitvec_unpack(block, data);
 | 
			
		||||
		rc = rcv_control_block(data, data_len, block, fn);
 | 
			
		||||
		bitvec_free(block);
 | 
			
		||||
		break;
 | 
			
		||||
	case GPRS_RLCMAC_CONTROL_BLOCK_OPT:
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_NOTICE, "GPRS_RLCMAC_CONTROL_BLOCK_OPT block payload is not supported.\n");
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_NOTICE, "Unknown RLCMAC block payload(%u).\n", payload);
 | 
			
		||||
		rc = -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gprs_rlcmac_tbf *gprs_rlcmac_pdch::tbf_from_list_by_tfi(
 | 
			
		||||
		LListHead<gprs_rlcmac_tbf> *tbf_list, uint8_t tfi,
 | 
			
		||||
		enum gprs_rlcmac_tbf_direction dir)
 | 
			
		||||
{
 | 
			
		||||
	gprs_rlcmac_tbf *tbf;
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> *pos;
 | 
			
		||||
 | 
			
		||||
	llist_for_each(pos, tbf_list) {
 | 
			
		||||
		tbf = pos->entry();
 | 
			
		||||
		if (tbf->tfi() != tfi)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!tbf->pdch[ts_no])
 | 
			
		||||
			continue;
 | 
			
		||||
		return tbf;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gprs_rlcmac_ul_tbf *gprs_rlcmac_pdch::ul_tbf_by_tfi(uint8_t tfi)
 | 
			
		||||
{
 | 
			
		||||
	return as_ul_tbf(tbf_by_tfi(tfi, GPRS_RLCMAC_UL_TBF));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gprs_rlcmac_dl_tbf *gprs_rlcmac_pdch::dl_tbf_by_tfi(uint8_t tfi)
 | 
			
		||||
{
 | 
			
		||||
	return as_dl_tbf(tbf_by_tfi(tfi, GPRS_RLCMAC_DL_TBF));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* lookup TBF Entity (by TFI) */
 | 
			
		||||
gprs_rlcmac_tbf *gprs_rlcmac_pdch::tbf_by_tfi(uint8_t tfi,
 | 
			
		||||
	enum gprs_rlcmac_tbf_direction dir)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_tbf *tbf;
 | 
			
		||||
 | 
			
		||||
	if (tfi >= 32)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	tbf = m_tbfs[dir][tfi];
 | 
			
		||||
 | 
			
		||||
	if (!tbf)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)) {
 | 
			
		||||
		return tbf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::attach_tbf(gprs_rlcmac_tbf *tbf)
 | 
			
		||||
{
 | 
			
		||||
	gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
 | 
			
		||||
	if (m_tbfs[tbf->direction][tbf->tfi()])
 | 
			
		||||
		LOGP(DRLCMAC, LOGL_ERROR, "PDCH(TS %d, TRX %d): "
 | 
			
		||||
			"%s has not been detached, overwriting it\n",
 | 
			
		||||
			ts_no, trx_no(),
 | 
			
		||||
			m_tbfs[tbf->direction][tbf->tfi()]->name());
 | 
			
		||||
 | 
			
		||||
	m_num_tbfs[tbf->direction] += 1;
 | 
			
		||||
	if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
 | 
			
		||||
		ul_tbf = as_ul_tbf(tbf);
 | 
			
		||||
		m_assigned_usf |= 1 << ul_tbf->m_usf[ts_no];
 | 
			
		||||
	}
 | 
			
		||||
	m_assigned_tfi[tbf->direction] |= 1UL << tbf->tfi();
 | 
			
		||||
	m_tbfs[tbf->direction][tbf->tfi()] = tbf;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_INFO, "PDCH(TS %d, TRX %d): Attaching %s, %d TBFs, "
 | 
			
		||||
		"USFs = %02x, TFIs = %08x.\n",
 | 
			
		||||
		ts_no, trx_no(), tbf->name(), m_num_tbfs[tbf->direction],
 | 
			
		||||
		m_assigned_usf, m_assigned_tfi[tbf->direction]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::detach_tbf(gprs_rlcmac_tbf *tbf)
 | 
			
		||||
{
 | 
			
		||||
	gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(m_num_tbfs[tbf->direction] > 0);
 | 
			
		||||
 | 
			
		||||
	m_num_tbfs[tbf->direction] -= 1;
 | 
			
		||||
	if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
 | 
			
		||||
		ul_tbf = as_ul_tbf(tbf);
 | 
			
		||||
		m_assigned_usf &= ~(1 << ul_tbf->m_usf[ts_no]);
 | 
			
		||||
	}
 | 
			
		||||
	m_assigned_tfi[tbf->direction] &= ~(1UL << tbf->tfi());
 | 
			
		||||
	m_tbfs[tbf->direction][tbf->tfi()] = NULL;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_INFO, "PDCH(TS %d, TRX %d): Detaching %s, %d TBFs, "
 | 
			
		||||
		"USFs = %02x, TFIs = %08x.\n",
 | 
			
		||||
		ts_no, trx_no(), tbf->name(), m_num_tbfs[tbf->direction],
 | 
			
		||||
		m_assigned_usf, m_assigned_tfi[tbf->direction]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::reserve(enum gprs_rlcmac_tbf_direction dir)
 | 
			
		||||
{
 | 
			
		||||
	m_num_reserved[dir] += 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_pdch::unreserve(enum gprs_rlcmac_tbf_direction dir)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(m_num_reserved[dir] > 0);
 | 
			
		||||
	m_num_reserved[dir] -= 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline BTS *gprs_rlcmac_pdch::bts() const
 | 
			
		||||
{
 | 
			
		||||
	return trx->bts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t gprs_rlcmac_pdch::trx_no() const
 | 
			
		||||
{
 | 
			
		||||
	return trx->trx_no;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline gprs_rlcmac_bts *gprs_rlcmac_pdch::bts_data() const
 | 
			
		||||
{
 | 
			
		||||
	return trx->bts->bts_data();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										147
									
								
								src/pdch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/pdch.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
			
		||||
/* pdch.h
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Ivan Klyuchnikov
 | 
			
		||||
 * Copyright (C) 2013 by Holger Hans Peter Freyther
 | 
			
		||||
 * Copyright (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <gsm_rlcmac.h>
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * PDCH instance
 | 
			
		||||
 */
 | 
			
		||||
struct gprs_rlcmac_pdch {
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
	struct gprs_rlcmac_paging *dequeue_paging();
 | 
			
		||||
	struct msgb *packet_paging_request();
 | 
			
		||||
 | 
			
		||||
	bool add_paging(uint8_t chan_needed, uint8_t *identity_lv);
 | 
			
		||||
 | 
			
		||||
	void free_resources();
 | 
			
		||||
 | 
			
		||||
	bool is_enabled() const;
 | 
			
		||||
 | 
			
		||||
	void enable();
 | 
			
		||||
	void disable();
 | 
			
		||||
 | 
			
		||||
	/* dispatching of messages */
 | 
			
		||||
	int rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
 | 
			
		||||
		struct pcu_l1_meas *meas);
 | 
			
		||||
	int rcv_block_gprs(uint8_t *data, uint8_t data_len, uint32_t fn,
 | 
			
		||||
		struct pcu_l1_meas *meas, GprsCodingScheme cs);
 | 
			
		||||
	int rcv_data_block(uint8_t *data, uint8_t data_len, uint32_t fn,
 | 
			
		||||
		struct pcu_l1_meas *meas, GprsCodingScheme cs);
 | 
			
		||||
 | 
			
		||||
	gprs_rlcmac_bts *bts_data() const;
 | 
			
		||||
	BTS *bts() const;
 | 
			
		||||
	uint8_t trx_no() const;
 | 
			
		||||
 | 
			
		||||
	struct gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi);
 | 
			
		||||
	struct gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi);
 | 
			
		||||
 | 
			
		||||
	void attach_tbf(gprs_rlcmac_tbf *tbf);
 | 
			
		||||
	void detach_tbf(gprs_rlcmac_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
	unsigned num_tbfs(enum gprs_rlcmac_tbf_direction dir) const;
 | 
			
		||||
 | 
			
		||||
	void reserve(enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
	void unreserve(enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
	unsigned num_reserved(enum gprs_rlcmac_tbf_direction dir) const;
 | 
			
		||||
 | 
			
		||||
	uint8_t assigned_usf() const;
 | 
			
		||||
	uint32_t assigned_tfi(enum gprs_rlcmac_tbf_direction dir) const;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	uint8_t m_is_enabled; /* TS is enabled */
 | 
			
		||||
	uint8_t tsc; /* TSC of this slot */
 | 
			
		||||
	uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
 | 
			
		||||
	uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
 | 
			
		||||
	uint8_t next_ctrl_prio; /* next kind of ctrl message to schedule */
 | 
			
		||||
	struct llist_head paging_list; /* list of paging messages */
 | 
			
		||||
	uint32_t last_rts_fn; /* store last frame number of RTS */
 | 
			
		||||
 | 
			
		||||
	/* back pointers */
 | 
			
		||||
	struct gprs_rlcmac_trx *trx;
 | 
			
		||||
	uint8_t ts_no;
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
private:
 | 
			
		||||
	int rcv_control_block(const uint8_t *data, uint8_t data_len, bitvec *rlc_block, uint32_t fn);
 | 
			
		||||
 | 
			
		||||
	void rcv_control_ack(Packet_Control_Acknowledgement_t *, uint32_t fn);
 | 
			
		||||
	void rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *, uint32_t fn);
 | 
			
		||||
	void rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *, uint32_t fn);
 | 
			
		||||
	void rcv_resource_request(Packet_Resource_Request_t *t, uint32_t fn);
 | 
			
		||||
	void rcv_measurement_report(Packet_Measurement_Report_t *t, uint32_t fn);
 | 
			
		||||
	gprs_rlcmac_tbf *tbf_from_list_by_tfi(
 | 
			
		||||
		LListHead<gprs_rlcmac_tbf> *tbf_list, uint8_t tfi,
 | 
			
		||||
		enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
	gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi,
 | 
			
		||||
		enum gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	uint8_t m_num_tbfs[2];
 | 
			
		||||
	uint8_t m_num_reserved[2];
 | 
			
		||||
	uint8_t m_assigned_usf; /* bit set */
 | 
			
		||||
	uint32_t m_assigned_tfi[2]; /* bit set */
 | 
			
		||||
	struct gprs_rlcmac_tbf *m_tbfs[2][32];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
 | 
			
		||||
inline unsigned gprs_rlcmac_pdch::num_tbfs(enum gprs_rlcmac_tbf_direction dir) const
 | 
			
		||||
{
 | 
			
		||||
	return m_num_tbfs[dir];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline unsigned gprs_rlcmac_pdch::num_reserved(
 | 
			
		||||
	enum gprs_rlcmac_tbf_direction dir) const
 | 
			
		||||
{
 | 
			
		||||
	return gprs_rlcmac_pdch::m_num_reserved[dir];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint8_t gprs_rlcmac_pdch::assigned_usf() const
 | 
			
		||||
{
 | 
			
		||||
	return m_assigned_usf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint32_t gprs_rlcmac_pdch::assigned_tfi(
 | 
			
		||||
	enum gprs_rlcmac_tbf_direction dir) const
 | 
			
		||||
{
 | 
			
		||||
	return m_assigned_tfi[dir];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlcmac_pdch::is_enabled() const
 | 
			
		||||
{
 | 
			
		||||
	return m_is_enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* __cplusplus */
 | 
			
		||||
@@ -23,46 +23,51 @@
 | 
			
		||||
#include <poll_controller.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#include <tbf.h>
 | 
			
		||||
#include <cxx_linuxlist.h>
 | 
			
		||||
#include <sba.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PollController::PollController(BTS& bts)
 | 
			
		||||
	: m_bts(bts)
 | 
			
		||||
{}
 | 
			
		||||
 | 
			
		||||
void PollController::expireTimedout(int frame_number)
 | 
			
		||||
static inline bool elapsed_fn_check(unsigned max_delay, int frame_number, uint32_t from)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t elapsed = (frame_number + GSM_MAX_FN - from) % GSM_MAX_FN;
 | 
			
		||||
 | 
			
		||||
	if (elapsed > max_delay && elapsed < 2715400)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PollController::expireTimedout(int frame_number, unsigned max_delay)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = m_bts.bts_data();
 | 
			
		||||
	struct gprs_rlcmac_dl_tbf *dl_tbf;
 | 
			
		||||
	struct gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
	struct gprs_rlcmac_sba *sba, *sba2;
 | 
			
		||||
	struct llist_pods *lpods;
 | 
			
		||||
	uint32_t elapsed;
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> *pos;
 | 
			
		||||
 | 
			
		||||
	/* check for poll timeout
 | 
			
		||||
	 * The UL frame numbers lag 3 behind the DL frames and the data
 | 
			
		||||
	 * indication is only sent after all 4 frames of the block have been
 | 
			
		||||
	 * received. Sometimes there is an idle frame between the end of one
 | 
			
		||||
	 * and start of another frame (every 3 blocks).  So the timeout should
 | 
			
		||||
	 * definitely be there if we're more than 8 frames past poll_fn. Let's
 | 
			
		||||
	 * stay on the safe side and say 13 or more. */
 | 
			
		||||
	llist_pods_for_each_entry(ul_tbf, &bts->ul_tbfs, list, lpods) {
 | 
			
		||||
		if (ul_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
 | 
			
		||||
			elapsed = (frame_number + 2715648 - ul_tbf->poll_fn)
 | 
			
		||||
								% 2715648;
 | 
			
		||||
			if (elapsed >= 13 && elapsed < 2715400)
 | 
			
		||||
	llist_for_each(pos, &m_bts.ul_tbfs()) {
 | 
			
		||||
		ul_tbf = as_ul_tbf(pos->entry());
 | 
			
		||||
		if (ul_tbf->poll_scheduled()) {
 | 
			
		||||
			if (elapsed_fn_check(max_delay, frame_number, ul_tbf->poll_fn))
 | 
			
		||||
				ul_tbf->poll_timeout();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	llist_pods_for_each_entry(dl_tbf, &bts->dl_tbfs, list, lpods) {
 | 
			
		||||
		if (dl_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
 | 
			
		||||
			elapsed = (frame_number + 2715648 - dl_tbf->poll_fn)
 | 
			
		||||
								% 2715648;
 | 
			
		||||
			if (elapsed >= 13 && elapsed < 2715400)
 | 
			
		||||
	llist_for_each(pos, &m_bts.dl_tbfs()) {
 | 
			
		||||
		dl_tbf = as_dl_tbf(pos->entry());
 | 
			
		||||
		if (dl_tbf->poll_scheduled()) {
 | 
			
		||||
			if (elapsed_fn_check(max_delay, frame_number, dl_tbf->poll_fn))
 | 
			
		||||
				dl_tbf->poll_timeout();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	llist_for_each_entry_safe(sba, sba2, &m_bts.sba()->m_sbas, list) {
 | 
			
		||||
		elapsed = (frame_number + 2715648 - sba->fn) % 2715648;
 | 
			
		||||
		if (elapsed >= 13 && elapsed < 2715400) {
 | 
			
		||||
		if (elapsed_fn_check(max_delay, frame_number, sba->fn)) {
 | 
			
		||||
			/* sba will be freed here */
 | 
			
		||||
			m_bts.sba()->timeout(sba);
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,6 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
 | 
			
		||||
struct BTS;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -34,7 +32,8 @@ class PollController {
 | 
			
		||||
public:
 | 
			
		||||
	PollController(BTS& bts);
 | 
			
		||||
 | 
			
		||||
	void expireTimedout(int frame_number);
 | 
			
		||||
	/* check for poll timeout */
 | 
			
		||||
	void expireTimedout(int frame_number, unsigned max_delay);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	BTS& m_bts;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										364
									
								
								src/rlc.cpp
									
									
									
									
									
								
							
							
						
						
									
										364
									
								
								src/rlc.cpp
									
									
									
									
									
								
							@@ -16,12 +16,19 @@
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
#include "bts.h"
 | 
			
		||||
#include "gprs_debug.h"
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
#include <rlc.h>
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/bitvec.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -31,6 +38,9 @@ uint8_t *gprs_rlc_data::prepare(size_t block_data_len)
 | 
			
		||||
	memset(block, 0x0, sizeof(block));
 | 
			
		||||
	memset(block, 0x2b, block_data_len);
 | 
			
		||||
 | 
			
		||||
	/* Initial value of puncturing scheme */
 | 
			
		||||
	next_ps = EGPRS_PS_1;
 | 
			
		||||
 | 
			
		||||
	return block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -53,9 +63,9 @@ void gprs_rlc_dl_window::reset()
 | 
			
		||||
	m_v_b.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_rlc_dl_window::resend_needed()
 | 
			
		||||
int gprs_rlc_dl_window::resend_needed() const
 | 
			
		||||
{
 | 
			
		||||
	for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
 | 
			
		||||
	for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
 | 
			
		||||
		if (m_v_b.is_nacked(bsn) || m_v_b.is_resend(bsn))
 | 
			
		||||
			return bsn;
 | 
			
		||||
	}
 | 
			
		||||
@@ -67,7 +77,7 @@ int gprs_rlc_dl_window::mark_for_resend()
 | 
			
		||||
{
 | 
			
		||||
	int resend = 0;
 | 
			
		||||
 | 
			
		||||
	for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
 | 
			
		||||
	for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
 | 
			
		||||
		if (m_v_b.is_unacked(bsn)) {
 | 
			
		||||
			/* mark to be re-send */
 | 
			
		||||
			m_v_b.mark_resend(bsn);
 | 
			
		||||
@@ -78,12 +88,38 @@ int gprs_rlc_dl_window::mark_for_resend()
 | 
			
		||||
	return resend;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Update the receive block bitmap */
 | 
			
		||||
uint16_t gprs_rlc_ul_window::update_egprs_rbb(uint8_t *rbb)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	uint16_t bsn;
 | 
			
		||||
	uint16_t bitmask = 0x80;
 | 
			
		||||
	int8_t pos = 0;
 | 
			
		||||
	int8_t bit_pos = 0;
 | 
			
		||||
	for (i = 0, bsn = (v_q()+1); ((bsn < (v_r())) && (i < ws())); i++,
 | 
			
		||||
					bsn = this->mod_sns(bsn + 1)) {
 | 
			
		||||
		if (m_v_n.is_received(bsn)) {
 | 
			
		||||
			rbb[pos] = rbb[pos] | bitmask;
 | 
			
		||||
		} else {
 | 
			
		||||
			rbb[pos] = rbb[pos] & (~bitmask);
 | 
			
		||||
		}
 | 
			
		||||
		bitmask = bitmask >> 1;
 | 
			
		||||
		bit_pos++;
 | 
			
		||||
		bit_pos = bit_pos % 8;
 | 
			
		||||
		if (bit_pos == 0) {
 | 
			
		||||
			pos++;
 | 
			
		||||
			bitmask = 0x80;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_rlc_dl_window::count_unacked()
 | 
			
		||||
{
 | 
			
		||||
	uint16_t unacked = 0;
 | 
			
		||||
	uint16_t bsn;
 | 
			
		||||
 | 
			
		||||
	for (bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
 | 
			
		||||
	for (bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
 | 
			
		||||
		if (!m_v_b.is_acked(bsn))
 | 
			
		||||
			unacked += 1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -91,19 +127,52 @@ int gprs_rlc_dl_window::count_unacked()
 | 
			
		||||
	return unacked;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn, uint16_t mod_sns)
 | 
			
		||||
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
 | 
			
		||||
{
 | 
			
		||||
	return (ssn - 1 - bitnum) & mod_sns;
 | 
			
		||||
	return (ssn - 1 - bitnum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint8_t ssn,
 | 
			
		||||
void gprs_rlc_dl_window::update(BTS *bts, const struct bitvec *rbb,
 | 
			
		||||
			uint16_t first_bsn, uint16_t *lost,
 | 
			
		||||
			uint16_t *received)
 | 
			
		||||
{
 | 
			
		||||
	unsigned dist = distance();
 | 
			
		||||
	unsigned num_blocks = rbb->cur_bit > dist
 | 
			
		||||
				? dist : rbb->cur_bit;
 | 
			
		||||
	unsigned bsn;
 | 
			
		||||
 | 
			
		||||
	/* first_bsn is in range V(A)..V(S) */
 | 
			
		||||
 | 
			
		||||
	for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
 | 
			
		||||
		bool is_ack;
 | 
			
		||||
		bsn = mod_sns(first_bsn + bitpos);
 | 
			
		||||
		if (bsn == mod_sns(v_a() - 1))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		is_ack = bitvec_get_bit_pos(rbb, bitpos) == 1;
 | 
			
		||||
 | 
			
		||||
		if (is_ack) {
 | 
			
		||||
			LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
 | 
			
		||||
			if (!m_v_b.is_acked(bsn))
 | 
			
		||||
				*received += 1;
 | 
			
		||||
			m_v_b.mark_acked(bsn);
 | 
			
		||||
		} else {
 | 
			
		||||
			LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
 | 
			
		||||
			m_v_b.mark_nacked(bsn);
 | 
			
		||||
			bts->rlc_nacked();
 | 
			
		||||
			*lost += 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint16_t ssn,
 | 
			
		||||
			uint16_t *lost, uint16_t *received)
 | 
			
		||||
{
 | 
			
		||||
	/* SSN - 1 is in range V(A)..V(S)-1 */
 | 
			
		||||
	for (int bitpos = 0; bitpos < ws(); bitpos++) {
 | 
			
		||||
		uint16_t bsn = bitnum_to_bsn(bitpos, ssn, mod_sns());
 | 
			
		||||
		uint16_t bsn = mod_sns(bitnum_to_bsn(bitpos, ssn));
 | 
			
		||||
 | 
			
		||||
		if (bsn == ((v_a() - 1) & mod_sns()))
 | 
			
		||||
		if (bsn == mod_sns(v_a() - 1))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (show_rbb[ws() - 1 - bitpos] == 'R') {
 | 
			
		||||
@@ -126,7 +195,7 @@ int gprs_rlc_dl_window::move_window()
 | 
			
		||||
	uint16_t bsn;
 | 
			
		||||
	int moved = 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = (bsn + 1) & mod_sns()) {
 | 
			
		||||
	for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
 | 
			
		||||
		if (m_v_b.is_acked(bsn)) {
 | 
			
		||||
			m_v_b.mark_invalid(bsn);
 | 
			
		||||
			moved += 1;
 | 
			
		||||
@@ -142,7 +211,7 @@ void gprs_rlc_dl_window::show_state(char *show_v_b)
 | 
			
		||||
	int i;
 | 
			
		||||
	uint16_t bsn;
 | 
			
		||||
 | 
			
		||||
	for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = (bsn + 1) & mod_sns()) {
 | 
			
		||||
	for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
 | 
			
		||||
		uint16_t index = bsn & mod_sns_half();
 | 
			
		||||
		switch(m_v_b.get_state(index)) {
 | 
			
		||||
		case GPRS_RLC_DL_BSN_INVALID:
 | 
			
		||||
@@ -170,12 +239,30 @@ void gprs_rlc_v_n::reset()
 | 
			
		||||
		m_v_n[i] = GPRS_RLC_UL_BSN_INVALID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlc_window::set_sns(uint16_t sns)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(sns >= RLC_GPRS_SNS);
 | 
			
		||||
	OSMO_ASSERT(sns <= RLC_MAX_SNS);
 | 
			
		||||
	/* check for 2^n */
 | 
			
		||||
	OSMO_ASSERT((sns & (-sns)) == sns);
 | 
			
		||||
	m_sns = sns;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlc_window::set_ws(uint16_t ws)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_INFO, "ws(%d)\n",
 | 
			
		||||
		ws);
 | 
			
		||||
	OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
 | 
			
		||||
	OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
 | 
			
		||||
	m_ws = ws;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Update the receive block bitmap */
 | 
			
		||||
void gprs_rlc_ul_window::update_rbb(char *rbb)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i=0; i < ws(); i++) {
 | 
			
		||||
		if (m_v_n.is_received(ssn()-1-i))
 | 
			
		||||
		if (m_v_n.is_received((ssn()-1-i) & mod_sns()))
 | 
			
		||||
			rbb[ws()-1-i] = 'R';
 | 
			
		||||
		else
 | 
			
		||||
			rbb[ws()-1-i] = 'I';
 | 
			
		||||
@@ -186,7 +273,7 @@ void gprs_rlc_ul_window::update_rbb(char *rbb)
 | 
			
		||||
void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t offset_v_r;
 | 
			
		||||
	offset_v_r = (bsn + 1 - v_r()) & mod_sns();
 | 
			
		||||
	offset_v_r = mod_sns(bsn + 1 - v_r());
 | 
			
		||||
	/* Positive offset, so raise. */
 | 
			
		||||
	if (offset_v_r < (sns() >> 1)) {
 | 
			
		||||
		while (offset_v_r--) {
 | 
			
		||||
@@ -210,7 +297,7 @@ uint16_t gprs_rlc_ul_window::raise_v_q()
 | 
			
		||||
		if (!m_v_n.is_received(v_q()))
 | 
			
		||||
			break;
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising "
 | 
			
		||||
			"V(Q) to %d\n", v_q(), (v_q() + 1) & mod_sns());
 | 
			
		||||
			"V(Q) to %d\n", v_q(), mod_sns(v_q() + 1));
 | 
			
		||||
		raise_v_q(1);
 | 
			
		||||
		count += 1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -218,10 +305,251 @@ uint16_t gprs_rlc_ul_window::raise_v_q()
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
 | 
			
		||||
void gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
 | 
			
		||||
{
 | 
			
		||||
	m_v_n.mark_received(bsn);
 | 
			
		||||
	raise_v_r(bsn);
 | 
			
		||||
 | 
			
		||||
	return raise_v_q();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
 | 
			
		||||
{
 | 
			
		||||
	bool was_valid = m_v_n.is_received(bsn);
 | 
			
		||||
	m_v_n.mark_missing(bsn);
 | 
			
		||||
 | 
			
		||||
	return was_valid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	GprsCodingScheme cs, bool with_padding, unsigned int header_bits,
 | 
			
		||||
	const unsigned int spb)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	unsigned int padding_bits = with_padding ? cs.optionalPaddingBits() : 0;
 | 
			
		||||
 | 
			
		||||
	memset(rlc, 0, sizeof(*rlc));
 | 
			
		||||
 | 
			
		||||
	rlc->cs = cs;
 | 
			
		||||
	rlc->with_padding = with_padding;
 | 
			
		||||
	rlc->num_data_blocks = cs.numDataBlocks();
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(rlc->num_data_blocks <= ARRAY_SIZE(rlc->block_info));
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < rlc->num_data_blocks; i++) {
 | 
			
		||||
		gprs_rlc_data_block_info_init(&rlc->block_info[i], cs,
 | 
			
		||||
			with_padding, spb);
 | 
			
		||||
 | 
			
		||||
		rlc->data_offs_bits[i] =
 | 
			
		||||
			header_bits + padding_bits +
 | 
			
		||||
			(i+1) * cs.numDataBlockHeaderBits() +
 | 
			
		||||
			i * 8 * rlc->block_info[0].data_len;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	GprsCodingScheme cs, bool with_padding, const unsigned int spb)
 | 
			
		||||
{
 | 
			
		||||
	return gprs_rlc_data_header_init(rlc, cs, with_padding,
 | 
			
		||||
		cs.numDataHeaderBitsDL(), spb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	GprsCodingScheme cs, bool with_padding)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * last parameter is sent as 0 since common function used
 | 
			
		||||
	 * for both DL and UL
 | 
			
		||||
	 */
 | 
			
		||||
	return gprs_rlc_data_header_init(rlc, cs, with_padding,
 | 
			
		||||
		cs.numDataHeaderBitsUL(), 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
 | 
			
		||||
	GprsCodingScheme cs, bool with_padding, const unsigned int spb)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int data_len = cs.maxDataBlockBytes();
 | 
			
		||||
	if (with_padding)
 | 
			
		||||
		data_len -= cs.optionalPaddingBits() / 8;
 | 
			
		||||
 | 
			
		||||
	rdbi->data_len = data_len;
 | 
			
		||||
	rdbi->bsn = 0;
 | 
			
		||||
	rdbi->ti  = 0;
 | 
			
		||||
	rdbi->e   = 1;
 | 
			
		||||
	rdbi->cv  = 15;
 | 
			
		||||
	rdbi->pi  = 0;
 | 
			
		||||
	rdbi->spb = spb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs,
 | 
			
		||||
	enum egprs_puncturing_values punct,
 | 
			
		||||
	enum egprs_puncturing_values punct2, bool with_padding)
 | 
			
		||||
{
 | 
			
		||||
	/* validate that punct and punct2 are as expected */
 | 
			
		||||
	switch (GprsCodingScheme::Scheme(cs)) {
 | 
			
		||||
	case GprsCodingScheme::MCS9:
 | 
			
		||||
	case GprsCodingScheme::MCS8:
 | 
			
		||||
	case GprsCodingScheme::MCS7:
 | 
			
		||||
		if (punct2 == EGPRS_PS_INVALID) {
 | 
			
		||||
			LOGP(DRLCMACDL, LOGL_ERROR,
 | 
			
		||||
			     "Invalid punct2 value for coding scheme %d: %d\n",
 | 
			
		||||
			     GprsCodingScheme::Scheme(cs), punct2);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		/* fall through */
 | 
			
		||||
	case GprsCodingScheme::MCS6:
 | 
			
		||||
	case GprsCodingScheme::MCS5:
 | 
			
		||||
	case GprsCodingScheme::MCS4:
 | 
			
		||||
	case GprsCodingScheme::MCS3:
 | 
			
		||||
	case GprsCodingScheme::MCS2:
 | 
			
		||||
	case GprsCodingScheme::MCS1:
 | 
			
		||||
		if (punct == EGPRS_PS_INVALID) {
 | 
			
		||||
			LOGP(DRLCMACDL, LOGL_ERROR,
 | 
			
		||||
			     "Invalid punct value for coding scheme %d: %d\n",
 | 
			
		||||
			     GprsCodingScheme::Scheme(cs), punct);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* See 3GPP TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1 */
 | 
			
		||||
	switch (GprsCodingScheme::Scheme(cs)) {
 | 
			
		||||
	case GprsCodingScheme::MCS1: return 0b1011 +
 | 
			
		||||
		punct % EGPRS_MAX_PS_NUM_2;
 | 
			
		||||
	case GprsCodingScheme::MCS2: return 0b1001 +
 | 
			
		||||
		punct % EGPRS_MAX_PS_NUM_2;
 | 
			
		||||
	case GprsCodingScheme::MCS3: return (with_padding ? 0b0110 : 0b0011) +
 | 
			
		||||
		punct % EGPRS_MAX_PS_NUM_3;
 | 
			
		||||
	case GprsCodingScheme::MCS4: return 0b0000 +
 | 
			
		||||
		punct % EGPRS_MAX_PS_NUM_3;
 | 
			
		||||
	case GprsCodingScheme::MCS5: return  0b100 +
 | 
			
		||||
		punct % EGPRS_MAX_PS_NUM_2;
 | 
			
		||||
	case GprsCodingScheme::MCS6: return (with_padding ? 0b010 : 0b000) +
 | 
			
		||||
		punct % EGPRS_MAX_PS_NUM_2;
 | 
			
		||||
	case GprsCodingScheme::MCS7: return 0b10100 +
 | 
			
		||||
		3 * (punct % EGPRS_MAX_PS_NUM_3) +
 | 
			
		||||
		punct2 % EGPRS_MAX_PS_NUM_3;
 | 
			
		||||
	case GprsCodingScheme::MCS8: return 0b01011 +
 | 
			
		||||
		3 * (punct % EGPRS_MAX_PS_NUM_3) +
 | 
			
		||||
		punct2 % EGPRS_MAX_PS_NUM_3;
 | 
			
		||||
	case GprsCodingScheme::MCS9: return 0b00000 +
 | 
			
		||||
		4 * (punct % EGPRS_MAX_PS_NUM_3) +
 | 
			
		||||
		punct2 % EGPRS_MAX_PS_NUM_3;
 | 
			
		||||
	default: ;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlc_mcs_cps_decode(unsigned int cps,
 | 
			
		||||
	GprsCodingScheme cs, int *punct, int *punct2, int *with_padding)
 | 
			
		||||
{
 | 
			
		||||
	*punct2 = -1;
 | 
			
		||||
	*with_padding = 0;
 | 
			
		||||
 | 
			
		||||
	switch (GprsCodingScheme::Scheme(cs)) {
 | 
			
		||||
	case GprsCodingScheme::MCS1:
 | 
			
		||||
		cps -= 0b1011; *punct = cps % 2; break;
 | 
			
		||||
	case GprsCodingScheme::MCS2:
 | 
			
		||||
		cps -= 0b1001; *punct = cps % 2; break;
 | 
			
		||||
	case GprsCodingScheme::MCS3:
 | 
			
		||||
		cps -= 0b0011; *punct = cps % 3; *with_padding = cps >= 3; break;
 | 
			
		||||
	case GprsCodingScheme::MCS4:
 | 
			
		||||
		cps -= 0b0000; *punct = cps % 3; break;
 | 
			
		||||
	case GprsCodingScheme::MCS5:
 | 
			
		||||
		cps -= 0b100; *punct = cps % 2; break;
 | 
			
		||||
	case GprsCodingScheme::MCS6:
 | 
			
		||||
		cps -= 0b000; *punct = cps % 2; *with_padding = cps >= 2; break;
 | 
			
		||||
	case GprsCodingScheme::MCS7:
 | 
			
		||||
		cps -= 0b10100; *punct = cps / 3; *punct2 = cps % 3; break;
 | 
			
		||||
	case GprsCodingScheme::MCS8:
 | 
			
		||||
		cps -= 0b01011; *punct = cps / 3; *punct2 = cps % 3; break;
 | 
			
		||||
	case GprsCodingScheme::MCS9:
 | 
			
		||||
		cps -= 0b00000; *punct = cps / 4; *punct2 = cps % 3; break;
 | 
			
		||||
	default: ;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Finds the PS value for retransmission with MCS change,
 | 
			
		||||
 * retransmission with no MCS change, fresh transmission cases.
 | 
			
		||||
 * The return value shall be used for current transmission only
 | 
			
		||||
 * 44.060 9.3.2.1 defines the PS selection for MCS change case
 | 
			
		||||
 * cs_current is the output of MCS selection algorithm for retx
 | 
			
		||||
 * cs is coding scheme of previous transmission of RLC data block
 | 
			
		||||
 */
 | 
			
		||||
enum egprs_puncturing_values gprs_get_punct_scheme(
 | 
			
		||||
	enum egprs_puncturing_values punct,
 | 
			
		||||
	const GprsCodingScheme &cs,
 | 
			
		||||
	const GprsCodingScheme &cs_current,
 | 
			
		||||
	const enum egprs_rlcmac_dl_spb spb)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * 10.4.8b of TS 44.060
 | 
			
		||||
	 * If it is second segment of the block
 | 
			
		||||
	 * dont change the puncturing scheme
 | 
			
		||||
	 */
 | 
			
		||||
	if (spb == EGPRS_RLCMAC_DL_SEC_SEG)
 | 
			
		||||
		return punct;
 | 
			
		||||
 | 
			
		||||
	/* TS  44.060 9.3.2.1.1 */
 | 
			
		||||
	if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS9) &&
 | 
			
		||||
	(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS6)) {
 | 
			
		||||
		if ((punct == EGPRS_PS_1) || (punct == EGPRS_PS_3))
 | 
			
		||||
			return EGPRS_PS_1;
 | 
			
		||||
		else if (punct == EGPRS_PS_2)
 | 
			
		||||
			return EGPRS_PS_2;
 | 
			
		||||
	} else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS6) &&
 | 
			
		||||
	(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS9)) {
 | 
			
		||||
		if (punct == EGPRS_PS_1)
 | 
			
		||||
			return EGPRS_PS_3;
 | 
			
		||||
		else if (punct == EGPRS_PS_2)
 | 
			
		||||
			return EGPRS_PS_2;
 | 
			
		||||
	} else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS7) &&
 | 
			
		||||
	(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS5))
 | 
			
		||||
		return EGPRS_PS_1;
 | 
			
		||||
	else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS5) &&
 | 
			
		||||
	(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS7))
 | 
			
		||||
		return EGPRS_PS_2;
 | 
			
		||||
	else if (cs != cs_current)
 | 
			
		||||
		return EGPRS_PS_1;
 | 
			
		||||
	/* TS  44.060 9.3.2.1.1 ends here */
 | 
			
		||||
	/*
 | 
			
		||||
	 * Below else will handle fresh transmission, retransmission with no
 | 
			
		||||
	 * MCS change case
 | 
			
		||||
	 */
 | 
			
		||||
	else
 | 
			
		||||
		return punct;
 | 
			
		||||
	return EGPRS_PS_INVALID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This function calculates puncturing scheme for retransmission of a RLC
 | 
			
		||||
 * block with same MCS. The computed value shall be used for next transmission
 | 
			
		||||
 * of the same RLC block
 | 
			
		||||
 * TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
 | 
			
		||||
 */
 | 
			
		||||
void gprs_update_punct_scheme(enum egprs_puncturing_values *punct,
 | 
			
		||||
	const GprsCodingScheme &cs)
 | 
			
		||||
{
 | 
			
		||||
	switch (GprsCodingScheme::Scheme(cs)) {
 | 
			
		||||
	case GprsCodingScheme::MCS1 :
 | 
			
		||||
	case GprsCodingScheme::MCS2 :
 | 
			
		||||
	case GprsCodingScheme::MCS5 :
 | 
			
		||||
	case GprsCodingScheme::MCS6 :
 | 
			
		||||
		*punct = ((enum egprs_puncturing_values)((*punct + 1) %
 | 
			
		||||
			EGPRS_MAX_PS_NUM_2));
 | 
			
		||||
		break;
 | 
			
		||||
	case GprsCodingScheme::MCS3 :
 | 
			
		||||
	case GprsCodingScheme::MCS4 :
 | 
			
		||||
	case GprsCodingScheme::MCS7 :
 | 
			
		||||
	case GprsCodingScheme::MCS8 :
 | 
			
		||||
	case GprsCodingScheme::MCS9 :
 | 
			
		||||
		*punct = ((enum egprs_puncturing_values)((*punct + 1) %
 | 
			
		||||
			EGPRS_MAX_PS_NUM_3));
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										310
									
								
								src/rlc.h
									
									
									
									
									
								
							
							
						
						
									
										310
									
								
								src/rlc.h
									
									
									
									
									
								
							@@ -19,14 +19,23 @@
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "gprs_coding_scheme.h"
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/endian.h>
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#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 RLC_GPRS_SNS 128 /* GPRS, must be power of 2 */
 | 
			
		||||
#define RLC_GPRS_WS  64 /* max window size */
 | 
			
		||||
#define RLC_EGPRS_MIN_WS 64 /* min window size */
 | 
			
		||||
#define RLC_EGPRS_MAX_WS 1024 /* min window size */
 | 
			
		||||
#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
 | 
			
		||||
#define RLC_EGPRS_MAX_BSN_DELTA 512
 | 
			
		||||
#define RLC_MAX_SNS  RLC_EGPRS_SNS
 | 
			
		||||
#define RLC_MAX_WS   RLC_EGPRS_MAX_WS
 | 
			
		||||
#define RLC_MAX_LEN 74 /* MCS-9 data unit */
 | 
			
		||||
 | 
			
		||||
struct BTS;
 | 
			
		||||
struct gprs_rlc_v_n;
 | 
			
		||||
 | 
			
		||||
/* The state of a BSN in the send/receive window */
 | 
			
		||||
enum gprs_rlc_ul_bsn_state {
 | 
			
		||||
@@ -45,24 +54,179 @@ enum gprs_rlc_dl_bsn_state {
 | 
			
		||||
	GPRS_RLC_DL_BSN_MAX,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * EGPRS resegment status information for UL
 | 
			
		||||
 * When only first split block is received bsn state
 | 
			
		||||
 * will be set to EGPRS_RESEG_FIRST_SEG_RXD and when
 | 
			
		||||
 * only second segment is received the state will be
 | 
			
		||||
 * set to EGPRS_RESEG_SECOND_SEG_RXD. When both Split
 | 
			
		||||
 * blocks are received the state will be set to
 | 
			
		||||
 * EGPRS_RESEG_DEFAULT
 | 
			
		||||
 * The EGPRS resegmentation feature allows MS to retransmit
 | 
			
		||||
 * RLC blocks of HeaderType1, HeaderType2 by segmenting
 | 
			
		||||
 * them to 2 HeaderType3 blocks(Example MCS5 will be
 | 
			
		||||
 * retransmitted as 2 MCS2 blocks). Table 10.4.8b.1 of 44.060
 | 
			
		||||
 * explains the possible values of SPB in HeadrType3 for UL
 | 
			
		||||
 * direction. When the MCS is changed at the PCU, PCU directs the
 | 
			
		||||
 * changed MCS to MS by PUAN or UPLINK ASSIGNMENT message along
 | 
			
		||||
 * with RESEGMENT flag, Then MS may decide to retransmit the
 | 
			
		||||
 * blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
 | 
			
		||||
 * The retransmission MCS is calculated based on current MCS of
 | 
			
		||||
 * the Block and demanded MCS by PCU. Section 10.3a.4.3 of 44.060
 | 
			
		||||
 * shows the HeadrType3 with SPB field present in it
 | 
			
		||||
*/
 | 
			
		||||
enum egprs_rlc_ul_reseg_bsn_state {
 | 
			
		||||
	EGPRS_RESEG_DEFAULT = 0,
 | 
			
		||||
	EGPRS_RESEG_FIRST_SEG_RXD = 0x01,
 | 
			
		||||
	EGPRS_RESEG_SECOND_SEG_RXD = 0x02,
 | 
			
		||||
	EGPRS_RESEG_INVALID = 0x04
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * EGPRS resegment status information for DL
 | 
			
		||||
 * When only first segment is sent, bsn state
 | 
			
		||||
 * will be set to EGPRS_RESEG_FIRST_SEG_SENT and when
 | 
			
		||||
 * second segment is sent the state will be
 | 
			
		||||
 * set to EGPRS_RESEG_SECOND_SEG_SENT.
 | 
			
		||||
 * EGPRS_RESEG_DL_INVALID is set to 8 considering there is a scope for
 | 
			
		||||
 * 3rd segment according to Table 10.4.8b.2 of 44.060
 | 
			
		||||
 * The EGPRS resegmentation feature allows PCU to retransmit
 | 
			
		||||
 * RLC blocks of HeaderType1, HeaderType2 by segmenting
 | 
			
		||||
 * them to 2 HeaderType3 blocks(Example MCS5 will be
 | 
			
		||||
 * retransmitted as 2 MCS2 blocks). Table 10.4.8b.2 of 44.060
 | 
			
		||||
 * explains the possible values of SPB in HeadrType3 for DL
 | 
			
		||||
 * direction.The PCU decides to retransmit the
 | 
			
		||||
 * blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
 | 
			
		||||
 * The retransmission MCS is calculated based on current MCS of
 | 
			
		||||
 * the Block and demanded MCS by PCU. Section 10.3a.3.3 of 44.060
 | 
			
		||||
 * shows the HeadrType3 with SPB field present in it
 | 
			
		||||
 */
 | 
			
		||||
enum egprs_rlc_dl_reseg_bsn_state {
 | 
			
		||||
	EGPRS_RESEG_DL_DEFAULT = 0,
 | 
			
		||||
	EGPRS_RESEG_FIRST_SEG_SENT = 0x01,
 | 
			
		||||
	EGPRS_RESEG_SECOND_SEG_SENT = 0x02,
 | 
			
		||||
	EGPRS_RESEG_DL_INVALID = 0x08
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Table 10.4.8b.2 of 44.060 */
 | 
			
		||||
enum egprs_rlcmac_dl_spb {
 | 
			
		||||
	EGPRS_RLCMAC_DL_NO_RETX = 0,
 | 
			
		||||
	EGPRS_RLCMAC_DL_FIRST_SEG = 2,
 | 
			
		||||
	EGPRS_RLCMAC_DL_SEC_SEG = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Valid puncturing scheme values
 | 
			
		||||
 * TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
 | 
			
		||||
 */
 | 
			
		||||
enum egprs_puncturing_values {
 | 
			
		||||
	EGPRS_PS_1,
 | 
			
		||||
	EGPRS_PS_2,
 | 
			
		||||
	EGPRS_PS_3,
 | 
			
		||||
	EGPRS_PS_INVALID,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * EGPRS_MAX_PS_NUM_2 is valid for MCS 1,2,5,6.
 | 
			
		||||
 * And EGPRS_MAX_PS_NUM_3 is valid for MCS 3,4,7,8,9
 | 
			
		||||
 * TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
 | 
			
		||||
 */
 | 
			
		||||
enum egprs_puncturing_types {
 | 
			
		||||
	EGPRS_MAX_PS_NUM_2 = 2,
 | 
			
		||||
	EGPRS_MAX_PS_NUM_3,
 | 
			
		||||
	EGPRS_MAX_PS_NUM_INVALID,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline uint16_t mod_sns_half()
 | 
			
		||||
{
 | 
			
		||||
	return (RLC_MAX_SNS / 2) - 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gprs_rlc_data_block_info {
 | 
			
		||||
	unsigned int data_len; /* EGPRS: N2, GPRS: N2-2, N-2 */
 | 
			
		||||
	unsigned int bsn;
 | 
			
		||||
	unsigned int ti;
 | 
			
		||||
	unsigned int e;
 | 
			
		||||
	unsigned int cv; /* FBI == 1 <=> CV == 0 */
 | 
			
		||||
	unsigned int pi;
 | 
			
		||||
	unsigned int spb;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlc_data_info {
 | 
			
		||||
	GprsCodingScheme cs;
 | 
			
		||||
	unsigned int r;
 | 
			
		||||
	unsigned int si;
 | 
			
		||||
	unsigned int tfi;
 | 
			
		||||
	unsigned int cps;
 | 
			
		||||
	unsigned int rsb;
 | 
			
		||||
	unsigned int usf;
 | 
			
		||||
	unsigned int es_p;
 | 
			
		||||
	unsigned int rrbp;
 | 
			
		||||
	unsigned int pr;
 | 
			
		||||
	uint8_t num_data_blocks; /* this can actually be only 0, 1, 2: enforced in gprs_rlc_data_header_init() */
 | 
			
		||||
	unsigned int with_padding;
 | 
			
		||||
	unsigned int data_offs_bits[2];
 | 
			
		||||
	struct gprs_rlc_data_block_info block_info[2];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* holds the current status of the block w.r.t UL/DL split blocks */
 | 
			
		||||
union split_block_status {
 | 
			
		||||
	egprs_rlc_ul_reseg_bsn_state block_status_ul;
 | 
			
		||||
	egprs_rlc_dl_reseg_bsn_state block_status_dl;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlc_data {
 | 
			
		||||
	uint8_t *prepare(size_t block_data_length);
 | 
			
		||||
	void put_data(const uint8_t *data, size_t len);
 | 
			
		||||
 | 
			
		||||
	/* block history */
 | 
			
		||||
	/* block data including LI headers */
 | 
			
		||||
	uint8_t block[RLC_MAX_LEN];
 | 
			
		||||
	/* block len of history */
 | 
			
		||||
	/* block data len including LI headers*/
 | 
			
		||||
	uint8_t len;
 | 
			
		||||
 | 
			
		||||
	uint8_t cs;
 | 
			
		||||
	struct gprs_rlc_data_block_info block_info;
 | 
			
		||||
	/*
 | 
			
		||||
	 * cs_current_trans is variable to hold the cs_last value for
 | 
			
		||||
	 * current transmission. cs_current_trans is same as cs_last during
 | 
			
		||||
	 * transmission case. during retransmission cs_current_trans is
 | 
			
		||||
	 * fetched from egprs_mcs_retx_tbl table based on
 | 
			
		||||
	 * cs and demanded cs.reference is 44.060 Table
 | 
			
		||||
	 * 8.1.1.1 and Table 8.1.1.2
 | 
			
		||||
	 * For UL. cs_last shall be used everywhere.
 | 
			
		||||
	 */
 | 
			
		||||
	GprsCodingScheme cs_current_trans;
 | 
			
		||||
	GprsCodingScheme cs_last;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The MCS of initial transmission of a BSN
 | 
			
		||||
	 * This variable is used for split block
 | 
			
		||||
	 * processing in DL
 | 
			
		||||
	 */
 | 
			
		||||
	GprsCodingScheme cs_init;
 | 
			
		||||
 | 
			
		||||
	/* puncturing scheme value to be used for next transmission*/
 | 
			
		||||
	enum egprs_puncturing_values next_ps;
 | 
			
		||||
 | 
			
		||||
	/* holds the status of the block w.r.t UL/DL split blocks*/
 | 
			
		||||
	union split_block_status spb_status;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	GprsCodingScheme cs, bool with_padding, const unsigned int spb);
 | 
			
		||||
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	GprsCodingScheme cs, bool with_padding);
 | 
			
		||||
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
 | 
			
		||||
	GprsCodingScheme cs, bool with_padding, const unsigned int spb);
 | 
			
		||||
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, enum egprs_puncturing_values
 | 
			
		||||
	punct, enum egprs_puncturing_values punct2, bool with_padding);
 | 
			
		||||
void gprs_rlc_mcs_cps_decode(unsigned int cps, GprsCodingScheme cs,
 | 
			
		||||
	int *punct, int *punct2, int *with_padding);
 | 
			
		||||
enum egprs_puncturing_values gprs_get_punct_scheme(enum egprs_puncturing_values
 | 
			
		||||
	punct, const GprsCodingScheme &cs,
 | 
			
		||||
	const GprsCodingScheme &cs_current_trans,
 | 
			
		||||
	const enum egprs_rlcmac_dl_spb spb);
 | 
			
		||||
void gprs_update_punct_scheme(enum egprs_puncturing_values *punct,
 | 
			
		||||
	const GprsCodingScheme &cs);
 | 
			
		||||
/*
 | 
			
		||||
 * I hold the currently transferred blocks and will provide
 | 
			
		||||
 * the routines to manipulate these arrays.
 | 
			
		||||
@@ -104,18 +268,28 @@ private:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * TODO: The UL/DL code could/should share a baseclass but
 | 
			
		||||
 * we are using llist_for_each_entry for the TBF which
 | 
			
		||||
 * requires everything which creates a requirement for a POD
 | 
			
		||||
 * type and in < C++11 something that is using even if the
 | 
			
		||||
 * most simple form of inheritance is not a POD anymore.
 | 
			
		||||
 * TODO: The UL/DL code could/should share a base class.
 | 
			
		||||
 */
 | 
			
		||||
struct gprs_rlc_dl_window {
 | 
			
		||||
	void reset();
 | 
			
		||||
class gprs_rlc_window {
 | 
			
		||||
public:
 | 
			
		||||
	gprs_rlc_window();
 | 
			
		||||
 | 
			
		||||
	const uint16_t mod_sns() const;
 | 
			
		||||
	const uint16_t mod_sns(uint16_t bsn) const;
 | 
			
		||||
	const uint16_t sns() const;
 | 
			
		||||
	const uint16_t ws() const;
 | 
			
		||||
 | 
			
		||||
	void set_sns(uint16_t sns);
 | 
			
		||||
	void set_ws(uint16_t ws);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	uint16_t m_sns;
 | 
			
		||||
	uint16_t m_ws;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlc_dl_window: public gprs_rlc_window {
 | 
			
		||||
	void reset();
 | 
			
		||||
 | 
			
		||||
	bool window_stalled() const;
 | 
			
		||||
	bool window_empty() const;
 | 
			
		||||
 | 
			
		||||
@@ -125,13 +299,16 @@ struct gprs_rlc_dl_window {
 | 
			
		||||
	const uint16_t v_s() const;
 | 
			
		||||
	const uint16_t v_s_mod(int offset) const;
 | 
			
		||||
	const uint16_t v_a() const;
 | 
			
		||||
	const int16_t distance() const;
 | 
			
		||||
	const uint16_t distance() const;
 | 
			
		||||
 | 
			
		||||
	/* Methods to manage reception */
 | 
			
		||||
	int resend_needed();
 | 
			
		||||
	int resend_needed() const;
 | 
			
		||||
	int mark_for_resend();
 | 
			
		||||
	void update(BTS *bts, char *show_rbb, uint8_t ssn,
 | 
			
		||||
	void update(BTS *bts, char *show_rbb, uint16_t ssn,
 | 
			
		||||
			uint16_t *lost, uint16_t *received);
 | 
			
		||||
	void update(BTS *bts, const struct bitvec *rbb,
 | 
			
		||||
			uint16_t first_bsn, uint16_t *lost,
 | 
			
		||||
			uint16_t *received);
 | 
			
		||||
	int move_window();
 | 
			
		||||
	void show_state(char *show_rbb);
 | 
			
		||||
	int count_unacked();
 | 
			
		||||
@@ -140,6 +317,8 @@ struct gprs_rlc_dl_window {
 | 
			
		||||
	uint16_t m_v_a;	/* ack state */
 | 
			
		||||
 | 
			
		||||
	gprs_rlc_v_b m_v_b;
 | 
			
		||||
 | 
			
		||||
	gprs_rlc_dl_window();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlc_v_n {
 | 
			
		||||
@@ -157,35 +336,41 @@ private:
 | 
			
		||||
	gprs_rlc_ul_bsn_state m_v_n[RLC_MAX_SNS/2]; /* receive state array */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlc_ul_window {
 | 
			
		||||
	const uint16_t mod_sns() const;
 | 
			
		||||
	const uint16_t sns() const;
 | 
			
		||||
	const uint16_t ws() const;
 | 
			
		||||
 | 
			
		||||
struct gprs_rlc_ul_window: public gprs_rlc_window {
 | 
			
		||||
	const uint16_t v_r() const;
 | 
			
		||||
	const uint16_t v_q() const;
 | 
			
		||||
 | 
			
		||||
	const void set_v_r(int);
 | 
			
		||||
	const void set_v_q(int);
 | 
			
		||||
	void reset_state();
 | 
			
		||||
 | 
			
		||||
	const uint16_t ssn() const;
 | 
			
		||||
 | 
			
		||||
	bool is_in_window(uint8_t bsn) const;
 | 
			
		||||
	bool is_in_window(uint16_t bsn) const;
 | 
			
		||||
	bool is_received(uint16_t bsn) const;
 | 
			
		||||
 | 
			
		||||
	void update_rbb(char *rbb);
 | 
			
		||||
	uint16_t update_egprs_rbb(uint8_t *rbb);
 | 
			
		||||
	void raise_v_r_to(int moves);
 | 
			
		||||
	void raise_v_r(const uint16_t bsn);
 | 
			
		||||
	uint16_t raise_v_q();
 | 
			
		||||
 | 
			
		||||
	void raise_v_q(int);
 | 
			
		||||
 | 
			
		||||
	uint16_t receive_bsn(const uint16_t bsn);
 | 
			
		||||
	void receive_bsn(const uint16_t bsn);
 | 
			
		||||
	bool invalidate_bsn(const uint16_t bsn);
 | 
			
		||||
 | 
			
		||||
	uint16_t m_v_r;	/* receive state */
 | 
			
		||||
	uint16_t m_v_q;	/* receive window state */
 | 
			
		||||
 | 
			
		||||
	gprs_rlc_v_n m_v_n;
 | 
			
		||||
 | 
			
		||||
	gprs_rlc_ul_window();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
/* TS 04.60  10.2.2 */
 | 
			
		||||
#if OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
struct rlc_ul_header {
 | 
			
		||||
	uint8_t	r:1,
 | 
			
		||||
		 si:1,
 | 
			
		||||
@@ -216,6 +401,14 @@ struct rlc_li_field {
 | 
			
		||||
		 m:1,
 | 
			
		||||
		 li:6;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct rlc_li_field_egprs {
 | 
			
		||||
	uint8_t	e:1,
 | 
			
		||||
		 li:7;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
#else
 | 
			
		||||
#  error "Only little endian headers are supported yet. TODO: add missing structs"
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlc_v_b::is_state(int bsn, const gprs_rlc_dl_bsn_state type) const
 | 
			
		||||
@@ -283,21 +476,39 @@ inline void gprs_rlc_v_b::mark_invalid(int bsn)
 | 
			
		||||
	return mark(bsn, GPRS_RLC_DL_BSN_INVALID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_dl_window::sns() const
 | 
			
		||||
inline gprs_rlc_window::gprs_rlc_window()
 | 
			
		||||
	: m_sns(RLC_GPRS_SNS)
 | 
			
		||||
	, m_ws(RLC_GPRS_WS)
 | 
			
		||||
{
 | 
			
		||||
	return RLC_MAX_SNS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_dl_window::ws() const
 | 
			
		||||
inline const uint16_t gprs_rlc_window::sns() const
 | 
			
		||||
{
 | 
			
		||||
	return RLC_MAX_WS;
 | 
			
		||||
	return m_sns;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_dl_window::mod_sns() const
 | 
			
		||||
inline const uint16_t gprs_rlc_window::ws() const
 | 
			
		||||
{
 | 
			
		||||
	return m_ws;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_window::mod_sns() const
 | 
			
		||||
{
 | 
			
		||||
	return sns() - 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_window::mod_sns(uint16_t bsn) const
 | 
			
		||||
{
 | 
			
		||||
	return bsn & mod_sns();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline gprs_rlc_dl_window::gprs_rlc_dl_window()
 | 
			
		||||
	: m_v_s(0)
 | 
			
		||||
	, m_v_a(0)
 | 
			
		||||
{
 | 
			
		||||
	reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_dl_window::v_s() const
 | 
			
		||||
{
 | 
			
		||||
	return m_v_s;
 | 
			
		||||
@@ -305,7 +516,7 @@ inline const uint16_t gprs_rlc_dl_window::v_s() const
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
 | 
			
		||||
{
 | 
			
		||||
	return (m_v_s + offset) & mod_sns();
 | 
			
		||||
	return mod_sns(m_v_s + offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_dl_window::v_a() const
 | 
			
		||||
@@ -315,7 +526,7 @@ inline const uint16_t gprs_rlc_dl_window::v_a() const
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlc_dl_window::window_stalled() const
 | 
			
		||||
{
 | 
			
		||||
	return ((m_v_s - m_v_a) & mod_sns()) == ws();
 | 
			
		||||
	return (mod_sns(m_v_s - m_v_a)) == ws();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlc_dl_window::window_empty() const
 | 
			
		||||
@@ -333,12 +544,19 @@ inline void gprs_rlc_dl_window::raise(int moves)
 | 
			
		||||
	m_v_a = (m_v_a + moves) & mod_sns();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const int16_t gprs_rlc_dl_window::distance() const
 | 
			
		||||
inline const uint16_t gprs_rlc_dl_window::distance() const
 | 
			
		||||
{
 | 
			
		||||
	return (m_v_s - m_v_a) & mod_sns();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlc_ul_window::is_in_window(uint8_t bsn) const
 | 
			
		||||
inline gprs_rlc_ul_window::gprs_rlc_ul_window()
 | 
			
		||||
	: m_v_r(0)
 | 
			
		||||
	, m_v_q(0)
 | 
			
		||||
{
 | 
			
		||||
	m_v_n.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlc_ul_window::is_in_window(uint16_t bsn) const
 | 
			
		||||
{
 | 
			
		||||
	uint16_t offset_v_q;
 | 
			
		||||
 | 
			
		||||
@@ -349,19 +567,29 @@ inline bool gprs_rlc_ul_window::is_in_window(uint8_t bsn) const
 | 
			
		||||
	return offset_v_q < ws();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_ul_window::sns() const
 | 
			
		||||
inline bool gprs_rlc_ul_window::is_received(uint16_t bsn) const
 | 
			
		||||
{
 | 
			
		||||
	return RLC_MAX_SNS;
 | 
			
		||||
	uint16_t offset_v_r;
 | 
			
		||||
 | 
			
		||||
	/* Offset to the end of the received window */
 | 
			
		||||
	offset_v_r = (m_v_r - 1 - bsn) & mod_sns();
 | 
			
		||||
	return is_in_window(bsn) && m_v_n.is_received(bsn) && offset_v_r < ws();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_ul_window::ws() const
 | 
			
		||||
inline void gprs_rlc_ul_window::reset_state()
 | 
			
		||||
{
 | 
			
		||||
	return RLC_MAX_WS;
 | 
			
		||||
	m_v_r = 0;
 | 
			
		||||
	m_v_q = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_ul_window::mod_sns() const
 | 
			
		||||
inline const void gprs_rlc_ul_window::set_v_r(int v_r)
 | 
			
		||||
{
 | 
			
		||||
	return sns() - 1;
 | 
			
		||||
	m_v_r = v_r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const void gprs_rlc_ul_window::set_v_q(int v_q)
 | 
			
		||||
{
 | 
			
		||||
	m_v_q = v_q;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const uint16_t gprs_rlc_ul_window::v_r() const
 | 
			
		||||
@@ -381,12 +609,12 @@ inline const uint16_t gprs_rlc_ul_window::ssn() const
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlc_ul_window::raise_v_r_to(int moves)
 | 
			
		||||
{
 | 
			
		||||
	m_v_r = (m_v_r + moves) & mod_sns();
 | 
			
		||||
	m_v_r = mod_sns(m_v_r + moves);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlc_ul_window::raise_v_q(int incr)
 | 
			
		||||
{
 | 
			
		||||
	m_v_q = (m_v_q + incr) & mod_sns();
 | 
			
		||||
	m_v_q = mod_sns(m_v_q + incr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlc_v_n::mark_received(int bsn)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								src/sba.cpp
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/sba.cpp
									
									
									
									
									
								
							@@ -20,12 +20,16 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sba.h>
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
#include <bts.h>
 | 
			
		||||
#include <pcu_utils.h>
 | 
			
		||||
#include <pdch.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
@@ -55,6 +59,9 @@ int SBAController::alloc(
 | 
			
		||||
	if (!sba)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	if (!gsm48_ta_is_valid(ta))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	for (trx = 0; trx < 8; trx++) {
 | 
			
		||||
		for (ts = 7; ts >= 0; ts--) {
 | 
			
		||||
			pdch = &m_bts.bts_data()->trx[trx].pdch[ts];
 | 
			
		||||
@@ -71,7 +78,7 @@ int SBAController::alloc(
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648;
 | 
			
		||||
	fn = next_fn(pdch->last_rts_fn, AGCH_START_OFFSET);
 | 
			
		||||
 | 
			
		||||
	sba->trx_no = trx;
 | 
			
		||||
	sba->ts_no = ts;
 | 
			
		||||
@@ -106,14 +113,13 @@ gprs_rlcmac_sba *SBAController::find(const gprs_rlcmac_pdch *pdch, uint32_t fn)
 | 
			
		||||
 | 
			
		||||
uint32_t SBAController::sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t sba_fn;
 | 
			
		||||
	uint32_t sba_fn = fn + 4;
 | 
			
		||||
	struct gprs_rlcmac_sba *sba;
 | 
			
		||||
 | 
			
		||||
	/* check special TBF for events */
 | 
			
		||||
	sba_fn = fn + 4;
 | 
			
		||||
	if ((block_nr % 3) == 2)
 | 
			
		||||
		sba_fn ++;
 | 
			
		||||
	sba_fn = sba_fn % 2715648;
 | 
			
		||||
		sba_fn++;
 | 
			
		||||
	sba_fn = sba_fn % GSM_MAX_FN;
 | 
			
		||||
	sba = find(trx, ts, sba_fn);
 | 
			
		||||
	if (sba)
 | 
			
		||||
		return sba_fn;
 | 
			
		||||
@@ -123,7 +129,9 @@ uint32_t SBAController::sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t bloc
 | 
			
		||||
 | 
			
		||||
int SBAController::timeout(struct gprs_rlcmac_sba *sba)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for SBA\n");
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_NOTICE,
 | 
			
		||||
	     "Poll timeout for SBA (TRX=%u, TS=%u, FN=%u, TA=%u)\n", sba->trx_no,
 | 
			
		||||
	     sba->ts_no, sba->fn, sba->ta);
 | 
			
		||||
	m_bts.sba_timedout();
 | 
			
		||||
	free_sba(sba);
 | 
			
		||||
	return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -27,8 +27,6 @@ extern "C" {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct BTS;
 | 
			
		||||
class PollController;
 | 
			
		||||
struct gprs_rlcmac_sba;
 | 
			
		||||
struct gprs_rlcmac_pdch;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1292
									
								
								src/tbf.cpp
									
									
									
									
									
								
							
							
						
						
									
										1292
									
								
								src/tbf.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										608
									
								
								src/tbf.h
									
									
									
									
									
								
							
							
						
						
									
										608
									
								
								src/tbf.h
									
									
									
									
									
								
							@@ -20,25 +20,31 @@
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
 | 
			
		||||
#include "gprs_rlcmac.h"
 | 
			
		||||
#include "llc.h"
 | 
			
		||||
#include "rlc.h"
 | 
			
		||||
#include "cxx_linuxlist.h"
 | 
			
		||||
#include <gprs_debug.h>
 | 
			
		||||
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
#include <gsm_timer.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
	#include <osmocom/core/linuxlist.h>
 | 
			
		||||
	#include <osmocom/core/logging.h>
 | 
			
		||||
	#include <osmocom/core/timer.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct bssgp_bvc_ctx;
 | 
			
		||||
struct rlc_ul_header;
 | 
			
		||||
struct msgb;
 | 
			
		||||
struct pcu_l1_meas;
 | 
			
		||||
class GprsMs;
 | 
			
		||||
struct gprs_rlcmac_bts;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * TBF instance
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define Tassign_agch 0,200000	/* waiting after IMM.ASS confirm */
 | 
			
		||||
#define Tassign_pacch 2,0	/* timeout for pacch assigment */
 | 
			
		||||
#define T_ASS_AGCH_USEC 200000	/* waiting after IMM.ASS confirm */
 | 
			
		||||
#define T_ASS_PACCH_SEC 2	/* timeout for pacch assigment */
 | 
			
		||||
#define T_REJ_PACCH_USEC 2000	/* timeout for tbf reject for PRR*/
 | 
			
		||||
 | 
			
		||||
enum gprs_rlcmac_tbf_state {
 | 
			
		||||
	GPRS_RLCMAC_NULL = 0,	/* new created TBF */
 | 
			
		||||
@@ -49,34 +55,134 @@ enum gprs_rlcmac_tbf_state {
 | 
			
		||||
	GPRS_RLCMAC_RELEASING,	/* releasing, wait to free TBI/USF */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gprs_rlcmac_tbf_poll_type {
 | 
			
		||||
	GPRS_RLCMAC_POLL_UL_ASS,
 | 
			
		||||
	GPRS_RLCMAC_POLL_DL_ASS,
 | 
			
		||||
	GPRS_RLCMAC_POLL_UL_ACK,
 | 
			
		||||
	GPRS_RLCMAC_POLL_DL_ACK,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum gprs_rlcmac_tbf_poll_state {
 | 
			
		||||
	GPRS_RLCMAC_POLL_NONE = 0,
 | 
			
		||||
	GPRS_RLCMAC_POLL_SCHED, /* a polling was scheduled */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct value_string gprs_rlcmac_tbf_poll_state_names[];
 | 
			
		||||
 | 
			
		||||
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 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct value_string gprs_rlcmac_tbf_dl_ass_state_names[];
 | 
			
		||||
 | 
			
		||||
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_SEND_ASS_REJ, /* send assignment reject next RTS */
 | 
			
		||||
	GPRS_RLCMAC_UL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct value_string gprs_rlcmac_tbf_ul_ass_state_names[];
 | 
			
		||||
 | 
			
		||||
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 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct value_string gprs_rlcmac_tbf_ul_ack_state_names[];
 | 
			
		||||
 | 
			
		||||
enum gprs_rlcmac_tbf_direction {
 | 
			
		||||
	GPRS_RLCMAC_DL_TBF,
 | 
			
		||||
	GPRS_RLCMAC_UL_TBF
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum tbf_dl_prio {
 | 
			
		||||
	DL_PRIO_NONE,
 | 
			
		||||
	DL_PRIO_SENT_DATA, /* the data has been sent and not (yet) nacked */
 | 
			
		||||
	DL_PRIO_LOW_AGE,   /* the age has reached the first threshold */
 | 
			
		||||
	DL_PRIO_NEW_DATA,  /* the data has not been sent yet or nacked */
 | 
			
		||||
	DL_PRIO_HIGH_AGE,  /* the age has reached the second threshold */
 | 
			
		||||
	DL_PRIO_CONTROL,   /* a control block needs to be sent */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum tbf_rlc_counters {
 | 
			
		||||
	TBF_CTR_RLC_NACKED,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum tbf_gprs_counters {
 | 
			
		||||
	TBF_CTR_GPRS_DL_CS1,
 | 
			
		||||
	TBF_CTR_GPRS_DL_CS2,
 | 
			
		||||
	TBF_CTR_GPRS_DL_CS3,
 | 
			
		||||
	TBF_CTR_GPRS_DL_CS4,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum tbf_egprs_counters {
 | 
			
		||||
	TBF_CTR_EGPRS_DL_MCS1,
 | 
			
		||||
	TBF_CTR_EGPRS_DL_MCS2,
 | 
			
		||||
	TBF_CTR_EGPRS_DL_MCS3,
 | 
			
		||||
	TBF_CTR_EGPRS_DL_MCS4,
 | 
			
		||||
	TBF_CTR_EGPRS_DL_MCS5,
 | 
			
		||||
	TBF_CTR_EGPRS_DL_MCS6,
 | 
			
		||||
	TBF_CTR_EGPRS_DL_MCS7,
 | 
			
		||||
	TBF_CTR_EGPRS_DL_MCS8,
 | 
			
		||||
	TBF_CTR_EGPRS_DL_MCS9,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum tbf_gprs_ul_counters {
 | 
			
		||||
        TBF_CTR_GPRS_UL_CS1,
 | 
			
		||||
        TBF_CTR_GPRS_UL_CS2,
 | 
			
		||||
        TBF_CTR_GPRS_UL_CS3,
 | 
			
		||||
        TBF_CTR_GPRS_UL_CS4,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum tbf_egprs_ul_counters {
 | 
			
		||||
        TBF_CTR_EGPRS_UL_MCS1,
 | 
			
		||||
        TBF_CTR_EGPRS_UL_MCS2,
 | 
			
		||||
        TBF_CTR_EGPRS_UL_MCS3,
 | 
			
		||||
        TBF_CTR_EGPRS_UL_MCS4,
 | 
			
		||||
        TBF_CTR_EGPRS_UL_MCS5,
 | 
			
		||||
        TBF_CTR_EGPRS_UL_MCS6,
 | 
			
		||||
        TBF_CTR_EGPRS_UL_MCS7,
 | 
			
		||||
        TBF_CTR_EGPRS_UL_MCS8,
 | 
			
		||||
        TBF_CTR_EGPRS_UL_MCS9,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define LOGPTBF(tbf, level, fmt, args...) LOGP(DTBF, level, "%s " fmt, tbf_name(tbf), ## args)
 | 
			
		||||
#define LOGPTBFUL(tbf, level, fmt, args...) LOGP(DTBFUL, level, "%s " fmt, tbf_name(tbf), ## args)
 | 
			
		||||
#define LOGPTBFDL(tbf, level, fmt, args...) LOGP(DTBFDL, level, "%s " fmt, tbf_name(tbf), ## args)
 | 
			
		||||
 | 
			
		||||
enum tbf_timers {
 | 
			
		||||
	/* internal assign/reject timer */
 | 
			
		||||
	T0,
 | 
			
		||||
 | 
			
		||||
	/* Wait for reuse of USF and TFI(s) after the MS uplink assignment for this TBF is invalid. */
 | 
			
		||||
	T3169,
 | 
			
		||||
 | 
			
		||||
	/* Wait for reuse of TFI(s) after sending of the last RLC Data Block on this TBF.
 | 
			
		||||
	   Wait for reuse of TFI(s) after sending the PACKET TBF RELEASE for an MBMS radio bearer. */
 | 
			
		||||
	T3191,
 | 
			
		||||
 | 
			
		||||
	/* Wait for reuse of TFI(s) after reception of the final PACKET DOWNLINK ACK/NACK from the
 | 
			
		||||
	   MS for this TBF. */
 | 
			
		||||
	T3193,
 | 
			
		||||
 | 
			
		||||
	/* Wait for reuse of TFI(s) when there is no response from the MS
 | 
			
		||||
	   (radio failure or cell change) for this TBF/MBMS radio bearer. */
 | 
			
		||||
	T3195,
 | 
			
		||||
	T_MAX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum tbf_counters { /* TBF counters from 3GPP TS 44.060 §13.4 */
 | 
			
		||||
	/* counters are reset when: */
 | 
			
		||||
	N3101, /* received a valid data block from mobile station in a block assigned for this USF */
 | 
			
		||||
	N3103, /* transmitting the final PACKET UPLINK ACK/NACK message */
 | 
			
		||||
	N3105, /* after sending a RRBP field in the downlink RLC data block, receives a valid RLC/MAC control message */
 | 
			
		||||
	N_MAX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define GPRS_RLCMAC_FLAG_CCCH		0 /* assignment on CCCH */
 | 
			
		||||
#define GPRS_RLCMAC_FLAG_PACCH		1 /* assignment on PACCH */
 | 
			
		||||
#define GPRS_RLCMAC_FLAG_UL_DATA	2 /* uplink data received */
 | 
			
		||||
@@ -87,42 +193,47 @@ enum gprs_rlcmac_tbf_direction {
 | 
			
		||||
#define GPRS_RLCMAC_FLAG_TO_DL_ASS	7
 | 
			
		||||
#define GPRS_RLCMAC_FLAG_TO_MASK	0xf0 /* timeout bits */
 | 
			
		||||
 | 
			
		||||
struct llist_pods {
 | 
			
		||||
	struct llist_head list;
 | 
			
		||||
	void *back;
 | 
			
		||||
};
 | 
			
		||||
#define T_START(tbf, t, sec, usec, r, f) tbf->t_start(t, sec, usec, r, f, __FILE__, __LINE__)
 | 
			
		||||
 | 
			
		||||
#define llist_pods_entry(ptr, type) \
 | 
			
		||||
	((type *)(container_of(ptr, struct llist_pods, list)->back))
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * llist_pods_for_each_entry - like llist_for_each_entry, but uses
 | 
			
		||||
 * struct llist_pods ->back to access the entry.
 | 
			
		||||
 * This is necessary for non-PODS classes because container_of is
 | 
			
		||||
 * not guaranteed to work anymore. */
 | 
			
		||||
#define llist_pods_for_each_entry(pos, head, member, lpods)			\
 | 
			
		||||
	for (lpods = llist_entry((head)->next, typeof(struct llist_pods), list), \
 | 
			
		||||
		     pos = ((typeof(pos))lpods->back),	\
 | 
			
		||||
		     prefetch(pos->member.list.next);				\
 | 
			
		||||
	     &lpods->list != (head);					\
 | 
			
		||||
	     lpods = llist_entry(lpods->list.next, typeof(struct llist_pods), list), \
 | 
			
		||||
		     pos = ((typeof(pos))lpods->back),\
 | 
			
		||||
		     prefetch(pos->member.list.next))
 | 
			
		||||
#define TBF_SET_STATE(t, st) do { t->set_state(st, __FILE__, __LINE__); } while(0)
 | 
			
		||||
#define TBF_SET_ASS_STATE_DL(t, st) do { t->set_ass_state_dl(st, __FILE__, __LINE__); } while(0)
 | 
			
		||||
#define TBF_SET_ASS_STATE_UL(t, st) do { t->set_ass_state_ul(st, __FILE__, __LINE__); } while(0)
 | 
			
		||||
#define TBF_SET_ACK_STATE(t, st) do { t->set_ack_state(st, __FILE__, __LINE__); } while(0)
 | 
			
		||||
#define TBF_POLL_SCHED_SET(t) do { t->poll_sched_set(__FILE__, __LINE__); } while(0)
 | 
			
		||||
#define TBF_POLL_SCHED_UNSET(t) do { t->poll_sched_unset(__FILE__, __LINE__); } while(0)
 | 
			
		||||
#define TBF_SET_ASS_ON(t, fl, chk) do { t->set_assigned_on(fl, chk, __FILE__, __LINE__); } while(0)
 | 
			
		||||
#define TBF_ASS_TYPE_SET(t, kind) do { t->ass_type_mod(kind, false, __FILE__, __LINE__); } while(0)
 | 
			
		||||
#define TBF_ASS_TYPE_UNSET(t, kind) do { t->ass_type_mod(kind, true, __FILE__, __LINE__); } while(0)
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_tbf {
 | 
			
		||||
	gprs_rlcmac_tbf(BTS *bts_, gprs_rlcmac_tbf_direction dir);
 | 
			
		||||
 | 
			
		||||
	static void free_all(struct gprs_rlcmac_trx *trx);
 | 
			
		||||
	static void free_all(struct gprs_rlcmac_pdch *pdch);
 | 
			
		||||
 | 
			
		||||
	bool state_is(enum gprs_rlcmac_tbf_state rhs) const;
 | 
			
		||||
	bool state_is_not(enum gprs_rlcmac_tbf_state rhs) const;
 | 
			
		||||
	void set_state(enum gprs_rlcmac_tbf_state new_state);
 | 
			
		||||
	bool dl_ass_state_is(enum gprs_rlcmac_tbf_dl_ass_state rhs) const;
 | 
			
		||||
	bool ul_ass_state_is(enum gprs_rlcmac_tbf_ul_ass_state rhs) const;
 | 
			
		||||
	bool ul_ack_state_is(enum gprs_rlcmac_tbf_ul_ack_state rhs) const;
 | 
			
		||||
	bool poll_scheduled() const;
 | 
			
		||||
	void set_state(enum gprs_rlcmac_tbf_state new_state, const char *file, int line);
 | 
			
		||||
	void set_ass_state_dl(enum gprs_rlcmac_tbf_dl_ass_state new_state, const char *file, int line);
 | 
			
		||||
	void set_ass_state_ul(enum gprs_rlcmac_tbf_ul_ass_state new_state, const char *file, int line);
 | 
			
		||||
	void set_ack_state(enum gprs_rlcmac_tbf_ul_ack_state new_state, const char *file, int line);
 | 
			
		||||
	void poll_sched_set(const char *file, int line);
 | 
			
		||||
	void poll_sched_unset(const char *file, int line);
 | 
			
		||||
	void check_pending_ass();
 | 
			
		||||
	bool check_n_clear(uint8_t state_flag);
 | 
			
		||||
	void set_assigned_on(uint8_t state_flag, bool check_ccch, const char *file, int line);
 | 
			
		||||
	void ass_type_mod(uint8_t t, bool unset, const char *file, int line);
 | 
			
		||||
	const char *state_name() const;
 | 
			
		||||
 | 
			
		||||
	const char *name() const;
 | 
			
		||||
 | 
			
		||||
	struct msgb *create_dl_ass(uint32_t fn);
 | 
			
		||||
	struct msgb *create_ul_ass(uint32_t fn);
 | 
			
		||||
	struct msgb *create_dl_ass(uint32_t fn, uint8_t ts);
 | 
			
		||||
	struct msgb *create_ul_ass(uint32_t fn, uint8_t ts);
 | 
			
		||||
	struct msgb *create_packet_access_reject();
 | 
			
		||||
 | 
			
		||||
	GprsMs *ms() const;
 | 
			
		||||
	void set_ms(GprsMs *ms);
 | 
			
		||||
@@ -131,11 +242,21 @@ struct gprs_rlcmac_tbf {
 | 
			
		||||
 | 
			
		||||
	int rlcmac_diag();
 | 
			
		||||
 | 
			
		||||
	bool n_inc(enum tbf_counters n);
 | 
			
		||||
	void n_reset(enum tbf_counters n);
 | 
			
		||||
 | 
			
		||||
	int update();
 | 
			
		||||
	void handle_timeout();
 | 
			
		||||
	void stop_timer();
 | 
			
		||||
	void stop_t3191();
 | 
			
		||||
	void stop_timers(const char *reason);
 | 
			
		||||
	bool timers_pending(enum tbf_timers t);
 | 
			
		||||
	void t_stop(enum tbf_timers t, const char *reason);
 | 
			
		||||
	void t_start(enum tbf_timers t, uint32_t sec, uint32_t microsec, const char *reason, bool force,
 | 
			
		||||
		     const char *file, unsigned line);
 | 
			
		||||
	int establish_dl_tbf_on_pacch();
 | 
			
		||||
 | 
			
		||||
	int check_polling(uint32_t fn, uint8_t ts,
 | 
			
		||||
		uint32_t *poll_fn, unsigned int *rrbp);
 | 
			
		||||
	void set_polling(uint32_t poll_fn, uint8_t ts, enum gprs_rlcmac_tbf_poll_type t);
 | 
			
		||||
	void poll_timeout();
 | 
			
		||||
 | 
			
		||||
	/** tlli handling */
 | 
			
		||||
@@ -146,6 +267,7 @@ struct gprs_rlcmac_tbf {
 | 
			
		||||
	void update_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction);
 | 
			
		||||
 | 
			
		||||
	uint8_t tfi() const;
 | 
			
		||||
	bool is_tfi_assigned() const;
 | 
			
		||||
 | 
			
		||||
	const char *imsi() const;
 | 
			
		||||
	void assign_imsi(const char *imsi);
 | 
			
		||||
@@ -153,18 +275,28 @@ struct gprs_rlcmac_tbf {
 | 
			
		||||
	void set_ta(uint8_t);
 | 
			
		||||
	uint8_t ms_class() const;
 | 
			
		||||
	void set_ms_class(uint8_t);
 | 
			
		||||
	uint8_t current_cs() const;
 | 
			
		||||
	gprs_llc_queue *llc_queue();
 | 
			
		||||
	const gprs_llc_queue *llc_queue() const;
 | 
			
		||||
	GprsCodingScheme current_cs() const;
 | 
			
		||||
	size_t llc_queue_size() const;
 | 
			
		||||
 | 
			
		||||
	time_t created_ts() const;
 | 
			
		||||
	uint8_t dl_slots() const;
 | 
			
		||||
	uint8_t ul_slots() const;
 | 
			
		||||
 | 
			
		||||
	bool is_control_ts(uint8_t ts) const;
 | 
			
		||||
 | 
			
		||||
	/* EGPRS */
 | 
			
		||||
	bool is_egprs_enabled() const;
 | 
			
		||||
	void disable_egprs();
 | 
			
		||||
 | 
			
		||||
	/* attempt to make things a bit more fair */
 | 
			
		||||
	void rotate_in_list();
 | 
			
		||||
 | 
			
		||||
	struct llist_pods list;
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf>& ms_list() {return this->m_ms_list;}
 | 
			
		||||
	const LListHead<gprs_rlcmac_tbf>& ms_list() const {return this->m_ms_list;}
 | 
			
		||||
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf>& list();
 | 
			
		||||
	const LListHead<gprs_rlcmac_tbf>& list() const;
 | 
			
		||||
 | 
			
		||||
	uint32_t state_flags;
 | 
			
		||||
	enum gprs_rlcmac_tbf_direction direction;
 | 
			
		||||
	struct gprs_rlcmac_trx *trx;
 | 
			
		||||
@@ -176,34 +308,22 @@ struct gprs_rlcmac_tbf {
 | 
			
		||||
 | 
			
		||||
	gprs_llc m_llc;
 | 
			
		||||
 | 
			
		||||
	enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
 | 
			
		||||
	enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
 | 
			
		||||
	enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
 | 
			
		||||
 | 
			
		||||
	enum gprs_rlcmac_tbf_poll_state poll_state;
 | 
			
		||||
	uint32_t poll_fn; /* frame number to poll */
 | 
			
		||||
	uint8_t poll_ts; /* TS to poll */
 | 
			
		||||
 | 
			
		||||
	gprs_rlc m_rlc;
 | 
			
		||||
	
 | 
			
		||||
	uint8_t n3105;	/* N3105 counter */
 | 
			
		||||
 | 
			
		||||
	struct osmo_timer_list	timer;
 | 
			
		||||
	unsigned int T; /* Txxxx number */
 | 
			
		||||
	unsigned int num_T_exp; /* number of consecutive T expirations */
 | 
			
		||||
	
 | 
			
		||||
	struct osmo_gsm_timer_list	gsm_timer;
 | 
			
		||||
	unsigned int fT; /* fTxxxx number */
 | 
			
		||||
	unsigned int num_fT_exp; /* number of consecutive fT expirations */
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
	struct Meas {
 | 
			
		||||
		struct timeval rssi_tv; /* timestamp for rssi calculation */
 | 
			
		||||
		int32_t rssi_sum; /* sum of rssi values */
 | 
			
		||||
		int rssi_num; /* number of rssi values added since rssi_tv */
 | 
			
		||||
	} meas;
 | 
			
		||||
 | 
			
		||||
	/* these should become protected but only after gprs_rlcmac_data.c
 | 
			
		||||
	 * stops to iterate over all tbf in its current form */
 | 
			
		||||
	enum gprs_rlcmac_tbf_state state;
 | 
			
		||||
		Meas();
 | 
			
		||||
	} meas;
 | 
			
		||||
 | 
			
		||||
	/* Remember if the tbf was in wait_release state when we want to
 | 
			
		||||
	 * schedule a new dl assignment */
 | 
			
		||||
@@ -222,10 +342,16 @@ struct gprs_rlcmac_tbf {
 | 
			
		||||
	uint8_t m_tfi;
 | 
			
		||||
	time_t m_created_ts;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr_group *m_ctrs;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	gprs_rlcmac_bts *bts_data() const;
 | 
			
		||||
	void enable_egprs();
 | 
			
		||||
	int set_tlli_from_ul(uint32_t new_tlli);
 | 
			
		||||
	void merge_and_clear_ms(GprsMs *old_ms);
 | 
			
		||||
 | 
			
		||||
	int extract_tlli(const uint8_t *data, const size_t len);
 | 
			
		||||
	gprs_llc_queue *llc_queue();
 | 
			
		||||
	const gprs_llc_queue *llc_queue() const;
 | 
			
		||||
 | 
			
		||||
	static const char *tbf_state_name[6];
 | 
			
		||||
 | 
			
		||||
@@ -236,34 +362,62 @@ protected:
 | 
			
		||||
	uint8_t m_ms_class;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	enum gprs_rlcmac_tbf_state state;
 | 
			
		||||
	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;
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> m_list;
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> m_ms_list;
 | 
			
		||||
	bool m_egprs_enabled;
 | 
			
		||||
	struct osmo_timer_list T[T_MAX];
 | 
			
		||||
	uint8_t N[N_MAX];
 | 
			
		||||
	mutable char m_name_buf[60];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	int8_t use_trx, uint8_t ms_class,
 | 
			
		||||
	int8_t use_trx, uint8_t ms_class, uint8_t egprs_ms_class,
 | 
			
		||||
	uint32_t tlli, uint8_t ta, GprsMs *ms);
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	GprsMs *ms, int8_t use_trx,
 | 
			
		||||
	uint8_t ms_class, uint8_t single_slot);
 | 
			
		||||
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms, int8_t use_trx, uint8_t ms_class,
 | 
			
		||||
					    uint8_t egprs_ms_class, bool single_slot);
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	GprsMs *ms, int8_t use_trx,
 | 
			
		||||
	uint8_t ms_class, uint8_t single_slot);
 | 
			
		||||
struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms, int8_t use_trx, uint8_t ms_class,
 | 
			
		||||
					    uint8_t egprs_ms_class, bool single_slot);
 | 
			
		||||
 | 
			
		||||
void tbf_free(struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
struct gprs_rlcmac_ul_tbf *handle_tbf_reject(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
	GprsMs *ms, uint32_t tlli, uint8_t trx_no, uint8_t ts_no);
 | 
			
		||||
 | 
			
		||||
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
 | 
			
		||||
                        unsigned int seconds, unsigned int microseconds);
 | 
			
		||||
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlcmac_tbf::state_is(enum gprs_rlcmac_tbf_state rhs) const
 | 
			
		||||
{
 | 
			
		||||
	return state == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlcmac_tbf::dl_ass_state_is(enum gprs_rlcmac_tbf_dl_ass_state rhs) const
 | 
			
		||||
{
 | 
			
		||||
	return dl_ass_state == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlcmac_tbf::ul_ass_state_is(enum gprs_rlcmac_tbf_ul_ass_state rhs) const
 | 
			
		||||
{
 | 
			
		||||
	return ul_ass_state == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlcmac_tbf::ul_ack_state_is(enum gprs_rlcmac_tbf_ul_ack_state rhs) const
 | 
			
		||||
{
 | 
			
		||||
	return ul_ack_state == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlcmac_tbf::poll_scheduled() const
 | 
			
		||||
{
 | 
			
		||||
	return poll_state == GPRS_RLCMAC_POLL_SCHED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlcmac_tbf::state_is_not(enum gprs_rlcmac_tbf_state rhs) const
 | 
			
		||||
{
 | 
			
		||||
	return state != rhs;
 | 
			
		||||
@@ -276,14 +430,147 @@ inline const char *gprs_rlcmac_tbf::state_name() const
 | 
			
		||||
	return tbf_state_name[state];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state)
 | 
			
		||||
/* Set assignment state and corrsponding flags */
 | 
			
		||||
inline void gprs_rlcmac_tbf::set_assigned_on(uint8_t state_flag, bool check_ccch, const char *file, int line)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DRLCMAC, LOGL_DEBUG, "%s changes state from %s to %s\n",
 | 
			
		||||
	set_state(GPRS_RLCMAC_ASSIGN, file, line);
 | 
			
		||||
	if (check_ccch) {
 | 
			
		||||
		if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)))
 | 
			
		||||
			ass_type_mod(state_flag, false, file, line);
 | 
			
		||||
	} else
 | 
			
		||||
		state_flags |= (1 << state_flag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::ass_type_mod(uint8_t t, bool unset, const char *file, int line)
 | 
			
		||||
{
 | 
			
		||||
	const char *ch = "UNKNOWN";
 | 
			
		||||
	switch (t) {
 | 
			
		||||
	case GPRS_RLCMAC_FLAG_CCCH:
 | 
			
		||||
		if (unset) {
 | 
			
		||||
			if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)))
 | 
			
		||||
				return;
 | 
			
		||||
		} else {
 | 
			
		||||
			if (state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))
 | 
			
		||||
				LOGPSRC(DTBF, LOGL_ERROR, file, line,
 | 
			
		||||
					"%s attempted to set ass. type CCCH which is already set.\n",
 | 
			
		||||
					tbf_name(this));
 | 
			
		||||
		}
 | 
			
		||||
		ch = "CCCH";
 | 
			
		||||
		break;
 | 
			
		||||
	case GPRS_RLCMAC_FLAG_PACCH:
 | 
			
		||||
		if (unset) {
 | 
			
		||||
			if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)))
 | 
			
		||||
				return;
 | 
			
		||||
		} else {
 | 
			
		||||
			if (state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))
 | 
			
		||||
				LOGPSRC(DTBF, LOGL_ERROR, file, line,
 | 
			
		||||
					"%s attempted to set ass. type PACCH which is already set.\n",
 | 
			
		||||
					tbf_name(this));
 | 
			
		||||
		}
 | 
			
		||||
		ch = "PACCH";
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGPSRC(DTBF, LOGL_ERROR, file, line, "%s attempted to %sset unexpected ass. type %d - FIXME!\n",
 | 
			
		||||
			tbf_name(this), unset ? "un" : "", t);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPSRC(DTBF, LOGL_INFO, file, line, "%s %sset ass. type %s [prev CCCH:%u, PACCH:%u]\n",
 | 
			
		||||
		tbf_name(this), unset ? "un" : "", ch,
 | 
			
		||||
		state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH),
 | 
			
		||||
		state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH));
 | 
			
		||||
 | 
			
		||||
	if (unset) {
 | 
			
		||||
		state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep to flags */
 | 
			
		||||
		state_flags &= ~(1 << t);
 | 
			
		||||
	} else
 | 
			
		||||
		state_flags |= (1 << t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state, const char *file, int line)
 | 
			
		||||
{
 | 
			
		||||
	LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes state from %s to %s\n",
 | 
			
		||||
		tbf_name(this),
 | 
			
		||||
		tbf_state_name[state], tbf_state_name[new_state]);
 | 
			
		||||
	state = new_state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::set_ass_state_dl(enum gprs_rlcmac_tbf_dl_ass_state new_state, const char *file, int line)
 | 
			
		||||
{
 | 
			
		||||
	LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes DL ASS state from %s to %s\n",
 | 
			
		||||
		tbf_name(this),
 | 
			
		||||
		get_value_string(gprs_rlcmac_tbf_dl_ass_state_names, dl_ass_state),
 | 
			
		||||
		get_value_string(gprs_rlcmac_tbf_dl_ass_state_names, new_state));
 | 
			
		||||
	dl_ass_state = new_state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::set_ass_state_ul(enum gprs_rlcmac_tbf_ul_ass_state new_state, const char *file, int line)
 | 
			
		||||
{
 | 
			
		||||
	LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes UL ASS state from %s to %s\n",
 | 
			
		||||
		tbf_name(this),
 | 
			
		||||
		get_value_string(gprs_rlcmac_tbf_ul_ass_state_names, ul_ass_state),
 | 
			
		||||
		get_value_string(gprs_rlcmac_tbf_ul_ass_state_names, new_state));
 | 
			
		||||
	ul_ass_state = new_state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::set_ack_state(enum gprs_rlcmac_tbf_ul_ack_state new_state, const char *file, int line)
 | 
			
		||||
{
 | 
			
		||||
	LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes UL ACK state from %s to %s\n",
 | 
			
		||||
		tbf_name(this),
 | 
			
		||||
		get_value_string(gprs_rlcmac_tbf_ul_ack_state_names, ul_ack_state),
 | 
			
		||||
		get_value_string(gprs_rlcmac_tbf_ul_ack_state_names, new_state));
 | 
			
		||||
	ul_ack_state = new_state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::poll_sched_set(const char *file, int line)
 | 
			
		||||
{
 | 
			
		||||
	LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes poll state from %s to GPRS_RLCMAC_POLL_SCHED\n",
 | 
			
		||||
		tbf_name(this), get_value_string(gprs_rlcmac_tbf_poll_state_names, poll_state));
 | 
			
		||||
	poll_state = GPRS_RLCMAC_POLL_SCHED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::poll_sched_unset(const char *file, int line)
 | 
			
		||||
{
 | 
			
		||||
	LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes poll state from %s to GPRS_RLCMAC_POLL_NONE\n",
 | 
			
		||||
		tbf_name(this), get_value_string(gprs_rlcmac_tbf_poll_state_names, poll_state));
 | 
			
		||||
	poll_state = GPRS_RLCMAC_POLL_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::check_pending_ass()
 | 
			
		||||
{
 | 
			
		||||
	if (ul_ass_state != GPRS_RLCMAC_UL_ASS_NONE)
 | 
			
		||||
		LOGPTBF(this, LOGL_ERROR, "FIXME: Software error: Pending uplink assignment in state %s. "
 | 
			
		||||
			"This may not happen, because the assignment message never gets transmitted. "
 | 
			
		||||
			"Please be sure not to free in this state. PLEASE FIX!\n",
 | 
			
		||||
			get_value_string(gprs_rlcmac_tbf_ul_ass_state_names, ul_ass_state));
 | 
			
		||||
 | 
			
		||||
	if (dl_ass_state != GPRS_RLCMAC_DL_ASS_NONE)
 | 
			
		||||
		LOGPTBF(this, LOGL_ERROR, "FIXME: Software error: Pending downlink assignment in state %s. "
 | 
			
		||||
			"This may not happen, because the assignment message never gets transmitted. "
 | 
			
		||||
			"Please be sure not to free in this state. PLEASE FIX!\n",
 | 
			
		||||
			get_value_string(gprs_rlcmac_tbf_dl_ass_state_names, dl_ass_state));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlcmac_tbf::check_n_clear(uint8_t state_flag)
 | 
			
		||||
{
 | 
			
		||||
	if ((state_flags & (1 << state_flag))) {
 | 
			
		||||
		state_flags &= ~(1 << state_flag);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline LListHead<gprs_rlcmac_tbf>& gprs_rlcmac_tbf::list()
 | 
			
		||||
{
 | 
			
		||||
	return this->m_list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline const LListHead<gprs_rlcmac_tbf>& gprs_rlcmac_tbf::list() const
 | 
			
		||||
{
 | 
			
		||||
	return this->m_list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline GprsMs *gprs_rlcmac_tbf::ms() const
 | 
			
		||||
{
 | 
			
		||||
	return m_ms;
 | 
			
		||||
@@ -294,6 +581,16 @@ inline bool gprs_rlcmac_tbf::is_tlli_valid() const
 | 
			
		||||
	return tlli() != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool gprs_rlcmac_tbf::is_tfi_assigned() const
 | 
			
		||||
{
 | 
			
		||||
	/* The TBF is established or has been assigned by a IMM.ASS for
 | 
			
		||||
	 * download */
 | 
			
		||||
	return state > GPRS_RLCMAC_ASSIGN ||
 | 
			
		||||
		(direction == GPRS_RLCMAC_DL_TBF &&
 | 
			
		||||
		 state == GPRS_RLCMAC_ASSIGN &&
 | 
			
		||||
		 (state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint8_t gprs_rlcmac_tbf::tfi() const
 | 
			
		||||
{
 | 
			
		||||
	return m_tfi;
 | 
			
		||||
@@ -304,31 +601,54 @@ inline time_t gprs_rlcmac_tbf::created_ts() const
 | 
			
		||||
	return m_created_ts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
 | 
			
		||||
	void cleanup();
 | 
			
		||||
inline bool gprs_rlcmac_tbf::is_egprs_enabled() const
 | 
			
		||||
{
 | 
			
		||||
	return m_egprs_enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::enable_egprs()
 | 
			
		||||
{
 | 
			
		||||
	m_egprs_enabled = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_tbf::disable_egprs()
 | 
			
		||||
{
 | 
			
		||||
	m_egprs_enabled = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
 | 
			
		||||
	gprs_rlcmac_dl_tbf(BTS *bts);
 | 
			
		||||
	gprs_rlc_dl_window *window();
 | 
			
		||||
	void cleanup();
 | 
			
		||||
	void enable_egprs();
 | 
			
		||||
	/* dispatch Unitdata.DL messages */
 | 
			
		||||
	static int handle(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
		const uint32_t tlli, const uint32_t old_tlli,
 | 
			
		||||
		const char *imsi, const uint8_t ms_class,
 | 
			
		||||
		const uint16_t delay_csec, const uint8_t *data, const uint16_t len);
 | 
			
		||||
		const uint8_t egprs_ms_class, const uint16_t delay_csec,
 | 
			
		||||
		const uint8_t *data, const uint16_t len);
 | 
			
		||||
 | 
			
		||||
	int append_data(const uint8_t ms_class,
 | 
			
		||||
			const uint16_t pdu_delay_csec,
 | 
			
		||||
			const uint8_t *data, const uint16_t len);
 | 
			
		||||
 | 
			
		||||
	int rcvd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb);
 | 
			
		||||
	int rcvd_dl_ack(bool final, uint8_t ssn, uint8_t *rbb);
 | 
			
		||||
	int rcvd_dl_ack(bool final_ack, unsigned first_bsn, struct bitvec *rbb);
 | 
			
		||||
	struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts);
 | 
			
		||||
	void trigger_ass(struct gprs_rlcmac_tbf *old_tbf);
 | 
			
		||||
 | 
			
		||||
	bool handle_ack_nack();
 | 
			
		||||
	void request_dl_ack();
 | 
			
		||||
	bool need_control_ts() const;
 | 
			
		||||
	bool have_data() const;
 | 
			
		||||
	int frames_since_last_poll(unsigned fn) const;
 | 
			
		||||
	int frames_since_last_drain(unsigned fn) const;
 | 
			
		||||
	bool keep_open(unsigned fn) const;
 | 
			
		||||
 | 
			
		||||
	bool is_control_ts(uint8_t ts) const {
 | 
			
		||||
		return ts == control_ts;
 | 
			
		||||
	}
 | 
			
		||||
	int release();
 | 
			
		||||
	int abort();
 | 
			
		||||
	uint16_t window_size() const;
 | 
			
		||||
	void set_window_size();
 | 
			
		||||
	void update_coding_scheme_counter_dl(const GprsCodingScheme cs);
 | 
			
		||||
 | 
			
		||||
	/* TODO: add the gettimeofday as parameter */
 | 
			
		||||
	struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
 | 
			
		||||
@@ -338,78 +658,178 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
 | 
			
		||||
	 * All states that need reset must be in this struct, so this is why
 | 
			
		||||
	 * variables are in both (dl and ul) structs and not outside union.
 | 
			
		||||
	 */
 | 
			
		||||
	gprs_rlc_dl_window m_window;
 | 
			
		||||
	int32_t m_tx_counter; /* count all transmitted blocks */
 | 
			
		||||
	uint8_t m_wait_confirm; /* wait for CCCH IMM.ASS cnf */
 | 
			
		||||
	bool m_dl_ack_requested;
 | 
			
		||||
	int32_t m_last_dl_poll_fn;
 | 
			
		||||
	int32_t m_last_dl_drained_fn;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
	struct BandWidth {
 | 
			
		||||
		struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
 | 
			
		||||
		uint32_t dl_bw_octets; /* number of octets since bw_tv */
 | 
			
		||||
		uint32_t dl_throughput; /* throughput to be displayed in stats */
 | 
			
		||||
 | 
			
		||||
		struct timeval dl_loss_tv; /* timestamp for loss calculation */
 | 
			
		||||
		uint16_t dl_loss_lost; /* sum of lost packets */
 | 
			
		||||
		uint16_t dl_loss_received; /* sum of received packets */
 | 
			
		||||
 | 
			
		||||
		BandWidth();
 | 
			
		||||
	} m_bw;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr_group *m_dl_gprs_ctrs;
 | 
			
		||||
	struct rate_ctr_group *m_dl_egprs_ctrs;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	struct msgb *create_new_bsn(const uint32_t fn, const uint8_t ts);
 | 
			
		||||
	struct ana_result {
 | 
			
		||||
		unsigned received_packets;
 | 
			
		||||
		unsigned lost_packets;
 | 
			
		||||
		unsigned received_bytes;
 | 
			
		||||
		unsigned lost_bytes;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	int take_next_bsn(uint32_t fn, int previous_bsn,
 | 
			
		||||
		bool *may_combine);
 | 
			
		||||
	bool restart_bsn_cycle();
 | 
			
		||||
	int create_new_bsn(const uint32_t fn, GprsCodingScheme cs);
 | 
			
		||||
	struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts,
 | 
			
		||||
					const int index);
 | 
			
		||||
					int index, int index2 = -1);
 | 
			
		||||
	int update_window(const uint8_t ssn, const uint8_t *rbb);
 | 
			
		||||
	int update_window(unsigned first_bsn, const struct bitvec *rbb);
 | 
			
		||||
	int maybe_start_new_window();
 | 
			
		||||
	bool dl_window_stalled() const;
 | 
			
		||||
	void reuse_tbf();
 | 
			
		||||
	void start_llc_timer();
 | 
			
		||||
	int analyse_errors(char *show_rbb, uint8_t ssn);
 | 
			
		||||
	int analyse_errors(char *show_rbb, uint8_t ssn, ana_result *res);
 | 
			
		||||
	void schedule_next_frame();
 | 
			
		||||
 | 
			
		||||
	enum egprs_rlc_dl_reseg_bsn_state egprs_dl_get_data
 | 
			
		||||
		(int bsn, uint8_t **block_data);
 | 
			
		||||
	unsigned int get_egprs_dl_spb_status(int bsn);
 | 
			
		||||
	enum egprs_rlcmac_dl_spb get_egprs_dl_spb(int bsn);
 | 
			
		||||
 | 
			
		||||
	struct osmo_timer_list m_llc_timer;
 | 
			
		||||
 | 
			
		||||
	/* Please note that all variables below will be reset when changing
 | 
			
		||||
	 * from WAIT RELEASE back to FLOW state (re-use of TBF).
 | 
			
		||||
	 * All states that need reset must be in this struct, so this is why
 | 
			
		||||
	 * variables are in both (dl and ul) structs and not outside union.
 | 
			
		||||
	 */
 | 
			
		||||
	gprs_rlc_dl_window m_window;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
 | 
			
		||||
	struct msgb *create_ul_ack(uint32_t fn);
 | 
			
		||||
 | 
			
		||||
	gprs_rlcmac_ul_tbf(BTS *bts);
 | 
			
		||||
	gprs_rlc_ul_window *window();
 | 
			
		||||
	struct msgb *create_ul_ack(uint32_t fn, uint8_t ts);
 | 
			
		||||
	bool ctrl_ack_to_toggle();
 | 
			
		||||
	bool handle_ctrl_ack();
 | 
			
		||||
	void enable_egprs();
 | 
			
		||||
	/* blocks were acked */
 | 
			
		||||
	int rcv_data_block_acknowledged(const uint8_t *data, size_t len,
 | 
			
		||||
		struct pcu_l1_meas *meas);
 | 
			
		||||
	int rcv_data_block_acknowledged(
 | 
			
		||||
		const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		uint8_t *data, struct pcu_l1_meas *meas);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* TODO: extract LLC class? */
 | 
			
		||||
	int assemble_forward_llc(const gprs_rlc_data *data);
 | 
			
		||||
	int snd_ul_ud();
 | 
			
		||||
 | 
			
		||||
	egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_spb(
 | 
			
		||||
		const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		struct gprs_rlc_data *block,
 | 
			
		||||
		uint8_t *data, const uint8_t block_idx);
 | 
			
		||||
 | 
			
		||||
	egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_first_seg(
 | 
			
		||||
		const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		struct gprs_rlc_data *block,
 | 
			
		||||
		uint8_t *data, const uint8_t block_idx);
 | 
			
		||||
 | 
			
		||||
	egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_second_seg(
 | 
			
		||||
		const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
		struct gprs_rlc_data *block,
 | 
			
		||||
		uint8_t *data, const uint8_t block_idx);
 | 
			
		||||
 | 
			
		||||
	uint16_t window_size() const;
 | 
			
		||||
	void set_window_size();
 | 
			
		||||
	void update_coding_scheme_counter_ul(const GprsCodingScheme cs);
 | 
			
		||||
 | 
			
		||||
	/* Please note that all variables here will be reset when changing
 | 
			
		||||
	 * from WAIT RELEASE back to FLOW state (re-use of TBF).
 | 
			
		||||
	 * All states that need reset must be in this struct, so this is why
 | 
			
		||||
	 * variables are in both (dl and ul) structs and not outside union.
 | 
			
		||||
	 */
 | 
			
		||||
	gprs_rlc_ul_window m_window;
 | 
			
		||||
	int32_t m_rx_counter; /* count all received blocks */
 | 
			
		||||
	uint8_t m_n3103;	/* N3103 counter */
 | 
			
		||||
	uint8_t m_usf[8];	/* list USFs per PDCH (timeslot) */
 | 
			
		||||
	uint8_t m_contention_resolution_done; /* set after done */
 | 
			
		||||
	uint8_t m_final_ack_sent; /* set if we sent final ack */
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr_group *m_ul_gprs_ctrs;
 | 
			
		||||
	struct rate_ctr_group *m_ul_egprs_ctrs;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	void maybe_schedule_uplink_acknack(const rlc_ul_header *rh);
 | 
			
		||||
	void maybe_schedule_uplink_acknack(const gprs_rlc_data_info *rlc);
 | 
			
		||||
 | 
			
		||||
	/* Please note that all variables below will be reset when changing
 | 
			
		||||
	 * from WAIT RELEASE back to FLOW state (re-use of TBF).
 | 
			
		||||
	 * All states that need reset must be in this struct, so this is why
 | 
			
		||||
	 * variables are in both (dl and ul) structs and not outside union.
 | 
			
		||||
	 */
 | 
			
		||||
	gprs_rlc_ul_window m_window;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
void update_tbf_ta(struct gprs_rlcmac_ul_tbf *tbf, int8_t ta_delta);
 | 
			
		||||
void set_tbf_ta(struct gprs_rlcmac_ul_tbf *tbf, uint8_t ta);
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
inline enum gprs_rlcmac_tbf_direction reverse(enum gprs_rlcmac_tbf_direction dir)
 | 
			
		||||
{
 | 
			
		||||
	return (enum gprs_rlcmac_tbf_direction)
 | 
			
		||||
		((int)GPRS_RLCMAC_UL_TBF - (int)dir + (int)GPRS_RLCMAC_DL_TBF);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/vty.h>
 | 
			
		||||
 | 
			
		||||
	void tbf_print_vty_info(struct vty *vty, struct llist_head *tbf);
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
inline uint16_t gprs_rlcmac_ul_tbf::window_size() const
 | 
			
		||||
{
 | 
			
		||||
	return m_window.ws();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline uint16_t gprs_rlcmac_dl_tbf::window_size() const
 | 
			
		||||
{
 | 
			
		||||
	return m_window.ws();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_ul_tbf::enable_egprs()
 | 
			
		||||
{
 | 
			
		||||
	m_window.set_sns(RLC_EGPRS_SNS);
 | 
			
		||||
	gprs_rlcmac_tbf::enable_egprs();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void gprs_rlcmac_dl_tbf::enable_egprs()
 | 
			
		||||
{
 | 
			
		||||
	m_window.set_sns(RLC_EGPRS_SNS);
 | 
			
		||||
	gprs_rlcmac_tbf::enable_egprs();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline gprs_rlcmac_ul_tbf *as_ul_tbf(gprs_rlcmac_tbf *tbf)
 | 
			
		||||
{
 | 
			
		||||
	if (tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
 | 
			
		||||
		return static_cast<gprs_rlcmac_ul_tbf *>(tbf);
 | 
			
		||||
	else
 | 
			
		||||
		return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline gprs_rlcmac_dl_tbf *as_dl_tbf(gprs_rlcmac_tbf *tbf)
 | 
			
		||||
{
 | 
			
		||||
	if (tbf && tbf->direction == GPRS_RLCMAC_DL_TBF)
 | 
			
		||||
		return static_cast<gprs_rlcmac_dl_tbf *>(tbf);
 | 
			
		||||
	else
 | 
			
		||||
		return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t egprs_window_size(const struct gprs_rlcmac_bts *bts_data, uint8_t slots);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1344
									
								
								src/tbf_dl.cpp
									
									
									
									
									
								
							
							
						
						
									
										1344
									
								
								src/tbf_dl.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										645
									
								
								src/tbf_ul.cpp
									
									
									
									
									
								
							
							
						
						
									
										645
									
								
								src/tbf_ul.cpp
									
									
									
									
									
								
							@@ -28,10 +28,21 @@
 | 
			
		||||
#include <gprs_bssgp_pcu.h>
 | 
			
		||||
#include <decoding.h>
 | 
			
		||||
#include <pcu_l1_if.h>
 | 
			
		||||
#include <gprs_coding_scheme.h>
 | 
			
		||||
#include <gprs_ms.h>
 | 
			
		||||
#include <llc.h>
 | 
			
		||||
#include "pcu_utils.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
	#include <osmocom/core/bitvec.h>
 | 
			
		||||
	#include <osmocom/core/logging.h>
 | 
			
		||||
	#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
	#include <osmocom/core/utils.h>
 | 
			
		||||
	#include <osmocom/gprs/gprs_bssgp_bss.h>
 | 
			
		||||
	#include <osmocom/gprs/protocol/gsm_08_18.h>
 | 
			
		||||
	#include <osmocom/gsm/tlv.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
@@ -42,7 +53,6 @@ extern "C" {
 | 
			
		||||
 | 
			
		||||
extern void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Store received block data in LLC message(s) and forward to SGSN
 | 
			
		||||
 * if complete.
 | 
			
		||||
@@ -51,157 +61,41 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
 | 
			
		||||
{
 | 
			
		||||
	const uint8_t *data = _data->block;
 | 
			
		||||
	uint8_t len = _data->len;
 | 
			
		||||
	const struct rlc_ul_header *rh = (const struct rlc_ul_header *) data;
 | 
			
		||||
	uint8_t e, m;
 | 
			
		||||
	struct rlc_li_field *li;
 | 
			
		||||
	uint8_t frame_offset[16], offset = 0, chunk;
 | 
			
		||||
	int i, frames = 0;
 | 
			
		||||
	const struct gprs_rlc_data_block_info *rdbi = &_data->block_info;
 | 
			
		||||
	GprsCodingScheme cs = _data->cs_last;
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
 | 
			
		||||
	Decoding::RlcData frames[16], *frame;
 | 
			
		||||
	int i, num_frames = 0;
 | 
			
		||||
	uint32_t dummy_tlli;
 | 
			
		||||
 | 
			
		||||
	data += 3;
 | 
			
		||||
	len -= 3;
 | 
			
		||||
	e = rh->e; /* if extended */
 | 
			
		||||
	m = 1; /* more frames, that means: the first frame */
 | 
			
		||||
	LOGPTBFUL(this, LOGL_DEBUG, "Assembling frames: (len=%d)\n", len);
 | 
			
		||||
 | 
			
		||||
	/* Parse frame offsets from length indicator(s), if any. */
 | 
			
		||||
	while (1) {
 | 
			
		||||
		if (frames == (int)sizeof(frame_offset)) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_ERROR, "%s too many frames in "
 | 
			
		||||
				"block\n", tbf_name(this));
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		frame_offset[frames++] = offset;
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset "
 | 
			
		||||
			"%d\n", frames, offset);
 | 
			
		||||
		if (!len)
 | 
			
		||||
			break;
 | 
			
		||||
		/* M == 0 and E == 0 is not allowed in this version. */
 | 
			
		||||
		if (!m && !e) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA "
 | 
			
		||||
				"ignored, because M='0' and E='0'.\n",
 | 
			
		||||
				tbf_name(this));
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		/* no more frames in this segment */
 | 
			
		||||
		if (e) {
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		/* There is a new frame and an LI that delimits it. */
 | 
			
		||||
		if (m) {
 | 
			
		||||
			li = (struct rlc_li_field *)data;
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n",
 | 
			
		||||
				li->li);
 | 
			
		||||
			/* Special case: LI == 0
 | 
			
		||||
			 * If the last segment would fit precisely into the
 | 
			
		||||
			 * rest of the RLC MAC block, there would be no way
 | 
			
		||||
			 * to delimit that this segment ends and is not
 | 
			
		||||
			 * continued in the next block.
 | 
			
		||||
			 * The special LI (0) is used to force the segment to
 | 
			
		||||
			 * extend into the next block, so it is delimited there.
 | 
			
		||||
			 * This LI must be skipped. Also it is the last LI.
 | 
			
		||||
			 */
 | 
			
		||||
			if (li->li == 0) {
 | 
			
		||||
				data++;
 | 
			
		||||
				len--;
 | 
			
		||||
				m = 1; /* M is ignored, we know there is more */
 | 
			
		||||
				break; /* handle E as '1', so we break! */
 | 
			
		||||
			}
 | 
			
		||||
			e = li->e;
 | 
			
		||||
			m = li->m;
 | 
			
		||||
			offset += li->li;
 | 
			
		||||
			data++;
 | 
			
		||||
			len--;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (!m) {
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare "
 | 
			
		||||
			"data\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n",
 | 
			
		||||
		len);
 | 
			
		||||
	/* TLLI */
 | 
			
		||||
	if (rh->ti) {
 | 
			
		||||
		if (len < 4) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA TLLI out of "
 | 
			
		||||
				"frame border\n", tbf_name(this));
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		data += 4;
 | 
			
		||||
		len -= 4;
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: "
 | 
			
		||||
			"%d\n", len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* PFI */
 | 
			
		||||
	if (rh->pi) {
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
 | 
			
		||||
			"please disable in SYSTEM INFORMATION\n");
 | 
			
		||||
		if (len < 1) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA PFI out of "
 | 
			
		||||
				"frame border\n", tbf_name(this));
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		data++;
 | 
			
		||||
		len--;
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: "
 | 
			
		||||
			"%d\n", len);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Now we have:
 | 
			
		||||
	 * - a list of frames offsets: frame_offset[]
 | 
			
		||||
	 * - number of frames: i
 | 
			
		||||
	 * - m == 0: Last frame carries spare data (end of TBF).
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	/* Check if last offset would exceed frame. */
 | 
			
		||||
	if (offset > len) {
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA ignored, "
 | 
			
		||||
			"because LI delimits data that exceeds block size.\n",
 | 
			
		||||
			tbf_name(this));
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	num_frames = Decoding::rlc_data_from_ul_data(
 | 
			
		||||
		rdbi, cs, data, &(frames[0]), ARRAY_SIZE(frames),
 | 
			
		||||
		&dummy_tlli);
 | 
			
		||||
 | 
			
		||||
	/* create LLC frames */
 | 
			
		||||
	for (i = 0; i < frames; i++) {
 | 
			
		||||
		/* last frame ? */
 | 
			
		||||
		if (i == frames - 1) {
 | 
			
		||||
			/* no more data in last frame */
 | 
			
		||||
			if (!m)
 | 
			
		||||
				break;
 | 
			
		||||
			/* data until end of frame */
 | 
			
		||||
			chunk = len - frame_offset[i];
 | 
			
		||||
		} else {
 | 
			
		||||
			/* data until next frame */
 | 
			
		||||
			chunk = frame_offset[i + 1] - frame_offset[i];
 | 
			
		||||
	for (i = 0; i < num_frames; i++) {
 | 
			
		||||
		frame = frames + i;
 | 
			
		||||
 | 
			
		||||
		if (frame->length) {
 | 
			
		||||
			bts->rlc_ul_payload_bytes(frame->length);
 | 
			
		||||
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG, "Frame %d "
 | 
			
		||||
				"starts at offset %d, "
 | 
			
		||||
				"length=%d, is_complete=%d\n",
 | 
			
		||||
				i + 1, frame->offset, frame->length,
 | 
			
		||||
				frame->is_complete);
 | 
			
		||||
 | 
			
		||||
			m_llc.append_frame(data + frame->offset, frame->length);
 | 
			
		||||
			m_llc.consume(frame->length);
 | 
			
		||||
		}
 | 
			
		||||
		if (!m_llc.fits_in_current_frame(chunk)) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "%s LLC frame exceeds "
 | 
			
		||||
				"maximum size %u.\n", tbf_name(this),
 | 
			
		||||
				m_llc.remaining_space());
 | 
			
		||||
			chunk = m_llc.remaining_space();
 | 
			
		||||
		}
 | 
			
		||||
		m_llc.append_frame(data + frame_offset[i], chunk);
 | 
			
		||||
		m_llc.consume(chunk);
 | 
			
		||||
		/* not last frame. */
 | 
			
		||||
		if (i != frames - 1) {
 | 
			
		||||
 | 
			
		||||
		if (frame->is_complete) {
 | 
			
		||||
			/* send frame to SGSN */
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n",
 | 
			
		||||
				tbf_name(this) , m_llc.frame_length());
 | 
			
		||||
			snd_ul_ud();
 | 
			
		||||
			m_llc.reset();
 | 
			
		||||
		/* also check if CV==0, because the frame may fill up the
 | 
			
		||||
		 * block precisely, then it is also complete. normally the
 | 
			
		||||
		 * frame would be extended into the next block with a 0-length
 | 
			
		||||
		 * delimiter added to this block. */
 | 
			
		||||
		} else if (rh->cv == 0) {
 | 
			
		||||
			/* send frame to SGSN */
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame "
 | 
			
		||||
				"that fits precisely in last block: "
 | 
			
		||||
				"len=%d\n", tbf_name(this), m_llc.frame_length());
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG, "complete UL frame len=%d\n", m_llc.frame_length());
 | 
			
		||||
			snd_ul_ud();
 | 
			
		||||
			bts->llc_ul_bytes(m_llc.frame_length());
 | 
			
		||||
			m_llc.reset();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -209,73 +103,87 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool gprs_rlcmac_ul_tbf::ctrl_ack_to_toggle()
 | 
			
		||||
{
 | 
			
		||||
	if (check_n_clear(GPRS_RLCMAC_FLAG_TO_UL_ACK))
 | 
			
		||||
		return true; /* GPRS_RLCMAC_FLAG_TO_UL_ACK was set, now cleared */
 | 
			
		||||
 | 
			
		||||
struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn)
 | 
			
		||||
	state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK);
 | 
			
		||||
	return false; /* GPRS_RLCMAC_FLAG_TO_UL_ACK was unset, now set */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool gprs_rlcmac_ul_tbf::handle_ctrl_ack()
 | 
			
		||||
{
 | 
			
		||||
	/* check if this control ack belongs to packet uplink ack */
 | 
			
		||||
	if (ul_ack_state_is(GPRS_RLCMAC_UL_ACK_WAIT_ACK)) {
 | 
			
		||||
		TBF_SET_ACK_STATE(this, GPRS_RLCMAC_UL_ACK_NONE);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn, uint8_t ts)
 | 
			
		||||
{
 | 
			
		||||
	int final = (state_is(GPRS_RLCMAC_FINISHED));
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	int rc;
 | 
			
		||||
	unsigned int rrbp = 0;
 | 
			
		||||
	uint32_t new_poll_fn = 0;
 | 
			
		||||
 | 
			
		||||
	if (final) {
 | 
			
		||||
		if (poll_state != GPRS_RLCMAC_POLL_NONE) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
 | 
			
		||||
				"sheduled for %s, so we must wait for "
 | 
			
		||||
				"final uplink ack...\n", tbf_name(this));
 | 
			
		||||
		if (poll_scheduled() && ul_ack_state_is(GPRS_RLCMAC_UL_ACK_WAIT_ACK)) {
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
				  "Polling is already scheduled, so we must wait for the final uplink ack...\n");
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
		if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
 | 
			
		||||
				"scheduled for single block allocation...\n");
 | 
			
		||||
 | 
			
		||||
		rc = check_polling(fn, ts, &new_poll_fn, &rrbp);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	msg = msgb_alloc(23, "rlcmac_ul_ack");
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	bitvec *ack_vec = bitvec_alloc(23);
 | 
			
		||||
	bitvec *ack_vec = bitvec_alloc(23, tall_pcu_ctx);
 | 
			
		||||
	if (!ack_vec) {
 | 
			
		||||
		msgb_free(msg);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	bitvec_unhex(ack_vec,
 | 
			
		||||
		"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
 | 
			
		||||
	RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
 | 
			
		||||
	Encoding::write_packet_uplink_ack(bts_data(), mac_control_block, this, final);
 | 
			
		||||
	encode_gsm_rlcmac_downlink(ack_vec, mac_control_block);
 | 
			
		||||
	Encoding::write_packet_uplink_ack(bts_data(), ack_vec, this, final, rrbp);
 | 
			
		||||
	bitvec_pack(ack_vec, msgb_put(msg, 23));
 | 
			
		||||
	bitvec_free(ack_vec);
 | 
			
		||||
	talloc_free(mac_control_block);
 | 
			
		||||
 | 
			
		||||
	/* now we must set this flag, so we are allowed to assign downlink
 | 
			
		||||
	 * TBF on PACCH. it is only allowed when TLLI is acknowledged. */
 | 
			
		||||
	m_contention_resolution_done = 1;
 | 
			
		||||
 | 
			
		||||
	if (final) {
 | 
			
		||||
		poll_state = GPRS_RLCMAC_POLL_SCHED;
 | 
			
		||||
		poll_fn = (fn + 13) % 2715648;
 | 
			
		||||
		set_polling(new_poll_fn, ts, GPRS_RLCMAC_POLL_UL_ACK);
 | 
			
		||||
		/* waiting for final acknowledge */
 | 
			
		||||
		ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK;
 | 
			
		||||
		m_final_ack_sent = 1;
 | 
			
		||||
	} else
 | 
			
		||||
		ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
 | 
			
		||||
		TBF_SET_ACK_STATE(this, GPRS_RLCMAC_UL_ACK_NONE);
 | 
			
		||||
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data,
 | 
			
		||||
	size_t len, struct pcu_l1_meas *meas)
 | 
			
		||||
/*! \brief receive data from PDCH/L1 */
 | 
			
		||||
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
 | 
			
		||||
	const struct gprs_rlc_data_info *rlc,
 | 
			
		||||
	uint8_t *data, struct pcu_l1_meas *meas)
 | 
			
		||||
{
 | 
			
		||||
	struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int8_t rssi = meas->have_rssi ? meas->rssi : 0;
 | 
			
		||||
 | 
			
		||||
	const uint16_t mod_sns = m_window.mod_sns();
 | 
			
		||||
	const uint16_t ws = m_window.ws();
 | 
			
		||||
 | 
			
		||||
	this->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA);
 | 
			
		||||
 | 
			
		||||
	LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. "
 | 
			
		||||
		"V(R)=%d)\n", rh->tfi, this->m_window.v_q(),
 | 
			
		||||
	LOGPTBFUL(this, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. "
 | 
			
		||||
		"V(R)=%d)\n", rlc->tfi, this->m_window.v_q(),
 | 
			
		||||
		this->m_window.v_r());
 | 
			
		||||
 | 
			
		||||
	/* process RSSI */
 | 
			
		||||
@@ -285,113 +193,190 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data,
 | 
			
		||||
	if (ms())
 | 
			
		||||
		ms()->update_l1_meas(meas);
 | 
			
		||||
 | 
			
		||||
	/* get TLLI */
 | 
			
		||||
	if (!this->is_tlli_valid()) {
 | 
			
		||||
		if (!extract_tlli(data, len))
 | 
			
		||||
			return 0;
 | 
			
		||||
	/* already have TLLI, but we stille get another one */
 | 
			
		||||
	} else if (rh->ti) {
 | 
			
		||||
		uint32_t tlli;
 | 
			
		||||
		rc = Decoding::tlli_from_ul_data(data, len, &tlli);
 | 
			
		||||
		if (rc) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI "
 | 
			
		||||
				"of UL DATA TFI=%d.\n", rh->tfi);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		if (tlli != this->tlli()) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL "
 | 
			
		||||
				"DATA TFI=%d. (Ignoring due to contention "
 | 
			
		||||
				"resolution)\n", rh->tfi);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	uint32_t new_tlli = 0;
 | 
			
		||||
	unsigned int block_idx;
 | 
			
		||||
 | 
			
		||||
	/* restart T3169 */
 | 
			
		||||
	tbf_timer_start(this, 3169, bts_data()->t3169, 0);
 | 
			
		||||
	T_START(this, T3169, bts_data()->t3169, 0, "acked (data)", true);
 | 
			
		||||
 | 
			
		||||
	/* Increment RX-counter */
 | 
			
		||||
	this->m_rx_counter++;
 | 
			
		||||
	update_coding_scheme_counter_ul(rlc->cs);
 | 
			
		||||
	/* Loop over num_blocks */
 | 
			
		||||
	for (block_idx = 0; block_idx < rlc->num_data_blocks; block_idx++) {
 | 
			
		||||
		int num_chunks;
 | 
			
		||||
		uint8_t *rlc_data;
 | 
			
		||||
		const struct gprs_rlc_data_block_info *rdbi =
 | 
			
		||||
			&rlc->block_info[block_idx];
 | 
			
		||||
		bool need_rlc_data = false;
 | 
			
		||||
		struct gprs_rlc_data *block;
 | 
			
		||||
 | 
			
		||||
	if (!m_window.is_in_window(rh->bsn)) {
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window "
 | 
			
		||||
			"%d..%d (it's normal)\n", rh->bsn,
 | 
			
		||||
			m_window.v_q(),
 | 
			
		||||
			(m_window.v_q() + ws - 1) & mod_sns);
 | 
			
		||||
		maybe_schedule_uplink_acknack(rh);
 | 
			
		||||
		return 0;
 | 
			
		||||
		LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
			  "Got %s RLC data block: CV=%d, BSN=%d, SPB=%d, PI=%d, E=%d, TI=%d, bitoffs=%d\n",
 | 
			
		||||
			  rlc->cs.name(),
 | 
			
		||||
			  rdbi->cv, rdbi->bsn, rdbi->spb,
 | 
			
		||||
			  rdbi->pi, rdbi->e, rdbi->ti,
 | 
			
		||||
			  rlc->data_offs_bits[block_idx]);
 | 
			
		||||
 | 
			
		||||
		/* Check whether the block needs to be decoded */
 | 
			
		||||
 | 
			
		||||
		if (!m_window.is_in_window(rdbi->bsn)) {
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG, "BSN %d out of window %d..%d (it's normal)\n",
 | 
			
		||||
				  rdbi->bsn,
 | 
			
		||||
				  m_window.v_q(), m_window.mod_sns(m_window.v_q() + ws - 1));
 | 
			
		||||
		} else if (m_window.is_received(rdbi->bsn)) {
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
				  "BSN %d already received\n", rdbi->bsn);
 | 
			
		||||
		} else {
 | 
			
		||||
			need_rlc_data = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!is_tlli_valid()) {
 | 
			
		||||
			if (!rdbi->ti) {
 | 
			
		||||
				LOGPTBFUL(this, LOGL_NOTICE, "Missing TLLI within UL DATA.\n");
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			need_rlc_data = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!need_rlc_data)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* Store block and meta info to BSN buffer */
 | 
			
		||||
 | 
			
		||||
		LOGPTBFUL(this, LOGL_DEBUG, "BSN %d storing in window (%d..%d)\n",
 | 
			
		||||
			  rdbi->bsn, m_window.v_q(),
 | 
			
		||||
			  m_window.mod_sns(m_window.v_q() + ws - 1));
 | 
			
		||||
		block = m_rlc.block(rdbi->bsn);
 | 
			
		||||
		OSMO_ASSERT(rdbi->data_len <= sizeof(block->block));
 | 
			
		||||
		rlc_data = &(block->block[0]);
 | 
			
		||||
 | 
			
		||||
		if (rdbi->spb) {
 | 
			
		||||
			egprs_rlc_ul_reseg_bsn_state assemble_status;
 | 
			
		||||
 | 
			
		||||
			assemble_status = handle_egprs_ul_spb(rlc,
 | 
			
		||||
						block, data, block_idx);
 | 
			
		||||
 | 
			
		||||
			if (assemble_status != EGPRS_RESEG_DEFAULT)
 | 
			
		||||
				return 0;
 | 
			
		||||
		} else {
 | 
			
		||||
			block->block_info = *rdbi;
 | 
			
		||||
			block->cs_last = rlc->cs;
 | 
			
		||||
			block->len =
 | 
			
		||||
				Decoding::rlc_copy_to_aligned_buffer(rlc,
 | 
			
		||||
				block_idx, data, rlc_data);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
			  "data_length=%d, data=%s\n",
 | 
			
		||||
			  block->len, osmo_hexdump(rlc_data, block->len));
 | 
			
		||||
		/* Get/Handle TLLI */
 | 
			
		||||
		if (rdbi->ti) {
 | 
			
		||||
			num_chunks = Decoding::rlc_data_from_ul_data(
 | 
			
		||||
				rdbi, rlc->cs, rlc_data, NULL, 0, &new_tlli);
 | 
			
		||||
 | 
			
		||||
			if (num_chunks < 0) {
 | 
			
		||||
				bts->decode_error();
 | 
			
		||||
				LOGPTBFUL(this, LOGL_NOTICE,
 | 
			
		||||
					  "Failed to decode TLLI of %s UL DATA TFI=%d.\n",
 | 
			
		||||
					  rlc->cs.name(), rlc->tfi);
 | 
			
		||||
				m_window.invalidate_bsn(rdbi->bsn);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (!this->is_tlli_valid()) {
 | 
			
		||||
				if (!new_tlli) {
 | 
			
		||||
					LOGPTBFUL(this, LOGL_NOTICE,
 | 
			
		||||
						  "TLLI = 0 within UL DATA.\n");
 | 
			
		||||
					m_window.invalidate_bsn(rdbi->bsn);
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
				LOGPTBFUL(this, LOGL_INFO,
 | 
			
		||||
					  "Decoded premier TLLI=0x%08x of UL DATA TFI=%d.\n",
 | 
			
		||||
					  tlli(), rlc->tfi);
 | 
			
		||||
				set_tlli_from_ul(new_tlli);
 | 
			
		||||
			} else if (new_tlli && new_tlli != tlli()) {
 | 
			
		||||
				LOGPTBFUL(this, LOGL_NOTICE,
 | 
			
		||||
					  "TLLI mismatch on UL DATA TFI=%d. (Ignoring due to contention resolution)\n",
 | 
			
		||||
					  rlc->tfi);
 | 
			
		||||
				m_window.invalidate_bsn(rdbi->bsn);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m_window.receive_bsn(rdbi->bsn);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Write block to buffer and set receive state array. */
 | 
			
		||||
	m_rlc.block(rh->bsn)->put_data(data, len);
 | 
			
		||||
	LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n",
 | 
			
		||||
		rh->bsn, m_window.v_q(),
 | 
			
		||||
		(m_window.v_q() + ws - 1) & mod_sns);
 | 
			
		||||
 | 
			
		||||
	/* Raise V(Q) if possible, and retrieve LLC frames from blocks.
 | 
			
		||||
	 * This is looped until there is a gap (non received block) or
 | 
			
		||||
	 * the window is empty.*/
 | 
			
		||||
	const uint16_t v_q_beg = m_window.v_q();
 | 
			
		||||
 | 
			
		||||
	const uint16_t count = m_window.receive_bsn(rh->bsn);
 | 
			
		||||
	const uint16_t count = m_window.raise_v_q();
 | 
			
		||||
 | 
			
		||||
	/* Retrieve LLC frames from blocks that are ready */
 | 
			
		||||
	for (uint16_t i = 0; i < count; ++i) {
 | 
			
		||||
		uint16_t index = (v_q_beg + i) & mod_sns;
 | 
			
		||||
		uint16_t index = m_window.mod_sns(v_q_beg + i);
 | 
			
		||||
		assemble_forward_llc(m_rlc.block(index));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check CV of last frame in buffer */
 | 
			
		||||
	if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */
 | 
			
		||||
	 && this->m_window.v_q() == this->m_window.v_r()) { /* if complete */
 | 
			
		||||
		struct rlc_ul_header *last_rh = (struct rlc_ul_header *)
 | 
			
		||||
			m_rlc.block((m_window.v_r() - 1) & mod_sns)->block;
 | 
			
		||||
		LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, "
 | 
			
		||||
			"last block: BSN=%d CV=%d\n", last_rh->bsn,
 | 
			
		||||
			last_rh->cv);
 | 
			
		||||
		if (last_rh->cv == 0) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL "
 | 
			
		||||
				"TBF\n");
 | 
			
		||||
			set_state(GPRS_RLCMAC_FINISHED);
 | 
			
		||||
		struct gprs_rlc_data *block =
 | 
			
		||||
			m_rlc.block(m_window.mod_sns(m_window.v_r() - 1));
 | 
			
		||||
		const struct gprs_rlc_data_block_info *rdbi =
 | 
			
		||||
			&block->block_info;
 | 
			
		||||
 | 
			
		||||
		LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
			  "No gaps in received block, last block: BSN=%d CV=%d\n",
 | 
			
		||||
			  rdbi->bsn, rdbi->cv);
 | 
			
		||||
		if (rdbi->cv == 0) {
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG, "Finished with UL TBF\n");
 | 
			
		||||
			TBF_SET_STATE(this, GPRS_RLCMAC_FINISHED);
 | 
			
		||||
			/* Reset N3103 counter. */
 | 
			
		||||
			this->m_n3103 = 0;
 | 
			
		||||
			this->n_reset(N3103);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* If TLLI is included or if we received half of the window, we send
 | 
			
		||||
	 * an ack/nack */
 | 
			
		||||
	maybe_schedule_uplink_acknack(rh);
 | 
			
		||||
	maybe_schedule_uplink_acknack(rlc);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh)
 | 
			
		||||
void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(
 | 
			
		||||
	const gprs_rlc_data_info *rlc)
 | 
			
		||||
{
 | 
			
		||||
	if (rh->si || rh->ti || state_is(GPRS_RLCMAC_FINISHED)
 | 
			
		||||
	 || (m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
 | 
			
		||||
		if (rh->si) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
 | 
			
		||||
				"because MS is stalled.\n");
 | 
			
		||||
	bool have_ti = rlc->block_info[0].ti ||
 | 
			
		||||
		(rlc->num_data_blocks > 1 && rlc->block_info[1].ti);
 | 
			
		||||
 | 
			
		||||
	if (rlc->si || have_ti || state_is(GPRS_RLCMAC_FINISHED) ||
 | 
			
		||||
		(m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0)
 | 
			
		||||
	{
 | 
			
		||||
		if (rlc->si) {
 | 
			
		||||
			LOGPTBFUL(this, LOGL_NOTICE,
 | 
			
		||||
				  "Scheduling Ack/Nack, because MS is stalled.\n");
 | 
			
		||||
		}
 | 
			
		||||
		if (rh->ti) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
 | 
			
		||||
				"because TLLI is included.\n");
 | 
			
		||||
		if (have_ti) {
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
				  "Scheduling Ack/Nack, because TLLI is included.\n");
 | 
			
		||||
		}
 | 
			
		||||
		if (state_is(GPRS_RLCMAC_FINISHED)) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
 | 
			
		||||
				"because last block has CV==0.\n");
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
				  "Scheduling Ack/Nack, because last block has CV==0.\n");
 | 
			
		||||
		}
 | 
			
		||||
		if ((m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
 | 
			
		||||
				"because %d frames received.\n",
 | 
			
		||||
				SEND_ACK_AFTER_FRAMES);
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
				  "Scheduling Ack/Nack, because %d frames received.\n",
 | 
			
		||||
				  SEND_ACK_AFTER_FRAMES);
 | 
			
		||||
		}
 | 
			
		||||
		if (ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) {
 | 
			
		||||
		if (ul_ack_state_is(GPRS_RLCMAC_UL_ACK_NONE)) {
 | 
			
		||||
			/* trigger sending at next RTS */
 | 
			
		||||
			ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK;
 | 
			
		||||
			TBF_SET_ACK_STATE(this, GPRS_RLCMAC_UL_ACK_SEND_ACK);
 | 
			
		||||
		} else {
 | 
			
		||||
			/* already triggered */
 | 
			
		||||
			LOGP(DRLCMACUL, LOGL_DEBUG, "-  Sending Ack/Nack is "
 | 
			
		||||
				"already triggered, don't schedule!\n");
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
				  "Sending Ack/Nack is already triggered, don't schedule!\n");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -423,3 +408,193 @@ int gprs_rlcmac_ul_tbf::snd_ul_ud()
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_second_seg(
 | 
			
		||||
	const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
 | 
			
		||||
	uint8_t *data, const uint8_t block_idx)
 | 
			
		||||
{
 | 
			
		||||
	const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
 | 
			
		||||
	union split_block_status *spb_status = &block->spb_status;
 | 
			
		||||
	uint8_t *rlc_data = &block->block[0];
 | 
			
		||||
 | 
			
		||||
        bts->spb_uplink_second_segment();
 | 
			
		||||
 | 
			
		||||
	if (spb_status->block_status_ul &
 | 
			
		||||
				EGPRS_RESEG_FIRST_SEG_RXD) {
 | 
			
		||||
		LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
			  "Second seg is received first seg is already present set the status to complete\n");
 | 
			
		||||
		spb_status->block_status_ul = EGPRS_RESEG_DEFAULT;
 | 
			
		||||
 | 
			
		||||
		block->len += Decoding::rlc_copy_to_aligned_buffer(rlc,
 | 
			
		||||
			block_idx, data, rlc_data + block->len);
 | 
			
		||||
		block->block_info.data_len += rdbi->data_len;
 | 
			
		||||
	} else if (spb_status->block_status_ul == EGPRS_RESEG_DEFAULT) {
 | 
			
		||||
		LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
			  "Second seg is received first seg is not received set the status to second seg received\n");
 | 
			
		||||
 | 
			
		||||
		block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
 | 
			
		||||
				block_idx, data,
 | 
			
		||||
				rlc_data + rlc->block_info[block_idx].data_len);
 | 
			
		||||
 | 
			
		||||
		spb_status->block_status_ul = EGPRS_RESEG_SECOND_SEG_RXD;
 | 
			
		||||
		block->block_info = *rdbi;
 | 
			
		||||
	}
 | 
			
		||||
	return spb_status->block_status_ul;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_first_seg(
 | 
			
		||||
	const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
 | 
			
		||||
	uint8_t *data, const uint8_t block_idx)
 | 
			
		||||
{
 | 
			
		||||
	const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
 | 
			
		||||
	uint8_t *rlc_data = &block->block[0];
 | 
			
		||||
	union split_block_status *spb_status = &block->spb_status;
 | 
			
		||||
 | 
			
		||||
	bts->spb_uplink_first_segment();
 | 
			
		||||
 | 
			
		||||
	if (spb_status->block_status_ul & EGPRS_RESEG_SECOND_SEG_RXD) {
 | 
			
		||||
		LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
			  "First seg is received second seg is already present set the status to complete\n");
 | 
			
		||||
 | 
			
		||||
		block->len += Decoding::rlc_copy_to_aligned_buffer(rlc,
 | 
			
		||||
				block_idx, data, rlc_data);
 | 
			
		||||
 | 
			
		||||
		block->block_info.data_len = block->len;
 | 
			
		||||
		spb_status->block_status_ul = EGPRS_RESEG_DEFAULT;
 | 
			
		||||
	} else if (spb_status->block_status_ul == EGPRS_RESEG_DEFAULT) {
 | 
			
		||||
		LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
			  "First seg is received second seg is not received set the status to first seg received\n");
 | 
			
		||||
 | 
			
		||||
		spb_status->block_status_ul = EGPRS_RESEG_FIRST_SEG_RXD;
 | 
			
		||||
		block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
 | 
			
		||||
					block_idx, data, rlc_data);
 | 
			
		||||
		block->block_info = *rdbi;
 | 
			
		||||
	}
 | 
			
		||||
	return spb_status->block_status_ul;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_spb(
 | 
			
		||||
	const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
 | 
			
		||||
	uint8_t *data, const uint8_t block_idx)
 | 
			
		||||
{
 | 
			
		||||
	const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
 | 
			
		||||
 | 
			
		||||
	LOGPTBFUL(this, LOGL_DEBUG,
 | 
			
		||||
		  "Got SPB(%d) cs(%s) data block with BSN (%d), TFI(%d).\n",
 | 
			
		||||
		  rdbi->spb,  rlc->cs.name(), rdbi->bsn, rlc->tfi);
 | 
			
		||||
 | 
			
		||||
	egprs_rlc_ul_reseg_bsn_state assemble_status = EGPRS_RESEG_INVALID;
 | 
			
		||||
 | 
			
		||||
	/* Section 10.4.8b of 44.060*/
 | 
			
		||||
	if (rdbi->spb == 2)
 | 
			
		||||
		assemble_status = handle_egprs_ul_first_seg(rlc,
 | 
			
		||||
						block, data, block_idx);
 | 
			
		||||
	else if (rdbi->spb == 3)
 | 
			
		||||
		assemble_status = handle_egprs_ul_second_seg(rlc,
 | 
			
		||||
						block, data, block_idx);
 | 
			
		||||
	else {
 | 
			
		||||
		LOGPTBFUL(this, LOGL_ERROR,
 | 
			
		||||
			  "spb(%d) Not supported SPB for this EGPRS configuration\n",
 | 
			
		||||
			  rdbi->spb);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * When the block is successfully constructed out of segmented blocks
 | 
			
		||||
	 * upgrade the MCS to the type 2
 | 
			
		||||
	 */
 | 
			
		||||
	if (assemble_status == EGPRS_RESEG_DEFAULT) {
 | 
			
		||||
		switch (GprsCodingScheme::Scheme(rlc->cs)) {
 | 
			
		||||
		case GprsCodingScheme::MCS3 :
 | 
			
		||||
			block->cs_last = GprsCodingScheme::MCS6;
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG, "Upgrading to MCS6\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::MCS2 :
 | 
			
		||||
			block->cs_last = GprsCodingScheme::MCS5;
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG, "Upgrading to MCS5\n");
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::MCS1 :
 | 
			
		||||
			LOGPTBFUL(this, LOGL_DEBUG, "Upgrading to MCS4\n");
 | 
			
		||||
			block->cs_last = GprsCodingScheme::MCS4;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			LOGPTBFUL(this, LOGL_ERROR,
 | 
			
		||||
				  "cs(%s) Error in Upgrading to higher MCS\n",
 | 
			
		||||
				  rlc->cs.name());
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return assemble_status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_ul_tbf::update_coding_scheme_counter_ul(const GprsCodingScheme cs)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t coding_scheme = 0;
 | 
			
		||||
 | 
			
		||||
	coding_scheme = GprsCodingScheme::Scheme(cs);
 | 
			
		||||
	if (cs.isGprs()) {
 | 
			
		||||
		switch (coding_scheme) {
 | 
			
		||||
		case GprsCodingScheme::CS1 :
 | 
			
		||||
			bts->gprs_ul_cs1();
 | 
			
		||||
			rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS1]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::CS2 :
 | 
			
		||||
			bts->gprs_ul_cs2();
 | 
			
		||||
			rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS2]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::CS3 :
 | 
			
		||||
			bts->gprs_ul_cs3();
 | 
			
		||||
			rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS3]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::CS4 :
 | 
			
		||||
			bts->gprs_ul_cs4();
 | 
			
		||||
			rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS4]);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		switch (coding_scheme) {
 | 
			
		||||
		case GprsCodingScheme::MCS1 :
 | 
			
		||||
			bts->egprs_ul_mcs1();
 | 
			
		||||
			rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS1]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::MCS2 :
 | 
			
		||||
			bts->egprs_ul_mcs2();
 | 
			
		||||
			rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS2]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::MCS3 :
 | 
			
		||||
			bts->egprs_ul_mcs3();
 | 
			
		||||
			rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS3]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::MCS4 :
 | 
			
		||||
			bts->egprs_ul_mcs4();
 | 
			
		||||
			rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS4]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::MCS5 :
 | 
			
		||||
			bts->egprs_ul_mcs5();
 | 
			
		||||
			rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS5]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::MCS6 :
 | 
			
		||||
			bts->egprs_ul_mcs6();
 | 
			
		||||
			rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS6]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::MCS7 :
 | 
			
		||||
			bts->egprs_ul_mcs7();
 | 
			
		||||
			rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS7]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::MCS8 :
 | 
			
		||||
			bts->egprs_ul_mcs8();
 | 
			
		||||
			rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS8]);
 | 
			
		||||
			break;
 | 
			
		||||
		case GprsCodingScheme::MCS9 :
 | 
			
		||||
			bts->egprs_ul_mcs9();
 | 
			
		||||
			rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS9]);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gprs_rlcmac_ul_tbf::set_window_size()
 | 
			
		||||
{
 | 
			
		||||
	uint16_t ws = egprs_window_size(bts->bts_data(), ul_slots());
 | 
			
		||||
	LOGPTBFUL(this, LOGL_INFO, "setting EGPRS UL window size to %u, base(%u) slots(%u) ws_pdch(%u)\n",
 | 
			
		||||
		  ws, bts->bts_data()->ws_base, pcu_bitcount(ul_slots()), bts->bts_data()->ws_pdch);
 | 
			
		||||
	m_window.set_ws(ws);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/ -I$(top_srcdir)/include/
 | 
			
		||||
AM_LDFLAGS = -lrt
 | 
			
		||||
 | 
			
		||||
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test
 | 
			
		||||
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest alloc/MslotTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test edge/EdgeTest bitcomp/BitcompTest fn/FnTest
 | 
			
		||||
noinst_PROGRAMS = emu/pcu_emu
 | 
			
		||||
 | 
			
		||||
rlcmac_RLCMACTest_SOURCES = rlcmac/RLCMACTest.cpp
 | 
			
		||||
@@ -18,6 +18,14 @@ alloc_AllocTest_LDADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
alloc_MslotTest_SOURCES = alloc/MslotTest.cpp
 | 
			
		||||
alloc_MslotTest_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libgprs.la \
 | 
			
		||||
	$(LIBOSMOGB_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
tbf_TbfTest_SOURCES = tbf/TbfTest.cpp
 | 
			
		||||
tbf_TbfTest_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libgprs.la \
 | 
			
		||||
@@ -25,6 +33,21 @@ tbf_TbfTest_LDADD = \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
tbf_TbfTest_LDFLAGS = -Wl,--wrap=pcu_sock_send
 | 
			
		||||
 | 
			
		||||
bitcomp_BitcompTest_SOURCES = bitcomp/BitcompTest.cpp ../src/egprs_rlc_compression.cpp
 | 
			
		||||
bitcomp_BitcompTest_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libgprs.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
edge_EdgeTest_SOURCES = edge/EdgeTest.cpp
 | 
			
		||||
edge_EdgeTest_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libgprs.la \
 | 
			
		||||
	$(LIBOSMOGB_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
emu_pcu_emu_SOURCES = emu/pcu_emu.cpp emu/test_replay_gprs_attach.cpp \
 | 
			
		||||
	emu/openbsc_clone.c emu/openbsc_clone.h emu/gprs_tests.h \
 | 
			
		||||
@@ -77,6 +100,14 @@ codel_codel_test_LDADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
fn_FnTest_SOURCES = fn/FnTest.cpp
 | 
			
		||||
fn_FnTest_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libgprs.la \
 | 
			
		||||
	$(LIBOSMOGB_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
 | 
			
		||||
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
 | 
			
		||||
	:;{ \
 | 
			
		||||
@@ -100,11 +131,14 @@ EXTRA_DIST = \
 | 
			
		||||
	rlcmac/RLCMACTest.ok rlcmac/RLCMACTest.err \
 | 
			
		||||
	alloc/AllocTest.ok alloc/AllocTest.err \
 | 
			
		||||
	tbf/TbfTest.ok tbf/TbfTest.err \
 | 
			
		||||
	bitcomp/BitcompTest.ok bitcomp/BitcompTest.err \
 | 
			
		||||
	types/TypesTest.ok types/TypesTest.err \
 | 
			
		||||
	ms/MsTest.ok ms/MsTest.err \
 | 
			
		||||
	ms/MsTest.ok ms/MsTest.err alloc/MslotTest.ok \
 | 
			
		||||
	llc/LlcTest.ok llc/LlcTest.err \
 | 
			
		||||
	llist/LListTest.ok llist/LListTest.err \
 | 
			
		||||
	codel/codel_test.ok
 | 
			
		||||
	codel/codel_test.ok \
 | 
			
		||||
	edge/EdgeTest.ok \
 | 
			
		||||
	fn/FnTest.ok
 | 
			
		||||
 | 
			
		||||
DISTCLEANFILES = atconfig
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "mslot_class.h"
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
@@ -35,38 +36,41 @@ extern "C" {
 | 
			
		||||
/* globals used by the code */
 | 
			
		||||
void *tall_pcu_ctx;
 | 
			
		||||
int16_t spoof_mnc = 0, spoof_mcc = 0;
 | 
			
		||||
bool spoof_mnc_3_digits = false;
 | 
			
		||||
 | 
			
		||||
static gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
		GprsMs *ms, gprs_rlcmac_tbf_direction dir,
 | 
			
		||||
		uint8_t use_trx,
 | 
			
		||||
		uint8_t ms_class, uint8_t single_slot)
 | 
			
		||||
		uint8_t ms_class, uint8_t egprs_ms_class, bool single_slot)
 | 
			
		||||
{
 | 
			
		||||
	if (dir == GPRS_RLCMAC_UL_TBF)
 | 
			
		||||
		return tbf_alloc_ul_tbf(bts, ms, use_trx, ms_class, single_slot);
 | 
			
		||||
		return tbf_alloc_ul_tbf(bts, ms, use_trx,
 | 
			
		||||
			ms_class, egprs_ms_class, single_slot);
 | 
			
		||||
	else
 | 
			
		||||
		return tbf_alloc_dl_tbf(bts, ms, use_trx, ms_class, single_slot);
 | 
			
		||||
		return tbf_alloc_dl_tbf(bts, ms, use_trx,
 | 
			
		||||
			ms_class, egprs_ms_class, single_slot);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void check_tfi_usage(BTS *the_bts)
 | 
			
		||||
{
 | 
			
		||||
	int pdch_no;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = the_bts->bts_data();
 | 
			
		||||
 | 
			
		||||
	struct gprs_rlcmac_tbf *tfi_usage[8][8][2][32] = {{{{NULL}}}};
 | 
			
		||||
	struct llist_head *tbf_lists[2] = {
 | 
			
		||||
		&bts->ul_tbfs,
 | 
			
		||||
		&bts->dl_tbfs
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> *tbf_lists[2] = {
 | 
			
		||||
		&the_bts->ul_tbfs(),
 | 
			
		||||
		&the_bts->dl_tbfs()
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	LListHead<gprs_rlcmac_tbf> *pos;
 | 
			
		||||
	gprs_rlcmac_tbf *tbf;
 | 
			
		||||
	struct llist_pods *lpods;
 | 
			
		||||
	unsigned list_idx;
 | 
			
		||||
	struct gprs_rlcmac_tbf **tbf_var;
 | 
			
		||||
 | 
			
		||||
	for (list_idx = 0; list_idx < ARRAY_SIZE(tbf_lists); list_idx += 1)
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
		llist_pods_for_each_entry(tbf, tbf_lists[list_idx], list, lpods) {
 | 
			
		||||
		llist_for_each(pos, tbf_lists[list_idx]) {
 | 
			
		||||
			tbf = pos->entry();
 | 
			
		||||
			for (pdch_no = 0; pdch_no < 8; pdch_no += 1) {
 | 
			
		||||
				struct gprs_rlcmac_pdch *pdch = tbf->pdch[pdch_no];
 | 
			
		||||
				if (pdch == NULL)
 | 
			
		||||
@@ -131,7 +135,7 @@ static void test_alloc_a(gprs_rlcmac_tbf_direction dir,
 | 
			
		||||
	 * least this part is working okay.
 | 
			
		||||
	 */
 | 
			
		||||
	for (i = 0; i < (int)ARRAY_SIZE(tbfs); ++i) {
 | 
			
		||||
		tbfs[i] = tbf_alloc(bts, NULL, dir, -1, 0, 0);
 | 
			
		||||
		tbfs[i] = tbf_alloc(bts, NULL, dir, -1, 0, 0, 0);
 | 
			
		||||
		if (tbfs[i] == NULL)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
@@ -148,9 +152,9 @@ static void test_alloc_a(gprs_rlcmac_tbf_direction dir,
 | 
			
		||||
		if (tbfs[i])
 | 
			
		||||
			tbf_free(tbfs[i]);
 | 
			
		||||
 | 
			
		||||
	tbfs[tfi] = tbf_alloc(bts, NULL, dir, -1, 0, 0);
 | 
			
		||||
	OSMO_ASSERT(tbfs[tfi]);
 | 
			
		||||
	tbf_free(tbfs[tfi]);
 | 
			
		||||
	tbfs[0] = tbf_alloc(bts, NULL, dir, -1, 0, 0, 0);
 | 
			
		||||
	OSMO_ASSERT(tbfs[0]);
 | 
			
		||||
	tbf_free(tbfs[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_alloc_a()
 | 
			
		||||
@@ -164,17 +168,177 @@ static void test_alloc_a()
 | 
			
		||||
	test_alloc_a(GPRS_RLCMAC_UL_TBF, 0x1e, 28);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dump_assignment(struct gprs_rlcmac_tbf *tbf, const char *dir)
 | 
			
		||||
static void dump_assignment(struct gprs_rlcmac_tbf *tbf, const char *dir, bool verbose)
 | 
			
		||||
{
 | 
			
		||||
	if (!verbose)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i < ARRAY_SIZE(tbf->pdch); ++i)
 | 
			
		||||
		if (tbf->pdch[i])
 | 
			
		||||
			printf("PDCH[%d] is used for %s\n", i, dir);
 | 
			
		||||
			printf("PDCH[%zu] is used for %s\n", i, dir);
 | 
			
		||||
	printf("PDCH[%d] is control_ts for %s\n", tbf->control_ts, dir);
 | 
			
		||||
	printf("PDCH[%d] is first common for %s\n", tbf->first_common_ts, dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_alloc_b(int ms_class)
 | 
			
		||||
#define ENABLE_PDCH(ts_no, enable_flag, trx)	\
 | 
			
		||||
		if (enable_flag)		\
 | 
			
		||||
			trx->pdch[ts_no].enable();
 | 
			
		||||
 | 
			
		||||
static inline void enable_ts_on_bts(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
				    bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_rlcmac_trx *trx = &bts->trx[0];
 | 
			
		||||
 | 
			
		||||
	ENABLE_PDCH(0, ts0, trx);
 | 
			
		||||
	ENABLE_PDCH(1, ts1, trx);
 | 
			
		||||
	ENABLE_PDCH(2, ts2, trx);
 | 
			
		||||
	ENABLE_PDCH(3, ts3, trx);
 | 
			
		||||
	ENABLE_PDCH(4, ts4, trx);
 | 
			
		||||
	ENABLE_PDCH(5, ts5, trx);
 | 
			
		||||
	ENABLE_PDCH(6, ts6, trx);
 | 
			
		||||
	ENABLE_PDCH(7, ts7, trx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool test_alloc_b_ul_dl(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7,
 | 
			
		||||
				      uint8_t ms_class, bool verbose)
 | 
			
		||||
{
 | 
			
		||||
	BTS the_bts;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = the_bts.bts_data();
 | 
			
		||||
	gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
	gprs_rlcmac_dl_tbf *dl_tbf;
 | 
			
		||||
 | 
			
		||||
	if (verbose)
 | 
			
		||||
		printf("Testing UL then DL assignment.\n");
 | 
			
		||||
 | 
			
		||||
	bts->alloc_algorithm = alloc_algorithm_b;
 | 
			
		||||
 | 
			
		||||
	enable_ts_on_bts(bts, ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7);
 | 
			
		||||
 | 
			
		||||
	ul_tbf = tbf_alloc_ul_tbf(bts, NULL, -1, ms_class, 0, true);
 | 
			
		||||
	if (!ul_tbf)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ul_tbf->ms());
 | 
			
		||||
	OSMO_ASSERT(ul_tbf->ms()->current_trx());
 | 
			
		||||
 | 
			
		||||
	dump_assignment(ul_tbf, "UL", verbose);
 | 
			
		||||
 | 
			
		||||
	/* assume final ack has not been sent */
 | 
			
		||||
	dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), ul_tbf->ms()->current_trx()->trx_no, ms_class, 0,
 | 
			
		||||
				  false);
 | 
			
		||||
	if (!dl_tbf)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	dump_assignment(dl_tbf, "DL", verbose);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
	check_tfi_usage(&the_bts);
 | 
			
		||||
 | 
			
		||||
	tbf_free(dl_tbf);
 | 
			
		||||
	tbf_free(ul_tbf);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool test_alloc_b_dl_ul(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7,
 | 
			
		||||
				      uint8_t ms_class, bool verbose)
 | 
			
		||||
{
 | 
			
		||||
	BTS the_bts;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = the_bts.bts_data();
 | 
			
		||||
	gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
	gprs_rlcmac_dl_tbf *dl_tbf;
 | 
			
		||||
 | 
			
		||||
	if (verbose)
 | 
			
		||||
		printf("Testing DL then UL assignment followed by update\n");
 | 
			
		||||
 | 
			
		||||
	bts->alloc_algorithm = alloc_algorithm_b;
 | 
			
		||||
 | 
			
		||||
	enable_ts_on_bts(bts, ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7);
 | 
			
		||||
 | 
			
		||||
	dl_tbf = tbf_alloc_dl_tbf(bts, NULL, -1, ms_class, 0, true);
 | 
			
		||||
	if (!dl_tbf)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	dl_tbf->update_ms(0x23, GPRS_RLCMAC_DL_TBF);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(dl_tbf->ms());
 | 
			
		||||
	OSMO_ASSERT(dl_tbf->ms()->current_trx());
 | 
			
		||||
 | 
			
		||||
	dump_assignment(dl_tbf, "DL", verbose);
 | 
			
		||||
 | 
			
		||||
	ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), dl_tbf->ms()->current_trx()->trx_no, ms_class, 0,
 | 
			
		||||
				  false);
 | 
			
		||||
	if (!ul_tbf)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
 | 
			
		||||
	ul_tbf->m_contention_resolution_done = 1;
 | 
			
		||||
 | 
			
		||||
	dump_assignment(ul_tbf, "UL", verbose);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
	/* now update the dl_tbf */
 | 
			
		||||
	dl_tbf->update();
 | 
			
		||||
	dump_assignment(dl_tbf, "DL", verbose);
 | 
			
		||||
	OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
	check_tfi_usage(&the_bts);
 | 
			
		||||
 | 
			
		||||
	tbf_free(dl_tbf);
 | 
			
		||||
	tbf_free(ul_tbf);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool test_alloc_b_jolly(uint8_t ms_class)
 | 
			
		||||
{
 | 
			
		||||
	BTS the_bts;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts = the_bts.bts_data();
 | 
			
		||||
	int tfi;
 | 
			
		||||
	uint8_t trx_no;
 | 
			
		||||
	gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
 | 
			
		||||
 | 
			
		||||
	printf("Testing jolly example\n");
 | 
			
		||||
 | 
			
		||||
	bts->alloc_algorithm = alloc_algorithm_b;
 | 
			
		||||
 | 
			
		||||
	enable_ts_on_bts(bts, false, true, true, true, true, false, false, false);
 | 
			
		||||
 | 
			
		||||
	tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
 | 
			
		||||
	OSMO_ASSERT(tfi >= 0);
 | 
			
		||||
	ul_tbf = tbf_alloc_ul_tbf(bts, NULL, .1, ms_class, 0, false);
 | 
			
		||||
	if (!ul_tbf)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(ul_tbf->ms());
 | 
			
		||||
	OSMO_ASSERT(ul_tbf->ms()->current_trx());
 | 
			
		||||
	trx_no = ul_tbf->ms()->current_trx()->trx_no;
 | 
			
		||||
	dump_assignment(ul_tbf, "UL", true);
 | 
			
		||||
 | 
			
		||||
	/* assume final ack has not been sent */
 | 
			
		||||
	dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0,
 | 
			
		||||
				  false);
 | 
			
		||||
	if (!dl_tbf)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	dump_assignment(dl_tbf, "DL", true);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
	check_tfi_usage(&the_bts);
 | 
			
		||||
 | 
			
		||||
	tbf_free(dl_tbf);
 | 
			
		||||
	tbf_free(ul_tbf);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_alloc_b_for_ms(uint8_t ms_class)
 | 
			
		||||
{
 | 
			
		||||
	bool rc;
 | 
			
		||||
 | 
			
		||||
	printf("Going to test multislot assignment MS_CLASS=%d\n", ms_class);
 | 
			
		||||
	/*
 | 
			
		||||
	 * PDCH is on TS 6,7,8 and we start with a UL allocation and
 | 
			
		||||
@@ -182,143 +346,26 @@ static void test_alloc_b(int ms_class)
 | 
			
		||||
	 *
 | 
			
		||||
	 * Uplink assigned and still available..
 | 
			
		||||
	 */
 | 
			
		||||
	{
 | 
			
		||||
		BTS the_bts;
 | 
			
		||||
		struct gprs_rlcmac_bts *bts;
 | 
			
		||||
		struct gprs_rlcmac_trx *trx;
 | 
			
		||||
		uint8_t trx_no;
 | 
			
		||||
 | 
			
		||||
		gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
 | 
			
		||||
 | 
			
		||||
		printf("Testing UL then DL assignment.\n");
 | 
			
		||||
 | 
			
		||||
		bts = the_bts.bts_data();
 | 
			
		||||
		bts->alloc_algorithm = alloc_algorithm_b;
 | 
			
		||||
 | 
			
		||||
		trx = &bts->trx[0];
 | 
			
		||||
		trx->pdch[5].enable();
 | 
			
		||||
		trx->pdch[6].enable();
 | 
			
		||||
		trx->pdch[7].enable();
 | 
			
		||||
 | 
			
		||||
		ul_tbf = tbf_alloc_ul_tbf(bts, NULL, -1, ms_class, 1);
 | 
			
		||||
		OSMO_ASSERT(ul_tbf);
 | 
			
		||||
		OSMO_ASSERT(ul_tbf->ms());
 | 
			
		||||
		OSMO_ASSERT(ul_tbf->ms()->current_trx());
 | 
			
		||||
		trx_no = ul_tbf->ms()->current_trx()->trx_no;
 | 
			
		||||
		dump_assignment(ul_tbf, "UL");
 | 
			
		||||
 | 
			
		||||
		/* assume final ack has not been sent */
 | 
			
		||||
		dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0);
 | 
			
		||||
		OSMO_ASSERT(dl_tbf);
 | 
			
		||||
		dump_assignment(dl_tbf, "DL");
 | 
			
		||||
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
		check_tfi_usage(&the_bts);
 | 
			
		||||
 | 
			
		||||
		tbf_free(dl_tbf);
 | 
			
		||||
		tbf_free(ul_tbf);
 | 
			
		||||
	}
 | 
			
		||||
	rc = test_alloc_b_ul_dl(false, false, false, false, false, true, true, true, ms_class, true);
 | 
			
		||||
	if (!rc)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test with the other order.. first DL and then UL
 | 
			
		||||
	 */
 | 
			
		||||
	{
 | 
			
		||||
		BTS the_bts;
 | 
			
		||||
		struct gprs_rlcmac_bts *bts;
 | 
			
		||||
		struct gprs_rlcmac_trx *trx;
 | 
			
		||||
		uint8_t trx_no;
 | 
			
		||||
 | 
			
		||||
		gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
		gprs_rlcmac_dl_tbf *dl_tbf;
 | 
			
		||||
 | 
			
		||||
		printf("Testing DL then UL assignment followed by update\n");
 | 
			
		||||
 | 
			
		||||
		bts = the_bts.bts_data();
 | 
			
		||||
		bts->alloc_algorithm = alloc_algorithm_b;
 | 
			
		||||
 | 
			
		||||
		trx = &bts->trx[0];
 | 
			
		||||
		trx->pdch[5].enable();
 | 
			
		||||
		trx->pdch[6].enable();
 | 
			
		||||
		trx->pdch[7].enable();
 | 
			
		||||
 | 
			
		||||
		dl_tbf = tbf_alloc_dl_tbf(bts, NULL, -1, ms_class, 1);
 | 
			
		||||
		dl_tbf->update_ms(0x23, GPRS_RLCMAC_DL_TBF);
 | 
			
		||||
		OSMO_ASSERT(dl_tbf);
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->ms());
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->ms()->current_trx());
 | 
			
		||||
		trx_no = dl_tbf->ms()->current_trx()->trx_no;
 | 
			
		||||
		dump_assignment(dl_tbf, "DL");
 | 
			
		||||
 | 
			
		||||
		ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), trx_no, ms_class, 0);
 | 
			
		||||
		ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
 | 
			
		||||
		ul_tbf->m_contention_resolution_done = 1;
 | 
			
		||||
		OSMO_ASSERT(ul_tbf);
 | 
			
		||||
		dump_assignment(ul_tbf, "UL");
 | 
			
		||||
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
		/* now update the dl_tbf */
 | 
			
		||||
		dl_tbf->update();
 | 
			
		||||
		dump_assignment(dl_tbf, "DL");
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
		check_tfi_usage(&the_bts);
 | 
			
		||||
 | 
			
		||||
		tbf_free(dl_tbf);
 | 
			
		||||
		tbf_free(ul_tbf);
 | 
			
		||||
	}
 | 
			
		||||
	rc = test_alloc_b_dl_ul(false, false, false, false, false, true, true, true, ms_class, true);
 | 
			
		||||
	if (!rc)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Andreas osmocom-pcu example */
 | 
			
		||||
	{
 | 
			
		||||
		BTS the_bts;
 | 
			
		||||
		struct gprs_rlcmac_bts *bts;
 | 
			
		||||
		struct gprs_rlcmac_trx *trx;
 | 
			
		||||
		int tfi;
 | 
			
		||||
		uint8_t trx_no;
 | 
			
		||||
 | 
			
		||||
		gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
 | 
			
		||||
 | 
			
		||||
		printf("Testing jolly example\n");
 | 
			
		||||
 | 
			
		||||
		bts = the_bts.bts_data();
 | 
			
		||||
		bts->alloc_algorithm = alloc_algorithm_b;
 | 
			
		||||
 | 
			
		||||
		trx = &bts->trx[0];
 | 
			
		||||
		trx->pdch[1].enable();
 | 
			
		||||
		trx->pdch[2].enable();
 | 
			
		||||
		trx->pdch[3].enable();
 | 
			
		||||
		trx->pdch[4].enable();
 | 
			
		||||
 | 
			
		||||
		tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
 | 
			
		||||
		OSMO_ASSERT(tfi >= 0);
 | 
			
		||||
		ul_tbf = tbf_alloc_ul_tbf(bts, NULL, .1, ms_class, 0);
 | 
			
		||||
		OSMO_ASSERT(ul_tbf);
 | 
			
		||||
		OSMO_ASSERT(ul_tbf->ms());
 | 
			
		||||
		OSMO_ASSERT(ul_tbf->ms()->current_trx());
 | 
			
		||||
		trx_no = ul_tbf->ms()->current_trx()->trx_no;
 | 
			
		||||
		dump_assignment(ul_tbf, "UL");
 | 
			
		||||
 | 
			
		||||
		/* assume final ack has not been sent */
 | 
			
		||||
		dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0);
 | 
			
		||||
		OSMO_ASSERT(dl_tbf);
 | 
			
		||||
		dump_assignment(dl_tbf, "DL");
 | 
			
		||||
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
		check_tfi_usage(&the_bts);
 | 
			
		||||
 | 
			
		||||
		tbf_free(dl_tbf);
 | 
			
		||||
		tbf_free(ul_tbf);
 | 
			
		||||
	}
 | 
			
		||||
	test_alloc_b_jolly(ms_class);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define ENABLE_PDCH(ts_no, enable_flag, trx)	\
 | 
			
		||||
		if (enable_flag)		\
 | 
			
		||||
			trx->pdch[ts_no].enable();
 | 
			
		||||
 | 
			
		||||
static void test_alloc_b(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7, int ms_class)
 | 
			
		||||
static void test_alloc_mass(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7, int ms_class)
 | 
			
		||||
{
 | 
			
		||||
	bool rc;
 | 
			
		||||
 | 
			
		||||
	/* we can test the allocation failures differently */
 | 
			
		||||
	if (!ts0 && !ts1 && !ts2 && !ts3 && !ts4 && !ts5 && !ts6 && !ts7)
 | 
			
		||||
		return;
 | 
			
		||||
@@ -334,97 +381,14 @@ static void test_alloc_b(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool
 | 
			
		||||
		ts7 ? 'O' : 'x', ms_class);
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		BTS the_bts;
 | 
			
		||||
		struct gprs_rlcmac_bts *bts;
 | 
			
		||||
		struct gprs_rlcmac_trx *trx;
 | 
			
		||||
		uint8_t trx_no;
 | 
			
		||||
 | 
			
		||||
		gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
 | 
			
		||||
 | 
			
		||||
		bts = the_bts.bts_data();
 | 
			
		||||
		bts->alloc_algorithm = alloc_algorithm_b;
 | 
			
		||||
 | 
			
		||||
		trx = &bts->trx[0];
 | 
			
		||||
		ENABLE_PDCH(0, ts0, trx);
 | 
			
		||||
		ENABLE_PDCH(1, ts1, trx);
 | 
			
		||||
		ENABLE_PDCH(2, ts2, trx);
 | 
			
		||||
		ENABLE_PDCH(3, ts3, trx);
 | 
			
		||||
		ENABLE_PDCH(4, ts4, trx);
 | 
			
		||||
		ENABLE_PDCH(5, ts5, trx);
 | 
			
		||||
		ENABLE_PDCH(6, ts6, trx);
 | 
			
		||||
		ENABLE_PDCH(7, ts7, trx);
 | 
			
		||||
 | 
			
		||||
		ul_tbf = tbf_alloc_ul_tbf(bts, NULL, -1, ms_class, 1);
 | 
			
		||||
		OSMO_ASSERT(ul_tbf->ms());
 | 
			
		||||
		OSMO_ASSERT(ul_tbf->ms()->current_trx());
 | 
			
		||||
		trx_no = ul_tbf->ms()->current_trx()->trx_no;
 | 
			
		||||
		OSMO_ASSERT(ul_tbf);
 | 
			
		||||
 | 
			
		||||
		/* assume final ack has not been sent */
 | 
			
		||||
		dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0);
 | 
			
		||||
		OSMO_ASSERT(dl_tbf);
 | 
			
		||||
 | 
			
		||||
		/* verify that both are on the same ts */
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
		check_tfi_usage(&the_bts);
 | 
			
		||||
 | 
			
		||||
		tbf_free(dl_tbf);
 | 
			
		||||
		tbf_free(ul_tbf);
 | 
			
		||||
	}
 | 
			
		||||
	rc = test_alloc_b_ul_dl(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class, false);
 | 
			
		||||
	if (!rc)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Test with the other order.. first DL and then UL
 | 
			
		||||
	 */
 | 
			
		||||
	{
 | 
			
		||||
		BTS the_bts;
 | 
			
		||||
		struct gprs_rlcmac_bts *bts;
 | 
			
		||||
		struct gprs_rlcmac_trx *trx;
 | 
			
		||||
		uint8_t trx_no;
 | 
			
		||||
 | 
			
		||||
		gprs_rlcmac_ul_tbf *ul_tbf;
 | 
			
		||||
		gprs_rlcmac_dl_tbf *dl_tbf;
 | 
			
		||||
 | 
			
		||||
		bts = the_bts.bts_data();
 | 
			
		||||
		bts->alloc_algorithm = alloc_algorithm_b;
 | 
			
		||||
 | 
			
		||||
		trx = &bts->trx[0];
 | 
			
		||||
		ENABLE_PDCH(0, ts0, trx);
 | 
			
		||||
		ENABLE_PDCH(1, ts1, trx);
 | 
			
		||||
		ENABLE_PDCH(2, ts2, trx);
 | 
			
		||||
		ENABLE_PDCH(3, ts3, trx);
 | 
			
		||||
		ENABLE_PDCH(4, ts4, trx);
 | 
			
		||||
		ENABLE_PDCH(5, ts5, trx);
 | 
			
		||||
		ENABLE_PDCH(6, ts6, trx);
 | 
			
		||||
		ENABLE_PDCH(7, ts7, trx);
 | 
			
		||||
 | 
			
		||||
		dl_tbf = tbf_alloc_dl_tbf(bts, NULL, -1, ms_class, 1);
 | 
			
		||||
		OSMO_ASSERT(dl_tbf);
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->ms());
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->ms()->current_trx());
 | 
			
		||||
		trx_no = dl_tbf->ms()->current_trx()->trx_no;
 | 
			
		||||
		dl_tbf->update_ms(0x23, GPRS_RLCMAC_DL_TBF);
 | 
			
		||||
 | 
			
		||||
		ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), trx_no, ms_class, 0);
 | 
			
		||||
		OSMO_ASSERT(ul_tbf);
 | 
			
		||||
		ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
 | 
			
		||||
		ul_tbf->m_contention_resolution_done = 1;
 | 
			
		||||
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
		/* now update the dl_tbf */
 | 
			
		||||
		dl_tbf->update();
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 | 
			
		||||
 | 
			
		||||
		OSMO_ASSERT(ul_tbf->ms_class() == ms_class);
 | 
			
		||||
		OSMO_ASSERT(dl_tbf->ms_class() == ms_class);
 | 
			
		||||
 | 
			
		||||
		check_tfi_usage(&the_bts);
 | 
			
		||||
 | 
			
		||||
		tbf_free(dl_tbf);
 | 
			
		||||
		tbf_free(ul_tbf);
 | 
			
		||||
	}
 | 
			
		||||
	test_alloc_b_dl_ul(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_all_alloc_b()
 | 
			
		||||
@@ -438,22 +402,20 @@ static void test_all_alloc_b()
 | 
			
		||||
       for (uint8_t ts5 = 0; ts5 < 2; ++ts5)
 | 
			
		||||
        for (uint8_t ts6 = 0; ts6 < 2; ++ts6)
 | 
			
		||||
         for (uint8_t ts7 = 0; ts7 < 2; ++ts7)
 | 
			
		||||
	  for (int ms_class = 0; ms_class < 30; ++ms_class)
 | 
			
		||||
		test_alloc_b(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class);
 | 
			
		||||
	  for (int ms_class = 0; ms_class < mslot_class_max(); ++ms_class)
 | 
			
		||||
		test_alloc_mass(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_alloc_b()
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < 30; ++i)
 | 
			
		||||
		test_alloc_b(i);
 | 
			
		||||
	for (int i = 0; i < mslot_class_max(); ++i)
 | 
			
		||||
		test_alloc_b_for_ms(i);
 | 
			
		||||
 | 
			
		||||
	test_all_alloc_b();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef int (*algo_t)(struct gprs_rlcmac_bts *bts,
 | 
			
		||||
		struct GprsMs *ms,
 | 
			
		||||
		struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
 | 
			
		||||
		int use_trx);
 | 
			
		||||
typedef int (*algo_t)(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
 | 
			
		||||
		      int8_t use_trx);
 | 
			
		||||
 | 
			
		||||
static char get_dir_char(uint8_t mask, uint8_t tx, uint8_t rx, uint8_t busy)
 | 
			
		||||
{
 | 
			
		||||
@@ -473,6 +435,19 @@ enum test_mode {
 | 
			
		||||
	TEST_MODE_UL_AFTER_DL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline char *test_mode_descr(enum test_mode t)
 | 
			
		||||
{
 | 
			
		||||
	switch (t) {
 | 
			
		||||
	case TEST_MODE_UL_ONLY: return (char*)"UL only";
 | 
			
		||||
	case TEST_MODE_DL_ONLY: return (char*)"DL only";
 | 
			
		||||
	case TEST_MODE_UL_AND_DL: return (char*)"UL and DL";
 | 
			
		||||
	case TEST_MODE_DL_AND_UL: return (char*)"DL and UL";
 | 
			
		||||
	case TEST_MODE_DL_AFTER_UL: return (char*)"DL after UL";
 | 
			
		||||
	case TEST_MODE_UL_AFTER_DL: return (char*)"UL after DL";
 | 
			
		||||
	default: return NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GprsMs *alloc_tbfs(BTS *the_bts, GprsMs *ms, unsigned ms_class,
 | 
			
		||||
	enum test_mode mode)
 | 
			
		||||
{
 | 
			
		||||
@@ -495,7 +470,7 @@ static GprsMs *alloc_tbfs(BTS *the_bts, GprsMs *ms, unsigned ms_class,
 | 
			
		||||
	case TEST_MODE_UL_AND_DL:
 | 
			
		||||
		if (ms && ms->ul_tbf())
 | 
			
		||||
			tbf_free(ms->ul_tbf());
 | 
			
		||||
		tbf = tbf_alloc_ul_tbf(bts, ms, trx_no, ms_class, 0);
 | 
			
		||||
		tbf = tbf_alloc_ul_tbf(bts, ms, trx_no, ms_class, 0, false);
 | 
			
		||||
		if (tbf == NULL)
 | 
			
		||||
			return NULL;
 | 
			
		||||
		break;
 | 
			
		||||
@@ -504,7 +479,7 @@ static GprsMs *alloc_tbfs(BTS *the_bts, GprsMs *ms, unsigned ms_class,
 | 
			
		||||
	case TEST_MODE_DL_AND_UL:
 | 
			
		||||
		if (ms && ms->dl_tbf())
 | 
			
		||||
			tbf_free(ms->dl_tbf());
 | 
			
		||||
		tbf = tbf_alloc_dl_tbf(bts, ms, trx_no, ms_class, 0);
 | 
			
		||||
		tbf = tbf_alloc_dl_tbf(bts, ms, trx_no, ms_class, 0, false);
 | 
			
		||||
		if (tbf == NULL)
 | 
			
		||||
			return NULL;
 | 
			
		||||
	}
 | 
			
		||||
@@ -600,6 +575,10 @@ static unsigned alloc_many_tbfs(BTS *the_bts, unsigned min_class,
 | 
			
		||||
			if (dl_tbf->pdch[i])
 | 
			
		||||
				dl_slots |= 1 << i;
 | 
			
		||||
 | 
			
		||||
		for (i = 0; ul_tbf && i < ARRAY_SIZE(ul_tbf->pdch); i += 1)
 | 
			
		||||
			if (ul_tbf->pdch[i])
 | 
			
		||||
				ul_slots |= 1 << i;
 | 
			
		||||
 | 
			
		||||
		for (i = 0; trx && i < ARRAY_SIZE(trx->pdch); i += 1) {
 | 
			
		||||
			struct gprs_rlcmac_pdch *pdch = &trx->pdch[i];
 | 
			
		||||
 | 
			
		||||
@@ -607,17 +586,17 @@ static unsigned alloc_many_tbfs(BTS *the_bts, unsigned min_class,
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (ul_tbf &&
 | 
			
		||||
				pdch->assigned_tfi(GPRS_RLCMAC_DL_TBF) != 0xffffffff)
 | 
			
		||||
				pdch->assigned_tfi(GPRS_RLCMAC_DL_TBF) != NO_FREE_TFI)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (dl_tbf &&
 | 
			
		||||
				pdch->assigned_tfi(GPRS_RLCMAC_UL_TBF) != 0xffffffff)
 | 
			
		||||
				pdch->assigned_tfi(GPRS_RLCMAC_UL_TBF) != NO_FREE_TFI)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			busy_slots |= 1 << i;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		printf(" TBF[%d] class %d reserves %c%c%c%c%c%c%c%c\n",
 | 
			
		||||
		printf(" TBF[%d] class %d reserves " OSMO_BIT_SPEC "\n",
 | 
			
		||||
			tfi, ms_class,
 | 
			
		||||
			get_dir_char(0x01, ul_slots, dl_slots, busy_slots),
 | 
			
		||||
			get_dir_char(0x02, ul_slots, dl_slots, busy_slots),
 | 
			
		||||
@@ -654,7 +633,8 @@ static void test_successive_allocation(algo_t algo, unsigned min_class,
 | 
			
		||||
	struct gprs_rlcmac_trx *trx;
 | 
			
		||||
	unsigned counter;
 | 
			
		||||
 | 
			
		||||
	printf("Going to test assignment with many TBF, %s\n", text);
 | 
			
		||||
	printf("Going to test assignment with many TBF, algorithm %s class %u..%u (%s)\n",
 | 
			
		||||
	       text, min_class, max_class, test_mode_descr(mode));
 | 
			
		||||
 | 
			
		||||
	bts = the_bts.bts_data();
 | 
			
		||||
	bts->alloc_algorithm = algo;
 | 
			
		||||
@@ -668,9 +648,11 @@ static void test_successive_allocation(algo_t algo, unsigned min_class,
 | 
			
		||||
 | 
			
		||||
	counter = alloc_many_tbfs(&the_bts, min_class, max_class, mode);
 | 
			
		||||
 | 
			
		||||
	printf("  Successfully allocated %d UL TBFs\n", counter);
 | 
			
		||||
	printf("  Successfully allocated %u UL TBFs, algorithm %s class %u..%u (%s)\n",
 | 
			
		||||
	       counter, text, min_class, max_class, test_mode_descr(mode));
 | 
			
		||||
	if (counter != expect_num)
 | 
			
		||||
		fprintf(stderr, "  Expected %d TBFs for %s\n", expect_num, text);
 | 
			
		||||
		fprintf(stderr, "  Expected %u TBFs (got %u), algorithm %s class %u..%u (%s)\n",
 | 
			
		||||
			expect_num, counter, text, min_class, max_class, test_mode_descr(mode));
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(counter == expect_num);
 | 
			
		||||
 | 
			
		||||
@@ -692,7 +674,7 @@ static void test_many_connections(algo_t algo, unsigned expect_num,
 | 
			
		||||
		TEST_MODE_DL_ONLY,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	printf("Going to test assignment with many connections, %s\n", text);
 | 
			
		||||
	printf("Going to test assignment with many connections, algorithm %s\n", text);
 | 
			
		||||
 | 
			
		||||
	bts = the_bts.bts_data();
 | 
			
		||||
	bts->alloc_algorithm = algo;
 | 
			
		||||
@@ -705,7 +687,7 @@ static void test_many_connections(algo_t algo, unsigned expect_num,
 | 
			
		||||
	trx->pdch[7].enable();
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(mode_seq); i += 1) {
 | 
			
		||||
		counter1 = alloc_many_tbfs(&the_bts, 1, 29, mode_seq[i]);
 | 
			
		||||
		counter1 = alloc_many_tbfs(&the_bts, 1, mslot_class_max(), mode_seq[i]);
 | 
			
		||||
		fprintf(stderr, "  Allocated %d TBFs (previously %d)\n",
 | 
			
		||||
			counter1, counter2);
 | 
			
		||||
 | 
			
		||||
@@ -727,67 +709,85 @@ static void test_many_connections(algo_t algo, unsigned expect_num,
 | 
			
		||||
 | 
			
		||||
	printf("  Successfully allocated %d TBFs\n", counter1);
 | 
			
		||||
	if (counter1 != (int)expect_num)
 | 
			
		||||
		fprintf(stderr, "  Expected %d TBFs for %s\n", expect_num, text);
 | 
			
		||||
		fprintf(stderr, "  Expected %d TBFs (got %d) for algorithm %s\n", expect_num, counter1, text);
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(expect_num == (unsigned)counter1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_successive_allocation()
 | 
			
		||||
static inline void test_a_b_dyn(enum test_mode mode, uint8_t exp_A, uint8_t exp_B, uint8_t exp_dyn)
 | 
			
		||||
{
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_AND_DL,
 | 
			
		||||
		35, "algorithm A (UL and DL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_AND_DL,
 | 
			
		||||
		32, "algorithm B class 10 (UL and DL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b, 12, 12, TEST_MODE_UL_AND_DL,
 | 
			
		||||
		32, "algorithm B class 12 (UL and DL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b, 1, 12, TEST_MODE_UL_AND_DL,
 | 
			
		||||
		32, "algorithm B class 1-12 (UL and DL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b, 1, 29, TEST_MODE_UL_AND_DL,
 | 
			
		||||
		32, "algorithm B class 1-29 (UL and DL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_dynamic, 1, 29, TEST_MODE_UL_AND_DL,
 | 
			
		||||
		35, "algorithm dynamic class 1-29 (UL and DL)");
 | 
			
		||||
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_AND_UL,
 | 
			
		||||
		35, "algorithm A (DL and UL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_AND_UL,
 | 
			
		||||
		32, "algorithm B class 10 (DL and UL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_AND_UL,
 | 
			
		||||
		32, "algorithm dynamic class 10 (DL and UL)");
 | 
			
		||||
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_AFTER_UL,
 | 
			
		||||
		160, "algorithm A (DL after UL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_AFTER_UL,
 | 
			
		||||
		32, "algorithm B class 10 (DL after UL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_AFTER_UL,
 | 
			
		||||
		95, "algorithm dynamic class 10 (DL after UL)");
 | 
			
		||||
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_AFTER_DL,
 | 
			
		||||
		35, "algorithm A (UL after DL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_AFTER_DL,
 | 
			
		||||
		32, "algorithm B class 10 (UL after DL)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_UL_AFTER_DL,
 | 
			
		||||
		35, "algorithm dynamic class 10 (UL after DL)");
 | 
			
		||||
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_ONLY,
 | 
			
		||||
		35, "algorithm A (UL only)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_ONLY,
 | 
			
		||||
		32, "algorithm B class 10 (UL only)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_UL_ONLY,
 | 
			
		||||
		35, "algorithm dynamic class 10 (UL only)");
 | 
			
		||||
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_ONLY,
 | 
			
		||||
		160, "algorithm A (DL ONLY)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_ONLY,
 | 
			
		||||
		32, "algorithm B class 10 (DL ONLY)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_ONLY,
 | 
			
		||||
		101, "algorithm dynamic class 10 (DL ONLY)");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_a,        1,  1, mode, exp_A,   "A");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b,       10, 10, mode, exp_B,   "B");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_dynamic, 10, 10, mode, exp_dyn, "dynamic");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_many_connections()
 | 
			
		||||
static void test_successive_allocations()
 | 
			
		||||
{
 | 
			
		||||
	test_many_connections(alloc_algorithm_a, 160, "algorithm A");
 | 
			
		||||
	test_many_connections(alloc_algorithm_b, 32, "algorithm B");
 | 
			
		||||
	test_many_connections(alloc_algorithm_dynamic, 160, "algorithm dynamic");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_a,       1,  1, TEST_MODE_UL_AND_DL, 35, "A");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b,      10, 10, TEST_MODE_UL_AND_DL, 32, "B");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b,      12, 12, TEST_MODE_UL_AND_DL, 32, "B");
 | 
			
		||||
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b,       1,                12, TEST_MODE_UL_AND_DL, 32, "B");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_b,       1, mslot_class_max(), TEST_MODE_UL_AND_DL, 32, "B");
 | 
			
		||||
	test_successive_allocation(alloc_algorithm_dynamic, 1, mslot_class_max(), TEST_MODE_UL_AND_DL, 35, "dynamic");
 | 
			
		||||
 | 
			
		||||
	test_a_b_dyn(TEST_MODE_DL_AND_UL,    35, 32,  32);
 | 
			
		||||
	test_a_b_dyn(TEST_MODE_DL_AFTER_UL, 160, 32,  95);
 | 
			
		||||
	test_a_b_dyn(TEST_MODE_UL_AFTER_DL,  35, 32,  35);
 | 
			
		||||
	test_a_b_dyn(TEST_MODE_UL_ONLY,      35, 32,  35);
 | 
			
		||||
	test_a_b_dyn(TEST_MODE_DL_ONLY,     160, 32, 101);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_2_consecutive_dl_tbfs()
 | 
			
		||||
{
 | 
			
		||||
	BTS the_bts;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
	struct gprs_rlcmac_trx *trx;
 | 
			
		||||
	uint8_t ms_class = 11;
 | 
			
		||||
	uint8_t egprs_ms_class = 11;
 | 
			
		||||
	gprs_rlcmac_tbf *dl_tbf1, *dl_tbf2;
 | 
			
		||||
	uint8_t numTs1 = 0, numTs2 = 0;
 | 
			
		||||
 | 
			
		||||
	printf("Testing DL TS allocation for Multi UEs\n");
 | 
			
		||||
 | 
			
		||||
	bts = the_bts.bts_data();
 | 
			
		||||
	bts->alloc_algorithm = alloc_algorithm_b;
 | 
			
		||||
 | 
			
		||||
	trx = &bts->trx[0];
 | 
			
		||||
	trx->pdch[4].enable();
 | 
			
		||||
	trx->pdch[5].enable();
 | 
			
		||||
	trx->pdch[6].enable();
 | 
			
		||||
	trx->pdch[7].enable();
 | 
			
		||||
 | 
			
		||||
	dl_tbf1 = tbf_alloc_dl_tbf(bts, NULL, 0, ms_class, egprs_ms_class,
 | 
			
		||||
				   false);
 | 
			
		||||
	OSMO_ASSERT(dl_tbf1);
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < 8; i++) {
 | 
			
		||||
		if (dl_tbf1->pdch[i])
 | 
			
		||||
			numTs1++;
 | 
			
		||||
	}
 | 
			
		||||
	OSMO_ASSERT(numTs1 == 4);
 | 
			
		||||
	printf("TBF1: numTs(%d)\n", numTs1);
 | 
			
		||||
 | 
			
		||||
	dl_tbf2 = tbf_alloc_dl_tbf(bts, NULL, 0, ms_class, egprs_ms_class,
 | 
			
		||||
				   false);
 | 
			
		||||
	OSMO_ASSERT(dl_tbf2);
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < 8; i++) {
 | 
			
		||||
		if (dl_tbf2->pdch[i])
 | 
			
		||||
			numTs2++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * TODO: currently 2nd DL TBF gets 3 TS
 | 
			
		||||
	 * This behaviour will be fixed in subsequent patch
 | 
			
		||||
	 */
 | 
			
		||||
	printf("TBF2: numTs(%d)\n", numTs2);
 | 
			
		||||
	OSMO_ASSERT(numTs2 == 3);
 | 
			
		||||
 | 
			
		||||
	tbf_free(dl_tbf1);
 | 
			
		||||
	tbf_free(dl_tbf2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
@@ -796,8 +796,8 @@ int main(int argc, char **argv)
 | 
			
		||||
	if (!tall_pcu_ctx)
 | 
			
		||||
		abort();
 | 
			
		||||
 | 
			
		||||
	msgb_set_talloc_ctx(tall_pcu_ctx);
 | 
			
		||||
	osmo_init_logging(&gprs_log_info);
 | 
			
		||||
	msgb_talloc_ctx_init(tall_pcu_ctx, 0);
 | 
			
		||||
	osmo_init_logging2(tall_pcu_ctx, &gprs_log_info);
 | 
			
		||||
	log_set_use_color(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, 0);
 | 
			
		||||
	if (getenv("LOGL_DEBUG"))
 | 
			
		||||
@@ -805,8 +805,11 @@ int main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
	test_alloc_a();
 | 
			
		||||
	test_alloc_b();
 | 
			
		||||
	test_successive_allocation();
 | 
			
		||||
	test_many_connections();
 | 
			
		||||
	test_successive_allocations();
 | 
			
		||||
	test_many_connections(alloc_algorithm_a, 160, "A");
 | 
			
		||||
	test_many_connections(alloc_algorithm_b, 32, "B");
 | 
			
		||||
	test_many_connections(alloc_algorithm_dynamic, 160, "dynamic");
 | 
			
		||||
	test_2_consecutive_dl_tbfs();
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										203621
									
								
								tests/alloc/AllocTest.err
									
									
									
									
									
								
							
							
						
						
									
										203621
									
								
								tests/alloc/AllocTest.err
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										188
									
								
								tests/alloc/MslotTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								tests/alloc/MslotTest.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,188 @@
 | 
			
		||||
/* MslotTest.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gprs_rlcmac.h"
 | 
			
		||||
#include "gprs_debug.h"
 | 
			
		||||
#include "tbf.h"
 | 
			
		||||
#include "bts.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
	#include "mslot_class.h"
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* globals used by the code */
 | 
			
		||||
void *tall_pcu_ctx;
 | 
			
		||||
int16_t spoof_mnc = 0, spoof_mcc = 0;
 | 
			
		||||
bool spoof_mnc_3_digits = false;
 | 
			
		||||
 | 
			
		||||
static inline void test_all_classes(struct gprs_rlcmac_trx *trx, bool clear_masks)
 | 
			
		||||
{
 | 
			
		||||
	int i, rc;
 | 
			
		||||
	uint8_t dl_slots = 0, ul_slots = 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 64; i++) {
 | 
			
		||||
		rc = find_multi_slots(trx, i, &ul_slots, &dl_slots);
 | 
			
		||||
 | 
			
		||||
		printf("    [%s] multislot class %3u - UL: " OSMO_BIT_SPEC " DL: " OSMO_BIT_SPEC " [%d]\n",
 | 
			
		||||
		       clear_masks ? "SEQ" : "ACC", i, OSMO_BIT_PRINT(ul_slots), OSMO_BIT_PRINT(dl_slots), rc);
 | 
			
		||||
 | 
			
		||||
		if (rc == -EINVAL)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		if (clear_masks) {
 | 
			
		||||
			dl_slots = 0;
 | 
			
		||||
			ul_slots = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void test_multislot_total_ascending(bool seq)
 | 
			
		||||
{
 | 
			
		||||
	BTS the_bts;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
	struct gprs_rlcmac_trx *trx;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
 | 
			
		||||
 | 
			
		||||
	bts = the_bts.bts_data();
 | 
			
		||||
 | 
			
		||||
	trx = &bts->trx[0];
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 8; i++) {
 | 
			
		||||
		printf("  Enabled PDCH %u for multislot tests...\n", i);
 | 
			
		||||
		trx->pdch[i].enable();
 | 
			
		||||
 | 
			
		||||
		test_all_classes(trx, seq);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void test_multislot_total_descending(bool seq)
 | 
			
		||||
{
 | 
			
		||||
	BTS the_bts;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
	struct gprs_rlcmac_trx *trx;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
 | 
			
		||||
 | 
			
		||||
	bts = the_bts.bts_data();
 | 
			
		||||
 | 
			
		||||
	trx = &bts->trx[0];
 | 
			
		||||
 | 
			
		||||
	for (i = 7; i >= 0; i--) {
 | 
			
		||||
		printf("  Enabled PDCH %u for multislot tests...\n", i);
 | 
			
		||||
		trx->pdch[i].enable();
 | 
			
		||||
 | 
			
		||||
		test_all_classes(trx, seq);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void test_multislot_middle(bool seq)
 | 
			
		||||
{
 | 
			
		||||
	BTS the_bts;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
	struct gprs_rlcmac_trx *trx;
 | 
			
		||||
 | 
			
		||||
	printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
 | 
			
		||||
 | 
			
		||||
	bts = the_bts.bts_data();
 | 
			
		||||
 | 
			
		||||
	trx = &bts->trx[0];
 | 
			
		||||
 | 
			
		||||
	trx->pdch[2].enable();
 | 
			
		||||
	trx->pdch[3].enable();
 | 
			
		||||
	trx->pdch[4].enable();
 | 
			
		||||
 | 
			
		||||
	test_all_classes(trx, seq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void test_multislot_ends(bool seq)
 | 
			
		||||
{
 | 
			
		||||
	BTS the_bts;
 | 
			
		||||
	struct gprs_rlcmac_bts *bts;
 | 
			
		||||
	struct gprs_rlcmac_trx *trx;
 | 
			
		||||
 | 
			
		||||
	printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
 | 
			
		||||
 | 
			
		||||
	bts = the_bts.bts_data();
 | 
			
		||||
 | 
			
		||||
	trx = &bts->trx[0];
 | 
			
		||||
 | 
			
		||||
	trx->pdch[0].enable();
 | 
			
		||||
	trx->pdch[7].enable();
 | 
			
		||||
 | 
			
		||||
	test_all_classes(trx, seq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void test_window_wrapper()
 | 
			
		||||
{
 | 
			
		||||
	uint16_t i;
 | 
			
		||||
	for (i = 0; i < 256 * 2 + 1; i++)
 | 
			
		||||
		printf("W[%03u] -> %3u %s\n",
 | 
			
		||||
		       i, mslot_wrap_window(i), mslot_wrap_window(i) < 256 ? "OK" : "FAIL");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	tall_pcu_ctx = talloc_named_const(NULL, 1, "MslotTest context");
 | 
			
		||||
	if (!tall_pcu_ctx)
 | 
			
		||||
		abort();
 | 
			
		||||
 | 
			
		||||
	msgb_talloc_ctx_init(tall_pcu_ctx, 0);
 | 
			
		||||
 | 
			
		||||
	osmo_init_logging2(tall_pcu_ctx, &gprs_log_info);
 | 
			
		||||
	log_set_use_color(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
 | 
			
		||||
 | 
			
		||||
	test_multislot_total_ascending(true);
 | 
			
		||||
	test_multislot_total_ascending(false);
 | 
			
		||||
 | 
			
		||||
	test_multislot_total_descending(true);
 | 
			
		||||
	test_multislot_total_descending(false);
 | 
			
		||||
 | 
			
		||||
	test_multislot_middle(true);
 | 
			
		||||
	test_multislot_middle(false);
 | 
			
		||||
 | 
			
		||||
	test_multislot_ends(true);
 | 
			
		||||
	test_multislot_ends(false);
 | 
			
		||||
 | 
			
		||||
	test_window_wrapper();
 | 
			
		||||
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * stubs that should not be reached
 | 
			
		||||
 */
 | 
			
		||||
extern "C" {
 | 
			
		||||
void l1if_pdch_req() { abort(); }
 | 
			
		||||
void l1if_connect_pdch() { abort(); }
 | 
			
		||||
void l1if_close_pdch() { abort(); }
 | 
			
		||||
void l1if_open_pdch() { abort(); }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2245
									
								
								tests/alloc/MslotTest.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2245
									
								
								tests/alloc/MslotTest.ok
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										217
									
								
								tests/bitcomp/BitcompTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								tests/bitcomp/BitcompTest.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,217 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "rlc.h"
 | 
			
		||||
#include "gprs_debug.h"
 | 
			
		||||
#include <gprs_rlcmac.h>
 | 
			
		||||
#include "egprs_rlc_compression.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/bitvec.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define NEW 1
 | 
			
		||||
#define MASK(n) (0xFF << (8-n))
 | 
			
		||||
#define MAX_CRBB_LEN 23
 | 
			
		||||
#define MAX_URBB_LEN 40
 | 
			
		||||
#define CEIL_DIV_8(x) (((x) + 7)/8)
 | 
			
		||||
#define _LOG(fmt, args...) \
 | 
			
		||||
	fprintf(stderr, fmt, ## args)
 | 
			
		||||
 | 
			
		||||
void *tall_pcu_ctx;
 | 
			
		||||
 | 
			
		||||
struct test_data {
 | 
			
		||||
	int8_t crbb_len;
 | 
			
		||||
	uint8_t cc;
 | 
			
		||||
	uint8_t crbb_data[MAX_CRBB_LEN]; /* compressed data */
 | 
			
		||||
	uint8_t ucmp_data[MAX_URBB_LEN]; /* uncompressed data */
 | 
			
		||||
	int ucmp_len;
 | 
			
		||||
	int expect_rc;
 | 
			
		||||
} test[] = {
 | 
			
		||||
		{ .crbb_len = 67, .cc = 1,
 | 
			
		||||
			.crbb_data = {
 | 
			
		||||
			0x02, 0x0c, 0xa0, 0x30, 0xcb, 0x1a, 0x0c, 0xe3, 0x6c
 | 
			
		||||
			},
 | 
			
		||||
			.ucmp_data = {
 | 
			
		||||
			0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xff, 0xff,
 | 
			
		||||
			0xff, 0xf8, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe,
 | 
			
		||||
			0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0
 | 
			
		||||
			},
 | 
			
		||||
			.ucmp_len = 194,
 | 
			
		||||
			.expect_rc = 0,
 | 
			
		||||
		},
 | 
			
		||||
		{ .crbb_len = 40, .cc = 1,
 | 
			
		||||
			.crbb_data = {
 | 
			
		||||
			0x53, 0x06, 0xc5, 0x40, 0x6d
 | 
			
		||||
			},
 | 
			
		||||
			.ucmp_data = {
 | 
			
		||||
			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00,
 | 
			
		||||
			0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
 | 
			
		||||
			0x00, 0x00, 0x00, 0x00, 0x00
 | 
			
		||||
			},
 | 
			
		||||
			.ucmp_len = 182,
 | 
			
		||||
			.expect_rc = 0,
 | 
			
		||||
		},
 | 
			
		||||
		{ .crbb_len = 8, .cc = 1,
 | 
			
		||||
			.crbb_data = {0x02},
 | 
			
		||||
			.ucmp_data = {0xff, 0xff, 0xff, 0xf8},
 | 
			
		||||
			.ucmp_len = 29,
 | 
			
		||||
			.expect_rc = 0,
 | 
			
		||||
		},
 | 
			
		||||
		{ .crbb_len = 103, .cc = 1,
 | 
			
		||||
			.crbb_data = {
 | 
			
		||||
			0x02, 0x0c, 0xe0, 0x41, 0xa0, 0x0c, 0x36, 0x0d, 0x03,
 | 
			
		||||
			0x71, 0xb0, 0x6e, 0x24
 | 
			
		||||
			},
 | 
			
		||||
			.ucmp_data = {
 | 
			
		||||
			0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xff,
 | 
			
		||||
			0xf8, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00,
 | 
			
		||||
			0x0f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x7f, 0xff,
 | 
			
		||||
			0xff, 0xff, 0x80, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff
 | 
			
		||||
			},
 | 
			
		||||
			.ucmp_len = 288,
 | 
			
		||||
			.expect_rc = 0,
 | 
			
		||||
		},
 | 
			
		||||
		/* Test vector from libosmocore test */
 | 
			
		||||
		{ .crbb_len = 35, .cc = 0,
 | 
			
		||||
			.crbb_data = {0xde, 0x88, 0x75, 0x65, 0x80},
 | 
			
		||||
			.ucmp_data = {0x37, 0x47, 0x81, 0xf0},
 | 
			
		||||
			.ucmp_len = 28,
 | 
			
		||||
			.expect_rc = 0,
 | 
			
		||||
		},
 | 
			
		||||
		{ .crbb_len = 18, .cc = 1,
 | 
			
		||||
			.crbb_data = {0xdd, 0x41, 0x00},
 | 
			
		||||
			.ucmp_data = {
 | 
			
		||||
			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 | 
			
		||||
			0xff, 0x00, 0x00
 | 
			
		||||
			},
 | 
			
		||||
			.ucmp_len = 90,
 | 
			
		||||
			.expect_rc = 0,
 | 
			
		||||
		},
 | 
			
		||||
		/* TODO: previously marked as "Invalid inputs" but succeeds */
 | 
			
		||||
		{ .crbb_len = 18, .cc = 1,
 | 
			
		||||
			.crbb_data = {0x1E, 0x70, 0xc0},
 | 
			
		||||
			.ucmp_data = {0xb0, 0x00, 0x00},
 | 
			
		||||
			.ucmp_len = 19,
 | 
			
		||||
			.expect_rc = 0,
 | 
			
		||||
		},
 | 
			
		||||
		/* Invalid inputs */
 | 
			
		||||
		{ .crbb_len = 14, .cc = 1,
 | 
			
		||||
			.crbb_data = {0x00, 0x1E, 0x7c},
 | 
			
		||||
			.ucmp_data = {0x0},
 | 
			
		||||
			.ucmp_len = 0,
 | 
			
		||||
			.expect_rc = -1,
 | 
			
		||||
		},
 | 
			
		||||
		{ .crbb_len = 24, .cc = 0,
 | 
			
		||||
			.crbb_data = {0x00, 0x00, 0x00},
 | 
			
		||||
			.ucmp_data = {0x0},
 | 
			
		||||
			.ucmp_len = 0,
 | 
			
		||||
			.expect_rc = -1,
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
bool result_matches(const bitvec &bits, const uint8_t *exp_data, unsigned int exp_len)
 | 
			
		||||
{
 | 
			
		||||
	if (bits.cur_bit != exp_len)
 | 
			
		||||
		return false;
 | 
			
		||||
	return (memcmp(exp_data, bits.data, CEIL_DIV_8(exp_len)) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*  To test decoding of compressed bitmap by Tree based method
 | 
			
		||||
 *  and to verify the result with expected result
 | 
			
		||||
 *  for invalid input verfication is suppressed
 | 
			
		||||
 */
 | 
			
		||||
static void test_EPDAN_decode_tree(void)
 | 
			
		||||
{
 | 
			
		||||
	bitvec dest;
 | 
			
		||||
	unsigned int itr;
 | 
			
		||||
	int rc;
 | 
			
		||||
	uint8_t bits_data[RLC_EGPRS_MAX_WS/8];
 | 
			
		||||
 | 
			
		||||
	printf("=== start %s ===\n", __func__);
 | 
			
		||||
 | 
			
		||||
	for (itr = 0 ; itr < (sizeof(test) / sizeof(test_data)) ; itr++) {
 | 
			
		||||
		memset(bits_data, 0, sizeof(bits_data));
 | 
			
		||||
		dest.data = bits_data;
 | 
			
		||||
		dest.data_len = sizeof(bits_data);
 | 
			
		||||
		dest.cur_bit = 0;
 | 
			
		||||
		_LOG("\nTest:%d\n"
 | 
			
		||||
		     "Tree based decoding:\n"
 | 
			
		||||
		     "uncompressed data = %s\n"
 | 
			
		||||
		     "len = %d\n",
 | 
			
		||||
		     itr + 1,
 | 
			
		||||
		     osmo_hexdump(test[itr].crbb_data,
 | 
			
		||||
				  CEIL_DIV_8(test[itr].crbb_len)),
 | 
			
		||||
		     test[itr].crbb_len);
 | 
			
		||||
		rc = egprs_compress::decompress_crbb(test[itr].crbb_len,
 | 
			
		||||
			test[itr].cc, test[itr].crbb_data, &dest);
 | 
			
		||||
		_LOG("rc = %d\n", rc);
 | 
			
		||||
		OSMO_ASSERT(test[itr].expect_rc == rc);
 | 
			
		||||
		if (rc < 0) {
 | 
			
		||||
			_LOG("Failed to decode CRBB: length %d, data %s\n",
 | 
			
		||||
			     test[itr].crbb_len,
 | 
			
		||||
			     osmo_hexdump(test[itr].crbb_data,
 | 
			
		||||
					  CEIL_DIV_8(test[itr].crbb_len)));
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (!result_matches(dest, test[itr].ucmp_data,
 | 
			
		||||
				    test[itr].ucmp_len)) {
 | 
			
		||||
			_LOG("\nTree based decoding: Error\n"
 | 
			
		||||
			     "expected data = %s\n"
 | 
			
		||||
			     "expected len = %d\n",
 | 
			
		||||
			     osmo_hexdump(test[itr].ucmp_data,
 | 
			
		||||
					  CEIL_DIV_8(test[itr].ucmp_len)),
 | 
			
		||||
			     test[itr].ucmp_len);
 | 
			
		||||
			_LOG("decoded data = %s\n"
 | 
			
		||||
			     "decoded len = %d\n",
 | 
			
		||||
			     osmo_hexdump(dest.data,
 | 
			
		||||
					  CEIL_DIV_8(dest.cur_bit)),
 | 
			
		||||
			     dest.cur_bit);
 | 
			
		||||
			OSMO_ASSERT(0);
 | 
			
		||||
		}
 | 
			
		||||
		_LOG("\nexpected data = %s\n"
 | 
			
		||||
		     "expected len = %d\n",
 | 
			
		||||
		     osmo_hexdump(test[itr].ucmp_data,
 | 
			
		||||
				  CEIL_DIV_8(test[itr].ucmp_len)),
 | 
			
		||||
		     test[itr].ucmp_len);
 | 
			
		||||
		_LOG("decoded data = %s\n"
 | 
			
		||||
		     "decoded len = %d\n",
 | 
			
		||||
		     osmo_hexdump(dest.data, CEIL_DIV_8(dest.cur_bit)),
 | 
			
		||||
		     dest.cur_bit);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printf("=== end %s ===\n", __func__);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile bitcompTest context");
 | 
			
		||||
	if (!tall_pcu_ctx)
 | 
			
		||||
		abort();
 | 
			
		||||
 | 
			
		||||
	osmo_init_logging2(tall_pcu_ctx, &gprs_log_info);
 | 
			
		||||
	log_set_use_color(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, 0);
 | 
			
		||||
	log_parse_category_mask(osmo_stderr_target, "DRLCMACUL,1");
 | 
			
		||||
 | 
			
		||||
	test_EPDAN_decode_tree();
 | 
			
		||||
 | 
			
		||||
	if (getenv("TALLOC_REPORT_FULL"))
 | 
			
		||||
		talloc_report_full(tall_pcu_ctx, stderr);
 | 
			
		||||
	talloc_free(tall_pcu_ctx);
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * stubs that should not be reached
 | 
			
		||||
 */
 | 
			
		||||
extern "C" {
 | 
			
		||||
void l1if_pdch_req() { abort(); }
 | 
			
		||||
void l1if_connect_pdch() { abort(); }
 | 
			
		||||
void l1if_close_pdch() { abort(); }
 | 
			
		||||
void l1if_open_pdch() { abort(); }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										131
									
								
								tests/bitcomp/BitcompTest.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								tests/bitcomp/BitcompTest.err
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
 | 
			
		||||
Test:1
 | 
			
		||||
Tree based decoding:
 | 
			
		||||
uncompressed data = 02 0c a0 30 cb 1a 0c e3 6c 
 | 
			
		||||
len = 67
 | 
			
		||||
Run_length = 29
 | 
			
		||||
Run_length = 26
 | 
			
		||||
Run_length = 30
 | 
			
		||||
Run_length = 27
 | 
			
		||||
Run_length = 31
 | 
			
		||||
Run_length = 19
 | 
			
		||||
Run_length = 32
 | 
			
		||||
rc = 0
 | 
			
		||||
 | 
			
		||||
expected data = ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff c0 
 | 
			
		||||
expected len = 194
 | 
			
		||||
decoded data = ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff c0 
 | 
			
		||||
decoded len = 194
 | 
			
		||||
 | 
			
		||||
Test:2
 | 
			
		||||
Tree based decoding:
 | 
			
		||||
uncompressed data = 53 06 c5 40 6d 
 | 
			
		||||
len = 40
 | 
			
		||||
Run_length = 50
 | 
			
		||||
Run_length = 40
 | 
			
		||||
Run_length = 51
 | 
			
		||||
Run_length = 41
 | 
			
		||||
rc = 0
 | 
			
		||||
 | 
			
		||||
expected data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 00 
 | 
			
		||||
expected len = 182
 | 
			
		||||
decoded data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 00 
 | 
			
		||||
decoded len = 182
 | 
			
		||||
 | 
			
		||||
Test:3
 | 
			
		||||
Tree based decoding:
 | 
			
		||||
uncompressed data = 02 
 | 
			
		||||
len = 8
 | 
			
		||||
Run_length = 29
 | 
			
		||||
rc = 0
 | 
			
		||||
 | 
			
		||||
expected data = ff ff ff f8 
 | 
			
		||||
expected len = 29
 | 
			
		||||
decoded data = ff ff ff f8 
 | 
			
		||||
decoded len = 29
 | 
			
		||||
 | 
			
		||||
Test:4
 | 
			
		||||
Tree based decoding:
 | 
			
		||||
uncompressed data = 02 0c e0 41 a0 0c 36 0d 03 71 b0 6e 24 
 | 
			
		||||
len = 103
 | 
			
		||||
Run_length = 29
 | 
			
		||||
Run_length = 19
 | 
			
		||||
Run_length = 29
 | 
			
		||||
Run_length = 20
 | 
			
		||||
Run_length = 30
 | 
			
		||||
Run_length = 21
 | 
			
		||||
Run_length = 31
 | 
			
		||||
Run_length = 22
 | 
			
		||||
Run_length = 32
 | 
			
		||||
Run_length = 22
 | 
			
		||||
Run_length = 33
 | 
			
		||||
rc = 0
 | 
			
		||||
 | 
			
		||||
expected data = ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff 
 | 
			
		||||
expected len = 288
 | 
			
		||||
decoded data = ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff 
 | 
			
		||||
decoded len = 288
 | 
			
		||||
 | 
			
		||||
Test:5
 | 
			
		||||
Tree based decoding:
 | 
			
		||||
uncompressed data = de 88 75 65 80 
 | 
			
		||||
len = 35
 | 
			
		||||
Run_length = 2
 | 
			
		||||
Run_length = 2
 | 
			
		||||
Run_length = 1
 | 
			
		||||
Run_length = 3
 | 
			
		||||
Run_length = 1
 | 
			
		||||
Run_length = 1
 | 
			
		||||
Run_length = 3
 | 
			
		||||
Run_length = 4
 | 
			
		||||
Run_length = 6
 | 
			
		||||
Run_length = 5
 | 
			
		||||
rc = 0
 | 
			
		||||
 | 
			
		||||
expected data = 37 47 81 f0 
 | 
			
		||||
expected len = 28
 | 
			
		||||
decoded data = 37 47 81 f0 
 | 
			
		||||
decoded len = 28
 | 
			
		||||
 | 
			
		||||
Test:6
 | 
			
		||||
Tree based decoding:
 | 
			
		||||
uncompressed data = dd 41 00 
 | 
			
		||||
len = 18
 | 
			
		||||
Run_length = 64
 | 
			
		||||
Run_length = 16
 | 
			
		||||
Run_length = 10
 | 
			
		||||
rc = 0
 | 
			
		||||
 | 
			
		||||
expected data = ff ff ff ff ff ff ff ff ff ff 00 00 
 | 
			
		||||
expected len = 90
 | 
			
		||||
decoded data = ff ff ff ff ff ff ff ff ff ff 00 00 
 | 
			
		||||
decoded len = 90
 | 
			
		||||
 | 
			
		||||
Test:7
 | 
			
		||||
Tree based decoding:
 | 
			
		||||
uncompressed data = 1e 70 c0 
 | 
			
		||||
len = 18
 | 
			
		||||
Run_length = 1
 | 
			
		||||
Run_length = 1
 | 
			
		||||
Run_length = 2
 | 
			
		||||
Run_length = 15
 | 
			
		||||
rc = 0
 | 
			
		||||
 | 
			
		||||
expected data = b0 00 00 
 | 
			
		||||
expected len = 19
 | 
			
		||||
decoded data = b0 00 00 
 | 
			
		||||
decoded len = 19
 | 
			
		||||
 | 
			
		||||
Test:8
 | 
			
		||||
Tree based decoding:
 | 
			
		||||
uncompressed data = 00 1e 
 | 
			
		||||
len = 14
 | 
			
		||||
rc = -1
 | 
			
		||||
Failed to decode CRBB: length 14, data 00 1e 
 | 
			
		||||
 | 
			
		||||
Test:9
 | 
			
		||||
Tree based decoding:
 | 
			
		||||
uncompressed data = 00 00 00 
 | 
			
		||||
len = 24
 | 
			
		||||
rc = -1
 | 
			
		||||
Failed to decode CRBB: length 24, data 00 00 00 
 | 
			
		||||
							
								
								
									
										2
									
								
								tests/bitcomp/BitcompTest.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/bitcomp/BitcompTest.ok
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
=== start test_EPDAN_decode_tree ===
 | 
			
		||||
=== end test_EPDAN_decode_tree ===
 | 
			
		||||
@@ -134,7 +134,7 @@ static struct log_info info = {};
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	osmo_init_logging(&info);
 | 
			
		||||
	osmo_init_logging2(NULL, &info);
 | 
			
		||||
	log_set_use_color(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_filename(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_log_level(osmo_stderr_target, LOGL_INFO);
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user