forked from sailfishos/ofono
		
	Compare commits
	
		
			1058 Commits
		
	
	
		
			mer/1.18+g
			...
			upgrade-4.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3b27dce313 | ||
| 
						 | 
					c0e67eee01 | ||
| 
						 | 
					ef5ee98508 | ||
| 
						 | 
					4220e7d5e8 | ||
| 
						 | 
					33c067a75f | ||
| 
						 | 
					29616c04d0 | ||
| 
						 | 
					beb997d914 | ||
| 
						 | 
					cefc03e5ed | ||
| 
						 | 
					85d99536ee | ||
| 
						 | 
					ea36baa4c1 | ||
| 
						 | 
					b95a089c00 | ||
| 
						 | 
					c8dbf5494b | ||
| 
						 | 
					cfb75f473d | ||
| 
						 | 
					edd91c94eb | ||
| 
						 | 
					af0ab142e1 | ||
| 
						 | 
					dffc04d404 | ||
| 
						 | 
					97b5fcbd87 | ||
| 
						 | 
					d7e740347f | ||
| 
						 | 
					1116ca2481 | ||
| 
						 | 
					3d147843c4 | ||
| 
						 | 
					c01dc63cbc | ||
| 
						 | 
					297926ed24 | ||
| 
						 | 
					6ef1174ea8 | ||
| 
						 | 
					4ad02792db | ||
| 
						 | 
					eddcb88af4 | ||
| 
						 | 
					f91df7f0fd | ||
| 
						 | 
					9d220ff9be | ||
| 
						 | 
					deefa2c454 | ||
| 
						 | 
					80224b283d | ||
| 
						 | 
					07e07b6ddc | ||
| 
						 | 
					bd836b4499 | ||
| 
						 | 
					6c289b1432 | ||
| 
						 | 
					296e46487f | ||
| 
						 | 
					09e98234aa | ||
| 
						 | 
					0f4cdba932 | ||
| 
						 | 
					870bac93e9 | ||
| 
						 | 
					246cd4e1d2 | ||
| 
						 | 
					ca20b65098 | ||
| 
						 | 
					8e35a047da | ||
| 
						 | 
					2b4b5a224d | ||
| 
						 | 
					50a5f2547e | ||
| 
						 | 
					d682fcd5fe | ||
| 
						 | 
					5799320480 | ||
| 
						 | 
					3eea7c868e | ||
| 
						 | 
					4844fc6cf9 | ||
| 
						 | 
					6976366051 | ||
| 
						 | 
					d3d776837b | ||
| 
						 | 
					f56c8a33b0 | ||
| 
						 | 
					ed2f625b8b | ||
| 
						 | 
					ed62d38632 | ||
| 
						 | 
					3f433c97c5 | ||
| 
						 | 
					86d8149c79 | ||
| 
						 | 
					f3eb9b868b | ||
| 
						 | 
					4e067fa827 | ||
| 
						 | 
					2ee5e4c827 | ||
| 
						 | 
					1366e426be | ||
| 
						 | 
					586c9b9262 | ||
| 
						 | 
					83554e071a | ||
| 
						 | 
					dc41c2d003 | ||
| 
						 | 
					550d41ae37 | ||
| 
						 | 
					60e4246d93 | ||
| 
						 | 
					50619607b0 | ||
| 
						 | 
					98b357f365 | ||
| 
						 | 
					56c488d10c | ||
| 
						 | 
					944cd603e8 | ||
| 
						 | 
					77be0d5e98 | ||
| 
						 | 
					d8dd20092c | ||
| 
						 | 
					c7faa21172 | ||
| 
						 | 
					98ffc61a03 | ||
| 
						 | 
					f2b1625872 | ||
| 
						 | 
					2d5a22284e | ||
| 
						 | 
					9d742180ab | ||
| 
						 | 
					f8b0ccc1b4 | ||
| 
						 | 
					7a54bb8cbe | ||
| 
						 | 
					56e0923dc3 | ||
| 
						 | 
					6dfce4b5e9 | ||
| 
						 | 
					4ec3568d71 | ||
| 
						 | 
					9b2b7127ef | ||
| 
						 | 
					1053577376 | ||
| 
						 | 
					22197b5e04 | ||
| 
						 | 
					3a358ddc9d | ||
| 
						 | 
					53929f9f1a | ||
| 
						 | 
					08bae57a2b | ||
| 
						 | 
					1915aeda76 | ||
| 
						 | 
					51bfb17cbc | ||
| 
						 | 
					a83b553032 | ||
| 
						 | 
					ec00abd62d | ||
| 
						 | 
					80924d5787 | ||
| 
						 | 
					2bdd05aa31 | ||
| 
						 | 
					544f02e5a2 | ||
| 
						 | 
					6d4638f9bf | ||
| 
						 | 
					6584919e9d | ||
| 
						 | 
					34fb44f4eb | ||
| 
						 | 
					c98d2f41c5 | ||
| 
						 | 
					b279be4528 | ||
| 
						 | 
					a7912fea39 | ||
| 
						 | 
					f291cea905 | ||
| 
						 | 
					68f7d30b77 | ||
| 
						 | 
					890a2697fe | ||
| 
						 | 
					9568c8449b | ||
| 
						 | 
					cf91be9742 | ||
| 
						 | 
					087771dc0f | ||
| 
						 | 
					645dfe47e5 | ||
| 
						 | 
					68e8b02d3b | ||
| 
						 | 
					aa4309e8cb | ||
| 
						 | 
					cb6b24d950 | ||
| 
						 | 
					6d1ab13c74 | ||
| 
						 | 
					45424a3f96 | ||
| 
						 | 
					4f7398e39d | ||
| 
						 | 
					cd118ce70b | ||
| 
						 | 
					99d4ce538e | ||
| 
						 | 
					9b2c4bcf76 | ||
| 
						 | 
					0122db04a3 | ||
| 
						 | 
					021db194cb | ||
| 
						 | 
					e0a0896205 | ||
| 
						 | 
					49d0bbbb28 | ||
| 
						 | 
					9193d06b77 | ||
| 
						 | 
					b0cd3e4544 | ||
| 
						 | 
					29cce6969b | ||
| 
						 | 
					b87f666e4b | ||
| 
						 | 
					1c1e4fa28b | ||
| 
						 | 
					f597119845 | ||
| 
						 | 
					633888932d | ||
| 
						 | 
					a37f325d4a | ||
| 
						 | 
					0afceac554 | ||
| 
						 | 
					109751bcc0 | ||
| 
						 | 
					30dfbf8fd7 | ||
| 
						 | 
					c7aab2e790 | ||
| 
						 | 
					412a2a0e7f | ||
| 
						 | 
					f1aeedd113 | ||
| 
						 | 
					639fce8eca | ||
| 
						 | 
					9cfd0a195e | ||
| 
						 | 
					cf2d8488cc | ||
| 
						 | 
					ab0ac10abd | ||
| 
						 | 
					b2df7de223 | ||
| 
						 | 
					b56930a87f | ||
| 
						 | 
					f65e8dd5af | ||
| 
						 | 
					f2439243b2 | ||
| 
						 | 
					93ff644856 | ||
| 
						 | 
					08f8555d51 | ||
| 
						 | 
					04b2a9b0f9 | ||
| 
						 | 
					4d513b68d2 | ||
| 
						 | 
					36f971dc78 | ||
| 
						 | 
					8ba2d96cff | ||
| 
						 | 
					a76f50be67 | ||
| 
						 | 
					43227086c0 | ||
| 
						 | 
					e40811fbc6 | ||
| 
						 | 
					8c543b054a | ||
| 
						 | 
					53233b4dc8 | ||
| 
						 | 
					4458fbf844 | ||
| 
						 | 
					542a086d93 | ||
| 
						 | 
					1176662a81 | ||
| 
						 | 
					ae29a08a76 | ||
| 
						 | 
					af1a569430 | ||
| 
						 | 
					a75558031b | ||
| 
						 | 
					139fff2e5d | ||
| 
						 | 
					12614d377a | ||
| 
						 | 
					463f4f183e | ||
| 
						 | 
					0ba7505f1e | ||
| 
						 | 
					c1156320ac | ||
| 
						 | 
					afe4fc66f0 | ||
| 
						 | 
					c04b14c49a | ||
| 
						 | 
					4d6aefcea5 | ||
| 
						 | 
					c3c4b21c32 | ||
| 
						 | 
					5ffc3fc426 | ||
| 
						 | 
					690d62820a | ||
| 
						 | 
					f1fbd04d66 | ||
| 
						 | 
					8eaf694b9a | ||
| 
						 | 
					5087fd9dd7 | ||
| 
						 | 
					92aebc3ce8 | ||
| 
						 | 
					c0a5b0fdf3 | ||
| 
						 | 
					587879b6c2 | ||
| 
						 | 
					f9ca5c30a6 | ||
| 
						 | 
					3f9dff449f | ||
| 
						 | 
					d554061955 | ||
| 
						 | 
					3ae306ed57 | ||
| 
						 | 
					6448c0c270 | ||
| 
						 | 
					e521938d95 | ||
| 
						 | 
					80158ea5bc | ||
| 
						 | 
					bfba3e2312 | ||
| 
						 | 
					e4fc7b9b0c | ||
| 
						 | 
					f1ec346941 | ||
| 
						 | 
					9f9bb11a66 | ||
| 
						 | 
					cf3143bb69 | ||
| 
						 | 
					56f46a80c0 | ||
| 
						 | 
					6fb02515f0 | ||
| 
						 | 
					4efb502fad | ||
| 
						 | 
					842331f701 | ||
| 
						 | 
					f743c89bc8 | ||
| 
						 | 
					20d9835aed | ||
| 
						 | 
					f4c24f5f83 | ||
| 
						 | 
					6c07b110c7 | ||
| 
						 | 
					6205fad90f | ||
| 
						 | 
					9b7358e5e5 | ||
| 
						 | 
					502cd55c4e | ||
| 
						 | 
					103b20bcfd | ||
| 
						 | 
					8b083e0121 | ||
| 
						 | 
					8b1fc771ea | ||
| 
						 | 
					7bb19531cd | ||
| 
						 | 
					252df1349a | ||
| 
						 | 
					5a2d64371c | ||
| 
						 | 
					6995849600 | ||
| 
						 | 
					69149b3039 | ||
| 
						 | 
					77eb51bc39 | ||
| 
						 | 
					d05b1137d5 | ||
| 
						 | 
					9eae00b28b | ||
| 
						 | 
					48a6492a68 | ||
| 
						 | 
					443898d8ef | ||
| 
						 | 
					23718794cc | ||
| 
						 | 
					450c5f9b69 | ||
| 
						 | 
					4f6b8b7243 | ||
| 
						 | 
					3eec92ec7a | ||
| 
						 | 
					4216026a7c | ||
| 
						 | 
					9eb2820397 | ||
| 
						 | 
					f33550f37f | ||
| 
						 | 
					020dc3020e | ||
| 
						 | 
					ad0b9e6303 | ||
| 
						 | 
					d773d28dad | ||
| 
						 | 
					4d9fea27ea | ||
| 
						 | 
					c4a8186f7a | ||
| 
						 | 
					2c5f1a6626 | ||
| 
						 | 
					dcb057802f | ||
| 
						 | 
					fd889b3fac | ||
| 
						 | 
					fe3f46f29b | ||
| 
						 | 
					b8eae5f967 | ||
| 
						 | 
					d818544d92 | ||
| 
						 | 
					dc1377eb0a | ||
| 
						 | 
					06599ff36d | ||
| 
						 | 
					4d2ef8b2da | ||
| 
						 | 
					cc7f5796bb | ||
| 
						 | 
					65d2b1306a | ||
| 
						 | 
					b484003494 | ||
| 
						 | 
					8a6ec5e645 | ||
| 
						 | 
					fdc4b27b05 | ||
| 
						 | 
					9b338c4055 | ||
| 
						 | 
					fbaf86d862 | ||
| 
						 | 
					d5c6316a13 | ||
| 
						 | 
					bf092b518c | ||
| 
						 | 
					9de95af924 | ||
| 
						 | 
					5988c88968 | ||
| 
						 | 
					354793cbe3 | ||
| 
						 | 
					259c6e2617 | ||
| 
						 | 
					aa88654d09 | ||
| 
						 | 
					50499bc69d | ||
| 
						 | 
					8c64f94743 | ||
| 
						 | 
					0eabc3ea79 | ||
| 
						 | 
					8116bd13d1 | ||
| 
						 | 
					30bbccfd91 | ||
| 
						 | 
					028a6ab1c7 | ||
| 
						 | 
					32607771b5 | ||
| 
						 | 
					d37dfc1ad1 | ||
| 
						 | 
					e0bfba6fbe | ||
| 
						 | 
					414791a6f0 | ||
| 
						 | 
					3fa059a027 | ||
| 
						 | 
					540b558147 | ||
| 
						 | 
					6bfb0d07ab | ||
| 
						 | 
					19a8a137c1 | ||
| 
						 | 
					11913b2a44 | ||
| 
						 | 
					2a1e29ab9e | ||
| 
						 | 
					a64cc4d5b9 | ||
| 
						 | 
					f464a7c6c1 | ||
| 
						 | 
					daceb07bb1 | ||
| 
						 | 
					f9ef31cd5c | ||
| 
						 | 
					c3c3bab6a2 | ||
| 
						 | 
					08f0bb08f1 | ||
| 
						 | 
					ce2b18ede2 | ||
| 
						 | 
					5f9e43c2da | ||
| 
						 | 
					2ee038a457 | ||
| 
						 | 
					3a4f63171f | ||
| 
						 | 
					64878cced6 | ||
| 
						 | 
					3f115b2d0d | ||
| 
						 | 
					c66eec1b1e | ||
| 
						 | 
					85d1eda7c9 | ||
| 
						 | 
					08d8d5de73 | ||
| 
						 | 
					172c97df83 | ||
| 
						 | 
					1e3e413714 | ||
| 
						 | 
					15ef3c9c0e | ||
| 
						 | 
					e9eb113a22 | ||
| 
						 | 
					abdfb38006 | ||
| 
						 | 
					79e22d4570 | ||
| 
						 | 
					aab791f2ad | ||
| 
						 | 
					90c0ffddea | ||
| 
						 | 
					a3149c53d9 | ||
| 
						 | 
					b02a37a9c5 | ||
| 
						 | 
					eecf9fde2c | ||
| 
						 | 
					f2bb5d08ba | ||
| 
						 | 
					d44a1af644 | ||
| 
						 | 
					50fbd5351b | ||
| 
						 | 
					9c8700b5c6 | ||
| 
						 | 
					9221153db9 | ||
| 
						 | 
					8997e02997 | ||
| 
						 | 
					7121855966 | ||
| 
						 | 
					0d013c3f9b | ||
| 
						 | 
					e2299cbddc | ||
| 
						 | 
					34cd275117 | ||
| 
						 | 
					2396ead477 | ||
| 
						 | 
					32dc1d1806 | ||
| 
						 | 
					aed679d6da | ||
| 
						 | 
					baea6fb7e9 | ||
| 
						 | 
					ba64ce870f | ||
| 
						 | 
					1e25fbbcbf | ||
| 
						 | 
					d13339af6c | ||
| 
						 | 
					890f3235cb | ||
| 
						 | 
					2603419fa4 | ||
| 
						 | 
					1abdcc9226 | ||
| 
						 | 
					928a905cce | ||
| 
						 | 
					f241580817 | ||
| 
						 | 
					26826c15c9 | ||
| 
						 | 
					5b3a045e6c | ||
| 
						 | 
					c8a08bd815 | ||
| 
						 | 
					53d1f1ce03 | ||
| 
						 | 
					1aec6df272 | ||
| 
						 | 
					b43df906ca | ||
| 
						 | 
					1d42a2a6a9 | ||
| 
						 | 
					bab1cb2479 | ||
| 
						 | 
					a8be51ceef | ||
| 
						 | 
					a658ec7e77 | ||
| 
						 | 
					960db7cf2b | ||
| 
						 | 
					842fe25bcd | ||
| 
						 | 
					7aa2cbb8cb | ||
| 
						 | 
					612b295eae | ||
| 
						 | 
					168919770d | ||
| 
						 | 
					b603be82ee | ||
| 
						 | 
					bbeaadd191 | ||
| 
						 | 
					99d7c4e884 | ||
| 
						 | 
					9753700d58 | ||
| 
						 | 
					7772c8971b | ||
| 
						 | 
					13b39ba633 | ||
| 
						 | 
					77ac688c5e | ||
| 
						 | 
					f4bbba9547 | ||
| 
						 | 
					17f5b9faa5 | ||
| 
						 | 
					4b266cfbfd | ||
| 
						 | 
					e56998d640 | ||
| 
						 | 
					b87fb13b7b | ||
| 
						 | 
					32753de8a7 | ||
| 
						 | 
					331c6e98d2 | ||
| 
						 | 
					69f1b7b36f | ||
| 
						 | 
					ca6447102f | ||
| 
						 | 
					9b3dc8143d | ||
| 
						 | 
					919df873f3 | ||
| 
						 | 
					7aed70b642 | ||
| 
						 | 
					0776d5b19b | ||
| 
						 | 
					ca4c2c4a07 | ||
| 
						 | 
					eab88cd6cb | ||
| 
						 | 
					0be2675072 | ||
| 
						 | 
					d226729730 | ||
| 
						 | 
					417bbaa963 | ||
| 
						 | 
					9974013cce | ||
| 
						 | 
					53e07c0932 | ||
| 
						 | 
					edce8b06c6 | ||
| 
						 | 
					8d72007e95 | ||
| 
						 | 
					ffa0e801a3 | ||
| 
						 | 
					f6c7117097 | ||
| 
						 | 
					3d9013eea8 | ||
| 
						 | 
					b76517559b | ||
| 
						 | 
					6bdb51dc29 | ||
| 
						 | 
					4f066a2133 | ||
| 
						 | 
					61d87e5cf9 | ||
| 
						 | 
					3d33bea585 | ||
| 
						 | 
					a741db6087 | ||
| 
						 | 
					f9a175b1a8 | ||
| 
						 | 
					04133f8316 | ||
| 
						 | 
					2e6ae0f001 | ||
| 
						 | 
					432c05928b | ||
| 
						 | 
					38054818ed | ||
| 
						 | 
					f3e4550d67 | ||
| 
						 | 
					a91c8de5c2 | ||
| 
						 | 
					9c29518418 | ||
| 
						 | 
					951e9439d4 | ||
| 
						 | 
					7cd984aa1a | ||
| 
						 | 
					06227e5e50 | ||
| 
						 | 
					bba23c3095 | ||
| 
						 | 
					a892edaea5 | ||
| 
						 | 
					2ede8f2464 | ||
| 
						 | 
					1759502c96 | ||
| 
						 | 
					134efba989 | ||
| 
						 | 
					9331cc1ecb | ||
| 
						 | 
					7c8da34a38 | ||
| 
						 | 
					a05523974e | ||
| 
						 | 
					71ef390b4a | ||
| 
						 | 
					717f6452aa | ||
| 
						 | 
					0803c21840 | ||
| 
						 | 
					095060b001 | ||
| 
						 | 
					c2971da092 | ||
| 
						 | 
					f07424f0aa | ||
| 
						 | 
					266a52a40a | ||
| 
						 | 
					eeea5476d1 | ||
| 
						 | 
					e38a63d179 | ||
| 
						 | 
					0ff8608ac3 | ||
| 
						 | 
					5a330b9852 | ||
| 
						 | 
					78a9323619 | ||
| 
						 | 
					8267e206eb | ||
| 
						 | 
					fac7684958 | ||
| 
						 | 
					6ba3170ce2 | ||
| 
						 | 
					b29730b268 | ||
| 
						 | 
					e095636c97 | ||
| 
						 | 
					6fef5444fb | ||
| 
						 | 
					0e62e613e8 | ||
| 
						 | 
					c08be69130 | ||
| 
						 | 
					419caedc2c | ||
| 
						 | 
					ee6a307804 | ||
| 
						 | 
					412d8c3d4d | ||
| 
						 | 
					0efebd16d9 | ||
| 
						 | 
					7a6928c02f | ||
| 
						 | 
					ec134e68d2 | ||
| 
						 | 
					a8be769c87 | ||
| 
						 | 
					a2d87f64c4 | ||
| 
						 | 
					3ecd55a205 | ||
| 
						 | 
					d8ea82b2f1 | ||
| 
						 | 
					c95fe16a9b | ||
| 
						 | 
					55e923250a | ||
| 
						 | 
					f5653ae240 | ||
| 
						 | 
					ecf23c1333 | ||
| 
						 | 
					e71036f7d7 | ||
| 
						 | 
					b8e8b930f8 | ||
| 
						 | 
					65a3f7ee46 | ||
| 
						 | 
					26c5c4bfa3 | ||
| 
						 | 
					2d35e5e28d | ||
| 
						 | 
					ae78d9a946 | ||
| 
						 | 
					243dd7d17c | ||
| 
						 | 
					acaafafbb9 | ||
| 
						 | 
					4f378c806b | ||
| 
						 | 
					bd33ff471c | ||
| 
						 | 
					d423608e46 | ||
| 
						 | 
					3b708effd9 | ||
| 
						 | 
					f01722cca5 | ||
| 
						 | 
					f62d53fbd0 | ||
| 
						 | 
					942aee3f25 | ||
| 
						 | 
					ecc83568fd | ||
| 
						 | 
					c911c05fcb | ||
| 
						 | 
					680979f782 | ||
| 
						 | 
					250a6abb71 | ||
| 
						 | 
					6c5d2ab803 | ||
| 
						 | 
					bf8cb3995c | ||
| 
						 | 
					8973e52e45 | ||
| 
						 | 
					0e8dc3605e | ||
| 
						 | 
					537a39f94a | ||
| 
						 | 
					c3d93e83d7 | ||
| 
						 | 
					7cdf3db124 | ||
| 
						 | 
					398942c78e | ||
| 
						 | 
					26e39508ad | ||
| 
						 | 
					a16fcd0d37 | ||
| 
						 | 
					432e700272 | ||
| 
						 | 
					aa694b592f | ||
| 
						 | 
					c5c8b72761 | ||
| 
						 | 
					2ab7aa0f97 | ||
| 
						 | 
					549fe2355f | ||
| 
						 | 
					7493187e47 | ||
| 
						 | 
					9a3d8d671c | ||
| 
						 | 
					39eac13743 | ||
| 
						 | 
					6329bb8639 | ||
| 
						 | 
					75b07c5c80 | ||
| 
						 | 
					4f6f964ca4 | ||
| 
						 | 
					7af95f6db5 | ||
| 
						 | 
					99f4667eb7 | ||
| 
						 | 
					c1c3ecab31 | ||
| 
						 | 
					83dc99658c | ||
| 
						 | 
					9a7b538087 | ||
| 
						 | 
					9f7a1ffe3f | ||
| 
						 | 
					1f81ec7d9d | ||
| 
						 | 
					6e833401cc | ||
| 
						 | 
					d9c68c4fb9 | ||
| 
						 | 
					9e6f7721a0 | ||
| 
						 | 
					9c529dcdcc | ||
| 
						 | 
					41814c6e6a | ||
| 
						 | 
					cf99a5769f | ||
| 
						 | 
					076e2f0ef1 | ||
| 
						 | 
					fd76cb72ad | ||
| 
						 | 
					554e4ab8e5 | ||
| 
						 | 
					08f3da7577 | ||
| 
						 | 
					2cbd3b6050 | ||
| 
						 | 
					78d3d1892d | ||
| 
						 | 
					1448bd2320 | ||
| 
						 | 
					ea8dfb48ab | ||
| 
						 | 
					80921e8b7e | ||
| 
						 | 
					e4cc912719 | ||
| 
						 | 
					c5f736d3c3 | ||
| 
						 | 
					ddf4cec9b8 | ||
| 
						 | 
					685d0b34d7 | ||
| 
						 | 
					896f2f7a71 | ||
| 
						 | 
					e96aacb9e7 | ||
| 
						 | 
					91560afeec | ||
| 
						 | 
					09fb8635c9 | ||
| 
						 | 
					1cb80d7d2f | ||
| 
						 | 
					7db5552e79 | ||
| 
						 | 
					d87e40d0ff | ||
| 
						 | 
					35131ff56b | ||
| 
						 | 
					c5cc678b2b | ||
| 
						 | 
					31be9a099b | ||
| 
						 | 
					ccaf993977 | ||
| 
						 | 
					74d633c58e | ||
| 
						 | 
					f870880cf9 | ||
| 
						 | 
					50c06afc73 | ||
| 
						 | 
					e4e0ccd51d | ||
| 
						 | 
					ee052af454 | ||
| 
						 | 
					296b272274 | ||
| 
						 | 
					9b9e5159f5 | ||
| 
						 | 
					fa8002200c | ||
| 
						 | 
					4cc71c78ec | ||
| 
						 | 
					27b31e65bb | ||
| 
						 | 
					e26d365a94 | ||
| 
						 | 
					63f06cd11c | ||
| 
						 | 
					96ca3aa907 | ||
| 
						 | 
					11a84853fe | ||
| 
						 | 
					a393cf0b11 | ||
| 
						 | 
					6f263ee8d5 | ||
| 
						 | 
					92a4760f46 | ||
| 
						 | 
					c43d41829f | ||
| 
						 | 
					25638a30c0 | ||
| 
						 | 
					ed669bf66c | ||
| 
						 | 
					e01dbd2b21 | ||
| 
						 | 
					a8f0f26df8 | ||
| 
						 | 
					56c84395ba | ||
| 
						 | 
					3bf2b1df5c | ||
| 
						 | 
					75041ccc37 | ||
| 
						 | 
					e91ef8a701 | ||
| 
						 | 
					620a20abdc | ||
| 
						 | 
					d85fa8a64d | ||
| 
						 | 
					d33b20889b | ||
| 
						 | 
					cb8801752c | ||
| 
						 | 
					a0722f8538 | ||
| 
						 | 
					e016281b86 | ||
| 
						 | 
					781a528625 | ||
| 
						 | 
					5b1ab91b77 | ||
| 
						 | 
					9604d9ef0a | ||
| 
						 | 
					598acaa1a8 | ||
| 
						 | 
					60193032f5 | ||
| 
						 | 
					9faf27ec28 | ||
| 
						 | 
					32c26c5a35 | ||
| 
						 | 
					79fb591342 | ||
| 
						 | 
					f6e46f78e3 | ||
| 
						 | 
					7c587772d1 | ||
| 
						 | 
					0d0728593b | ||
| 
						 | 
					fd3916b2c7 | ||
| 
						 | 
					c35557c2ed | ||
| 
						 | 
					bb07543dd6 | ||
| 
						 | 
					d346f1289c | ||
| 
						 | 
					e170b6df4c | ||
| 
						 | 
					761cd320bb | ||
| 
						 | 
					60bc47aea2 | ||
| 
						 | 
					183e4dab4b | ||
| 
						 | 
					d6cdfc92ad | ||
| 
						 | 
					b68752640c | ||
| 
						 | 
					a53fc6ea7e | ||
| 
						 | 
					63fe971077 | ||
| 
						 | 
					011f3b74d1 | ||
| 
						 | 
					4d2e314ad6 | ||
| 
						 | 
					d846618057 | ||
| 
						 | 
					38115199f7 | ||
| 
						 | 
					f88c7ce919 | ||
| 
						 | 
					9d6b3ec124 | ||
| 
						 | 
					6dcf5cebc1 | ||
| 
						 | 
					0e87392c90 | ||
| 
						 | 
					dab76692db | ||
| 
						 | 
					21bc90f638 | ||
| 
						 | 
					d8707d52be | ||
| 
						 | 
					fa0abf892d | ||
| 
						 | 
					8a28d4eea8 | ||
| 
						 | 
					4f0be99683 | ||
| 
						 | 
					95933beb2d | ||
| 
						 | 
					018a712e29 | ||
| 
						 | 
					e6777f1ecc | ||
| 
						 | 
					a58e1a5e9b | ||
| 
						 | 
					61be41240f | ||
| 
						 | 
					dbb40560c6 | ||
| 
						 | 
					9bf50bb3e3 | ||
| 
						 | 
					d2353c46a8 | ||
| 
						 | 
					6701b53737 | ||
| 
						 | 
					6612bfa1da | ||
| 
						 | 
					c732d5192d | ||
| 
						 | 
					6815772b17 | ||
| 
						 | 
					e4766ef2c4 | ||
| 
						 | 
					7aa396636b | ||
| 
						 | 
					096cd04044 | ||
| 
						 | 
					5eabe96602 | ||
| 
						 | 
					05dec021c0 | ||
| 
						 | 
					6f7209b045 | ||
| 
						 | 
					a766281a02 | ||
| 
						 | 
					13b4802bec | ||
| 
						 | 
					e1547fdaf4 | ||
| 
						 | 
					08c36b2885 | ||
| 
						 | 
					0180c9febf | ||
| 
						 | 
					49034cfc69 | ||
| 
						 | 
					4685e3f0de | ||
| 
						 | 
					72be5bdff2 | ||
| 
						 | 
					79c1abfdd3 | ||
| 
						 | 
					c88cffaa2e | ||
| 
						 | 
					17e66090ec | ||
| 
						 | 
					100cf7df1d | ||
| 
						 | 
					280ed19215 | ||
| 
						 | 
					ae0f5b0ff6 | ||
| 
						 | 
					dbfc642eb3 | ||
| 
						 | 
					e5e5108913 | ||
| 
						 | 
					9035db3129 | ||
| 
						 | 
					81391a4101 | ||
| 
						 | 
					60d449c01d | ||
| 
						 | 
					b0ccc39866 | ||
| 
						 | 
					bd2aa28405 | ||
| 
						 | 
					67e31bb519 | ||
| 
						 | 
					b848827976 | ||
| 
						 | 
					ab6764dcc0 | ||
| 
						 | 
					da42039c80 | ||
| 
						 | 
					392c00c65e | ||
| 
						 | 
					7a0fe98d95 | ||
| 
						 | 
					d508a2f2bb | ||
| 
						 | 
					8f070cf583 | ||
| 
						 | 
					d2d8117723 | ||
| 
						 | 
					6897e57353 | ||
| 
						 | 
					6a8c2aa9c1 | ||
| 
						 | 
					076e388d45 | ||
| 
						 | 
					bcafe0dc3d | ||
| 
						 | 
					9272075f55 | ||
| 
						 | 
					526072d7a3 | ||
| 
						 | 
					0680063527 | ||
| 
						 | 
					4e08680e5f | ||
| 
						 | 
					1d85caa7f9 | ||
| 
						 | 
					db0ef91c81 | ||
| 
						 | 
					54d8c78a50 | ||
| 
						 | 
					905c886269 | ||
| 
						 | 
					f0c7a373ae | ||
| 
						 | 
					4673da16d5 | ||
| 
						 | 
					0dc2acee4e | ||
| 
						 | 
					f749284029 | ||
| 
						 | 
					778b9f08aa | ||
| 
						 | 
					40db3f7067 | ||
| 
						 | 
					e198cf04c0 | ||
| 
						 | 
					fbd59ba56f | ||
| 
						 | 
					cff9ded7e6 | ||
| 
						 | 
					c74386b5e6 | ||
| 
						 | 
					028f54c26f | ||
| 
						 | 
					c066f34ea1 | ||
| 
						 | 
					b1c79d5cae | ||
| 
						 | 
					0cc61dcfe8 | ||
| 
						 | 
					5852bebda0 | ||
| 
						 | 
					768c028a11 | ||
| 
						 | 
					6b93ea0cc6 | ||
| 
						 | 
					4a485a7aa0 | ||
| 
						 | 
					f6fb277cf4 | ||
| 
						 | 
					03f150838b | ||
| 
						 | 
					9731ca1a87 | ||
| 
						 | 
					a09f4c070d | ||
| 
						 | 
					f018f5a255 | ||
| 
						 | 
					286396bf91 | ||
| 
						 | 
					5d6baccced | ||
| 
						 | 
					514f4cf9cc | ||
| 
						 | 
					ff8408e6dd | ||
| 
						 | 
					df1294d77c | ||
| 
						 | 
					2323ebacb3 | ||
| 
						 | 
					c780eff0ce | ||
| 
						 | 
					373248a35b | ||
| 
						 | 
					fe219b648d | ||
| 
						 | 
					33f55c569f | ||
| 
						 | 
					d6cf954354 | ||
| 
						 | 
					d8e852cb5e | ||
| 
						 | 
					83e3ec0e98 | ||
| 
						 | 
					e35f537f72 | ||
| 
						 | 
					c7daf5aa43 | ||
| 
						 | 
					490a9c06f4 | ||
| 
						 | 
					320b3f4605 | ||
| 
						 | 
					e35dae17d9 | ||
| 
						 | 
					f4522f4a00 | ||
| 
						 | 
					ce85c94426 | ||
| 
						 | 
					4027bdc04e | ||
| 
						 | 
					c57f99bf01 | ||
| 
						 | 
					54d610ce6a | ||
| 
						 | 
					f7f9e32743 | ||
| 
						 | 
					8c9e370486 | ||
| 
						 | 
					19b80236f6 | ||
| 
						 | 
					2ec6fc749d | ||
| 
						 | 
					0a3bdd20f4 | ||
| 
						 | 
					284919e76a | ||
| 
						 | 
					ddcbb89fa1 | ||
| 
						 | 
					5ec6b8e7ec | ||
| 
						 | 
					f94681f6f6 | ||
| 
						 | 
					de8edc84fa | ||
| 
						 | 
					90faf1b3a5 | ||
| 
						 | 
					2b139b6974 | ||
| 
						 | 
					168f193efb | ||
| 
						 | 
					32d8b5ccfc | ||
| 
						 | 
					f400ceff80 | ||
| 
						 | 
					2186c60630 | ||
| 
						 | 
					d5fb195e2f | ||
| 
						 | 
					cbb08079d2 | ||
| 
						 | 
					0583a831fb | ||
| 
						 | 
					a8a0758e90 | ||
| 
						 | 
					9e267487f4 | ||
| 
						 | 
					c8a774dfee | ||
| 
						 | 
					b88518d0f3 | ||
| 
						 | 
					b223ccc675 | ||
| 
						 | 
					947a41a5fc | ||
| 
						 | 
					e0b4e8694d | ||
| 
						 | 
					c8db770c99 | ||
| 
						 | 
					1534143e31 | ||
| 
						 | 
					d9ad9caf30 | ||
| 
						 | 
					71de574e87 | ||
| 
						 | 
					11efbd68e6 | ||
| 
						 | 
					9981f07797 | ||
| 
						 | 
					24733f776e | ||
| 
						 | 
					50ec234239 | ||
| 
						 | 
					b4991076c6 | ||
| 
						 | 
					8b02884696 | ||
| 
						 | 
					8e224a21f6 | ||
| 
						 | 
					96e191b2d2 | ||
| 
						 | 
					7ae3aad622 | ||
| 
						 | 
					3d5d88241e | ||
| 
						 | 
					31e62567e6 | ||
| 
						 | 
					8c3127ef21 | ||
| 
						 | 
					1c1fc4199e | ||
| 
						 | 
					bfe2f95c4c | ||
| 
						 | 
					7e4d99236b | ||
| 
						 | 
					0935a227be | ||
| 
						 | 
					ffdeb3692c | ||
| 
						 | 
					627904e382 | ||
| 
						 | 
					0ab0677765 | ||
| 
						 | 
					fe6af108ca | ||
| 
						 | 
					650ff3642f | ||
| 
						 | 
					41d310aa61 | ||
| 
						 | 
					27adf83a4b | ||
| 
						 | 
					55d227ba46 | ||
| 
						 | 
					dcc1d366f0 | ||
| 
						 | 
					83cf94824d | ||
| 
						 | 
					3a0c598805 | ||
| 
						 | 
					b098314251 | ||
| 
						 | 
					4ae6c6c0b1 | ||
| 
						 | 
					d5f0f3b32d | ||
| 
						 | 
					0209e9847b | ||
| 
						 | 
					a499ac07ca | ||
| 
						 | 
					04342bbe69 | ||
| 
						 | 
					d0d3e4f2f1 | ||
| 
						 | 
					edcbc5c7e3 | ||
| 
						 | 
					1df55e3042 | ||
| 
						 | 
					df93fceb4f | ||
| 
						 | 
					d21d1a166f | ||
| 
						 | 
					458f905262 | ||
| 
						 | 
					9f474ba723 | ||
| 
						 | 
					a3b4421422 | ||
| 
						 | 
					28bc1e37ed | ||
| 
						 | 
					c0b96a4319 | ||
| 
						 | 
					4296616d00 | ||
| 
						 | 
					3a43f96fe4 | ||
| 
						 | 
					b82a1001e2 | ||
| 
						 | 
					84dc7e2016 | ||
| 
						 | 
					1482728a61 | ||
| 
						 | 
					428f62041b | ||
| 
						 | 
					4b6ec99973 | ||
| 
						 | 
					0c01da5378 | ||
| 
						 | 
					8004756c3d | ||
| 
						 | 
					e01df1a3f1 | ||
| 
						 | 
					46820a7ba0 | ||
| 
						 | 
					0493629a9d | ||
| 
						 | 
					41b3459a5d | ||
| 
						 | 
					b27373c8a4 | ||
| 
						 | 
					b450c8fbe3 | ||
| 
						 | 
					1ac24f32e3 | ||
| 
						 | 
					977fc5bc15 | ||
| 
						 | 
					787bddf47b | ||
| 
						 | 
					c32cd532f2 | ||
| 
						 | 
					18f2345124 | ||
| 
						 | 
					00b623e8c4 | ||
| 
						 | 
					452108d058 | ||
| 
						 | 
					6b0712dae4 | ||
| 
						 | 
					9a309f499b | ||
| 
						 | 
					a204c993e5 | ||
| 
						 | 
					e881376127 | ||
| 
						 | 
					66c98d724c | ||
| 
						 | 
					5c38fe6a84 | ||
| 
						 | 
					e3bb317504 | ||
| 
						 | 
					713022a7e8 | ||
| 
						 | 
					6b79f32715 | ||
| 
						 | 
					2386e99ad8 | ||
| 
						 | 
					6ca82960c9 | ||
| 
						 | 
					93891578fc | ||
| 
						 | 
					7bcadcd300 | ||
| 
						 | 
					b3f8dc4a24 | ||
| 
						 | 
					38e3122217 | ||
| 
						 | 
					f7de0ab3ef | ||
| 
						 | 
					defe008062 | ||
| 
						 | 
					ec930e17c8 | ||
| 
						 | 
					9f1731cffa | ||
| 
						 | 
					ed8d55d2d5 | ||
| 
						 | 
					7b6a461b83 | ||
| 
						 | 
					3b0ff8fd83 | ||
| 
						 | 
					00b5886cf9 | ||
| 
						 | 
					c16fd4e642 | ||
| 
						 | 
					4b92ac8ba6 | ||
| 
						 | 
					5b432b8280 | ||
| 
						 | 
					31aff54463 | ||
| 
						 | 
					c669ec3c88 | ||
| 
						 | 
					8c2f54abe0 | ||
| 
						 | 
					804121cbed | ||
| 
						 | 
					839e626ee6 | ||
| 
						 | 
					3a17724136 | ||
| 
						 | 
					7b0c6610e0 | ||
| 
						 | 
					0e0b1e98c5 | ||
| 
						 | 
					b0975c44b1 | ||
| 
						 | 
					25a6049cf6 | ||
| 
						 | 
					02fcbdb245 | ||
| 
						 | 
					a3a8ea4183 | ||
| 
						 | 
					92d7fb848b | ||
| 
						 | 
					0b6327a7fc | ||
| 
						 | 
					84bd588152 | ||
| 
						 | 
					9e952cf042 | ||
| 
						 | 
					27a1a05aa7 | ||
| 
						 | 
					657841e2b0 | ||
| 
						 | 
					02172f6922 | ||
| 
						 | 
					0dd225b594 | ||
| 
						 | 
					452d0d4b5a | ||
| 
						 | 
					2edae61c0b | ||
| 
						 | 
					141abd5390 | ||
| 
						 | 
					22faa0f26a | ||
| 
						 | 
					4d2453f3a8 | ||
| 
						 | 
					7a5f52c1f3 | ||
| 
						 | 
					1ad109f8c7 | ||
| 
						 | 
					1347755b6f | ||
| 
						 | 
					62253744a7 | ||
| 
						 | 
					9a608210cd | ||
| 
						 | 
					adbfdb23a7 | ||
| 
						 | 
					ac5d0abe5e | ||
| 
						 | 
					8dbaaa5efe | ||
| 
						 | 
					fa1bcc1c19 | ||
| 
						 | 
					32138ecd04 | ||
| 
						 | 
					5e999f0b47 | ||
| 
						 | 
					5c74095f44 | ||
| 
						 | 
					55befb87cd | ||
| 
						 | 
					9d7a0f8615 | ||
| 
						 | 
					974100732c | ||
| 
						 | 
					35a6a4d8d0 | ||
| 
						 | 
					f2a64c4d15 | ||
| 
						 | 
					ed1e90990e | ||
| 
						 | 
					c8a4727243 | ||
| 
						 | 
					2ccabbbdef | ||
| 
						 | 
					7c3638143d | ||
| 
						 | 
					a0e8b24c70 | ||
| 
						 | 
					41d432211e | ||
| 
						 | 
					94f6138e23 | ||
| 
						 | 
					e82ce81858 | ||
| 
						 | 
					c18fa5e038 | ||
| 
						 | 
					c7c53adbb5 | ||
| 
						 | 
					30a9ef7e7a | ||
| 
						 | 
					064181f903 | ||
| 
						 | 
					7d22ed86f8 | ||
| 
						 | 
					0641a981d1 | ||
| 
						 | 
					ceb6741a67 | ||
| 
						 | 
					4797cab10b | ||
| 
						 | 
					fbf001bbec | ||
| 
						 | 
					8e90e96509 | ||
| 
						 | 
					80e9b97036 | ||
| 
						 | 
					01103f32ae | ||
| 
						 | 
					4bef0c7b33 | ||
| 
						 | 
					693d5a77bd | ||
| 
						 | 
					a2333ead45 | ||
| 
						 | 
					aa6a436af5 | ||
| 
						 | 
					ee350d6b4b | ||
| 
						 | 
					26b85c0606 | ||
| 
						 | 
					068190a7a5 | ||
| 
						 | 
					1b292f7cf2 | ||
| 
						 | 
					cd9a19c090 | ||
| 
						 | 
					6d357e70a4 | ||
| 
						 | 
					8f4817106d | ||
| 
						 | 
					d2ce689008 | ||
| 
						 | 
					b470166c87 | ||
| 
						 | 
					158a0da0b2 | ||
| 
						 | 
					b4bbf0462c | ||
| 
						 | 
					d80b96790f | ||
| 
						 | 
					accb571fd6 | ||
| 
						 | 
					523a4b6a81 | ||
| 
						 | 
					9d8a6a4978 | ||
| 
						 | 
					f2fa85aa47 | ||
| 
						 | 
					a189d13b4a | ||
| 
						 | 
					3b79a77d78 | ||
| 
						 | 
					c2ee34e51c | ||
| 
						 | 
					c3bead1c9b | ||
| 
						 | 
					a26f1a4b5c | ||
| 
						 | 
					1eacfdf592 | ||
| 
						 | 
					ba14ed43e4 | ||
| 
						 | 
					802b3008be | ||
| 
						 | 
					3b0191d145 | ||
| 
						 | 
					282d32f70d | ||
| 
						 | 
					e0c349a18c | ||
| 
						 | 
					69d65dc002 | ||
| 
						 | 
					33e70ddce4 | ||
| 
						 | 
					d3ada8fcb3 | ||
| 
						 | 
					4c21ca4e26 | ||
| 
						 | 
					a3301ec1d2 | ||
| 
						 | 
					6f11bfc632 | ||
| 
						 | 
					74262b9ef8 | ||
| 
						 | 
					199a610607 | ||
| 
						 | 
					af2d223f0f | ||
| 
						 | 
					0b6fcf8b71 | ||
| 
						 | 
					cc05aeccd1 | ||
| 
						 | 
					5728444ad3 | ||
| 
						 | 
					4cbb6b5919 | ||
| 
						 | 
					5699bb4932 | ||
| 
						 | 
					09fa97c53a | ||
| 
						 | 
					3eaa8a46bd | ||
| 
						 | 
					4401319136 | ||
| 
						 | 
					472ddcf0b1 | ||
| 
						 | 
					b7e0f276a1 | ||
| 
						 | 
					5d251aea3a | ||
| 
						 | 
					cdc0065284 | ||
| 
						 | 
					6d65dc5bf0 | ||
| 
						 | 
					5d02c0bba4 | ||
| 
						 | 
					b99513e080 | ||
| 
						 | 
					3cf328c781 | ||
| 
						 | 
					bce68611a1 | ||
| 
						 | 
					cc497feee7 | ||
| 
						 | 
					725606af8d | ||
| 
						 | 
					52db6e5459 | ||
| 
						 | 
					83441bc203 | ||
| 
						 | 
					4054f09b60 | ||
| 
						 | 
					38aa8cff87 | ||
| 
						 | 
					4959292938 | ||
| 
						 | 
					c69cea52cf | ||
| 
						 | 
					4e0b8f7b48 | ||
| 
						 | 
					9c87063c4a | ||
| 
						 | 
					dc5a87f4f2 | ||
| 
						 | 
					ecd35181a3 | ||
| 
						 | 
					96d6daf67e | ||
| 
						 | 
					e9702f6ec1 | ||
| 
						 | 
					0746c615bc | ||
| 
						 | 
					970020a3b0 | ||
| 
						 | 
					f3d5e3d5c6 | ||
| 
						 | 
					bb880ab14a | ||
| 
						 | 
					922d5e17ee | ||
| 
						 | 
					7886ce04a1 | ||
| 
						 | 
					5213398826 | ||
| 
						 | 
					0bbcc127be | ||
| 
						 | 
					f7db0a0459 | ||
| 
						 | 
					2d18086c80 | ||
| 
						 | 
					0f4560c2eb | ||
| 
						 | 
					7d80344d6b | ||
| 
						 | 
					c0c4148099 | ||
| 
						 | 
					34755f1a79 | ||
| 
						 | 
					282d560c37 | ||
| 
						 | 
					e4bca84876 | ||
| 
						 | 
					aa0ded78b0 | ||
| 
						 | 
					eb15b12caf | ||
| 
						 | 
					81b5c716e2 | ||
| 
						 | 
					33c330988f | ||
| 
						 | 
					910057a265 | ||
| 
						 | 
					19f0f8d96e | ||
| 
						 | 
					f1f3c17c4c | ||
| 
						 | 
					29d891cbce | ||
| 
						 | 
					89fa0d5d6a | ||
| 
						 | 
					c382d9f456 | ||
| 
						 | 
					b209b6bee6 | ||
| 
						 | 
					ee3323e98b | ||
| 
						 | 
					9200e387e1 | ||
| 
						 | 
					2bc58a7f6f | ||
| 
						 | 
					35079b11fe | ||
| 
						 | 
					955d5882b2 | ||
| 
						 | 
					2583fa99ce | ||
| 
						 | 
					b8bb15ce9c | ||
| 
						 | 
					cbf24c7b08 | ||
| 
						 | 
					a4c4d1526e | ||
| 
						 | 
					18d1a8834a | ||
| 
						 | 
					8343d96db5 | ||
| 
						 | 
					415fce9368 | ||
| 
						 | 
					33257a139d | ||
| 
						 | 
					f580867c12 | ||
| 
						 | 
					edaba80ad1 | ||
| 
						 | 
					e68314b07d | ||
| 
						 | 
					d13e48b638 | ||
| 
						 | 
					e0edfca358 | ||
| 
						 | 
					7cd2075ada | ||
| 
						 | 
					3ccacfd5f7 | ||
| 
						 | 
					5ee13f8e2c | ||
| 
						 | 
					6ab9dcb553 | ||
| 
						 | 
					102061107a | ||
| 
						 | 
					2bb7d629f5 | ||
| 
						 | 
					5ce01787e8 | ||
| 
						 | 
					1d57cb0e73 | ||
| 
						 | 
					3d84c0a120 | ||
| 
						 | 
					091cf21c0b | ||
| 
						 | 
					351ac1e9db | ||
| 
						 | 
					b7481a918f | ||
| 
						 | 
					e1e4381105 | ||
| 
						 | 
					cc3ca52e61 | ||
| 
						 | 
					a71779ea2a | ||
| 
						 | 
					a9d2849bbb | ||
| 
						 | 
					ca29c8e538 | ||
| 
						 | 
					85fe1b7174 | ||
| 
						 | 
					56e0d9dffa | ||
| 
						 | 
					6867ba65cb | ||
| 
						 | 
					6199eaa4d8 | ||
| 
						 | 
					fabdd6799c | ||
| 
						 | 
					1219ab6a3f | ||
| 
						 | 
					85a956d9eb | ||
| 
						 | 
					c83d992a3b | ||
| 
						 | 
					b22027017c | ||
| 
						 | 
					1fa137b36d | ||
| 
						 | 
					cfd837b1db | ||
| 
						 | 
					735ad21e89 | ||
| 
						 | 
					c9078404de | ||
| 
						 | 
					e375195c92 | ||
| 
						 | 
					ef5610f741 | ||
| 
						 | 
					aef9bbd3e0 | ||
| 
						 | 
					c6eb410f21 | ||
| 
						 | 
					08b3ea3d0f | ||
| 
						 | 
					2978862417 | ||
| 
						 | 
					19228c9e67 | ||
| 
						 | 
					9be791d531 | ||
| 
						 | 
					6b9eb7bf8f | ||
| 
						 | 
					01f8989aee | ||
| 
						 | 
					2f5efaf591 | ||
| 
						 | 
					ca1d06c37a | ||
| 
						 | 
					5f45928a84 | ||
| 
						 | 
					19f74e6c85 | ||
| 
						 | 
					41d5cfcab2 | ||
| 
						 | 
					357c5db580 | ||
| 
						 | 
					8cea5b9f96 | ||
| 
						 | 
					5fb35d5fb4 | ||
| 
						 | 
					e8d57bb928 | ||
| 
						 | 
					2bfde2418e | ||
| 
						 | 
					c710ce76c1 | ||
| 
						 | 
					78acd90464 | ||
| 
						 | 
					e51b3ca0c8 | ||
| 
						 | 
					b3c8813bd4 | ||
| 
						 | 
					e2a3acd9d0 | ||
| 
						 | 
					2054ca9570 | ||
| 
						 | 
					48dbb7912a | ||
| 
						 | 
					d7e7ad671d | ||
| 
						 | 
					5b5a86dc80 | ||
| 
						 | 
					c232524e99 | ||
| 
						 | 
					bfd09a5c14 | ||
| 
						 | 
					f8adcd2550 | ||
| 
						 | 
					2a97567147 | ||
| 
						 | 
					04d84b615e | ||
| 
						 | 
					6e34792323 | ||
| 
						 | 
					d6a59f5dc4 | ||
| 
						 | 
					23e299055f | ||
| 
						 | 
					a56ef3ba0f | ||
| 
						 | 
					7294433906 | ||
| 
						 | 
					d7263cd344 | ||
| 
						 | 
					2f3b469fbb | ||
| 
						 | 
					4187e7ee8f | ||
| 
						 | 
					4d3f89bae0 | ||
| 
						 | 
					cbd1c5d524 | ||
| 
						 | 
					7976e44746 | ||
| 
						 | 
					3d6e220686 | ||
| 
						 | 
					919526d392 | ||
| 
						 | 
					b7082146e8 | ||
| 
						 | 
					094a296a14 | ||
| 
						 | 
					5c259e751b | ||
| 
						 | 
					69e5d5b356 | ||
| 
						 | 
					56e7d0e8ea | ||
| 
						 | 
					bb2ae6d1a1 | ||
| 
						 | 
					cf7692db49 | ||
| 
						 | 
					63f3311cd6 | ||
| 
						 | 
					c5aae77d41 | ||
| 
						 | 
					f8e21c8ad4 | ||
| 
						 | 
					8e6dfe433b | ||
| 
						 | 
					ae23bb552b | ||
| 
						 | 
					2becd051d4 | ||
| 
						 | 
					8cfb1d5ca3 | ||
| 
						 | 
					50f35458f6 | ||
| 
						 | 
					51843accf7 | ||
| 
						 | 
					fb856dc7d6 | 
							
								
								
									
										61
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										61
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,61 +0,0 @@
 | 
			
		||||
*.o
 | 
			
		||||
*.lo
 | 
			
		||||
*.la
 | 
			
		||||
.deps
 | 
			
		||||
.libs
 | 
			
		||||
.dirstamp
 | 
			
		||||
Makefile
 | 
			
		||||
Makefile.in
 | 
			
		||||
aclocal.m4
 | 
			
		||||
config.guess
 | 
			
		||||
config.h
 | 
			
		||||
config.h.in
 | 
			
		||||
config.log
 | 
			
		||||
config.status
 | 
			
		||||
config.sub
 | 
			
		||||
configure
 | 
			
		||||
depcomp
 | 
			
		||||
compile
 | 
			
		||||
install-sh
 | 
			
		||||
libtool
 | 
			
		||||
ltmain.sh
 | 
			
		||||
missing
 | 
			
		||||
stamp-h1
 | 
			
		||||
autom4te.cache
 | 
			
		||||
test-driver
 | 
			
		||||
test-suite.log
 | 
			
		||||
 | 
			
		||||
ofono.pc
 | 
			
		||||
include/ofono
 | 
			
		||||
include/version.h
 | 
			
		||||
src/builtin.h
 | 
			
		||||
src/ofonod
 | 
			
		||||
src/ofono.service
 | 
			
		||||
dundee/dundee
 | 
			
		||||
dundee/dundee.service
 | 
			
		||||
 | 
			
		||||
unit/test-common
 | 
			
		||||
unit/test-util
 | 
			
		||||
unit/test-idmap
 | 
			
		||||
unit/test-sms
 | 
			
		||||
unit/test-sms-root
 | 
			
		||||
unit/test-simutil
 | 
			
		||||
unit/test-mux
 | 
			
		||||
unit/test-caif
 | 
			
		||||
unit/test-stkutil
 | 
			
		||||
unit/test-cdmasms
 | 
			
		||||
unit/test-*.log
 | 
			
		||||
unit/test-*.trs
 | 
			
		||||
 | 
			
		||||
tools/huawei-audio
 | 
			
		||||
tools/auto-enable
 | 
			
		||||
tools/get-location
 | 
			
		||||
tools/lookup-apn
 | 
			
		||||
tools/lookup-provider-name
 | 
			
		||||
tools/tty-redirector
 | 
			
		||||
tools/qmi
 | 
			
		||||
tools/stktest
 | 
			
		||||
 | 
			
		||||
gatchat/gsmdial
 | 
			
		||||
gatchat/test-server
 | 
			
		||||
gatchat/test-qcdm
 | 
			
		||||
							
								
								
									
										7
									
								
								.mailmap
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								.mailmap
									
									
									
									
									
								
							@@ -1,7 +0,0 @@
 | 
			
		||||
Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>	<luiz.dentz-von@nokia.com>
 | 
			
		||||
Zhenhua Zhang <zhenhua.zhang@intel.com>			<zhenhua.zhang@intel.com>
 | 
			
		||||
Pekka Pessi <pekka.pessi@nokia.com>			<Pekka.Pessi@nokia.com>
 | 
			
		||||
Pekka Pessi <pekka.pessi@nokia.com>			<ppessi@hamsa.research.nokia.com>
 | 
			
		||||
Lasse Kunnasluoto <lasse.kunnasluoto@tieto.com>		<Lasse.Kunnasluoto@tieto.com>
 | 
			
		||||
Syam Sidhardhan <s.syam@samsung.com>			<syamsidhardh@gmail.com>
 | 
			
		||||
Michael Dietrich <mdt@emdete.de>			<mdt@emdete.de>
 | 
			
		||||
							
								
								
									
										41
									
								
								ofono/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								ofono/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -32,6 +32,8 @@ src/ofono.service
 | 
			
		||||
dundee/dundee
 | 
			
		||||
dundee/dundee.service
 | 
			
		||||
 | 
			
		||||
test-driver
 | 
			
		||||
test-suite.log
 | 
			
		||||
unit/test-common
 | 
			
		||||
unit/test-util
 | 
			
		||||
unit/test-idmap
 | 
			
		||||
@@ -42,11 +44,50 @@ unit/test-mux
 | 
			
		||||
unit/test-caif
 | 
			
		||||
unit/test-stkutil
 | 
			
		||||
unit/test-cdmasms
 | 
			
		||||
unit/test-dbus-access
 | 
			
		||||
unit/test-dbus-clients
 | 
			
		||||
unit/test-dbus-queue
 | 
			
		||||
unit/test-gprs-filter
 | 
			
		||||
unit/test-ril_config
 | 
			
		||||
unit/test-ril_ecclist
 | 
			
		||||
unit/test-ril_util
 | 
			
		||||
unit/test-ril_vendor
 | 
			
		||||
unit/test-ril-transport
 | 
			
		||||
unit/test-rilmodem-cb
 | 
			
		||||
unit/test-rilmodem-cs
 | 
			
		||||
unit/test-rilmodem-gprs
 | 
			
		||||
unit/test-rilmodem-sms
 | 
			
		||||
unit/test-sailfish_access
 | 
			
		||||
unit/test-sailfish_cell_info
 | 
			
		||||
unit/test-sailfish_cell_info_dbus
 | 
			
		||||
unit/test-sailfish_manager
 | 
			
		||||
unit/test-sailfish_sim_info
 | 
			
		||||
unit/test-sailfish_sim_info_dbus
 | 
			
		||||
unit/test-config
 | 
			
		||||
unit/test-watch
 | 
			
		||||
unit/test-sms-filter
 | 
			
		||||
unit/test-voicecall-filter
 | 
			
		||||
unit/test-*.log
 | 
			
		||||
unit/test-*.trs
 | 
			
		||||
unit/test-mbim
 | 
			
		||||
 | 
			
		||||
unit/test-grilreply
 | 
			
		||||
unit/test-grilrequest
 | 
			
		||||
unit/test-grilunsol
 | 
			
		||||
unit/test-provision
 | 
			
		||||
unit/html
 | 
			
		||||
 | 
			
		||||
plugins/sailfish_manager/*.gcda
 | 
			
		||||
plugins/sailfish_manager/*.gcno
 | 
			
		||||
drivers/*/*.gcda
 | 
			
		||||
drivers/*/*.gcno
 | 
			
		||||
drivers/*/*.gcov
 | 
			
		||||
plugins/*/*.gcda
 | 
			
		||||
plugins/*/*.gcno
 | 
			
		||||
plugins/*/*.gcov
 | 
			
		||||
*/*.gcda
 | 
			
		||||
*/*.gcno
 | 
			
		||||
*/*.gcov
 | 
			
		||||
 | 
			
		||||
tools/huawei-audio
 | 
			
		||||
tools/auto-enable
 | 
			
		||||
 
 | 
			
		||||
@@ -113,3 +113,20 @@ Anirudh Gargi <anirudh.gargi@intel.com>
 | 
			
		||||
Nishanth V <nishanth.v@intel.com>
 | 
			
		||||
Antara Borwankar <antara.borwankar@gmail.com>
 | 
			
		||||
Martin Chaplet <m.chaplet@kerlink.fr>
 | 
			
		||||
Suman Mallela <suman.m@intel.com>
 | 
			
		||||
Rajagopal Aravindan <rajagopalx.aravindan@intel.com>
 | 
			
		||||
Ankit Navik <ankit.p.navik@intel.com>
 | 
			
		||||
Antoine Aubert <a.aubert@overkiz.com>
 | 
			
		||||
Djalal Harouni <djalal@endocode.com>
 | 
			
		||||
Christophe Ronco <c.ronco@kerlink.fr>
 | 
			
		||||
Vincent Cesson <vincent.cesson@smile.fr>
 | 
			
		||||
Piotr Haber <gluedig@gmail.com>
 | 
			
		||||
André Draszik <git@andred.net>
 | 
			
		||||
Lukasz Nowak <lnowak@tycoint.com>
 | 
			
		||||
Jonas Bonn <jonas@southpole.se>
 | 
			
		||||
Matthijs Kooijman <matthijs@stdin.nl>
 | 
			
		||||
Clayton Craft <clayton@craftyguy.net>
 | 
			
		||||
Joey Hewitt <joey@joeyhewitt.com>
 | 
			
		||||
Richard Röjfors <richard.rojfors@gmail.com>
 | 
			
		||||
Philippe De Swert <philippe.deswert@nomovok.com>
 | 
			
		||||
Gabriel Lucas <gabriel.lucas@smile.fr>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,64 @@
 | 
			
		||||
ver 1.23:
 | 
			
		||||
	Fix issue with handling SIM AID sessions.
 | 
			
		||||
	Add support for QMI LTE bearer handling.
 | 
			
		||||
	Add support for memory location dialing.
 | 
			
		||||
 | 
			
		||||
ver 1.22:
 | 
			
		||||
	Fix issue with GPIO handling and Nokia modems.
 | 
			
		||||
	Fix issue with SIM state callback and AT modems.
 | 
			
		||||
	Fix issue with data mode and DCD for U-Blox modems.
 | 
			
		||||
	Fix issue with SMS receive on QMI based Quectel EC21.
 | 
			
		||||
	Fix issue with HFP support and last call dialed request.
 | 
			
		||||
	Fix issue with PIM retires handling and Gemalto modems.
 | 
			
		||||
	Fix issue with atom registration and SIM state handling.
 | 
			
		||||
	Add support for handling SIM card AID session management.
 | 
			
		||||
	Add support for handling GSM/UMTS and IMS authentication.
 | 
			
		||||
	Add support for IP Multimedia Subsystem (IMS) atom.
 | 
			
		||||
	Add support for MBIM based modems.
 | 
			
		||||
 | 
			
		||||
ver 1.21:
 | 
			
		||||
	Fix issue with USSD notification received handling.
 | 
			
		||||
	Fix issue with crashing SIM filesystem notifications.
 | 
			
		||||
	Fix issue with LTE bearer reporting and Huawei modems.
 | 
			
		||||
	Fix issue with invalid memory access and QMI.
 | 
			
		||||
	Add support for QMI SIM writing functionality.
 | 
			
		||||
	Add support for RAT selection for QMI modems.
 | 
			
		||||
	Add support for network monitor agent interface.
 | 
			
		||||
	Add support for Cinterion Hardware Monitor interface.
 | 
			
		||||
	Add support for LTE atom driver for Huawei modems.
 | 
			
		||||
	Add support for LTE atom driver for AT modems.
 | 
			
		||||
	Add support for Intel xmm7xxx series modems.
 | 
			
		||||
 | 
			
		||||
ver 1.20:
 | 
			
		||||
	Fix issue with context removal before activation.
 | 
			
		||||
	Fix issue with update during GPRS context activation.
 | 
			
		||||
	Fix issue with receiving UTF-16 encoded messages.
 | 
			
		||||
	Fix issue with invalid access in CBS decoding.
 | 
			
		||||
	Fix issue with signal strength on QMI modems.
 | 
			
		||||
	Fix issue with PIN handling with QMI modems.
 | 
			
		||||
	Fix issue with QMI notification message handling.
 | 
			
		||||
	Fix issue with facility lock query on SIM removal.
 | 
			
		||||
	Fix issue with parsing +CLCC and +CCWA fields.
 | 
			
		||||
	Add support for obtaining IMSI via EF reading.
 | 
			
		||||
	Add support for additional netmon info types.
 | 
			
		||||
	Add support for provisioning via configuration files.
 | 
			
		||||
	Add support for Gemalto P-family series of modems.
 | 
			
		||||
	Add support for Telit HE910 and UE910 variants.
 | 
			
		||||
	Add support for Intel SoFIA SIM Toolkit interfaces.
 | 
			
		||||
	Add support for Intel SoFIA LTE features.
 | 
			
		||||
	Add support for U-Blox TOBY-L2 LTE feature.
 | 
			
		||||
	Add support for dedicated LTE atom.
 | 
			
		||||
 | 
			
		||||
ver 1.19:
 | 
			
		||||
	Fix issue with DHCP parsing and Huawei modems.
 | 
			
		||||
	Fix issue with detecting Huawei E3372 modem.
 | 
			
		||||
	Fix issue with handling serving cell info.
 | 
			
		||||
	Fix issue with handling SIM SC facility lock.
 | 
			
		||||
	Fix issue with Android RIL PIN retry logic.
 | 
			
		||||
	Fix issue with Android RIL and RAT handling.
 | 
			
		||||
	Add support for Android RIL cell broadcast.
 | 
			
		||||
	Add support for SoFIA 3GR thermal management.
 | 
			
		||||
 | 
			
		||||
ver 1.18:
 | 
			
		||||
	Fix issue with cell broadcast and use-after-fee.
 | 
			
		||||
	Fix issue with repeated held call indicator.
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,12 @@ pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \
 | 
			
		||||
			include/cdma-provision.h include/handsfree.h \
 | 
			
		||||
			include/sim-mnclength.h \
 | 
			
		||||
			include/handsfree-audio.h include/siri.h \
 | 
			
		||||
			include/netmon.h
 | 
			
		||||
			include/sms-filter.h include/gprs-filter.h \
 | 
			
		||||
			include/voicecall-filter.h include/dbus-access.h \
 | 
			
		||||
			include/ril-constants.h include/ril-transport.h \
 | 
			
		||||
			include/watch.h gdbus/gdbus.h include/dbus-clients.h \
 | 
			
		||||
			include/netmon.h include/lte.h include/ims.h \
 | 
			
		||||
			include/storage.h
 | 
			
		||||
 | 
			
		||||
nodist_pkginclude_HEADERS = include/version.h
 | 
			
		||||
 | 
			
		||||
@@ -58,7 +63,7 @@ endif
 | 
			
		||||
builtin_modules =
 | 
			
		||||
builtin_sources =
 | 
			
		||||
builtin_libadd =
 | 
			
		||||
builtin_cflags =
 | 
			
		||||
builtin_cflags = -DSAILFISH_OS
 | 
			
		||||
 | 
			
		||||
noinst_LTLIBRARIES += gdbus/libgdbus-internal.la
 | 
			
		||||
 | 
			
		||||
@@ -107,8 +112,6 @@ gril_sources = gril/gril.h gril/gril.c \
 | 
			
		||||
btio_sources = btio/btio.h btio/btio.c
 | 
			
		||||
 | 
			
		||||
if UDEV
 | 
			
		||||
builtin_modules += udev
 | 
			
		||||
builtin_sources += plugins/udev.c
 | 
			
		||||
builtin_cflags += @UDEV_CFLAGS@
 | 
			
		||||
builtin_libadd += @UDEV_LIBS@
 | 
			
		||||
 | 
			
		||||
@@ -116,8 +119,24 @@ builtin_modules += udevng
 | 
			
		||||
builtin_sources += plugins/udevng.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if SAILFISH_MANAGER
 | 
			
		||||
builtin_modules += sailfish_manager
 | 
			
		||||
builtin_sources += plugins/sailfish_manager/sailfish_cell_info.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_cell_info_dbus.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_manager.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_manager_dbus.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_sim_info.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_sim_info_dbus.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if SAILFISH_ACCESS
 | 
			
		||||
builtin_modules += sailfish_access
 | 
			
		||||
builtin_sources += plugins/sailfish_access.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if RILMODEM
 | 
			
		||||
if JOLLA_RILMODEM
 | 
			
		||||
if SAILFISH_RILMODEM
 | 
			
		||||
 | 
			
		||||
builtin_modules += ril
 | 
			
		||||
builtin_sources +=	drivers/ril/ril_call_barring.c \
 | 
			
		||||
@@ -125,36 +144,43 @@ builtin_sources +=	drivers/ril/ril_call_barring.c \
 | 
			
		||||
			drivers/ril/ril_call_settings.c \
 | 
			
		||||
			drivers/ril/ril_call_volume.c \
 | 
			
		||||
			drivers/ril/ril_cell_info.c \
 | 
			
		||||
			drivers/ril/ril_cell_info_dbus.c \
 | 
			
		||||
			drivers/ril/ril_config.c \
 | 
			
		||||
			drivers/ril/ril_connman.c \
 | 
			
		||||
			drivers/ril/ril_cbs.c \
 | 
			
		||||
			drivers/ril/ril_data.c \
 | 
			
		||||
			drivers/ril/ril_devinfo.c \
 | 
			
		||||
			drivers/ril/ril_devmon.c \
 | 
			
		||||
			drivers/ril/ril_devmon_auto.c \
 | 
			
		||||
			drivers/ril/ril_devmon_combine.c \
 | 
			
		||||
			drivers/ril/ril_devmon_ds.c \
 | 
			
		||||
			drivers/ril/ril_devmon_ss.c \
 | 
			
		||||
			drivers/ril/ril_devmon_ur.c \
 | 
			
		||||
			drivers/ril/ril_ecclist.c \
 | 
			
		||||
			drivers/ril/ril_gprs.c \
 | 
			
		||||
			drivers/ril/ril_gprs_context.c \
 | 
			
		||||
			drivers/ril/ril_modem.c \
 | 
			
		||||
			drivers/ril/ril_mtu.c \
 | 
			
		||||
			drivers/ril/ril_netmon.c \
 | 
			
		||||
			drivers/ril/ril_netreg.c \
 | 
			
		||||
			drivers/ril/ril_network.c \
 | 
			
		||||
			drivers/ril/ril_oem_raw.c \
 | 
			
		||||
			drivers/ril/ril_phonebook.c \
 | 
			
		||||
			drivers/ril/ril_plugin.c \
 | 
			
		||||
			drivers/ril/ril_plugin_dbus.c \
 | 
			
		||||
			drivers/ril/ril_radio.c \
 | 
			
		||||
			drivers/ril/ril_radio_caps.c \
 | 
			
		||||
			drivers/ril/ril_radio_settings.c \
 | 
			
		||||
			drivers/ril/ril_sim.c \
 | 
			
		||||
			drivers/ril/ril_sim_card.c \
 | 
			
		||||
			drivers/ril/ril_sim_info.c \
 | 
			
		||||
			drivers/ril/ril_sim_info_dbus.c \
 | 
			
		||||
			drivers/ril/ril_sim_settings.c \
 | 
			
		||||
			drivers/ril/ril_sms.c \
 | 
			
		||||
			drivers/ril/ril_stk.c \
 | 
			
		||||
			drivers/ril/ril_ussd.c \
 | 
			
		||||
			drivers/ril/ril_util.c \
 | 
			
		||||
			drivers/ril/ril_vendor.c \
 | 
			
		||||
			drivers/ril/ril_voicecall.c
 | 
			
		||||
 | 
			
		||||
# Vendor specific extensions
 | 
			
		||||
builtin_sources +=	drivers/ril/ril_vendor_mtk.c
 | 
			
		||||
 | 
			
		||||
if DATAFILES
 | 
			
		||||
dist_conf_DATA += drivers/ril/ril_subscription.conf
 | 
			
		||||
endif
 | 
			
		||||
@@ -172,8 +198,8 @@ builtin_sources += plugins/ril.c plugins/ril.h
 | 
			
		||||
builtin_modules += infineon
 | 
			
		||||
builtin_sources += plugins/infineon.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += ril_sofia3gr
 | 
			
		||||
builtin_sources += plugins/ril_sofia3gr.c
 | 
			
		||||
builtin_modules += ril_intel
 | 
			
		||||
builtin_sources += plugins/ril_intel.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += rilmodem
 | 
			
		||||
builtin_sources += drivers/rilmodem/rilmodem.h \
 | 
			
		||||
@@ -197,7 +223,9 @@ builtin_sources += drivers/rilmodem/rilmodem.h \
 | 
			
		||||
			drivers/rilmodem/call-barring.c \
 | 
			
		||||
			drivers/rilmodem/netmon.c \
 | 
			
		||||
			drivers/rilmodem/stk.c \
 | 
			
		||||
			drivers/infineonmodem/infineon_constants.h
 | 
			
		||||
			drivers/rilmodem/cbs.c \
 | 
			
		||||
			drivers/infineonmodem/infineon_constants.h \
 | 
			
		||||
			drivers/rilmodem/lte.c
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
@@ -256,11 +284,14 @@ qmi_sources = drivers/qmimodem/qmi.h drivers/qmimodem/qmi.c \
 | 
			
		||||
					drivers/qmimodem/ctl.h \
 | 
			
		||||
					drivers/qmimodem/dms.h \
 | 
			
		||||
					drivers/qmimodem/nas.h \
 | 
			
		||||
					drivers/qmimodem/nas.c \
 | 
			
		||||
					drivers/qmimodem/uim.h \
 | 
			
		||||
					drivers/qmimodem/wms.h \
 | 
			
		||||
					drivers/qmimodem/wds.h \
 | 
			
		||||
					drivers/qmimodem/pds.h \
 | 
			
		||||
					drivers/qmimodem/common.h
 | 
			
		||||
					drivers/qmimodem/common.h \
 | 
			
		||||
					drivers/qmimodem/wda.h \
 | 
			
		||||
					drivers/qmimodem/voice.h
 | 
			
		||||
 | 
			
		||||
builtin_modules += qmimodem
 | 
			
		||||
builtin_sources += $(qmi_sources) \
 | 
			
		||||
@@ -276,8 +307,10 @@ builtin_sources += $(qmi_sources) \
 | 
			
		||||
			drivers/qmimodem/ussd.c \
 | 
			
		||||
			drivers/qmimodem/gprs.c \
 | 
			
		||||
			drivers/qmimodem/gprs-context.c \
 | 
			
		||||
			drivers/qmimodem/lte.c \
 | 
			
		||||
			drivers/qmimodem/radio-settings.c \
 | 
			
		||||
			drivers/qmimodem/location-reporting.c
 | 
			
		||||
			drivers/qmimodem/location-reporting.c \
 | 
			
		||||
			drivers/qmimodem/netmon.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += gobi
 | 
			
		||||
builtin_sources += plugins/gobi.c
 | 
			
		||||
@@ -307,8 +340,8 @@ builtin_sources += 	drivers/atmodem/atmodem.h \
 | 
			
		||||
			drivers/atmodem/atutil.c \
 | 
			
		||||
			drivers/atmodem/gprs.c \
 | 
			
		||||
			drivers/atmodem/gprs-context.c \
 | 
			
		||||
			drivers/atmodem/sim-auth.c \
 | 
			
		||||
			drivers/atmodem/gnss.c
 | 
			
		||||
			drivers/atmodem/gnss.c \
 | 
			
		||||
			drivers/atmodem/lte.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += nwmodem
 | 
			
		||||
builtin_sources += drivers/atmodem/atutil.h \
 | 
			
		||||
@@ -365,7 +398,8 @@ builtin_modules += telitmodem
 | 
			
		||||
builtin_sources += drivers/atmodem/atutil.h \
 | 
			
		||||
			drivers/telitmodem/telitmodem.h \
 | 
			
		||||
			drivers/telitmodem/telitmodem.c \
 | 
			
		||||
			drivers/telitmodem/location-reporting.c
 | 
			
		||||
			drivers/telitmodem/location-reporting.c \
 | 
			
		||||
			drivers/telitmodem/gprs-context-ncm.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += hsomodem
 | 
			
		||||
builtin_sources += drivers/atmodem/atutil.h \
 | 
			
		||||
@@ -427,9 +461,24 @@ builtin_modules += ubloxmodem
 | 
			
		||||
builtin_sources += drivers/atmodem/atutil.h \
 | 
			
		||||
			drivers/ubloxmodem/ubloxmodem.h \
 | 
			
		||||
			drivers/ubloxmodem/ubloxmodem.c \
 | 
			
		||||
			drivers/ubloxmodem/gprs-context.c
 | 
			
		||||
			drivers/ubloxmodem/gprs-context.c \
 | 
			
		||||
			drivers/ubloxmodem/netmon.c \
 | 
			
		||||
			drivers/ubloxmodem/lte.c
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
builtin_modules += gemaltomodem
 | 
			
		||||
builtin_sources += drivers/atmodem/atutil.h \
 | 
			
		||||
			drivers/gemaltomodem/gemaltomodem.h \
 | 
			
		||||
			drivers/gemaltomodem/gemaltomodem.c \
 | 
			
		||||
			drivers/gemaltomodem/location-reporting.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += xmm7modem
 | 
			
		||||
builtin_sources += drivers/atmodem/atutil.h \
 | 
			
		||||
			drivers/xmm7modem/xmm7modem.h \
 | 
			
		||||
			drivers/xmm7modem/xmm7modem.c \
 | 
			
		||||
			drivers/xmm7modem/radio-settings.c \
 | 
			
		||||
			drivers/xmm7modem/ims.c
 | 
			
		||||
 | 
			
		||||
if PHONESIM
 | 
			
		||||
builtin_modules += phonesim
 | 
			
		||||
builtin_sources += plugins/phonesim.c
 | 
			
		||||
@@ -494,6 +543,9 @@ builtin_sources += plugins/caif.c
 | 
			
		||||
builtin_modules += cinterion
 | 
			
		||||
builtin_sources += plugins/cinterion.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += gemalto
 | 
			
		||||
builtin_sources += plugins/gemalto.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += nokia
 | 
			
		||||
builtin_sources += plugins/nokia.c
 | 
			
		||||
 | 
			
		||||
@@ -521,24 +573,27 @@ builtin_sources += plugins/samsung.c
 | 
			
		||||
builtin_modules += sim900
 | 
			
		||||
builtin_sources += plugins/sim900.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += telit
 | 
			
		||||
builtin_sources += plugins/telit.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += quectel
 | 
			
		||||
builtin_sources += plugins/quectel.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += ublox
 | 
			
		||||
builtin_sources += plugins/ublox.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += he910
 | 
			
		||||
builtin_sources += plugins/he910.c
 | 
			
		||||
builtin_modules += xmm7xxx
 | 
			
		||||
builtin_sources += plugins/xmm7xxx.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
builtin_modules += connman
 | 
			
		||||
builtin_sources += plugins/connman.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += mnclength
 | 
			
		||||
builtin_sources += plugins/mnclength.c
 | 
			
		||||
 | 
			
		||||
if BLUETOOTH
 | 
			
		||||
if BLUEZ4
 | 
			
		||||
builtin_modules += telit
 | 
			
		||||
builtin_sources += plugins/telit.c plugins/bluez4.h
 | 
			
		||||
 | 
			
		||||
builtin_modules += sap
 | 
			
		||||
builtin_sources += plugins/sap.c plugins/bluez4.h
 | 
			
		||||
 | 
			
		||||
@@ -574,6 +629,11 @@ builtin_sources += plugins/bluez5.c plugins/bluez5.h
 | 
			
		||||
 | 
			
		||||
builtin_modules += hfp_ag_bluez5
 | 
			
		||||
builtin_sources += plugins/hfp_ag_bluez5.c plugins/bluez5.h
 | 
			
		||||
 | 
			
		||||
if SAILFISH_BT
 | 
			
		||||
builtin_modules += sfos_bt
 | 
			
		||||
builtin_sources += plugins/sailfish_bt.c
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if UPOWER
 | 
			
		||||
@@ -587,17 +647,32 @@ builtin_modules += nettime
 | 
			
		||||
builtin_sources += plugins/nettime.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if SAILFISH_DEBUGLOG
 | 
			
		||||
builtin_modules += debuglog
 | 
			
		||||
builtin_sources += plugins/sailfish_debuglog.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if SAILFISH_PROVISION
 | 
			
		||||
builtin_sources += plugins/sailfish_provision.c
 | 
			
		||||
PROVISION = 1
 | 
			
		||||
else
 | 
			
		||||
if PROVISION
 | 
			
		||||
builtin_sources += plugins/provision.c
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if PROVISION
 | 
			
		||||
builtin_sources += plugins/mbpi.h plugins/mbpi.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += provision
 | 
			
		||||
builtin_sources += plugins/provision.h plugins/provision.c
 | 
			
		||||
builtin_sources += plugins/provision.h
 | 
			
		||||
 | 
			
		||||
builtin_modules += cdma_provision
 | 
			
		||||
builtin_sources += plugins/cdma-provision.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += mnclength
 | 
			
		||||
builtin_sources += plugins/mnclength.c
 | 
			
		||||
builtin_modules += file_provision
 | 
			
		||||
builtin_sources += plugins/file-provision.c
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if MAINTAINER_MODE
 | 
			
		||||
@@ -629,16 +704,9 @@ builtin_sources += plugins/smart-messaging.c
 | 
			
		||||
builtin_modules += push_notification
 | 
			
		||||
builtin_sources += plugins/push-notification.c
 | 
			
		||||
 | 
			
		||||
if PUSHFORWARDER
 | 
			
		||||
builtin_modules += push_forwarder
 | 
			
		||||
builtin_sources += plugins/push-forwarder.c
 | 
			
		||||
builtin_cflags += @WSPCODEC_CFLAGS@
 | 
			
		||||
builtin_libadd += @WSPCODEC_LIBS@
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if DEBUGLOG
 | 
			
		||||
builtin_modules += debuglog
 | 
			
		||||
builtin_sources += plugins/debuglog.c
 | 
			
		||||
if SAILFISH_PUSHFORWARDER
 | 
			
		||||
builtin_modules += pushforwarder
 | 
			
		||||
builtin_sources += plugins/sailfish_pushforwarder.c
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
builtin_modules += sms_history
 | 
			
		||||
@@ -647,9 +715,40 @@ builtin_sources += plugins/smshistory.c
 | 
			
		||||
builtin_modules += allowed_apns
 | 
			
		||||
builtin_sources += plugins/allowed-apns.c
 | 
			
		||||
 | 
			
		||||
if ELL
 | 
			
		||||
builtin_cflags += @ELL_CFLAGS@
 | 
			
		||||
builtin_libadd += @ELL_LIBS@
 | 
			
		||||
 | 
			
		||||
if MBIMMODEM
 | 
			
		||||
mbim_sources =	drivers/mbimmodem/mbim.h \
 | 
			
		||||
		drivers/mbimmodem/mbim.c \
 | 
			
		||||
		drivers/mbimmodem/mbim-desc.h \
 | 
			
		||||
		drivers/mbimmodem/mbim-desc.c \
 | 
			
		||||
		drivers/mbimmodem/mbim-message.h \
 | 
			
		||||
		drivers/mbimmodem/mbim-message.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += mbimmodem
 | 
			
		||||
builtin_sources += $(mbim_sources) \
 | 
			
		||||
			drivers/mbimmodem/util.h \
 | 
			
		||||
			drivers/mbimmodem/util.c \
 | 
			
		||||
			drivers/mbimmodem/mbimmodem.h \
 | 
			
		||||
			drivers/mbimmodem/mbimmodem.c \
 | 
			
		||||
			drivers/mbimmodem/devinfo.c \
 | 
			
		||||
			drivers/mbimmodem/sim.c \
 | 
			
		||||
			drivers/mbimmodem/network-registration.c \
 | 
			
		||||
			drivers/mbimmodem/sms.c \
 | 
			
		||||
			drivers/mbimmodem/gprs.c \
 | 
			
		||||
			drivers/mbimmodem/gprs-context.c
 | 
			
		||||
 | 
			
		||||
builtin_modules += mbim
 | 
			
		||||
builtin_sources += plugins/mbim.c
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
sbin_PROGRAMS = src/ofonod
 | 
			
		||||
 | 
			
		||||
src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \
 | 
			
		||||
			src/mtu-watch.c \
 | 
			
		||||
			src/main.c src/ofono.h src/log.c src/plugin.c \
 | 
			
		||||
			src/modem.c src/common.h src/common.c \
 | 
			
		||||
			src/manager.c src/dbus.c src/util.h src/util.c \
 | 
			
		||||
@@ -675,8 +774,13 @@ src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \
 | 
			
		||||
			src/cdma-provision.c src/handsfree.c \
 | 
			
		||||
			src/handsfree-audio.c src/bluetooth.h \
 | 
			
		||||
			src/sim-mnclength.c src/voicecallagent.c \
 | 
			
		||||
			src/hfp.h src/siri.c \
 | 
			
		||||
			src/netmon.c
 | 
			
		||||
			src/sms-filter.c src/gprs-filter.c \
 | 
			
		||||
			src/dbus-clients.c src/dbus-queue.c \
 | 
			
		||||
			src/dbus-access.c src/config.c \
 | 
			
		||||
			src/voicecall-filter.c src/ril-transport.c \
 | 
			
		||||
			src/hfp.h src/siri.c src/watchlist.c \
 | 
			
		||||
			src/netmon.c src/lte.c src/ims.c \
 | 
			
		||||
			src/netmonagent.c src/netmonagent.h
 | 
			
		||||
 | 
			
		||||
src_ofonod_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
 | 
			
		||||
			@GLIB_LIBS@ @DBUS_LIBS@ -ldl
 | 
			
		||||
@@ -686,7 +790,8 @@ src_ofonod_LDFLAGS = -Wl,--export-dynamic \
 | 
			
		||||
 | 
			
		||||
BUILT_SOURCES = $(local_headers) src/builtin.h
 | 
			
		||||
 | 
			
		||||
CLEANFILES = $(BUILT_SOURCES) $(rules_DATA)
 | 
			
		||||
CLEANFILES = $(BUILT_SOURCES) $(rules_DATA) \
 | 
			
		||||
	$(shell find . -name "*.gcda") $(shell find . -name "*.gcno")
 | 
			
		||||
 | 
			
		||||
plugindir = $(pkglibdir)/plugins
 | 
			
		||||
 | 
			
		||||
@@ -702,7 +807,8 @@ AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ $(builtin_cflags) \
 | 
			
		||||
 | 
			
		||||
AM_CPPFLAGS = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/src \
 | 
			
		||||
			-I$(srcdir)/gdbus -I$(srcdir)/gisi -I$(srcdir)/gatchat \
 | 
			
		||||
			-I$(srcdir)/btio -I$(srcdir)/gril
 | 
			
		||||
			-I$(srcdir)/btio -I$(srcdir)/gril \
 | 
			
		||||
			-I$(srcdir)/plugins/sailfish_manager
 | 
			
		||||
 | 
			
		||||
doc_files = doc/overview.txt doc/ofono-paper.txt doc/release-faq.txt \
 | 
			
		||||
		doc/manager-api.txt doc/modem-api.txt doc/network-api.txt \
 | 
			
		||||
@@ -724,7 +830,10 @@ doc_files = doc/overview.txt doc/ofono-paper.txt doc/release-faq.txt \
 | 
			
		||||
			doc/certification.txt doc/siri-api.txt \
 | 
			
		||||
			doc/telit-modem.txt \
 | 
			
		||||
			doc/networkmonitor-api.txt \
 | 
			
		||||
			doc/allowed-apns-api.txt
 | 
			
		||||
			doc/allowed-apns-api.txt \
 | 
			
		||||
			doc/lte-api.txt \
 | 
			
		||||
			doc/cinterion-hardware-monitor-api.txt \
 | 
			
		||||
			doc/ims-api.txt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
test_scripts = test/backtrace \
 | 
			
		||||
@@ -758,6 +867,7 @@ test_scripts = test/backtrace \
 | 
			
		||||
		test/receive-sms \
 | 
			
		||||
		test/remove-contexts \
 | 
			
		||||
		test/send-sms \
 | 
			
		||||
		test/cancel-sms \
 | 
			
		||||
		test/set-mic-volume \
 | 
			
		||||
		test/set-speaker-volume \
 | 
			
		||||
		test/test-stk-menu \
 | 
			
		||||
@@ -828,7 +938,15 @@ test_scripts = test/backtrace \
 | 
			
		||||
		test/set-sms-smsc \
 | 
			
		||||
		test/set-sms-bearer \
 | 
			
		||||
		test/get-serving-cell-info \
 | 
			
		||||
		test/list-allowed-access-points
 | 
			
		||||
		test/list-allowed-access-points \
 | 
			
		||||
		test/enable-throttling \
 | 
			
		||||
		test/disable-throttling \
 | 
			
		||||
		test/set-lte-property \
 | 
			
		||||
		test/test-serving-cell-info \
 | 
			
		||||
		test/ims-register \
 | 
			
		||||
		test/ims-unregister \
 | 
			
		||||
		test/list-applications
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if TEST
 | 
			
		||||
testdir = $(pkglibdir)/test
 | 
			
		||||
@@ -840,42 +958,175 @@ EXTRA_DIST = src/genbuiltin plugins/ofono.rules plugins/ofono-speedup.rules \
 | 
			
		||||
 | 
			
		||||
dist_man_MANS = doc/ofonod.8
 | 
			
		||||
 | 
			
		||||
if TEST_COVERAGE
 | 
			
		||||
COVERAGE_OPT = --coverage
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
unit_objects =
 | 
			
		||||
 | 
			
		||||
unit_tests = unit/test-common unit/test-util unit/test-idmap \
 | 
			
		||||
				unit/test-simutil unit/test-stkutil \
 | 
			
		||||
				unit/test-sms unit/test-cdmasms \
 | 
			
		||||
				unit/test-provision
 | 
			
		||||
				unit/test-sms unit/test-cdmasms
 | 
			
		||||
 | 
			
		||||
if SAILFISH_MANAGER
 | 
			
		||||
 | 
			
		||||
unit_test_sailfish_cell_info_SOURCES = unit/test-sailfish_cell_info.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_cell_info.c
 | 
			
		||||
unit_test_sailfish_cell_info_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT)
 | 
			
		||||
unit_test_sailfish_cell_info_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_sailfish_cell_info_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-sailfish_cell_info
 | 
			
		||||
 | 
			
		||||
unit_test_sailfish_cell_info_dbus_SOURCES = unit/test-dbus.c \
 | 
			
		||||
			unit/test-sailfish_cell_info_dbus.c \
 | 
			
		||||
			unit/fake_sailfish_cell_info.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_cell_info.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_cell_info_dbus.c \
 | 
			
		||||
			gdbus/object.c src/dbus-clients.c \
 | 
			
		||||
			src/dbus.c src/log.c
 | 
			
		||||
unit_test_sailfish_cell_info_dbus_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT) \
 | 
			
		||||
			@DBUS_GLIB_CFLAGS@
 | 
			
		||||
unit_test_sailfish_cell_info_dbus_LDADD = @DBUS_GLIB_LIBS@ @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_sailfish_cell_info_dbus_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-sailfish_cell_info_dbus
 | 
			
		||||
 | 
			
		||||
unit_test_sailfish_sim_info_SOURCES = unit/test-sailfish_sim_info.c \
 | 
			
		||||
			unit/fake_watch.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_sim_info.c \
 | 
			
		||||
			src/storage.c src/watchlist.c src/log.c
 | 
			
		||||
unit_test_sailfish_sim_info_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS) \
 | 
			
		||||
			-DSTORAGEDIR='"/tmp/ofono"'
 | 
			
		||||
unit_test_sailfish_sim_info_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_sailfish_sim_info_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-sailfish_sim_info
 | 
			
		||||
 | 
			
		||||
unit_test_sailfish_sim_info_dbus_SOURCES = unit/test-sailfish_sim_info_dbus.c \
 | 
			
		||||
			unit/test-dbus.c unit/fake_watch.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_sim_info.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_sim_info_dbus.c \
 | 
			
		||||
			gdbus/object.c \
 | 
			
		||||
			src/dbus.c src/storage.c src/watchlist.c src/log.c
 | 
			
		||||
unit_test_sailfish_sim_info_dbus_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS) \
 | 
			
		||||
			@DBUS_GLIB_CFLAGS@ -DSTORAGEDIR='"/tmp/ofono"'
 | 
			
		||||
unit_test_sailfish_sim_info_dbus_LDADD = @DBUS_GLIB_LIBS@ @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_sailfish_sim_info_dbus_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-sailfish_sim_info_dbus
 | 
			
		||||
 | 
			
		||||
unit_test_sailfish_manager_SOURCES = unit/test-sailfish_manager.c \
 | 
			
		||||
			unit/fake_watch.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_manager.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_cell_info.c \
 | 
			
		||||
			plugins/sailfish_manager/sailfish_sim_info.c \
 | 
			
		||||
			src/storage.c src/log.c
 | 
			
		||||
unit_test_sailfish_manager_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT) \
 | 
			
		||||
			-DSTORAGEDIR='"/tmp/ofono"'
 | 
			
		||||
unit_test_sailfish_manager_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_sailfish_manager_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-sailfish_manager
 | 
			
		||||
 | 
			
		||||
unit_test_watch_SOURCES = unit/test-watch.c src/watch.c \
 | 
			
		||||
			src/log.c src/watchlist.c
 | 
			
		||||
unit_test_watch_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT) \
 | 
			
		||||
			-DSTORAGEDIR='"/tmp/ofono"'
 | 
			
		||||
unit_test_watch_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_watch_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-watch
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
unit_test_config_SOURCES = unit/test-config.c drivers/ril/ril_util.c \
 | 
			
		||||
				src/config.c src/log.c
 | 
			
		||||
unit_test_config_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_config_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_config_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-config
 | 
			
		||||
 | 
			
		||||
if SAILFISH_ACCESS
 | 
			
		||||
unit_test_sailfish_access_SOURCES = unit/test-sailfish_access.c \
 | 
			
		||||
			plugins/sailfish_access.c src/dbus-access.c src/log.c
 | 
			
		||||
unit_test_sailfish_access_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT)
 | 
			
		||||
unit_test_sailfish_access_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_sailfish_access_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-sailfish_access
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
unit_test_dbus_access_SOURCES = unit/test-dbus-access.c src/dbus-access.c \
 | 
			
		||||
			src/log.c
 | 
			
		||||
unit_test_dbus_access_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT)
 | 
			
		||||
unit_test_dbus_access_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_dbus_access_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-dbus-access
 | 
			
		||||
 | 
			
		||||
if RILMODEM
 | 
			
		||||
if JOLLA_RILMODEM
 | 
			
		||||
if SAILFISH_RILMODEM
 | 
			
		||||
 | 
			
		||||
unit_test_ril_config_SOURCES = unit/test-ril_config.c drivers/ril/ril_util.c \
 | 
			
		||||
				drivers/ril/ril_config.c src/log.c
 | 
			
		||||
unit_test_ril_config_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_ril_config_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_ril_config_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-ril_config
 | 
			
		||||
 | 
			
		||||
unit_test_ril_ecclist_SOURCES = unit/test-ril_ecclist.c \
 | 
			
		||||
				drivers/ril/ril_ecclist.c src/log.c
 | 
			
		||||
unit_test_ril_ecclist_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_ril_ecclist_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_ril_ecclist_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-ril_ecclist
 | 
			
		||||
 | 
			
		||||
unit_test_ril_util_SOURCES = unit/test-ril_util.c drivers/ril/ril_util.c \
 | 
			
		||||
				src/log.c
 | 
			
		||||
unit_test_ril_util_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_ril_util_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_ril_util_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-ril_util
 | 
			
		||||
 | 
			
		||||
unit_test_ril_vendor_SOURCES = unit/test-ril_vendor.c unit/fake_watch.c \
 | 
			
		||||
	drivers/ril/ril_vendor.c drivers/ril/ril_vendor_mtk.c  \
 | 
			
		||||
	drivers/ril/ril_util.c src/log.c
 | 
			
		||||
unit_test_ril_vendor_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_ril_vendor_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_ril_vendor_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-ril_vendor
 | 
			
		||||
 | 
			
		||||
else
 | 
			
		||||
unit_tests += unit/test-rilmodem-cs \
 | 
			
		||||
				unit/test-rilmodem-cs \
 | 
			
		||||
				unit/test-rilmodem-sms \
 | 
			
		||||
				unit/test-rilmodem-cb
 | 
			
		||||
				unit/test-rilmodem-cb \
 | 
			
		||||
				unit/test-rilmodem-gprs
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if ELL
 | 
			
		||||
if MBIMMODEM
 | 
			
		||||
unit_tests += unit/test-mbim
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = $(unit_tests) \
 | 
			
		||||
			unit/test-sms-root unit/test-mux unit/test-caif
 | 
			
		||||
 | 
			
		||||
unit_test_common_SOURCES = unit/test-common.c src/common.c src/util.c
 | 
			
		||||
unit_test_common_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_common_LDADD = @GLIB_LIBS@
 | 
			
		||||
unit_objects += $(unit_test_common_OBJECTS)
 | 
			
		||||
 | 
			
		||||
unit_test_util_SOURCES = unit/test-util.c src/util.c
 | 
			
		||||
unit_test_util_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_util_LDADD = @GLIB_LIBS@
 | 
			
		||||
unit_objects += $(unit_test_utils_OBJECTS)
 | 
			
		||||
 | 
			
		||||
unit_test_idmap_SOURCES = unit/test-idmap.c src/idmap.c
 | 
			
		||||
unit_test_idmap_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_idmap_LDADD = @GLIB_LIBS@
 | 
			
		||||
unit_objects += $(unit_test_idmap_OBJECTS)
 | 
			
		||||
 | 
			
		||||
unit_test_simutil_SOURCES = unit/test-simutil.c src/util.c \
 | 
			
		||||
                                src/simutil.c src/smsutil.c src/storage.c
 | 
			
		||||
unit_test_simutil_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_simutil_LDADD = @GLIB_LIBS@
 | 
			
		||||
unit_objects += $(unit_test_simutil_OBJECTS)
 | 
			
		||||
 | 
			
		||||
@@ -883,19 +1134,23 @@ unit_test_stkutil_SOURCES = unit/test-stkutil.c unit/stk-test-data.h \
 | 
			
		||||
				src/util.c \
 | 
			
		||||
                                src/storage.c src/smsutil.c \
 | 
			
		||||
                                src/simutil.c src/stkutil.c
 | 
			
		||||
unit_test_stkutil_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_stkutil_LDADD = @GLIB_LIBS@
 | 
			
		||||
unit_objects += $(unit_test_stkutil_OBJECTS)
 | 
			
		||||
 | 
			
		||||
unit_test_sms_SOURCES = unit/test-sms.c src/util.c src/smsutil.c src/storage.c
 | 
			
		||||
unit_test_sms_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_sms_LDADD = @GLIB_LIBS@
 | 
			
		||||
unit_objects += $(unit_test_sms_OBJECTS)
 | 
			
		||||
 | 
			
		||||
unit_test_cdmasms_SOURCES = unit/test-cdmasms.c src/cdma-smsutil.c
 | 
			
		||||
unit_test_cdmasms_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_cdmasms_LDADD = @GLIB_LIBS@
 | 
			
		||||
unit_objects += $(unit_test_cdmasms_OBJECTS)
 | 
			
		||||
 | 
			
		||||
unit_test_sms_root_SOURCES = unit/test-sms-root.c \
 | 
			
		||||
					src/util.c src/smsutil.c src/storage.c
 | 
			
		||||
unit_test_sms_root_CFLAGS = -DSTORAGEDIR='"/tmp/ofono"' $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_sms_root_LDADD = @GLIB_LIBS@
 | 
			
		||||
unit_objects += $(unit_test_sms_root_OBJECTS)
 | 
			
		||||
 | 
			
		||||
@@ -906,24 +1161,72 @@ unit_objects += $(unit_test_mux_OBJECTS)
 | 
			
		||||
unit_test_caif_SOURCES = unit/test-caif.c $(gatchat_sources) \
 | 
			
		||||
					drivers/stemodem/caif_socket.h \
 | 
			
		||||
					drivers/stemodem/if_caif.h
 | 
			
		||||
unit_test_caif_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_caif_LDADD = @GLIB_LIBS@
 | 
			
		||||
unit_objects += $(unit_test_caif_OBJECTS)
 | 
			
		||||
 | 
			
		||||
unit_test_dbus_clients_SOURCES = unit/test-dbus-clients.c unit/test-dbus.c \
 | 
			
		||||
				src/dbus-clients.c gdbus/object.c \
 | 
			
		||||
				src/dbus.c src/log.c
 | 
			
		||||
unit_test_dbus_clients_CFLAGS =  @DBUS_GLIB_CFLAGS@ $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_dbus_clients_LDADD = @DBUS_GLIB_LIBS@ @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_dbus_clients_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-dbus-clients
 | 
			
		||||
 | 
			
		||||
unit_test_dbus_queue_SOURCES = unit/test-dbus-queue.c unit/test-dbus.c \
 | 
			
		||||
				src/dbus-queue.c gdbus/object.c \
 | 
			
		||||
				src/dbus.c src/log.c
 | 
			
		||||
unit_test_dbus_queue_CFLAGS =  @DBUS_GLIB_CFLAGS@ $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_dbus_queue_LDADD = @DBUS_GLIB_LIBS@ @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_dbus_queue_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-dbus-queue
 | 
			
		||||
 | 
			
		||||
unit_test_provision_SOURCES = unit/test-provision.c \
 | 
			
		||||
				plugins/provision.h plugins/provision.c \
 | 
			
		||||
				plugins/mbpi.c src/gprs-provision.c \
 | 
			
		||||
				src/log.c
 | 
			
		||||
				plugins/provision.h plugins/mbpi.c \
 | 
			
		||||
				plugins/sailfish_provision.c \
 | 
			
		||||
				src/gprs-provision.c src/log.c
 | 
			
		||||
unit_test_provision_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_provision_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_provision_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-provision
 | 
			
		||||
 | 
			
		||||
if RILMODEM
 | 
			
		||||
if JOLLA_RILMODEM
 | 
			
		||||
unit_test_ril_transport_SOURCES = unit/test-ril-transport.c \
 | 
			
		||||
				src/ril-transport.c src/log.c
 | 
			
		||||
unit_test_ril_transport_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_ril_transport_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_ril_transport_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-ril-transport
 | 
			
		||||
 | 
			
		||||
unit_test_sms_filter_SOURCES = unit/test-sms-filter.c \
 | 
			
		||||
				src/sms-filter.c src/log.c
 | 
			
		||||
unit_test_sms_filter_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_sms_filter_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_sms_filter_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-sms-filter
 | 
			
		||||
 | 
			
		||||
unit_test_gprs_filter_SOURCES = unit/test-gprs-filter.c \
 | 
			
		||||
				src/gprs-filter.c src/log.c
 | 
			
		||||
unit_test_gprs_filter_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_gprs_filter_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_gprs_filter_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-gprs-filter
 | 
			
		||||
 | 
			
		||||
unit_test_voicecall_filter_SOURCES = unit/test-voicecall-filter.c \
 | 
			
		||||
				src/voicecall-filter.c src/log.c \
 | 
			
		||||
				src/common.c src/util.c
 | 
			
		||||
unit_test_voicecall_filter_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
 | 
			
		||||
unit_test_voicecall_filter_LDADD = @GLIB_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_voicecall_filter_OBJECTS)
 | 
			
		||||
unit_tests += unit/test-voicecall-filter
 | 
			
		||||
 | 
			
		||||
test_rilmodem_sources = $(gril_sources) src/log.c src/common.c src/util.c \
 | 
			
		||||
				gatchat/ringbuffer.h gatchat/ringbuffer.c \
 | 
			
		||||
				unit/rilmodem-test-server.h \
 | 
			
		||||
				unit/rilmodem-test-server.c \
 | 
			
		||||
				src/simutil.c
 | 
			
		||||
				unit/rilmodem-test-engine.h \
 | 
			
		||||
				unit/rilmodem-test-engine.c \
 | 
			
		||||
				src/simutil.c \
 | 
			
		||||
				drivers/rilmodem/rilutil.c
 | 
			
		||||
 | 
			
		||||
unit_test_rilmodem_cs_SOURCES = $(test_rilmodem_sources) \
 | 
			
		||||
					unit/test-rilmodem-cs.c \
 | 
			
		||||
@@ -946,8 +1249,18 @@ unit_test_rilmodem_cb_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
 | 
			
		||||
					@GLIB_LIBS@ @DBUS_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_rilmodem_cb_OBJECTS)
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
unit_test_rilmodem_gprs_SOURCES = $(test_rilmodem_sources) \
 | 
			
		||||
					unit/test-rilmodem-gprs.c \
 | 
			
		||||
					drivers/rilmodem/gprs.c
 | 
			
		||||
unit_test_rilmodem_gprs_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
 | 
			
		||||
					@GLIB_LIBS@ @DBUS_LIBS@ -ldl
 | 
			
		||||
unit_objects += $(unit_test_rilmodem_gprs_OBJECTS)
 | 
			
		||||
 | 
			
		||||
unit_test_mbim_SOURCES = unit/test-mbim.c \
 | 
			
		||||
			 drivers/mbimmodem/mbim-message.c \
 | 
			
		||||
			 drivers/mbimmodem/mbim.c
 | 
			
		||||
unit_test_mbim_LDADD = @ELL_LIBS@
 | 
			
		||||
unit_objects += $(unit_test_mbim_OBJECTS)
 | 
			
		||||
 | 
			
		||||
TESTS = $(unit_tests)
 | 
			
		||||
 | 
			
		||||
@@ -975,13 +1288,6 @@ tools_lookup_provider_name_LDADD = @GLIB_LIBS@
 | 
			
		||||
tools_tty_redirector_SOURCES = tools/tty-redirector.c
 | 
			
		||||
tools_tty_redirector_LDADD = @GLIB_LIBS@
 | 
			
		||||
 | 
			
		||||
if QMIMODEM
 | 
			
		||||
noinst_PROGRAMS += tools/qmi
 | 
			
		||||
 | 
			
		||||
tools_qmi_SOURCES = $(qmi_sources) tools/qmi.c
 | 
			
		||||
tools_qmi_LDADD = @GLIB_LIBS@
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if MAINTAINER_MODE
 | 
			
		||||
noinst_PROGRAMS += tools/stktest
 | 
			
		||||
 | 
			
		||||
@@ -1050,6 +1356,10 @@ include/ofono/version.h: include/version.h
 | 
			
		||||
	$(AM_V_at)$(MKDIR_P) include/ofono
 | 
			
		||||
	$(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
 | 
			
		||||
 | 
			
		||||
include/ofono/gdbus.h: $(abs_top_srcdir)/gdbus/gdbus.h
 | 
			
		||||
	$(AM_V_at)$(MKDIR_P) include/ofono
 | 
			
		||||
	$(AM_V_GEN)$(LN_S) $< $@
 | 
			
		||||
 | 
			
		||||
include/ofono/%.h: $(abs_top_srcdir)/include/%.h
 | 
			
		||||
	$(AM_V_at)$(MKDIR_P) include/ofono
 | 
			
		||||
	$(AM_V_GEN)$(LN_S) $< $@
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ AC_DEFUN([COMPILER_FLAGS], [
 | 
			
		||||
		CFLAGS="$CFLAGS -Wmissing-declarations"
 | 
			
		||||
		CFLAGS="$CFLAGS -Wredundant-decls"
 | 
			
		||||
		CFLAGS="$CFLAGS -Wcast-align"
 | 
			
		||||
		CFLAGS="$CFLAGS -Wno-format-truncation"
 | 
			
		||||
		CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
 | 
			
		||||
	fi
 | 
			
		||||
])
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
AC_PREREQ(2.60)
 | 
			
		||||
AC_INIT(ofono, 1.18)
 | 
			
		||||
AC_INIT(ofono, 1.23)
 | 
			
		||||
 | 
			
		||||
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
 | 
			
		||||
AC_CONFIG_HEADERS(config.h)
 | 
			
		||||
@@ -33,7 +33,7 @@ AC_PROG_LIBTOOL
 | 
			
		||||
AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],
 | 
			
		||||
			[disable code optimization through compiler]), [
 | 
			
		||||
	if (test "${enableval}" = "no"); then
 | 
			
		||||
		CFLAGS="$CFLAGS -O0"
 | 
			
		||||
		CFLAGS="$CFLAGS -O0 -U_FORTIFY_SOURCE"
 | 
			
		||||
	fi
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
@@ -69,6 +69,16 @@ PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.32, dummy=yes,
 | 
			
		||||
AC_SUBST(GLIB_CFLAGS)
 | 
			
		||||
AC_SUBST(GLIB_LIBS)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(GOBJECT, gobject-2.0, dummy=yes,
 | 
			
		||||
				AC_MSG_ERROR(GObject is required))
 | 
			
		||||
GLIB_CFLAGS="$GLIB_CFLAGS $GOBJECT_CFLAGS"
 | 
			
		||||
GLIB_LIBS="$GLIB_LIBS $GOBJECT_LIBS"
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(GIO, gio-2.0, dummy=yes,
 | 
			
		||||
				AC_MSG_ERROR(GIO is required))
 | 
			
		||||
GLIB_CFLAGS="$GLIB_CFLAGS $GIO_CFLAGS"
 | 
			
		||||
GLIB_LIBS="$GLIB_LIBS $GIO_LIBS"
 | 
			
		||||
 | 
			
		||||
if (test "${enable_threads}" = "yes"); then
 | 
			
		||||
	AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required])
 | 
			
		||||
	PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.16, dummy=yes,
 | 
			
		||||
@@ -167,22 +177,51 @@ AC_ARG_ENABLE(rilmodem, AC_HELP_STRING([--disable-rilmodem],
 | 
			
		||||
                                        [enable_rilmodem=${enableval}])
 | 
			
		||||
AM_CONDITIONAL(RILMODEM, test "${enable_rilmodem}" != "no")
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(jolla-rilmodem,
 | 
			
		||||
        AC_HELP_STRING([--enable-jolla-rilmodem], [enable Jolla RIL modem]),
 | 
			
		||||
        [enable_jolla_rilmodem=${enableval}], [enable_jolla_rilmodem="no"])
 | 
			
		||||
AM_CONDITIONAL(JOLLA_RILMODEM, test "${enable_jolla_rilmodem}" != "no")
 | 
			
		||||
AC_ARG_ENABLE(sailfish-rilmodem, AC_HELP_STRING([--enable-sailfish-rilmodem],
 | 
			
		||||
				[enable Sailfish RIL modem]),
 | 
			
		||||
				[enable_sailfish_rilmodem=${enableval}],
 | 
			
		||||
				[enable_sailfish_rilmodem="no"])
 | 
			
		||||
AM_CONDITIONAL(SAILFISH_RILMODEM, test "${enable_sailfish_rilmodem}" != "no")
 | 
			
		||||
 | 
			
		||||
if (test "${enable_jolla_rilmodem}" = "yes"); then
 | 
			
		||||
	PKG_CHECK_MODULES(GRILIO, libgrilio >= 1.0.6, dummy=yes,
 | 
			
		||||
				AC_MSG_ERROR(libgrilio >= 1.0.6 is required))
 | 
			
		||||
	PKG_CHECK_MODULES(GLIBUTIL, libglibutil >= 1.0.5, dummy=yes,
 | 
			
		||||
				AC_MSG_ERROR(libglibutil >= 1.0.5 is required))
 | 
			
		||||
	PKG_CHECK_MODULES(LIBMCE, libmce-glib, dummy=yes,
 | 
			
		||||
				AC_MSG_ERROR(libmce-glib is required))
 | 
			
		||||
	CFLAGS="$CFLAGS $GRILIO_CFLAGS $GLIBUTIL_CFLAGS $LIBMCE_CFLAGS"
 | 
			
		||||
	LIBS="$LIBS $GRILIO_LIBS $GLIBUTIL_LIBS $LIBMCE_LIBS"
 | 
			
		||||
PKG_CHECK_MODULES(GLIBUTIL, libglibutil >= 1.0.35, dummy=yes,
 | 
			
		||||
			AC_MSG_ERROR(libglibutil >= 1.0.35 is required))
 | 
			
		||||
CFLAGS="$CFLAGS $GLIBUTIL_CFLAGS"
 | 
			
		||||
LIBS="$LIBS $GLIBUTIL_LIBS"
 | 
			
		||||
 | 
			
		||||
if (test "${enable_sailfish_rilmodem}" = "yes"); then
 | 
			
		||||
	PKG_CHECK_MODULES(GRILIO, libgrilio >= 1.0.38, dummy=yes,
 | 
			
		||||
				AC_MSG_ERROR(libgrilio >= 1.0.38 is required))
 | 
			
		||||
	PKG_CHECK_MODULES(LIBMCE, libmce-glib >= 1.0.6, dummy=yes,
 | 
			
		||||
				AC_MSG_ERROR(libmce-glib >= 1.0.6 is required))
 | 
			
		||||
	CFLAGS="$CFLAGS $GRILIO_CFLAGS $LIBMCE_CFLAGS"
 | 
			
		||||
	LIBS="$LIBS $GRILIO_LIBS $LIBMCE_LIBS"
 | 
			
		||||
	enable_sailfish_manager=yes
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sailfish-manager,
 | 
			
		||||
		AC_HELP_STRING([--enable-sailfish-manager],
 | 
			
		||||
			[enable Sailfish OS modem manager plugin]),
 | 
			
		||||
					[enable_sailfish_manager=${enableval}])
 | 
			
		||||
AM_CONDITIONAL(SAILFISH_MANAGER, test "${enable_sailfish_manager}" = "yes")
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1, dummy=yes,
 | 
			
		||||
		AC_MSG_ERROR(dbus-glib is required by unit tests))
 | 
			
		||||
		AC_SUBST(DBUS_GLIB_CFLAGS)
 | 
			
		||||
		AC_SUBST(DBUS_GLIB_LIBS)
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(add-remove-context, AC_HELP_STRING([--disable-add-remove-context],
 | 
			
		||||
		[don't allow to add or remove connection context over D-Bus]), [
 | 
			
		||||
	if (test "${enableval}" = "no"); then
 | 
			
		||||
		CFLAGS="$CFLAGS -DDISABLE_ADD_REMOVE_CONTEXT"
 | 
			
		||||
	fi
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(test-coverage,
 | 
			
		||||
	AC_HELP_STRING([--enable-test-coverage], [enable test code coverage]),
 | 
			
		||||
				[enable_test_coverage=${enableval}],
 | 
			
		||||
					[enable_test_coverage="no"])
 | 
			
		||||
AM_CONDITIONAL(TEST_COVERAGE, test "${enable_test_coverage}" != "no")
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(qmimodem, AC_HELP_STRING([--disable-qmimodem],
 | 
			
		||||
				[disable Qualcomm QMI modem support]),
 | 
			
		||||
					[enable_qmimodem=${enableval}])
 | 
			
		||||
@@ -206,6 +245,16 @@ fi
 | 
			
		||||
AM_CONDITIONAL(BLUEZ4, test "${enable_bluetooth}" != "no" && test "${enable_bluez4}" = "yes")
 | 
			
		||||
AM_CONDITIONAL(BLUETOOTH, test "${enable_bluetooth}" != "no")
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sailfish-bt, AC_HELP_STRING([--enable-sailfish-bt],
 | 
			
		||||
			[enable Sailfish OS Bluetooth plugin]),
 | 
			
		||||
					[enable_sailfish_bt=${enableval}])
 | 
			
		||||
AM_CONDITIONAL(SAILFISH_BT, test "${enable_sailfish_bt}" = "yes")
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sailfish-provision, AC_HELP_STRING([--enable-sailfish-provision],
 | 
			
		||||
			[enable Sailfish OS provisioning plugin]),
 | 
			
		||||
				[enable_sailfish_provision=${enableval}])
 | 
			
		||||
AM_CONDITIONAL(SAILFISH_PROVISION, test "${enable_sailfish_provision=$}" = "yes")
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(nettime, AC_HELP_STRING([--disable-nettime],
 | 
			
		||||
                                [disable Nettime plugin]),
 | 
			
		||||
                                        [enable_nettime=${enableval}])
 | 
			
		||||
@@ -243,27 +292,61 @@ AC_ARG_ENABLE(upower, AC_HELP_STRING([--disable-upower],
 | 
			
		||||
					[enable_upower=${enableval}])
 | 
			
		||||
AM_CONDITIONAL(UPOWER, test "${enable_power}" != "no")
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(mbimmodem, AC_HELP_STRING([--enable-mbimmodem],
 | 
			
		||||
				[enable MBIM based modem support]),
 | 
			
		||||
					[enable_mbimmodem=${enableval}])
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(ell, AC_HELP_STRING([--enable-ell],
 | 
			
		||||
				[enable support for ell]),
 | 
			
		||||
					[enable_ell=${enableval}])
 | 
			
		||||
 | 
			
		||||
if (test "${enable_ell}" = "yes"); then
 | 
			
		||||
	AC_DEFINE(HAVE_ELL, 1, [Defined if Ell is enabled])
 | 
			
		||||
	PKG_CHECK_MODULES(ELL, ell >= 0.2, dummy=yes,
 | 
			
		||||
			  AC_MSG_ERROR(ell library >= 0.2 is required))
 | 
			
		||||
	AC_SUBST(ELL_CFLAGS)
 | 
			
		||||
	AC_SUBST(ELL_LIBS)
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AM_CONDITIONAL(MBIMMODEM, test "${enable_ell}" != "no" && test "${enable_mbimmodem}" = "yes")
 | 
			
		||||
AM_CONDITIONAL(ELL, test "${enable_ell}" != "no")
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
 | 
			
		||||
			[do not install configuration and data files]),
 | 
			
		||||
					[enable_datafiles=${enableval}])
 | 
			
		||||
AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(pushforwarder, AC_HELP_STRING([--disable-pushforwarder],
 | 
			
		||||
                                [disable Push Forwarder plugin]),
 | 
			
		||||
                                        [enable_pushforwarder=${enableval}])
 | 
			
		||||
AM_CONDITIONAL(PUSHFORWARDER, test "${enable_pushforwarder}" != "no")
 | 
			
		||||
if (test "${enable_pushforwarder}" != "no"); then
 | 
			
		||||
AC_ARG_ENABLE(sailfish-pushforwarder, AC_HELP_STRING([--enable-sailfish-pushforwarder],
 | 
			
		||||
			[enable Sailfish OS push forwarder plugin]),
 | 
			
		||||
			[enable_sailfish_pushforwarder=${enableval}],
 | 
			
		||||
			[enable_sailfish_pushforwarder="no"])
 | 
			
		||||
AM_CONDITIONAL(SAILFISH_PUSHFORWARDER, test "${enable_sailfish_pushforwarder}" != "no")
 | 
			
		||||
if (test "${enable_sailfish_pushforwarder}" != "no"); then
 | 
			
		||||
	PKG_CHECK_MODULES(WSPCODEC, libwspcodec >= 2.0, dummy=yes,
 | 
			
		||||
		AC_MSG_ERROR(WSP decoder is required))
 | 
			
		||||
	AC_SUBST(WSPCODEC_CFLAGS)
 | 
			
		||||
	AC_SUBST(WSPCODEC_LIBS)
 | 
			
		||||
			AC_MSG_ERROR(WSP decoder is required))
 | 
			
		||||
	CFLAGS="$CFLAGS $WSPCODEC_CFLAGS"
 | 
			
		||||
	LIBS="$LIBS $WSPCODEC_LIBS"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(debuglog,
 | 
			
		||||
	AC_HELP_STRING([--enable-debuglog], [enable log control plugin]),
 | 
			
		||||
			[enable_debuglog=${enableval}], [enable_debuglog="no"])
 | 
			
		||||
AM_CONDITIONAL(DEBUGLOG, test "${enable_debuglog}" != "no")
 | 
			
		||||
if (test "${enable_debuglog}" = "yes"); then
 | 
			
		||||
AC_ARG_ENABLE(sailfish-access, AC_HELP_STRING([--enable-sailfish-access],
 | 
			
		||||
			[enable Sailfish OS access plugin]),
 | 
			
		||||
			[enable_sailfish_access=${enableval}],
 | 
			
		||||
			[enable_sailfish_access="no"])
 | 
			
		||||
 | 
			
		||||
AM_CONDITIONAL(SAILFISH_ACCESS, test "${enable_sailfish_access}" != "no")
 | 
			
		||||
if (test "${enable_sailfish_access}" == "yes"); then
 | 
			
		||||
	PKG_CHECK_MODULES(DBUSACCESS, libdbusaccess, dummy=yes,
 | 
			
		||||
				AC_MSG_ERROR(libdbusaccess is required))
 | 
			
		||||
	CFLAGS="$CFLAGS $DBUSACCESS_CFLAGS"
 | 
			
		||||
	LIBS="$LIBS $DBUSACCESS_LIBS"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sailfish-debuglog, AC_HELP_STRING([--enable-sailfish-debuglog],
 | 
			
		||||
			[enable Sailfish OS debug log plugin]),
 | 
			
		||||
			[enable_sailfish_debuglog=${enableval}],
 | 
			
		||||
			[enable_sailfish_debuglog="no"])
 | 
			
		||||
AM_CONDITIONAL(SAILFISH_DEBUGLOG, test "${enable_sailfish_debuglog}" != "no")
 | 
			
		||||
if (test "${enable_sailfish_debuglog}" = "yes"); then
 | 
			
		||||
	PKG_CHECK_MODULES(DBUSLOG, libdbuslogserver-dbus, dummy=yes,
 | 
			
		||||
				AC_MSG_ERROR(libdbuslogserver-dbus is required))
 | 
			
		||||
	CFLAGS="$CFLAGS $DBUSLOG_CFLAGS"
 | 
			
		||||
@@ -284,7 +367,7 @@ if (test "$localstatedir" = '${prefix}/var'); then
 | 
			
		||||
else
 | 
			
		||||
	storagedir="${localstatedir}/lib/ofono"
 | 
			
		||||
fi
 | 
			
		||||
AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}",
 | 
			
		||||
AC_DEFINE_UNQUOTED(DEFAULT_STORAGEDIR, "${storagedir}",
 | 
			
		||||
			[Directory for the storage files])
 | 
			
		||||
 | 
			
		||||
if (test "$sysconfdir" = '${prefix}/etc'); then
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								ofono/doc/cinterion-hardware-monitor-api.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								ofono/doc/cinterion-hardware-monitor-api.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
 | 
			
		||||
HardwareMonitor hierarchy
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
Service		org.ofono
 | 
			
		||||
Interface	org.ofono.cinterion.HardwareMonitor
 | 
			
		||||
Object path	/{device0,device1,...}
 | 
			
		||||
 | 
			
		||||
Methods		array{string,variant} GetStatistics
 | 
			
		||||
 | 
			
		||||
			Returns an array of dict entries representing the
 | 
			
		||||
			current temperature and supply voltage of the modem.
 | 
			
		||||
 | 
			
		||||
			Units:
 | 
			
		||||
			Temperature: Celsius
 | 
			
		||||
			Voltage: mV
 | 
			
		||||
@@ -19,7 +19,7 @@ Besides the kernel coding style above, oFono has special flavors for its own.
 | 
			
		||||
Some of them are mandatory (marked as 'M'), while some others are optional
 | 
			
		||||
(marked as 'O'), but generally preferred.
 | 
			
		||||
 | 
			
		||||
M1: Blank line before and after an if/while/do/for statement
 | 
			
		||||
M1: Blank line before and after an if/while/do/for/switch statement
 | 
			
		||||
============================================================
 | 
			
		||||
There should be a blank line before if statement unless the if is nested and
 | 
			
		||||
not preceded by an expression or variable declaration.
 | 
			
		||||
 
 | 
			
		||||
@@ -278,6 +278,13 @@ Properties	boolean Active [readwrite]
 | 
			
		||||
				via this proxy.  All other values are left
 | 
			
		||||
				out in this case.
 | 
			
		||||
 | 
			
		||||
			array{string} ProxyCSCF [readonly, optional]
 | 
			
		||||
 | 
			
		||||
				Holds the list of P-CSCF (SIP proxy) for this
 | 
			
		||||
				context. Only used by IMS connections.
 | 
			
		||||
 | 
			
		||||
				This is a Sailfish OS specific extension.
 | 
			
		||||
 | 
			
		||||
		dict IPv6.Settings [readonly, optional]
 | 
			
		||||
 | 
			
		||||
			Holds all the IPv6 network settings
 | 
			
		||||
@@ -304,6 +311,13 @@ Properties	boolean Active [readwrite]
 | 
			
		||||
 | 
			
		||||
				Holds the gateway IP for this connection.
 | 
			
		||||
 | 
			
		||||
			array{string} ProxyCSCF [readonly, optional]
 | 
			
		||||
 | 
			
		||||
				Holds the list of P-CSCF (SIP proxy) for this
 | 
			
		||||
				context. Only used by IMS connections.
 | 
			
		||||
 | 
			
		||||
				This is a Sailfish OS specific extension.
 | 
			
		||||
 | 
			
		||||
		string MessageProxy [readwrite, MMS only]
 | 
			
		||||
 | 
			
		||||
			Holds the MMS Proxy setting.
 | 
			
		||||
 
 | 
			
		||||
@@ -76,6 +76,22 @@ Methods		dict GetProperties()
 | 
			
		||||
					 [service].Error.NotImplemented
 | 
			
		||||
					 [service].Error.NotAllowed
 | 
			
		||||
 | 
			
		||||
		fd, byte Acquire()
 | 
			
		||||
 | 
			
		||||
			Attempts to establish the SCO audio connection
 | 
			
		||||
			returning the filedescriptor of the connection and the
 | 
			
		||||
			codec in use.
 | 
			
		||||
 | 
			
		||||
			Note: Contrary to Connect this does not call
 | 
			
		||||
			NewConnection so it can be called in a blocking
 | 
			
		||||
			manner.
 | 
			
		||||
 | 
			
		||||
			Possible Errors: [service].Error.InProgress
 | 
			
		||||
					 [service].Error.Failed
 | 
			
		||||
					 [service].Error.NotAvailable
 | 
			
		||||
					 [service].Error.NotImplemented
 | 
			
		||||
					 [service].Error.NotAllowed
 | 
			
		||||
 | 
			
		||||
Signals		PropertyChanged(string name, variant value)
 | 
			
		||||
 | 
			
		||||
			This signal indicates a changed value of the given
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								ofono/doc/ims-api.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								ofono/doc/ims-api.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
IpMultimediaSystem Hierarchy
 | 
			
		||||
============================
 | 
			
		||||
 | 
			
		||||
Service		org.ofono
 | 
			
		||||
Interface	org.ofono.IpMultimediaSystem
 | 
			
		||||
Object path	[variable prefix]/{modem0,modem1,...}
 | 
			
		||||
 | 
			
		||||
Methods		dict GetProperties()
 | 
			
		||||
 | 
			
		||||
			Returns all IpMultimediaSystem configuration properties.
 | 
			
		||||
 | 
			
		||||
		void SetProperty(string property, variant value)
 | 
			
		||||
 | 
			
		||||
			Changes the value of the specified property. Only
 | 
			
		||||
			properties that are listed as readwrite are
 | 
			
		||||
			changeable. On success a PropertyChanged signal
 | 
			
		||||
			will be emitted.
 | 
			
		||||
 | 
			
		||||
			Possible Errors: [service].Error.InProgress
 | 
			
		||||
					 [service].Error.InvalidArguments
 | 
			
		||||
					 [service].Error.Failed
 | 
			
		||||
 | 
			
		||||
		void Register()
 | 
			
		||||
 | 
			
		||||
			Attempts to register to IMS. A successful method return
 | 
			
		||||
			indicates that the registration process could be
 | 
			
		||||
			initiated successfully. The actual registration state
 | 
			
		||||
			will be reflected by the 'Registered' property.
 | 
			
		||||
 | 
			
		||||
			Possible Errors: [service].Error.InProgress
 | 
			
		||||
					 [service].Error.NotImplemented
 | 
			
		||||
 | 
			
		||||
		void Unregister()
 | 
			
		||||
 | 
			
		||||
			Attempts to unregister from IMS. A successful method
 | 
			
		||||
			return indicates that the unregistration process could
 | 
			
		||||
			be initiated successfully. The actual unregistration
 | 
			
		||||
			state will be reflected by the 'Registered' property.
 | 
			
		||||
 | 
			
		||||
			Possible Errors: [service].Error.InProgress
 | 
			
		||||
					 [service].Error.NotImplemented
 | 
			
		||||
 | 
			
		||||
Signals		PropertyChanged(string property, variant value)
 | 
			
		||||
 | 
			
		||||
			This signal indicates a changed value of the given
 | 
			
		||||
			property.
 | 
			
		||||
 | 
			
		||||
Properties	boolean Registered [readonly]
 | 
			
		||||
 | 
			
		||||
			Contains the current IMS registration state.
 | 
			
		||||
 | 
			
		||||
		boolean VoiceCapable [readonly, optional]
 | 
			
		||||
 | 
			
		||||
			Boolean representing whether voice call transfer over
 | 
			
		||||
			RTP (IMS) is available.
 | 
			
		||||
 | 
			
		||||
		boolean SmsCapable [readonly, optional]
 | 
			
		||||
 | 
			
		||||
			Boolean representing whether SMS-over-IMS is available.
 | 
			
		||||
							
								
								
									
										35
									
								
								ofono/doc/lte-api.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								ofono/doc/lte-api.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
LongTermEvolution Hierarchy
 | 
			
		||||
 | 
			
		||||
Service		org.ofono
 | 
			
		||||
Interface	org.ofono.LongTermEvolution
 | 
			
		||||
Object path	[variable prefix]/{modem0,modem1,...}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Methods		dict GetProperties()
 | 
			
		||||
 | 
			
		||||
			Returns all LongTermEvolution configuration properties.
 | 
			
		||||
 | 
			
		||||
		void SetProperty(string property, variant value)
 | 
			
		||||
 | 
			
		||||
			Changes the value of the specified property. Only
 | 
			
		||||
			properties that are listed as readwrite are
 | 
			
		||||
			changeable. On success a PropertyChanged signal
 | 
			
		||||
			will be emitted.
 | 
			
		||||
 | 
			
		||||
			Possible Errors: [service].Error.InProgress
 | 
			
		||||
					 [service].Error.InvalidArguments
 | 
			
		||||
					 [service].Error.Failed
 | 
			
		||||
 | 
			
		||||
Signals		PropertyChanged(string property, variant value)
 | 
			
		||||
 | 
			
		||||
			This signal indicates a changed value of the given
 | 
			
		||||
			property.
 | 
			
		||||
 | 
			
		||||
Properties	string DefaultAccessPointName [readwrite]
 | 
			
		||||
 | 
			
		||||
			On LongTermEvolution, contexts activate automatically.
 | 
			
		||||
			This property allows selection of an APN to be used on
 | 
			
		||||
			next automatic activation.
 | 
			
		||||
 | 
			
		||||
			Setting this property to an empty string clears the
 | 
			
		||||
			default APN from the modem.
 | 
			
		||||
@@ -95,6 +95,13 @@ Properties	boolean Powered [readwrite]
 | 
			
		||||
			String representing the software version number of the
 | 
			
		||||
			modem device.
 | 
			
		||||
 | 
			
		||||
		string SystemPath [readonly, optional]
 | 
			
		||||
 | 
			
		||||
			String representing the system path for the modem
 | 
			
		||||
			device.
 | 
			
		||||
			For modems detected by udev events, this corresponds to
 | 
			
		||||
			the modem sysfs path.
 | 
			
		||||
 | 
			
		||||
		array{string} Features [readonly]
 | 
			
		||||
 | 
			
		||||
			List of currently enabled features. It uses simple
 | 
			
		||||
@@ -127,6 +134,8 @@ Properties	boolean Powered [readwrite]
 | 
			
		||||
				org.ofono.CallVolume
 | 
			
		||||
				org.ofono.CellBroadcast
 | 
			
		||||
				org.ofono.Handsfree
 | 
			
		||||
				org.ofono.IpMultimediaSystem
 | 
			
		||||
				org.ofono.LongTermEvolution
 | 
			
		||||
				org.ofono.LocationReporting
 | 
			
		||||
				org.ofono.MessageManager
 | 
			
		||||
				org.ofono.MessageWaiting
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,34 @@ Methods		a{sv} GetServingCellInformation()
 | 
			
		||||
			are available, their valid value ranges and
 | 
			
		||||
			applicability to different cell types.
 | 
			
		||||
 | 
			
		||||
		void RegisterAgent(object path)
 | 
			
		||||
 | 
			
		||||
			Registers an agent which will be called whenever the
 | 
			
		||||
			modem registers to or moves to a new cell.
 | 
			
		||||
 | 
			
		||||
		void UnregisterAgent(object path)
 | 
			
		||||
 | 
			
		||||
			Unregisters an agent.
 | 
			
		||||
 | 
			
		||||
NetworkMonitorAgent Hierarchy [experimental]
 | 
			
		||||
=============================
 | 
			
		||||
 | 
			
		||||
Service		unique name
 | 
			
		||||
Interface	org.ofono.NetworkMonitorAgent
 | 
			
		||||
Object path	freely definable
 | 
			
		||||
 | 
			
		||||
Methods		void ServingCellInformationChanged(a{sv}) [noreply]
 | 
			
		||||
 | 
			
		||||
			This method is called whenever the serving cell
 | 
			
		||||
			information has been updated.
 | 
			
		||||
 | 
			
		||||
			Possible Errors: None
 | 
			
		||||
 | 
			
		||||
		void Release() [noreply]
 | 
			
		||||
 | 
			
		||||
			Agent is being released, possibly because of oFono
 | 
			
		||||
			terminating, NetworkMonitor interface is being torn
 | 
			
		||||
			down or modem off.  No UnregisterAgent call is needed.
 | 
			
		||||
 | 
			
		||||
Network Monitor Property Types
 | 
			
		||||
==============================
 | 
			
		||||
@@ -77,7 +105,46 @@ byte TimingAdvance [optional, gsm]
 | 
			
		||||
 | 
			
		||||
	Contains the Timing Advance.  Valid range of values is 0-219.
 | 
			
		||||
 | 
			
		||||
byte Strength [optional, gsm, umts]
 | 
			
		||||
byte Strength [optional, gsm, umts, lte]
 | 
			
		||||
 | 
			
		||||
	Contains the signal strength.  Valid values are 0-31.  Refer to <rssi>
 | 
			
		||||
	in 27.007, Section 8.5.
 | 
			
		||||
 | 
			
		||||
byte ReceivedSignalCodePower [optional, umts]
 | 
			
		||||
 | 
			
		||||
        Contains the Received Signal Code Power.  Valid range of values
 | 
			
		||||
        is 0-96. Refer to <rscp> in 27.007, Section 8.69 for more details.
 | 
			
		||||
 | 
			
		||||
byte ReceivedEnergyRatio [optional, umts]
 | 
			
		||||
 | 
			
		||||
        Contains the Ratio of received energy per PN chip to the total
 | 
			
		||||
        received power spectral density.  Valid range of values is 0-49.
 | 
			
		||||
        Refer to <ecno> in 27.007, Section 8.69 for more details.
 | 
			
		||||
 | 
			
		||||
byte ReferenceSignalReceivedQuality [optional, lte]
 | 
			
		||||
 | 
			
		||||
        Contains the Reference Signal Received Quality.  Valid range of
 | 
			
		||||
        values is 0-34. Refer to <rsrq> in 27.007, Section 8.69 for more
 | 
			
		||||
        details.
 | 
			
		||||
 | 
			
		||||
byte ReferenceSignalReceivedPower [optional, lte]
 | 
			
		||||
 | 
			
		||||
        Contains the Reference Signal Received Power.  Valid range of values
 | 
			
		||||
        is 0-97. Refer to <rsrp> in 27.007, Section 8.69 for more details.
 | 
			
		||||
 | 
			
		||||
uint16 EARFCN [optional, lte]
 | 
			
		||||
 | 
			
		||||
	Contains E-UTRA Absolute Radio Frequency Channel Number.  Valid
 | 
			
		||||
	range of values is 0-65535. Refer to Carrier frequency and
 | 
			
		||||
	EARFCN in 36.101, Section 5.7.3 for more details.
 | 
			
		||||
 | 
			
		||||
byte EBand [optional, lte]
 | 
			
		||||
 | 
			
		||||
	Contains E-UTRA operating Band.  Valid range of values is 1-43.
 | 
			
		||||
	Refer to Operating bands in 36.101, Section 5.5 for more
 | 
			
		||||
	details.
 | 
			
		||||
 | 
			
		||||
byte ChannelQualityIndicator [optional, lte]
 | 
			
		||||
 | 
			
		||||
	Contains Channel Quality Indicator.  Refer to Channel Quality
 | 
			
		||||
	Indicator definition in 36.213, Section 7.2.3 for more details.
 | 
			
		||||
 
 | 
			
		||||
@@ -200,3 +200,8 @@ Properties	boolean Present [readonly]
 | 
			
		||||
			might have changed the retry counters, i.e. calls to
 | 
			
		||||
			ChangePin(), EnterPin(), ResetPin() LockPin(),
 | 
			
		||||
			UnlockPin().
 | 
			
		||||
 | 
			
		||||
		string ImsPrivateIdentity [readonly, optional]
 | 
			
		||||
 | 
			
		||||
			Contains the SIM's ImsPrivateIdentity, read from the
 | 
			
		||||
			ISIM.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										104
									
								
								ofono/doc/sim-auth-api.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								ofono/doc/sim-auth-api.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
SimAuthentication heiarchy [experimental]
 | 
			
		||||
===========================================
 | 
			
		||||
 | 
			
		||||
Service		org.ofono
 | 
			
		||||
Interface	org.ofono.SimAuthentication
 | 
			
		||||
Object path	[variable prefix]/{modem0,modem1,...}
 | 
			
		||||
 | 
			
		||||
Methods		array{object,dict} GetApplications()
 | 
			
		||||
 | 
			
		||||
			Get an array of all SIM applications found during
 | 
			
		||||
			discovery. In the format "a{oa{sv}}" where 'o' is
 | 
			
		||||
			the object path for the application e.g.
 | 
			
		||||
 | 
			
		||||
			o = "/modem1/A0000000871004FFFFFFFF8906190000"
 | 
			
		||||
 | 
			
		||||
			Each dictionary will contain 'Type' e.g. 'Ims' and
 | 
			
		||||
			'Name' e.g. 'ISim'
 | 
			
		||||
 | 
			
		||||
			For each application there will be a corresponding
 | 
			
		||||
			object that matches the path (o). The type will
 | 
			
		||||
			signify which interfaces are under that object (below).
 | 
			
		||||
 | 
			
		||||
			type = Umts --> org.ofono.USimApplication
 | 
			
		||||
			type = Ims  --> org.ofono.ISimApplication
 | 
			
		||||
 | 
			
		||||
SimAuth USIM application heiarchy [experimental]
 | 
			
		||||
===========================================
 | 
			
		||||
 | 
			
		||||
Service		org.ofono
 | 
			
		||||
Interface	org.ofono.USimApplication
 | 
			
		||||
Object path	[variable prefix]/{modem0,modem1,...}/{AID name}
 | 
			
		||||
 | 
			
		||||
Methods		dict GetProperties()
 | 
			
		||||
 | 
			
		||||
			Returns properties for the USimApplication. See
 | 
			
		||||
			properties section for available properties.
 | 
			
		||||
 | 
			
		||||
		array{dict{string, array{byte}}}
 | 
			
		||||
			GsmAuthenticate(array{array{byte}} rands)
 | 
			
		||||
 | 
			
		||||
			Run the USIM application GSM AUTHENTICATE algorithm
 | 
			
		||||
			with N random challenges 'rands'. This should be an
 | 
			
		||||
			array of an array of bytes ("aay"). The number of
 | 
			
		||||
			random challenges is limited to a maximum of 3.
 | 
			
		||||
 | 
			
		||||
			Returns the derived Kc/SRES values as an array of
 | 
			
		||||
			dictionaries. The index of each dictionary matches
 | 
			
		||||
			the index of the rand value in the method call. The
 | 
			
		||||
			keys for each dictionary are "Kc" and "SRES" and both
 | 
			
		||||
			are arrays of bytes.
 | 
			
		||||
 | 
			
		||||
			Possible Errors:
 | 
			
		||||
				[service].Error.NotSupported
 | 
			
		||||
				[service].Error.Busy
 | 
			
		||||
 | 
			
		||||
		dict{string, array{byte}}
 | 
			
		||||
			UmtsAuthenticate(array{byte} rand, array{byte} autn)
 | 
			
		||||
 | 
			
		||||
			Run the UMTS AUTHENTICATE algorithm in the 3G
 | 
			
		||||
			context with 'rand' and 'autn'. A dictionary will be
 | 
			
		||||
			returned containing 'RES', 'CK', 'IK' and possibly
 | 
			
		||||
			'Kc' if service 27 is available. If there was a
 | 
			
		||||
			sync error 'AUTS' will be returned.
 | 
			
		||||
 | 
			
		||||
			Possible Errors: [service].Error.NotSupported
 | 
			
		||||
 | 
			
		||||
Properties	string Type [readonly]
 | 
			
		||||
 | 
			
		||||
			Type of application: 'Umts'
 | 
			
		||||
 | 
			
		||||
		string Name [readonly]
 | 
			
		||||
 | 
			
		||||
			Human readable name: 'USim'
 | 
			
		||||
 | 
			
		||||
SimAuth ISIM application heiarchy [experimental]
 | 
			
		||||
===========================================
 | 
			
		||||
 | 
			
		||||
Service		org.ofono
 | 
			
		||||
Interface	org.ofono.ISimApplication
 | 
			
		||||
Object		[variable prefix]/{modem0,modem1,...}/{AID name}
 | 
			
		||||
 | 
			
		||||
Methods		dict GetProperties()
 | 
			
		||||
 | 
			
		||||
			Returns properties for the ISimApplication. See
 | 
			
		||||
			the properties section for available properties.
 | 
			
		||||
 | 
			
		||||
		dict{string, array{byte}
 | 
			
		||||
			ImsAuthenticate(array{byte} rand, array{byte} autn)
 | 
			
		||||
 | 
			
		||||
			Run the UMTS AUTHENTICATE algorithm in the IMS
 | 
			
		||||
			context with 'rand' and 'autn'. A dictionary will be
 | 
			
		||||
			returned containing 'RES', 'CK', 'IK' and possibly
 | 
			
		||||
			'Kc' if service 27 is available. If there was a
 | 
			
		||||
			sync error 'AUTS' will be returned.
 | 
			
		||||
 | 
			
		||||
			Possible Errors: [service].Error.NotSupported
 | 
			
		||||
 | 
			
		||||
Properties	string Type [readonly]
 | 
			
		||||
 | 
			
		||||
			Type of application: 'Ims'
 | 
			
		||||
 | 
			
		||||
		string Name [readonly]
 | 
			
		||||
 | 
			
		||||
			Human readable name: 'ISim'
 | 
			
		||||
@@ -17,3 +17,30 @@ GPS:
 | 
			
		||||
  After setting the configuration, a power cycle is required.
 | 
			
		||||
  Port Configiuration #8 is available since firmware 12.00.004. Firmware version
 | 
			
		||||
  can be checked using 'AT+CGMR'.
 | 
			
		||||
 | 
			
		||||
LE910 V2
 | 
			
		||||
========
 | 
			
		||||
 | 
			
		||||
Default USB composition of LE910V2 uses PID 0x36 (AT#PORTCFG=0)
 | 
			
		||||
and consists of 6 serial ports (CDC-ACM standard, /dev/ttyACMx)
 | 
			
		||||
and 1 network adapter using CDC-NCM standard (wwanx or usbx).
 | 
			
		||||
 | 
			
		||||
NCM interface configuration follows Telit documentation
 | 
			
		||||
(both documents available on Telit Download Zone - registration required)
 | 
			
		||||
"GE/HE/UE910, UL865, LE910 V2 Linux USB Driver - User Guide r0"
 | 
			
		||||
(document 1VV0301255 Rev.0 - 2016-01-22)
 | 
			
		||||
and "Telit LE910-V2 NCM SETUP r3"
 | 
			
		||||
(document 1VV0301246 Rev.3 - 2016-11-29).
 | 
			
		||||
 | 
			
		||||
After context is setup, NCM mode activated and PDP context activated
 | 
			
		||||
connection configuration can be read using
 | 
			
		||||
AT+CGPADDR=context_id and AT+CGCONTRDP=context_id commands.
 | 
			
		||||
This is done automatically and results available via
 | 
			
		||||
org.ofono.ConnectionContext.GetProperties DBus method.
 | 
			
		||||
 | 
			
		||||
Then Linux network interface needs to be configured:
 | 
			
		||||
    ifconfig <Interface> <Address> netmask <Netmask> up
 | 
			
		||||
    route add default gw <Gateway>
 | 
			
		||||
    arp -s <Gateway> 11:22:33:44:55:66
 | 
			
		||||
 | 
			
		||||
Only after these steps network interface is usable.
 | 
			
		||||
 
 | 
			
		||||
@@ -59,6 +59,27 @@ Methods		dict GetProperties()
 | 
			
		||||
					 [service].Error.NotImplemented
 | 
			
		||||
					 [service].Error.Failed
 | 
			
		||||
 | 
			
		||||
		object DialLast()
 | 
			
		||||
 | 
			
		||||
			Initiates a new outgoing call to the last dialled number.
 | 
			
		||||
 | 
			
		||||
			Possible Errors: [service].Error.InProgress
 | 
			
		||||
					 [service].Error.InvalidArguments
 | 
			
		||||
					 [service].Error.InvalidFormat
 | 
			
		||||
					 [service].Error.NotImplemented
 | 
			
		||||
					 [service].Error.Failed
 | 
			
		||||
 | 
			
		||||
		object DialMemory(string memory position, string hide_callerid)
 | 
			
		||||
 | 
			
		||||
			Initiates a new outgoing call to the number in the given memory
 | 
			
		||||
			position/favourite. For callerid see the Dial method.
 | 
			
		||||
 | 
			
		||||
			Possible Errors: [service].Error.InProgress
 | 
			
		||||
					 [service].Error.InvalidArguments
 | 
			
		||||
					 [service].Error.InvalidFormat
 | 
			
		||||
					 [service].Error.NotImplemented
 | 
			
		||||
					 [service].Error.Failed
 | 
			
		||||
 | 
			
		||||
		void Transfer()
 | 
			
		||||
 | 
			
		||||
			Joins the currently Active (or Outgoing, depending
 | 
			
		||||
 
 | 
			
		||||
@@ -50,15 +50,14 @@ static int atmodem_init(void)
 | 
			
		||||
	at_call_volume_init();
 | 
			
		||||
	at_gprs_init();
 | 
			
		||||
	at_gprs_context_init();
 | 
			
		||||
	at_sim_auth_init();
 | 
			
		||||
	at_gnss_init();
 | 
			
		||||
	at_lte_init();
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void atmodem_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	at_sim_auth_exit();
 | 
			
		||||
	at_stk_exit();
 | 
			
		||||
	at_sim_exit();
 | 
			
		||||
	at_sms_exit();
 | 
			
		||||
@@ -76,6 +75,7 @@ static void atmodem_exit(void)
 | 
			
		||||
	at_gprs_exit();
 | 
			
		||||
	at_gprs_context_exit();
 | 
			
		||||
	at_gnss_exit();
 | 
			
		||||
	at_lte_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OFONO_PLUGIN_DEFINE(atmodem, "AT modem driver", VERSION,
 | 
			
		||||
 
 | 
			
		||||
@@ -74,3 +74,6 @@ extern void at_sim_auth_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void at_gnss_init(void);
 | 
			
		||||
extern void at_gnss_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void at_lte_init(void);
 | 
			
		||||
extern void at_lte_exit(void);
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@
 | 
			
		||||
#include <gatchat.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#define OFONO_API_SUBJECT_TO_CHANGE
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
@@ -614,3 +615,42 @@ void at_util_sim_state_query_free(struct at_util_sim_state_query *req)
 | 
			
		||||
 | 
			
		||||
	g_free(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * CGCONTRDP returns addr + netmask in the same string in the form
 | 
			
		||||
 * of "a.b.c.d.m.m.m.m" for IPv4.
 | 
			
		||||
 * address/netmask must be able to hold
 | 
			
		||||
 * 255.255.255.255 + null = 16 characters
 | 
			
		||||
 */
 | 
			
		||||
int at_util_get_ipv4_address_and_netmask(const char *addrnetmask,
 | 
			
		||||
						char *address, char *netmask)
 | 
			
		||||
{
 | 
			
		||||
	const char *s = addrnetmask;
 | 
			
		||||
	const char *net = NULL;
 | 
			
		||||
 | 
			
		||||
	int ret = -EINVAL;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	/* Count 7 dots for ipv4, less or more means error. */
 | 
			
		||||
	for (i = 0; i < 9; i++, s++) {
 | 
			
		||||
		s = strchr(s, '.');
 | 
			
		||||
 | 
			
		||||
		if (!s)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (i == 3) {
 | 
			
		||||
			/* set netmask ptr and break the string */
 | 
			
		||||
			net = s + 1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (i == 7) {
 | 
			
		||||
		memcpy(address, addrnetmask, net - addrnetmask);
 | 
			
		||||
		address[net - addrnetmask - 1] = '\0';
 | 
			
		||||
		strcpy(netmask, net);
 | 
			
		||||
 | 
			
		||||
		ret = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -83,6 +83,9 @@ struct at_util_sim_state_query *at_util_sim_state_query_new(GAtChat *chat,
 | 
			
		||||
						GDestroyNotify destroy);
 | 
			
		||||
void at_util_sim_state_query_free(struct at_util_sim_state_query *req);
 | 
			
		||||
 | 
			
		||||
int at_util_get_ipv4_address_and_netmask(const char *addrnetmask,
 | 
			
		||||
						char *address, char *netmask);
 | 
			
		||||
 | 
			
		||||
struct cb_data {
 | 
			
		||||
	void *cb;
 | 
			
		||||
	void *data;
 | 
			
		||||
 
 | 
			
		||||
@@ -43,10 +43,11 @@
 | 
			
		||||
#include "atmodem.h"
 | 
			
		||||
#include "vendor.h"
 | 
			
		||||
 | 
			
		||||
#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun"
 | 
			
		||||
#define TUN_DEV "/dev/net/tun"
 | 
			
		||||
 | 
			
		||||
#define STATIC_IP_NETMASK "255.255.255.255"
 | 
			
		||||
 | 
			
		||||
static const char *cgdata_prefix[] = { "+CGDATA:", NULL };
 | 
			
		||||
static const char *none_prefix[] = { NULL };
 | 
			
		||||
 | 
			
		||||
enum state {
 | 
			
		||||
@@ -67,6 +68,7 @@ struct gprs_context_data {
 | 
			
		||||
	ofono_gprs_context_cb_t cb;
 | 
			
		||||
	void *cb_data;                                  /* Callback data */
 | 
			
		||||
	unsigned int vendor;
 | 
			
		||||
	gboolean use_atd99;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ppp_debug(const char *str, void *data)
 | 
			
		||||
@@ -210,7 +212,7 @@ static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (gcd->vendor == OFONO_VENDOR_SIMCOM_SIM900)
 | 
			
		||||
	if (gcd->use_atd99)
 | 
			
		||||
		sprintf(buf, "ATD*99***%u#", gcd->active_context);
 | 
			
		||||
	else
 | 
			
		||||
		sprintf(buf, "AT+CGDATA=\"PPP\",%u", gcd->active_context);
 | 
			
		||||
@@ -247,6 +249,8 @@ static void at_gprs_activate_primary(struct ofono_gprs_context *gc,
 | 
			
		||||
 | 
			
		||||
	/* We only support CHAP and PAP */
 | 
			
		||||
	switch (ctx->auth_method) {
 | 
			
		||||
	case OFONO_GPRS_AUTH_METHOD_ANY:
 | 
			
		||||
	case OFONO_GPRS_AUTH_METHOD_NONE:
 | 
			
		||||
	case OFONO_GPRS_AUTH_METHOD_CHAP:
 | 
			
		||||
		gcd->auth_method = G_AT_PPP_AUTH_METHOD_CHAP;
 | 
			
		||||
		break;
 | 
			
		||||
@@ -294,6 +298,8 @@ static void at_gprs_activate_primary(struct ofono_gprs_context *gc,
 | 
			
		||||
			 * prefix, this is the least invasive place to set it.
 | 
			
		||||
			 */
 | 
			
		||||
			switch (ctx->auth_method) {
 | 
			
		||||
			case OFONO_GPRS_AUTH_METHOD_ANY:
 | 
			
		||||
			case OFONO_GPRS_AUTH_METHOD_NONE:
 | 
			
		||||
			case OFONO_GPRS_AUTH_METHOD_CHAP:
 | 
			
		||||
				snprintf(buf + len, sizeof(buf) - len - 3,
 | 
			
		||||
						",\"CHAP:%s\"", ctx->apn);
 | 
			
		||||
@@ -378,6 +384,43 @@ static void cgev_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
		g_at_ppp_shutdown(gcd->ppp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_cgdata_test_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
				gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs_context *gc = user_data;
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	const char *data_type;
 | 
			
		||||
	gboolean found = FALSE;
 | 
			
		||||
 | 
			
		||||
	gcd->use_atd99 = TRUE;
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		DBG("not ok");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "+CGDATA:")) {
 | 
			
		||||
		DBG("no +CGDATA line");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_open_list(&iter)) {
 | 
			
		||||
		DBG("no list found");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (!found && g_at_result_iter_next_string(&iter, &data_type)) {
 | 
			
		||||
		if (g_str_equal(data_type, "PPP")) {
 | 
			
		||||
			found = TRUE;
 | 
			
		||||
			gcd->use_atd99 = FALSE;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
error:
 | 
			
		||||
	DBG("use_atd99:%d", gcd->use_atd99);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int at_gprs_context_probe(struct ofono_gprs_context *gc,
 | 
			
		||||
					unsigned int vendor, void *data)
 | 
			
		||||
{
 | 
			
		||||
@@ -387,7 +430,7 @@ static int at_gprs_context_probe(struct ofono_gprs_context *gc,
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (stat(TUN_SYSFS_DIR, &st) < 0) {
 | 
			
		||||
	if (stat(TUN_DEV, &st) < 0) {
 | 
			
		||||
		ofono_error("Missing support for TUN/TAP devices");
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	}
 | 
			
		||||
@@ -405,6 +448,15 @@ static int at_gprs_context_probe(struct ofono_gprs_context *gc,
 | 
			
		||||
	if (chat == NULL)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	switch (vendor) {
 | 
			
		||||
	case OFONO_VENDOR_SIMCOM_SIM900:
 | 
			
		||||
		gcd->use_atd99 = FALSE;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		g_at_chat_send(chat, "AT+CGDATA=?", cgdata_prefix,
 | 
			
		||||
						at_cgdata_test_cb, gc, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,8 @@ struct gprs_data {
 | 
			
		||||
	GAtChat *chat;
 | 
			
		||||
	unsigned int vendor;
 | 
			
		||||
	unsigned int last_auto_context_id;
 | 
			
		||||
	gboolean telit_try_reattach;
 | 
			
		||||
	int attached;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void at_cgatt_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
@@ -73,8 +75,10 @@ static void at_gprs_set_attached(struct ofono_gprs *gprs, int attached,
 | 
			
		||||
	snprintf(buf, sizeof(buf), "AT+CGATT=%i", attached ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(gd->chat, buf, none_prefix,
 | 
			
		||||
				at_cgatt_cb, cbd, g_free) > 0)
 | 
			
		||||
				at_cgatt_cb, cbd, g_free) > 0) {
 | 
			
		||||
		gd->attached = attached;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
 | 
			
		||||
@@ -194,6 +198,28 @@ static void cgreg_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
				NULL, NULL, NULL, gd->vendor) == FALSE)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Telit AT modem firmware (tested with UE910-EUR) generates
 | 
			
		||||
	 * +CGREG: 0\r\n\r\n+CGEV: NW DETACH
 | 
			
		||||
	 * after a context is de-activated and ppp connection closed.
 | 
			
		||||
	 * Then, after a random amount of time (observed from a few seconds
 | 
			
		||||
	 * to a few hours), an unsolicited +CGREG: 1 arrives.
 | 
			
		||||
	 * Attempt to fix the problem, by sending AT+CGATT=1 once.
 | 
			
		||||
	 * This does not re-activate the context, but if a network connection
 | 
			
		||||
	 * is still correct, will generate an immediate +CGREG: 1.
 | 
			
		||||
	 */
 | 
			
		||||
	if (gd->vendor == OFONO_VENDOR_TELIT) {
 | 
			
		||||
		if (gd->attached && !status && !gd->telit_try_reattach) {
 | 
			
		||||
			DBG("Trying to re-attach gprs network");
 | 
			
		||||
			gd->telit_try_reattach = TRUE;
 | 
			
		||||
			g_at_chat_send(gd->chat, "AT+CGATT=1", none_prefix,
 | 
			
		||||
					NULL, NULL, NULL);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		gd->telit_try_reattach = FALSE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_status_notify(gprs, status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -214,6 +240,11 @@ static void cgev_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
 | 
			
		||||
	if (g_str_equal(event, "NW DETACH") ||
 | 
			
		||||
			g_str_equal(event, "ME DETACH")) {
 | 
			
		||||
		if (gd->vendor == OFONO_VENDOR_TELIT &&
 | 
			
		||||
				gd->telit_try_reattach)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		gd->attached = FALSE;
 | 
			
		||||
		ofono_gprs_detached_notify(gprs);
 | 
			
		||||
		return;
 | 
			
		||||
	} else if (g_str_has_prefix(event, "ME PDN ACT")) {
 | 
			
		||||
@@ -296,6 +327,26 @@ static void huawei_mode_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
	ofono_gprs_bearer_notify(gprs, bearer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void huawei_hcsq_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs *gprs = user_data;
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	const char *mode;
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "^HCSQ:"))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_string(&iter, &mode))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!strcmp("LTE", mode))
 | 
			
		||||
		ofono_gprs_bearer_notify(gprs, 7); /* LTE */
 | 
			
		||||
 | 
			
		||||
	/* in other modes, notification ^MODE is used */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void telit_mode_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs *gprs = user_data;
 | 
			
		||||
@@ -323,6 +374,9 @@ static void telit_mode_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
	case 3:
 | 
			
		||||
		bearer = 5;    /* HSDPA */
 | 
			
		||||
		break;
 | 
			
		||||
	case 4:
 | 
			
		||||
		bearer = 7;    /* LTE */
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		bearer = 0;
 | 
			
		||||
		break;
 | 
			
		||||
@@ -398,6 +452,8 @@ static void gprs_initialized(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
	case OFONO_VENDOR_HUAWEI:
 | 
			
		||||
		g_at_chat_register(gd->chat, "^MODE:", huawei_mode_notify,
 | 
			
		||||
						FALSE, gprs, NULL);
 | 
			
		||||
		g_at_chat_register(gd->chat, "^HCSQ:", huawei_hcsq_notify,
 | 
			
		||||
						FALSE, gprs, NULL);
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_VENDOR_UBLOX:
 | 
			
		||||
	case OFONO_VENDOR_UBLOX_TOBY_L2:
 | 
			
		||||
@@ -411,6 +467,7 @@ static void gprs_initialized(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
						FALSE, gprs, NULL);
 | 
			
		||||
		g_at_chat_send(gd->chat, "AT#PSNT=1", none_prefix,
 | 
			
		||||
						NULL, NULL, NULL);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		g_at_chat_register(gd->chat, "+CPSB:", cpsb_notify,
 | 
			
		||||
						FALSE, gprs, NULL);
 | 
			
		||||
@@ -522,7 +579,7 @@ static void at_cgdcont_test_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
		if (g_at_result_iter_next_range(&iter, &min, &max) == FALSE)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!g_at_result_iter_close_list(&iter))
 | 
			
		||||
		if (!g_at_result_iter_skip_next(&iter))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (g_at_result_iter_open_list(&iter))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										142
									
								
								ofono/drivers/atmodem/lte.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								ofono/drivers/atmodem/lte.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/gprs-context.h>
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/lte.h>
 | 
			
		||||
 | 
			
		||||
#include "gatchat.h"
 | 
			
		||||
#include "gatresult.h"
 | 
			
		||||
 | 
			
		||||
#include "atmodem.h"
 | 
			
		||||
 | 
			
		||||
struct lte_driver_data {
 | 
			
		||||
	GAtChat *chat;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void at_lte_set_default_attach_info_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
							gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_lte_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
 | 
			
		||||
	DBG("ok %d", ok);
 | 
			
		||||
 | 
			
		||||
	decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
	cb(&error, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_lte_set_default_attach_info(const struct ofono_lte *lte,
 | 
			
		||||
			const struct ofono_lte_default_attach_info *info,
 | 
			
		||||
			ofono_lte_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct lte_driver_data *ldd = ofono_lte_get_data(lte);
 | 
			
		||||
	char buf[32 + OFONO_GPRS_MAX_APN_LENGTH + 1];
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
 | 
			
		||||
	DBG("LTE config with APN: %s", info->apn);
 | 
			
		||||
 | 
			
		||||
	if (strlen(info->apn) > 0)
 | 
			
		||||
		snprintf(buf, sizeof(buf), "AT+CGDCONT=0,\"IP\",\"%s\"",
 | 
			
		||||
				info->apn);
 | 
			
		||||
	else
 | 
			
		||||
		snprintf(buf, sizeof(buf), "AT+CGDCONT=0,\"IP\"");
 | 
			
		||||
 | 
			
		||||
	/* We can't do much in case of failure so don't check response. */
 | 
			
		||||
	if (g_at_chat_send(ldd->chat, buf, NULL,
 | 
			
		||||
			at_lte_set_default_attach_info_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean lte_delayed_register(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_lte *lte = user_data;
 | 
			
		||||
 | 
			
		||||
	ofono_lte_register(lte);
 | 
			
		||||
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int at_lte_probe(struct ofono_lte *lte, void *data)
 | 
			
		||||
{
 | 
			
		||||
	GAtChat *chat = data;
 | 
			
		||||
	struct lte_driver_data *ldd;
 | 
			
		||||
 | 
			
		||||
	DBG("at lte probe");
 | 
			
		||||
 | 
			
		||||
	ldd = g_try_new0(struct lte_driver_data, 1);
 | 
			
		||||
	if (!ldd)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	ldd->chat = g_at_chat_clone(chat);
 | 
			
		||||
 | 
			
		||||
	ofono_lte_set_data(lte, ldd);
 | 
			
		||||
 | 
			
		||||
	g_idle_add(lte_delayed_register, lte);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_lte_remove(struct ofono_lte *lte)
 | 
			
		||||
{
 | 
			
		||||
	struct lte_driver_data *ldd = ofono_lte_get_data(lte);
 | 
			
		||||
 | 
			
		||||
	DBG("at lte remove");
 | 
			
		||||
 | 
			
		||||
	g_at_chat_unref(ldd->chat);
 | 
			
		||||
 | 
			
		||||
	ofono_lte_set_data(lte, NULL);
 | 
			
		||||
 | 
			
		||||
	g_free(ldd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_lte_driver driver = {
 | 
			
		||||
	.name				= "atmodem",
 | 
			
		||||
	.probe				= at_lte_probe,
 | 
			
		||||
	.remove				= at_lte_remove,
 | 
			
		||||
	.set_default_attach_info	= at_lte_set_default_attach_info,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void at_lte_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_lte_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void at_lte_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_lte_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
@@ -1088,6 +1088,27 @@ static void huawei_mode_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void huawei_hcsq_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_netreg *netreg = user_data;
 | 
			
		||||
	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	const char *mode;
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "^HCSQ:"))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_string(&iter, &mode))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!strcmp("LTE", mode))
 | 
			
		||||
		nd->tech = ACCESS_TECHNOLOGY_EUTRAN;
 | 
			
		||||
 | 
			
		||||
	/* for other technologies, notification ^MODE is used */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void huawei_nwtime_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_netreg *netreg = user_data;
 | 
			
		||||
@@ -1896,6 +1917,10 @@ static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
		g_at_chat_register(nd->chat, "^MODE:", huawei_mode_notify,
 | 
			
		||||
						FALSE, netreg, NULL);
 | 
			
		||||
 | 
			
		||||
		/* Register for 4G system mode reports */
 | 
			
		||||
		g_at_chat_register(nd->chat, "^HCSQ:", huawei_hcsq_notify,
 | 
			
		||||
						FALSE, netreg, NULL);
 | 
			
		||||
 | 
			
		||||
		/* Register for network time reports */
 | 
			
		||||
		g_at_chat_register(nd->chat, "^NWTIME:", huawei_nwtime_notify,
 | 
			
		||||
						FALSE, netreg, NULL);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,164 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/sim-auth.h>
 | 
			
		||||
 | 
			
		||||
#include "gatchat.h"
 | 
			
		||||
#include "gatresult.h"
 | 
			
		||||
#include "simutil.h"
 | 
			
		||||
#include "vendor.h"
 | 
			
		||||
 | 
			
		||||
#include "atmodem.h"
 | 
			
		||||
 | 
			
		||||
struct sim_auth_data {
 | 
			
		||||
	GAtChat *chat;
 | 
			
		||||
	unsigned int vendor;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *cuad_prefix[] = { "+CUAD:", NULL };
 | 
			
		||||
 | 
			
		||||
static void at_discover_apps_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
				gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	ofono_sim_list_apps_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
	const unsigned char *dataobj;
 | 
			
		||||
	gint linelen;
 | 
			
		||||
	unsigned char *buffer;
 | 
			
		||||
	int len;
 | 
			
		||||
 | 
			
		||||
	decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		cb(&error, NULL, 0, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	len = 0;
 | 
			
		||||
	while (g_at_result_iter_next(&iter, "+CUAD:")) {
 | 
			
		||||
		if (!g_at_result_iter_next_hexstring(&iter, NULL, &linelen))
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		len += linelen;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	buffer = g_malloc(len);
 | 
			
		||||
	len = 0;
 | 
			
		||||
 | 
			
		||||
	while (g_at_result_iter_next(&iter, "+CUAD:")) {
 | 
			
		||||
		g_at_result_iter_next_hexstring(&iter, &dataobj, &linelen);
 | 
			
		||||
		memcpy(buffer + len, dataobj, linelen);
 | 
			
		||||
		len += linelen;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cb(&error, buffer, len, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_free(buffer);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_discover_apps(struct ofono_sim_auth *sa,
 | 
			
		||||
				ofono_sim_list_apps_cb_t cb,
 | 
			
		||||
				void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_auth_data *sad = ofono_sim_auth_get_data(sa);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(sad->chat, "AT+CUAD", cuad_prefix,
 | 
			
		||||
					at_discover_apps_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean at_sim_auth_register(gpointer user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sim_auth *sa = user;
 | 
			
		||||
 | 
			
		||||
	ofono_sim_auth_register(sa);
 | 
			
		||||
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int at_sim_auth_probe(struct ofono_sim_auth *sa, unsigned int vendor,
 | 
			
		||||
				void *data)
 | 
			
		||||
{
 | 
			
		||||
	GAtChat *chat = data;
 | 
			
		||||
	struct sim_auth_data *sad;
 | 
			
		||||
 | 
			
		||||
	sad = g_new0(struct sim_auth_data, 1);
 | 
			
		||||
	sad->chat = g_at_chat_clone(chat);
 | 
			
		||||
	sad->vendor = vendor;
 | 
			
		||||
 | 
			
		||||
	ofono_sim_auth_set_data(sa, sad);
 | 
			
		||||
	g_idle_add(at_sim_auth_register, sa);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_sim_auth_remove(struct ofono_sim_auth *sa)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_auth_data *sad = ofono_sim_auth_get_data(sa);
 | 
			
		||||
 | 
			
		||||
	g_idle_remove_by_data(sa);
 | 
			
		||||
	ofono_sim_auth_set_data(sa, NULL);
 | 
			
		||||
 | 
			
		||||
	g_at_chat_unref(sad->chat);
 | 
			
		||||
	g_free(sad);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_sim_auth_driver driver = {
 | 
			
		||||
	.name		= "atmodem",
 | 
			
		||||
	.probe		= at_sim_auth_probe,
 | 
			
		||||
	.remove		= at_sim_auth_remove,
 | 
			
		||||
	.list_apps	= at_discover_apps,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void at_sim_auth_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_sim_auth_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void at_sim_auth_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_sim_auth_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
@@ -39,6 +39,7 @@
 | 
			
		||||
#include "gatresult.h"
 | 
			
		||||
#include "simutil.h"
 | 
			
		||||
#include "vendor.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
#include "atmodem.h"
 | 
			
		||||
 | 
			
		||||
@@ -50,7 +51,7 @@
 | 
			
		||||
struct sim_data {
 | 
			
		||||
	GAtChat *chat;
 | 
			
		||||
	unsigned int vendor;
 | 
			
		||||
	guint ready_id;
 | 
			
		||||
	guint passwd_type_mask;
 | 
			
		||||
	struct at_util_sim_state_query *sim_state_query;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -64,14 +65,36 @@ static const char *pinnum_prefix[] = { "%PINNUM:", NULL };
 | 
			
		||||
static const char *oercn_prefix[] = { "_OERCN:", NULL };
 | 
			
		||||
static const char *cpinr_prefixes[] = { "+CPINR:", "+CPINRE:", NULL };
 | 
			
		||||
static const char *epin_prefix[] = { "*EPIN:", NULL };
 | 
			
		||||
static const char *spic_prefix[] = { "+SPIC:", NULL };
 | 
			
		||||
static const char *simcom_spic_prefix[] = { "+SPIC:", NULL };
 | 
			
		||||
static const char *cinterion_spic_prefix[] = { "^SPIC:", NULL };
 | 
			
		||||
static const char *pct_prefix[] = { "#PCT:", NULL };
 | 
			
		||||
static const char *pnnm_prefix[] = { "+PNNM:", NULL };
 | 
			
		||||
static const char *qpinc_prefix[] = { "+QPINC:", NULL };
 | 
			
		||||
static const char *upincnt_prefix[] = { "+UPINCNT:", NULL };
 | 
			
		||||
static const char *cuad_prefix[] = { "+CUAD:", NULL };
 | 
			
		||||
static const char *ccho_prefix[] = { "+CCHO:", NULL };
 | 
			
		||||
static const char *crla_prefix[] = { "+CRLA:", NULL };
 | 
			
		||||
static const char *cgla_prefix[] = { "+CGLA:", NULL };
 | 
			
		||||
static const char *none_prefix[] = { NULL };
 | 
			
		||||
 | 
			
		||||
static void at_crsm_info_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
static void append_file_path(char *buf, const unsigned char *path,
 | 
			
		||||
		unsigned int path_len)
 | 
			
		||||
{
 | 
			
		||||
	if (path_len > 0) {
 | 
			
		||||
		*buf++ = ',';
 | 
			
		||||
		*buf++ = ',';
 | 
			
		||||
		*buf++ = '\"';
 | 
			
		||||
 | 
			
		||||
		for (; path_len; path_len--)
 | 
			
		||||
			buf += sprintf(buf, "%02hhX", *path++);
 | 
			
		||||
 | 
			
		||||
		*buf++ = '\"';
 | 
			
		||||
		*buf = '\0';
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_response_common_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
		gpointer user_data, const char *prefix)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
@@ -93,7 +116,7 @@ static void at_crsm_info_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "+CRSM:"))
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, prefix))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_next_number(&iter, &sw1);
 | 
			
		||||
@@ -134,6 +157,11 @@ error:
 | 
			
		||||
				EF_STATUS_INVALIDATED, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_crsm_info_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	get_response_common_cb(ok, result, user_data, "+CRSM:");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_sim_read_info(struct ofono_sim *sim, int fileid,
 | 
			
		||||
				const unsigned char *path,
 | 
			
		||||
				unsigned int path_len,
 | 
			
		||||
@@ -175,15 +203,7 @@ static void at_sim_read_info(struct ofono_sim *sim, int fileid,
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (path_len > 0) {
 | 
			
		||||
		len += sprintf(buf + len, ",,\"");
 | 
			
		||||
 | 
			
		||||
		for (; path_len; path_len--)
 | 
			
		||||
			len += sprintf(buf + len, "%02hhX", *path++);
 | 
			
		||||
 | 
			
		||||
		buf[len++] = '\"';
 | 
			
		||||
		buf[len] = '\0';
 | 
			
		||||
	}
 | 
			
		||||
	append_file_path(buf + len, path, path_len);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(sd->chat, buf, crsm_prefix,
 | 
			
		||||
				at_crsm_info_cb, cbd, g_free) > 0)
 | 
			
		||||
@@ -257,17 +277,7 @@ static void at_sim_read_binary(struct ofono_sim *sim, int fileid,
 | 
			
		||||
	len = snprintf(buf, sizeof(buf), "AT+CRSM=176,%i,%i,%i,%i", fileid,
 | 
			
		||||
			start >> 8, start & 0xff, length);
 | 
			
		||||
 | 
			
		||||
	if (path_len > 0) {
 | 
			
		||||
		buf[len++] = ',';
 | 
			
		||||
		buf[len++] = ',';
 | 
			
		||||
		buf[len++] = '\"';
 | 
			
		||||
 | 
			
		||||
		for (; path_len; path_len--)
 | 
			
		||||
			len += sprintf(buf + len, "%02hhX", *path++);
 | 
			
		||||
 | 
			
		||||
		buf[len++] = '\"';
 | 
			
		||||
		buf[len] = '\0';
 | 
			
		||||
	}
 | 
			
		||||
	append_file_path(buf + len, path, path_len);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(sd->chat, buf, crsm_prefix,
 | 
			
		||||
				at_crsm_read_cb, cbd, g_free) > 0)
 | 
			
		||||
@@ -287,10 +297,13 @@ static void at_sim_read_record(struct ofono_sim *sim, int fileid,
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[128];
 | 
			
		||||
	unsigned int len;
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof(buf), "AT+CRSM=178,%i,%i,4,%i", fileid,
 | 
			
		||||
	len = snprintf(buf, sizeof(buf), "AT+CRSM=178,%i,%i,4,%i", fileid,
 | 
			
		||||
			record, length);
 | 
			
		||||
 | 
			
		||||
	append_file_path(buf + len, path, path_len);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(sd->chat, buf, crsm_prefix,
 | 
			
		||||
				at_crsm_read_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
@@ -836,7 +849,7 @@ static void at_cpinr_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
	cb(&error, retries, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_spic_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
static void simcom_spic_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_sim_pin_retries_cb_t cb = cbd->cb;
 | 
			
		||||
@@ -1053,6 +1066,46 @@ error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cinterion_spic_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
							gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	struct ofono_sim *sim = cbd->user;
 | 
			
		||||
	ofono_sim_pin_retries_cb_t cb = cbd->cb;
 | 
			
		||||
	const char *final = g_at_result_final_response(result);
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
	int retries[OFONO_SIM_PASSWORD_INVALID];
 | 
			
		||||
	size_t i;
 | 
			
		||||
	int pin_type = ofono_sim_get_password_type(sim);
 | 
			
		||||
 | 
			
		||||
	decode_at_error(&error, final);
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		cb(&error, NULL, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
 | 
			
		||||
		retries[i] = -1;
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "^SPIC:"))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_number(&iter, &retries[pin_type]))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	DBG("Retry : %d, type : %d", retries[pin_type], pin_type);
 | 
			
		||||
	cb(&error, retries, cbd->data);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_pin_retries_query(struct ofono_sim *sim,
 | 
			
		||||
					ofono_sim_pin_retries_cb_t cb,
 | 
			
		||||
					void *data)
 | 
			
		||||
@@ -1100,8 +1153,8 @@ static void at_pin_retries_query(struct ofono_sim *sim,
 | 
			
		||||
			return;
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_VENDOR_SIMCOM:
 | 
			
		||||
		if (g_at_chat_send(sd->chat, "AT+SPIC", spic_prefix,
 | 
			
		||||
					at_spic_cb, cbd, g_free) > 0)
 | 
			
		||||
		if (g_at_chat_send(sd->chat, "AT+SPIC", simcom_spic_prefix,
 | 
			
		||||
					simcom_spic_cb, cbd, g_free) > 0)
 | 
			
		||||
			return;
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_VENDOR_TELIT:
 | 
			
		||||
@@ -1125,6 +1178,11 @@ static void at_pin_retries_query(struct ofono_sim *sim,
 | 
			
		||||
					upincnt_cb, cbd, g_free) > 0)
 | 
			
		||||
			return;
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_VENDOR_CINTERION:
 | 
			
		||||
		if (g_at_chat_send(sd->chat, "AT^SPIC", cinterion_spic_prefix,
 | 
			
		||||
					cinterion_spic_cb, cbd, g_free) > 0)
 | 
			
		||||
			return;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		if (g_at_chat_send(sd->chat, "AT+CPINR", cpinr_prefixes,
 | 
			
		||||
					at_cpinr_cb, cbd, g_free) > 0)
 | 
			
		||||
@@ -1215,99 +1273,24 @@ static void at_pin_query(struct ofono_sim *sim, ofono_sim_passwd_cb_t cb,
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_xsim_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	struct sim_data *sd = cbd->user;
 | 
			
		||||
	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error = { .type = OFONO_ERROR_TYPE_NO_ERROR };
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	int state;
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "+XSIM:"))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_number(&iter, &state))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	switch (state) {
 | 
			
		||||
	case 3:	/* PIN verified – Ready */
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cb(&error, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_at_chat_unregister(sd->chat, sd->ready_id);
 | 
			
		||||
	sd->ready_id = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_epev_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	struct sim_data *sd = cbd->user;
 | 
			
		||||
	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error = { .type = OFONO_ERROR_TYPE_NO_ERROR };
 | 
			
		||||
 | 
			
		||||
	cb(&error, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_at_chat_unregister(sd->chat, sd->ready_id);
 | 
			
		||||
	sd->ready_id = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_qss_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	struct sim_data *sd = cbd->user;
 | 
			
		||||
	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error = { .type = OFONO_ERROR_TYPE_NO_ERROR };
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	int state;
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "#QSS:"))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_number(&iter, &state))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	switch (state) {
 | 
			
		||||
	case 3:	/* SIM inserted and READY. */
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cb(&error, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_at_chat_unregister(sd->chat, sd->ready_id);
 | 
			
		||||
	sd->ready_id = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sim_state_cb(gboolean present, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	struct sim_data *sd = cbd->user;
 | 
			
		||||
	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_sim *sim = user_data;
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
 | 
			
		||||
	at_util_sim_state_query_free(sd->sim_state_query);
 | 
			
		||||
	sd->sim_state_query = NULL;
 | 
			
		||||
 | 
			
		||||
	if (present == 1)
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
	else
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
		ofono_sim_initialized_notify(sim);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_pin_send_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
				gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	struct sim_data *sd = cbd->user;
 | 
			
		||||
	struct ofono_sim *sim = cbd->user;
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
 | 
			
		||||
@@ -1317,36 +1300,6 @@ static void at_pin_send_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	switch (sd->vendor) {
 | 
			
		||||
	case OFONO_VENDOR_IFX:
 | 
			
		||||
		/*
 | 
			
		||||
		 * On the IFX modem, AT+CPIN? can return READY too
 | 
			
		||||
		 * early and so use +XSIM notification to detect
 | 
			
		||||
		 * the ready state of the SIM.
 | 
			
		||||
		 */
 | 
			
		||||
		sd->ready_id = g_at_chat_register(sd->chat, "+XSIM",
 | 
			
		||||
							at_xsim_notify,
 | 
			
		||||
							FALSE, cbd, g_free);
 | 
			
		||||
		return;
 | 
			
		||||
	case OFONO_VENDOR_MBM:
 | 
			
		||||
		/*
 | 
			
		||||
		 * On the MBM modem, AT+CPIN? keeps returning SIM PIN
 | 
			
		||||
		 * for a moment after successful AT+CPIN="..", but then
 | 
			
		||||
		 * sends *EPEV when that changes.
 | 
			
		||||
		 */
 | 
			
		||||
		sd->ready_id = g_at_chat_register(sd->chat, "*EPEV",
 | 
			
		||||
							at_epev_notify,
 | 
			
		||||
							FALSE, cbd, g_free);
 | 
			
		||||
		return;
 | 
			
		||||
	case OFONO_VENDOR_TELIT:
 | 
			
		||||
		/*
 | 
			
		||||
		 * On the Telit modem, AT+CPIN? can return READY too
 | 
			
		||||
		 * early and so use #QSS notification to detect
 | 
			
		||||
		 * the ready state of the SIM.
 | 
			
		||||
		 */
 | 
			
		||||
		sd->ready_id = g_at_chat_register(sd->chat, "#QSS",
 | 
			
		||||
							at_qss_notify,
 | 
			
		||||
							FALSE, cbd, g_free);
 | 
			
		||||
		return;
 | 
			
		||||
	case OFONO_VENDOR_ZTE:
 | 
			
		||||
	case OFONO_VENDOR_ALCATEL:
 | 
			
		||||
	case OFONO_VENDOR_HUAWEI:
 | 
			
		||||
@@ -1364,15 +1317,12 @@ static void at_pin_send_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
		 * state.
 | 
			
		||||
		 */
 | 
			
		||||
		sd->sim_state_query = at_util_sim_state_query_new(sd->chat,
 | 
			
		||||
						2, 20, sim_state_cb, cbd,
 | 
			
		||||
						g_free);
 | 
			
		||||
		return;
 | 
			
		||||
						2, 20, sim_state_cb, sim,
 | 
			
		||||
						NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	cb(&error, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_pin_send(struct ofono_sim *sim, const char *passwd,
 | 
			
		||||
@@ -1383,12 +1333,12 @@ static void at_pin_send(struct ofono_sim *sim, const char *passwd,
 | 
			
		||||
	char buf[64];
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	cbd->user = sd;
 | 
			
		||||
	cbd->user = sim;
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof(buf), "AT+CPIN=\"%s\"", passwd);
 | 
			
		||||
 | 
			
		||||
	ret = g_at_chat_send(sd->chat, buf, none_prefix,
 | 
			
		||||
				at_pin_send_cb, cbd, NULL);
 | 
			
		||||
				at_pin_send_cb, cbd, g_free);
 | 
			
		||||
 | 
			
		||||
	memset(buf, 0, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
@@ -1458,9 +1408,8 @@ static void at_pin_enable(struct ofono_sim *sim,
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[64];
 | 
			
		||||
	int ret;
 | 
			
		||||
	unsigned int len = sizeof(at_clck_cpwd_fac) / sizeof(*at_clck_cpwd_fac);
 | 
			
		||||
 | 
			
		||||
	if (passwd_type >= len || at_clck_cpwd_fac[passwd_type] == NULL)
 | 
			
		||||
	if (!(sd->passwd_type_mask & (1 << passwd_type)))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",%i,\"%s\"",
 | 
			
		||||
@@ -1489,10 +1438,8 @@ static void at_change_passwd(struct ofono_sim *sim,
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[64];
 | 
			
		||||
	int ret;
 | 
			
		||||
	unsigned int len = sizeof(at_clck_cpwd_fac) / sizeof(*at_clck_cpwd_fac);
 | 
			
		||||
 | 
			
		||||
	if (passwd_type >= len ||
 | 
			
		||||
			at_clck_cpwd_fac[passwd_type] == NULL)
 | 
			
		||||
	if (!(sd->passwd_type_mask & (1 << passwd_type)))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof(buf), "AT+CPWD=\"%s\",\"%s\",\"%s\"",
 | 
			
		||||
@@ -1549,9 +1496,8 @@ static void at_query_clck(struct ofono_sim *sim,
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[64];
 | 
			
		||||
	unsigned int len = sizeof(at_clck_cpwd_fac) / sizeof(*at_clck_cpwd_fac);
 | 
			
		||||
 | 
			
		||||
	if (passwd_type >= len || at_clck_cpwd_fac[passwd_type] == NULL)
 | 
			
		||||
	if (!(sd->passwd_type_mask & (1 << passwd_type)))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",2",
 | 
			
		||||
@@ -1567,13 +1513,363 @@ error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean at_sim_register(gpointer user)
 | 
			
		||||
static void at_clck_query_cb(gboolean ok, GAtResult *result, gpointer user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sim *sim = user;
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	const char *fac;
 | 
			
		||||
 | 
			
		||||
	if (!ok)
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	/* e.g. +CLCK: ("SC","FD","PN","PU","PP","PC","PF") */
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "+CLCK:") ||
 | 
			
		||||
				!g_at_result_iter_open_list(&iter))
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	/* Clear the default mask */
 | 
			
		||||
	sd->passwd_type_mask = 0;
 | 
			
		||||
 | 
			
		||||
	/* Set the bits for <fac>s that are actually supported */
 | 
			
		||||
	while (g_at_result_iter_next_string(&iter, &fac)) {
 | 
			
		||||
		unsigned int i;
 | 
			
		||||
 | 
			
		||||
		/* Find it in the list of known <fac>s */
 | 
			
		||||
		for (i = 0; i < ARRAY_SIZE(at_clck_cpwd_fac); i++) {
 | 
			
		||||
			if (!g_strcmp0(at_clck_cpwd_fac[i], fac)) {
 | 
			
		||||
				sd->passwd_type_mask |= (1 << i);
 | 
			
		||||
				DBG("found %s", fac);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	ofono_sim_register(sim);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	return FALSE;
 | 
			
		||||
static void at_discover_apps_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
				gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	ofono_sim_list_apps_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
	const unsigned char *buffer;
 | 
			
		||||
	int len;
 | 
			
		||||
 | 
			
		||||
	decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		cb(&error, NULL, 0, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "+CUAD:"))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_hexstring(&iter, &buffer, &len))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	cb(&error, buffer, len, cbd->data);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_discover_apps(struct ofono_sim *sim,
 | 
			
		||||
				ofono_sim_list_apps_cb_t cb,
 | 
			
		||||
				void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(sd->chat, "AT+CUAD", cuad_prefix,
 | 
			
		||||
			at_discover_apps_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_open_channel_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
		gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	ofono_sim_open_channel_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
	int session_id = -1;
 | 
			
		||||
 | 
			
		||||
	decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
 | 
			
		||||
	if (!ok)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "+CCHO:"))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_number(&iter, &session_id))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	cb(&error, session_id, cbd->data);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	cb(&error, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_open_channel(struct ofono_sim *sim, const unsigned char *aid,
 | 
			
		||||
		ofono_sim_open_channel_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char cmd[43];
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	strcpy(cmd, "AT+CCHO=\"");
 | 
			
		||||
	ret += 9;
 | 
			
		||||
 | 
			
		||||
	encode_hex_own_buf(aid, 16, 0, cmd + ret);
 | 
			
		||||
	ret += 32;
 | 
			
		||||
 | 
			
		||||
	strcpy(cmd + ret, "\"");
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(sd->chat, cmd, ccho_prefix, at_open_channel_cb,
 | 
			
		||||
			cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_close_channel_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
		gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_sim_close_channel_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
 | 
			
		||||
	decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
 | 
			
		||||
	if (cb)
 | 
			
		||||
		cb(&error, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_close_channel(struct ofono_sim *sim, int session_id,
 | 
			
		||||
		ofono_sim_close_channel_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char cmd[15];
 | 
			
		||||
 | 
			
		||||
	sprintf(cmd, "AT+CCHC=%d", session_id);
 | 
			
		||||
 | 
			
		||||
	g_at_chat_send(sd->chat, cmd, NULL, at_close_channel_cb, cbd, g_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_crla_read_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
				gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	ofono_sim_read_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
	const guint8 *response;
 | 
			
		||||
	gint sw1, sw2, len;
 | 
			
		||||
 | 
			
		||||
	decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		cb(&error, NULL, 0, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "+CRLA:")) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_next_number(&iter, &sw1);
 | 
			
		||||
	g_at_result_iter_next_number(&iter, &sw2);
 | 
			
		||||
 | 
			
		||||
	if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) ||
 | 
			
		||||
			(sw1 == 0x90 && sw2 != 0x00)) {
 | 
			
		||||
		memset(&error, 0, sizeof(error));
 | 
			
		||||
 | 
			
		||||
		error.type = OFONO_ERROR_TYPE_SIM;
 | 
			
		||||
		error.error = (sw1 << 8) | sw2;
 | 
			
		||||
 | 
			
		||||
		cb(&error, NULL, 0, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_hexstring(&iter, &response, &len)) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DBG("crla_read_cb: %02x, %02x, %d", sw1, sw2, len);
 | 
			
		||||
 | 
			
		||||
	cb(&error, response, len, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_session_read_binary(struct ofono_sim *sim, int session,
 | 
			
		||||
				int fileid, int start, int length,
 | 
			
		||||
				const unsigned char *path,
 | 
			
		||||
				unsigned int path_len,
 | 
			
		||||
				ofono_sim_read_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[64];
 | 
			
		||||
	unsigned int len;
 | 
			
		||||
 | 
			
		||||
	len = snprintf(buf, sizeof(buf), "AT+CRLA=%i,176,%i,%i,%i,%i,,",
 | 
			
		||||
			session, fileid, start >> 8, start & 0xff, length);
 | 
			
		||||
 | 
			
		||||
	append_file_path(buf + len, path, path_len);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(sd->chat, buf, crla_prefix,
 | 
			
		||||
			at_crla_read_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_session_read_record(struct ofono_sim *sim, int session_id,
 | 
			
		||||
				int fileid, int record, int length,
 | 
			
		||||
				const unsigned char *path,
 | 
			
		||||
				unsigned int path_len,
 | 
			
		||||
				ofono_sim_read_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[128];
 | 
			
		||||
	unsigned int len;
 | 
			
		||||
 | 
			
		||||
	len = snprintf(buf, sizeof(buf), "AT+CRLA=%i,178,%i,%i,4,%i",
 | 
			
		||||
			session_id, fileid, record, length);
 | 
			
		||||
 | 
			
		||||
	append_file_path(buf + len, path, path_len);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(sd->chat, buf, crla_prefix,
 | 
			
		||||
			at_crla_read_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_crla_info_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	get_response_common_cb(ok, result, user_data, "+CRLA:");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_session_read_info(struct ofono_sim *sim, int session_id,
 | 
			
		||||
				int fileid, const unsigned char *path,
 | 
			
		||||
				unsigned int path_len,
 | 
			
		||||
				ofono_sim_file_info_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[128];
 | 
			
		||||
	unsigned int len;
 | 
			
		||||
 | 
			
		||||
	len = snprintf(buf, sizeof(buf), "AT+CRLA=%i,192,%i", session_id,
 | 
			
		||||
			fileid);
 | 
			
		||||
 | 
			
		||||
	append_file_path(buf + len, path, path_len);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(sd->chat, buf, crla_prefix,
 | 
			
		||||
			at_crla_info_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL,
 | 
			
		||||
				EF_STATUS_INVALIDATED, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void logical_access_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
		gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_sim_logical_access_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
	const char *str_data;
 | 
			
		||||
	unsigned char *raw;
 | 
			
		||||
	gint len = 0;
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
 | 
			
		||||
	decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
 | 
			
		||||
	if (!ok)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "+CGLA:"))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_number(&iter, &len))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_string(&iter, &str_data))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	raw = alloca(len / 2);
 | 
			
		||||
 | 
			
		||||
	decode_hex_own_buf(str_data, len, NULL, 0, raw);
 | 
			
		||||
 | 
			
		||||
	cb(&error, raw, len / 2, cbd->data);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	cb(&error, NULL, 0, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_logical_access(struct ofono_sim *sim, int session_id,
 | 
			
		||||
		const unsigned char *pdu, unsigned int len,
 | 
			
		||||
		ofono_sim_logical_access_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
	char cmd[(len * 2) + 19];
 | 
			
		||||
 | 
			
		||||
	ret = sprintf(cmd, "AT+CGLA=%d,%d,\"", session_id, len * 2);
 | 
			
		||||
 | 
			
		||||
	encode_hex_own_buf(pdu, len, 0, cmd + ret);
 | 
			
		||||
	ret += len * 2;
 | 
			
		||||
 | 
			
		||||
	strcpy(cmd + ret, "\"");
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(sd->chat, cmd, cgla_prefix, logical_access_cb,
 | 
			
		||||
			cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int at_sim_probe(struct ofono_sim *sim, unsigned int vendor,
 | 
			
		||||
@@ -1581,18 +1877,22 @@ static int at_sim_probe(struct ofono_sim *sim, unsigned int vendor,
 | 
			
		||||
{
 | 
			
		||||
	GAtChat *chat = data;
 | 
			
		||||
	struct sim_data *sd;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	sd = g_new0(struct sim_data, 1);
 | 
			
		||||
	sd->chat = g_at_chat_clone(chat);
 | 
			
		||||
	sd->vendor = vendor;
 | 
			
		||||
 | 
			
		||||
	if (sd->vendor == OFONO_VENDOR_MBM)
 | 
			
		||||
		g_at_chat_send(sd->chat, "AT*EPEE=1", NULL, NULL, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
	ofono_sim_set_data(sim, sd);
 | 
			
		||||
	g_idle_add(at_sim_register, sim);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
	/* <fac>s supported by default */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(at_clck_cpwd_fac); i++)
 | 
			
		||||
		if (at_clck_cpwd_fac[i])
 | 
			
		||||
			sd->passwd_type_mask |= (1 << i);
 | 
			
		||||
 | 
			
		||||
	/* Query supported <fac>s */
 | 
			
		||||
	return g_at_chat_send(sd->chat, "AT+CLCK=?", clck_prefix,
 | 
			
		||||
				at_clck_query_cb, sim, NULL) ? 0 : -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_sim_remove(struct ofono_sim *sim)
 | 
			
		||||
@@ -1628,6 +1928,13 @@ static struct ofono_sim_driver driver = {
 | 
			
		||||
	.lock			= at_pin_enable,
 | 
			
		||||
	.change_passwd		= at_change_passwd,
 | 
			
		||||
	.query_facility_lock	= at_query_clck,
 | 
			
		||||
	.list_apps		= at_discover_apps,
 | 
			
		||||
	.open_channel		= at_open_channel,
 | 
			
		||||
	.close_channel		= at_close_channel,
 | 
			
		||||
	.session_read_binary	= at_session_read_binary,
 | 
			
		||||
	.session_read_record	= at_session_read_record,
 | 
			
		||||
	.session_read_info	= at_session_read_info,
 | 
			
		||||
	.logical_access		= at_logical_access
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct ofono_sim_driver driver_noef = {
 | 
			
		||||
 
 | 
			
		||||
@@ -319,26 +319,6 @@ static void at_cnma_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
				"Further SMS reception is not guaranteed");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean at_parse_cmt(GAtResult *result,	const char **pdu, int *pdulen)
 | 
			
		||||
{
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "+CMT:"))
 | 
			
		||||
		return FALSE;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_skip_next(&iter))
 | 
			
		||||
		return FALSE;
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next_number(&iter, pdulen))
 | 
			
		||||
		return FALSE;
 | 
			
		||||
 | 
			
		||||
	*pdu = g_at_result_pdu(result);
 | 
			
		||||
 | 
			
		||||
	return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void at_ack_delivery(struct ofono_sms *sms)
 | 
			
		||||
{
 | 
			
		||||
	struct sms_data *data = ofono_sms_get_data(sms);
 | 
			
		||||
@@ -347,11 +327,21 @@ static inline void at_ack_delivery(struct ofono_sms *sms)
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	/* We must acknowledge the PDU using CNMA */
 | 
			
		||||
	if (data->cnma_ack_pdu)
 | 
			
		||||
		snprintf(buf, sizeof(buf), "AT+CNMA=1,%d\r%s",
 | 
			
		||||
				data->cnma_ack_pdu_len, data->cnma_ack_pdu);
 | 
			
		||||
	else /* Should be a safe fallback */
 | 
			
		||||
	if (data->cnma_ack_pdu) {
 | 
			
		||||
		switch (data->vendor) {
 | 
			
		||||
		case OFONO_VENDOR_CINTERION:
 | 
			
		||||
			snprintf(buf, sizeof(buf), "AT+CNMA=1");
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			snprintf(buf, sizeof(buf), "AT+CNMA=1,%d\r%s",
 | 
			
		||||
					data->cnma_ack_pdu_len,
 | 
			
		||||
					data->cnma_ack_pdu);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Should be a safe fallback */
 | 
			
		||||
		snprintf(buf, sizeof(buf), "AT+CNMA=0");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_at_chat_send(data->chat, buf, none_prefix, at_cnma_cb, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
@@ -409,16 +399,34 @@ static void at_cmt_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sms *sms = user_data;
 | 
			
		||||
	struct sms_data *data = ofono_sms_get_data(sms);
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	const char *hexpdu;
 | 
			
		||||
	unsigned char pdu[176];
 | 
			
		||||
	long pdu_len;
 | 
			
		||||
	int tpdu_len;
 | 
			
		||||
	unsigned char pdu[176];
 | 
			
		||||
 | 
			
		||||
	if (!at_parse_cmt(result, &hexpdu, &tpdu_len)) {
 | 
			
		||||
		ofono_error("Unable to parse CMT notification");
 | 
			
		||||
		return;
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (!g_at_result_iter_next(&iter, "+CMT:"))
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	switch (data->vendor) {
 | 
			
		||||
	case OFONO_VENDOR_CINTERION:
 | 
			
		||||
		if (!g_at_result_iter_next_number(&iter, &tpdu_len))
 | 
			
		||||
			goto err;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		if (!g_at_result_iter_skip_next(&iter))
 | 
			
		||||
			goto err;
 | 
			
		||||
 | 
			
		||||
		if (!g_at_result_iter_next_number(&iter, &tpdu_len))
 | 
			
		||||
			goto err;
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hexpdu = g_at_result_pdu(result);
 | 
			
		||||
 | 
			
		||||
	if (strlen(hexpdu) > sizeof(pdu) * 2) {
 | 
			
		||||
		ofono_error("Bad PDU length in CMT notification");
 | 
			
		||||
		return;
 | 
			
		||||
@@ -431,6 +439,9 @@ static void at_cmt_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
 | 
			
		||||
	if (data->vendor != OFONO_VENDOR_SIMCOM)
 | 
			
		||||
		at_ack_delivery(sms);
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	ofono_error("Unable to parse CMT notification");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void at_cmgr_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
@@ -742,7 +753,7 @@ static void at_sms_initialized(struct ofono_sms *sms)
 | 
			
		||||
 | 
			
		||||
static void at_sms_not_supported(struct ofono_sms *sms)
 | 
			
		||||
{
 | 
			
		||||
	ofono_error("SMS not supported by this modem.  If this is in error"
 | 
			
		||||
	ofono_error("SMS not supported by this modem.  If this is an error"
 | 
			
		||||
			" please submit patches to support this hardware");
 | 
			
		||||
 | 
			
		||||
	ofono_sms_remove(sms);
 | 
			
		||||
 
 | 
			
		||||
@@ -47,4 +47,5 @@ enum ofono_vendor {
 | 
			
		||||
	OFONO_VENDOR_UBLOX,
 | 
			
		||||
	OFONO_VENDOR_UBLOX_TOBY_L2,
 | 
			
		||||
	OFONO_VENDOR_CINTERION,
 | 
			
		||||
	OFONO_VENDOR_XMM,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@
 | 
			
		||||
#include "cdmamodem.h"
 | 
			
		||||
#include "drivers/atmodem/vendor.h"
 | 
			
		||||
 | 
			
		||||
#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun"
 | 
			
		||||
#define TUN_DEV "/dev/net/tun"
 | 
			
		||||
 | 
			
		||||
#define STATIC_IP_NETMASK "255.255.255.255"
 | 
			
		||||
 | 
			
		||||
@@ -285,7 +285,7 @@ static int cdma_connman_probe(struct ofono_cdma_connman *cm,
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (stat(TUN_SYSFS_DIR, &st) < 0) {
 | 
			
		||||
	if (stat(TUN_DEV, &st) < 0) {
 | 
			
		||||
		ofono_error("Missing support for TUN/TAP devices");
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										49
									
								
								ofono/drivers/gemaltomodem/gemaltomodem.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								ofono/drivers/gemaltomodem/gemaltomodem.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017 Vincent Cesson. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
#include <gatchat.h>
 | 
			
		||||
 | 
			
		||||
#define OFONO_API_SUBJECT_TO_CHANGE
 | 
			
		||||
#include <ofono/plugin.h>
 | 
			
		||||
#include <ofono/types.h>
 | 
			
		||||
 | 
			
		||||
#include "gemaltomodem.h"
 | 
			
		||||
 | 
			
		||||
static int gemaltomodem_init(void)
 | 
			
		||||
{
 | 
			
		||||
	gemalto_location_reporting_init();
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gemaltomodem_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	gemalto_location_reporting_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OFONO_PLUGIN_DEFINE(gemaltomodem, "Gemalto modem driver", VERSION,
 | 
			
		||||
			OFONO_PLUGIN_PRIORITY_DEFAULT,
 | 
			
		||||
			gemaltomodem_init, gemaltomodem_exit)
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
 | 
			
		||||
 *  Copyright (C) 2017 Vincent Cesson. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -19,11 +19,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <drivers/atmodem/atutil.h>
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
extern void gemalto_location_reporting_init();
 | 
			
		||||
extern void gemalto_location_reporting_exit();
 | 
			
		||||
							
								
								
									
										237
									
								
								ofono/drivers/gemaltomodem/location-reporting.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								ofono/drivers/gemaltomodem/location-reporting.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,237 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017 Vincent Cesson. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/location-reporting.h>
 | 
			
		||||
 | 
			
		||||
#include "gatchat.h"
 | 
			
		||||
#include "gatresult.h"
 | 
			
		||||
#include "gattty.h"
 | 
			
		||||
 | 
			
		||||
#include "gemaltomodem.h"
 | 
			
		||||
 | 
			
		||||
static const char *sgpsc_prefix[] = { "^SGPSC:", NULL };
 | 
			
		||||
 | 
			
		||||
struct gps_data {
 | 
			
		||||
	GAtChat *chat;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void gemalto_gps_disable_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
							gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	struct ofono_location_reporting *lr = cbd->user;
 | 
			
		||||
	ofono_location_reporting_disable_cb_t cb = cbd->cb;
 | 
			
		||||
 | 
			
		||||
	DBG("lr=%p, ok=%d", lr, ok);
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		struct ofono_error error;
 | 
			
		||||
 | 
			
		||||
		decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
		cb(&error, cbd->data);
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gemalto_location_reporting_disable(
 | 
			
		||||
				struct ofono_location_reporting *lr,
 | 
			
		||||
				ofono_location_reporting_disable_cb_t cb,
 | 
			
		||||
				void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gps_data *gd = ofono_location_reporting_get_data(lr);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
 | 
			
		||||
	DBG("lr=%p", lr);
 | 
			
		||||
 | 
			
		||||
	cbd->user = lr;
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(gd->chat, "AT^SGPSC=\"Engine\",0", sgpsc_prefix,
 | 
			
		||||
				gemalto_gps_disable_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int enable_data_stream(struct ofono_location_reporting *lr)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_modem *modem;
 | 
			
		||||
	const char *gps_dev;
 | 
			
		||||
	GHashTable *options;
 | 
			
		||||
	GIOChannel *channel;
 | 
			
		||||
	int fd;
 | 
			
		||||
 | 
			
		||||
	modem = ofono_location_reporting_get_modem(lr);
 | 
			
		||||
	gps_dev = ofono_modem_get_string(modem, "GPS");
 | 
			
		||||
 | 
			
		||||
	options = g_hash_table_new(g_str_hash, g_str_equal);
 | 
			
		||||
	if (options == NULL)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	g_hash_table_insert(options, "Baud", "115200");
 | 
			
		||||
 | 
			
		||||
	channel = g_at_tty_open(gps_dev, options);
 | 
			
		||||
 | 
			
		||||
	g_hash_table_destroy(options);
 | 
			
		||||
 | 
			
		||||
	if (channel == NULL)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	fd = g_io_channel_unix_get_fd(channel);
 | 
			
		||||
 | 
			
		||||
	g_io_channel_set_close_on_unref(channel, FALSE);
 | 
			
		||||
	g_io_channel_unref(channel);
 | 
			
		||||
 | 
			
		||||
	return fd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gemalto_sgpsc_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
					gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_location_reporting_enable_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_location_reporting *lr = cbd->user;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
	int fd;
 | 
			
		||||
 | 
			
		||||
	DBG("lr=%p ok=%d", lr, ok);
 | 
			
		||||
 | 
			
		||||
	decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		cb(&error, -1, cbd->data);
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fd = enable_data_stream(lr);
 | 
			
		||||
 | 
			
		||||
	if (fd < 0) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cb(&error, fd, cbd->data);
 | 
			
		||||
	close(fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gemalto_location_reporting_enable(struct ofono_location_reporting *lr,
 | 
			
		||||
					ofono_location_reporting_enable_cb_t cb,
 | 
			
		||||
					void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gps_data *gd = ofono_location_reporting_get_data(lr);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
 | 
			
		||||
	DBG("lr=%p", lr);
 | 
			
		||||
 | 
			
		||||
	cbd->user = lr;
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(gd->chat, "AT^SGPSC=\"Engine\",2", sgpsc_prefix,
 | 
			
		||||
				gemalto_sgpsc_cb, cbd, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gemalto_location_reporting_support_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
							gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_location_reporting *lr = user_data;
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		ofono_location_reporting_remove(lr);
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofono_location_reporting_register(lr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int gemalto_location_reporting_probe(struct ofono_location_reporting *lr,
 | 
			
		||||
						unsigned int vendor, void *data)
 | 
			
		||||
{
 | 
			
		||||
	GAtChat *chat = data;
 | 
			
		||||
	struct gps_data *gd;
 | 
			
		||||
 | 
			
		||||
	gd = g_try_new0(struct gps_data, 1);
 | 
			
		||||
	if (gd == NULL)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	gd->chat = g_at_chat_clone(chat);
 | 
			
		||||
 | 
			
		||||
	ofono_location_reporting_set_data(lr, gd);
 | 
			
		||||
 | 
			
		||||
	g_at_chat_send(gd->chat, "AT^SGPSC=?", sgpsc_prefix,
 | 
			
		||||
					gemalto_location_reporting_support_cb,
 | 
			
		||||
					lr, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void gemalto_location_reporting_remove(struct ofono_location_reporting *lr)
 | 
			
		||||
{
 | 
			
		||||
	struct gps_data *gd = ofono_location_reporting_get_data(lr);
 | 
			
		||||
 | 
			
		||||
	ofono_location_reporting_set_data(lr, NULL);
 | 
			
		||||
 | 
			
		||||
	g_at_chat_unref(gd->chat);
 | 
			
		||||
	g_free(gd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_location_reporting_driver driver = {
 | 
			
		||||
	.name			= "gemaltomodem",
 | 
			
		||||
	.type			= OFONO_LOCATION_REPORTING_TYPE_NMEA,
 | 
			
		||||
	.probe			= gemalto_location_reporting_probe,
 | 
			
		||||
	.remove			= gemalto_location_reporting_remove,
 | 
			
		||||
	.enable			= gemalto_location_reporting_enable,
 | 
			
		||||
	.disable		= gemalto_location_reporting_disable,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void gemalto_location_reporting_init()
 | 
			
		||||
{
 | 
			
		||||
	ofono_location_reporting_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gemalto_location_reporting_exit()
 | 
			
		||||
{
 | 
			
		||||
	ofono_location_reporting_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
@@ -125,6 +125,7 @@ static struct ofono_call *create_call(struct ofono_voicecall *vc, int type,
 | 
			
		||||
	if (clip != 2) {
 | 
			
		||||
		strncpy(call->phone_number.number, num,
 | 
			
		||||
			OFONO_MAX_PHONE_NUMBER_LENGTH);
 | 
			
		||||
		call->phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
 | 
			
		||||
		call->phone_number.type = num_type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -404,6 +405,45 @@ static void hfp_dial(struct ofono_voicecall *vc,
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hfp_dial_last(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb,
 | 
			
		||||
			void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct voicecall_data *vd = ofono_voicecall_get_data(vc);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
 | 
			
		||||
	cbd->user = vc;
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(vd->chat, "AT+BLDN", none_prefix,
 | 
			
		||||
				atd_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hfp_dial_memory(struct ofono_voicecall *vc,
 | 
			
		||||
				unsigned int memory_location,
 | 
			
		||||
				ofono_voicecall_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct voicecall_data *vd = ofono_voicecall_get_data(vc);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[256];
 | 
			
		||||
 | 
			
		||||
	cbd->user = vc;
 | 
			
		||||
	DBG("Calling memory location %d\n", memory_location);
 | 
			
		||||
	snprintf(buf, sizeof(buf), "ATD>%d;", memory_location);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(vd->chat, buf, none_prefix,
 | 
			
		||||
				atd_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
	DBG("at_chat_failed");
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hfp_template(const char *cmd, struct ofono_voicecall *vc,
 | 
			
		||||
			GAtResultFunc result_cb, unsigned int affected_types,
 | 
			
		||||
			ofono_voicecall_cb_t cb, void *data)
 | 
			
		||||
@@ -1268,6 +1308,8 @@ static struct ofono_voicecall_driver driver = {
 | 
			
		||||
	.probe			= hfp_voicecall_probe,
 | 
			
		||||
	.remove			= hfp_voicecall_remove,
 | 
			
		||||
	.dial			= hfp_dial,
 | 
			
		||||
	.dial_last		= hfp_dial_last,
 | 
			
		||||
	.dial_memory		= hfp_dial_memory,
 | 
			
		||||
	.answer			= hfp_answer,
 | 
			
		||||
	.hangup_active		= hfp_hangup,
 | 
			
		||||
	.hold_all_active	= hfp_hold_all_active,
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ static gboolean get_next_addr(GAtResultIter *iter, char **addr)
 | 
			
		||||
	if (g_at_result_iter_next_unquoted_string(iter, &str) == FALSE)
 | 
			
		||||
		return FALSE;
 | 
			
		||||
 | 
			
		||||
	val = strtol(str, NULL, 16);
 | 
			
		||||
	val = strtoul(str, NULL, 16);
 | 
			
		||||
 | 
			
		||||
	if (addr)
 | 
			
		||||
		*addr = g_strdup_printf("%u.%u.%u.%u",
 | 
			
		||||
 
 | 
			
		||||
@@ -42,11 +42,13 @@
 | 
			
		||||
 | 
			
		||||
static const char *none_prefix[] = { NULL };
 | 
			
		||||
static const char *syscfg_prefix[] = { "^SYSCFG:", NULL };
 | 
			
		||||
static const char *syscfgex_prefix[] = { "^SYSCFGEX:", NULL };
 | 
			
		||||
 | 
			
		||||
#define HUAWEI_BAND_ANY 0x3FFFFFFF
 | 
			
		||||
 | 
			
		||||
struct radio_settings_data {
 | 
			
		||||
	GAtChat *chat;
 | 
			
		||||
	ofono_bool_t syscfgex_cap;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct huawei_band_gsm_table {
 | 
			
		||||
@@ -176,20 +178,76 @@ error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void syscfgex_query_mode_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
					gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
 | 
			
		||||
	enum ofono_radio_access_mode mode;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
	const char *acqorder;
 | 
			
		||||
 | 
			
		||||
	decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		cb(&error, -1, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	if (g_at_result_iter_next(&iter, "^SYSCFGEX:") == FALSE)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (g_at_result_iter_next_string(&iter, &acqorder) == FALSE)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if ((strcmp(acqorder, "00") == 0) ||
 | 
			
		||||
			(strstr(acqorder, "01") &&
 | 
			
		||||
				strstr(acqorder, "02") &&
 | 
			
		||||
				strstr(acqorder, "03")))
 | 
			
		||||
		mode = OFONO_RADIO_ACCESS_MODE_ANY;
 | 
			
		||||
	else if (strstr(acqorder, "03"))
 | 
			
		||||
		mode = OFONO_RADIO_ACCESS_MODE_LTE;
 | 
			
		||||
	else if (strstr(acqorder, "02"))
 | 
			
		||||
		mode = OFONO_RADIO_ACCESS_MODE_UMTS;
 | 
			
		||||
	else if (strstr(acqorder, "01"))
 | 
			
		||||
		mode = OFONO_RADIO_ACCESS_MODE_GSM;
 | 
			
		||||
	else
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	cb(&error, mode, cbd->data);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void huawei_query_rat_mode(struct ofono_radio_settings *rs,
 | 
			
		||||
			ofono_radio_settings_rat_mode_query_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(rsd->chat, "AT^SYSCFG?", syscfg_prefix,
 | 
			
		||||
				syscfg_query_mode_cb, cbd, g_free) == 0) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, -1, data);
 | 
			
		||||
		g_free(cbd);
 | 
			
		||||
	}
 | 
			
		||||
	if (rsd->syscfgex_cap && g_at_chat_send(rsd->chat, "AT^SYSCFGEX?",
 | 
			
		||||
					syscfgex_prefix,
 | 
			
		||||
					syscfgex_query_mode_cb,
 | 
			
		||||
					cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!rsd->syscfgex_cap && g_at_chat_send(rsd->chat, "AT^SYSCFG?",
 | 
			
		||||
					syscfg_prefix,
 | 
			
		||||
					syscfg_query_mode_cb,
 | 
			
		||||
					cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, data);
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void syscfg_modify_mode_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
static void syscfgxx_modify_mode_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
							gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
@@ -200,12 +258,11 @@ static void syscfg_modify_mode_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
	cb(&error, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void huawei_set_rat_mode(struct ofono_radio_settings *rs,
 | 
			
		||||
static void syscfg_set_rat_mode(struct radio_settings_data *rsd,
 | 
			
		||||
				enum ofono_radio_access_mode mode,
 | 
			
		||||
				ofono_radio_settings_rat_mode_set_cb_t cb,
 | 
			
		||||
				void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[40];
 | 
			
		||||
	unsigned int value = 2, acq_order = 0;
 | 
			
		||||
@@ -231,7 +288,7 @@ static void huawei_set_rat_mode(struct ofono_radio_settings *rs,
 | 
			
		||||
							value, acq_order);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(rsd->chat, buf, none_prefix,
 | 
			
		||||
					syscfg_modify_mode_cb, cbd, g_free) > 0)
 | 
			
		||||
				syscfgxx_modify_mode_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
@@ -239,7 +296,55 @@ error:
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void syscfg_modify_band_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
static void syscfgex_set_rat_mode(struct radio_settings_data *rsd,
 | 
			
		||||
				enum ofono_radio_access_mode mode,
 | 
			
		||||
				ofono_radio_settings_rat_mode_set_cb_t cb,
 | 
			
		||||
				void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[50];
 | 
			
		||||
	char *atcmd = "AT^SYSCFGEX=\"%s\",40000000,2,4,40000000,,";
 | 
			
		||||
	char *acqorder = "030201";
 | 
			
		||||
 | 
			
		||||
	switch (mode) {
 | 
			
		||||
	case OFONO_RADIO_ACCESS_MODE_ANY:
 | 
			
		||||
		acqorder = "00";
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_RADIO_ACCESS_MODE_GSM:
 | 
			
		||||
		acqorder = "01";
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_RADIO_ACCESS_MODE_UMTS:
 | 
			
		||||
		acqorder = "02";
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_RADIO_ACCESS_MODE_LTE:
 | 
			
		||||
		acqorder = "03";
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof(buf), atcmd, acqorder);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(rsd->chat, buf, none_prefix,
 | 
			
		||||
			syscfgxx_modify_mode_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void huawei_set_rat_mode(struct ofono_radio_settings *rs,
 | 
			
		||||
				enum ofono_radio_access_mode mode,
 | 
			
		||||
				ofono_radio_settings_rat_mode_set_cb_t cb,
 | 
			
		||||
				void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
 | 
			
		||||
 | 
			
		||||
	if (rsd->syscfgex_cap)
 | 
			
		||||
		syscfgex_set_rat_mode(rsd, mode, cb, data);
 | 
			
		||||
	else
 | 
			
		||||
		syscfg_set_rat_mode(rsd, mode, cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void syscfgxx_modify_band_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
							gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
@@ -250,13 +355,54 @@ static void syscfg_modify_band_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
	cb(&error, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void huawei_set_band(struct ofono_radio_settings *rs,
 | 
			
		||||
static void syscfgex_set_band(struct radio_settings_data *rsd,
 | 
			
		||||
					enum ofono_radio_band_gsm band_gsm,
 | 
			
		||||
					enum ofono_radio_band_umts band_umts,
 | 
			
		||||
					ofono_radio_settings_band_set_cb_t cb,
 | 
			
		||||
					void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[50];
 | 
			
		||||
	char *atcmd = "AT^SYSCFGEX=\"99\",%x,2,4,40000000,,";
 | 
			
		||||
	unsigned int huawei_band;
 | 
			
		||||
 | 
			
		||||
	if (band_gsm == OFONO_RADIO_BAND_GSM_ANY
 | 
			
		||||
			&& band_umts == OFONO_RADIO_BAND_UMTS_ANY) {
 | 
			
		||||
		huawei_band = HUAWEI_BAND_ANY;
 | 
			
		||||
	} else {
 | 
			
		||||
		unsigned int huawei_band_gsm;
 | 
			
		||||
		unsigned int huawei_band_umts;
 | 
			
		||||
 | 
			
		||||
		huawei_band_gsm = band_gsm_to_huawei(band_gsm);
 | 
			
		||||
 | 
			
		||||
		if (!huawei_band_gsm)
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		huawei_band_umts = band_umts_to_huawei(band_umts);
 | 
			
		||||
 | 
			
		||||
		if (!huawei_band_umts)
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		huawei_band = huawei_band_gsm | huawei_band_umts;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof(buf), atcmd, huawei_band);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(rsd->chat, buf, none_prefix,
 | 
			
		||||
			syscfgxx_modify_band_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void syscfg_set_band(struct radio_settings_data *rsd,
 | 
			
		||||
					enum ofono_radio_band_gsm band_gsm,
 | 
			
		||||
					enum ofono_radio_band_umts band_umts,
 | 
			
		||||
					ofono_radio_settings_band_set_cb_t cb,
 | 
			
		||||
					void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	char buf[40];
 | 
			
		||||
	unsigned int huawei_band;
 | 
			
		||||
@@ -284,7 +430,7 @@ static void huawei_set_band(struct ofono_radio_settings *rs,
 | 
			
		||||
	snprintf(buf, sizeof(buf), "AT^SYSCFG=16,3,%x,2,4", huawei_band);
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_send(rsd->chat, buf, none_prefix,
 | 
			
		||||
					syscfg_modify_band_cb, cbd, g_free) > 0)
 | 
			
		||||
				syscfgxx_modify_band_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
@@ -292,6 +438,20 @@ error:
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void huawei_set_band(struct ofono_radio_settings *rs,
 | 
			
		||||
					enum ofono_radio_band_gsm band_gsm,
 | 
			
		||||
					enum ofono_radio_band_umts band_umts,
 | 
			
		||||
					ofono_radio_settings_band_set_cb_t cb,
 | 
			
		||||
					void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
 | 
			
		||||
 | 
			
		||||
	if (rsd->syscfgex_cap)
 | 
			
		||||
		syscfgex_set_band(rsd, band_gsm, band_umts, cb, data);
 | 
			
		||||
	else
 | 
			
		||||
		syscfg_set_band(rsd, band_gsm, band_umts, cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void syscfg_query_band_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
					gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
@@ -364,6 +524,21 @@ static void syscfg_support_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
	ofono_radio_settings_register(rs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void syscfgex_support_cb(gboolean ok, GAtResult *result,
 | 
			
		||||
				gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_radio_settings *rs = user_data;
 | 
			
		||||
	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		g_at_chat_send(rsd->chat, "AT^SYSCFG=?", syscfg_prefix,
 | 
			
		||||
					syscfg_support_cb, rs, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rsd->syscfgex_cap = 1;
 | 
			
		||||
	ofono_radio_settings_register(rs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int huawei_radio_settings_probe(struct ofono_radio_settings *rs,
 | 
			
		||||
					unsigned int vendor, void *data)
 | 
			
		||||
{
 | 
			
		||||
@@ -378,8 +553,8 @@ static int huawei_radio_settings_probe(struct ofono_radio_settings *rs,
 | 
			
		||||
 | 
			
		||||
	ofono_radio_settings_set_data(rs, rsd);
 | 
			
		||||
 | 
			
		||||
	g_at_chat_send(rsd->chat, "AT^SYSCFG=?", syscfg_prefix,
 | 
			
		||||
					syscfg_support_cb, rs, NULL);
 | 
			
		||||
	g_at_chat_send(rsd->chat, "AT^SYSCFGEX=?", syscfgex_prefix,
 | 
			
		||||
					syscfgex_support_cb, rs, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -400,8 +575,8 @@ static struct ofono_radio_settings_driver driver = {
 | 
			
		||||
	.remove			= huawei_radio_settings_remove,
 | 
			
		||||
	.query_rat_mode		= huawei_query_rat_mode,
 | 
			
		||||
	.set_rat_mode		= huawei_set_rat_mode,
 | 
			
		||||
	.query_band             = huawei_query_band,
 | 
			
		||||
	.set_band               = huawei_set_band,
 | 
			
		||||
	.query_band		= huawei_query_band,
 | 
			
		||||
	.set_band		= huawei_set_band,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void huawei_radio_settings_init(void)
 | 
			
		||||
 
 | 
			
		||||
@@ -42,13 +42,14 @@
 | 
			
		||||
 | 
			
		||||
#include "ifxmodem.h"
 | 
			
		||||
 | 
			
		||||
#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun"
 | 
			
		||||
#define TUN_DEV "/dev/net/tun"
 | 
			
		||||
 | 
			
		||||
#define STATIC_IP_NETMASK "255.255.255.255"
 | 
			
		||||
 | 
			
		||||
static const char *none_prefix[] = { NULL };
 | 
			
		||||
static const char *xdns_prefix[] = { "+XDNS:", NULL };
 | 
			
		||||
static const char *cgpaddr_prefix[] = { "+CGPADDR:", NULL };
 | 
			
		||||
static const char *cgcontrdp_prefix[] = { "+CGCONTRDP:", NULL };
 | 
			
		||||
 | 
			
		||||
enum state {
 | 
			
		||||
	STATE_IDLE,
 | 
			
		||||
@@ -59,17 +60,20 @@ enum state {
 | 
			
		||||
 | 
			
		||||
struct gprs_context_data {
 | 
			
		||||
	GAtChat *chat;
 | 
			
		||||
	unsigned int vendor;
 | 
			
		||||
	unsigned int active_context;
 | 
			
		||||
	char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
 | 
			
		||||
	char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
 | 
			
		||||
	GAtRawIP *rawip;
 | 
			
		||||
	enum state state;
 | 
			
		||||
	enum ofono_gprs_proto proto;
 | 
			
		||||
	char address[32];
 | 
			
		||||
	char dns1[32];
 | 
			
		||||
	char dns2[32];
 | 
			
		||||
	char address[64];
 | 
			
		||||
	char gateway[64];
 | 
			
		||||
	char netmask[64];
 | 
			
		||||
	char dns1[64];
 | 
			
		||||
	char dns2[64];
 | 
			
		||||
	ofono_gprs_context_cb_t cb;
 | 
			
		||||
	void *cb_data;                                  /* Callback data */
 | 
			
		||||
	void *cb_data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void rawip_debug(const char *str, void *data)
 | 
			
		||||
@@ -257,11 +261,136 @@ error:
 | 
			
		||||
	failed_setup(gc, NULL, TRUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cgcontrdp_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs_context *gc = user_data;
 | 
			
		||||
	struct ofono_modem *modem = ofono_gprs_context_get_modem(gc);
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	GAtResultIter iter;
 | 
			
		||||
 | 
			
		||||
	const char *laddrnetmask = NULL;
 | 
			
		||||
	const char *gw = NULL;
 | 
			
		||||
	const char *interface;
 | 
			
		||||
	const char *dns[3];
 | 
			
		||||
 | 
			
		||||
	DBG("ok %d", ok);
 | 
			
		||||
 | 
			
		||||
	if (!ok) {
 | 
			
		||||
		struct ofono_error error;
 | 
			
		||||
 | 
			
		||||
		decode_at_error(&error, g_at_result_final_response(result));
 | 
			
		||||
		gcd->cb(&error, gcd->cb_data);
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_at_result_iter_init(&iter, result);
 | 
			
		||||
 | 
			
		||||
	while (g_at_result_iter_next(&iter, "+CGCONTRDP:")) {
 | 
			
		||||
		/* skip cid, bearer_id, apn */
 | 
			
		||||
		g_at_result_iter_skip_next(&iter);
 | 
			
		||||
		g_at_result_iter_skip_next(&iter);
 | 
			
		||||
		g_at_result_iter_skip_next(&iter);
 | 
			
		||||
 | 
			
		||||
		if (!g_at_result_iter_next_string(&iter, &laddrnetmask))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (!g_at_result_iter_next_string(&iter, &gw))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (!g_at_result_iter_next_string(&iter, &dns[0]))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (!g_at_result_iter_next_string(&iter, &dns[1]))
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	strncpy(gcd->dns1, dns[0], sizeof(gcd->dns1));
 | 
			
		||||
	strncpy(gcd->dns2, dns[1], sizeof(gcd->dns2));
 | 
			
		||||
	dns[2] = 0;
 | 
			
		||||
 | 
			
		||||
	DBG("DNS: %s, %s\n", gcd->dns1, gcd->dns2);
 | 
			
		||||
 | 
			
		||||
	if (!laddrnetmask || at_util_get_ipv4_address_and_netmask(laddrnetmask,
 | 
			
		||||
					gcd->address, gcd->netmask) < 0) {
 | 
			
		||||
		failed_setup(gc, NULL, TRUE);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (gw)
 | 
			
		||||
		strncpy(gcd->gateway, gw, sizeof(gcd->gateway));
 | 
			
		||||
 | 
			
		||||
	gcd->state = STATE_ACTIVE;
 | 
			
		||||
 | 
			
		||||
	DBG("address: %s\n", gcd->address);
 | 
			
		||||
	DBG("netmask: %s\n", gcd->netmask);
 | 
			
		||||
	DBG("DNS1: %s\n", gcd->dns1);
 | 
			
		||||
	DBG("DNS2: %s\n", gcd->dns2);
 | 
			
		||||
	DBG("Gateway: %s\n", gcd->gateway);
 | 
			
		||||
 | 
			
		||||
	interface = ofono_modem_get_string(modem, "NetworkInterface");
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_context_set_interface(gc, interface);
 | 
			
		||||
	ofono_gprs_context_set_ipv4_address(gc, gcd->address, TRUE);
 | 
			
		||||
 | 
			
		||||
	if (gcd->netmask[0])
 | 
			
		||||
		ofono_gprs_context_set_ipv4_netmask(gc, gcd->netmask);
 | 
			
		||||
 | 
			
		||||
	if (gcd->gateway[0])
 | 
			
		||||
		ofono_gprs_context_set_ipv4_gateway(gc, gcd->gateway);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ifx_read_settings(struct ofono_gprs_context *gc)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	char buf[64];
 | 
			
		||||
 | 
			
		||||
	gcd->address[0] = '\0';
 | 
			
		||||
	gcd->gateway[0] = '\0';
 | 
			
		||||
	gcd->netmask[0] = '\0';
 | 
			
		||||
	gcd->dns1[0] = '\0';
 | 
			
		||||
	gcd->dns2[0] = '\0';
 | 
			
		||||
 | 
			
		||||
	/* read IP configuration info */
 | 
			
		||||
	if(gcd->vendor == OFONO_VENDOR_XMM) {
 | 
			
		||||
		snprintf(buf, sizeof(buf), "AT+CGCONTRDP=%u",
 | 
			
		||||
							gcd->active_context);
 | 
			
		||||
 | 
			
		||||
		if (g_at_chat_send(gcd->chat, buf, cgcontrdp_prefix,
 | 
			
		||||
					cgcontrdp_cb, gc, NULL) > 0)
 | 
			
		||||
			return;
 | 
			
		||||
	} else {
 | 
			
		||||
		sprintf(buf, "AT+CGPADDR=%u", gcd->active_context);
 | 
			
		||||
 | 
			
		||||
		if (g_at_chat_send(gcd->chat, buf, cgpaddr_prefix,
 | 
			
		||||
						address_cb, gc, NULL) > 0)
 | 
			
		||||
			return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	failed_setup(gc, NULL, TRUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ifx_gprs_read_settings(struct ofono_gprs_context *gc,
 | 
			
		||||
					unsigned int cid,
 | 
			
		||||
					ofono_gprs_context_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	DBG("cid %u", cid);
 | 
			
		||||
 | 
			
		||||
	gcd->active_context = cid;
 | 
			
		||||
	gcd->cb = cb;
 | 
			
		||||
	gcd->cb_data = data;
 | 
			
		||||
 | 
			
		||||
	ifx_read_settings(gc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void activate_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs_context *gc = user_data;
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	char buf[64];
 | 
			
		||||
 | 
			
		||||
	DBG("ok %d", ok);
 | 
			
		||||
 | 
			
		||||
@@ -271,19 +400,14 @@ static void activate_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sprintf(buf, "AT+CGPADDR=%u", gcd->active_context);
 | 
			
		||||
	if (g_at_chat_send(gcd->chat, buf, cgpaddr_prefix,
 | 
			
		||||
					address_cb, gc, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	failed_setup(gc, NULL, TRUE);
 | 
			
		||||
	ifx_read_settings(gc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs_context *gc = user_data;
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	char buf[128];
 | 
			
		||||
	char buf[384];
 | 
			
		||||
 | 
			
		||||
	DBG("ok %d", ok);
 | 
			
		||||
 | 
			
		||||
@@ -387,7 +511,8 @@ static void deactivate_cb(gboolean ok, GAtResult *result, gpointer user_data)
 | 
			
		||||
	gcd->active_context = 0;
 | 
			
		||||
	gcd->state = STATE_IDLE;
 | 
			
		||||
 | 
			
		||||
	g_at_chat_resume(gcd->chat);
 | 
			
		||||
	if (gcd->vendor != OFONO_VENDOR_XMM)
 | 
			
		||||
		g_at_chat_resume(gcd->chat);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
 | 
			
		||||
}
 | 
			
		||||
@@ -409,11 +534,25 @@ static void ifx_gprs_deactivate_primary(struct ofono_gprs_context *gc,
 | 
			
		||||
	g_at_rawip_shutdown(gcd->rawip);
 | 
			
		||||
 | 
			
		||||
	sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
 | 
			
		||||
	if (g_at_chat_send(chat, buf, none_prefix,
 | 
			
		||||
				deactivate_cb, gc, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, data);
 | 
			
		||||
	if (gcd->vendor == OFONO_VENDOR_XMM) {
 | 
			
		||||
		if (g_at_chat_send(gcd->chat, buf, none_prefix,
 | 
			
		||||
				deactivate_cb, gc, NULL) > 0)
 | 
			
		||||
			return;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (g_at_chat_send(chat, buf, none_prefix,
 | 
			
		||||
				deactivate_cb, gc, NULL) > 0)
 | 
			
		||||
			return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ifx_gprs_detach_shutdown(struct ofono_gprs_context *gc,
 | 
			
		||||
						unsigned int cid)
 | 
			
		||||
{
 | 
			
		||||
	DBG("");
 | 
			
		||||
	ifx_gprs_deactivate_primary(gc, cid, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cgev_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
@@ -451,14 +590,13 @@ static void cgev_notify(GAtResult *result, gpointer user_data)
 | 
			
		||||
 | 
			
		||||
		g_at_rawip_unref(gcd->rawip);
 | 
			
		||||
		gcd->rawip = NULL;
 | 
			
		||||
		g_at_chat_resume(gcd->chat);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_context_deactivated(gc, gcd->active_context);
 | 
			
		||||
 | 
			
		||||
	gcd->active_context = 0;
 | 
			
		||||
	gcd->state = STATE_IDLE;
 | 
			
		||||
 | 
			
		||||
	g_at_chat_resume(gcd->chat);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ifx_gprs_context_probe(struct ofono_gprs_context *gc,
 | 
			
		||||
@@ -470,23 +608,27 @@ static int ifx_gprs_context_probe(struct ofono_gprs_context *gc,
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (stat(TUN_SYSFS_DIR, &st) < 0) {
 | 
			
		||||
	if (stat(TUN_DEV, &st) < 0) {
 | 
			
		||||
		ofono_error("Missing support for TUN/TAP devices");
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (g_at_chat_get_slave(chat) == NULL)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	if (vendor != OFONO_VENDOR_XMM) {
 | 
			
		||||
		if (g_at_chat_get_slave(chat) == NULL)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gcd = g_try_new0(struct gprs_context_data, 1);
 | 
			
		||||
	if (gcd == NULL)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	gcd->vendor = vendor;
 | 
			
		||||
	gcd->chat = g_at_chat_clone(chat);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_context_set_data(gc, gcd);
 | 
			
		||||
 | 
			
		||||
	chat = g_at_chat_get_slave(gcd->chat);
 | 
			
		||||
	if (vendor != OFONO_VENDOR_XMM)
 | 
			
		||||
		chat = g_at_chat_get_slave(gcd->chat);
 | 
			
		||||
 | 
			
		||||
	g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
 | 
			
		||||
 | 
			
		||||
@@ -516,6 +658,8 @@ static struct ofono_gprs_context_driver driver = {
 | 
			
		||||
	.remove			= ifx_gprs_context_remove,
 | 
			
		||||
	.activate_primary	= ifx_gprs_activate_primary,
 | 
			
		||||
	.deactivate_primary	= ifx_gprs_deactivate_primary,
 | 
			
		||||
	.read_settings		= ifx_gprs_read_settings,
 | 
			
		||||
	.detach_shutdown	= ifx_gprs_detach_shutdown
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void ifx_gprs_context_init(void)
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <drivers/atmodem/atutil.h>
 | 
			
		||||
#include <drivers/atmodem/vendor.h>
 | 
			
		||||
 | 
			
		||||
extern void ifx_voicecall_init(void);
 | 
			
		||||
extern void ifx_voicecall_exit(void);
 | 
			
		||||
 
 | 
			
		||||
@@ -646,8 +646,31 @@ error:
 | 
			
		||||
/* ISI callback: PIN state (enabled/disabled) query */
 | 
			
		||||
static void sec_code_state_resp_cb(const GIsiMessage *msg, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
	check_sec_response(msg, opaque, SEC_CODE_STATE_OK_RESP,
 | 
			
		||||
				SEC_CODE_STATE_FAIL_RESP);
 | 
			
		||||
	struct isi_cb_data *cbd = opaque;
 | 
			
		||||
	ofono_query_facility_lock_cb_t cb = cbd->cb;
 | 
			
		||||
	int locked;
 | 
			
		||||
	uint8_t state;
 | 
			
		||||
	uint8_t status;
 | 
			
		||||
 | 
			
		||||
	if (!g_isi_msg_data_get_byte(msg, 0, &state) ||
 | 
			
		||||
			!g_isi_msg_data_get_byte(msg, 1, &status))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (state != SEC_CODE_STATE_OK_RESP)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (status == SEC_CODE_ENABLE)
 | 
			
		||||
		locked = 1;
 | 
			
		||||
	else if (status == SEC_CODE_DISABLE)
 | 
			
		||||
		locked = 0;
 | 
			
		||||
	else
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, locked, cbd->data);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void isi_query_locked(struct ofono_sim *sim,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										107
									
								
								ofono/drivers/mbimmodem/devinfo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								ofono/drivers/mbimmodem/devinfo.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/devinfo.h>
 | 
			
		||||
 | 
			
		||||
#include "drivers/mbimmodem/mbimmodem.h"
 | 
			
		||||
 | 
			
		||||
struct devinfo_data {
 | 
			
		||||
	struct l_idle *delayed_register;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void mbim_query_revision(struct ofono_devinfo *info,
 | 
			
		||||
				ofono_devinfo_query_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_modem *modem = ofono_devinfo_get_modem(info);
 | 
			
		||||
	const char *revision = ofono_modem_get_string(modem, "FirmwareInfo");
 | 
			
		||||
 | 
			
		||||
	if (revision)
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, revision, data);
 | 
			
		||||
	else
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, NULL, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_query_serial(struct ofono_devinfo *info,
 | 
			
		||||
				ofono_devinfo_query_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_modem *modem = ofono_devinfo_get_modem(info);
 | 
			
		||||
	const char *serial = ofono_modem_get_string(modem, "DeviceId");
 | 
			
		||||
 | 
			
		||||
	if (serial)
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, serial, data);
 | 
			
		||||
	else
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, NULL, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void delayed_register(struct l_idle *idle, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_devinfo *info = user_data;
 | 
			
		||||
	struct devinfo_data *dd = ofono_devinfo_get_data(info);
 | 
			
		||||
 | 
			
		||||
	l_idle_remove(idle);
 | 
			
		||||
	dd->delayed_register = NULL;
 | 
			
		||||
 | 
			
		||||
	ofono_devinfo_register(info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mbim_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
 | 
			
		||||
				void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct devinfo_data *dd = l_new(struct devinfo_data, 1);
 | 
			
		||||
 | 
			
		||||
	dd->delayed_register = l_idle_create(delayed_register, info, NULL);
 | 
			
		||||
	ofono_devinfo_set_data(info, dd);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_devinfo_remove(struct ofono_devinfo *info)
 | 
			
		||||
{
 | 
			
		||||
	struct devinfo_data *dd = ofono_devinfo_get_data(info);
 | 
			
		||||
 | 
			
		||||
	ofono_devinfo_set_data(info, NULL);
 | 
			
		||||
	l_idle_remove(dd->delayed_register);
 | 
			
		||||
	l_free(dd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_devinfo_driver driver = {
 | 
			
		||||
	.name			= "mbim",
 | 
			
		||||
	.probe			= mbim_devinfo_probe,
 | 
			
		||||
	.remove			= mbim_devinfo_remove,
 | 
			
		||||
	.query_revision		= mbim_query_revision,
 | 
			
		||||
	.query_serial		= mbim_query_serial,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mbim_devinfo_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_devinfo_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mbim_devinfo_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_devinfo_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										464
									
								
								ofono/drivers/mbimmodem/gprs-context.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										464
									
								
								ofono/drivers/mbimmodem/gprs-context.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,464 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/gprs-context.h>
 | 
			
		||||
 | 
			
		||||
#include "drivers/mbimmodem/mbim.h"
 | 
			
		||||
#include "drivers/mbimmodem/mbim-message.h"
 | 
			
		||||
#include "drivers/mbimmodem/mbimmodem.h"
 | 
			
		||||
 | 
			
		||||
enum state {
 | 
			
		||||
	STATE_IDLE,
 | 
			
		||||
	STATE_ENABLING,
 | 
			
		||||
	STATE_DISABLING,
 | 
			
		||||
	STATE_ACTIVE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct gprs_context_data {
 | 
			
		||||
	struct mbim_device *device;
 | 
			
		||||
	unsigned int active_context;
 | 
			
		||||
	enum ofono_gprs_proto proto;
 | 
			
		||||
	enum state state;
 | 
			
		||||
	ofono_gprs_context_cb_t cb;
 | 
			
		||||
	void *cb_data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static uint32_t proto_to_context_ip_type(enum ofono_gprs_proto proto)
 | 
			
		||||
{
 | 
			
		||||
	switch (proto) {
 | 
			
		||||
	case OFONO_GPRS_PROTO_IP:
 | 
			
		||||
		return 1; /* MBIMContextIPTypeIPv4 */
 | 
			
		||||
	case OFONO_GPRS_PROTO_IPV6:
 | 
			
		||||
		return 2; /* MBIMContextIPTypeIPv6 */
 | 
			
		||||
	case OFONO_GPRS_PROTO_IPV4V6:
 | 
			
		||||
		return 3; /* MBIMContextIPTypeIPv4v6 */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t auth_method_to_auth_protocol(enum ofono_gprs_auth_method method)
 | 
			
		||||
{
 | 
			
		||||
	switch (method) {
 | 
			
		||||
	case OFONO_GPRS_AUTH_METHOD_CHAP:
 | 
			
		||||
		return 2; /* MBIMAuthProtocolChap */
 | 
			
		||||
	case OFONO_GPRS_AUTH_METHOD_PAP:
 | 
			
		||||
		return 1; /* MBIMAuthProtocolPap */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_deactivate_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs_context *gc = user;
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	gcd->active_context = 0;
 | 
			
		||||
	gcd->state = STATE_IDLE;
 | 
			
		||||
 | 
			
		||||
	if (!gcd->cb)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
 | 
			
		||||
	else
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_gprs_deactivate_primary(struct ofono_gprs_context *gc,
 | 
			
		||||
					unsigned int cid,
 | 
			
		||||
					ofono_gprs_context_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	DBG("cid %u", cid);
 | 
			
		||||
 | 
			
		||||
	gcd->state = STATE_DISABLING;
 | 
			
		||||
	gcd->cb = cb;
 | 
			
		||||
	gcd->cb_data = data;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_CONNECT,
 | 
			
		||||
					MBIM_COMMAND_TYPE_SET);
 | 
			
		||||
	mbim_message_set_arguments(message, "uusssuuu16y",
 | 
			
		||||
					cid, 0, NULL, NULL, NULL, 0, 0, 0,
 | 
			
		||||
					mbim_context_type_internet);
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(gcd->device, GPRS_CONTEXT_GROUP, message,
 | 
			
		||||
				mbim_deactivate_cb, gc, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
 | 
			
		||||
	if (cb)
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_ip_configuration_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs_context *gc = user;
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	struct ofono_modem *modem = ofono_gprs_context_get_modem(gc);
 | 
			
		||||
	const char *interface;
 | 
			
		||||
	uint32_t session_id;
 | 
			
		||||
	uint32_t ipv4_config_available;
 | 
			
		||||
	uint32_t ipv6_config_available;
 | 
			
		||||
	uint32_t n_ipv4_addr;
 | 
			
		||||
	uint32_t ipv4_addr_offset;
 | 
			
		||||
	uint32_t n_ipv6_addr;
 | 
			
		||||
	uint32_t ipv6_addr_offset;
 | 
			
		||||
	uint32_t ipv4_gw_offset;
 | 
			
		||||
	uint32_t ipv6_gw_offset;
 | 
			
		||||
	uint32_t n_ipv4_dns;
 | 
			
		||||
	uint32_t ipv4_dns_offset;
 | 
			
		||||
	uint32_t n_ipv6_dns;
 | 
			
		||||
	uint32_t ipv6_dns_offset;
 | 
			
		||||
	uint32_t ipv4_mtu;
 | 
			
		||||
	uint32_t ipv6_mtu;
 | 
			
		||||
 | 
			
		||||
	struct in6_addr ipv6;
 | 
			
		||||
	struct in_addr ipv4;
 | 
			
		||||
	char buf[INET6_ADDRSTRLEN];
 | 
			
		||||
 | 
			
		||||
	DBG("%u", mbim_message_get_error(message));
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uuuuuuuuuuuuuuu",
 | 
			
		||||
				&session_id,
 | 
			
		||||
				&ipv4_config_available, &ipv6_config_available,
 | 
			
		||||
				&n_ipv4_addr, &ipv4_addr_offset,
 | 
			
		||||
				&n_ipv6_addr, &ipv6_addr_offset,
 | 
			
		||||
				&ipv4_gw_offset, &ipv6_gw_offset,
 | 
			
		||||
				&n_ipv4_dns, &ipv4_dns_offset,
 | 
			
		||||
				&n_ipv6_dns, &ipv6_dns_offset,
 | 
			
		||||
				&ipv4_mtu, &ipv6_mtu))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (gcd->proto == OFONO_GPRS_PROTO_IPV6)
 | 
			
		||||
		goto ipv6;
 | 
			
		||||
 | 
			
		||||
	if (ipv4_config_available & 0x1) { /* Address Info present */
 | 
			
		||||
		uint32_t prefix;
 | 
			
		||||
 | 
			
		||||
		if (!mbim_message_get_ipv4_element(message, ipv4_addr_offset,
 | 
			
		||||
							&prefix, &ipv4))
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		inet_ntop(AF_INET, &ipv4, buf, sizeof(buf));
 | 
			
		||||
		ofono_gprs_context_set_ipv4_address(gc, buf, TRUE);
 | 
			
		||||
		ofono_gprs_context_set_ipv4_prefix_length(gc, prefix);
 | 
			
		||||
	} else
 | 
			
		||||
		ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE);
 | 
			
		||||
 | 
			
		||||
	if (ipv4_config_available & 0x2) { /* IPv4 Gateway info */
 | 
			
		||||
		if (!mbim_message_get_ipv4_address(message,
 | 
			
		||||
							ipv4_gw_offset, &ipv4))
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		inet_ntop(AF_INET, &ipv4, buf, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
		ofono_gprs_context_set_ipv4_gateway(gc, buf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ipv4_config_available & 0x3) { /* IPv4 DNS Info */
 | 
			
		||||
		const char *dns[3];
 | 
			
		||||
		char dns1[INET_ADDRSTRLEN];
 | 
			
		||||
		char dns2[INET_ADDRSTRLEN];
 | 
			
		||||
 | 
			
		||||
		memset(dns, 0, sizeof(dns));
 | 
			
		||||
 | 
			
		||||
		if (n_ipv4_dns > 1) { /* Grab second DNS */
 | 
			
		||||
			if (!mbim_message_get_ipv4_address(message,
 | 
			
		||||
							ipv4_dns_offset + 4,
 | 
			
		||||
							&ipv4))
 | 
			
		||||
				goto error;
 | 
			
		||||
 | 
			
		||||
			inet_ntop(AF_INET, &ipv4, dns2, sizeof(dns2));
 | 
			
		||||
			dns[1] = dns2;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (n_ipv4_dns > 0) { /* Grab first DNS */
 | 
			
		||||
			if (!mbim_message_get_ipv4_address(message,
 | 
			
		||||
							ipv4_dns_offset,
 | 
			
		||||
							&ipv4))
 | 
			
		||||
				goto error;
 | 
			
		||||
 | 
			
		||||
			inet_ntop(AF_INET, &ipv4, dns1, sizeof(dns1));
 | 
			
		||||
			dns[0] = dns1;
 | 
			
		||||
 | 
			
		||||
			ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (gcd->proto == OFONO_GPRS_PROTO_IP)
 | 
			
		||||
		goto done;
 | 
			
		||||
ipv6:
 | 
			
		||||
	if (ipv6_config_available & 0x1) { /* Address Info present */
 | 
			
		||||
		uint32_t prefix;
 | 
			
		||||
 | 
			
		||||
		if (!mbim_message_get_ipv6_element(message, ipv6_addr_offset,
 | 
			
		||||
							&prefix, &ipv6))
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		inet_ntop(AF_INET6, &ipv6, buf, sizeof(buf));
 | 
			
		||||
		ofono_gprs_context_set_ipv6_address(gc, buf);
 | 
			
		||||
		ofono_gprs_context_set_ipv6_prefix_length(gc, prefix);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ipv6_config_available & 0x2) { /* IPv6 Gateway info */
 | 
			
		||||
		if (!mbim_message_get_ipv6_address(message,
 | 
			
		||||
							ipv6_gw_offset, &ipv6))
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		inet_ntop(AF_INET6, &ipv6, buf, sizeof(buf));
 | 
			
		||||
 | 
			
		||||
		ofono_gprs_context_set_ipv6_gateway(gc, buf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ipv6_config_available & 0x3) { /* IPv6 DNS Info */
 | 
			
		||||
		const char *dns[3];
 | 
			
		||||
		char dns1[INET6_ADDRSTRLEN];
 | 
			
		||||
		char dns2[INET6_ADDRSTRLEN];
 | 
			
		||||
 | 
			
		||||
		memset(dns, 0, sizeof(dns));
 | 
			
		||||
 | 
			
		||||
		if (n_ipv6_dns > 1) { /* Grab second DNS */
 | 
			
		||||
			if (!mbim_message_get_ipv6_address(message,
 | 
			
		||||
							ipv6_dns_offset + 16,
 | 
			
		||||
							&ipv6))
 | 
			
		||||
				goto error;
 | 
			
		||||
 | 
			
		||||
			inet_ntop(AF_INET6, &ipv6, dns2, sizeof(dns2));
 | 
			
		||||
			dns[1] = dns2;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (n_ipv6_dns > 0) { /* Grab first DNS */
 | 
			
		||||
			if (!mbim_message_get_ipv6_address(message,
 | 
			
		||||
							ipv6_dns_offset,
 | 
			
		||||
							&ipv6))
 | 
			
		||||
				goto error;
 | 
			
		||||
 | 
			
		||||
			inet_ntop(AF_INET6, &ipv6, dns1, sizeof(dns1));
 | 
			
		||||
			dns[0] = dns1;
 | 
			
		||||
 | 
			
		||||
			ofono_gprs_context_set_ipv6_dns_servers(gc, dns);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
done:
 | 
			
		||||
 | 
			
		||||
	gcd->state = STATE_ACTIVE;
 | 
			
		||||
	interface = ofono_modem_get_string(modem, "NetworkInterface");
 | 
			
		||||
	ofono_gprs_context_set_interface(gc, interface);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
 | 
			
		||||
	gcd->cb = NULL;
 | 
			
		||||
	gcd->cb_data = NULL;
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
 | 
			
		||||
	gcd->state = STATE_IDLE;
 | 
			
		||||
	gcd->cb = NULL;
 | 
			
		||||
	gcd->cb_data = NULL;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_CONNECT,
 | 
			
		||||
					MBIM_COMMAND_TYPE_SET);
 | 
			
		||||
	mbim_message_set_arguments(message, "uusssuuu16y",
 | 
			
		||||
					gcd->active_context, 0,
 | 
			
		||||
					NULL, NULL, NULL, 0, 0, 0,
 | 
			
		||||
					mbim_context_type_internet);
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_send(gcd->device, GPRS_CONTEXT_GROUP, message,
 | 
			
		||||
				NULL, NULL, NULL))
 | 
			
		||||
		mbim_message_unref(message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_activate_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs_context *gc = user;
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_IP_CONFIGURATION,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	mbim_message_set_arguments(message, "uuuuuuuuuuuuuuu",
 | 
			
		||||
				gcd->active_context,
 | 
			
		||||
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(gcd->device, GPRS_CONTEXT_GROUP, message,
 | 
			
		||||
				mbim_ip_configuration_cb, gc, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
 | 
			
		||||
	gcd->state = STATE_IDLE;
 | 
			
		||||
	gcd->cb = NULL;
 | 
			
		||||
	gcd->cb_data = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_gprs_activate_primary(struct ofono_gprs_context *gc,
 | 
			
		||||
				const struct ofono_gprs_primary_context *ctx,
 | 
			
		||||
				ofono_gprs_context_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	DBG("cid %u", ctx->cid);
 | 
			
		||||
 | 
			
		||||
	gcd->state = STATE_ENABLING;
 | 
			
		||||
	gcd->cb = cb;
 | 
			
		||||
	gcd->cb_data = data;
 | 
			
		||||
	gcd->active_context = ctx->cid;
 | 
			
		||||
	gcd->proto = ctx->proto;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_CONNECT,
 | 
			
		||||
					MBIM_COMMAND_TYPE_SET);
 | 
			
		||||
	mbim_message_set_arguments(message, "uusssuuu16y",
 | 
			
		||||
				ctx->cid,
 | 
			
		||||
				1, /* MBIMActivationCommandActivate */
 | 
			
		||||
				ctx->apn,
 | 
			
		||||
				ctx->username[0] ? ctx->username : NULL,
 | 
			
		||||
				ctx->password[0] ? ctx->password : NULL,
 | 
			
		||||
				0, /*MBIMCompressionNone */
 | 
			
		||||
				auth_method_to_auth_protocol(ctx->auth_method),
 | 
			
		||||
				proto_to_context_ip_type(ctx->proto),
 | 
			
		||||
				mbim_context_type_internet);
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(gcd->device, GPRS_CONTEXT_GROUP, message,
 | 
			
		||||
				mbim_activate_cb, gc, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_gprs_detach_shutdown(struct ofono_gprs_context *gc,
 | 
			
		||||
						unsigned int cid)
 | 
			
		||||
{
 | 
			
		||||
	DBG("");
 | 
			
		||||
	mbim_gprs_deactivate_primary(gc, cid, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_connect_notify(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t session_id;
 | 
			
		||||
	uint32_t activation_state;
 | 
			
		||||
	uint32_t voice_call_state;
 | 
			
		||||
	uint32_t ip_type;
 | 
			
		||||
	uint8_t context_type[16];
 | 
			
		||||
	uint32_t nw_error;
 | 
			
		||||
	char uuidstr[37];
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uuuu16yu",
 | 
			
		||||
					&session_id, &activation_state,
 | 
			
		||||
					&voice_call_state, &ip_type,
 | 
			
		||||
					context_type, &nw_error))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	DBG("session_id: %u, activation_state: %u, ip_type: %u",
 | 
			
		||||
			session_id, activation_state, ip_type);
 | 
			
		||||
	l_uuid_to_string(context_type, uuidstr, sizeof(uuidstr));
 | 
			
		||||
	DBG("context_type: %s, nw_error: %u", uuidstr, nw_error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mbim_gprs_context_probe(struct ofono_gprs_context *gc,
 | 
			
		||||
					unsigned int vendor, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mbim_device *device = data;
 | 
			
		||||
	struct gprs_context_data *gcd;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_register(device, GPRS_CONTEXT_GROUP,
 | 
			
		||||
					mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_CONNECT,
 | 
			
		||||
					mbim_connect_notify, gc, NULL))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	gcd = l_new(struct gprs_context_data, 1);
 | 
			
		||||
	gcd->device = mbim_device_ref(device);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_context_set_data(gc, gcd);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_gprs_context_remove(struct ofono_gprs_context *gc)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_context_set_data(gc, NULL);
 | 
			
		||||
 | 
			
		||||
	mbim_device_cancel_group(gcd->device, GPRS_CONTEXT_GROUP);
 | 
			
		||||
	mbim_device_unregister_group(gcd->device, GPRS_CONTEXT_GROUP);
 | 
			
		||||
	mbim_device_unref(gcd->device);
 | 
			
		||||
	gcd->device = NULL;
 | 
			
		||||
	l_free(gcd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_gprs_context_driver driver = {
 | 
			
		||||
	.name			= "mbim",
 | 
			
		||||
	.probe			= mbim_gprs_context_probe,
 | 
			
		||||
	.remove			= mbim_gprs_context_remove,
 | 
			
		||||
	.activate_primary	= mbim_gprs_activate_primary,
 | 
			
		||||
	.deactivate_primary	= mbim_gprs_deactivate_primary,
 | 
			
		||||
	.detach_shutdown	= mbim_gprs_detach_shutdown
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mbim_gprs_context_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_gprs_context_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mbim_gprs_context_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_gprs_context_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										299
									
								
								ofono/drivers/mbimmodem/gprs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								ofono/drivers/mbimmodem/gprs.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,299 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/gprs.h>
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
#include "drivers/mbimmodem/mbim.h"
 | 
			
		||||
#include "drivers/mbimmodem/mbim-message.h"
 | 
			
		||||
#include "drivers/mbimmodem/mbimmodem.h"
 | 
			
		||||
 | 
			
		||||
struct gprs_data {
 | 
			
		||||
	struct mbim_device *device;
 | 
			
		||||
	struct l_idle *delayed_register;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void mbim_packet_service_set_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	ofono_gprs_cb_t cb = cbd->cb;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
	else
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_gprs_set_attached(struct ofono_gprs *gprs, int attached,
 | 
			
		||||
					ofono_gprs_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_data *gd = ofono_gprs_get_data(gprs);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_PACKET_SERVICE,
 | 
			
		||||
					MBIM_COMMAND_TYPE_SET);
 | 
			
		||||
	/*
 | 
			
		||||
	 * MBIMPacketServiceActionAttach (0) or
 | 
			
		||||
	 * MBIMPacketServiceActionDetach (1)
 | 
			
		||||
	 */
 | 
			
		||||
	mbim_message_set_arguments(message, "u", attached ? 0 : 1);
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(gd->device, GPRS_GROUP, message,
 | 
			
		||||
				mbim_packet_service_set_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_packet_service_query_cb(struct mbim_message *message,
 | 
			
		||||
								void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	ofono_gprs_status_cb_t cb = cbd->cb;
 | 
			
		||||
	uint32_t dummy;
 | 
			
		||||
	uint32_t state;
 | 
			
		||||
 | 
			
		||||
	DBG("%u", mbim_message_get_error(message));
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uu", &dummy, &state))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (state == 2)
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb,
 | 
			
		||||
					NETWORK_REGISTRATION_STATUS_REGISTERED,
 | 
			
		||||
					cbd->data);
 | 
			
		||||
	else
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, NETWORK_REGISTRATION_STATUS_UNKNOWN,
 | 
			
		||||
					cbd->data);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_gprs_registration_status(struct ofono_gprs *gprs,
 | 
			
		||||
					ofono_gprs_status_cb_t cb,
 | 
			
		||||
					void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_data *gd = ofono_gprs_get_data(gprs);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_PACKET_SERVICE,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	mbim_message_set_arguments(message, "");
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(gd->device, GPRS_GROUP, message,
 | 
			
		||||
				mbim_packet_service_query_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_packet_service_changed(struct mbim_message *message,
 | 
			
		||||
								void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs *gprs = user;
 | 
			
		||||
	uint32_t nw_error;
 | 
			
		||||
	uint32_t packet_service_state;
 | 
			
		||||
	uint32_t highest_avail_data_class;
 | 
			
		||||
	uint64_t uplink_speed;
 | 
			
		||||
	uint64_t downlink_speed;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uuutt",
 | 
			
		||||
						&nw_error,
 | 
			
		||||
						&packet_service_state,
 | 
			
		||||
						&highest_avail_data_class,
 | 
			
		||||
						&uplink_speed,
 | 
			
		||||
						&downlink_speed))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	DBG("uplink: %"PRIu64", downlink: %"PRIu64,
 | 
			
		||||
					uplink_speed, downlink_speed);
 | 
			
		||||
	DBG("nw_error: %u", nw_error);
 | 
			
		||||
 | 
			
		||||
	if (packet_service_state == 2) {
 | 
			
		||||
		uint32_t bearer =
 | 
			
		||||
			mbim_data_class_to_tech(highest_avail_data_class);
 | 
			
		||||
 | 
			
		||||
		ofono_gprs_status_notify(gprs,
 | 
			
		||||
			NETWORK_REGISTRATION_STATUS_REGISTERED);
 | 
			
		||||
		ofono_gprs_bearer_notify(gprs, bearer);
 | 
			
		||||
	} else
 | 
			
		||||
		ofono_gprs_status_notify(gprs,
 | 
			
		||||
				NETWORK_REGISTRATION_STATUS_UNKNOWN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void provisioned_contexts_query_cb(struct mbim_message *message,
 | 
			
		||||
								void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct mbim_message_iter contexts;
 | 
			
		||||
	uint32_t n_contexts;
 | 
			
		||||
	uint32_t id;
 | 
			
		||||
	uint8_t type[16];
 | 
			
		||||
	char *apn;
 | 
			
		||||
	char *username;
 | 
			
		||||
	char *password;
 | 
			
		||||
	uint32_t compression;
 | 
			
		||||
	uint32_t auth_protocol;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "a(u16ysssuu)",
 | 
			
		||||
						&n_contexts, &contexts))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	DBG("n_contexts: %u", n_contexts);
 | 
			
		||||
 | 
			
		||||
	while (mbim_message_iter_next_entry(&contexts, &id, type, &apn,
 | 
			
		||||
						&username, &password,
 | 
			
		||||
						&compression, &auth_protocol)) {
 | 
			
		||||
		char uuidstr[37];
 | 
			
		||||
 | 
			
		||||
		l_uuid_to_string(type, uuidstr, sizeof(uuidstr));
 | 
			
		||||
		DBG("id: %u, type: %s", id, uuidstr);
 | 
			
		||||
		DBG("apn: %s, username: %s, password: %s",
 | 
			
		||||
			apn, username, password);
 | 
			
		||||
		DBG("compression: %u, auth_protocol: %u",
 | 
			
		||||
			compression, auth_protocol);
 | 
			
		||||
 | 
			
		||||
		l_free(apn);
 | 
			
		||||
		l_free(username);
 | 
			
		||||
		l_free(password);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void delayed_register(struct l_idle *idle, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs *gprs = user_data;
 | 
			
		||||
	struct gprs_data *gd = ofono_gprs_get_data(gprs);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	l_idle_remove(idle);
 | 
			
		||||
	gd->delayed_register = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Query provisioned contexts for debugging purposes only */
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_PROVISIONED_CONTEXTS,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	mbim_message_set_arguments(message, "");
 | 
			
		||||
	mbim_device_send(gd->device, 0, message,
 | 
			
		||||
				provisioned_contexts_query_cb, gprs, NULL);
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_register(gd->device, GPRS_GROUP,
 | 
			
		||||
					mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_PACKET_SERVICE,
 | 
			
		||||
					mbim_packet_service_changed,
 | 
			
		||||
					gprs, NULL))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_register(gprs);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ofono_gprs_remove(gprs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mbim_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor,
 | 
			
		||||
								void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mbim_device *device = data;
 | 
			
		||||
	struct gprs_data *gd;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	gd = l_new(struct gprs_data, 1);
 | 
			
		||||
	gd->device = mbim_device_ref(device);
 | 
			
		||||
	gd->delayed_register = l_idle_create(delayed_register, gprs, NULL);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_set_data(gprs, gd);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_gprs_remove(struct ofono_gprs *gprs)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_data *gd = ofono_gprs_get_data(gprs);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_set_data(gprs, NULL);
 | 
			
		||||
 | 
			
		||||
	l_idle_remove(gd->delayed_register);
 | 
			
		||||
	mbim_device_cancel_group(gd->device, GPRS_GROUP);
 | 
			
		||||
	mbim_device_unregister_group(gd->device, GPRS_GROUP);
 | 
			
		||||
	mbim_device_unref(gd->device);
 | 
			
		||||
	gd->device = NULL;
 | 
			
		||||
	l_free(gd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_gprs_driver driver = {
 | 
			
		||||
	.name			= "mbim",
 | 
			
		||||
	.probe			= mbim_gprs_probe,
 | 
			
		||||
	.remove			= mbim_gprs_remove,
 | 
			
		||||
	.set_attached		= mbim_gprs_set_attached,
 | 
			
		||||
	.attached_status	= mbim_gprs_registration_status,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mbim_gprs_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_gprs_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mbim_gprs_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_gprs_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										80
									
								
								ofono/drivers/mbimmodem/mbim-desc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								ofono/drivers/mbimmodem/mbim-desc.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
#include "mbim-desc.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Attempts to find MBIM specific descriptors.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns true if the MBIM Function descriptor was found, false otherwise.
 | 
			
		||||
 */
 | 
			
		||||
bool mbim_find_descriptors(const uint8_t *data, size_t data_len,
 | 
			
		||||
				const struct mbim_desc **out_desc,
 | 
			
		||||
				const struct mbim_extended_desc **out_ext_desc)
 | 
			
		||||
{
 | 
			
		||||
	bool r = false;
 | 
			
		||||
 | 
			
		||||
	while (data_len > 3) {
 | 
			
		||||
		uint8_t len = data[0];
 | 
			
		||||
 | 
			
		||||
		if (data[1] != 0x24)
 | 
			
		||||
			goto next;
 | 
			
		||||
 | 
			
		||||
		/* MBIM v1.0, Table 4-3 */
 | 
			
		||||
		switch (data[2]) {
 | 
			
		||||
		case 0x1b:
 | 
			
		||||
			if (!out_desc)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			if (len != sizeof(struct mbim_desc) || data_len < len)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			*out_desc = (const struct mbim_desc *) data;
 | 
			
		||||
			r = true;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x1c:
 | 
			
		||||
			if (!out_ext_desc)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			if (len != sizeof(struct mbim_extended_desc) ||
 | 
			
		||||
					data_len < len)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			*out_ext_desc =
 | 
			
		||||
				(const struct mbim_extended_desc *) data;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
next:
 | 
			
		||||
		data_len -= len;
 | 
			
		||||
		data += len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								ofono/drivers/mbimmodem/mbim-desc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								ofono/drivers/mbimmodem/mbim-desc.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
 | 
			
		||||
/* MBIM v1.0, Section 6.4: MBIM Functional Descriptor */
 | 
			
		||||
struct mbim_desc {
 | 
			
		||||
	uint8_t bFunctionLength;
 | 
			
		||||
	uint8_t bDescriptorType;
 | 
			
		||||
	uint8_t bDescriptorSubtype;
 | 
			
		||||
	__le16 bcdMBIMVersion;
 | 
			
		||||
	__le16 wMaxControlMessage;
 | 
			
		||||
	uint8_t bNumberFilters;
 | 
			
		||||
	uint8_t bMaxFilterSize;
 | 
			
		||||
	__le16 wMaxSegmentSize;
 | 
			
		||||
	uint8_t bmNetworkCapabilities;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
/* MBIM v1.0, Section 6.5: MBIM Extended Functional Descriptor */
 | 
			
		||||
struct mbim_extended_desc {
 | 
			
		||||
	uint8_t	bFunctionLength;
 | 
			
		||||
	uint8_t	bDescriptorType;
 | 
			
		||||
	uint8_t bDescriptorSubtype;
 | 
			
		||||
	__le16 bcdMBIMExtendedVersion;
 | 
			
		||||
	uint8_t bMaxOutstandingCommandMessages;
 | 
			
		||||
	__le16 wMTU;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
bool mbim_find_descriptors(const uint8_t *data, size_t data_len,
 | 
			
		||||
				const struct mbim_desc **out_desc,
 | 
			
		||||
				const struct mbim_extended_desc **out_ext_desc);
 | 
			
		||||
							
								
								
									
										1738
									
								
								ofono/drivers/mbimmodem/mbim-message.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1738
									
								
								ofono/drivers/mbimmodem/mbim-message.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										96
									
								
								ofono/drivers/mbimmodem/mbim-message.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								ofono/drivers/mbimmodem/mbim-message.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
 | 
			
		||||
struct mbim_message;
 | 
			
		||||
struct mbim_message_iter;
 | 
			
		||||
 | 
			
		||||
enum mbim_command_type {
 | 
			
		||||
	MBIM_COMMAND_TYPE_QUERY = 0,
 | 
			
		||||
	MBIM_COMMAND_TYPE_SET = 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mbim_message_iter {
 | 
			
		||||
	const char *sig_start;
 | 
			
		||||
	uint8_t sig_len;
 | 
			
		||||
	uint8_t sig_pos;
 | 
			
		||||
	const struct iovec *iov;
 | 
			
		||||
	uint32_t n_iov;
 | 
			
		||||
	uint32_t cur_iov;
 | 
			
		||||
	size_t cur_iov_offset;
 | 
			
		||||
	size_t len;
 | 
			
		||||
	size_t pos;
 | 
			
		||||
	size_t base_offset;
 | 
			
		||||
	uint32_t n_elem;
 | 
			
		||||
	char container_type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mbim_message *mbim_message_new(const uint8_t *uuid, uint32_t cid,
 | 
			
		||||
					enum mbim_command_type type);
 | 
			
		||||
struct mbim_message *mbim_message_ref(struct mbim_message *msg);
 | 
			
		||||
void mbim_message_unref(struct mbim_message *msg);
 | 
			
		||||
 | 
			
		||||
uint32_t mbim_message_get_error(struct mbim_message *message);
 | 
			
		||||
uint32_t mbim_message_get_cid(struct mbim_message *message);
 | 
			
		||||
const uint8_t *mbim_message_get_uuid(struct mbim_message *message);
 | 
			
		||||
bool mbim_message_get_arguments(struct mbim_message *message,
 | 
			
		||||
						const char *signature, ...);
 | 
			
		||||
 | 
			
		||||
bool mbim_message_get_ipv4_address(struct mbim_message *message,
 | 
			
		||||
					uint32_t offset,
 | 
			
		||||
					struct in_addr *addr);
 | 
			
		||||
bool mbim_message_get_ipv4_element(struct mbim_message *message,
 | 
			
		||||
					uint32_t offset,
 | 
			
		||||
					uint32_t *prefix_len,
 | 
			
		||||
					struct in_addr *addr);
 | 
			
		||||
bool mbim_message_get_ipv6_address(struct mbim_message *essage,
 | 
			
		||||
					uint32_t offset,
 | 
			
		||||
					struct in6_addr *addr);
 | 
			
		||||
bool mbim_message_get_ipv6_element(struct mbim_message *message,
 | 
			
		||||
					uint32_t offset,
 | 
			
		||||
					uint32_t *prefix_len,
 | 
			
		||||
					struct in6_addr *addr);
 | 
			
		||||
 | 
			
		||||
bool mbim_message_iter_next_entry(struct mbim_message_iter *iter, ...);
 | 
			
		||||
 | 
			
		||||
struct mbim_message_builder *mbim_message_builder_new(struct mbim_message *msg);
 | 
			
		||||
void mbim_message_builder_free(struct mbim_message_builder *builder);
 | 
			
		||||
bool mbim_message_builder_append_basic(struct mbim_message_builder *builder,
 | 
			
		||||
					char type, const void *value);
 | 
			
		||||
bool mbim_message_builder_append_bytes(struct mbim_message_builder *builder,
 | 
			
		||||
					size_t len, const uint8_t *bytes);
 | 
			
		||||
bool mbim_message_builder_enter_struct(struct mbim_message_builder *builder,
 | 
			
		||||
					const char *signature);
 | 
			
		||||
bool mbim_message_builder_leave_struct(struct mbim_message_builder *builder);
 | 
			
		||||
bool mbim_message_builder_enter_array(struct mbim_message_builder *builder,
 | 
			
		||||
					const char *signature);
 | 
			
		||||
bool mbim_message_builder_leave_array(struct mbim_message_builder *builder);
 | 
			
		||||
bool mbim_message_builder_enter_databuf(struct mbim_message_builder *builder,
 | 
			
		||||
					const char *signature);
 | 
			
		||||
bool mbim_message_builder_leave_databuf(struct mbim_message_builder *builder);
 | 
			
		||||
struct mbim_message *mbim_message_builder_finalize(
 | 
			
		||||
					struct mbim_message_builder *builder);
 | 
			
		||||
 | 
			
		||||
bool mbim_message_set_arguments(struct mbim_message *message,
 | 
			
		||||
						const char *signature, ...);
 | 
			
		||||
							
								
								
									
										59
									
								
								ofono/drivers/mbimmodem/mbim-private.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								ofono/drivers/mbimmodem/mbim-private.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define align_len(len, boundary) (((len)+(boundary)-1) & ~((boundary)-1))
 | 
			
		||||
 | 
			
		||||
enum mbim_control_message {
 | 
			
		||||
	MBIM_OPEN_MSG = 0x1,
 | 
			
		||||
	MBIM_CLOSE_MSG = 0x2,
 | 
			
		||||
	MBIM_COMMAND_MSG = 0x3,
 | 
			
		||||
	MBIM_HOST_ERROR_MSG = 0x4,
 | 
			
		||||
	MBIM_OPEN_DONE = 0x80000001,
 | 
			
		||||
	MBIM_CLOSE_DONE = 0x80000002,
 | 
			
		||||
	MBIM_COMMAND_DONE = 0x80000003,
 | 
			
		||||
	MBIM_FUNCTION_ERROR_MSG = 0x80000004,
 | 
			
		||||
	MBIM_INDICATE_STATUS_MSG = 0x80000007,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* MBIM v1.0, Section 9.1 */
 | 
			
		||||
struct mbim_message_header {
 | 
			
		||||
	__le32 type;
 | 
			
		||||
	__le32 len;
 | 
			
		||||
	__le32 tid;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
/* MBIM v1.0, Section 9.1 */
 | 
			
		||||
struct mbim_fragment_header {
 | 
			
		||||
	__le32 num_frags;
 | 
			
		||||
	__le32 cur_frag;
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct mbim_message *_mbim_message_build(const void *header,
 | 
			
		||||
						struct iovec *frags,
 | 
			
		||||
						uint32_t n_frags);
 | 
			
		||||
struct mbim_message *_mbim_message_new_command_done(const uint8_t *uuid,
 | 
			
		||||
					uint32_t cid, uint32_t status);
 | 
			
		||||
uint32_t _mbim_information_buffer_offset(uint32_t type);
 | 
			
		||||
void _mbim_message_set_tid(struct mbim_message *message, uint32_t tid);
 | 
			
		||||
void *_mbim_message_to_bytearray(struct mbim_message *message, size_t *out_len);
 | 
			
		||||
void *_mbim_message_get_header(struct mbim_message *message, size_t *out_len);
 | 
			
		||||
struct iovec *_mbim_message_get_body(struct mbim_message *message,
 | 
			
		||||
					size_t *out_n_iov, size_t *out_len);
 | 
			
		||||
							
								
								
									
										1218
									
								
								ofono/drivers/mbimmodem/mbim.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1218
									
								
								ofono/drivers/mbimmodem/mbim.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										149
									
								
								ofono/drivers/mbimmodem/mbim.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								ofono/drivers/mbimmodem/mbim.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,149 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
struct mbim_device;
 | 
			
		||||
struct mbim_message;
 | 
			
		||||
 | 
			
		||||
#define MBIM_CID_DEVICE_CAPS			1
 | 
			
		||||
#define MBIM_CID_SUBSCRIBER_READY_STATUS	2
 | 
			
		||||
#define MBIM_CID_RADIO_STATE			3
 | 
			
		||||
#define MBIM_CID_PIN				4
 | 
			
		||||
#define MBIM_CID_PIN_LIST			5
 | 
			
		||||
#define MBIM_CID_HOME_PROVIDER			6
 | 
			
		||||
#define MBIM_CID_PREFERRED_PROVIDERS		7
 | 
			
		||||
#define MBIM_CID_VISIBLE_PROVIDERS		8
 | 
			
		||||
#define MBIM_CID_REGISTER_STATE			9
 | 
			
		||||
#define MBIM_CID_PACKET_SERVICE			10
 | 
			
		||||
#define MBIM_CID_SIGNAL_STATE			11
 | 
			
		||||
#define MBIM_CID_CONNECT			12
 | 
			
		||||
#define MBIM_CID_PROVISIONED_CONTEXTS		13
 | 
			
		||||
#define MBIM_CID_SERVICE_ACTIVATION		14
 | 
			
		||||
#define MBIM_CID_IP_CONFIGURATION		15
 | 
			
		||||
#define MBIM_CID_DEVICE_SERVICES		16
 | 
			
		||||
#define MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST	19
 | 
			
		||||
#define MBIM_CID_PACKET_STATISTICS		20
 | 
			
		||||
#define MBIM_CID_NETWORK_IDLE_HINT		21
 | 
			
		||||
#define MBIM_CID_EMERGENCY_MODE			22
 | 
			
		||||
#define MBIM_CID_IP_PACKET_FILTERS		23
 | 
			
		||||
#define MBIM_CID_MULTICARRIER_PROVIDERS		24
 | 
			
		||||
 | 
			
		||||
#define MBIM_CID_SMS_CONFIGURATION		1
 | 
			
		||||
#define MBIM_CID_SMS_READ			2
 | 
			
		||||
#define MBIM_CID_SMS_SEND			3
 | 
			
		||||
#define MBIM_CID_SMS_DELETE			4
 | 
			
		||||
#define MBIM_CID_SMS_MESSAGE_STORE_STATUS	5
 | 
			
		||||
 | 
			
		||||
#define MBIM_CID_USSD				1
 | 
			
		||||
 | 
			
		||||
#define MBIM_CID_PHONEBOOK_CONFIGURATION	1
 | 
			
		||||
#define MBIM_CID_PHONEBOOK_READ			2
 | 
			
		||||
#define MBIM_CID_PHONEBOOK_DELETE		3
 | 
			
		||||
#define MBIM_CID_PHONEBOOK_WRITE		4
 | 
			
		||||
 | 
			
		||||
#define MBIM_CID_STK_PAC			1
 | 
			
		||||
#define MBIM_CID_STK_TERMINAL_RESPONSE		2
 | 
			
		||||
#define MBIM_CID_STK_ENVELOPE			3
 | 
			
		||||
 | 
			
		||||
#define MBIM_CID_AKA_AUTH			1
 | 
			
		||||
#define MBIM_CID_AKAP_AUTH			2
 | 
			
		||||
#define MBIM_CID_SIM_AUTH			3
 | 
			
		||||
 | 
			
		||||
#define MBIM_CID_DSS_CONNECT			1
 | 
			
		||||
 | 
			
		||||
/* Table 10-11 */
 | 
			
		||||
enum mbim_data_class {
 | 
			
		||||
	MBIM_DATA_CLASS_NONE		= 0x00,
 | 
			
		||||
	MBIM_DATA_CLASS_GPRS		= 0x01,
 | 
			
		||||
	MBIM_DATA_CLASS_EDGE		= 0x02,
 | 
			
		||||
	MBIM_DATA_CLASS_UMTS		= 0x04,
 | 
			
		||||
	MBIM_DATA_CLASS_HSDPA		= 0x08,
 | 
			
		||||
	MBIM_DATA_CLASS_HSUPA		= 0x10,
 | 
			
		||||
	MBIM_DATA_CLASS_LTE		= 0x20,
 | 
			
		||||
	MBIM_DATA_CLASS_1XRTT		= 0x10000,
 | 
			
		||||
	MBIM_DATA_CLASS_EVDO		= 0x20000,
 | 
			
		||||
	MBIM_DATA_CLASS_EVDO_REVA	= 0x40000,
 | 
			
		||||
	MBIM_DATA_CLASS_1XEVDV		= 0x80000,
 | 
			
		||||
	MBIM_DATA_CLASS_3XRTT		= 0x100000,
 | 
			
		||||
	MBIM_DATA_CLASS_1XEVDO_REVB	= 0x200000,
 | 
			
		||||
	MBIM_DATA_CLASS_UMB		= 0x400000,
 | 
			
		||||
	MBIM_DATA_CLASS_CUSTOM		= 0x80000000,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef void (*mbim_device_debug_func_t) (const char *str, void *user_data);
 | 
			
		||||
typedef void (*mbim_device_disconnect_func_t) (void *user_data);
 | 
			
		||||
typedef void (*mbim_device_destroy_func_t) (void *user_data);
 | 
			
		||||
typedef void (*mbim_device_ready_func_t) (void *user_data);
 | 
			
		||||
typedef void (*mbim_device_reply_func_t) (struct mbim_message *message,
 | 
			
		||||
							void *user_data);
 | 
			
		||||
 | 
			
		||||
extern const uint8_t mbim_uuid_basic_connect[];
 | 
			
		||||
extern const uint8_t mbim_uuid_sms[];
 | 
			
		||||
extern const uint8_t mbim_uuid_ussd[];
 | 
			
		||||
extern const uint8_t mbim_uuid_phonebook[];
 | 
			
		||||
extern const uint8_t mbim_uuid_stk[];
 | 
			
		||||
extern const uint8_t mbim_uuid_auth[];
 | 
			
		||||
extern const uint8_t mbim_uuid_dss[];
 | 
			
		||||
 | 
			
		||||
extern const uint8_t mbim_context_type_none[];
 | 
			
		||||
extern const uint8_t mbim_context_type_internet[];
 | 
			
		||||
extern const uint8_t mbim_context_type_vpn[];
 | 
			
		||||
extern const uint8_t mbim_context_type_voice[];
 | 
			
		||||
extern const uint8_t mbim_context_type_video_share[];
 | 
			
		||||
extern const uint8_t mbim_context_type_purchase[];
 | 
			
		||||
extern const uint8_t mbim_context_type_ims[];
 | 
			
		||||
extern const uint8_t mbim_context_type_mms[];
 | 
			
		||||
extern const uint8_t mbim_context_type_local[];
 | 
			
		||||
 | 
			
		||||
struct mbim_device *mbim_device_new(int fd, uint32_t max_segment_size);
 | 
			
		||||
bool mbim_device_set_close_on_unref(struct mbim_device *device, bool do_close);
 | 
			
		||||
struct mbim_device *mbim_device_ref(struct mbim_device *device);
 | 
			
		||||
void mbim_device_unref(struct mbim_device *device);
 | 
			
		||||
bool mbim_device_shutdown(struct mbim_device *device);
 | 
			
		||||
 | 
			
		||||
bool mbim_device_set_max_outstanding(struct mbim_device *device, uint32_t max);
 | 
			
		||||
 | 
			
		||||
bool mbim_device_set_debug(struct mbim_device *device,
 | 
			
		||||
				mbim_device_debug_func_t func, void *user_data,
 | 
			
		||||
				mbim_device_destroy_func_t destroy);
 | 
			
		||||
bool mbim_device_set_disconnect_handler(struct mbim_device *device,
 | 
			
		||||
					mbim_device_disconnect_func_t function,
 | 
			
		||||
					void *user_data,
 | 
			
		||||
					mbim_device_destroy_func_t destroy);
 | 
			
		||||
bool mbim_device_set_ready_handler(struct mbim_device *device,
 | 
			
		||||
					mbim_device_ready_func_t function,
 | 
			
		||||
					void *user_data,
 | 
			
		||||
					mbim_device_destroy_func_t destroy);
 | 
			
		||||
 | 
			
		||||
uint32_t mbim_device_send(struct mbim_device *device, uint32_t gid,
 | 
			
		||||
				struct mbim_message *message,
 | 
			
		||||
				mbim_device_reply_func_t function,
 | 
			
		||||
				void *user_data,
 | 
			
		||||
				mbim_device_destroy_func_t destroy);
 | 
			
		||||
bool mbim_device_cancel(struct mbim_device *device, uint32_t tid);
 | 
			
		||||
bool mbim_device_cancel_group(struct mbim_device *device, uint32_t gid);
 | 
			
		||||
 | 
			
		||||
uint32_t mbim_device_register(struct mbim_device *device, uint32_t gid,
 | 
			
		||||
				const uint8_t *uuid, uint32_t cid,
 | 
			
		||||
				mbim_device_reply_func_t notify,
 | 
			
		||||
				void *user_data,
 | 
			
		||||
				mbim_device_destroy_func_t destroy);
 | 
			
		||||
bool mbim_device_unregister(struct mbim_device *device, uint32_t id);
 | 
			
		||||
bool mbim_device_unregister_group(struct mbim_device *device, uint32_t gid);
 | 
			
		||||
							
								
								
									
										53
									
								
								ofono/drivers/mbimmodem/mbimmodem.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								ofono/drivers/mbimmodem/mbimmodem.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define OFONO_API_SUBJECT_TO_CHANGE
 | 
			
		||||
#include <ofono/plugin.h>
 | 
			
		||||
 | 
			
		||||
#include "mbimmodem.h"
 | 
			
		||||
 | 
			
		||||
static int mbimmodem_init(void)
 | 
			
		||||
{
 | 
			
		||||
	mbim_devinfo_init();
 | 
			
		||||
	mbim_sim_init();
 | 
			
		||||
	mbim_netreg_init();
 | 
			
		||||
	mbim_sms_exit();
 | 
			
		||||
	mbim_gprs_init();
 | 
			
		||||
	mbim_gprs_context_init();
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbimmodem_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	mbim_gprs_context_exit();
 | 
			
		||||
	mbim_gprs_exit();
 | 
			
		||||
	mbim_sms_exit();
 | 
			
		||||
	mbim_netreg_exit();
 | 
			
		||||
	mbim_sim_exit();
 | 
			
		||||
	mbim_devinfo_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OFONO_PLUGIN_DEFINE(mbimmodem, "MBIM modem driver", VERSION,
 | 
			
		||||
		OFONO_PLUGIN_PRIORITY_DEFAULT, mbimmodem_init, mbimmodem_exit)
 | 
			
		||||
							
								
								
									
										48
									
								
								ofono/drivers/mbimmodem/mbimmodem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								ofono/drivers/mbimmodem/mbimmodem.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
enum MBIM_GROUP {
 | 
			
		||||
	SIM_GROUP = 1,
 | 
			
		||||
	NETREG_GROUP = 2,
 | 
			
		||||
	SMS_GROUP = 3,
 | 
			
		||||
	GPRS_GROUP = 4,
 | 
			
		||||
	GPRS_CONTEXT_GROUP = 101,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern void mbim_devinfo_init(void);
 | 
			
		||||
extern void mbim_devinfo_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void mbim_sim_init(void);
 | 
			
		||||
extern void mbim_sim_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void mbim_netreg_init(void);
 | 
			
		||||
extern void mbim_netreg_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void mbim_sms_init(void);
 | 
			
		||||
extern void mbim_sms_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void mbim_gprs_init(void);
 | 
			
		||||
extern void mbim_gprs_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void mbim_gprs_context_init(void);
 | 
			
		||||
extern void mbim_gprs_context_exit(void);
 | 
			
		||||
							
								
								
									
										416
									
								
								ofono/drivers/mbimmodem/network-registration.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								ofono/drivers/mbimmodem/network-registration.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,416 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/netreg.h>
 | 
			
		||||
 | 
			
		||||
#include "src/common.h"
 | 
			
		||||
 | 
			
		||||
#include "drivers/mbimmodem/mbim.h"
 | 
			
		||||
#include "drivers/mbimmodem/mbim-message.h"
 | 
			
		||||
#include "drivers/mbimmodem/mbimmodem.h"
 | 
			
		||||
 | 
			
		||||
struct netreg_data {
 | 
			
		||||
	struct mbim_device *device;
 | 
			
		||||
	struct l_idle *delayed_register;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline int register_state_to_status(uint32_t register_state)
 | 
			
		||||
{
 | 
			
		||||
	switch (register_state) {
 | 
			
		||||
	case 0:	/* MBIMRegisterStateUnknown */
 | 
			
		||||
		return NETWORK_REGISTRATION_STATUS_UNKNOWN;
 | 
			
		||||
	case 1: /* MBIMRegisterStateDeregistered */
 | 
			
		||||
		return NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
 | 
			
		||||
	case 2: /* MBIMRegisterStateSearching */
 | 
			
		||||
		return NETWORK_REGISTRATION_STATUS_SEARCHING;
 | 
			
		||||
	case 3: /* MBIMRegisterStateHome */
 | 
			
		||||
		return NETWORK_REGISTRATION_STATUS_REGISTERED;
 | 
			
		||||
	case 4: /* MBIMRegisterStateRoaming */
 | 
			
		||||
	case 5: /* MBIMRegisterStatePartner */
 | 
			
		||||
		return NETWORK_REGISTRATION_STATUS_ROAMING;
 | 
			
		||||
	case 6: /* MBIMRegisterStateDenied */
 | 
			
		||||
		return NETWORK_REGISTRATION_STATUS_DENIED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NETWORK_REGISTRATION_STATUS_UNKNOWN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_register_state_changed(struct mbim_message *message,
 | 
			
		||||
								void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_netreg *netreg = user;
 | 
			
		||||
	uint32_t nw_error;
 | 
			
		||||
	uint32_t register_state;
 | 
			
		||||
	uint32_t register_mode;
 | 
			
		||||
	uint32_t available_data_classes;
 | 
			
		||||
	int status;
 | 
			
		||||
	int tech;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uuuu",
 | 
			
		||||
						&nw_error, ®ister_state,
 | 
			
		||||
						®ister_mode,
 | 
			
		||||
						&available_data_classes))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	DBG("NwError: %u, RegisterMode: %u", nw_error, register_mode);
 | 
			
		||||
 | 
			
		||||
	status = register_state_to_status(register_state);
 | 
			
		||||
	tech = mbim_data_class_to_tech(available_data_classes);
 | 
			
		||||
 | 
			
		||||
	ofono_netreg_status_notify(netreg, status, -1, -1, tech);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_registration_status_cb(struct mbim_message *message,
 | 
			
		||||
								void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	ofono_netreg_status_cb_t cb = cbd->cb;
 | 
			
		||||
	uint32_t dummy;
 | 
			
		||||
	uint32_t register_state;
 | 
			
		||||
	uint32_t available_data_classes;
 | 
			
		||||
	int status;
 | 
			
		||||
	int tech;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uuuu",
 | 
			
		||||
						&dummy, ®ister_state,
 | 
			
		||||
						&dummy,
 | 
			
		||||
						&available_data_classes))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	status = register_state_to_status(register_state);
 | 
			
		||||
	tech = mbim_data_class_to_tech(available_data_classes);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, status, -1, -1, tech, cbd->data);
 | 
			
		||||
	return;
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_registration_status(struct ofono_netreg *netreg,
 | 
			
		||||
					ofono_netreg_status_cb_t cb,
 | 
			
		||||
					void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_REGISTER_STATE,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	mbim_message_set_arguments(message, "");
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(nd->device, NETREG_GROUP, message,
 | 
			
		||||
				mbim_registration_status_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_current_operator_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	ofono_netreg_operator_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_network_operator op;
 | 
			
		||||
	uint32_t dummy;
 | 
			
		||||
	uint32_t register_state;
 | 
			
		||||
	uint32_t available_data_classes;
 | 
			
		||||
	L_AUTO_FREE_VAR(char *, provider_id) = NULL;
 | 
			
		||||
	L_AUTO_FREE_VAR(char *, provider_name) = NULL;
 | 
			
		||||
	L_AUTO_FREE_VAR(char *, roaming_text) = NULL;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uuuuusss",
 | 
			
		||||
						&dummy, ®ister_state, &dummy,
 | 
			
		||||
						&available_data_classes, &dummy,
 | 
			
		||||
						&provider_id, &provider_name,
 | 
			
		||||
						&roaming_text))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (register_state < 3 || register_state > 5)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	DBG("provider: %s(%s)", provider_name, provider_id);
 | 
			
		||||
 | 
			
		||||
	/* If MBIMRegisterStateRoaming or MBIMRegisterStatePartner */
 | 
			
		||||
	if (register_state == 4 || register_state == 5)
 | 
			
		||||
		DBG("roaming text: %s", roaming_text);
 | 
			
		||||
 | 
			
		||||
	strncpy(op.name, provider_name, OFONO_MAX_OPERATOR_NAME_LENGTH);
 | 
			
		||||
	op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
 | 
			
		||||
 | 
			
		||||
	strncpy(op.mcc, provider_id, OFONO_MAX_MCC_LENGTH);
 | 
			
		||||
	op.mcc[OFONO_MAX_MCC_LENGTH] = '\0';
 | 
			
		||||
 | 
			
		||||
	strncpy(op.mnc, provider_id + OFONO_MAX_MCC_LENGTH,
 | 
			
		||||
						OFONO_MAX_MNC_LENGTH);
 | 
			
		||||
	op.mnc[OFONO_MAX_MNC_LENGTH] = '\0';
 | 
			
		||||
 | 
			
		||||
	/* Set to current */
 | 
			
		||||
	op.status = 2;
 | 
			
		||||
	op.tech = mbim_data_class_to_tech(available_data_classes);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, &op, cbd->data);
 | 
			
		||||
	return;
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_current_operator(struct ofono_netreg *netreg,
 | 
			
		||||
				ofono_netreg_operator_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_REGISTER_STATE,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	mbim_message_set_arguments(message, "");
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(nd->device, NETREG_GROUP, message,
 | 
			
		||||
				mbim_current_operator_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_register_state_set_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	ofono_netreg_register_cb_t cb = cbd->cb;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
	else
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_register_auto(struct ofono_netreg *netreg,
 | 
			
		||||
				ofono_netreg_register_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	static const uint32_t data_class = MBIM_DATA_CLASS_GPRS |
 | 
			
		||||
						MBIM_DATA_CLASS_EDGE |
 | 
			
		||||
						MBIM_DATA_CLASS_UMTS |
 | 
			
		||||
						MBIM_DATA_CLASS_HSDPA |
 | 
			
		||||
						MBIM_DATA_CLASS_HSUPA |
 | 
			
		||||
						MBIM_DATA_CLASS_LTE;
 | 
			
		||||
	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_REGISTER_STATE,
 | 
			
		||||
					MBIM_COMMAND_TYPE_SET);
 | 
			
		||||
	mbim_message_set_arguments(message, "suu", NULL, 0, data_class);
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(nd->device, NETREG_GROUP, message,
 | 
			
		||||
				mbim_register_state_set_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int convert_signal_strength(uint32_t strength)
 | 
			
		||||
{
 | 
			
		||||
	if (strength == 99)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	return strength * 100 / 31;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_signal_state_query_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	ofono_netreg_strength_cb_t cb = cbd->cb;
 | 
			
		||||
	uint32_t strength;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "u", &strength))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, convert_signal_strength(strength), cbd->data);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_signal_strength(struct ofono_netreg *netreg,
 | 
			
		||||
				ofono_netreg_strength_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_SIGNAL_STATE,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	mbim_message_set_arguments(message, "");
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(nd->device, NETREG_GROUP, message,
 | 
			
		||||
				mbim_signal_state_query_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_signal_state_changed(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_netreg *netreg = user;
 | 
			
		||||
	uint32_t strength;
 | 
			
		||||
	uint32_t error_rate;
 | 
			
		||||
	uint32_t signal_strength_interval;
 | 
			
		||||
	uint32_t rssi_threshold;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uuuu",
 | 
			
		||||
						&strength, &error_rate,
 | 
			
		||||
						&signal_strength_interval,
 | 
			
		||||
						&rssi_threshold))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	DBG("strength: %u, error_rate: %u", strength, error_rate);
 | 
			
		||||
	DBG("strength interval: %u, rssi_threshold: %u",
 | 
			
		||||
				signal_strength_interval, rssi_threshold);
 | 
			
		||||
 | 
			
		||||
	ofono_netreg_strength_notify(netreg, convert_signal_strength(strength));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void delayed_register(struct l_idle *idle, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_netreg *netreg = user_data;
 | 
			
		||||
	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	l_idle_remove(idle);
 | 
			
		||||
	nd->delayed_register = NULL;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_register(nd->device, NETREG_GROUP,
 | 
			
		||||
					mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_SIGNAL_STATE,
 | 
			
		||||
					mbim_signal_state_changed,
 | 
			
		||||
					netreg, NULL))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_register(nd->device, NETREG_GROUP,
 | 
			
		||||
					mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_REGISTER_STATE,
 | 
			
		||||
					mbim_register_state_changed,
 | 
			
		||||
					netreg, NULL))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	ofono_netreg_register(netreg);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ofono_netreg_remove(netreg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mbim_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
 | 
			
		||||
					void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mbim_device *device = data;
 | 
			
		||||
	struct netreg_data *nd = l_new(struct netreg_data, 1);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	nd->device = mbim_device_ref(device);
 | 
			
		||||
	nd->delayed_register = l_idle_create(delayed_register, netreg, NULL);
 | 
			
		||||
 | 
			
		||||
	ofono_netreg_set_data(netreg, nd);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_netreg_remove(struct ofono_netreg *netreg)
 | 
			
		||||
{
 | 
			
		||||
	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	ofono_netreg_set_data(netreg, NULL);
 | 
			
		||||
 | 
			
		||||
	l_idle_remove(nd->delayed_register);
 | 
			
		||||
	mbim_device_cancel_group(nd->device, NETREG_GROUP);
 | 
			
		||||
	mbim_device_unregister_group(nd->device, NETREG_GROUP);
 | 
			
		||||
	mbim_device_unref(nd->device);
 | 
			
		||||
	nd->device = NULL;
 | 
			
		||||
	l_free(nd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_netreg_driver driver = {
 | 
			
		||||
	.name				= "mbim",
 | 
			
		||||
	.probe				= mbim_netreg_probe,
 | 
			
		||||
	.remove				= mbim_netreg_remove,
 | 
			
		||||
	.registration_status		= mbim_registration_status,
 | 
			
		||||
	.current_operator		= mbim_current_operator,
 | 
			
		||||
	.register_auto			= mbim_register_auto,
 | 
			
		||||
	.strength			= mbim_signal_strength,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mbim_netreg_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_netreg_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mbim_netreg_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_netreg_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										533
									
								
								ofono/drivers/mbimmodem/sim.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										533
									
								
								ofono/drivers/mbimmodem/sim.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,533 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/sim.h>
 | 
			
		||||
 | 
			
		||||
#include "drivers/mbimmodem/mbim.h"
 | 
			
		||||
#include "drivers/mbimmodem/mbim-message.h"
 | 
			
		||||
#include "drivers/mbimmodem/mbimmodem.h"
 | 
			
		||||
 | 
			
		||||
struct sim_data {
 | 
			
		||||
	struct mbim_device *device;
 | 
			
		||||
	char *iccid;
 | 
			
		||||
	char *imsi;
 | 
			
		||||
	uint32_t last_pin_type;
 | 
			
		||||
	bool present : 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void mbim_sim_state_changed(struct ofono_sim *sim, uint32_t ready_state)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
 | 
			
		||||
	DBG("ready_state: %u", ready_state);
 | 
			
		||||
 | 
			
		||||
	switch (ready_state) {
 | 
			
		||||
	case 0: /* Not Initialized */
 | 
			
		||||
		break;
 | 
			
		||||
	case 1: /* Initialized */
 | 
			
		||||
		if (!sd->present)
 | 
			
		||||
			ofono_sim_inserted_notify(sim, true);
 | 
			
		||||
 | 
			
		||||
		sd->present = true;
 | 
			
		||||
		ofono_sim_initialized_notify(sim);
 | 
			
		||||
		break;
 | 
			
		||||
	case 6: /* Device Locked */
 | 
			
		||||
		if (!sd->present)
 | 
			
		||||
			ofono_sim_inserted_notify(sim, true);
 | 
			
		||||
 | 
			
		||||
		sd->present = true;
 | 
			
		||||
		break;
 | 
			
		||||
	case 2: /* Not inserted */
 | 
			
		||||
	case 3: /* Bad SIM */
 | 
			
		||||
	case 4: /* Failure */
 | 
			
		||||
	case 5: /* Not activated */
 | 
			
		||||
		if (sd->present)
 | 
			
		||||
			ofono_sim_inserted_notify(sim, false);
 | 
			
		||||
 | 
			
		||||
		sd->present = false;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_read_imsi(struct ofono_sim *sim,
 | 
			
		||||
				ofono_sim_imsi_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, sd->imsi, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum ofono_sim_password_type mbim_pin_type_to_sim_password(
 | 
			
		||||
							uint32_t pin_type)
 | 
			
		||||
{
 | 
			
		||||
	switch (pin_type) {
 | 
			
		||||
	case 0:  /* No Pin */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_NONE;
 | 
			
		||||
	case 2: /* PIN1 key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_SIM_PIN;
 | 
			
		||||
	case 3: /* PIN2 key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_SIM_PIN2;
 | 
			
		||||
	case 4: /* device to SIM key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHSIM_PIN;
 | 
			
		||||
	case 5: /* device to very first SIM key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHFSIM_PIN;
 | 
			
		||||
	case 6: /* network personalization key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHNET_PIN;
 | 
			
		||||
	case 7: /* network subset personalization key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHNETSUB_PIN;
 | 
			
		||||
	case 8: /* service provider (SP) personalization key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHSP_PIN;
 | 
			
		||||
	case 9: /* corporate personalization key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHCORP_PIN;
 | 
			
		||||
	case 11: /* PUK1 */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_SIM_PUK;
 | 
			
		||||
	case 12: /* PUK2 */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_SIM_PUK2;
 | 
			
		||||
	case 13: /* device to very first SIM PIN unlock key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHFSIM_PUK;
 | 
			
		||||
	case 14: /* network personalization unlock key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHNET_PUK;
 | 
			
		||||
	case 15: /* network subset personaliation unlock key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHNETSUB_PUK;
 | 
			
		||||
	case 16: /* service provider (SP) personalization unlock key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHSP_PUK;
 | 
			
		||||
	case 17: /* corporate personalization unlock key */
 | 
			
		||||
		return OFONO_SIM_PASSWORD_PHCORP_PUK;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return OFONO_SIM_PASSWORD_INVALID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t mbim_pin_type_from_sim_password(
 | 
			
		||||
					enum ofono_sim_password_type type)
 | 
			
		||||
{
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case OFONO_SIM_PASSWORD_SIM_PIN:
 | 
			
		||||
		return 2; /* PIN1 key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_SIM_PIN2:
 | 
			
		||||
		return 3; /* PIN2 key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHSIM_PIN:
 | 
			
		||||
		return 4; /* device to SIM key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHFSIM_PIN:
 | 
			
		||||
		return 5; /* device to very first SIM key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHNET_PIN:
 | 
			
		||||
		return 6; /* network personalization key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHNETSUB_PIN:
 | 
			
		||||
		return 7; /* network subset personalization key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHSP_PIN:
 | 
			
		||||
		return 8; /* service provider (SP) personalization key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHCORP_PIN:
 | 
			
		||||
		return 9; /* corporate personalization key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_SIM_PUK:
 | 
			
		||||
		return 11; /* PUK1 */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_SIM_PUK2:
 | 
			
		||||
		return 12; /* PUK2 */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHFSIM_PUK:
 | 
			
		||||
		return 13; /* device to very first SIM PIN unlock key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHNET_PUK:
 | 
			
		||||
		return 14; /* network personalization unlock key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHNETSUB_PUK:
 | 
			
		||||
		return 15; /* network subset personaliation unlock key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHSP_PUK:
 | 
			
		||||
		return 16; /* service provider (SP) personalization unlock key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_PHCORP_PUK:
 | 
			
		||||
		return 17; /* corporate personalization unlock key */
 | 
			
		||||
	case OFONO_SIM_PASSWORD_NONE:
 | 
			
		||||
	case OFONO_SIM_PASSWORD_INVALID:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_pin_query_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	struct sim_data *sd = cbd->user;
 | 
			
		||||
	ofono_sim_passwd_cb_t cb = cbd->cb;
 | 
			
		||||
	uint32_t pin_type;
 | 
			
		||||
	uint32_t pin_state;
 | 
			
		||||
	enum ofono_sim_password_type sim_password;
 | 
			
		||||
	bool r;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	r = mbim_message_get_arguments(message, "uu",
 | 
			
		||||
					&pin_type, &pin_state);
 | 
			
		||||
	if (!r)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	sim_password = mbim_pin_type_to_sim_password(pin_type);
 | 
			
		||||
	if (sim_password == OFONO_SIM_PASSWORD_INVALID)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (pin_state == 0)
 | 
			
		||||
		sim_password = OFONO_SIM_PASSWORD_NONE;
 | 
			
		||||
 | 
			
		||||
	sd->last_pin_type = pin_type;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, sim_password, cbd->data);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_pin_query(struct ofono_sim *sim,
 | 
			
		||||
				ofono_sim_passwd_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	cbd->user = sd;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_PIN,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	mbim_message_set_arguments(message, "");
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(sd->device, SIM_GROUP, message,
 | 
			
		||||
				mbim_pin_query_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_pin_retries_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	ofono_sim_pin_retries_cb_t cb = cbd->cb;
 | 
			
		||||
	int retries[OFONO_SIM_PASSWORD_INVALID];
 | 
			
		||||
	size_t i;
 | 
			
		||||
	uint32_t pin_type;
 | 
			
		||||
	uint32_t pin_state;
 | 
			
		||||
	uint32_t remaining;
 | 
			
		||||
	enum ofono_sim_password_type sim_password;
 | 
			
		||||
	bool r;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	r = mbim_message_get_arguments(message, "uuu",
 | 
			
		||||
					&pin_type, &pin_state, &remaining);
 | 
			
		||||
	if (!r)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	sim_password = mbim_pin_type_to_sim_password(pin_type);
 | 
			
		||||
	if (sim_password == OFONO_SIM_PASSWORD_INVALID)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
 | 
			
		||||
		retries[i] = -1;
 | 
			
		||||
 | 
			
		||||
	if (pin_state == 0 || sim_password == OFONO_SIM_PASSWORD_NONE) {
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, retries, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (remaining == 0xffffffff)
 | 
			
		||||
		retries[sim_password] = -1;
 | 
			
		||||
	else
 | 
			
		||||
		retries[sim_password] = remaining;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, retries, cbd->data);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_pin_retries_query(struct ofono_sim *sim,
 | 
			
		||||
				ofono_sim_pin_retries_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_PIN,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	mbim_message_set_arguments(message, "");
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(sd->device, SIM_GROUP, message,
 | 
			
		||||
				mbim_pin_retries_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_pin_set_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
	else
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_pin_set(struct ofono_sim *sim, uint32_t pin_type,
 | 
			
		||||
						uint32_t pin_operation,
 | 
			
		||||
						const char *old_passwd,
 | 
			
		||||
						const char *new_passwd,
 | 
			
		||||
						ofono_sim_lock_unlock_cb_t cb,
 | 
			
		||||
						void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	DBG("%u %u %s %s", pin_type, pin_operation, old_passwd, new_passwd);
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_PIN,
 | 
			
		||||
					MBIM_COMMAND_TYPE_SET);
 | 
			
		||||
	mbim_message_set_arguments(message, "uuss", pin_type, pin_operation,
 | 
			
		||||
					old_passwd, new_passwd);
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(sd->device, SIM_GROUP, message,
 | 
			
		||||
				mbim_pin_set_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_pin_enter(struct ofono_sim *sim, const char *passwd,
 | 
			
		||||
				ofono_sim_lock_unlock_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
 | 
			
		||||
	/* Use MBIMPinOperationEnter (0) and NULL second PIN */
 | 
			
		||||
	mbim_pin_set(sim, sd->last_pin_type, 0, passwd, NULL, cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_puk_enter(struct ofono_sim *sim, const char *puk,
 | 
			
		||||
				const char *passwd,
 | 
			
		||||
				ofono_sim_lock_unlock_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
 | 
			
		||||
	/* Use MBIMPinOperationEnter (0) and second PIN */
 | 
			
		||||
	mbim_pin_set(sim, sd->last_pin_type, 0, puk, passwd, cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_pin_enable(struct ofono_sim *sim,
 | 
			
		||||
				enum ofono_sim_password_type passwd_type,
 | 
			
		||||
				int enable, const char *passwd,
 | 
			
		||||
				ofono_sim_lock_unlock_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t pin_type = mbim_pin_type_from_sim_password(passwd_type);
 | 
			
		||||
 | 
			
		||||
	if (pin_type == 0) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Use MBIMPinOperationEnable (1) or MBIMPinOperationDisable (2) */
 | 
			
		||||
	mbim_pin_set(sim, pin_type, enable ? 1 : 2, passwd, NULL, cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_pin_change(struct ofono_sim *sim,
 | 
			
		||||
				enum ofono_sim_password_type passwd_type,
 | 
			
		||||
				const char *old_passwd, const char *new_passwd,
 | 
			
		||||
				ofono_sim_lock_unlock_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t pin_type = mbim_pin_type_from_sim_password(passwd_type);
 | 
			
		||||
 | 
			
		||||
	if (pin_type == 0) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Use MBIMPinOperationChange (3) */
 | 
			
		||||
	mbim_pin_set(sim, pin_type, 3, old_passwd, new_passwd, cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_subscriber_ready_status_changed(struct mbim_message *message,
 | 
			
		||||
								void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sim *sim = user;
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	uint32_t ready_state;
 | 
			
		||||
	char *imsi;
 | 
			
		||||
	char *iccid;
 | 
			
		||||
	uint32_t ready_info;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "ussu",
 | 
			
		||||
					&ready_state, &imsi,
 | 
			
		||||
					&iccid, &ready_info))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(sd->iccid);
 | 
			
		||||
	sd->iccid = iccid;
 | 
			
		||||
 | 
			
		||||
	l_free(sd->imsi);
 | 
			
		||||
	sd->imsi = imsi;
 | 
			
		||||
 | 
			
		||||
	DBG("%s %s", iccid, imsi);
 | 
			
		||||
 | 
			
		||||
	mbim_sim_state_changed(sim, ready_state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_subscriber_ready_status_cb(struct mbim_message *message,
 | 
			
		||||
								void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sim *sim = user;
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
	uint32_t ready_state;
 | 
			
		||||
	char *imsi;
 | 
			
		||||
	char *iccid;
 | 
			
		||||
	uint32_t ready_info;
 | 
			
		||||
	bool r;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	/* We don't bother parsing MSISDN/MDN array */
 | 
			
		||||
	r = mbim_message_get_arguments(message, "ussu",
 | 
			
		||||
					&ready_state, &imsi,
 | 
			
		||||
					&iccid, &ready_info);
 | 
			
		||||
	if (!r)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	sd->iccid = iccid;
 | 
			
		||||
	sd->imsi = imsi;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_register(sd->device, SIM_GROUP,
 | 
			
		||||
					mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_SUBSCRIBER_READY_STATUS,
 | 
			
		||||
					mbim_subscriber_ready_status_changed,
 | 
			
		||||
					sim, NULL))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	ofono_sim_register(sim);
 | 
			
		||||
	DBG("%s %s", iccid, imsi);
 | 
			
		||||
	mbim_sim_state_changed(sim, ready_state);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ofono_sim_remove(sim);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mbim_sim_probe(struct ofono_sim *sim, unsigned int vendor,
 | 
			
		||||
				void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mbim_device *device = data;
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
	struct sim_data *sd;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_basic_connect,
 | 
			
		||||
					MBIM_CID_SUBSCRIBER_READY_STATUS,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	if (!message)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	mbim_message_set_arguments(message, "");
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_send(device, SIM_GROUP, message,
 | 
			
		||||
				mbim_subscriber_ready_status_cb, sim, NULL)) {
 | 
			
		||||
		mbim_message_unref(message);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sd = l_new(struct sim_data, 1);
 | 
			
		||||
	sd->device = mbim_device_ref(device);
 | 
			
		||||
	ofono_sim_set_data(sim, sd);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sim_remove(struct ofono_sim *sim)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *sd = ofono_sim_get_data(sim);
 | 
			
		||||
 | 
			
		||||
	ofono_sim_set_data(sim, NULL);
 | 
			
		||||
 | 
			
		||||
	mbim_device_cancel_group(sd->device, SIM_GROUP);
 | 
			
		||||
	mbim_device_unregister_group(sd->device, SIM_GROUP);
 | 
			
		||||
	mbim_device_unref(sd->device);
 | 
			
		||||
	sd->device = NULL;
 | 
			
		||||
 | 
			
		||||
	l_free(sd->iccid);
 | 
			
		||||
	l_free(sd->imsi);
 | 
			
		||||
	l_free(sd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_sim_driver driver = {
 | 
			
		||||
	.name			= "mbim",
 | 
			
		||||
	.probe			= mbim_sim_probe,
 | 
			
		||||
	.remove			= mbim_sim_remove,
 | 
			
		||||
	.read_imsi		= mbim_read_imsi,
 | 
			
		||||
	.query_passwd_state	= mbim_pin_query,
 | 
			
		||||
	.query_pin_retries	= mbim_pin_retries_query,
 | 
			
		||||
	.send_passwd		= mbim_pin_enter,
 | 
			
		||||
	.reset_passwd		= mbim_puk_enter,
 | 
			
		||||
	.change_passwd		= mbim_pin_change,
 | 
			
		||||
	.lock			= mbim_pin_enable,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mbim_sim_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_sim_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mbim_sim_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_sim_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										516
									
								
								ofono/drivers/mbimmodem/sms.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										516
									
								
								ofono/drivers/mbimmodem/sms.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,516 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/sms.h>
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
#include "drivers/mbimmodem/mbim.h"
 | 
			
		||||
#include "drivers/mbimmodem/mbim-message.h"
 | 
			
		||||
#include "drivers/mbimmodem/mbimmodem.h"
 | 
			
		||||
 | 
			
		||||
struct sms_data {
 | 
			
		||||
	struct mbim_device *device;
 | 
			
		||||
	uint32_t configuration_notify_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void mbim_sca_set_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	ofono_sms_sca_set_cb_t cb = cbd->cb;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
	else
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sca_set(struct ofono_sms *sms,
 | 
			
		||||
			const struct ofono_phone_number *sca,
 | 
			
		||||
			ofono_sms_sca_set_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sms_data *sd = ofono_sms_get_data(sms);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
	const char *numberstr = phone_number_to_string(sca);
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_sms,
 | 
			
		||||
					MBIM_CID_SMS_CONFIGURATION,
 | 
			
		||||
					MBIM_COMMAND_TYPE_SET);
 | 
			
		||||
	mbim_message_set_arguments(message, "us", 0, numberstr);
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(sd->device, SMS_GROUP, message,
 | 
			
		||||
				mbim_sca_set_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sca_query_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	ofono_sms_sca_query_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_phone_number sca;
 | 
			
		||||
	uint32_t dummy;
 | 
			
		||||
	L_AUTO_FREE_VAR(char *, number) = NULL;
 | 
			
		||||
	const char *p;
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uuuus",
 | 
			
		||||
					&dummy, &dummy, &dummy, &dummy,
 | 
			
		||||
					&number))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (number[0] == '+') {
 | 
			
		||||
		p = number + 1;
 | 
			
		||||
		sca.type = 145;
 | 
			
		||||
	} else {
 | 
			
		||||
		p = number;
 | 
			
		||||
		sca.type = 129;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	strncpy(sca.number, p, OFONO_MAX_PHONE_NUMBER_LENGTH);
 | 
			
		||||
	sca.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, &sca, cbd->data);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sca_query(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb,
 | 
			
		||||
					void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sms_data *sd = ofono_sms_get_data(sms);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_sms,
 | 
			
		||||
					MBIM_CID_SMS_CONFIGURATION,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	mbim_message_set_arguments(message, "");
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(sd->device, SMS_GROUP, message,
 | 
			
		||||
				mbim_sca_query_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_delete_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	DBG("%u", mbim_message_get_error(message));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sms_send_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user;
 | 
			
		||||
	struct sms_data *sd = cbd->user;
 | 
			
		||||
	ofono_sms_submit_cb_t cb = cbd->cb;
 | 
			
		||||
	uint32_t mr;
 | 
			
		||||
	struct mbim_message *delete;
 | 
			
		||||
 | 
			
		||||
	DBG("%u", mbim_message_get_error(message));
 | 
			
		||||
 | 
			
		||||
	if (mbim_message_get_error(message) != 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "u", &mr))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	/* Just in case, send an SMS DELETE command for Sent messages */
 | 
			
		||||
	delete = mbim_message_new(mbim_uuid_sms,
 | 
			
		||||
					MBIM_CID_SMS_DELETE,
 | 
			
		||||
					MBIM_COMMAND_TYPE_SET);
 | 
			
		||||
	mbim_message_set_arguments(delete, "uu", 4, 0);
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_send(sd->device, SMS_GROUP, delete,
 | 
			
		||||
				mbim_delete_cb, NULL, NULL))
 | 
			
		||||
		mbim_message_unref(delete);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, mr, cbd->data);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_submit(struct ofono_sms *sms, const unsigned char *pdu,
 | 
			
		||||
			int pdu_len, int tpdu_len, int mms,
 | 
			
		||||
			ofono_sms_submit_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sms_data *sd = ofono_sms_get_data(sms);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	DBG("pdu_len: %d tpdu_len: %d mms: %d", pdu_len, tpdu_len, mms);
 | 
			
		||||
 | 
			
		||||
	cbd->user = sd;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_sms,
 | 
			
		||||
					MBIM_CID_SMS_SEND,
 | 
			
		||||
					MBIM_COMMAND_TYPE_SET);
 | 
			
		||||
	mbim_message_set_arguments(message, "ud", 0, "ay", pdu_len, pdu);
 | 
			
		||||
 | 
			
		||||
	if (mbim_device_send(sd->device, SMS_GROUP, message,
 | 
			
		||||
				mbim_sms_send_cb, cbd, l_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	l_free(cbd);
 | 
			
		||||
	mbim_message_unref(message);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sms_send_delete(struct sms_data *sd, uint32_t index)
 | 
			
		||||
{
 | 
			
		||||
	struct mbim_message *delete;
 | 
			
		||||
 | 
			
		||||
	DBG("%u", index);
 | 
			
		||||
 | 
			
		||||
	delete = mbim_message_new(mbim_uuid_sms,
 | 
			
		||||
					MBIM_CID_SMS_DELETE,
 | 
			
		||||
					MBIM_COMMAND_TYPE_SET);
 | 
			
		||||
	mbim_message_set_arguments(delete, "uu", 1, index);
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_send(sd->device, SMS_GROUP, delete,
 | 
			
		||||
				mbim_delete_cb, NULL, NULL))
 | 
			
		||||
		mbim_message_unref(delete);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_parse_sms_read_info(struct mbim_message *message,
 | 
			
		||||
							struct ofono_sms *sms)
 | 
			
		||||
{
 | 
			
		||||
	struct sms_data *sd = ofono_sms_get_data(sms);
 | 
			
		||||
	uint32_t format;
 | 
			
		||||
	uint32_t n_sms;
 | 
			
		||||
	struct mbim_message_iter array;
 | 
			
		||||
	struct mbim_message_iter bytes;
 | 
			
		||||
	uint32_t index;
 | 
			
		||||
	uint32_t status;
 | 
			
		||||
	uint32_t pdu_len;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "ua(uuay)",
 | 
			
		||||
						&format, &n_sms, &array))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (format != 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	while (mbim_message_iter_next_entry(&array, &index, &status,
 | 
			
		||||
							&pdu_len, &bytes)) {
 | 
			
		||||
		int i = 0;
 | 
			
		||||
 | 
			
		||||
		/* Ignore Draft (2) and Sent (3) messages */
 | 
			
		||||
		if (status == 0 || status == 1) {
 | 
			
		||||
			uint8_t pdu[176];
 | 
			
		||||
			uint32_t tpdu_len;
 | 
			
		||||
 | 
			
		||||
			while (mbim_message_iter_next_entry(&bytes, pdu + i))
 | 
			
		||||
				i++;
 | 
			
		||||
 | 
			
		||||
			tpdu_len = pdu_len - pdu[0] - 1;
 | 
			
		||||
			ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		mbim_sms_send_delete(sd, index);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sms_read_notify(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sms *sms = user;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	mbim_parse_sms_read_info(message, sms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sms_read_new_query_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sms *sms = user;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	mbim_parse_sms_read_info(message, sms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sms_message_store_status_changed(struct mbim_message *message,
 | 
			
		||||
								void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sms *sms = user;
 | 
			
		||||
	struct sms_data *sd = ofono_sms_get_data(sms);
 | 
			
		||||
	uint32_t flag;
 | 
			
		||||
	uint32_t index;
 | 
			
		||||
	struct mbim_message *read_query;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uu", &flag, &index))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	DBG("%u %u", flag, index);
 | 
			
		||||
 | 
			
		||||
	/* MBIM_SMS_FLAG_NEW_MESSAGE not set */
 | 
			
		||||
	if ((flag & 2) == 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	read_query = mbim_message_new(mbim_uuid_sms,
 | 
			
		||||
					MBIM_CID_SMS_READ,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	if (!read_query)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Query using MBIMSmsFormatPdu(0) and MBIMSmsFlagNew (2) */
 | 
			
		||||
	mbim_message_set_arguments(read_query, "uuu", 0, 2, 0);
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_send(sd->device, SMS_GROUP, read_query,
 | 
			
		||||
				mbim_sms_read_new_query_cb, sms, NULL))
 | 
			
		||||
		mbim_message_unref(read_query);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sms_read_all_query_cb(struct mbim_message *message, void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sms *sms = user;
 | 
			
		||||
	struct sms_data *sd = ofono_sms_get_data(sms);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	mbim_parse_sms_read_info(message, sms);
 | 
			
		||||
 | 
			
		||||
	mbim_device_register(sd->device, SMS_GROUP, mbim_uuid_sms,
 | 
			
		||||
				MBIM_CID_SMS_MESSAGE_STORE_STATUS,
 | 
			
		||||
				mbim_sms_message_store_status_changed,
 | 
			
		||||
				sms, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool mbim_sms_finish_init(struct ofono_sms *sms)
 | 
			
		||||
{
 | 
			
		||||
	struct sms_data *sd = ofono_sms_get_data(sms);
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Class 0 SMS comes via SMS_READ notification, so register for these
 | 
			
		||||
	 * here.  After that we send an SMS_READ request to retrieve any new
 | 
			
		||||
	 * SMS messages.  In the callback we will register to
 | 
			
		||||
	 * MESSAGE_STORE_STATUS to receive notification that new SMS messages
 | 
			
		||||
	 * have arrived
 | 
			
		||||
	 */
 | 
			
		||||
	if (!mbim_device_register(sd->device, SMS_GROUP,
 | 
			
		||||
					mbim_uuid_sms,
 | 
			
		||||
					MBIM_CID_SMS_READ,
 | 
			
		||||
					mbim_sms_read_notify, sms, NULL))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_sms,
 | 
			
		||||
					MBIM_CID_SMS_READ,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	if (!message)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* Query using MBIMSmsFormatPdu(0) and MBIMSmsFlagAll (0) */
 | 
			
		||||
	mbim_message_set_arguments(message, "uuu", 0, 0, 0);
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_send(sd->device, SMS_GROUP, message,
 | 
			
		||||
				mbim_sms_read_all_query_cb, sms, NULL)) {
 | 
			
		||||
		mbim_message_unref(message);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sms_configuration_changed(struct mbim_message *message,
 | 
			
		||||
								void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sms *sms = user;
 | 
			
		||||
	struct sms_data *sd = ofono_sms_get_data(sms);
 | 
			
		||||
	uint32_t storage_state;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "u", &storage_state))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (storage_state != 1)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	mbim_device_unregister(sd->device, sd->configuration_notify_id);
 | 
			
		||||
	sd->configuration_notify_id = 0;
 | 
			
		||||
 | 
			
		||||
	if (!mbim_sms_finish_init(sms))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	ofono_sms_register(sms);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ofono_sms_remove(sms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sms_configuration_query_cb(struct mbim_message *message,
 | 
			
		||||
								void *user)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sms *sms = user;
 | 
			
		||||
	struct sms_data *sd = ofono_sms_get_data(sms);
 | 
			
		||||
	uint32_t error;
 | 
			
		||||
	uint32_t storage_state;
 | 
			
		||||
	uint32_t format;
 | 
			
		||||
	uint32_t max_messages;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	error = mbim_message_get_error(message);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * SUBSCRIBER_READY_STATUS tells us that a SIM is in ReadyState,
 | 
			
		||||
	 * unfortunately that seems to be not enough to know that the SMS
 | 
			
		||||
	 * state is initialized.  Handle this here, if we get an error 14
 | 
			
		||||
	 * 'MBIM_STATUS_NOT_INITIALIZED', then listen for the
 | 
			
		||||
	 * SMS_CONFIGURATION notification.  Why some devices return an error
 | 
			
		||||
	 * here instead of responding with a 0 storage state is a mystery
 | 
			
		||||
	 */
 | 
			
		||||
	switch (error) {
 | 
			
		||||
	case 14: /* Seems SIM ReadyState is sometimes not enough */
 | 
			
		||||
		goto setup_notification;
 | 
			
		||||
	case 0:
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* We don't bother parsing CdmaShortMessageSize or ScAddress array */
 | 
			
		||||
	if (!mbim_message_get_arguments(message, "uuu",
 | 
			
		||||
					&storage_state, &format, &max_messages))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	DBG("storage_state: %u, format: %u, max_messages: %u",
 | 
			
		||||
			storage_state, format, max_messages);
 | 
			
		||||
 | 
			
		||||
	if (format != 0) {
 | 
			
		||||
		DBG("Unsupported SMS Format, expect 0 (PDU)");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (storage_state == 1) {
 | 
			
		||||
		if (!mbim_sms_finish_init(sms))
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		ofono_sms_register(sms);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
setup_notification:
 | 
			
		||||
	/* Wait for storage_state to go to Initialized before registering */
 | 
			
		||||
	sd->configuration_notify_id = mbim_device_register(sd->device,
 | 
			
		||||
						SMS_GROUP,
 | 
			
		||||
						mbim_uuid_sms,
 | 
			
		||||
						MBIM_CID_SMS_CONFIGURATION,
 | 
			
		||||
						mbim_sms_configuration_changed,
 | 
			
		||||
						sms, NULL);
 | 
			
		||||
	if (sd->configuration_notify_id > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ofono_sms_remove(sms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mbim_sms_probe(struct ofono_sms *sms, unsigned int vendor,
 | 
			
		||||
							void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mbim_device *device = data;
 | 
			
		||||
	struct sms_data *sd;
 | 
			
		||||
	struct mbim_message *message;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	message = mbim_message_new(mbim_uuid_sms,
 | 
			
		||||
					MBIM_CID_SMS_CONFIGURATION,
 | 
			
		||||
					MBIM_COMMAND_TYPE_QUERY);
 | 
			
		||||
	if (!message)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	mbim_message_set_arguments(message, "");
 | 
			
		||||
 | 
			
		||||
	if (!mbim_device_send(device, SMS_GROUP, message,
 | 
			
		||||
				mbim_sms_configuration_query_cb, sms, NULL)) {
 | 
			
		||||
		mbim_message_unref(message);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sd = l_new(struct sms_data, 1);
 | 
			
		||||
	sd->device = mbim_device_ref(device);
 | 
			
		||||
	ofono_sms_set_data(sms, sd);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mbim_sms_remove(struct ofono_sms *sms)
 | 
			
		||||
{
 | 
			
		||||
	struct sms_data *sd = ofono_sms_get_data(sms);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	ofono_sms_set_data(sms, NULL);
 | 
			
		||||
 | 
			
		||||
	mbim_device_cancel_group(sd->device, SMS_GROUP);
 | 
			
		||||
	mbim_device_unregister_group(sd->device, SMS_GROUP);
 | 
			
		||||
	mbim_device_unref(sd->device);
 | 
			
		||||
	sd->device = NULL;
 | 
			
		||||
	l_free(sd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_sms_driver driver = {
 | 
			
		||||
	.name		= "mbim",
 | 
			
		||||
	.probe		= mbim_sms_probe,
 | 
			
		||||
	.remove		= mbim_sms_remove,
 | 
			
		||||
	.sca_query	= mbim_sca_query,
 | 
			
		||||
	.sca_set	= mbim_sca_set,
 | 
			
		||||
	.submit		= mbim_submit,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void mbim_sms_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_sms_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mbim_sms_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_sms_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								ofono/drivers/mbimmodem/util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								ofono/drivers/mbimmodem/util.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include "src/common.h"
 | 
			
		||||
#include "mbim.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
int mbim_data_class_to_tech(uint32_t n)
 | 
			
		||||
{
 | 
			
		||||
	if (n & MBIM_DATA_CLASS_LTE)
 | 
			
		||||
		return ACCESS_TECHNOLOGY_EUTRAN;
 | 
			
		||||
 | 
			
		||||
	if (n & (MBIM_DATA_CLASS_HSUPA | MBIM_DATA_CLASS_HSDPA))
 | 
			
		||||
		return ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
 | 
			
		||||
 | 
			
		||||
	if (n & MBIM_DATA_CLASS_HSUPA)
 | 
			
		||||
		return ACCESS_TECHNOLOGY_UTRAN_HSUPA;
 | 
			
		||||
 | 
			
		||||
	if (n & MBIM_DATA_CLASS_HSDPA)
 | 
			
		||||
		return ACCESS_TECHNOLOGY_UTRAN_HSDPA;
 | 
			
		||||
 | 
			
		||||
	if (n & MBIM_DATA_CLASS_UMTS)
 | 
			
		||||
		return ACCESS_TECHNOLOGY_UTRAN;
 | 
			
		||||
 | 
			
		||||
	if (n & MBIM_DATA_CLASS_EDGE)
 | 
			
		||||
		return ACCESS_TECHNOLOGY_GSM_EGPRS;
 | 
			
		||||
 | 
			
		||||
	if (n & MBIM_DATA_CLASS_GPRS)
 | 
			
		||||
		return ACCESS_TECHNOLOGY_GSM;
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										59
									
								
								ofono/drivers/mbimmodem/util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								ofono/drivers/mbimmodem/util.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Intel Corporation. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <ell/ell.h>
 | 
			
		||||
 | 
			
		||||
struct cb_data {
 | 
			
		||||
	void *cb;
 | 
			
		||||
	void *data;
 | 
			
		||||
	void *user;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline struct cb_data *cb_data_new(void *cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *ret;
 | 
			
		||||
 | 
			
		||||
	ret = l_new(struct cb_data, 1);
 | 
			
		||||
	ret->cb = cb;
 | 
			
		||||
	ret->data = data;
 | 
			
		||||
	ret->user = NULL;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CALLBACK_WITH_FAILURE(cb, args...)		\
 | 
			
		||||
	do {						\
 | 
			
		||||
		struct ofono_error cb_e;		\
 | 
			
		||||
		cb_e.type = OFONO_ERROR_TYPE_FAILURE;	\
 | 
			
		||||
		cb_e.error = 0;				\
 | 
			
		||||
							\
 | 
			
		||||
		cb(&cb_e, ##args);			\
 | 
			
		||||
	} while (0)					\
 | 
			
		||||
 | 
			
		||||
#define CALLBACK_WITH_SUCCESS(f, args...)		\
 | 
			
		||||
	do {						\
 | 
			
		||||
		struct ofono_error e;			\
 | 
			
		||||
		e.type = OFONO_ERROR_TYPE_NO_ERROR;	\
 | 
			
		||||
		e.error = 0;				\
 | 
			
		||||
		f(&e, ##args);				\
 | 
			
		||||
	} while (0)
 | 
			
		||||
 | 
			
		||||
int mbim_data_class_to_tech(uint32_t n);
 | 
			
		||||
@@ -23,6 +23,8 @@
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/devinfo.h>
 | 
			
		||||
@@ -125,7 +127,8 @@ static void get_ids_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	str = qmi_result_get_string(result, QMI_DMS_RESULT_ESN);
 | 
			
		||||
	if (!str) {
 | 
			
		||||
	/* Telit qmi modems return a "0" string when ESN is not available. */
 | 
			
		||||
	if (!str || strcmp(str, "0") == 0) {
 | 
			
		||||
		str = qmi_result_get_string(result, QMI_DMS_RESULT_IMEI);
 | 
			
		||||
		if (!str) {
 | 
			
		||||
			CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
 
 | 
			
		||||
@@ -24,18 +24,22 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/gprs-context.h>
 | 
			
		||||
 | 
			
		||||
#include "qmi.h"
 | 
			
		||||
#include "wda.h"
 | 
			
		||||
#include "wds.h"
 | 
			
		||||
 | 
			
		||||
#include "qmimodem.h"
 | 
			
		||||
 | 
			
		||||
struct gprs_context_data {
 | 
			
		||||
	struct qmi_service *wds;
 | 
			
		||||
	struct qmi_service *wda;
 | 
			
		||||
	struct qmi_device *dev;
 | 
			
		||||
	unsigned int active_context;
 | 
			
		||||
	uint32_t pkt_handle;
 | 
			
		||||
};
 | 
			
		||||
@@ -61,8 +65,12 @@ static void pkt_status_notify(struct qmi_result *result, void *user_data)
 | 
			
		||||
 | 
			
		||||
	switch (status->status) {
 | 
			
		||||
	case QMI_WDS_CONN_STATUS_DISCONNECTED:
 | 
			
		||||
		ofono_gprs_context_deactivated(gc, data->active_context);
 | 
			
		||||
		data->active_context = 0;
 | 
			
		||||
		if (data->pkt_handle) {
 | 
			
		||||
			/* The context has been disconnected by the network */
 | 
			
		||||
			ofono_gprs_context_deactivated(gc, data->active_context);
 | 
			
		||||
			data->pkt_handle = 0;
 | 
			
		||||
			data->active_context = 0;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -75,18 +83,68 @@ static void get_settings_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
	struct ofono_modem *modem;
 | 
			
		||||
	const char *interface;
 | 
			
		||||
	uint8_t pdp_type, ip_family;
 | 
			
		||||
	uint32_t ip_addr;
 | 
			
		||||
	struct in_addr addr;
 | 
			
		||||
	char* straddr;
 | 
			
		||||
	char* apn;
 | 
			
		||||
	const char *dns[3] = { NULL, NULL, NULL };
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL))
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	apn = qmi_result_get_string(result, QMI_WDS_RESULT_APN);
 | 
			
		||||
	if (apn) {
 | 
			
		||||
		DBG("APN: %s", apn);
 | 
			
		||||
		g_free(apn);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_get_uint8(result, QMI_WDS_RESULT_PDP_TYPE, &pdp_type))
 | 
			
		||||
		DBG("PDP type %d", pdp_type);
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_get_uint8(result, QMI_WDS_RESULT_IP_FAMILY, &ip_family))
 | 
			
		||||
		DBG("IP family %d", ip_family);
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_get_uint32(result,QMI_WDS_RESULT_IP_ADDRESS, &ip_addr)) {
 | 
			
		||||
		addr.s_addr = htonl(ip_addr);
 | 
			
		||||
		straddr = inet_ntoa(addr);
 | 
			
		||||
		DBG("IP addr: %s", straddr);
 | 
			
		||||
		ofono_gprs_context_set_ipv4_address(gc, straddr, 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_get_uint32(result,QMI_WDS_RESULT_GATEWAY, &ip_addr)) {
 | 
			
		||||
		addr.s_addr = htonl(ip_addr);
 | 
			
		||||
		straddr = inet_ntoa(addr);
 | 
			
		||||
		DBG("Gateway: %s", straddr);
 | 
			
		||||
		ofono_gprs_context_set_ipv4_gateway(gc, straddr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_get_uint32(result,
 | 
			
		||||
				QMI_WDS_RESULT_GATEWAY_NETMASK, &ip_addr)) {
 | 
			
		||||
		addr.s_addr = htonl(ip_addr);
 | 
			
		||||
		straddr = inet_ntoa(addr);
 | 
			
		||||
		DBG("Gateway netmask: %s", straddr);
 | 
			
		||||
		ofono_gprs_context_set_ipv4_netmask(gc, straddr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_get_uint32(result,
 | 
			
		||||
				QMI_WDS_RESULT_PRIMARY_DNS, &ip_addr)) {
 | 
			
		||||
		addr.s_addr = htonl(ip_addr);
 | 
			
		||||
		dns[0] = inet_ntoa(addr);
 | 
			
		||||
		DBG("Primary DNS: %s", dns[0]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_get_uint32(result,
 | 
			
		||||
				QMI_WDS_RESULT_SECONDARY_DNS, &ip_addr)) {
 | 
			
		||||
		addr.s_addr = htonl(ip_addr);
 | 
			
		||||
		dns[1] = inet_ntoa(addr);
 | 
			
		||||
		DBG("Secondary DNS: %s", dns[1]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dns[0])
 | 
			
		||||
		ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	modem = ofono_gprs_context_get_modem(gc);
 | 
			
		||||
	interface = ofono_modem_get_string(modem, "NetworkInterface");
 | 
			
		||||
@@ -94,8 +152,6 @@ done:
 | 
			
		||||
	ofono_gprs_context_set_interface(gc, interface);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void start_net_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
@@ -120,8 +176,12 @@ static void start_net_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
 | 
			
		||||
	data->pkt_handle = handle;
 | 
			
		||||
 | 
			
		||||
	/* Duplicate cbd, the old one will be freed when this method returns */
 | 
			
		||||
	cbd = cb_data_new(cb, cbd->data);
 | 
			
		||||
	cbd->user = gc;
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->wds, QMI_WDS_GET_SETTINGS, NULL,
 | 
			
		||||
					get_settings_cb, cbd, NULL) > 0)
 | 
			
		||||
					get_settings_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	modem = ofono_gprs_context_get_modem(gc);
 | 
			
		||||
@@ -131,12 +191,39 @@ static void start_net_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	data->active_context = 0;
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This function gets called for "automatic" contexts, those which are
 | 
			
		||||
 * not activated via activate_primary.  For these, we will still need
 | 
			
		||||
 * to call start_net in order to get the packet handle for the context.
 | 
			
		||||
 * The process for automatic contexts is essentially identical to that
 | 
			
		||||
 * for others.
 | 
			
		||||
 */
 | 
			
		||||
static void qmi_gprs_read_settings(struct ofono_gprs_context* gc,
 | 
			
		||||
					unsigned int cid,
 | 
			
		||||
					ofono_gprs_context_cb_t cb,
 | 
			
		||||
					void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
 | 
			
		||||
	DBG("cid %u", cid);
 | 
			
		||||
 | 
			
		||||
	data->active_context = cid;
 | 
			
		||||
 | 
			
		||||
	cbd->user = gc;
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->wds, QMI_WDS_START_NET, NULL,
 | 
			
		||||
					start_net_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	data->active_context = 0;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
 | 
			
		||||
@@ -151,6 +238,7 @@ static void qmi_activate_primary(struct ofono_gprs_context *gc,
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
	uint8_t ip_family;
 | 
			
		||||
	uint8_t auth;
 | 
			
		||||
 | 
			
		||||
	DBG("cid %u", ctx->cid);
 | 
			
		||||
 | 
			
		||||
@@ -178,8 +266,31 @@ static void qmi_activate_primary(struct ofono_gprs_context *gc,
 | 
			
		||||
 | 
			
		||||
	qmi_param_append_uint8(param, QMI_WDS_PARAM_IP_FAMILY, ip_family);
 | 
			
		||||
 | 
			
		||||
	switch (ctx->auth_method) {
 | 
			
		||||
	case OFONO_GPRS_AUTH_METHOD_CHAP:
 | 
			
		||||
		auth = QMI_WDS_AUTHENTICATION_CHAP;
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_GPRS_AUTH_METHOD_PAP:
 | 
			
		||||
		auth = QMI_WDS_AUTHENTICATION_PAP;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		auth = QMI_WDS_AUTHENTICATION_NONE;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	qmi_param_append_uint8(param, QMI_WDS_PARAM_AUTHENTICATION_PREFERENCE,
 | 
			
		||||
					auth);
 | 
			
		||||
 | 
			
		||||
	if (ctx->username[0] != '\0')
 | 
			
		||||
		qmi_param_append(param, QMI_WDS_PARAM_USERNAME,
 | 
			
		||||
					strlen(ctx->username), ctx->username);
 | 
			
		||||
 | 
			
		||||
	if (ctx->password[0] != '\0')
 | 
			
		||||
		qmi_param_append(param, QMI_WDS_PARAM_PASSWORD,
 | 
			
		||||
					strlen(ctx->password), ctx->password);
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->wds, QMI_WDS_START_NET, param,
 | 
			
		||||
					start_net_cb, cbd, NULL) > 0)
 | 
			
		||||
					start_net_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
@@ -202,17 +313,19 @@ static void stop_net_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL)) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
		if (cb)
 | 
			
		||||
			CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data->active_context = 0;
 | 
			
		||||
 | 
			
		||||
	data->pkt_handle = 0;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
	if (cb)
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
	else
 | 
			
		||||
		ofono_gprs_context_deactivated(gc, data->active_context);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
	data->active_context = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_deactivate_primary(struct ofono_gprs_context *gc,
 | 
			
		||||
@@ -233,17 +346,26 @@ static void qmi_deactivate_primary(struct ofono_gprs_context *gc,
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->wds, QMI_WDS_STOP_NET, param,
 | 
			
		||||
					stop_net_cb, cbd, NULL) > 0)
 | 
			
		||||
					stop_net_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
	if (cb)
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, user_data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_gprs_context_detach_shutdown(struct ofono_gprs_context *gc,
 | 
			
		||||
						unsigned int cid)
 | 
			
		||||
{
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	qmi_deactivate_primary(gc, cid, NULL, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_wds_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs_context *gc = user_data;
 | 
			
		||||
@@ -263,6 +385,69 @@ static void create_wds_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
					pkt_status_notify, gc, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_data_format_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs_context *gc = user_data;
 | 
			
		||||
	struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
 | 
			
		||||
	uint32_t llproto;
 | 
			
		||||
	enum qmi_device_expected_data_format expected_llproto;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL))
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	if (!qmi_result_get_uint32(result, QMI_WDA_LL_PROTOCOL, &llproto))
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	expected_llproto = qmi_device_get_expected_data_format(data->dev);
 | 
			
		||||
 | 
			
		||||
	if ((llproto == QMI_WDA_DATA_LINK_PROTOCOL_802_3) &&
 | 
			
		||||
			(expected_llproto ==
 | 
			
		||||
				QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP)) {
 | 
			
		||||
		if (!qmi_device_set_expected_data_format(data->dev,
 | 
			
		||||
					QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3))
 | 
			
		||||
			DBG("Fail to set expected data to 802.3");
 | 
			
		||||
		else
 | 
			
		||||
			DBG("expected data set to 802.3");
 | 
			
		||||
	} else if ((llproto == QMI_WDA_DATA_LINK_PROTOCOL_RAW_IP) &&
 | 
			
		||||
			(expected_llproto ==
 | 
			
		||||
				QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3)) {
 | 
			
		||||
		if (!qmi_device_set_expected_data_format(data->dev,
 | 
			
		||||
					QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP))
 | 
			
		||||
			DBG("Fail to set expected data to raw-ip");
 | 
			
		||||
		else
 | 
			
		||||
			DBG("expected data set to raw-ip");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	qmi_service_create_shared(data->dev, QMI_SERVICE_WDS, create_wds_cb, gc,
 | 
			
		||||
									NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_wda_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs_context *gc = user_data;
 | 
			
		||||
	struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!service) {
 | 
			
		||||
		DBG("Failed to request WDA service, continue initialization");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data->wda = qmi_service_ref(service);
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->wda, QMI_WDA_GET_DATA_FORMAT, NULL,
 | 
			
		||||
					get_data_format_cb, gc, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	qmi_service_create_shared(data->dev, QMI_SERVICE_WDS, create_wds_cb, gc,
 | 
			
		||||
									NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qmi_gprs_context_probe(struct ofono_gprs_context *gc,
 | 
			
		||||
					unsigned int vendor, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
@@ -274,8 +459,9 @@ static int qmi_gprs_context_probe(struct ofono_gprs_context *gc,
 | 
			
		||||
	data = g_new0(struct gprs_context_data, 1);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_context_set_data(gc, data);
 | 
			
		||||
	data->dev = device;
 | 
			
		||||
 | 
			
		||||
	qmi_service_create(device, QMI_SERVICE_WDS, create_wds_cb, gc, NULL);
 | 
			
		||||
	qmi_service_create(device, QMI_SERVICE_WDA, create_wda_cb, gc, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -288,9 +474,15 @@ static void qmi_gprs_context_remove(struct ofono_gprs_context *gc)
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_context_set_data(gc, NULL);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unregister_all(data->wds);
 | 
			
		||||
	if (data->wds) {
 | 
			
		||||
		qmi_service_unregister_all(data->wds);
 | 
			
		||||
		qmi_service_unref(data->wds);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	qmi_service_unref(data->wds);
 | 
			
		||||
	if (data->wda) {
 | 
			
		||||
		qmi_service_unregister_all(data->wda);
 | 
			
		||||
		qmi_service_unref(data->wda);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_free(data);
 | 
			
		||||
}
 | 
			
		||||
@@ -301,6 +493,8 @@ static struct ofono_gprs_context_driver driver = {
 | 
			
		||||
	.remove			= qmi_gprs_context_remove,
 | 
			
		||||
	.activate_primary	= qmi_activate_primary,
 | 
			
		||||
	.deactivate_primary	= qmi_deactivate_primary,
 | 
			
		||||
	.read_settings		= qmi_gprs_read_settings,
 | 
			
		||||
	.detach_shutdown	= qmi_gprs_context_detach_shutdown,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void qmi_gprs_context_init(void)
 | 
			
		||||
 
 | 
			
		||||
@@ -29,17 +29,23 @@
 | 
			
		||||
 | 
			
		||||
#include "qmi.h"
 | 
			
		||||
#include "nas.h"
 | 
			
		||||
#include "wds.h"
 | 
			
		||||
 | 
			
		||||
#include "src/common.h"
 | 
			
		||||
#include "qmimodem.h"
 | 
			
		||||
 | 
			
		||||
struct gprs_data {
 | 
			
		||||
	struct qmi_device *dev;
 | 
			
		||||
	struct qmi_service *nas;
 | 
			
		||||
	struct qmi_service *wds;
 | 
			
		||||
	unsigned int last_auto_context_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool extract_ss_info(struct qmi_result *result, int *status)
 | 
			
		||||
static bool extract_ss_info(struct qmi_result *result, int *status, int *tech)
 | 
			
		||||
{
 | 
			
		||||
	const struct qmi_nas_serving_system *ss;
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
@@ -47,14 +53,165 @@ static bool extract_ss_info(struct qmi_result *result, int *status)
 | 
			
		||||
	if (!ss)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (ss->ps_state == QMI_NAS_ATTACH_STATUS_ATTACHED)
 | 
			
		||||
		*status = 0x01;
 | 
			
		||||
	if (ss->ps_state == QMI_NAS_ATTACH_STATE_ATTACHED)
 | 
			
		||||
		*status = NETWORK_REGISTRATION_STATUS_REGISTERED;
 | 
			
		||||
	else
 | 
			
		||||
		*status = 0x00;
 | 
			
		||||
		*status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
 | 
			
		||||
 | 
			
		||||
	*tech = -1;
 | 
			
		||||
	for (i = 0; i < ss->radio_if_count; i++) {
 | 
			
		||||
		DBG("radio in use %d", ss->radio_if[i]);
 | 
			
		||||
 | 
			
		||||
		*tech = qmi_nas_rat_to_tech(ss->radio_if[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_lte_attach_param_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs *gprs = user_data;
 | 
			
		||||
	struct gprs_data *data = ofono_gprs_get_data(gprs);
 | 
			
		||||
	char *apn = NULL;
 | 
			
		||||
	uint16_t error;
 | 
			
		||||
	uint8_t iptype;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, &error)) {
 | 
			
		||||
		ofono_error("Failed to query LTE attach params: %hd", error);
 | 
			
		||||
		goto noapn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* APN */
 | 
			
		||||
	apn = qmi_result_get_string(result, 0x10);
 | 
			
		||||
	if (!apn) {
 | 
			
		||||
		DBG("Default profile has no APN setting");
 | 
			
		||||
		goto noapn;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_get_uint8(result, 0x11, &iptype))
 | 
			
		||||
		ofono_info("LTE attach IP type: %hhd", iptype);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_cid_activated(gprs, data->last_auto_context_id, apn);
 | 
			
		||||
	g_free(apn);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
noapn:
 | 
			
		||||
	data->last_auto_context_id = 0;
 | 
			
		||||
	ofono_error("LTE bearer established but APN not set");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_default_profile_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs* gprs = user_data;
 | 
			
		||||
	struct gprs_data *data = ofono_gprs_get_data(gprs);
 | 
			
		||||
	uint16_t error;
 | 
			
		||||
	uint8_t index;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, &error)) {
 | 
			
		||||
		ofono_error("Get default profile error: %hd", error);
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Profile index */
 | 
			
		||||
	if (!qmi_result_get_uint8(result, 0x01, &index)) {
 | 
			
		||||
		ofono_error("Failed query default profile");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DBG("Default profile index: %hhd", index);
 | 
			
		||||
 | 
			
		||||
	data->last_auto_context_id = index;
 | 
			
		||||
 | 
			
		||||
	/* Get LTE Attach Parameters */
 | 
			
		||||
	if (qmi_service_send(data->wds, 0x85, NULL,
 | 
			
		||||
				get_lte_attach_param_cb, gprs, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	data->last_auto_context_id = 0;
 | 
			
		||||
	ofono_error("LTE bearer established but APN not set");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Query the settings in effect on the default bearer.  These may be
 | 
			
		||||
 * implicit or may even be something other than requested as the gateway
 | 
			
		||||
 * is allowed to override whatever was requested by the user.
 | 
			
		||||
 */
 | 
			
		||||
static void get_lte_attach_params(struct ofono_gprs* gprs)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_data *data = ofono_gprs_get_data(gprs);
 | 
			
		||||
	struct {
 | 
			
		||||
		uint8_t type;
 | 
			
		||||
		uint8_t family;
 | 
			
		||||
	} __attribute((packed)) p = {
 | 
			
		||||
		.type = 0,   /* 3GPP */
 | 
			
		||||
		.family = 0, /* embedded */
 | 
			
		||||
	};
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (data->last_auto_context_id != 0)
 | 
			
		||||
		return; /* Established or in progress */
 | 
			
		||||
 | 
			
		||||
	/* Set query in progress */
 | 
			
		||||
	data->last_auto_context_id = -1;
 | 
			
		||||
 | 
			
		||||
	/* First we query the default profile in order to find out which
 | 
			
		||||
	 * context the modem has activated.
 | 
			
		||||
	 */
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
	if (!param)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	/* Profile type */
 | 
			
		||||
	qmi_param_append(param, 0x1, sizeof(p), &p);
 | 
			
		||||
 | 
			
		||||
	/* Get default profile */
 | 
			
		||||
	if (qmi_service_send(data->wds, 0x49, param,
 | 
			
		||||
				get_default_profile_cb, gprs, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ofono_warn("Unable to query LTE APN... will not activate context");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int handle_ss_info(struct qmi_result *result, struct ofono_gprs *gprs)
 | 
			
		||||
{
 | 
			
		||||
	struct gprs_data *data = ofono_gprs_get_data(gprs);
 | 
			
		||||
	int status;
 | 
			
		||||
	int tech;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!extract_ss_info(result, &status, &tech))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (status == NETWORK_REGISTRATION_STATUS_REGISTERED) {
 | 
			
		||||
		if (tech == ACCESS_TECHNOLOGY_EUTRAN) {
 | 
			
		||||
			/* On LTE we are effectively always attached; and
 | 
			
		||||
			 * the default bearer is established as soon as the
 | 
			
		||||
			 * network is joined.  We just need to query the
 | 
			
		||||
			 * parameters in effect on the default bearer and
 | 
			
		||||
			 * let the ofono core know about the activated
 | 
			
		||||
			 * context.
 | 
			
		||||
			 */
 | 
			
		||||
			get_lte_attach_params(gprs);
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		data->last_auto_context_id = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ss_info_notify(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs *gprs = user_data;
 | 
			
		||||
@@ -62,10 +219,10 @@ static void ss_info_notify(struct qmi_result *result, void *user_data)
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!extract_ss_info(result, &status))
 | 
			
		||||
		return;
 | 
			
		||||
	status = handle_ss_info(result, gprs);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_status_notify(gprs, status);
 | 
			
		||||
	if (status >= 0)
 | 
			
		||||
		ofono_gprs_status_notify(gprs, status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void attach_detach_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
@@ -124,22 +281,26 @@ error:
 | 
			
		||||
static void get_ss_info_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	struct ofono_gprs *gprs = cbd->user;
 | 
			
		||||
	ofono_gprs_status_cb_t cb = cbd->cb;
 | 
			
		||||
	int status;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL)) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (qmi_result_set_error(result, NULL))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (!extract_ss_info(result, &status)) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	status = handle_ss_info(result, gprs);
 | 
			
		||||
 | 
			
		||||
	if (status < 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, status, cbd->data);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_attached_status(struct ofono_gprs *gprs,
 | 
			
		||||
@@ -150,6 +311,7 @@ static void qmi_attached_status(struct ofono_gprs *gprs,
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	cbd->user = gprs;
 | 
			
		||||
	if (qmi_service_send(data->nas, QMI_NAS_GET_SS_INFO, NULL,
 | 
			
		||||
					get_ss_info_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
@@ -159,6 +321,36 @@ static void qmi_attached_status(struct ofono_gprs *gprs,
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_wds_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs *gprs = user_data;
 | 
			
		||||
	struct gprs_data *data = ofono_gprs_get_data(gprs);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!service) {
 | 
			
		||||
		ofono_error("Failed to request WDS service");
 | 
			
		||||
		ofono_gprs_remove(gprs);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data->wds = qmi_service_ref(service);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * First get the SS info - the modem may already be connected,
 | 
			
		||||
	 * and the state-change notification may never arrive
 | 
			
		||||
	 */
 | 
			
		||||
	qmi_service_send(data->nas, QMI_NAS_GET_SS_INFO, NULL,
 | 
			
		||||
					ss_info_notify, gprs, NULL);
 | 
			
		||||
 | 
			
		||||
	qmi_service_register(data->nas, QMI_NAS_SS_INFO_IND,
 | 
			
		||||
					ss_info_notify, gprs, NULL);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_set_cid_range(gprs, 1, 1);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_register(gprs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_nas_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_gprs *gprs = user_data;
 | 
			
		||||
@@ -174,12 +366,8 @@ static void create_nas_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
 | 
			
		||||
	data->nas = qmi_service_ref(service);
 | 
			
		||||
 | 
			
		||||
	qmi_service_register(data->nas, QMI_NAS_SS_INFO_IND,
 | 
			
		||||
					ss_info_notify, gprs, NULL);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_set_cid_range(gprs, 1, 1);
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_register(gprs);
 | 
			
		||||
	qmi_service_create_shared(data->dev, QMI_SERVICE_WDS,
 | 
			
		||||
						create_wds_cb, gprs, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qmi_gprs_probe(struct ofono_gprs *gprs,
 | 
			
		||||
@@ -194,7 +382,10 @@ static int qmi_gprs_probe(struct ofono_gprs *gprs,
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_set_data(gprs, data);
 | 
			
		||||
 | 
			
		||||
	qmi_service_create(device, QMI_SERVICE_NAS, create_nas_cb, gprs, NULL);
 | 
			
		||||
	data->dev = device;
 | 
			
		||||
 | 
			
		||||
	qmi_service_create_shared(device, QMI_SERVICE_NAS,
 | 
			
		||||
						create_nas_cb, gprs, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -207,6 +398,9 @@ static void qmi_gprs_remove(struct ofono_gprs *gprs)
 | 
			
		||||
 | 
			
		||||
	ofono_gprs_set_data(gprs, NULL);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unregister_all(data->wds);
 | 
			
		||||
	qmi_service_unref(data->wds);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unregister_all(data->nas);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unref(data->nas);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										264
									
								
								ofono/drivers/qmimodem/lte.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								ofono/drivers/qmimodem/lte.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,264 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2018  Jonas Bonn. All rights reserved.
 | 
			
		||||
 *  Copyright (C) 2018  Norrbonn AB. All rights reserved.
 | 
			
		||||
 *  Copyright (C) 2018  Data Respons ASA. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/gprs-context.h>
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/lte.h>
 | 
			
		||||
 | 
			
		||||
#include "qmi.h"
 | 
			
		||||
#include "wds.h"
 | 
			
		||||
 | 
			
		||||
#include "qmimodem.h"
 | 
			
		||||
 | 
			
		||||
struct lte_data {
 | 
			
		||||
	struct qmi_service *wds;
 | 
			
		||||
	uint8_t default_profile;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void modify_profile_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_lte_cb_t cb = cbd->cb;
 | 
			
		||||
	uint16_t error;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, &error)) {
 | 
			
		||||
		DBG("Failed to modify profile: %d", error);
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmimodem_lte_set_default_attach_info(const struct ofono_lte *lte,
 | 
			
		||||
			const struct ofono_lte_default_attach_info *info,
 | 
			
		||||
			ofono_lte_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct lte_data *ldd = ofono_lte_get_data(lte);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct qmi_param* param;
 | 
			
		||||
	struct {
 | 
			
		||||
		uint8_t type;
 | 
			
		||||
		uint8_t index;
 | 
			
		||||
	} __attribute__((packed)) p = {
 | 
			
		||||
		.type = 0, /* 3GPP */
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	p.index = ldd->default_profile;
 | 
			
		||||
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
	if (!param)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	/* Profile selector */
 | 
			
		||||
	qmi_param_append(param, 0x01, sizeof(p), &p);
 | 
			
		||||
 | 
			
		||||
	/* WDS APN Name */
 | 
			
		||||
	qmi_param_append(param, QMI_WDS_PARAM_APN,
 | 
			
		||||
				strlen(info->apn), info->apn);
 | 
			
		||||
 | 
			
		||||
	/* Modify profile */
 | 
			
		||||
	if (qmi_service_send(ldd->wds, 0x28, param,
 | 
			
		||||
					modify_profile_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void reset_profile_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_lte *lte = user_data;
 | 
			
		||||
	uint16_t error;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, &error))
 | 
			
		||||
		ofono_error("Reset profile error: %hd", error);
 | 
			
		||||
 | 
			
		||||
	ofono_lte_register(lte);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_default_profile_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_lte *lte = user_data;
 | 
			
		||||
	struct lte_data *ldd = ofono_lte_get_data(lte);
 | 
			
		||||
	uint16_t error;
 | 
			
		||||
	uint8_t index;
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
	struct {
 | 
			
		||||
		uint8_t type;
 | 
			
		||||
		uint8_t index;
 | 
			
		||||
	} __attribute__((packed)) p = {
 | 
			
		||||
		.type = 0, /* 3GPP */
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, &error)) {
 | 
			
		||||
		ofono_error("Get default profile error: %hd", error);
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Profile index */
 | 
			
		||||
	if (!qmi_result_get_uint8(result, 0x01, &index)) {
 | 
			
		||||
		ofono_error("Failed query default profile");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DBG("Default profile index: %hhd", index);
 | 
			
		||||
 | 
			
		||||
	ldd->default_profile = index;
 | 
			
		||||
 | 
			
		||||
	p.index = index;
 | 
			
		||||
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
	if (!param)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	/* Profile selector */
 | 
			
		||||
	qmi_param_append(param, 0x01, sizeof(p), &p);
 | 
			
		||||
 | 
			
		||||
	/* Reset profile */
 | 
			
		||||
	if (qmi_service_send(ldd->wds, 0x4b, param,
 | 
			
		||||
				reset_profile_cb, lte, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ofono_error("Failed to reset profile %hhd", index);
 | 
			
		||||
	ofono_lte_remove(lte);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_wds_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_lte *lte = user_data;
 | 
			
		||||
	struct lte_data *ldd = ofono_lte_get_data(lte);
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
	struct {
 | 
			
		||||
		uint8_t type;
 | 
			
		||||
		uint8_t family;
 | 
			
		||||
	} __attribute((packed)) p = {
 | 
			
		||||
		.type = 0,   /* 3GPP */
 | 
			
		||||
		.family = 0, /* embedded */
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!service) {
 | 
			
		||||
		ofono_error("Failed to request WDS service");
 | 
			
		||||
		ofono_lte_remove(lte);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ldd->wds = qmi_service_ref(service);
 | 
			
		||||
 | 
			
		||||
	/* Query the default profile */
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
	if (!param)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	/* Profile type */
 | 
			
		||||
	qmi_param_append(param, 0x1, sizeof(p), &p);
 | 
			
		||||
 | 
			
		||||
	/* Get default profile */
 | 
			
		||||
	if (qmi_service_send(ldd->wds, 0x49, param,
 | 
			
		||||
				get_default_profile_cb, lte, NULL) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ofono_error("Failed to query default profile");
 | 
			
		||||
	ofono_lte_register(lte);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qmimodem_lte_probe(struct ofono_lte *lte, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct qmi_device *device = data;
 | 
			
		||||
	struct lte_data *ldd;
 | 
			
		||||
 | 
			
		||||
	DBG("qmimodem lte probe");
 | 
			
		||||
 | 
			
		||||
	ldd = g_try_new0(struct lte_data, 1);
 | 
			
		||||
	if (!ldd)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	ofono_lte_set_data(lte, ldd);
 | 
			
		||||
 | 
			
		||||
	qmi_service_create_shared(device, QMI_SERVICE_WDS,
 | 
			
		||||
					create_wds_cb, lte, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmimodem_lte_remove(struct ofono_lte *lte)
 | 
			
		||||
{
 | 
			
		||||
	struct lte_data *ldd = ofono_lte_get_data(lte);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	ofono_lte_set_data(lte, NULL);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unregister_all(ldd->wds);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unref(ldd->wds);
 | 
			
		||||
 | 
			
		||||
	g_free(ldd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_lte_driver driver = {
 | 
			
		||||
	.name				= "qmimodem",
 | 
			
		||||
	.probe				= qmimodem_lte_probe,
 | 
			
		||||
	.remove				= qmimodem_lte_remove,
 | 
			
		||||
	.set_default_attach_info	= qmimodem_lte_set_default_attach_info,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void qmi_lte_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_lte_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qmi_lte_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_lte_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								ofono/drivers/qmimodem/nas.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								ofono/drivers/qmimodem/nas.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Jonas Bonn. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "nas.h"
 | 
			
		||||
 | 
			
		||||
#include "src/common.h"
 | 
			
		||||
 | 
			
		||||
int qmi_nas_rat_to_tech(uint8_t rat)
 | 
			
		||||
{
 | 
			
		||||
	switch (rat) {
 | 
			
		||||
	case QMI_NAS_NETWORK_RAT_GSM:
 | 
			
		||||
		return ACCESS_TECHNOLOGY_GSM;
 | 
			
		||||
	case QMI_NAS_NETWORK_RAT_UMTS:
 | 
			
		||||
		return ACCESS_TECHNOLOGY_UTRAN;
 | 
			
		||||
	case QMI_NAS_NETWORK_RAT_LTE:
 | 
			
		||||
		return ACCESS_TECHNOLOGY_EUTRAN;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
@@ -19,6 +19,8 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#define QMI_NAS_RESET			0	/* Reset NAS service state variables */
 | 
			
		||||
#define QMI_NAS_ABORT			1	/* Abort previously issued NAS command */
 | 
			
		||||
#define QMI_NAS_EVENT			2	/* Connection state report indication */
 | 
			
		||||
@@ -33,6 +35,8 @@
 | 
			
		||||
#define QMI_NAS_SS_INFO_IND		36	/* Current serving system info indication */
 | 
			
		||||
#define QMI_NAS_GET_HOME_INFO		37	/* Get info about home network */
 | 
			
		||||
 | 
			
		||||
#define QMI_NAS_SET_SYSTEM_SELECTION_PREF 51
 | 
			
		||||
#define QMI_NAS_GET_SYSTEM_SELECTION_PREF 52
 | 
			
		||||
 | 
			
		||||
/* Set NAS state report conditions */
 | 
			
		||||
#define QMI_NAS_PARAM_REPORT_SIGNAL_STRENGTH	0x10
 | 
			
		||||
@@ -63,7 +67,7 @@ struct qmi_nas_rf_info {
 | 
			
		||||
} __attribute__((__packed__));
 | 
			
		||||
 | 
			
		||||
/* Get the signal strength */
 | 
			
		||||
#define QMI_NAS_RESULT_SIGNAL_STRENGTH		0x10
 | 
			
		||||
#define QMI_NAS_RESULT_SIGNAL_STRENGTH		0x01
 | 
			
		||||
 | 
			
		||||
/* Scan for visible network */
 | 
			
		||||
#define QMI_NAS_PARAM_NETWORK_MASK		0x10	/* uint8 bitmask */
 | 
			
		||||
@@ -95,6 +99,7 @@ struct qmi_nas_network_rat {
 | 
			
		||||
	} __attribute__((__packed__)) info[0];
 | 
			
		||||
} __attribute__((__packed__));
 | 
			
		||||
 | 
			
		||||
#define QMI_NAS_NETWORK_RAT_NONE		0x00
 | 
			
		||||
#define QMI_NAS_NETWORK_RAT_GSM			0x04
 | 
			
		||||
#define QMI_NAS_NETWORK_RAT_UMTS		0x05
 | 
			
		||||
#define QMI_NAS_NETWORK_RAT_LTE			0x08
 | 
			
		||||
@@ -140,9 +145,29 @@ struct qmi_nas_current_plmn {
 | 
			
		||||
#define QMI_NAS_RESULT_LOCATION_AREA_CODE	0x1d	/* uint16 */
 | 
			
		||||
#define QMI_NAS_RESULT_CELL_ID			0x1e	/* uint32 */
 | 
			
		||||
 | 
			
		||||
#define QMI_NAS_ATTACH_STATUS_INVALID		0x00
 | 
			
		||||
#define QMI_NAS_ATTACH_STATUS_ATTACHED		0x01
 | 
			
		||||
#define QMI_NAS_ATTACH_STATUS_DETACHED		0x02
 | 
			
		||||
/* qmi_nas_serving_system.status */
 | 
			
		||||
#define QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED	0x00
 | 
			
		||||
#define QMI_NAS_REGISTRATION_STATE_REGISTERED		0x01
 | 
			
		||||
#define QMI_NAS_REGISTRATION_STATE_SEARCHING		0x02
 | 
			
		||||
#define QMI_NAS_REGISTRATION_STATE_DENIED		0x03
 | 
			
		||||
#define QMI_NAS_REGISTRATION_STATE_UNKNOWN		0x04
 | 
			
		||||
 | 
			
		||||
#define QMI_NAS_RESULT_3GGP_DST 0x1b
 | 
			
		||||
#define QMI_NAS_RESULT_3GPP_TIME 0x1c
 | 
			
		||||
struct qmi_nas_3gpp_time {
 | 
			
		||||
	uint16_t year;
 | 
			
		||||
	uint8_t month;
 | 
			
		||||
	uint8_t day;
 | 
			
		||||
	uint8_t hour;
 | 
			
		||||
	uint8_t minute;
 | 
			
		||||
	uint8_t second;
 | 
			
		||||
	uint8_t timezone;
 | 
			
		||||
} __attribute__((__packed__));
 | 
			
		||||
 | 
			
		||||
/* cs_state/ps_state */
 | 
			
		||||
#define QMI_NAS_ATTACH_STATE_INVALID		0x00
 | 
			
		||||
#define QMI_NAS_ATTACH_STATE_ATTACHED		0x01
 | 
			
		||||
#define QMI_NAS_ATTACH_STATE_DETACHED		0x02
 | 
			
		||||
 | 
			
		||||
/* Get info about home network */
 | 
			
		||||
#define QMI_NAS_RESULT_HOME_NETWORK		0x01
 | 
			
		||||
@@ -152,3 +177,14 @@ struct qmi_nas_home_network {
 | 
			
		||||
	uint8_t desc_len;
 | 
			
		||||
	char desc[0];
 | 
			
		||||
} __attribute__((__packed__));
 | 
			
		||||
 | 
			
		||||
#define QMI_NAS_RAT_MODE_PREF_ANY		(-1)
 | 
			
		||||
#define QMI_NAS_RAT_MODE_PREF_GSM		(1 << 2)
 | 
			
		||||
#define QMI_NAS_RAT_MODE_PREF_UMTS		(1 << 3)
 | 
			
		||||
#define QMI_NAS_RAT_MODE_PREF_LTE		(1 << 4)
 | 
			
		||||
 | 
			
		||||
#define QMI_NAS_PARAM_SYSTEM_SELECTION_PREF_MODE	0x11
 | 
			
		||||
 | 
			
		||||
#define QMI_NAS_RESULT_SYSTEM_SELECTION_PREF_MODE	0x11
 | 
			
		||||
 | 
			
		||||
int qmi_nas_rat_to_tech(uint8_t rat);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										286
									
								
								ofono/drivers/qmimodem/netmon.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								ofono/drivers/qmimodem/netmon.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,286 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Jonas Bonn. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 *  You should have received a copy of the GNU General Public License
 | 
			
		||||
 *  along with this program; if not, write to the Free Software
 | 
			
		||||
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/netmon.h>
 | 
			
		||||
 | 
			
		||||
#include "qmi.h"
 | 
			
		||||
#include "nas.h"
 | 
			
		||||
 | 
			
		||||
#include "qmimodem.h"
 | 
			
		||||
#include "src/common.h"
 | 
			
		||||
 | 
			
		||||
struct netmon_data {
 | 
			
		||||
	struct qmi_service *nas;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void get_rssi_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	struct ofono_netmon *netmon = cbd->user;
 | 
			
		||||
	ofono_netmon_cb_t cb = cbd->cb;
 | 
			
		||||
	struct {
 | 
			
		||||
		enum ofono_netmon_cell_type type;
 | 
			
		||||
		int rssi;
 | 
			
		||||
		int ber;
 | 
			
		||||
		int rsrq;
 | 
			
		||||
		int rsrp;
 | 
			
		||||
	} props;
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
	int16_t rsrp;
 | 
			
		||||
	const struct {
 | 
			
		||||
		int8_t value;
 | 
			
		||||
		int8_t rat;
 | 
			
		||||
	} __attribute__((__packed__)) *rsrq;
 | 
			
		||||
	const struct {
 | 
			
		||||
		uint16_t count;
 | 
			
		||||
		struct {
 | 
			
		||||
			uint8_t rssi;
 | 
			
		||||
			int8_t rat;
 | 
			
		||||
		} __attribute__((__packed__)) info[0];
 | 
			
		||||
	} __attribute__((__packed__)) *rssi;
 | 
			
		||||
	const struct {
 | 
			
		||||
		uint16_t count;
 | 
			
		||||
		struct {
 | 
			
		||||
			uint16_t rate;
 | 
			
		||||
			int8_t rat;
 | 
			
		||||
		} __attribute__((__packed__)) info[0];
 | 
			
		||||
	} __attribute__((__packed__)) *ber;
 | 
			
		||||
	int i;
 | 
			
		||||
	uint16_t num;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL)) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* RSSI */
 | 
			
		||||
	rssi = qmi_result_get(result, 0x11, &len);
 | 
			
		||||
	num = GUINT16_FROM_LE(rssi->count);
 | 
			
		||||
	if (rssi) {
 | 
			
		||||
		for (i = 0; i < num; i++) {
 | 
			
		||||
			DBG("RSSI: %hhu on RAT %hhd",
 | 
			
		||||
				rssi->info[i].rssi,
 | 
			
		||||
				rssi->info[i].rat);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Get cell type from RSSI info... it will be the same
 | 
			
		||||
		 * for all the other entries
 | 
			
		||||
		 */
 | 
			
		||||
		props.type = qmi_nas_rat_to_tech(rssi->info[0].rat);
 | 
			
		||||
		switch (rssi->info[0].rat) {
 | 
			
		||||
		case QMI_NAS_NETWORK_RAT_GSM:
 | 
			
		||||
			props.type = OFONO_NETMON_CELL_TYPE_GSM;
 | 
			
		||||
			break;
 | 
			
		||||
		case QMI_NAS_NETWORK_RAT_UMTS:
 | 
			
		||||
			props.type = OFONO_NETMON_CELL_TYPE_UMTS;
 | 
			
		||||
			break;
 | 
			
		||||
		case QMI_NAS_NETWORK_RAT_LTE:
 | 
			
		||||
			props.type = OFONO_NETMON_CELL_TYPE_LTE;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			props.type = OFONO_NETMON_CELL_TYPE_GSM;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		props.rssi = (rssi->info[0].rssi + 113) / 2;
 | 
			
		||||
		if (props.rssi > 31) props.rssi = 31;
 | 
			
		||||
		if (props.rssi < 0) props.rssi = 0;
 | 
			
		||||
	} else {
 | 
			
		||||
		props.type = QMI_NAS_NETWORK_RAT_GSM;
 | 
			
		||||
		props.rssi = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Bit error rate */
 | 
			
		||||
	ber = qmi_result_get(result, 0x15, &len);
 | 
			
		||||
	num = GUINT16_FROM_LE(ber->count);
 | 
			
		||||
	if (ber) {
 | 
			
		||||
		for (i = 0; i < ber->count; i++) {
 | 
			
		||||
			DBG("Bit error rate: %hu on RAT %hhd",
 | 
			
		||||
				GUINT16_FROM_LE(ber->info[i].rate),
 | 
			
		||||
				ber->info[i].rat);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		props.ber = GUINT16_FROM_LE(ber->info[0].rate);
 | 
			
		||||
		if (props.ber > 7)
 | 
			
		||||
			props.ber = -1;
 | 
			
		||||
	} else {
 | 
			
		||||
		props.ber = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* LTE RSRQ */
 | 
			
		||||
	rsrq = qmi_result_get(result, 0x16, &len);
 | 
			
		||||
	if (rsrq) {
 | 
			
		||||
		DBG("RSRQ: %hhd on RAT %hhd",
 | 
			
		||||
			rsrq->value,
 | 
			
		||||
			rsrq->rat);
 | 
			
		||||
 | 
			
		||||
		if (rsrq->value == 0) {
 | 
			
		||||
			props.rsrq = -1;
 | 
			
		||||
		} else {
 | 
			
		||||
			props.rsrq = (rsrq->value + 19) * 2;
 | 
			
		||||
			if (props.rsrq > 34) props.rsrq = 34;
 | 
			
		||||
			if (props.rsrq < 0) props.rsrq = 0;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		props.rsrq = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* LTE RSRP */
 | 
			
		||||
	if (qmi_result_get_int16(result, 0x18, &rsrp)) {
 | 
			
		||||
		DBG("Got LTE RSRP: %hd", rsrp);
 | 
			
		||||
 | 
			
		||||
		if (rsrp == 0) {
 | 
			
		||||
			props.rsrp = -1;
 | 
			
		||||
		} else {
 | 
			
		||||
			props.rsrp = rsrp + 140;
 | 
			
		||||
			if (props.rsrp > 97) props.rsrp = 97;
 | 
			
		||||
			if (props.rsrp < 0) props.rsrp = 0;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		props.rsrp = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofono_netmon_serving_cell_notify(netmon,
 | 
			
		||||
				props.type,
 | 
			
		||||
				OFONO_NETMON_INFO_RSSI, props.rssi,
 | 
			
		||||
				OFONO_NETMON_INFO_BER, props.ber,
 | 
			
		||||
				OFONO_NETMON_INFO_RSRQ, props.rsrq,
 | 
			
		||||
				OFONO_NETMON_INFO_RSRP, props.rsrp,
 | 
			
		||||
				OFONO_NETMON_INFO_INVALID);
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_netmon_request_update(struct ofono_netmon *netmon,
 | 
			
		||||
					ofono_netmon_cb_t cb,
 | 
			
		||||
					void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct netmon_data *data = ofono_netmon_get_data(netmon);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	cbd->user = netmon;
 | 
			
		||||
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
	if (!param)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	/* Request all signal strength items: mask=0xff */
 | 
			
		||||
	qmi_param_append_uint16(param, 0x10, 255);
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->nas, QMI_NAS_GET_RSSI, param,
 | 
			
		||||
					get_rssi_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_nas_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_netmon *netmon = user_data;
 | 
			
		||||
	struct netmon_data *nmd = ofono_netmon_get_data(netmon);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!service) {
 | 
			
		||||
		ofono_error("Failed to request NAS service");
 | 
			
		||||
		ofono_netmon_remove(netmon);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nmd->nas = qmi_service_ref(service);
 | 
			
		||||
 | 
			
		||||
	ofono_netmon_register(netmon);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qmi_netmon_probe(struct ofono_netmon *netmon,
 | 
			
		||||
					unsigned int vendor, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct qmi_device *device = user_data;
 | 
			
		||||
	struct netmon_data *nmd;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	nmd = g_new0(struct netmon_data, 1);
 | 
			
		||||
 | 
			
		||||
	ofono_netmon_set_data(netmon, nmd);
 | 
			
		||||
 | 
			
		||||
	qmi_service_create_shared(device, QMI_SERVICE_NAS,
 | 
			
		||||
					create_nas_cb, netmon, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_netmon_remove(struct ofono_netmon *netmon)
 | 
			
		||||
{
 | 
			
		||||
	struct netmon_data *nmd = ofono_netmon_get_data(netmon);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	ofono_netmon_set_data(netmon, NULL);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unregister_all(nmd->nas);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unref(nmd->nas);
 | 
			
		||||
 | 
			
		||||
	g_free(nmd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_netmon_driver driver = {
 | 
			
		||||
	.name			= "qmimodem",
 | 
			
		||||
	.probe			= qmi_netmon_probe,
 | 
			
		||||
	.remove			= qmi_netmon_remove,
 | 
			
		||||
	.request_update		= qmi_netmon_request_update,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void qmi_netmon_init(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_netmon_driver_register(&driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qmi_netmon_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	ofono_netmon_driver_unregister(&driver);
 | 
			
		||||
}
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <endian.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
@@ -43,18 +44,36 @@ struct netreg_data {
 | 
			
		||||
	uint8_t current_rat;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int rat_to_tech(uint8_t rat)
 | 
			
		||||
static bool extract_ss_info_time(
 | 
			
		||||
		struct qmi_result *result,
 | 
			
		||||
		struct ofono_network_time *time)
 | 
			
		||||
{
 | 
			
		||||
	switch (rat) {
 | 
			
		||||
	case QMI_NAS_NETWORK_RAT_GSM:
 | 
			
		||||
		return ACCESS_TECHNOLOGY_GSM;
 | 
			
		||||
	case QMI_NAS_NETWORK_RAT_UMTS:
 | 
			
		||||
		return ACCESS_TECHNOLOGY_UTRAN;
 | 
			
		||||
	case QMI_NAS_NETWORK_RAT_LTE:
 | 
			
		||||
		return ACCESS_TECHNOLOGY_EUTRAN;
 | 
			
		||||
	const struct qmi_nas_3gpp_time *time_3gpp = NULL;
 | 
			
		||||
	uint8_t dst_3gpp;
 | 
			
		||||
	bool dst_3gpp_valid;
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
 | 
			
		||||
	/* parse 3gpp time & dst */
 | 
			
		||||
	dst_3gpp_valid = qmi_result_get_uint8(result, QMI_NAS_RESULT_3GGP_DST,
 | 
			
		||||
						&dst_3gpp);
 | 
			
		||||
 | 
			
		||||
	time_3gpp = qmi_result_get(result, QMI_NAS_RESULT_3GPP_TIME, &len);
 | 
			
		||||
	if (time_3gpp && len == sizeof(struct qmi_nas_3gpp_time) &&
 | 
			
		||||
			dst_3gpp_valid) {
 | 
			
		||||
		time->year = le16toh(time_3gpp->year);
 | 
			
		||||
		time->mon = time_3gpp->month;
 | 
			
		||||
		time->mday = time_3gpp->day;
 | 
			
		||||
		time->hour = time_3gpp->hour;
 | 
			
		||||
		time->min = time_3gpp->minute;
 | 
			
		||||
		time->sec = time_3gpp->second;
 | 
			
		||||
		time->utcoff = time_3gpp->timezone * 15 * 60;
 | 
			
		||||
		time->dst = dst_3gpp;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
	/* TODO: 3gpp2 */
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool extract_ss_info(struct qmi_result *result, int *status,
 | 
			
		||||
@@ -64,7 +83,7 @@ static bool extract_ss_info(struct qmi_result *result, int *status,
 | 
			
		||||
	const struct qmi_nas_serving_system *ss;
 | 
			
		||||
	const struct qmi_nas_current_plmn *plmn;
 | 
			
		||||
	uint8_t i, roaming;
 | 
			
		||||
	uint16_t value16, len;
 | 
			
		||||
	uint16_t value16, len, opname_len;
 | 
			
		||||
	uint32_t value32;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
@@ -82,13 +101,13 @@ static bool extract_ss_info(struct qmi_result *result, int *status,
 | 
			
		||||
	for (i = 0; i < ss->radio_if_count; i++) {
 | 
			
		||||
		DBG("radio in use %d", ss->radio_if[i]);
 | 
			
		||||
 | 
			
		||||
		*tech = rat_to_tech(ss->radio_if[i]);
 | 
			
		||||
		*tech = qmi_nas_rat_to_tech(ss->radio_if[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_get_uint8(result, QMI_NAS_RESULT_ROAMING_STATUS,
 | 
			
		||||
								&roaming)) {
 | 
			
		||||
		if (ss->status == 1 && roaming == 0)
 | 
			
		||||
			*status = 5;
 | 
			
		||||
			*status = NETWORK_REGISTRATION_STATUS_ROAMING;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!operator)
 | 
			
		||||
@@ -100,8 +119,21 @@ static bool extract_ss_info(struct qmi_result *result, int *status,
 | 
			
		||||
						GUINT16_FROM_LE(plmn->mcc));
 | 
			
		||||
		snprintf(operator->mnc, OFONO_MAX_MNC_LENGTH + 1, "%02d",
 | 
			
		||||
						GUINT16_FROM_LE(plmn->mnc));
 | 
			
		||||
		strncpy(operator->name, plmn->desc, plmn->desc_len);
 | 
			
		||||
		operator->name[plmn->desc_len] = '\0';
 | 
			
		||||
		opname_len = plmn->desc_len;
 | 
			
		||||
		if (opname_len > OFONO_MAX_OPERATOR_NAME_LENGTH)
 | 
			
		||||
			opname_len = OFONO_MAX_OPERATOR_NAME_LENGTH;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Telit QMI modems can return non-utf-8 characters in
 | 
			
		||||
		 * plmn-desc. When that happens, libdbus will abort ofono.
 | 
			
		||||
		 * If non-utf-8 characters are detected, use mccmnc string.
 | 
			
		||||
		 */
 | 
			
		||||
		if (g_utf8_validate(plmn->desc, opname_len, NULL)) {
 | 
			
		||||
			strncpy(operator->name, plmn->desc, opname_len);
 | 
			
		||||
			operator->name[opname_len] = '\0';
 | 
			
		||||
		} else
 | 
			
		||||
			snprintf(operator->name, OFONO_MAX_OPERATOR_NAME_LENGTH,
 | 
			
		||||
					"%s%s",	operator->mcc, operator->mnc);
 | 
			
		||||
 | 
			
		||||
		DBG("%s (%s:%s)", operator->name, operator->mcc, operator->mnc);
 | 
			
		||||
	}
 | 
			
		||||
@@ -125,11 +157,15 @@ static bool extract_ss_info(struct qmi_result *result, int *status,
 | 
			
		||||
static void ss_info_notify(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_netreg *netreg = user_data;
 | 
			
		||||
	struct ofono_network_time net_time;
 | 
			
		||||
	struct netreg_data *data = ofono_netreg_get_data(netreg);
 | 
			
		||||
	int status, lac, cellid, tech;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (extract_ss_info_time(result, &net_time))
 | 
			
		||||
		ofono_netreg_time_notify(netreg, &net_time);
 | 
			
		||||
 | 
			
		||||
	if (!extract_ss_info(result, &status, &lac, &cellid, &tech,
 | 
			
		||||
							&data->operator))
 | 
			
		||||
		return;
 | 
			
		||||
@@ -265,7 +301,7 @@ static void scan_nets_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
		DBG("%03d:%02d %d", netrat->info[i].mcc, netrat->info[i].mnc,
 | 
			
		||||
							netrat->info[i].rat);
 | 
			
		||||
 | 
			
		||||
		list[i].tech = rat_to_tech(netrat->info[i].rat);
 | 
			
		||||
		list[i].tech = qmi_nas_rat_to_tech(netrat->info[i].rat);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
@@ -296,6 +332,7 @@ static void register_net_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_netreg_register_cb_t cb = cbd->cb;
 | 
			
		||||
	uint16_t error;
 | 
			
		||||
	int cme_error;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
@@ -305,7 +342,8 @@ static void register_net_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
			goto done;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
		cme_error = qmi_error_to_ofono_cme(error);
 | 
			
		||||
		CALLBACK_WITH_CME_ERROR(cb, cme_error, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -357,7 +395,7 @@ static void qmi_register_manual(struct ofono_netreg *netreg,
 | 
			
		||||
 | 
			
		||||
	info.mcc = atoi(mcc);
 | 
			
		||||
	info.mnc = atoi(mnc);
 | 
			
		||||
	info.rat = data->current_rat;
 | 
			
		||||
	info.rat = QMI_NAS_NETWORK_RAT_NO_CHANGE;
 | 
			
		||||
 | 
			
		||||
	qmi_param_append(param, QMI_NAS_PARAM_REGISTER_MANUAL_INFO,
 | 
			
		||||
						sizeof(info), &info);
 | 
			
		||||
@@ -451,10 +489,11 @@ static void event_notify(struct qmi_result *result, void *user_data)
 | 
			
		||||
        if (ss) {
 | 
			
		||||
		int strength;
 | 
			
		||||
 | 
			
		||||
		DBG("signal with %d dBm on %d", ss->dbm, ss->rat);
 | 
			
		||||
 | 
			
		||||
		strength = dbm_to_strength(ss->dbm);
 | 
			
		||||
 | 
			
		||||
		DBG("signal with %d%%(%d dBm) on %d",
 | 
			
		||||
				strength, ss->dbm, ss->rat);
 | 
			
		||||
 | 
			
		||||
		ofono_netreg_strength_notify(netreg, strength);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -474,10 +513,17 @@ static void event_notify(struct qmi_result *result, void *user_data)
 | 
			
		||||
static void set_event_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_netreg *netreg = user_data;
 | 
			
		||||
	struct netreg_data *data = ofono_netreg_get_data(netreg);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	ofono_netreg_register(netreg);
 | 
			
		||||
 | 
			
		||||
	qmi_service_register(data->nas, QMI_NAS_EVENT,
 | 
			
		||||
					event_notify, netreg, NULL);
 | 
			
		||||
 | 
			
		||||
	qmi_service_register(data->nas, QMI_NAS_SS_INFO_IND,
 | 
			
		||||
					ss_info_notify, netreg, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_nas_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
@@ -499,12 +545,6 @@ static void create_nas_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
 | 
			
		||||
	data->nas = qmi_service_ref(service);
 | 
			
		||||
 | 
			
		||||
	qmi_service_register(data->nas, QMI_NAS_EVENT,
 | 
			
		||||
					event_notify, netreg, NULL);
 | 
			
		||||
 | 
			
		||||
	qmi_service_register(data->nas, QMI_NAS_SS_INFO_IND,
 | 
			
		||||
					ss_info_notify, netreg, NULL);
 | 
			
		||||
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
	if (!param)
 | 
			
		||||
		goto done;
 | 
			
		||||
@@ -543,7 +583,7 @@ static int qmi_netreg_probe(struct ofono_netreg *netreg,
 | 
			
		||||
 | 
			
		||||
	ofono_netreg_set_data(netreg, data);
 | 
			
		||||
 | 
			
		||||
	qmi_service_create(device, QMI_SERVICE_NAS,
 | 
			
		||||
	qmi_service_create_shared(device, QMI_SERVICE_NAS,
 | 
			
		||||
					create_nas_cb, netreg, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,8 @@
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
@@ -33,12 +35,18 @@
 | 
			
		||||
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
 | 
			
		||||
#include "qmi.h"
 | 
			
		||||
#include "ctl.h"
 | 
			
		||||
 | 
			
		||||
typedef void (*qmi_message_func_t)(uint16_t message, uint16_t length,
 | 
			
		||||
					const void *buffer, void *user_data);
 | 
			
		||||
 | 
			
		||||
struct discovery {
 | 
			
		||||
	qmi_destroy_func_t destroy;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct qmi_device {
 | 
			
		||||
	int ref_count;
 | 
			
		||||
	int fd;
 | 
			
		||||
@@ -49,6 +57,7 @@ struct qmi_device {
 | 
			
		||||
	GQueue *req_queue;
 | 
			
		||||
	GQueue *control_queue;
 | 
			
		||||
	GQueue *service_queue;
 | 
			
		||||
	GQueue *discovery_queue;
 | 
			
		||||
	uint8_t next_control_tid;
 | 
			
		||||
	uint16_t next_service_tid;
 | 
			
		||||
	qmi_debug_func_t debug_func;
 | 
			
		||||
@@ -60,6 +69,12 @@ struct qmi_device {
 | 
			
		||||
	uint8_t version_count;
 | 
			
		||||
	GHashTable *service_list;
 | 
			
		||||
	unsigned int release_users;
 | 
			
		||||
	qmi_shutdown_func_t shutdown_func;
 | 
			
		||||
	void *shutdown_user_data;
 | 
			
		||||
	qmi_destroy_func_t shutdown_destroy;
 | 
			
		||||
	guint shutdown_source;
 | 
			
		||||
	bool shutting_down : 1;
 | 
			
		||||
	bool destroyed : 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct qmi_service {
 | 
			
		||||
@@ -209,6 +224,14 @@ static gint __request_compare(gconstpointer a, gconstpointer b)
 | 
			
		||||
	return req->tid - tid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __discovery_free(gpointer data, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct discovery *d = data;
 | 
			
		||||
	qmi_destroy_func_t destroy = d->destroy;
 | 
			
		||||
 | 
			
		||||
	destroy(d);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __notify_free(gpointer data, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct qmi_notify *notify = data;
 | 
			
		||||
@@ -313,8 +336,12 @@ static const char *__service_type_to_string(uint8_t type)
 | 
			
		||||
		return "UIM";
 | 
			
		||||
	case QMI_SERVICE_PBM:
 | 
			
		||||
		return "PBM";
 | 
			
		||||
	case QMI_SERVICE_QCHAT:
 | 
			
		||||
		return "QCHAT";
 | 
			
		||||
	case QMI_SERVICE_RMTFS:
 | 
			
		||||
		return "RMTFS";
 | 
			
		||||
	case QMI_SERVICE_TEST:
 | 
			
		||||
		return "TEST";
 | 
			
		||||
	case QMI_SERVICE_LOC:
 | 
			
		||||
		return "LOC";
 | 
			
		||||
	case QMI_SERVICE_SAR:
 | 
			
		||||
@@ -326,9 +353,21 @@ static const char *__service_type_to_string(uint8_t type)
 | 
			
		||||
	case QMI_SERVICE_TS:
 | 
			
		||||
		return "TS";
 | 
			
		||||
	case QMI_SERVICE_TMD:
 | 
			
		||||
		return "TMS";
 | 
			
		||||
		return "TMD";
 | 
			
		||||
	case QMI_SERVICE_WDA:
 | 
			
		||||
		return "WDA";
 | 
			
		||||
	case QMI_SERVICE_CSVT:
 | 
			
		||||
		return "CSVT";
 | 
			
		||||
	case QMI_SERVICE_COEX:
 | 
			
		||||
		return "COEX";
 | 
			
		||||
	case QMI_SERVICE_PDC:
 | 
			
		||||
		return "PDC";
 | 
			
		||||
	case QMI_SERVICE_RFRPE:
 | 
			
		||||
		return "RFRPE";
 | 
			
		||||
	case QMI_SERVICE_DSD:
 | 
			
		||||
		return "DSD";
 | 
			
		||||
	case QMI_SERVICE_SSCTL:
 | 
			
		||||
		return "SSCTL";
 | 
			
		||||
	case QMI_SERVICE_CAT_OLD:
 | 
			
		||||
		return "CAT";
 | 
			
		||||
	case QMI_SERVICE_RMS:
 | 
			
		||||
@@ -437,6 +476,17 @@ static const char *__error_to_string(uint16_t error)
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qmi_error_to_ofono_cme(int qmi_error) {
 | 
			
		||||
	switch (qmi_error) {
 | 
			
		||||
	case 0x0019:
 | 
			
		||||
		return 4; /* Not Supported */
 | 
			
		||||
	case 0x0052:
 | 
			
		||||
		return 32; /* Access Denied */
 | 
			
		||||
	default:
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __debug_msg(const char dir, const void *buf, size_t len,
 | 
			
		||||
				qmi_debug_func_t function, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
@@ -758,7 +808,7 @@ static void handle_packet(struct qmi_device *device,
 | 
			
		||||
 | 
			
		||||
		tid = GUINT16_FROM_LE(service->transaction);
 | 
			
		||||
 | 
			
		||||
		if (service->type == 0x04 && tid == 0x0000) {
 | 
			
		||||
		if (service->type == 0x04) {
 | 
			
		||||
			handle_indication(device, hdr->service, hdr->client,
 | 
			
		||||
							message, length, data);
 | 
			
		||||
			return;
 | 
			
		||||
@@ -838,6 +888,21 @@ static void read_watch_destroy(gpointer user_data)
 | 
			
		||||
	device->read_watch = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __qmi_device_discovery_started(struct qmi_device *device,
 | 
			
		||||
						struct discovery *d)
 | 
			
		||||
{
 | 
			
		||||
	g_queue_push_tail(device->discovery_queue, d);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __qmi_device_discovery_complete(struct qmi_device *device,
 | 
			
		||||
						struct discovery *d)
 | 
			
		||||
{
 | 
			
		||||
	if (g_queue_remove(device->discovery_queue, d) != TRUE)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	__discovery_free(d, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void service_destroy(gpointer data)
 | 
			
		||||
{
 | 
			
		||||
	struct qmi_service *service = data;
 | 
			
		||||
@@ -891,6 +956,7 @@ struct qmi_device *qmi_device_new(int fd)
 | 
			
		||||
	device->req_queue = g_queue_new();
 | 
			
		||||
	device->control_queue = g_queue_new();
 | 
			
		||||
	device->service_queue = g_queue_new();
 | 
			
		||||
	device->discovery_queue = g_queue_new();
 | 
			
		||||
 | 
			
		||||
	device->service_list = g_hash_table_new_full(g_direct_hash,
 | 
			
		||||
					g_direct_equal, NULL, service_destroy);
 | 
			
		||||
@@ -927,6 +993,9 @@ void qmi_device_unref(struct qmi_device *device)
 | 
			
		||||
	g_queue_foreach(device->req_queue, __request_free, NULL);
 | 
			
		||||
	g_queue_free(device->req_queue);
 | 
			
		||||
 | 
			
		||||
	g_queue_foreach(device->discovery_queue, __discovery_free, NULL);
 | 
			
		||||
	g_queue_free(device->discovery_queue);
 | 
			
		||||
 | 
			
		||||
	if (device->write_watch > 0)
 | 
			
		||||
		g_source_remove(device->write_watch);
 | 
			
		||||
 | 
			
		||||
@@ -936,12 +1005,18 @@ void qmi_device_unref(struct qmi_device *device)
 | 
			
		||||
	if (device->close_on_unref)
 | 
			
		||||
		close(device->fd);
 | 
			
		||||
 | 
			
		||||
	if (device->shutdown_source)
 | 
			
		||||
		g_source_remove(device->shutdown_source);
 | 
			
		||||
 | 
			
		||||
	g_hash_table_destroy(device->service_list);
 | 
			
		||||
 | 
			
		||||
	g_free(device->version_str);
 | 
			
		||||
	g_free(device->version_list);
 | 
			
		||||
 | 
			
		||||
	g_free(device);
 | 
			
		||||
	if (device->shutting_down)
 | 
			
		||||
		device->destroyed = true;
 | 
			
		||||
	else
 | 
			
		||||
		g_free(device);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qmi_device_set_debug(struct qmi_device *device,
 | 
			
		||||
@@ -962,6 +1037,23 @@ void qmi_device_set_close_on_unref(struct qmi_device *device, bool do_close)
 | 
			
		||||
	device->close_on_unref = do_close;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qmi_result_print_tlvs(struct qmi_result *result)
 | 
			
		||||
{
 | 
			
		||||
	const void *ptr = result->data;
 | 
			
		||||
	uint16_t len = result->length;
 | 
			
		||||
 | 
			
		||||
	while (len > QMI_TLV_HDR_SIZE) {
 | 
			
		||||
		const struct qmi_tlv_hdr *tlv = ptr;
 | 
			
		||||
		uint16_t tlv_length = GUINT16_FROM_LE(tlv->length);
 | 
			
		||||
 | 
			
		||||
		DBG("tlv: 0x%02x len 0x%04x", tlv->type, tlv->length);
 | 
			
		||||
 | 
			
		||||
		ptr += QMI_TLV_HDR_SIZE + tlv_length;
 | 
			
		||||
		len -= QMI_TLV_HDR_SIZE + tlv_length;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const void *tlv_get(const void *data, uint16_t size,
 | 
			
		||||
					uint8_t type, uint16_t *length)
 | 
			
		||||
{
 | 
			
		||||
@@ -987,13 +1079,30 @@ static const void *tlv_get(const void *data, uint16_t size,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct discover_data {
 | 
			
		||||
	struct discovery super;
 | 
			
		||||
	struct qmi_device *device;
 | 
			
		||||
	qmi_discover_func_t func;
 | 
			
		||||
	void *user_data;
 | 
			
		||||
	qmi_destroy_func_t destroy;
 | 
			
		||||
	uint8_t tid;
 | 
			
		||||
	guint timeout;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void discover_data_free(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct discover_data *data = user_data;
 | 
			
		||||
 | 
			
		||||
	if (data->timeout) {
 | 
			
		||||
		g_source_remove(data->timeout);
 | 
			
		||||
		data->timeout = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (data->destroy)
 | 
			
		||||
		data->destroy(data->user_data);
 | 
			
		||||
 | 
			
		||||
	g_free(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void discover_callback(uint16_t message, uint16_t length,
 | 
			
		||||
					const void *buffer, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
@@ -1007,8 +1116,6 @@ static void discover_callback(uint16_t message, uint16_t length,
 | 
			
		||||
	uint8_t count;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	g_source_remove(data->timeout);
 | 
			
		||||
 | 
			
		||||
	count = 0;
 | 
			
		||||
	list = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -1038,6 +1145,13 @@ static void discover_callback(uint16_t message, uint16_t length,
 | 
			
		||||
		uint8_t type = service_list->services[i].type;
 | 
			
		||||
		const char *name = __service_type_to_string(type);
 | 
			
		||||
 | 
			
		||||
		if (name)
 | 
			
		||||
			__debug_device(device, "found service [%s %d.%d]",
 | 
			
		||||
				       name, major, minor);
 | 
			
		||||
		else
 | 
			
		||||
			__debug_device(device, "found service [%d %d.%d]",
 | 
			
		||||
				       type, major, minor);
 | 
			
		||||
 | 
			
		||||
		if (type == QMI_SERVICE_CONTROL) {
 | 
			
		||||
			device->control_major = major;
 | 
			
		||||
			device->control_minor = minor;
 | 
			
		||||
@@ -1050,13 +1164,6 @@ static void discover_callback(uint16_t message, uint16_t length,
 | 
			
		||||
		list[count].name = name;
 | 
			
		||||
 | 
			
		||||
		count++;
 | 
			
		||||
 | 
			
		||||
		if (name)
 | 
			
		||||
			__debug_device(device, "found service [%s %d.%d]",
 | 
			
		||||
							name, major, minor);
 | 
			
		||||
		else
 | 
			
		||||
			__debug_device(device, "found service [%d %d.%d]",
 | 
			
		||||
							type, major, minor);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ptr = tlv_get(buffer, length, 0x10, &len);
 | 
			
		||||
@@ -1065,13 +1172,6 @@ static void discover_callback(uint16_t message, uint16_t length,
 | 
			
		||||
 | 
			
		||||
	device->version_str = strndup(ptr + 1, *((uint8_t *) ptr));
 | 
			
		||||
 | 
			
		||||
	service_list = ptr + *((uint8_t *) ptr) + 1;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < service_list->count; i++) {
 | 
			
		||||
		if (service_list->services[i].type == QMI_SERVICE_CONTROL)
 | 
			
		||||
			continue;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	device->version_list = list;
 | 
			
		||||
	device->version_count = count;
 | 
			
		||||
@@ -1079,27 +1179,45 @@ done:
 | 
			
		||||
	if (data->func)
 | 
			
		||||
		data->func(count, list, data->user_data);
 | 
			
		||||
 | 
			
		||||
	if (data->destroy)
 | 
			
		||||
		data->destroy(data->user_data);
 | 
			
		||||
 | 
			
		||||
	g_free(data);
 | 
			
		||||
	__qmi_device_discovery_complete(data->device, &data->super);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean discover_reply(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct discover_data *data = user_data;
 | 
			
		||||
	struct qmi_device *device = data->device;
 | 
			
		||||
	unsigned int tid = data->tid;
 | 
			
		||||
	GList *list;
 | 
			
		||||
	struct qmi_request *req = NULL;
 | 
			
		||||
 | 
			
		||||
	data->timeout = 0;
 | 
			
		||||
 | 
			
		||||
	/* remove request from queues */
 | 
			
		||||
	if (tid != 0) {
 | 
			
		||||
		list = g_queue_find_custom(device->req_queue,
 | 
			
		||||
				GUINT_TO_POINTER(tid), __request_compare);
 | 
			
		||||
 | 
			
		||||
		if (list) {
 | 
			
		||||
			req = list->data;
 | 
			
		||||
			g_queue_delete_link(device->req_queue, list);
 | 
			
		||||
		} else {
 | 
			
		||||
			list = g_queue_find_custom(device->control_queue,
 | 
			
		||||
				GUINT_TO_POINTER(tid), __request_compare);
 | 
			
		||||
 | 
			
		||||
			if (list) {
 | 
			
		||||
				req = list->data;
 | 
			
		||||
				g_queue_delete_link(device->control_queue,
 | 
			
		||||
								list);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (data->func)
 | 
			
		||||
		data->func(device->version_count,
 | 
			
		||||
				device->version_list, data->user_data);
 | 
			
		||||
 | 
			
		||||
	if (data->destroy)
 | 
			
		||||
		data->destroy(data->user_data);
 | 
			
		||||
 | 
			
		||||
	g_free(data);
 | 
			
		||||
	__qmi_device_discovery_complete(data->device, &data->super);
 | 
			
		||||
	__request_free(req, NULL);
 | 
			
		||||
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
@@ -1120,13 +1238,15 @@ bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func,
 | 
			
		||||
	if (!data)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	data->super.destroy = discover_data_free;
 | 
			
		||||
	data->device = device;
 | 
			
		||||
	data->func = func;
 | 
			
		||||
	data->user_data = user_data;
 | 
			
		||||
	data->destroy = destroy;
 | 
			
		||||
 | 
			
		||||
	if (device->version_list) {
 | 
			
		||||
		g_timeout_add_seconds(0, discover_reply, data);
 | 
			
		||||
		data->timeout = g_timeout_add_seconds(0, discover_reply, data);
 | 
			
		||||
		__qmi_device_discovery_started(device, &data->super);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1143,10 +1263,12 @@ bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func,
 | 
			
		||||
 | 
			
		||||
	hdr->type = 0x00;
 | 
			
		||||
	hdr->transaction = device->next_control_tid++;
 | 
			
		||||
	data->tid = hdr->transaction;
 | 
			
		||||
 | 
			
		||||
	__request_submit(device, req, hdr->transaction);
 | 
			
		||||
 | 
			
		||||
	data->timeout = g_timeout_add_seconds(5, discover_reply, data);
 | 
			
		||||
	__qmi_device_discovery_started(device, &data->super);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
@@ -1177,63 +1299,315 @@ static void release_client(struct qmi_device *device,
 | 
			
		||||
	__request_submit(device, req, hdr->transaction);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct shutdown_data {
 | 
			
		||||
	struct qmi_device *device;
 | 
			
		||||
	qmi_shutdown_func_t func;
 | 
			
		||||
	void *user_data;
 | 
			
		||||
	qmi_destroy_func_t destroy;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static gboolean shutdown_reply(gpointer user_data)
 | 
			
		||||
static void shutdown_destroy(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct shutdown_data *data = user_data;
 | 
			
		||||
	struct qmi_device *device = user_data;
 | 
			
		||||
 | 
			
		||||
	if (data->func)
 | 
			
		||||
		data->func(data->user_data);
 | 
			
		||||
	if (device->shutdown_destroy)
 | 
			
		||||
		device->shutdown_destroy(device->shutdown_user_data);
 | 
			
		||||
 | 
			
		||||
	g_free(data);
 | 
			
		||||
	device->shutdown_source = 0;
 | 
			
		||||
 | 
			
		||||
	return FALSE;
 | 
			
		||||
	if (device->destroyed)
 | 
			
		||||
		g_free(device);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean shutdown_timeout(gpointer user_data)
 | 
			
		||||
static gboolean shutdown_callback(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct shutdown_data *data = user_data;
 | 
			
		||||
	struct qmi_device *device = data->device;
 | 
			
		||||
	struct qmi_device *device = user_data;
 | 
			
		||||
 | 
			
		||||
	if (device->release_users > 0)
 | 
			
		||||
		return TRUE;
 | 
			
		||||
 | 
			
		||||
	return shutdown_reply(data);
 | 
			
		||||
	device->shutting_down = true;
 | 
			
		||||
 | 
			
		||||
	if (device->shutdown_func)
 | 
			
		||||
		device->shutdown_func(device->shutdown_user_data);
 | 
			
		||||
 | 
			
		||||
	device->shutting_down = true;
 | 
			
		||||
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool qmi_device_shutdown(struct qmi_device *device, qmi_shutdown_func_t func,
 | 
			
		||||
				void *user_data, qmi_destroy_func_t destroy)
 | 
			
		||||
{
 | 
			
		||||
	struct shutdown_data *data;
 | 
			
		||||
 | 
			
		||||
	if (!device)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (device->shutdown_source > 0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	__debug_device(device, "device %p shutdown", device);
 | 
			
		||||
 | 
			
		||||
	data = g_try_new0(struct shutdown_data, 1);
 | 
			
		||||
	if (!data)
 | 
			
		||||
	device->shutdown_source = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
 | 
			
		||||
						0, shutdown_callback, device,
 | 
			
		||||
						shutdown_destroy);
 | 
			
		||||
	if (device->shutdown_source == 0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	data->device = device;
 | 
			
		||||
	data->func = func;
 | 
			
		||||
	data->user_data = user_data;
 | 
			
		||||
	data->destroy = destroy;
 | 
			
		||||
 | 
			
		||||
	if (device->release_users > 0)
 | 
			
		||||
		g_timeout_add_seconds(0, shutdown_timeout, data);
 | 
			
		||||
	else
 | 
			
		||||
		g_timeout_add_seconds(0, shutdown_reply, data);
 | 
			
		||||
	device->shutdown_func = func;
 | 
			
		||||
	device->shutdown_user_data = user_data;
 | 
			
		||||
	device->shutdown_destroy = destroy;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct sync_data {
 | 
			
		||||
	qmi_sync_func_t func;
 | 
			
		||||
	void *user_data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void qmi_device_sync_callback(uint16_t message, uint16_t length,
 | 
			
		||||
				     const void *buffer, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sync_data *data = user_data;
 | 
			
		||||
 | 
			
		||||
	if (data->func)
 | 
			
		||||
		data->func(data->user_data);
 | 
			
		||||
 | 
			
		||||
	g_free(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* sync will release all previous clients */
 | 
			
		||||
bool qmi_device_sync(struct qmi_device *device,
 | 
			
		||||
		     qmi_sync_func_t func, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct qmi_request *req;
 | 
			
		||||
	struct qmi_control_hdr *hdr;
 | 
			
		||||
	struct sync_data *func_data;
 | 
			
		||||
 | 
			
		||||
	if (!device)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	__debug_device(device, "Sending sync to reset QMI");
 | 
			
		||||
 | 
			
		||||
	func_data = g_new0(struct sync_data, 1);
 | 
			
		||||
	func_data->func = func;
 | 
			
		||||
	func_data->user_data = user_data;
 | 
			
		||||
 | 
			
		||||
	req = __request_alloc(QMI_SERVICE_CONTROL, 0x00,
 | 
			
		||||
			QMI_CTL_SYNC, QMI_CONTROL_HDR_SIZE,
 | 
			
		||||
			NULL, 0,
 | 
			
		||||
			qmi_device_sync_callback, func_data, (void **) &hdr);
 | 
			
		||||
 | 
			
		||||
	if (device->next_control_tid < 1)
 | 
			
		||||
		device->next_control_tid = 1;
 | 
			
		||||
 | 
			
		||||
	hdr->type = 0x00;
 | 
			
		||||
	hdr->transaction = device->next_control_tid++;
 | 
			
		||||
 | 
			
		||||
	__request_submit(device, req, hdr->transaction);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* if the device support the QMI call SYNC over the CTL interface */
 | 
			
		||||
bool qmi_device_is_sync_supported(struct qmi_device *device)
 | 
			
		||||
{
 | 
			
		||||
	if (device == NULL)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return (device->control_major > 1 ||
 | 
			
		||||
		(device->control_major == 1 && device->control_minor >= 5));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool get_device_file_name(struct qmi_device *device,
 | 
			
		||||
					char *file_name, int size)
 | 
			
		||||
{
 | 
			
		||||
	pid_t pid;
 | 
			
		||||
	char temp[100];
 | 
			
		||||
	ssize_t result;
 | 
			
		||||
 | 
			
		||||
	if (size <= 0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	pid = getpid();
 | 
			
		||||
 | 
			
		||||
	snprintf(temp, 100, "/proc/%d/fd/%d", (int) pid, device->fd);
 | 
			
		||||
	temp[99] = 0;
 | 
			
		||||
 | 
			
		||||
	result = readlink(temp, file_name, size - 1);
 | 
			
		||||
 | 
			
		||||
	if (result == -1 || result >= size - 1) {
 | 
			
		||||
		DBG("Error %d in readlink", errno);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	file_name[result] = 0;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *get_first_dir_in_directory(char *dir_path)
 | 
			
		||||
{
 | 
			
		||||
	DIR *dir;
 | 
			
		||||
	struct dirent *dir_entry;
 | 
			
		||||
	char *dir_name = NULL;
 | 
			
		||||
 | 
			
		||||
	dir = opendir(dir_path);
 | 
			
		||||
 | 
			
		||||
	if (!dir)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	dir_entry = readdir(dir);
 | 
			
		||||
 | 
			
		||||
	while ((dir_entry != NULL)) {
 | 
			
		||||
		if (dir_entry->d_type == DT_DIR &&
 | 
			
		||||
				strcmp(dir_entry->d_name, ".") != 0 &&
 | 
			
		||||
				strcmp(dir_entry->d_name, "..") != 0) {
 | 
			
		||||
			dir_name = g_strdup(dir_entry->d_name);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dir_entry = readdir(dir);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	closedir(dir);
 | 
			
		||||
	return dir_name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *get_device_interface(struct qmi_device *device)
 | 
			
		||||
{
 | 
			
		||||
	char * const driver_names[] = { "usbmisc", "usb" };
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	char file_path[PATH_MAX];
 | 
			
		||||
	char *file_name;
 | 
			
		||||
	char *interface = NULL;
 | 
			
		||||
 | 
			
		||||
	if (!get_device_file_name(device, file_path, sizeof(file_path)))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	file_name = basename(file_path);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < G_N_ELEMENTS(driver_names) && !interface; i++) {
 | 
			
		||||
		gchar *sysfs_path;
 | 
			
		||||
 | 
			
		||||
		sysfs_path = g_strdup_printf("/sys/class/%s/%s/device/net/",
 | 
			
		||||
						driver_names[i], file_name);
 | 
			
		||||
		interface = get_first_dir_in_directory(sysfs_path);
 | 
			
		||||
		g_free(sysfs_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return interface;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum qmi_device_expected_data_format qmi_device_get_expected_data_format(
 | 
			
		||||
						struct qmi_device *device)
 | 
			
		||||
{
 | 
			
		||||
	char *sysfs_path = NULL;
 | 
			
		||||
	char *interface = NULL;
 | 
			
		||||
	int fd = -1;
 | 
			
		||||
	char value;
 | 
			
		||||
	enum qmi_device_expected_data_format expected =
 | 
			
		||||
					QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN;
 | 
			
		||||
 | 
			
		||||
	if (!device)
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	interface = get_device_interface(device);
 | 
			
		||||
 | 
			
		||||
	if (!interface) {
 | 
			
		||||
		DBG("Error while getting interface name");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Build sysfs file path and open it */
 | 
			
		||||
	sysfs_path = g_strdup_printf("/sys/class/net/%s/qmi/raw_ip", interface);
 | 
			
		||||
 | 
			
		||||
	fd = open(sysfs_path, O_RDONLY);
 | 
			
		||||
	if (fd < 0) {
 | 
			
		||||
		/* maybe not supported by kernel */
 | 
			
		||||
		DBG("Error %d in open(%s)", errno, sysfs_path);
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (read(fd, &value, 1) != 1) {
 | 
			
		||||
		DBG("Error %d in read(%s)", errno, sysfs_path);
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (value == 'Y')
 | 
			
		||||
		expected = QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP;
 | 
			
		||||
	else if (value == 'N')
 | 
			
		||||
		expected = QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3;
 | 
			
		||||
	else
 | 
			
		||||
		DBG("Unexpected sysfs file contents");
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	if (fd >= 0)
 | 
			
		||||
		close(fd);
 | 
			
		||||
 | 
			
		||||
	if (sysfs_path)
 | 
			
		||||
		g_free(sysfs_path);
 | 
			
		||||
 | 
			
		||||
	if (interface)
 | 
			
		||||
		g_free(interface);
 | 
			
		||||
 | 
			
		||||
	return expected;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool qmi_device_set_expected_data_format(struct qmi_device *device,
 | 
			
		||||
			enum qmi_device_expected_data_format format)
 | 
			
		||||
{
 | 
			
		||||
	bool res = false;
 | 
			
		||||
	char *sysfs_path = NULL;
 | 
			
		||||
	char *interface = NULL;
 | 
			
		||||
	int fd = -1;
 | 
			
		||||
	char value;
 | 
			
		||||
 | 
			
		||||
	if (!device)
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	switch (format) {
 | 
			
		||||
	case QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3:
 | 
			
		||||
		value = 'N';
 | 
			
		||||
		break;
 | 
			
		||||
	case QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP:
 | 
			
		||||
		value = 'Y';
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		DBG("Unhandled format: %d", (int) format);
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	interface = get_device_interface(device);
 | 
			
		||||
 | 
			
		||||
	if (!interface) {
 | 
			
		||||
		DBG("Error while getting interface name");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Build sysfs file path and open it */
 | 
			
		||||
	sysfs_path = g_strdup_printf("/sys/class/net/%s/qmi/raw_ip", interface);
 | 
			
		||||
 | 
			
		||||
	fd = open(sysfs_path, O_WRONLY);
 | 
			
		||||
	if (fd < 0) {
 | 
			
		||||
		/* maybe not supported by kernel */
 | 
			
		||||
		DBG("Error %d in open(%s)", errno, sysfs_path);
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (write(fd, &value, 1) != 1) {
 | 
			
		||||
		DBG("Error %d in write(%s)", errno, sysfs_path);
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res = true;
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	if (fd >= 0)
 | 
			
		||||
		close(fd);
 | 
			
		||||
 | 
			
		||||
	if (sysfs_path)
 | 
			
		||||
		g_free(sysfs_path);
 | 
			
		||||
 | 
			
		||||
	if (interface)
 | 
			
		||||
		g_free(interface);
 | 
			
		||||
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct qmi_param *qmi_param_new(void)
 | 
			
		||||
{
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
@@ -1435,6 +1809,27 @@ bool qmi_result_get_uint8(struct qmi_result *result, uint8_t type,
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool qmi_result_get_int16(struct qmi_result *result, uint8_t type,
 | 
			
		||||
							int16_t *value)
 | 
			
		||||
{
 | 
			
		||||
	const unsigned char *ptr;
 | 
			
		||||
	uint16_t len, tmp;
 | 
			
		||||
 | 
			
		||||
	if (!result || !type)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	ptr = tlv_get(result->data, result->length, type, &len);
 | 
			
		||||
	if (!ptr)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	memcpy(&tmp, ptr, 2);
 | 
			
		||||
 | 
			
		||||
	if (value)
 | 
			
		||||
		*value = GINT16_FROM_LE(tmp);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool qmi_result_get_uint16(struct qmi_result *result, uint8_t type,
 | 
			
		||||
							uint16_t *value)
 | 
			
		||||
{
 | 
			
		||||
@@ -1501,6 +1896,7 @@ bool qmi_result_get_uint64(struct qmi_result *result, uint8_t type,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct service_create_data {
 | 
			
		||||
	struct discovery super;
 | 
			
		||||
	struct qmi_device *device;
 | 
			
		||||
	bool shared;
 | 
			
		||||
	uint8_t type;
 | 
			
		||||
@@ -1512,16 +1908,29 @@ struct service_create_data {
 | 
			
		||||
	guint timeout;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static gboolean service_create_reply(gpointer user_data)
 | 
			
		||||
static void service_create_data_free(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct service_create_data *data = user_data;
 | 
			
		||||
 | 
			
		||||
	data->func(NULL, data->user_data);
 | 
			
		||||
	if (data->timeout) {
 | 
			
		||||
		g_source_remove(data->timeout);
 | 
			
		||||
		data->timeout = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (data->destroy)
 | 
			
		||||
		data->destroy(data->user_data);
 | 
			
		||||
 | 
			
		||||
	g_free(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean service_create_reply(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct service_create_data *data = user_data;
 | 
			
		||||
 | 
			
		||||
	data->timeout = 0;
 | 
			
		||||
	data->func(NULL, data->user_data);
 | 
			
		||||
 | 
			
		||||
	__qmi_device_discovery_complete(data->device, &data->super);
 | 
			
		||||
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
@@ -1537,8 +1946,6 @@ static void service_create_callback(uint16_t message, uint16_t length,
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
	unsigned int hash_id;
 | 
			
		||||
 | 
			
		||||
	g_source_remove(data->timeout);
 | 
			
		||||
 | 
			
		||||
	result_code = tlv_get(buffer, length, 0x02, &len);
 | 
			
		||||
	if (!result_code)
 | 
			
		||||
		goto done;
 | 
			
		||||
@@ -1580,13 +1987,9 @@ static void service_create_callback(uint16_t message, uint16_t length,
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	data->func(service, data->user_data);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unref(service);
 | 
			
		||||
 | 
			
		||||
	if (data->destroy)
 | 
			
		||||
		data->destroy(data->user_data);
 | 
			
		||||
 | 
			
		||||
	g_free(data);
 | 
			
		||||
	__qmi_device_discovery_complete(data->device, &data->super);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void service_create_discover(uint8_t count,
 | 
			
		||||
@@ -1617,7 +2020,9 @@ static void service_create_discover(uint8_t count,
 | 
			
		||||
		if (data->timeout > 0)
 | 
			
		||||
			g_source_remove(data->timeout);
 | 
			
		||||
 | 
			
		||||
		g_timeout_add_seconds(0, service_create_reply, data);
 | 
			
		||||
		data->timeout = g_timeout_add_seconds(0,
 | 
			
		||||
						service_create_reply, data);
 | 
			
		||||
		__qmi_device_discovery_started(device, &data->super);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1640,6 +2045,7 @@ static bool service_create(struct qmi_device *device, bool shared,
 | 
			
		||||
	if (!data)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	data->super.destroy = service_create_data_free;
 | 
			
		||||
	data->device = device;
 | 
			
		||||
	data->shared = shared;
 | 
			
		||||
	data->type = type;
 | 
			
		||||
@@ -1662,6 +2068,7 @@ static bool service_create(struct qmi_device *device, bool shared,
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	data->timeout = g_timeout_add_seconds(8, service_create_reply, data);
 | 
			
		||||
	__qmi_device_discovery_started(device, &data->super);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
@@ -1680,17 +2087,23 @@ bool qmi_service_create(struct qmi_device *device,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct service_create_shared_data {
 | 
			
		||||
	struct discovery super;
 | 
			
		||||
	struct qmi_service *service;
 | 
			
		||||
	struct qmi_device *device;
 | 
			
		||||
	qmi_create_func_t func;
 | 
			
		||||
	void *user_data;
 | 
			
		||||
	qmi_destroy_func_t destroy;
 | 
			
		||||
	guint timeout;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static gboolean service_create_shared_reply(gpointer user_data)
 | 
			
		||||
static void service_create_shared_data_free(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct service_create_shared_data *data = user_data;
 | 
			
		||||
 | 
			
		||||
	data->func(data->service, data->user_data);
 | 
			
		||||
	if (data->timeout) {
 | 
			
		||||
		g_source_remove(data->timeout);
 | 
			
		||||
		data->timeout = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	qmi_service_unref(data->service);
 | 
			
		||||
 | 
			
		||||
@@ -1698,6 +2111,16 @@ static gboolean service_create_shared_reply(gpointer user_data)
 | 
			
		||||
		data->destroy(data->user_data);
 | 
			
		||||
 | 
			
		||||
	g_free(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean service_create_shared_reply(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct service_create_shared_data *data = user_data;
 | 
			
		||||
 | 
			
		||||
	data->timeout = 0;
 | 
			
		||||
	data->func(data->service, data->user_data);
 | 
			
		||||
 | 
			
		||||
	__qmi_device_discovery_complete(data->device, &data->super);
 | 
			
		||||
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
@@ -1724,13 +2147,16 @@ bool qmi_service_create_shared(struct qmi_device *device,
 | 
			
		||||
		if (!data)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		data->super.destroy = service_create_shared_data_free;
 | 
			
		||||
		data->service = qmi_service_ref(service);
 | 
			
		||||
 | 
			
		||||
		data->device = device;
 | 
			
		||||
		data->func = func;
 | 
			
		||||
		data->user_data = user_data;
 | 
			
		||||
		data->destroy = destroy;
 | 
			
		||||
 | 
			
		||||
		g_timeout_add(0, service_create_shared_reply, data);
 | 
			
		||||
		data->timeout = g_timeout_add(0,
 | 
			
		||||
					service_create_shared_reply, data);
 | 
			
		||||
		__qmi_device_discovery_started(device, &data->super);
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -35,18 +35,32 @@
 | 
			
		||||
#define QMI_SERVICE_CAT		10	/* Card application toolkit service */
 | 
			
		||||
#define QMI_SERVICE_UIM		11	/* UIM service */
 | 
			
		||||
#define QMI_SERVICE_PBM		12	/* Phonebook service */
 | 
			
		||||
#define QMI_SERVICE_QCHAT	13
 | 
			
		||||
#define QMI_SERVICE_RMTFS	14	/* Remote file system service */
 | 
			
		||||
#define QMI_SERVICE_TEST	15
 | 
			
		||||
#define QMI_SERVICE_LOC		16	/* Location service */
 | 
			
		||||
#define QMI_SERVICE_SAR		17	/* Specific absorption rate service */
 | 
			
		||||
#define QMI_SERVICE_CSD		20	/* Core sound driver service */
 | 
			
		||||
#define QMI_SERVICE_EFS		21	/* Embedded file system service */
 | 
			
		||||
#define QMI_SERVICE_TS		23	/* Thermal sensors service */
 | 
			
		||||
#define QMI_SERVICE_TMD		24	/* Thermal mitigation device service */
 | 
			
		||||
#define QMI_SERVICE_WDA		26	/* Wireless data administrative service */
 | 
			
		||||
#define QMI_SERVICE_CSVT	29
 | 
			
		||||
#define QMI_SERVICE_COEX	34
 | 
			
		||||
#define QMI_SERVICE_PDC		36	/* Persistent device configuration service */
 | 
			
		||||
#define QMI_SERVICE_RFRPE	41
 | 
			
		||||
#define QMI_SERVICE_DSD		42
 | 
			
		||||
#define QMI_SERVICE_SSCTL	43
 | 
			
		||||
#define QMI_SERVICE_CAT_OLD	224	/* Card application toolkit service */
 | 
			
		||||
#define QMI_SERVICE_RMS		225	/* Remote management service */
 | 
			
		||||
#define QMI_SERVICE_OMA		226	/* OMA device management service */
 | 
			
		||||
 | 
			
		||||
enum qmi_device_expected_data_format {
 | 
			
		||||
	QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN,
 | 
			
		||||
	QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3,
 | 
			
		||||
	QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct qmi_version {
 | 
			
		||||
	uint8_t type;
 | 
			
		||||
	uint16_t major;
 | 
			
		||||
@@ -62,7 +76,7 @@ typedef void (*qmi_destroy_func_t)(void *user_data);
 | 
			
		||||
struct qmi_device;
 | 
			
		||||
 | 
			
		||||
typedef void (*qmi_debug_func_t)(const char *str, void *user_data);
 | 
			
		||||
 | 
			
		||||
typedef void (*qmi_sync_func_t)(void *user_data);
 | 
			
		||||
typedef void (*qmi_shutdown_func_t)(void *user_data);
 | 
			
		||||
typedef void (*qmi_discover_func_t)(uint8_t count,
 | 
			
		||||
			const struct qmi_version *list, void *user_data);
 | 
			
		||||
@@ -82,6 +96,14 @@ bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func,
 | 
			
		||||
bool qmi_device_shutdown(struct qmi_device *device, qmi_shutdown_func_t func,
 | 
			
		||||
				void *user_data, qmi_destroy_func_t destroy);
 | 
			
		||||
 | 
			
		||||
bool qmi_device_sync(struct qmi_device *device,
 | 
			
		||||
		     qmi_sync_func_t func, void *user_data);
 | 
			
		||||
bool qmi_device_is_sync_supported(struct qmi_device *device);
 | 
			
		||||
 | 
			
		||||
enum qmi_device_expected_data_format qmi_device_get_expected_data_format(
 | 
			
		||||
						struct qmi_device *device);
 | 
			
		||||
bool qmi_device_set_expected_data_format(struct qmi_device *device,
 | 
			
		||||
			enum qmi_device_expected_data_format format);
 | 
			
		||||
 | 
			
		||||
struct qmi_param;
 | 
			
		||||
 | 
			
		||||
@@ -112,13 +134,17 @@ const void *qmi_result_get(struct qmi_result *result, uint8_t type,
 | 
			
		||||
char *qmi_result_get_string(struct qmi_result *result, uint8_t type);
 | 
			
		||||
bool qmi_result_get_uint8(struct qmi_result *result, uint8_t type,
 | 
			
		||||
							uint8_t *value);
 | 
			
		||||
bool qmi_result_get_int16(struct qmi_result *result, uint8_t type,
 | 
			
		||||
							int16_t *value);
 | 
			
		||||
bool qmi_result_get_uint16(struct qmi_result *result, uint8_t type,
 | 
			
		||||
							uint16_t *value);
 | 
			
		||||
bool qmi_result_get_uint32(struct qmi_result *result, uint8_t type,
 | 
			
		||||
							uint32_t *value);
 | 
			
		||||
bool qmi_result_get_uint64(struct qmi_result *result, uint8_t type,
 | 
			
		||||
							uint64_t *value);
 | 
			
		||||
void qmi_result_print_tlvs(struct qmi_result *result);
 | 
			
		||||
 | 
			
		||||
int qmi_error_to_ofono_cme(int qmi_error);
 | 
			
		||||
 | 
			
		||||
struct qmi_service;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,16 +39,20 @@ static int qmimodem_init(void)
 | 
			
		||||
	qmi_ussd_init();
 | 
			
		||||
	qmi_gprs_init();
 | 
			
		||||
	qmi_gprs_context_init();
 | 
			
		||||
	qmi_lte_init();
 | 
			
		||||
	qmi_radio_settings_init();
 | 
			
		||||
	qmi_location_reporting_init();
 | 
			
		||||
	qmi_netmon_init();
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmimodem_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	qmi_netmon_exit();
 | 
			
		||||
	qmi_location_reporting_exit();
 | 
			
		||||
	qmi_radio_settings_exit();
 | 
			
		||||
	qmi_lte_exit();
 | 
			
		||||
	qmi_gprs_context_exit();
 | 
			
		||||
	qmi_gprs_exit();
 | 
			
		||||
	qmi_ussd_exit();
 | 
			
		||||
 
 | 
			
		||||
@@ -48,8 +48,14 @@ extern void qmi_gprs_exit(void);
 | 
			
		||||
extern void qmi_gprs_context_init(void);
 | 
			
		||||
extern void qmi_gprs_context_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void qmi_lte_init(void);
 | 
			
		||||
extern void qmi_lte_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void qmi_radio_settings_init(void);
 | 
			
		||||
extern void qmi_radio_settings_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void qmi_location_reporting_init(void);
 | 
			
		||||
extern void qmi_location_reporting_exit(void);
 | 
			
		||||
 | 
			
		||||
extern void qmi_netmon_init(void);
 | 
			
		||||
extern void qmi_netmon_exit(void);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,15 +29,202 @@
 | 
			
		||||
 | 
			
		||||
#include "qmi.h"
 | 
			
		||||
#include "nas.h"
 | 
			
		||||
#include "dms.h"
 | 
			
		||||
 | 
			
		||||
#include "qmimodem.h"
 | 
			
		||||
 | 
			
		||||
struct settings_data {
 | 
			
		||||
	struct qmi_service *nas;
 | 
			
		||||
	struct qmi_service *dms;
 | 
			
		||||
	uint16_t major;
 | 
			
		||||
	uint16_t minor;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void get_system_selection_pref_cb(struct qmi_result *result,
 | 
			
		||||
							void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
 | 
			
		||||
	enum ofono_radio_access_mode mode = OFONO_RADIO_ACCESS_MODE_ANY;
 | 
			
		||||
	uint16_t pref;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL)) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	qmi_result_get_uint16(result,
 | 
			
		||||
			QMI_NAS_RESULT_SYSTEM_SELECTION_PREF_MODE, &pref);
 | 
			
		||||
 | 
			
		||||
	switch (pref) {
 | 
			
		||||
	case QMI_NAS_RAT_MODE_PREF_GSM:
 | 
			
		||||
		mode = OFONO_RADIO_ACCESS_MODE_GSM;
 | 
			
		||||
		break;
 | 
			
		||||
	case QMI_NAS_RAT_MODE_PREF_UMTS:
 | 
			
		||||
		mode = OFONO_RADIO_ACCESS_MODE_UMTS;
 | 
			
		||||
		break;
 | 
			
		||||
	case QMI_NAS_RAT_MODE_PREF_LTE:
 | 
			
		||||
		mode = OFONO_RADIO_ACCESS_MODE_LTE;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, mode, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_query_rat_mode(struct ofono_radio_settings *rs,
 | 
			
		||||
			ofono_radio_settings_rat_mode_query_cb_t cb,
 | 
			
		||||
			void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct settings_data *data = ofono_radio_settings_get_data(rs);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->nas,
 | 
			
		||||
				QMI_NAS_GET_SYSTEM_SELECTION_PREF, NULL,
 | 
			
		||||
				get_system_selection_pref_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_system_selection_pref_cb(struct qmi_result *result,
 | 
			
		||||
							void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL)) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_set_rat_mode(struct ofono_radio_settings *rs,
 | 
			
		||||
			enum ofono_radio_access_mode mode,
 | 
			
		||||
			ofono_radio_settings_rat_mode_set_cb_t cb,
 | 
			
		||||
			void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct settings_data *data = ofono_radio_settings_get_data(rs);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
	uint16_t pref = QMI_NAS_RAT_MODE_PREF_ANY;
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	switch (mode) {
 | 
			
		||||
	case OFONO_RADIO_ACCESS_MODE_ANY:
 | 
			
		||||
		pref = QMI_NAS_RAT_MODE_PREF_ANY;
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_RADIO_ACCESS_MODE_GSM:
 | 
			
		||||
		pref = QMI_NAS_RAT_MODE_PREF_GSM;
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_RADIO_ACCESS_MODE_UMTS:
 | 
			
		||||
		pref = QMI_NAS_RAT_MODE_PREF_UMTS;
 | 
			
		||||
		break;
 | 
			
		||||
	case OFONO_RADIO_ACCESS_MODE_LTE:
 | 
			
		||||
		pref = QMI_NAS_RAT_MODE_PREF_LTE;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
	if (!param) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, user_data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	qmi_param_append_uint16(param, QMI_NAS_PARAM_SYSTEM_SELECTION_PREF_MODE,
 | 
			
		||||
			pref);
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->nas,
 | 
			
		||||
				QMI_NAS_SET_SYSTEM_SELECTION_PREF, param,
 | 
			
		||||
				set_system_selection_pref_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, user_data);
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_caps_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_radio_settings_available_rats_query_cb_t cb = cbd->cb;
 | 
			
		||||
	const struct qmi_dms_device_caps *caps;
 | 
			
		||||
	unsigned int available_rats;
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
	uint8_t i;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	caps = qmi_result_get(result, QMI_DMS_RESULT_DEVICE_CAPS, &len);
 | 
			
		||||
	if (!caps)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	available_rats = 0;
 | 
			
		||||
	for (i = 0; i < caps->radio_if_count; i++) {
 | 
			
		||||
		switch (caps->radio_if[i]) {
 | 
			
		||||
		case QMI_DMS_RADIO_IF_GSM:
 | 
			
		||||
			available_rats |= OFONO_RADIO_ACCESS_MODE_GSM;
 | 
			
		||||
			break;
 | 
			
		||||
		case QMI_DMS_RADIO_IF_UMTS:
 | 
			
		||||
			available_rats |= OFONO_RADIO_ACCESS_MODE_UMTS;
 | 
			
		||||
			break;
 | 
			
		||||
		case QMI_DMS_RADIO_IF_LTE:
 | 
			
		||||
			available_rats |= OFONO_RADIO_ACCESS_MODE_LTE;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, available_rats, cbd->data);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_query_available_rats(struct ofono_radio_settings *rs,
 | 
			
		||||
			ofono_radio_settings_available_rats_query_cb_t cb,
 | 
			
		||||
			void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct settings_data *rsd = ofono_radio_settings_get_data(rs);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
 | 
			
		||||
	if (!rsd->dms)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(rsd->dms, QMI_DMS_GET_CAPS, NULL,
 | 
			
		||||
					get_caps_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_dms_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_radio_settings *rs = user_data;
 | 
			
		||||
	struct settings_data *data = ofono_radio_settings_get_data(rs);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!service)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	data->dms = qmi_service_ref(service);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_nas_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_radio_settings *rs = user_data;
 | 
			
		||||
@@ -74,10 +261,12 @@ static int qmi_radio_settings_probe(struct ofono_radio_settings *rs,
 | 
			
		||||
 | 
			
		||||
	ofono_radio_settings_set_data(rs, data);
 | 
			
		||||
 | 
			
		||||
	qmi_service_create(device, QMI_SERVICE_NAS, create_nas_cb, rs, NULL);
 | 
			
		||||
	qmi_service_create_shared(device, QMI_SERVICE_DMS,
 | 
			
		||||
						create_dms_cb, rs, NULL);
 | 
			
		||||
	qmi_service_create_shared(device, QMI_SERVICE_NAS,
 | 
			
		||||
						create_nas_cb, rs, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_radio_settings_remove(struct ofono_radio_settings *rs)
 | 
			
		||||
@@ -88,6 +277,9 @@ static void qmi_radio_settings_remove(struct ofono_radio_settings *rs)
 | 
			
		||||
 | 
			
		||||
	ofono_radio_settings_set_data(rs, NULL);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unregister_all(data->dms);
 | 
			
		||||
	qmi_service_unref(data->dms);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unregister_all(data->nas);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unref(data->nas);
 | 
			
		||||
@@ -99,6 +291,9 @@ static struct ofono_radio_settings_driver driver = {
 | 
			
		||||
	.name		= "qmimodem",
 | 
			
		||||
	.probe		= qmi_radio_settings_probe,
 | 
			
		||||
	.remove		= qmi_radio_settings_remove,
 | 
			
		||||
	.set_rat_mode	= qmi_set_rat_mode,
 | 
			
		||||
	.query_rat_mode = qmi_query_rat_mode,
 | 
			
		||||
	.query_available_rats = qmi_query_available_rats,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void qmi_radio_settings_init(void)
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@
 | 
			
		||||
#include <ofono/sim.h>
 | 
			
		||||
 | 
			
		||||
#include "qmi.h"
 | 
			
		||||
#include "dms.h"
 | 
			
		||||
#include "uim.h"
 | 
			
		||||
 | 
			
		||||
#include "qmimodem.h"
 | 
			
		||||
@@ -38,15 +39,36 @@
 | 
			
		||||
#define EF_STATUS_INVALIDATED 0
 | 
			
		||||
#define EF_STATUS_VALID 1
 | 
			
		||||
 | 
			
		||||
struct sim_data {
 | 
			
		||||
	struct qmi_service *uim;
 | 
			
		||||
	uint32_t event_mask;
 | 
			
		||||
/* max number of retry of commands that can temporary fail */
 | 
			
		||||
#define MAX_RETRY_COUNT 100
 | 
			
		||||
 | 
			
		||||
enum get_card_status_result {
 | 
			
		||||
	GET_CARD_STATUS_RESULT_OK, /* No error */
 | 
			
		||||
	GET_CARD_STATUS_RESULT_ERROR, /* Definitive error */
 | 
			
		||||
	GET_CARD_STATUS_RESULT_TEMP_ERROR, /* error, a retry could work */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* information from QMI_UIM_GET_CARD_STATUS command */
 | 
			
		||||
struct sim_status {
 | 
			
		||||
	uint8_t card_state;
 | 
			
		||||
	uint8_t app_type;
 | 
			
		||||
	uint8_t passwd_state;
 | 
			
		||||
	int retries[OFONO_SIM_PASSWORD_INVALID];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct sim_data {
 | 
			
		||||
	struct qmi_device *qmi_dev;
 | 
			
		||||
	struct qmi_service *dms;
 | 
			
		||||
	struct qmi_service *uim;
 | 
			
		||||
	uint32_t event_mask;
 | 
			
		||||
	uint8_t app_type;
 | 
			
		||||
	uint32_t retry_count;
 | 
			
		||||
	guint poll_source;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void qmi_query_passwd_state(struct ofono_sim *sim,
 | 
			
		||||
				ofono_sim_passwd_cb_t cb, void *user_data);
 | 
			
		||||
 | 
			
		||||
static int create_fileid_data(uint8_t app_type, int fileid,
 | 
			
		||||
					const unsigned char *path,
 | 
			
		||||
					unsigned int path_len,
 | 
			
		||||
@@ -146,7 +168,7 @@ static void qmi_read_attributes(struct ofono_sim *sim, int fileid,
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
	unsigned char aid_data[2] = { 0x06, 0x00 };
 | 
			
		||||
	unsigned char aid_data[2] = { 0x00, 0x00 };
 | 
			
		||||
	unsigned char fileid_data[9];
 | 
			
		||||
	int fileid_len;
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
@@ -211,7 +233,7 @@ static void qmi_read_transparent(struct ofono_sim *sim,
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
	unsigned char aid_data[2] = { 0x06, 0x00 };
 | 
			
		||||
	unsigned char aid_data[2] = { 0x00, 0x00 };
 | 
			
		||||
	unsigned char read_data[4];
 | 
			
		||||
	unsigned char fileid_data[9];
 | 
			
		||||
	int fileid_len;
 | 
			
		||||
@@ -257,7 +279,7 @@ static void qmi_read_record(struct ofono_sim *sim,
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
	unsigned char aid_data[2] = { 0x06, 0x00 };
 | 
			
		||||
	unsigned char aid_data[2] = { 0x00, 0x00 };
 | 
			
		||||
	unsigned char read_data[4];
 | 
			
		||||
	unsigned char fileid_data[9];
 | 
			
		||||
	int fileid_len;
 | 
			
		||||
@@ -295,76 +317,214 @@ error:
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_query_passwd_state(struct ofono_sim *sim,
 | 
			
		||||
				ofono_sim_passwd_cb_t cb, void *user_data)
 | 
			
		||||
static void write_generic_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_sim_write_cb_t cb = cbd->cb;
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
	const uint8_t *card_result;
 | 
			
		||||
	uint8_t sw1, sw2;
 | 
			
		||||
 | 
			
		||||
	DBG("passwd state %d", data->passwd_state);
 | 
			
		||||
 | 
			
		||||
	if (data->passwd_state == OFONO_SIM_PASSWORD_INVALID) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, -1, user_data);
 | 
			
		||||
	card_result = qmi_result_get(result, 0x10, &len);
 | 
			
		||||
	if (card_result == NULL || len != 2) {
 | 
			
		||||
		DBG("card_result: %p, len: %d", card_result, (int) len);
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, data->passwd_state, user_data);
 | 
			
		||||
}
 | 
			
		||||
	sw1 = card_result[0];
 | 
			
		||||
	sw2 = card_result[1];
 | 
			
		||||
 | 
			
		||||
static void qmi_query_pin_retries(struct ofono_sim *sim,
 | 
			
		||||
				ofono_sim_pin_retries_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	DBG("%02x, %02x", sw1, sw2);
 | 
			
		||||
 | 
			
		||||
	DBG("passwd state %d", data->passwd_state);
 | 
			
		||||
	if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) ||
 | 
			
		||||
			(sw1 == 0x90 && sw2 != 0x00)) {
 | 
			
		||||
		struct ofono_error error;
 | 
			
		||||
 | 
			
		||||
	if (data->passwd_state == OFONO_SIM_PASSWORD_INVALID) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, NULL, user_data);
 | 
			
		||||
		ofono_error("%s: error sw1 %02x sw2 %02x", __func__, sw1, sw2);
 | 
			
		||||
 | 
			
		||||
		error.type = OFONO_ERROR_TYPE_SIM;
 | 
			
		||||
		error.error = (sw1 << 8) | sw2;
 | 
			
		||||
 | 
			
		||||
		cb(&error, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, data->retries, user_data);
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void card_setup(const struct qmi_uim_slot_info *slot,
 | 
			
		||||
static void write_generic(struct ofono_sim *sim,
 | 
			
		||||
			uint16_t qmi_message, int fileid,
 | 
			
		||||
			int start_or_recordnum,
 | 
			
		||||
			int length, const unsigned char *value,
 | 
			
		||||
			const unsigned char *path, unsigned int path_len,
 | 
			
		||||
			ofono_sim_write_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
	unsigned char aid_data[2] = { 0x00, 0x00 };
 | 
			
		||||
	unsigned char write_data[4 + length];
 | 
			
		||||
	unsigned char fileid_data[9];
 | 
			
		||||
	int fileid_len;
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
 | 
			
		||||
	DBG("file id 0x%04x path len %d", fileid, path_len);
 | 
			
		||||
 | 
			
		||||
	fileid_len = create_fileid_data(data->app_type, fileid,
 | 
			
		||||
			path, path_len, fileid_data);
 | 
			
		||||
 | 
			
		||||
	if (fileid_len < 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	write_data[0] = start_or_recordnum & 0xff;
 | 
			
		||||
	write_data[1] = (start_or_recordnum & 0xff00) >> 8;
 | 
			
		||||
	write_data[2] = length & 0xff;
 | 
			
		||||
	write_data[3] = (length & 0xff00) >> 8;
 | 
			
		||||
	memcpy(&write_data[4], value, length);
 | 
			
		||||
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
	if (!param)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	qmi_param_append(param, 0x01, sizeof(aid_data), aid_data);
 | 
			
		||||
	qmi_param_append(param, 0x02, fileid_len, fileid_data);
 | 
			
		||||
	qmi_param_append(param, 0x03, 4 + length, write_data);
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->uim, qmi_message, param,
 | 
			
		||||
					write_generic_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, user_data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_write_transparent(struct ofono_sim *sim,
 | 
			
		||||
				int fileid, int start, int length,
 | 
			
		||||
				const unsigned char *value,
 | 
			
		||||
				const unsigned char *path,
 | 
			
		||||
				unsigned int path_len,
 | 
			
		||||
				ofono_sim_write_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	write_generic(sim, QMI_UIM_WRITE_TRANSPARENT, fileid, start,
 | 
			
		||||
				length, value, path, path_len, cb, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_write_linear(struct ofono_sim *sim,
 | 
			
		||||
				int fileid, int record, int length,
 | 
			
		||||
				const unsigned char *value,
 | 
			
		||||
				const unsigned char *path,
 | 
			
		||||
				unsigned int path_len,
 | 
			
		||||
				ofono_sim_write_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	write_generic(sim, QMI_UIM_WRITE_RECORD, fileid, record,
 | 
			
		||||
				length, value, path, path_len, cb, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_write_cyclic(struct ofono_sim *sim,
 | 
			
		||||
				int fileid, int length,
 | 
			
		||||
				const unsigned char *value,
 | 
			
		||||
				const unsigned char *path,
 | 
			
		||||
				unsigned int path_len,
 | 
			
		||||
				ofono_sim_write_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	write_generic(sim, QMI_UIM_WRITE_RECORD, fileid, 0,
 | 
			
		||||
				length, value, path, path_len, cb, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_imsi_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_sim_imsi_cb_t cb = cbd->cb;
 | 
			
		||||
	char *str;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL)) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	str = qmi_result_get_string(result, QMI_DMS_RESULT_IMSI);
 | 
			
		||||
	if (!str) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, str, cbd->data);
 | 
			
		||||
 | 
			
		||||
	qmi_free(str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_read_imsi(struct ofono_sim *sim,
 | 
			
		||||
				ofono_sim_imsi_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->dms, QMI_DMS_GET_IMSI, NULL,
 | 
			
		||||
					get_imsi_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if a retry could give another (better) result */
 | 
			
		||||
static bool get_card_status(const struct qmi_uim_slot_info *slot,
 | 
			
		||||
					const struct qmi_uim_app_info1 *info1,
 | 
			
		||||
					const struct qmi_uim_app_info2 *info2,
 | 
			
		||||
							struct sim_data *data)
 | 
			
		||||
					struct sim_status *sim_stat)
 | 
			
		||||
{
 | 
			
		||||
	data->card_state = slot->card_state;
 | 
			
		||||
	data->app_type = info1->app_type;
 | 
			
		||||
	bool need_retry = false;
 | 
			
		||||
	sim_stat->card_state = slot->card_state;
 | 
			
		||||
	sim_stat->app_type = info1->app_type;
 | 
			
		||||
 | 
			
		||||
	switch (info1->app_state) {
 | 
			
		||||
	case 0x02:	/* PIN1 or UPIN is required */
 | 
			
		||||
		data->passwd_state = OFONO_SIM_PASSWORD_SIM_PIN;
 | 
			
		||||
		sim_stat->passwd_state = OFONO_SIM_PASSWORD_SIM_PIN;
 | 
			
		||||
		break;
 | 
			
		||||
	case 0x03:	/* PUK1 or PUK for UPIN is required */
 | 
			
		||||
		data->passwd_state = OFONO_SIM_PASSWORD_SIM_PUK;
 | 
			
		||||
		sim_stat->passwd_state = OFONO_SIM_PASSWORD_SIM_PUK;
 | 
			
		||||
		break;
 | 
			
		||||
	case 0x04:	/* Personalization state must be checked. */
 | 
			
		||||
		/* This is temporary, we could retry and get another result */
 | 
			
		||||
		sim_stat->passwd_state = OFONO_SIM_PASSWORD_INVALID;
 | 
			
		||||
		need_retry = true;
 | 
			
		||||
		break;
 | 
			
		||||
	case 0x07:	/* Ready */
 | 
			
		||||
		data->passwd_state = OFONO_SIM_PASSWORD_NONE;
 | 
			
		||||
		sim_stat->passwd_state = OFONO_SIM_PASSWORD_NONE;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		data->passwd_state = OFONO_SIM_PASSWORD_INVALID;
 | 
			
		||||
		DBG("info1->app_state:0x%x: OFONO_SIM_PASSWORD_INVALID",
 | 
			
		||||
			info1->app_state);
 | 
			
		||||
		sim_stat->passwd_state = OFONO_SIM_PASSWORD_INVALID;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data->retries[OFONO_SIM_PASSWORD_SIM_PIN] = info2->pin1_retries;
 | 
			
		||||
	data->retries[OFONO_SIM_PASSWORD_SIM_PUK] = info2->puk1_retries;
 | 
			
		||||
	sim_stat->retries[OFONO_SIM_PASSWORD_SIM_PIN] = info2->pin1_retries;
 | 
			
		||||
	sim_stat->retries[OFONO_SIM_PASSWORD_SIM_PUK] = info2->puk1_retries;
 | 
			
		||||
 | 
			
		||||
	data->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = info2->pin2_retries;
 | 
			
		||||
	data->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = info2->puk2_retries;
 | 
			
		||||
	sim_stat->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = info2->pin2_retries;
 | 
			
		||||
	sim_stat->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = info2->puk2_retries;
 | 
			
		||||
 | 
			
		||||
	return need_retry;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_card_status_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
static enum get_card_status_result handle_get_card_status_result(
 | 
			
		||||
		struct qmi_result *result, struct sim_status *sim_stat)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sim *sim = user_data;
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	const void *ptr;
 | 
			
		||||
	const struct qmi_uim_card_status *status;
 | 
			
		||||
	uint16_t len, offset;
 | 
			
		||||
	uint8_t i;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
	enum get_card_status_result res = GET_CARD_STATUS_RESULT_ERROR;
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL))
 | 
			
		||||
		goto done;
 | 
			
		||||
@@ -397,15 +557,211 @@ static void get_card_status_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
 | 
			
		||||
			index = GUINT16_FROM_LE(status->index_gw_pri);
 | 
			
		||||
 | 
			
		||||
			if ((index & 0xff) == i && (index >> 8) == n)
 | 
			
		||||
				card_setup(slot, info1, info2, data);
 | 
			
		||||
			if ((index & 0xff) == i && (index >> 8) == n) {
 | 
			
		||||
				if (get_card_status(slot, info1, info2,
 | 
			
		||||
								sim_stat))
 | 
			
		||||
					res = GET_CARD_STATUS_RESULT_TEMP_ERROR;
 | 
			
		||||
				else
 | 
			
		||||
					res = GET_CARD_STATUS_RESULT_OK;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean query_passwd_state_retry(gpointer userdata)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = userdata;
 | 
			
		||||
	ofono_sim_passwd_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_sim *sim = cbd->user;
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
 | 
			
		||||
	data->poll_source = 0;
 | 
			
		||||
 | 
			
		||||
	qmi_query_passwd_state(sim, cb, cbd->data);
 | 
			
		||||
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void query_passwd_state_cb(struct qmi_result *result,
 | 
			
		||||
					void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_sim_passwd_cb_t cb = cbd->cb;
 | 
			
		||||
	struct ofono_sim *sim = cbd->user;
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct sim_status sim_stat;
 | 
			
		||||
	enum get_card_status_result res;
 | 
			
		||||
	struct cb_data *retry_cbd;
 | 
			
		||||
 | 
			
		||||
	res = handle_get_card_status_result(result, &sim_stat);
 | 
			
		||||
	switch (res) {
 | 
			
		||||
	case GET_CARD_STATUS_RESULT_OK:
 | 
			
		||||
		DBG("passwd state %d", sim_stat.passwd_state);
 | 
			
		||||
		data->retry_count = 0;
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, sim_stat.passwd_state, cbd->data);
 | 
			
		||||
		break;
 | 
			
		||||
	case GET_CARD_STATUS_RESULT_TEMP_ERROR:
 | 
			
		||||
		data->retry_count++;
 | 
			
		||||
		if (data->retry_count > MAX_RETRY_COUNT) {
 | 
			
		||||
			DBG("Failed after %d attempts", data->retry_count);
 | 
			
		||||
			data->retry_count = 0;
 | 
			
		||||
			CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
		} else {
 | 
			
		||||
			DBG("Retry command");
 | 
			
		||||
			retry_cbd = cb_data_new(cb, cbd->data);
 | 
			
		||||
			retry_cbd->user = sim;
 | 
			
		||||
			data->poll_source = g_timeout_add(20,
 | 
			
		||||
						query_passwd_state_retry,
 | 
			
		||||
						retry_cbd);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case GET_CARD_STATUS_RESULT_ERROR:
 | 
			
		||||
		DBG("Command failed");
 | 
			
		||||
		data->retry_count = 0;
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_query_passwd_state(struct ofono_sim *sim,
 | 
			
		||||
				ofono_sim_passwd_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	cbd->user = sim;
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->uim, QMI_UIM_GET_CARD_STATUS, NULL,
 | 
			
		||||
					query_passwd_state_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void query_pin_retries_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_sim_pin_retries_cb_t cb = cbd->cb;
 | 
			
		||||
	struct sim_status sim_stat;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (handle_get_card_status_result(result, &sim_stat) !=
 | 
			
		||||
					GET_CARD_STATUS_RESULT_OK) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, sim_stat.retries, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_query_pin_retries(struct ofono_sim *sim,
 | 
			
		||||
				ofono_sim_pin_retries_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->uim, QMI_UIM_GET_CARD_STATUS, NULL,
 | 
			
		||||
					query_pin_retries_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pin_send_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL)) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_pin_send(struct ofono_sim *sim, const char *passwd,
 | 
			
		||||
			ofono_sim_lock_unlock_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, user_data);
 | 
			
		||||
	int passwd_len;
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
	struct qmi_uim_param_message_info *info_data;
 | 
			
		||||
	unsigned char session_info_data[2];
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!passwd)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	passwd_len = strlen(passwd);
 | 
			
		||||
 | 
			
		||||
	if (passwd_len <= 0 || passwd_len > 0xFF)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
	if (!param)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	/* param info */
 | 
			
		||||
	info_data = alloca(2 + passwd_len);
 | 
			
		||||
	info_data->pin_id = 0x01; /* PIN 1 */
 | 
			
		||||
	info_data->length = (uint8_t) passwd_len;
 | 
			
		||||
	memcpy(info_data->pin_value, passwd, passwd_len);
 | 
			
		||||
	qmi_param_append(param, QMI_UIM_PARAM_MESSAGE_INFO, 2 + passwd_len,
 | 
			
		||||
					info_data);
 | 
			
		||||
	/* param Session Information */
 | 
			
		||||
	session_info_data[0] = 0x6;
 | 
			
		||||
	session_info_data[1] = 0x0;
 | 
			
		||||
	qmi_param_append(param, QMI_UIM_PARAM_MESSAGE_SESSION_INFO, 2,
 | 
			
		||||
					session_info_data);
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->uim, QMI_UIM_VERIFY_PIN, param,
 | 
			
		||||
					pin_send_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void get_card_status_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sim *sim = user_data;
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
	struct sim_status sim_stat;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (handle_get_card_status_result(result, &sim_stat) !=
 | 
			
		||||
					GET_CARD_STATUS_RESULT_OK) {
 | 
			
		||||
		data->app_type = 0;	/* Unknown */
 | 
			
		||||
		sim_stat.card_state = 0x00;	/* Absent */
 | 
			
		||||
	} else {
 | 
			
		||||
		data->app_type = sim_stat.app_type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofono_sim_register(sim);
 | 
			
		||||
 | 
			
		||||
	switch (data->card_state) {
 | 
			
		||||
	switch (sim_stat.card_state) {
 | 
			
		||||
	case 0x00:	/* Absent */
 | 
			
		||||
	case 0x02:	/* Error */
 | 
			
		||||
		break;
 | 
			
		||||
@@ -465,30 +821,44 @@ static void create_uim_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	qmi_service_unref(data->uim);
 | 
			
		||||
 | 
			
		||||
	ofono_sim_remove(sim);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_dms_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sim *sim = user_data;
 | 
			
		||||
	struct sim_data *data = ofono_sim_get_data(sim);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!service) {
 | 
			
		||||
		ofono_error("Failed to request DMS service");
 | 
			
		||||
		ofono_sim_remove(sim);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data->dms = qmi_service_ref(service);
 | 
			
		||||
 | 
			
		||||
	qmi_service_create(data->qmi_dev, QMI_SERVICE_UIM, create_uim_cb, sim,
 | 
			
		||||
					NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qmi_sim_probe(struct ofono_sim *sim,
 | 
			
		||||
				unsigned int vendor, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct qmi_device *device = user_data;
 | 
			
		||||
	struct sim_data *data;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	data = g_new0(struct sim_data, 1);
 | 
			
		||||
 | 
			
		||||
	data->passwd_state = OFONO_SIM_PASSWORD_INVALID;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
 | 
			
		||||
		data->retries[i] = -1;
 | 
			
		||||
	data->qmi_dev = device;
 | 
			
		||||
 | 
			
		||||
	ofono_sim_set_data(sim, data);
 | 
			
		||||
 | 
			
		||||
	qmi_service_create(device, QMI_SERVICE_UIM, create_uim_cb, sim, NULL);
 | 
			
		||||
	qmi_service_create_shared(device, QMI_SERVICE_DMS,
 | 
			
		||||
						create_dms_cb, sim, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -501,9 +871,18 @@ static void qmi_sim_remove(struct ofono_sim *sim)
 | 
			
		||||
 | 
			
		||||
	ofono_sim_set_data(sim, NULL);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unregister_all(data->uim);
 | 
			
		||||
	if (data->poll_source > 0)
 | 
			
		||||
		g_source_remove(data->poll_source);
 | 
			
		||||
 | 
			
		||||
	qmi_service_unref(data->uim);
 | 
			
		||||
	if (data->uim) {
 | 
			
		||||
		qmi_service_unregister_all(data->uim);
 | 
			
		||||
		qmi_service_unref(data->uim);
 | 
			
		||||
		data->uim = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (data->dms) {
 | 
			
		||||
		qmi_service_unregister_all(data->dms);
 | 
			
		||||
		qmi_service_unref(data->dms);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g_free(data);
 | 
			
		||||
}
 | 
			
		||||
@@ -516,8 +895,13 @@ static struct ofono_sim_driver driver = {
 | 
			
		||||
	.read_file_transparent	= qmi_read_transparent,
 | 
			
		||||
	.read_file_linear	= qmi_read_record,
 | 
			
		||||
	.read_file_cyclic	= qmi_read_record,
 | 
			
		||||
	.write_file_transparent = qmi_write_transparent,
 | 
			
		||||
	.write_file_linear	= qmi_write_linear,
 | 
			
		||||
	.write_file_cyclic	= qmi_write_cyclic,
 | 
			
		||||
	.read_imsi		= qmi_read_imsi,
 | 
			
		||||
	.query_passwd_state	= qmi_query_passwd_state,
 | 
			
		||||
	.query_pin_retries	= qmi_query_pin_retries,
 | 
			
		||||
	.send_passwd		= qmi_pin_send,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void qmi_sim_init(void)
 | 
			
		||||
 
 | 
			
		||||
@@ -277,7 +277,7 @@ static void qmi_bearer_query(struct ofono_sms *sms,
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (data->major < 1 && data->minor < 2)
 | 
			
		||||
	if (data->major < 1 || (data->major == 1 && data->minor < 2))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(data->wms, QMI_WMS_GET_DOMAIN_PREF, NULL,
 | 
			
		||||
@@ -315,7 +315,7 @@ static void qmi_bearer_set(struct ofono_sms *sms, int bearer,
 | 
			
		||||
 | 
			
		||||
	DBG("bearer %d", bearer);
 | 
			
		||||
 | 
			
		||||
	if (data->major < 1 && data->minor < 2)
 | 
			
		||||
	if (data->major < 1 || (data->major == 1 && data->minor < 2))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	domain = bearer_to_domain(bearer);
 | 
			
		||||
@@ -334,9 +334,38 @@ error:
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void raw_read_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sms *sms = user_data;
 | 
			
		||||
	const struct qmi_wms_raw_message* msg;
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
	uint16_t error;
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, &error)) {
 | 
			
		||||
		DBG("Raw read error: %d (%s)", error,
 | 
			
		||||
			qmi_result_get_error(result));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Raw message data */
 | 
			
		||||
	msg = qmi_result_get(result, 0x01, &len);
 | 
			
		||||
	if (msg) {
 | 
			
		||||
		uint16_t plen;
 | 
			
		||||
		uint16_t tpdu_len;
 | 
			
		||||
 | 
			
		||||
		plen = GUINT16_FROM_LE(msg->msg_length);
 | 
			
		||||
		tpdu_len = plen - msg->msg_data[0] - 1;
 | 
			
		||||
 | 
			
		||||
		ofono_sms_deliver_notify(sms, msg->msg_data, plen, tpdu_len);
 | 
			
		||||
	} else {
 | 
			
		||||
		DBG("No message data available at requested position");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void event_notify(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_sms *sms = user_data;
 | 
			
		||||
	struct sms_data *data = ofono_sms_get_data(sms);
 | 
			
		||||
	const struct qmi_wms_result_new_msg_notify *notify;
 | 
			
		||||
	const struct qmi_wms_result_message *message;
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
@@ -360,6 +389,34 @@ static void event_notify(struct qmi_result *result, void *user_data)
 | 
			
		||||
		DBG("msg format %d PDU length %d", message->msg_format, plen);
 | 
			
		||||
 | 
			
		||||
		ofono_sms_deliver_notify(sms, message->msg_data, plen, plen);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* The Quectel EC21, at least, does not provide the
 | 
			
		||||
		 * message data in the event notification, so a 'raw read'
 | 
			
		||||
		 * needs to be issued in order to query the message itself
 | 
			
		||||
		 */
 | 
			
		||||
		struct qmi_param *param;
 | 
			
		||||
 | 
			
		||||
		param = qmi_param_new();
 | 
			
		||||
		if (!param)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		/* Message memory storage ID */
 | 
			
		||||
		qmi_param_append(param, 0x01, sizeof(*notify), notify);
 | 
			
		||||
		/* The 'message mode' parameter is documented as optional,
 | 
			
		||||
		 * but the Quectel EC21 errors out with error 17 (missing
 | 
			
		||||
		 * argument) if it is not provided... we default to 3GPP
 | 
			
		||||
		 * here because that's what works for me and it's not clear
 | 
			
		||||
		 * how to actually query what this should be otherwise...
 | 
			
		||||
		 */
 | 
			
		||||
		/* Message mode */
 | 
			
		||||
		qmi_param_append_uint8(param, 0x10,
 | 
			
		||||
				QMI_WMS_MESSAGE_MODE_GSMWCDMA);
 | 
			
		||||
 | 
			
		||||
		if (qmi_service_send(data->wms, QMI_WMS_RAW_READ, param,
 | 
			
		||||
					raw_read_cb, sms, NULL) > 0)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		qmi_param_free(param);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -411,7 +468,7 @@ static void get_routes_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
	new_list->count = GUINT16_TO_LE(1);
 | 
			
		||||
	new_list->route[0].msg_type = QMI_WMS_MSG_TYPE_P2P;
 | 
			
		||||
	new_list->route[0].msg_class = QMI_WMS_MSG_CLASS_NONE;
 | 
			
		||||
	new_list->route[0].storage_type = QMI_WMS_STORAGE_TYPE_NV;
 | 
			
		||||
	new_list->route[0].storage_type = QMI_WMS_STORAGE_TYPE_NONE;
 | 
			
		||||
	new_list->route[0].action = QMI_WMS_ACTION_TRANSFER_AND_ACK;
 | 
			
		||||
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,8 @@
 | 
			
		||||
#define QMI_UIM_WRITE_RECORD		35	/* Write a record */
 | 
			
		||||
#define QMI_UIM_GET_FILE_ATTRIBUTES	36	/* Get file attributes */
 | 
			
		||||
 | 
			
		||||
#define QMI_UIM_VERIFY_PIN		38	/* Verify PIN */
 | 
			
		||||
 | 
			
		||||
#define QMI_UIM_EVENT_REGISTRATION	46	/* Register for indications */
 | 
			
		||||
#define QMI_UIM_GET_CARD_STATUS		47	/* Get card status */
 | 
			
		||||
 | 
			
		||||
@@ -91,3 +93,12 @@ struct qmi_uim_file_attributes {
 | 
			
		||||
	uint16_t raw_len;
 | 
			
		||||
	uint8_t raw_value[0];
 | 
			
		||||
} __attribute__((__packed__));
 | 
			
		||||
 | 
			
		||||
/* Verify PIN parameter */
 | 
			
		||||
#define QMI_UIM_PARAM_MESSAGE_SESSION_INFO	0x01
 | 
			
		||||
#define QMI_UIM_PARAM_MESSAGE_INFO	0x02
 | 
			
		||||
struct qmi_uim_param_message_info {
 | 
			
		||||
	uint8_t pin_id;
 | 
			
		||||
	uint8_t length;
 | 
			
		||||
	uint8_t pin_value[0];
 | 
			
		||||
} __attribute__((__packed__));
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2011-2012  Intel Corporation. All rights reserved.
 | 
			
		||||
 *  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 version 2 as
 | 
			
		||||
@@ -23,20 +24,103 @@
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
#include <ofono/modem.h>
 | 
			
		||||
#include <ofono/ussd.h>
 | 
			
		||||
 | 
			
		||||
#include <smsutil.h>
 | 
			
		||||
#include "qmi.h"
 | 
			
		||||
 | 
			
		||||
#include "qmimodem.h"
 | 
			
		||||
 | 
			
		||||
#include "voice.h"
 | 
			
		||||
 | 
			
		||||
struct ussd_data {
 | 
			
		||||
	struct qmi_service *voice;
 | 
			
		||||
	uint16_t major;
 | 
			
		||||
	uint16_t minor;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int validate_ussd_data(const struct qmi_ussd_data *data, uint16_t size)
 | 
			
		||||
{
 | 
			
		||||
	if (data == NULL)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	if (size < sizeof(*data))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	if (size < sizeof(*data) + data->length)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	if (data->dcs < QMI_USSD_DCS_ASCII || data->dcs > QMI_USSD_DCS_UCS2)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int convert_qmi_dcs_gsm_dcs(int qmi_dcs, int *gsm_dcs)
 | 
			
		||||
{
 | 
			
		||||
	switch (qmi_dcs) {
 | 
			
		||||
	case QMI_USSD_DCS_ASCII:
 | 
			
		||||
		*gsm_dcs = USSD_DCS_8BIT;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void async_orig_ind(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_ussd *ussd = user_data;
 | 
			
		||||
	const struct qmi_ussd_data *qmi_ussd;
 | 
			
		||||
	uint16_t error = 0;
 | 
			
		||||
	uint16_t len;
 | 
			
		||||
	int gsm_dcs;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	qmi_result_get_uint16(result, QMI_VOICE_PARAM_ASYNC_USSD_ERROR, &error);
 | 
			
		||||
 | 
			
		||||
	switch (error) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		/* no error */
 | 
			
		||||
		break;
 | 
			
		||||
	case 92:
 | 
			
		||||
		qmi_result_get_uint16(result,
 | 
			
		||||
					QMI_VOICE_PARAM_ASYNC_USSD_FAILURE_CASE,
 | 
			
		||||
					&error);
 | 
			
		||||
		DBG("Failure Cause: 0x%04x", error);
 | 
			
		||||
		goto error;
 | 
			
		||||
	default:
 | 
			
		||||
		DBG("USSD Error 0x%04x", error);
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	qmi_ussd = qmi_result_get(result, QMI_VOICE_PARAM_ASYNC_USSD_DATA,
 | 
			
		||||
					&len);
 | 
			
		||||
	if (qmi_ussd == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (validate_ussd_data(qmi_ussd, len))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	if (convert_qmi_dcs_gsm_dcs(qmi_ussd->dcs, &gsm_dcs))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	ofono_ussd_notify(ussd, OFONO_USSD_STATUS_NOTIFY, gsm_dcs,
 | 
			
		||||
				qmi_ussd->data, qmi_ussd->length);
 | 
			
		||||
	return;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ofono_ussd_notify(ussd, OFONO_USSD_STATUS_TERMINATED, 0, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_voice_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_ussd *ussd = user_data;
 | 
			
		||||
@@ -44,7 +128,7 @@ static void create_voice_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (!service) {
 | 
			
		||||
	if (service == NULL) {
 | 
			
		||||
		ofono_error("Failed to request Voice service");
 | 
			
		||||
		ofono_ussd_remove(ussd);
 | 
			
		||||
		return;
 | 
			
		||||
@@ -58,6 +142,9 @@ static void create_voice_cb(struct qmi_service *service, void *user_data)
 | 
			
		||||
 | 
			
		||||
	data->voice = qmi_service_ref(service);
 | 
			
		||||
 | 
			
		||||
	qmi_service_register(data->voice, QMI_VOICE_ASYNC_ORIG_USSD,
 | 
			
		||||
					async_orig_ind, ussd, NULL);
 | 
			
		||||
 | 
			
		||||
	ofono_ussd_register(ussd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -77,7 +164,6 @@ static int qmi_ussd_probe(struct ofono_ussd *ussd,
 | 
			
		||||
						create_voice_cb, ussd, NULL);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_ussd_remove(struct ofono_ussd *ussd)
 | 
			
		||||
@@ -93,10 +179,103 @@ static void qmi_ussd_remove(struct ofono_ussd *ussd)
 | 
			
		||||
	g_free(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_ussd_cancel(struct ofono_ussd *ussd,
 | 
			
		||||
				ofono_ussd_cb_t cb, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ussd_data *ud = ofono_ussd_get_data(ussd);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(ud->voice, QMI_VOICE_CANCEL_USSD, NULL,
 | 
			
		||||
					NULL, NULL, NULL) > 0)
 | 
			
		||||
		CALLBACK_WITH_SUCCESS(cb, user_data);
 | 
			
		||||
	else
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The cb is called when the request (on modem layer) reports success or
 | 
			
		||||
 * failure. It doesn't contain a network result. We get the network answer
 | 
			
		||||
 * via VOICE_IND.
 | 
			
		||||
 */
 | 
			
		||||
static void qmi_ussd_request_cb(struct qmi_result *result, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct cb_data *cbd = user_data;
 | 
			
		||||
	ofono_ussd_cb_t cb = cbd->cb;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	qmi_result_print_tlvs(result);
 | 
			
		||||
 | 
			
		||||
	if (qmi_result_set_error(result, NULL)) {
 | 
			
		||||
		CALLBACK_WITH_FAILURE(cb, cbd->data);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qmi_ussd_request(struct ofono_ussd *ussd, int dcs,
 | 
			
		||||
			const unsigned char *pdu, int len,
 | 
			
		||||
			ofono_ussd_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ussd_data *ud = ofono_ussd_get_data(ussd);
 | 
			
		||||
	struct cb_data *cbd = cb_data_new(cb, data);
 | 
			
		||||
	struct qmi_ussd_data *qmi_ussd;
 | 
			
		||||
	struct qmi_param *param;
 | 
			
		||||
	char *utf8 = NULL;
 | 
			
		||||
	long utf8_len = 0;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
 | 
			
		||||
	switch (dcs) {
 | 
			
		||||
	case 0xf: /* 7bit GSM unspecific */
 | 
			
		||||
		utf8 = ussd_decode(dcs, len, pdu);
 | 
			
		||||
		if (!utf8)
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		utf8_len = strlen(utf8);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		DBG("Unsupported USSD Data Coding Scheme 0x%x", dcs);
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * So far only DCS_ASCII works.
 | 
			
		||||
	 * DCS_8BIT and DCS_UCS2 is broken, because the modem firmware
 | 
			
		||||
	 * (least on a EC20) encodes those in-correctly onto the air interface,
 | 
			
		||||
	 * resulting in wrong decoded USSD data.
 | 
			
		||||
	 */
 | 
			
		||||
	qmi_ussd = alloca(sizeof(struct qmi_ussd_data) + utf8_len);
 | 
			
		||||
	qmi_ussd->dcs = QMI_USSD_DCS_ASCII;
 | 
			
		||||
	qmi_ussd->length = len;
 | 
			
		||||
	memcpy(qmi_ussd->data, utf8, utf8_len);
 | 
			
		||||
 | 
			
		||||
	param = qmi_param_new();
 | 
			
		||||
	if (param == NULL)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	qmi_param_append(param, QMI_VOICE_PARAM_USS_DATA,
 | 
			
		||||
			sizeof(struct qmi_ussd_data) + utf8_len, qmi_ussd);
 | 
			
		||||
 | 
			
		||||
	if (qmi_service_send(ud->voice, QMI_VOICE_ASYNC_ORIG_USSD, param,
 | 
			
		||||
					qmi_ussd_request_cb, cbd, g_free) > 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	qmi_param_free(param);
 | 
			
		||||
error:
 | 
			
		||||
	g_free(utf8);
 | 
			
		||||
	g_free(cbd);
 | 
			
		||||
	CALLBACK_WITH_FAILURE(cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ofono_ussd_driver driver = {
 | 
			
		||||
	.name		= "qmimodem",
 | 
			
		||||
	.probe		= qmi_ussd_probe,
 | 
			
		||||
	.remove		= qmi_ussd_remove,
 | 
			
		||||
	.request	= qmi_ussd_request,
 | 
			
		||||
	.cancel		= qmi_ussd_cancel
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void qmi_ussd_init(void)
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,15 @@ static inline struct cb_data *cb_data_new(void *cb, void *data)
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CALLBACK_WITH_CME_ERROR(cb, err, args...)	\
 | 
			
		||||
	do {						\
 | 
			
		||||
		struct ofono_error cb_e;		\
 | 
			
		||||
		cb_e.type = OFONO_ERROR_TYPE_CME;	\
 | 
			
		||||
		cb_e.error = err;			\
 | 
			
		||||
							\
 | 
			
		||||
		cb(&cb_e, ##args);			\
 | 
			
		||||
	} while (0)					\
 | 
			
		||||
 | 
			
		||||
#define CALLBACK_WITH_FAILURE(cb, args...)		\
 | 
			
		||||
	do {						\
 | 
			
		||||
		struct ofono_error cb_e;		\
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								ofono/drivers/qmimodem/voice.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								ofono/drivers/qmimodem/voice.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  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 version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  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.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define QMI_VOICE_PARAM_USS_DATA 0x01
 | 
			
		||||
 | 
			
		||||
#define QMI_VOICE_PARAM_ASYNC_USSD_ERROR 0x10
 | 
			
		||||
#define QMI_VOICE_PARAM_ASYNC_USSD_FAILURE_CASE 0x11
 | 
			
		||||
#define QMI_VOICE_PARAM_ASYNC_USSD_DATA 0x12
 | 
			
		||||
 | 
			
		||||
#define QMI_VOICE_PARAM_USSD_IND_USER_ACTION 0x01
 | 
			
		||||
#define QMI_VOICE_PARAM_USSD_IND_DATA 0x10
 | 
			
		||||
#define QMI_VOICE_PARAM_USSD_IND_UCS2 0x11
 | 
			
		||||
 | 
			
		||||
/* according to GSM TS 23.038 section 5
 | 
			
		||||
 * coding group 1111, No message class, 8 bit data
 | 
			
		||||
 */
 | 
			
		||||
#define USSD_DCS_8BIT 0xf4
 | 
			
		||||
/* coding group 01xx, Class 0, UCS2 (16 bit) */
 | 
			
		||||
#define USSD_DCS_UCS2 0x48
 | 
			
		||||
/* default alphabet Language unspecific */
 | 
			
		||||
#define USSD_DCS_UNSPECIFIC 0x0f
 | 
			
		||||
 | 
			
		||||
/* based on qmi ussd definition */
 | 
			
		||||
enum qmi_ussd_dcs {
 | 
			
		||||
	QMI_USSD_DCS_ASCII = 0x1,
 | 
			
		||||
	QMI_USSD_DCS_8BIT,
 | 
			
		||||
	QMI_USSD_DCS_UCS2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum qmi_ussd_user_required {
 | 
			
		||||
	QMI_USSD_NO_USER_ACTION_REQUIRED = 0x1,
 | 
			
		||||
	QMI_USSD_USER_ACTION_REQUIRED,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* QMI service voice. Using an enum to prevent doublicated entries */
 | 
			
		||||
enum voice_commands {
 | 
			
		||||
	QMI_VOICE_CANCEL_USSD =		0x3c,
 | 
			
		||||
	QMI_VOICE_USSD_RELEASE_IND =	0x3d,
 | 
			
		||||
	QMI_VOICE_USSD_IND =		0x3e,
 | 
			
		||||
	QMI_VOICE_SUPS_IND =		0x42,
 | 
			
		||||
	QMI_VOICE_ASYNC_ORIG_USSD =	0x43,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct qmi_ussd_data {
 | 
			
		||||
	uint8_t dcs;
 | 
			
		||||
	uint8_t length;
 | 
			
		||||
	uint8_t data[0];
 | 
			
		||||
} __attribute__((__packed__));
 | 
			
		||||
							
								
								
									
										25
									
								
								ofono/drivers/qmimodem/wda.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								ofono/drivers/qmimodem/wda.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  oFono - Open Source Telephony
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017  Kerlink SA. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  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.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define QMI_WDA_SET_DATA_FORMAT	32	/* Set data format */
 | 
			
		||||
#define QMI_WDA_GET_DATA_FORMAT	33	/* Get data format */
 | 
			
		||||
 | 
			
		||||
/* Get and set data format interface */
 | 
			
		||||
#define QMI_WDA_LL_PROTOCOL	0x11	/* uint32_t */
 | 
			
		||||
#define QMI_WDA_DATA_LINK_PROTOCOL_UNKNOWN	0
 | 
			
		||||
#define QMI_WDA_DATA_LINK_PROTOCOL_802_3	1
 | 
			
		||||
#define QMI_WDA_DATA_LINK_PROTOCOL_RAW_IP	2
 | 
			
		||||
@@ -30,6 +30,13 @@
 | 
			
		||||
/* Start WDS network interface */
 | 
			
		||||
#define QMI_WDS_PARAM_APN			0x14	/* string */
 | 
			
		||||
#define QMI_WDS_PARAM_IP_FAMILY			0x19	/* uint8 */
 | 
			
		||||
#define QMI_WDS_PARAM_USERNAME			0x17	/* string */
 | 
			
		||||
#define QMI_WDS_PARAM_PASSWORD			0x18	/* string */
 | 
			
		||||
#define QMI_WDS_PARAM_AUTHENTICATION_PREFERENCE	0x16	/* uint8 */
 | 
			
		||||
 | 
			
		||||
#define QMI_WDS_AUTHENTICATION_NONE		0x0
 | 
			
		||||
#define QMI_WDS_AUTHENTICATION_PAP		0x1
 | 
			
		||||
#define QMI_WDS_AUTHENTICATION_CHAP		0x2
 | 
			
		||||
 | 
			
		||||
#define QMI_WDS_RESULT_PKT_HANDLE		0x01	/* uint32 */
 | 
			
		||||
 | 
			
		||||
@@ -51,10 +58,12 @@ struct qmi_wds_notify_conn_status {
 | 
			
		||||
 | 
			
		||||
/* Get the runtime data session settings */
 | 
			
		||||
#define QMI_WDS_RESULT_PDP_TYPE			0x11	/* uint8 */
 | 
			
		||||
#define QMI_WDS_RESULT_APN			0x14	/* string */
 | 
			
		||||
#define QMI_WDS_RESULT_PRIMARY_DNS		0x15	/* uint32 IPv4 */
 | 
			
		||||
#define QMI_WDS_RESULT_SECONDARY_DNS		0x16	/* uint32 IPv4 */
 | 
			
		||||
#define QMI_WDS_RESULT_IP_ADDRESS		0x1e	/* uint32 IPv4 */
 | 
			
		||||
#define QMI_WDS_RESULT_GATEWAY			0x20	/* uint32 IPv4 */
 | 
			
		||||
#define QMI_WDS_RESULT_GATEWAY_NETMASK		0x21	/* uint32 IPv4 */
 | 
			
		||||
#define QMI_WDS_RESULT_IP_FAMILY		0x2b	/* uint8 */
 | 
			
		||||
 | 
			
		||||
#define QMI_WDS_PDP_TYPE_IPV4			0x00
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,8 @@
 | 
			
		||||
 | 
			
		||||
#define QMI_WMS_RAW_SEND		32	/* Send a raw message */
 | 
			
		||||
 | 
			
		||||
#define QMI_WMS_RAW_READ		34	/* Read raw message from storage*/
 | 
			
		||||
 | 
			
		||||
#define QMI_WMS_GET_MSG_LIST		49	/* Get list of messages from the device */
 | 
			
		||||
#define QMI_WMS_SET_ROUTES		50	/* Set routes for message memory storage */
 | 
			
		||||
#define QMI_WMS_GET_ROUTES		51	/* Get routes for message memory storage */
 | 
			
		||||
@@ -62,9 +64,17 @@ struct qmi_wms_param_message {
 | 
			
		||||
#define QMI_WMS_STORAGE_TYPE_UIM		0
 | 
			
		||||
#define QMI_WMS_STORAGE_TYPE_NV			1
 | 
			
		||||
#define QMI_WMS_STORAGE_TYPE_UNKNOWN		2
 | 
			
		||||
#define QMI_WMS_STORAGE_TYPE_NONE		255
 | 
			
		||||
 | 
			
		||||
#define QMI_WMS_MESSAGE_MODE_GSMWCDMA		1
 | 
			
		||||
 | 
			
		||||
struct qmi_wms_raw_message {
 | 
			
		||||
	uint8_t msg_tag;
 | 
			
		||||
	uint8_t msg_format;
 | 
			
		||||
	uint16_t msg_length;
 | 
			
		||||
	uint8_t msg_data[0];
 | 
			
		||||
} __attribute__((__packed__));
 | 
			
		||||
 | 
			
		||||
/* Get routes for message memory storage */
 | 
			
		||||
#define QMI_WMS_RESULT_ROUTE_LIST		0x01
 | 
			
		||||
#define QMI_WMS_PARAM_ROUTE_LIST		0x01
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2015 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2015-2018 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -14,9 +14,9 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "ril_plugin.h"
 | 
			
		||||
#include "ril_sim_card.h"
 | 
			
		||||
#include "ril_util.h"
 | 
			
		||||
#include "ril_log.h"
 | 
			
		||||
#include "ril_constants.h"
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
@@ -27,11 +27,11 @@
 | 
			
		||||
 * ril.h does not state that string count must be given, but that is
 | 
			
		||||
 * still expected by the modem
 | 
			
		||||
 */
 | 
			
		||||
#define RIL_QUERY_STRING_COUNT 4
 | 
			
		||||
#define RIL_SET_STRING_COUNT 5
 | 
			
		||||
#define RIL_SET_PW_STRING_COUNT 3
 | 
			
		||||
 | 
			
		||||
struct ril_call_barring {
 | 
			
		||||
	struct ril_sim_card *card;
 | 
			
		||||
	GRilIoQueue *q;
 | 
			
		||||
	guint timer_id;
 | 
			
		||||
};
 | 
			
		||||
@@ -107,7 +107,7 @@ static void ril_call_barring_query(struct ofono_call_barring *b,
 | 
			
		||||
{
 | 
			
		||||
	struct ril_call_barring *bd = ofono_call_barring_get_data(b);
 | 
			
		||||
	char cls_textual[RIL_MAX_SERVICE_LENGTH];
 | 
			
		||||
	GRilIoRequest *req = grilio_request_new();
 | 
			
		||||
	GRilIoRequest *req;
 | 
			
		||||
 | 
			
		||||
	DBG("lock: %s, services to query: %d", lock, cls);
 | 
			
		||||
 | 
			
		||||
@@ -124,15 +124,9 @@ static void ril_call_barring_query(struct ofono_call_barring *b,
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * See 3GPP 27.007 7.4 for parameter descriptions.
 | 
			
		||||
	 * According to ril.h password should be empty string "" when not
 | 
			
		||||
	 * needed, but in reality we only need to give string length as 0
 | 
			
		||||
	 */
 | 
			
		||||
	grilio_request_append_int32(req, RIL_QUERY_STRING_COUNT);
 | 
			
		||||
	grilio_request_append_utf8(req, lock);    /* Facility code */
 | 
			
		||||
	grilio_request_append_int32(req, 0);      /* Password length */
 | 
			
		||||
	grilio_request_append_utf8(req, cls_textual);
 | 
			
		||||
	grilio_request_append_utf8(req, NULL);    /* AID (not yet supported) */
 | 
			
		||||
 | 
			
		||||
	req = grilio_request_array_utf8_new(4, lock, "", cls_textual,
 | 
			
		||||
					ril_sim_card_app_aid(bd->card));
 | 
			
		||||
	ril_call_barring_submit_request(bd, req,
 | 
			
		||||
				RIL_REQUEST_QUERY_FACILITY_LOCK,
 | 
			
		||||
				ril_call_barring_query_cb, cb, data);
 | 
			
		||||
@@ -183,7 +177,7 @@ static void ril_call_barring_set(struct ofono_call_barring *b,
 | 
			
		||||
					RIL_FACILITY_UNLOCK);
 | 
			
		||||
	grilio_request_append_utf8(req, passwd);
 | 
			
		||||
	grilio_request_append_utf8(req, cls_textual);
 | 
			
		||||
	grilio_request_append_utf8(req, NULL);  /* AID (not yet supported) */
 | 
			
		||||
	grilio_request_append_utf8(req, ril_sim_card_app_aid(bd->card));
 | 
			
		||||
 | 
			
		||||
	ril_call_barring_submit_request(bd, req,
 | 
			
		||||
				RIL_REQUEST_SET_FACILITY_LOCK,
 | 
			
		||||
@@ -244,6 +238,7 @@ static int ril_call_barring_probe(struct ofono_call_barring *b,
 | 
			
		||||
	struct ril_call_barring *bd = g_new0(struct ril_call_barring, 1);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
	bd->card = ril_sim_card_ref(modem->sim_card);
 | 
			
		||||
	bd->q = grilio_queue_new(ril_modem_io(modem));
 | 
			
		||||
	bd->timer_id = g_idle_add(ril_call_barring_register, b);
 | 
			
		||||
	ofono_call_barring_set_data(b, bd);
 | 
			
		||||
@@ -261,6 +256,7 @@ static void ril_call_barring_remove(struct ofono_call_barring *b)
 | 
			
		||||
		g_source_remove(bd->timer_id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ril_sim_card_unref(bd->card);
 | 
			
		||||
	grilio_queue_cancel_all(bd->q, FALSE);
 | 
			
		||||
	grilio_queue_unref(bd->q);
 | 
			
		||||
	g_free(bd);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2015-2016 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2015-2017 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -16,7 +16,6 @@
 | 
			
		||||
#include "ril_plugin.h"
 | 
			
		||||
#include "ril_util.h"
 | 
			
		||||
#include "ril_log.h"
 | 
			
		||||
#include "ril_constants.h"
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2015 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2015-2017 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -16,7 +16,6 @@
 | 
			
		||||
#include "ril_plugin.h"
 | 
			
		||||
#include "ril_util.h"
 | 
			
		||||
#include "ril_log.h"
 | 
			
		||||
#include "ril_constants.h"
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2015 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2015-2017 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -16,7 +16,6 @@
 | 
			
		||||
#include "ril_plugin.h"
 | 
			
		||||
#include "ril_util.h"
 | 
			
		||||
#include "ril_log.h"
 | 
			
		||||
#include "ril_constants.h"
 | 
			
		||||
 | 
			
		||||
struct ril_call_volume {
 | 
			
		||||
	struct ofono_call_volume *v;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2015 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2015-2020 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -16,27 +16,141 @@
 | 
			
		||||
#include "ril_plugin.h"
 | 
			
		||||
#include "ril_util.h"
 | 
			
		||||
#include "ril_log.h"
 | 
			
		||||
#include "ril_constants.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_strv.h>
 | 
			
		||||
 | 
			
		||||
struct ril_cbs {
 | 
			
		||||
	struct ofono_cbs *cbs;
 | 
			
		||||
	GRilIoChannel *io;
 | 
			
		||||
	guint timer_id;
 | 
			
		||||
	GRilIoQueue *q;
 | 
			
		||||
	char *log_prefix;
 | 
			
		||||
	guint register_id;
 | 
			
		||||
	gulong event_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ril_set_topics(struct ofono_cbs *cbs, const char *topics,
 | 
			
		||||
				ofono_cbs_set_cb_t cb, void *data)
 | 
			
		||||
struct ril_cbs_cbd {
 | 
			
		||||
	struct ril_cbs *cd;
 | 
			
		||||
	ofono_cbs_set_cb_t cb;
 | 
			
		||||
	gpointer data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define RIL_CBS_CHECK_RETRY_MS    1000
 | 
			
		||||
#define RIL_CBS_CHECK_RETRY_COUNT 30
 | 
			
		||||
 | 
			
		||||
#define DBG_(cd,fmt,args...) DBG("%s" fmt, (cd)->log_prefix, ##args)
 | 
			
		||||
 | 
			
		||||
#define ril_cbs_cbd_free g_free
 | 
			
		||||
 | 
			
		||||
static struct ril_cbs_cbd *ril_cbs_cbd_new(struct ril_cbs *cd,
 | 
			
		||||
		ofono_cbs_set_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
	cb(ril_error_ok(&error), data);
 | 
			
		||||
	struct ril_cbs_cbd *cbd = g_new(struct ril_cbs_cbd, 1);
 | 
			
		||||
 | 
			
		||||
	cbd->cd = cd;
 | 
			
		||||
	cbd->cb = cb;
 | 
			
		||||
	cbd->data = data;
 | 
			
		||||
	return cbd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_clear_topics(struct ofono_cbs *cbs,
 | 
			
		||||
static gboolean ril_cbs_retry(GRilIoRequest *request, int ril_status,
 | 
			
		||||
               const void *resp_data, guint resp_len, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
       return ril_status == RIL_E_INVALID_STATE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cbs_request_activation(struct ril_cbs *cd,
 | 
			
		||||
		gboolean activate, GRilIoChannelResponseFunc response,
 | 
			
		||||
		GDestroyNotify destroy, void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	GRilIoRequest* req = grilio_request_sized_new(8);
 | 
			
		||||
 | 
			
		||||
	grilio_request_append_int32(req, 1);
 | 
			
		||||
	grilio_request_append_int32(req, activate ? 0 :1);
 | 
			
		||||
 | 
			
		||||
	DBG_(cd, "%sactivating CB", activate ? "" : "de");
 | 
			
		||||
	grilio_request_set_retry_func(req, ril_cbs_retry);
 | 
			
		||||
	grilio_request_set_retry(req, RIL_CBS_CHECK_RETRY_MS,
 | 
			
		||||
				RIL_CBS_CHECK_RETRY_COUNT);
 | 
			
		||||
	grilio_queue_send_request_full(cd->q, req,
 | 
			
		||||
				RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION,
 | 
			
		||||
				response, destroy, user_data);
 | 
			
		||||
	grilio_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cbs_set_config(struct ril_cbs *cd, const char *topics,
 | 
			
		||||
			GRilIoChannelResponseFunc response,
 | 
			
		||||
			GDestroyNotify destroy, void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	char **list = topics ? g_strsplit(topics, ",", 0) : NULL;
 | 
			
		||||
	int i, n = gutil_strv_length(list);
 | 
			
		||||
	GRilIoRequest* req = grilio_request_new();
 | 
			
		||||
 | 
			
		||||
	grilio_request_append_int32(req, n);
 | 
			
		||||
	for (i = 0; i < n; i++) {
 | 
			
		||||
		const char *entry = list[i];
 | 
			
		||||
		const char *delim = strchr(entry, '-');
 | 
			
		||||
		int from, to;
 | 
			
		||||
		if (delim) {
 | 
			
		||||
			char **range = g_strsplit(topics, "-", 0);
 | 
			
		||||
			from = atoi(range[0]);
 | 
			
		||||
			to = atoi(range[1]);
 | 
			
		||||
			g_strfreev(range);
 | 
			
		||||
		} else {
 | 
			
		||||
			from = to = atoi(entry);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		grilio_request_append_int32(req, from);
 | 
			
		||||
		grilio_request_append_int32(req, to);
 | 
			
		||||
		grilio_request_append_int32(req, 0);
 | 
			
		||||
		grilio_request_append_int32(req, 0xff);
 | 
			
		||||
		grilio_request_append_int32(req, 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DBG_(cd, "configuring CB");
 | 
			
		||||
	grilio_request_set_retry_func(req, ril_cbs_retry);
 | 
			
		||||
	grilio_request_set_retry(req, RIL_CBS_CHECK_RETRY_MS,
 | 
			
		||||
			RIL_CBS_CHECK_RETRY_COUNT);
 | 
			
		||||
	grilio_queue_send_request_full(cd->q, req,
 | 
			
		||||
			RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG,
 | 
			
		||||
			response, destroy, user_data);
 | 
			
		||||
	grilio_request_unref(req);
 | 
			
		||||
	g_strfreev(list);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cbs_cb(GRilIoChannel *io, int ril_status,
 | 
			
		||||
				const void *data, guint len, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cbs_cbd *cbd = user_data;
 | 
			
		||||
 | 
			
		||||
	if (cbd->cb) {
 | 
			
		||||
		struct ofono_error error;
 | 
			
		||||
 | 
			
		||||
		if (ril_status == RIL_E_SUCCESS) {
 | 
			
		||||
			cbd->cb(ril_error_ok(&error), cbd->data);
 | 
			
		||||
		} else {
 | 
			
		||||
			cbd->cb(ril_error_failure(&error), cbd->data);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cbs_set_topics(struct ofono_cbs *cbs, const char *topics,
 | 
			
		||||
				ofono_cbs_set_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
	cb(ril_error_ok(&error), data);
 | 
			
		||||
	struct ril_cbs *cd = ofono_cbs_get_data(cbs);
 | 
			
		||||
 | 
			
		||||
	DBG_(cd, "%s", topics);
 | 
			
		||||
	ril_cbs_set_config(cd, topics, ril_cbs_cb, ril_cbs_cbd_free,
 | 
			
		||||
					ril_cbs_cbd_new(cd, cb, data));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cbs_clear_topics(struct ofono_cbs *cbs,
 | 
			
		||||
				ofono_cbs_set_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cbs *cd = ofono_cbs_get_data(cbs);
 | 
			
		||||
 | 
			
		||||
	DBG_(cd, "");
 | 
			
		||||
	ril_cbs_request_activation(cd, FALSE, ril_cbs_cb, ril_cbs_cbd_free,
 | 
			
		||||
					ril_cbs_cbd_new(cd, cb, data));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cbs_notify(GRilIoChannel *io, guint code,
 | 
			
		||||
@@ -44,32 +158,40 @@ static void ril_cbs_notify(GRilIoChannel *io, guint code,
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cbs *cd = user_data;
 | 
			
		||||
	GRilIoParser rilp;
 | 
			
		||||
	char* pdu;
 | 
			
		||||
	guint32 pdu_len;
 | 
			
		||||
 | 
			
		||||
	GASSERT(code == RIL_UNSOL_ON_USSD);
 | 
			
		||||
	GASSERT(code == RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS);
 | 
			
		||||
	grilio_parser_init(&rilp, data, len);
 | 
			
		||||
	pdu = grilio_parser_get_utf8(&rilp);
 | 
			
		||||
	DBG("%s", pdu);
 | 
			
		||||
	if (pdu) {
 | 
			
		||||
		ofono_cbs_notify(cd->cbs, (const guchar *)pdu, strlen(pdu));
 | 
			
		||||
		g_free(pdu);
 | 
			
		||||
	if (grilio_parser_get_uint32(&rilp, &pdu_len)) {
 | 
			
		||||
		const void* pdu = grilio_parser_get_bytes(&rilp, pdu_len);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * By default assume that it's a length followed by the
 | 
			
		||||
		 * binary PDU data.
 | 
			
		||||
		 */
 | 
			
		||||
		if (pdu && grilio_parser_bytes_remaining(&rilp) < 4) {
 | 
			
		||||
			DBG_(cd, "%u bytes", pdu_len);
 | 
			
		||||
			ofono_cbs_notify(cd->cbs, pdu, pdu_len);
 | 
			
		||||
		} else {
 | 
			
		||||
			/*
 | 
			
		||||
			 * But I've seen cell broadcasts arriving without
 | 
			
		||||
			 * the length, simply as a blob.
 | 
			
		||||
			 */
 | 
			
		||||
			ofono_cbs_notify(cd->cbs, data, len);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean ril_cbs_register(gpointer user_data)
 | 
			
		||||
static gboolean ril_cbs_register(void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cbs *cd = user_data;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
	GASSERT(cd->timer_id);
 | 
			
		||||
	cd->timer_id = 0;
 | 
			
		||||
	ofono_cbs_register(cd->cbs);
 | 
			
		||||
 | 
			
		||||
	DBG_(cd, "registering for CB");
 | 
			
		||||
	cd->register_id = 0;
 | 
			
		||||
	cd->event_id = grilio_channel_add_unsol_event_handler(cd->io,
 | 
			
		||||
		ril_cbs_notify, RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, cd);
 | 
			
		||||
 | 
			
		||||
	/* Single-shot */
 | 
			
		||||
	return FALSE;
 | 
			
		||||
	ofono_cbs_register(cd->cbs);
 | 
			
		||||
	return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ril_cbs_probe(struct ofono_cbs *cbs, unsigned int vendor,
 | 
			
		||||
@@ -78,11 +200,15 @@ static int ril_cbs_probe(struct ofono_cbs *cbs, unsigned int vendor,
 | 
			
		||||
	struct ril_modem *modem = data;
 | 
			
		||||
	struct ril_cbs *cd = g_try_new0(struct ril_cbs, 1);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
	cd->cbs = cbs;
 | 
			
		||||
	cd->io = grilio_channel_ref(ril_modem_io(modem));
 | 
			
		||||
	cd->timer_id = g_idle_add(ril_cbs_register, cd);
 | 
			
		||||
	ofono_cbs_set_data(cbs, cd);
 | 
			
		||||
	cd->log_prefix = (modem->log_prefix && modem->log_prefix[0]) ?
 | 
			
		||||
		g_strconcat(modem->log_prefix, " ", NULL) : g_strdup("");
 | 
			
		||||
	cd->cbs = cbs;
 | 
			
		||||
 | 
			
		||||
	DBG_(cd, "");
 | 
			
		||||
	cd->io = grilio_channel_ref(ril_modem_io(modem));
 | 
			
		||||
	cd->q = grilio_queue_new(cd->io);
 | 
			
		||||
	cd->register_id = g_idle_add(ril_cbs_register, cd);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -90,15 +216,16 @@ static void ril_cbs_remove(struct ofono_cbs *cbs)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cbs *cd = ofono_cbs_get_data(cbs);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
	ofono_cbs_set_data(cbs, NULL);
 | 
			
		||||
 | 
			
		||||
	if (cd->timer_id > 0) {
 | 
			
		||||
		g_source_remove(cd->timer_id);
 | 
			
		||||
	DBG_(cd, "");
 | 
			
		||||
	if (cd->register_id) {
 | 
			
		||||
		g_source_remove(cd->register_id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofono_cbs_set_data(cbs, NULL);
 | 
			
		||||
	grilio_channel_remove_handler(cd->io, cd->event_id);
 | 
			
		||||
	grilio_channel_unref(cd->io);
 | 
			
		||||
	grilio_queue_cancel_all(cd->q, FALSE);
 | 
			
		||||
	grilio_queue_unref(cd->q);
 | 
			
		||||
	g_free(cd->log_prefix);
 | 
			
		||||
	g_free(cd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -106,8 +233,8 @@ const struct ofono_cbs_driver ril_cbs_driver = {
 | 
			
		||||
	.name           = RILMODEM_DRIVER,
 | 
			
		||||
	.probe          = ril_cbs_probe,
 | 
			
		||||
	.remove         = ril_cbs_remove,
 | 
			
		||||
	.set_topics     = ril_set_topics,
 | 
			
		||||
	.clear_topics   = ril_clear_topics
 | 
			
		||||
	.set_topics     = ril_cbs_set_topics,
 | 
			
		||||
	.clear_topics   = ril_cbs_clear_topics
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2016 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2016-2020 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2020 Open Mobile Platform LLC.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -23,27 +24,30 @@
 | 
			
		||||
#include <grilio_request.h>
 | 
			
		||||
#include <grilio_parser.h>
 | 
			
		||||
 | 
			
		||||
#include <gutil_idlepool.h>
 | 
			
		||||
#include <gutil_misc.h>
 | 
			
		||||
 | 
			
		||||
#define DISPLAY_ON_UPDATE_RATE  (1000)  /* 1 sec */
 | 
			
		||||
#define DISPLAY_OFF_UPDATE_RATE (60000) /* 1 min */
 | 
			
		||||
#define DEFAULT_UPDATE_RATE_MS  (10000) /* 10 sec */
 | 
			
		||||
#define MAX_RETRIES             (5)
 | 
			
		||||
 | 
			
		||||
typedef GObjectClass RilCellInfoClass;
 | 
			
		||||
typedef struct ril_cell_info RilCellInfo;
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info_priv {
 | 
			
		||||
struct ril_cell_info {
 | 
			
		||||
	GObject object;
 | 
			
		||||
	struct sailfish_cell_info info;
 | 
			
		||||
	GRilIoChannel *io;
 | 
			
		||||
	MceDisplay *display;
 | 
			
		||||
	struct ril_radio *radio;
 | 
			
		||||
	struct ril_sim_card *sim_card;
 | 
			
		||||
	gulong display_state_event_id;
 | 
			
		||||
	gulong radio_state_event_id;
 | 
			
		||||
	gulong sim_status_event_id;
 | 
			
		||||
	gboolean sim_card_ready;
 | 
			
		||||
	int update_rate_ms;
 | 
			
		||||
	char *log_prefix;
 | 
			
		||||
	gulong event_id;
 | 
			
		||||
	guint query_id;
 | 
			
		||||
	guint set_rate_id;
 | 
			
		||||
	gboolean enabled;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_cell_info_signal {
 | 
			
		||||
@@ -60,63 +64,36 @@ G_DEFINE_TYPE(RilCellInfo, ril_cell_info, G_TYPE_OBJECT)
 | 
			
		||||
#define RIL_CELL_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
 | 
			
		||||
	RIL_CELL_INFO_TYPE, RilCellInfo))
 | 
			
		||||
 | 
			
		||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->priv->log_prefix, ##args)
 | 
			
		||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
 | 
			
		||||
 | 
			
		||||
gint ril_cell_compare_location(const struct ril_cell *c1,
 | 
			
		||||
					const struct ril_cell *c2)
 | 
			
		||||
static inline void ril_cell_free(struct sailfish_cell *cell)
 | 
			
		||||
{
 | 
			
		||||
	if (c1 && c2) {
 | 
			
		||||
		if (c1->type != c2->type) {
 | 
			
		||||
			return c1->type - c2->type;
 | 
			
		||||
		} else if (c1->type == RIL_CELL_INFO_TYPE_GSM) {
 | 
			
		||||
			const struct ril_cell_info_gsm *g1 = &c1->info.gsm;
 | 
			
		||||
			const struct ril_cell_info_gsm *g2 = &c2->info.gsm;
 | 
			
		||||
 | 
			
		||||
			if (g1->lac != g2->lac) {
 | 
			
		||||
				return g1->lac - g2->lac;
 | 
			
		||||
			} else {
 | 
			
		||||
				return g1->cid - g2->cid;
 | 
			
		||||
			}
 | 
			
		||||
		} else if (c2->type == RIL_CELL_INFO_TYPE_WCDMA) {
 | 
			
		||||
			const struct ril_cell_info_wcdma *w1 = &c1->info.wcdma;
 | 
			
		||||
			const struct ril_cell_info_wcdma *w2 = &c2->info.wcdma;
 | 
			
		||||
 | 
			
		||||
			if (w1->lac != w2->lac) {
 | 
			
		||||
				return w1->lac - w2->lac;
 | 
			
		||||
			} else {
 | 
			
		||||
				return w1->cid - w2->cid;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			const struct ril_cell_info_lte *l1 = &c1->info.lte;
 | 
			
		||||
			const struct ril_cell_info_lte *l2 = &c2->info.lte;
 | 
			
		||||
 | 
			
		||||
			GASSERT(c1->type == RIL_CELL_INFO_TYPE_LTE);
 | 
			
		||||
			if (l1->ci != l2->ci) {
 | 
			
		||||
				return l1->ci - l2->ci;
 | 
			
		||||
			} else if (l1->pci != l2->pci) {
 | 
			
		||||
				return l1->pci - l2->pci;
 | 
			
		||||
			} else {
 | 
			
		||||
				return l1->tac - l2->tac;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else if (c1) {
 | 
			
		||||
		return 1;
 | 
			
		||||
	} else if (c2) {
 | 
			
		||||
		return -1;
 | 
			
		||||
	} else {
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	g_slice_free(struct sailfish_cell, cell);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gint ril_cell_compare_func(gconstpointer v1, gconstpointer v2)
 | 
			
		||||
static void ril_cell_free1(gpointer cell)
 | 
			
		||||
{
 | 
			
		||||
	return ril_cell_compare_location(v1, v2);
 | 
			
		||||
	ril_cell_free(cell);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *ril_cell_info_int_format(int value, const char *format)
 | 
			
		||||
{
 | 
			
		||||
	if (value == SAILFISH_CELL_INVALID_VALUE) {
 | 
			
		||||
		return "";
 | 
			
		||||
	} else {
 | 
			
		||||
		static GUtilIdlePool *ril_cell_info_pool = NULL;
 | 
			
		||||
		GUtilIdlePool *pool = gutil_idle_pool_get(&ril_cell_info_pool);
 | 
			
		||||
		char *str = g_strdup_printf(format, value);
 | 
			
		||||
 | 
			
		||||
		gutil_idle_pool_add(pool, str, g_free);
 | 
			
		||||
		return str;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean ril_cell_info_list_identical(GSList *l1, GSList *l2)
 | 
			
		||||
{
 | 
			
		||||
	while (l1 && l2) {
 | 
			
		||||
		if (memcmp(l1->data, l2->data, sizeof(struct ril_cell))) {
 | 
			
		||||
		if (memcmp(l1->data, l2->data, sizeof(struct sailfish_cell))) {
 | 
			
		||||
			return FALSE;
 | 
			
		||||
		}
 | 
			
		||||
		l1 = l1->next;
 | 
			
		||||
@@ -127,121 +104,166 @@ static gboolean ril_cell_info_list_identical(GSList *l1, GSList *l2)
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_update_cells(struct ril_cell_info *self, GSList *l)
 | 
			
		||||
{
 | 
			
		||||
	if (!ril_cell_info_list_identical(self->cells, l)) {
 | 
			
		||||
		g_slist_free_full(self->cells, g_free);
 | 
			
		||||
		self->cells = l;
 | 
			
		||||
		g_signal_emit(self, ril_cell_info_signals[
 | 
			
		||||
						SIGNAL_CELLS_CHANGED], 0);
 | 
			
		||||
	if (!ril_cell_info_list_identical(self->info.cells, l)) {
 | 
			
		||||
		g_slist_free_full(self->info.cells, ril_cell_free1);
 | 
			
		||||
		self->info.cells = l;
 | 
			
		||||
		g_signal_emit(self, ril_cell_info_signals
 | 
			
		||||
			[SIGNAL_CELLS_CHANGED], 0);
 | 
			
		||||
	} else {
 | 
			
		||||
		g_slist_free_full(l, g_free);
 | 
			
		||||
		g_slist_free_full(l, ril_cell_free1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ril_cell *ril_cell_info_parse_cell_gsm(GRilIoParser *rilp,
 | 
			
		||||
							gboolean registered)
 | 
			
		||||
static struct sailfish_cell *ril_cell_info_parse_cell_gsm(GRilIoParser *rilp,
 | 
			
		||||
					guint version, gboolean registered)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell *cell = g_new0(struct ril_cell, 1);
 | 
			
		||||
	struct ril_cell_info_gsm *gsm = &cell->info.gsm;
 | 
			
		||||
	struct sailfish_cell *cell = g_slice_new0(struct sailfish_cell);
 | 
			
		||||
	struct sailfish_cell_info_gsm *gsm = &cell->info.gsm;
 | 
			
		||||
 | 
			
		||||
	/* Optional RIL_CellIdentityGsm_v12 part */
 | 
			
		||||
	gsm->arfcn = SAILFISH_CELL_INVALID_VALUE;
 | 
			
		||||
	gsm->bsic = SAILFISH_CELL_INVALID_VALUE;
 | 
			
		||||
	/* Optional RIL_GSM_SignalStrength_v12 part */
 | 
			
		||||
	gsm->timingAdvance = SAILFISH_CELL_INVALID_VALUE;
 | 
			
		||||
	/* RIL_CellIdentityGsm */
 | 
			
		||||
	if (grilio_parser_get_int32(rilp, &gsm->mcc) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, &gsm->mnc) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, &gsm->lac) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, &gsm->cid) &&
 | 
			
		||||
		(version < 12 || /* RIL_CellIdentityGsm_v12 part */
 | 
			
		||||
			(grilio_parser_get_int32(rilp, &gsm->arfcn) &&
 | 
			
		||||
			grilio_parser_get_int32(rilp, &gsm->bsic))) &&
 | 
			
		||||
		/* RIL_GW_SignalStrength */
 | 
			
		||||
		grilio_parser_get_int32(rilp, &gsm->signalStrength) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, &gsm->bitErrorRate)) {
 | 
			
		||||
		DBG("[gsm] reg=%d,mcc=%d,mnc=%d,lac=%d,cid=%d,"
 | 
			
		||||
			"strength=%d,err=%d", registered, gsm->mcc, gsm->mnc,
 | 
			
		||||
			gsm->lac, gsm->cid, gsm->signalStrength,
 | 
			
		||||
			gsm->bitErrorRate);
 | 
			
		||||
		cell->type = RIL_CELL_INFO_TYPE_GSM;
 | 
			
		||||
		grilio_parser_get_int32(rilp, &gsm->bitErrorRate) &&
 | 
			
		||||
		(version < 12 || /* RIL_GSM_SignalStrength_v12 part */
 | 
			
		||||
			grilio_parser_get_int32(rilp, &gsm->timingAdvance))) {
 | 
			
		||||
		DBG("[gsm] reg=%d%s%s%s%s%s%s%s%s%s", registered,
 | 
			
		||||
			ril_cell_info_int_format(gsm->mcc, ",mcc=%d"),
 | 
			
		||||
			ril_cell_info_int_format(gsm->mnc, ",mnc=%d"),
 | 
			
		||||
			ril_cell_info_int_format(gsm->lac, ",lac=%d"),
 | 
			
		||||
			ril_cell_info_int_format(gsm->cid, ",cid=%d"),
 | 
			
		||||
			ril_cell_info_int_format(gsm->arfcn, ",arfcn=%d"),
 | 
			
		||||
			ril_cell_info_int_format(gsm->bsic, ",bsic=%d"),
 | 
			
		||||
			ril_cell_info_int_format(gsm->signalStrength,
 | 
			
		||||
							",strength=%d"),
 | 
			
		||||
			ril_cell_info_int_format(gsm->bitErrorRate, ",err=%d"),
 | 
			
		||||
			ril_cell_info_int_format(gsm->timingAdvance, ",t=%d"));
 | 
			
		||||
		cell->type = SAILFISH_CELL_TYPE_GSM;
 | 
			
		||||
		cell->registered = registered;
 | 
			
		||||
		return cell;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofono_error("failed to parse GSM cell info");
 | 
			
		||||
	g_free(cell);
 | 
			
		||||
	ril_cell_free(cell);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ril_cell *ril_cell_info_parse_cell_wcdma(GRilIoParser *rilp,
 | 
			
		||||
							gboolean registered)
 | 
			
		||||
static struct sailfish_cell *ril_cell_info_parse_cell_wcdma(GRilIoParser *rilp,
 | 
			
		||||
					guint version, gboolean registered)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell *cell = g_new0(struct ril_cell, 1);
 | 
			
		||||
	struct ril_cell_info_wcdma *wcdma = &cell->info.wcdma;
 | 
			
		||||
	struct sailfish_cell *cell = g_slice_new0(struct sailfish_cell);
 | 
			
		||||
	struct sailfish_cell_info_wcdma *wcdma = &cell->info.wcdma;
 | 
			
		||||
 | 
			
		||||
	/* Optional RIL_CellIdentityWcdma_v12 part */
 | 
			
		||||
	wcdma->uarfcn = SAILFISH_CELL_INVALID_VALUE;
 | 
			
		||||
	if (grilio_parser_get_int32(rilp, &wcdma->mcc) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, &wcdma->mnc) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, &wcdma->lac) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, &wcdma->cid) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, &wcdma->psc) &&
 | 
			
		||||
		(version < 12 || /* RIL_CellIdentityWcdma_v12 part */
 | 
			
		||||
			grilio_parser_get_int32(rilp, &wcdma->uarfcn)) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, &wcdma->signalStrength) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, &wcdma->bitErrorRate)) {
 | 
			
		||||
		DBG("[wcdma] reg=%d,mcc=%d,mnc=%d,lac=%d,cid=%d,psc=%d,"
 | 
			
		||||
			"strength=%d,err=%d", registered, wcdma->mcc,
 | 
			
		||||
			wcdma->mnc, wcdma->lac, wcdma->cid, wcdma->psc,
 | 
			
		||||
			wcdma->signalStrength, wcdma->bitErrorRate);
 | 
			
		||||
		cell->type = RIL_CELL_INFO_TYPE_WCDMA;
 | 
			
		||||
		DBG("[wcdma] reg=%d%s%s%s%s%s%s%s", registered,
 | 
			
		||||
			ril_cell_info_int_format(wcdma->mcc, ",mcc=%d"),
 | 
			
		||||
			ril_cell_info_int_format(wcdma->mnc, ",mnc=%d"),
 | 
			
		||||
			ril_cell_info_int_format(wcdma->lac, ",lac=%d"),
 | 
			
		||||
			ril_cell_info_int_format(wcdma->cid, ",cid=%d"),
 | 
			
		||||
			ril_cell_info_int_format(wcdma->psc, ",psc=%d"),
 | 
			
		||||
			ril_cell_info_int_format(wcdma->signalStrength,
 | 
			
		||||
							",strength=%d"),
 | 
			
		||||
			ril_cell_info_int_format(wcdma->bitErrorRate,
 | 
			
		||||
							",err=%d"));
 | 
			
		||||
		cell->type = SAILFISH_CELL_TYPE_WCDMA;
 | 
			
		||||
		cell->registered = registered;
 | 
			
		||||
		return cell;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofono_error("failed to parse WCDMA cell info");
 | 
			
		||||
	g_free(cell);
 | 
			
		||||
	ril_cell_free(cell);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ril_cell *ril_cell_info_parse_cell_lte(GRilIoParser *rilp,
 | 
			
		||||
							gboolean registered)
 | 
			
		||||
static struct sailfish_cell *ril_cell_info_parse_cell_lte(GRilIoParser *rilp,
 | 
			
		||||
					guint version, gboolean registered)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell *cell = g_new0(struct ril_cell, 1);
 | 
			
		||||
	struct ril_cell_info_lte *lte = &cell->info.lte;
 | 
			
		||||
	struct sailfish_cell *cell = g_slice_new0(struct sailfish_cell);
 | 
			
		||||
	struct sailfish_cell_info_lte *lte = &cell->info.lte;
 | 
			
		||||
 | 
			
		||||
	/* Optional RIL_CellIdentityLte_v12 part */
 | 
			
		||||
	lte->earfcn = SAILFISH_CELL_INVALID_VALUE;
 | 
			
		||||
	if (grilio_parser_get_int32(rilp, <e->mcc) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, <e->mnc) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, <e->ci) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, <e->pci) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, <e->tac) &&
 | 
			
		||||
		(version < 12 || /* RIL_CellIdentityLte_v12 part */
 | 
			
		||||
			grilio_parser_get_int32(rilp, <e->earfcn)) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, <e->signalStrength) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, <e->rsrp) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, <e->rsrq) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, <e->rssnr) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, <e->cqi) &&
 | 
			
		||||
		grilio_parser_get_int32(rilp, <e->timingAdvance)) {
 | 
			
		||||
		DBG("[lte] reg=%d,mcc=%d,mnc=%d,ci=%d,pci=%d,tac=%d,"
 | 
			
		||||
			"strength=%d,rsrp=%d,rsrq=0x%x,rssnr=0x%x,cqi=%d,"
 | 
			
		||||
			"t=0x%x", registered, lte->mcc, lte->mnc, lte->ci,
 | 
			
		||||
			lte->pci, lte->tac, lte->signalStrength, lte->rsrp,
 | 
			
		||||
			lte->rsrq, lte->rssnr, lte->cqi, lte->timingAdvance);
 | 
			
		||||
		cell->type = RIL_CELL_INFO_TYPE_LTE;
 | 
			
		||||
		DBG("[lte] reg=%d%s%s%s%s%s%s%s%s%s%s%s", registered,
 | 
			
		||||
			ril_cell_info_int_format(lte->mcc, ",mcc=%d"),
 | 
			
		||||
			ril_cell_info_int_format(lte->mnc, ",mnc=%d"),
 | 
			
		||||
			ril_cell_info_int_format(lte->ci, ",ci=%d"),
 | 
			
		||||
			ril_cell_info_int_format(lte->pci, ",pci=%d"),
 | 
			
		||||
			ril_cell_info_int_format(lte->tac, ",tac=%d"),
 | 
			
		||||
			ril_cell_info_int_format(lte->signalStrength,
 | 
			
		||||
							",strength=%d"),
 | 
			
		||||
			ril_cell_info_int_format(lte->rsrp, ",rsrp=%d"),
 | 
			
		||||
			ril_cell_info_int_format(lte->rsrq, ",rsrq=%d"),
 | 
			
		||||
			ril_cell_info_int_format(lte->rssnr, ",rssnr=%d"),
 | 
			
		||||
			ril_cell_info_int_format(lte->cqi, ",cqi=%d"),
 | 
			
		||||
			ril_cell_info_int_format(lte->timingAdvance, ",t=%d"));
 | 
			
		||||
		cell->type = SAILFISH_CELL_TYPE_LTE;
 | 
			
		||||
		cell->registered = registered;
 | 
			
		||||
		return cell;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ofono_error("failed to parse LTE cell info");
 | 
			
		||||
	g_free(cell);
 | 
			
		||||
	ril_cell_free(cell);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static enum ril_cell_info_type ril_cell_info_parse_cell(GRilIoParser *rilp,
 | 
			
		||||
						struct ril_cell **cell_ptr)
 | 
			
		||||
static gboolean ril_cell_info_parse_cell(GRilIoParser *rilp, guint v,
 | 
			
		||||
					struct sailfish_cell **cell_ptr)
 | 
			
		||||
{
 | 
			
		||||
	int type, reg;
 | 
			
		||||
 | 
			
		||||
	if (grilio_parser_get_int32(rilp, &type) &&
 | 
			
		||||
			grilio_parser_get_int32(rilp, ®) &&
 | 
			
		||||
			/* Skip timestamp */
 | 
			
		||||
			grilio_parser_get_int32_array(rilp, NULL, 3)) {
 | 
			
		||||
		int skip = 0;
 | 
			
		||||
		struct ril_cell *cell = NULL;
 | 
			
		||||
		struct sailfish_cell *cell = NULL;
 | 
			
		||||
 | 
			
		||||
		/* Normalize the boolean value */
 | 
			
		||||
		reg = (reg != FALSE);
 | 
			
		||||
 | 
			
		||||
		switch (type) {
 | 
			
		||||
		case RIL_CELL_INFO_TYPE_GSM:
 | 
			
		||||
			cell = ril_cell_info_parse_cell_gsm(rilp, reg);
 | 
			
		||||
			cell = ril_cell_info_parse_cell_gsm(rilp, v, reg);
 | 
			
		||||
			break;
 | 
			
		||||
		case RIL_CELL_INFO_TYPE_WCDMA:
 | 
			
		||||
			cell = ril_cell_info_parse_cell_wcdma(rilp, reg);
 | 
			
		||||
			cell = ril_cell_info_parse_cell_wcdma(rilp, v, reg);
 | 
			
		||||
			break;
 | 
			
		||||
		case RIL_CELL_INFO_TYPE_LTE:
 | 
			
		||||
			cell = ril_cell_info_parse_cell_lte(rilp, reg);
 | 
			
		||||
			cell = ril_cell_info_parse_cell_lte(rilp, v, reg);
 | 
			
		||||
			break;
 | 
			
		||||
		case RIL_CELL_INFO_TYPE_CDMA:
 | 
			
		||||
			skip = 10;
 | 
			
		||||
@@ -256,20 +278,20 @@ static enum ril_cell_info_type ril_cell_info_parse_cell(GRilIoParser *rilp,
 | 
			
		||||
 | 
			
		||||
		if (cell) {
 | 
			
		||||
			*cell_ptr = cell;
 | 
			
		||||
			return type;
 | 
			
		||||
			return TRUE;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (skip && grilio_parser_get_int32_array(rilp, NULL, skip)) {
 | 
			
		||||
			*cell_ptr = NULL;
 | 
			
		||||
			return type;
 | 
			
		||||
			return TRUE;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*cell_ptr = NULL;
 | 
			
		||||
	return RIL_CELL_INFO_TYPE_NONE;
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GSList *ril_cell_info_parse_list(const void *data, guint len)
 | 
			
		||||
static GSList *ril_cell_info_parse_list(guint v, const void *data, guint len)
 | 
			
		||||
{
 | 
			
		||||
	GSList *l = NULL;
 | 
			
		||||
	GRilIoParser rilp;
 | 
			
		||||
@@ -277,17 +299,18 @@ static GSList *ril_cell_info_parse_list(const void *data, guint len)
 | 
			
		||||
 | 
			
		||||
	grilio_parser_init(&rilp, data, len);
 | 
			
		||||
	if (grilio_parser_get_int32(&rilp, &n) && n > 0) {
 | 
			
		||||
		struct ril_cell *c;
 | 
			
		||||
		struct sailfish_cell *c;
 | 
			
		||||
 | 
			
		||||
		DBG("%d cell(s):", n);
 | 
			
		||||
		for (i=0; i<n && ril_cell_info_parse_cell(&rilp, &c); i++) {
 | 
			
		||||
		for (i=0; i<n && ril_cell_info_parse_cell(&rilp, v, &c); i++) {
 | 
			
		||||
			if (c) {
 | 
			
		||||
				l = g_slist_insert_sorted(l, c,
 | 
			
		||||
						ril_cell_compare_func);
 | 
			
		||||
						sailfish_cell_compare_func);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	GASSERT(grilio_parser_at_end(&rilp));
 | 
			
		||||
	return l;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -297,85 +320,80 @@ static void ril_cell_info_list_changed_cb(GRilIoChannel *io, guint code,
 | 
			
		||||
	struct ril_cell_info *self = RIL_CELL_INFO(user_data);
 | 
			
		||||
 | 
			
		||||
	DBG_(self, "");
 | 
			
		||||
	ril_cell_info_update_cells(self, ril_cell_info_parse_list(data, len));
 | 
			
		||||
	ril_cell_info_update_cells(self, ril_cell_info_parse_list
 | 
			
		||||
						(io->ril_version, data, len));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_list_cb(GRilIoChannel *io, int status,
 | 
			
		||||
				const void *data, guint len, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info *self = RIL_CELL_INFO(user_data);
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
 | 
			
		||||
	DBG_(self, "");
 | 
			
		||||
	GASSERT(priv->query_id);
 | 
			
		||||
	priv->query_id = 0;
 | 
			
		||||
	ril_cell_info_update_cells(self, ril_cell_info_parse_list(data, len));
 | 
			
		||||
	GASSERT(self->query_id);
 | 
			
		||||
	self->query_id = 0;
 | 
			
		||||
	ril_cell_info_update_cells(self,
 | 
			
		||||
		(status == RIL_E_SUCCESS && self->enabled) ?
 | 
			
		||||
		ril_cell_info_parse_list(io->ril_version, data, len) : NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_set_rate_cb(GRilIoChannel *io, int status,
 | 
			
		||||
				const void *data, guint len, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info *self = RIL_CELL_INFO(user_data);
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
 | 
			
		||||
	DBG_(self, "");
 | 
			
		||||
	GASSERT(priv->set_rate_id);
 | 
			
		||||
	priv->set_rate_id = 0;
 | 
			
		||||
	GASSERT(self->set_rate_id);
 | 
			
		||||
	self->set_rate_id = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean ril_cell_info_retry(GRilIoRequest* request, int ril_status,
 | 
			
		||||
		const void* response_data, guint response_len, void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info *self = RIL_CELL_INFO(user_data);
 | 
			
		||||
 | 
			
		||||
	switch (ril_status) {
 | 
			
		||||
	case RIL_E_SUCCESS:
 | 
			
		||||
	case RIL_E_RADIO_NOT_AVAILABLE:
 | 
			
		||||
		return FALSE;
 | 
			
		||||
	default:
 | 
			
		||||
		return self->enabled;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_query(struct ril_cell_info *self)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
	GRilIoRequest *req = grilio_request_new();
 | 
			
		||||
 | 
			
		||||
	grilio_request_set_retry(req, RIL_RETRY_MS, -1);
 | 
			
		||||
	grilio_channel_cancel_request(priv->io, priv->query_id, FALSE);
 | 
			
		||||
	priv->query_id = grilio_channel_send_request_full(priv->io, req,
 | 
			
		||||
	grilio_request_set_retry(req, RIL_RETRY_MS, MAX_RETRIES);
 | 
			
		||||
	grilio_request_set_retry_func(req, ril_cell_info_retry);
 | 
			
		||||
	grilio_channel_cancel_request(self->io, self->query_id, FALSE);
 | 
			
		||||
	self->query_id = grilio_channel_send_request_full(self->io, req,
 | 
			
		||||
		RIL_REQUEST_GET_CELL_INFO_LIST, ril_cell_info_list_cb,
 | 
			
		||||
		NULL, self);
 | 
			
		||||
	grilio_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_set_rate(struct ril_cell_info *self, int ms)
 | 
			
		||||
static void ril_cell_info_set_rate(struct ril_cell_info *self)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
	GRilIoRequest *req = grilio_request_sized_new(8);
 | 
			
		||||
	GRilIoRequest *req = grilio_request_array_int32_new(1,
 | 
			
		||||
		(self->update_rate_ms >= 0 && self->enabled) ?
 | 
			
		||||
		self->update_rate_ms : INT_MAX);
 | 
			
		||||
 | 
			
		||||
	grilio_request_append_int32(req, 1);
 | 
			
		||||
	grilio_request_append_int32(req, ms);
 | 
			
		||||
	grilio_request_set_retry(req, RIL_RETRY_MS, -1);
 | 
			
		||||
	grilio_channel_cancel_request(priv->io, priv->set_rate_id, FALSE);
 | 
			
		||||
	priv->set_rate_id = grilio_channel_send_request_full(priv->io, req,
 | 
			
		||||
	grilio_request_set_retry(req, RIL_RETRY_MS, MAX_RETRIES);
 | 
			
		||||
	grilio_request_set_retry_func(req, ril_cell_info_retry);
 | 
			
		||||
	grilio_channel_cancel_request(self->io, self->set_rate_id, FALSE);
 | 
			
		||||
	self->set_rate_id = grilio_channel_send_request_full(self->io, req,
 | 
			
		||||
			RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE,
 | 
			
		||||
			ril_cell_info_set_rate_cb, NULL, self);
 | 
			
		||||
	grilio_request_unref(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_update_rate(struct ril_cell_info *self)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
 | 
			
		||||
	ril_cell_info_set_rate(self,
 | 
			
		||||
		(priv->display->state == MCE_DISPLAY_STATE_OFF) ?
 | 
			
		||||
		DISPLAY_OFF_UPDATE_RATE : DISPLAY_ON_UPDATE_RATE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_display_state_cb(MceDisplay *display, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info *self = RIL_CELL_INFO(arg);
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
 | 
			
		||||
	if (priv->sim_card_ready) {
 | 
			
		||||
		ril_cell_info_update_rate(self);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_refresh(struct ril_cell_info *self)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
 | 
			
		||||
	/* RIL_REQUEST_GET_CELL_INFO_LIST fails without SIM card */
 | 
			
		||||
	if (priv->radio->state == RADIO_STATE_ON && priv->sim_card_ready) {
 | 
			
		||||
	if (self->enabled && self->radio->state == RADIO_STATE_ON &&
 | 
			
		||||
						self->sim_card_ready) {
 | 
			
		||||
		ril_cell_info_query(self);
 | 
			
		||||
	} else {
 | 
			
		||||
		ril_cell_info_update_cells(self, NULL);
 | 
			
		||||
@@ -393,124 +411,180 @@ static void ril_cell_info_radio_state_cb(struct ril_radio *radio, void *arg)
 | 
			
		||||
static void ril_cell_info_sim_status_cb(struct ril_sim_card *sim, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info *self = RIL_CELL_INFO(arg);
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
	const gboolean sim_card_was_ready = priv->sim_card_ready;
 | 
			
		||||
 | 
			
		||||
	DBG_(self, "%sready", ril_sim_card_ready(sim) ? "" : "not ");
 | 
			
		||||
	priv->sim_card_ready = ril_sim_card_ready(sim);
 | 
			
		||||
	if (priv->sim_card_ready != sim_card_was_ready) {
 | 
			
		||||
		ril_cell_info_refresh(self);
 | 
			
		||||
		if (priv->sim_card_ready) {
 | 
			
		||||
			ril_cell_info_update_rate(self);
 | 
			
		||||
	self->sim_card_ready = ril_sim_card_ready(sim);
 | 
			
		||||
	DBG_(self, "%sready", self->sim_card_ready ? "" : "not ");
 | 
			
		||||
	ril_cell_info_refresh(self);
 | 
			
		||||
	if (self->sim_card_ready) {
 | 
			
		||||
		ril_cell_info_set_rate(self);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* sailfish_cell_info interface callbacks */
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info_closure {
 | 
			
		||||
	GCClosure cclosure;
 | 
			
		||||
	sailfish_cell_info_cb_t cb;
 | 
			
		||||
	void *arg;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline struct ril_cell_info *ril_cell_info_cast
 | 
			
		||||
					(struct sailfish_cell_info *info)
 | 
			
		||||
{
 | 
			
		||||
	return G_CAST(info, struct ril_cell_info, info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_ref_proc(struct sailfish_cell_info *info)
 | 
			
		||||
{
 | 
			
		||||
	g_object_ref(ril_cell_info_cast(info));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_unref_proc(struct sailfish_cell_info *info)
 | 
			
		||||
{
 | 
			
		||||
	g_object_unref(ril_cell_info_cast(info));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_cells_changed_cb(struct ril_cell_info *self,
 | 
			
		||||
					struct ril_cell_info_closure *closure)
 | 
			
		||||
{
 | 
			
		||||
	closure->cb(&self->info, closure->arg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gulong ril_cell_info_add_cells_changed_handler_proc
 | 
			
		||||
				(struct sailfish_cell_info *info,
 | 
			
		||||
					sailfish_cell_info_cb_t cb, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	if (cb) {
 | 
			
		||||
		struct ril_cell_info_closure *closure =
 | 
			
		||||
			(struct ril_cell_info_closure *) g_closure_new_simple
 | 
			
		||||
				(sizeof(struct ril_cell_info_closure), NULL);
 | 
			
		||||
		GCClosure* cc = &closure->cclosure;
 | 
			
		||||
 | 
			
		||||
		cc->closure.data = closure;
 | 
			
		||||
		cc->callback = G_CALLBACK(ril_cell_info_cells_changed_cb);
 | 
			
		||||
		closure->cb = cb;
 | 
			
		||||
		closure->arg = arg;
 | 
			
		||||
		return g_signal_connect_closure_by_id(ril_cell_info_cast(info),
 | 
			
		||||
				ril_cell_info_signals[SIGNAL_CELLS_CHANGED], 0,
 | 
			
		||||
				&cc->closure, FALSE);
 | 
			
		||||
	} else {
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_remove_handler_proc(struct sailfish_cell_info *info,
 | 
			
		||||
								gulong id)
 | 
			
		||||
{
 | 
			
		||||
	if (G_LIKELY(id)) {
 | 
			
		||||
		g_signal_handler_disconnect(ril_cell_info_cast(info), id);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_set_update_interval_proc
 | 
			
		||||
				(struct sailfish_cell_info *info, int ms)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info *self = ril_cell_info_cast(info);
 | 
			
		||||
 | 
			
		||||
	if (self->update_rate_ms != ms) {
 | 
			
		||||
		self->update_rate_ms = ms;
 | 
			
		||||
		DBG_(self, "%d ms", ms);
 | 
			
		||||
		if (self->enabled && self->sim_card_ready) {
 | 
			
		||||
			ril_cell_info_set_rate(self);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong ril_cell_info_add_cells_changed_handler(struct ril_cell_info *self,
 | 
			
		||||
					ril_cell_info_cb_t cb, void *arg)
 | 
			
		||||
void ril_cell_info_set_enabled_proc(struct sailfish_cell_info *info,
 | 
			
		||||
							gboolean enabled)
 | 
			
		||||
{
 | 
			
		||||
	return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self,
 | 
			
		||||
		SIGNAL_CELLS_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
 | 
			
		||||
}
 | 
			
		||||
	struct ril_cell_info *self = ril_cell_info_cast(info);
 | 
			
		||||
 | 
			
		||||
void ril_cell_info_remove_handler(struct ril_cell_info *self, gulong id)
 | 
			
		||||
{
 | 
			
		||||
	if (G_LIKELY(self) && G_LIKELY(id)) {
 | 
			
		||||
		g_signal_handler_disconnect(self, id);
 | 
			
		||||
	if (self->enabled != enabled) {
 | 
			
		||||
		self->enabled = enabled;
 | 
			
		||||
		DBG_(self, "%d", enabled);
 | 
			
		||||
		ril_cell_info_refresh(self);
 | 
			
		||||
		if (self->sim_card_ready) {
 | 
			
		||||
			ril_cell_info_set_rate(self);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info *ril_cell_info_new(GRilIoChannel *io,
 | 
			
		||||
		const char *log_prefix, MceDisplay *display,
 | 
			
		||||
		struct ril_radio *radio, struct ril_sim_card *sim_card)
 | 
			
		||||
struct sailfish_cell_info *ril_cell_info_new(GRilIoChannel *io,
 | 
			
		||||
			const char *log_prefix, struct ril_radio *radio,
 | 
			
		||||
			struct ril_sim_card *sim_card)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info *self = g_object_new(RIL_CELL_INFO_TYPE, 0);
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
	static const struct sailfish_cell_info_proc ril_cell_info_proc = {
 | 
			
		||||
		ril_cell_info_ref_proc,
 | 
			
		||||
		ril_cell_info_unref_proc,
 | 
			
		||||
		ril_cell_info_add_cells_changed_handler_proc,
 | 
			
		||||
		ril_cell_info_remove_handler_proc,
 | 
			
		||||
		ril_cell_info_set_update_interval_proc,
 | 
			
		||||
		ril_cell_info_set_enabled_proc
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	priv->io = grilio_channel_ref(io);
 | 
			
		||||
	priv->display = mce_display_ref(display);
 | 
			
		||||
	priv->radio = ril_radio_ref(radio);
 | 
			
		||||
	priv->sim_card = ril_sim_card_ref(sim_card);
 | 
			
		||||
	priv->log_prefix = (log_prefix && log_prefix[0]) ?
 | 
			
		||||
	struct ril_cell_info *self = g_object_new(RIL_CELL_INFO_TYPE, 0);
 | 
			
		||||
 | 
			
		||||
	self->info.proc = &ril_cell_info_proc;
 | 
			
		||||
	self->io = grilio_channel_ref(io);
 | 
			
		||||
	self->radio = ril_radio_ref(radio);
 | 
			
		||||
	self->sim_card = ril_sim_card_ref(sim_card);
 | 
			
		||||
	self->log_prefix = (log_prefix && log_prefix[0]) ?
 | 
			
		||||
		g_strconcat(log_prefix, " ", NULL) : g_strdup("");
 | 
			
		||||
	DBG_(self, "");
 | 
			
		||||
	priv->event_id = grilio_channel_add_unsol_event_handler(priv->io,
 | 
			
		||||
	self->event_id = grilio_channel_add_unsol_event_handler(self->io,
 | 
			
		||||
		ril_cell_info_list_changed_cb, RIL_UNSOL_CELL_INFO_LIST, self);
 | 
			
		||||
	priv->display_state_event_id =
 | 
			
		||||
		mce_display_add_state_changed_handler(display,
 | 
			
		||||
			ril_cell_info_display_state_cb, self);
 | 
			
		||||
	priv->radio_state_event_id =
 | 
			
		||||
	self->radio_state_event_id =
 | 
			
		||||
		ril_radio_add_state_changed_handler(radio,
 | 
			
		||||
			ril_cell_info_radio_state_cb, self);
 | 
			
		||||
	priv->sim_status_event_id =
 | 
			
		||||
		ril_sim_card_add_status_changed_handler(priv->sim_card,
 | 
			
		||||
	self->sim_status_event_id =
 | 
			
		||||
		ril_sim_card_add_status_changed_handler(self->sim_card,
 | 
			
		||||
			ril_cell_info_sim_status_cb, self);
 | 
			
		||||
	priv->sim_card_ready = ril_sim_card_ready(sim_card);
 | 
			
		||||
	if (priv->sim_card_ready) {
 | 
			
		||||
		ril_cell_info_query(self);
 | 
			
		||||
		ril_cell_info_update_rate(self);
 | 
			
		||||
	}
 | 
			
		||||
	return self;
 | 
			
		||||
}
 | 
			
		||||
	self->sim_card_ready = ril_sim_card_ready(sim_card);
 | 
			
		||||
	ril_cell_info_refresh(self);
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info *ril_cell_info_ref(struct ril_cell_info *self)
 | 
			
		||||
{
 | 
			
		||||
	if (G_LIKELY(self)) {
 | 
			
		||||
		g_object_ref(RIL_CELL_INFO(self));
 | 
			
		||||
		return self;
 | 
			
		||||
	} else {
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ril_cell_info_unref(struct ril_cell_info *self)
 | 
			
		||||
{
 | 
			
		||||
	if (G_LIKELY(self)) {
 | 
			
		||||
		g_object_unref(RIL_CELL_INFO(self));
 | 
			
		||||
	/* Disable updates by default */
 | 
			
		||||
	self->enabled = FALSE;
 | 
			
		||||
	if (self->sim_card_ready) {
 | 
			
		||||
		ril_cell_info_set_rate(self);
 | 
			
		||||
	}
 | 
			
		||||
	return &self->info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_init(struct ril_cell_info *self)
 | 
			
		||||
{
 | 
			
		||||
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, RIL_CELL_INFO_TYPE,
 | 
			
		||||
						struct ril_cell_info_priv);
 | 
			
		||||
	self->update_rate_ms = DEFAULT_UPDATE_RATE_MS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_dispose(GObject *object)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info *self = RIL_CELL_INFO(object);
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
 | 
			
		||||
	grilio_channel_remove_handlers(priv->io, &priv->event_id, 1);
 | 
			
		||||
	if (priv->query_id) {
 | 
			
		||||
		grilio_channel_cancel_request(priv->io, priv->query_id, FALSE);
 | 
			
		||||
		priv->query_id = 0;
 | 
			
		||||
	grilio_channel_remove_handlers(self->io, &self->event_id, 1);
 | 
			
		||||
	if (self->query_id) {
 | 
			
		||||
		grilio_channel_cancel_request(self->io, self->query_id, FALSE);
 | 
			
		||||
		self->query_id = 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (priv->set_rate_id) {
 | 
			
		||||
		grilio_channel_cancel_request(priv->io, priv->set_rate_id,
 | 
			
		||||
	if (self->set_rate_id) {
 | 
			
		||||
		grilio_channel_cancel_request(self->io, self->set_rate_id,
 | 
			
		||||
									FALSE);
 | 
			
		||||
		priv->set_rate_id = 0;
 | 
			
		||||
		self->set_rate_id = 0;
 | 
			
		||||
	}
 | 
			
		||||
	gutil_disconnect_handlers(priv->display,
 | 
			
		||||
					&priv->display_state_event_id, 1);
 | 
			
		||||
	ril_radio_remove_handlers(priv->radio, &priv->radio_state_event_id, 1);
 | 
			
		||||
	ril_sim_card_remove_handlers(priv->sim_card,
 | 
			
		||||
					&priv->sim_status_event_id, 1);
 | 
			
		||||
	ril_radio_remove_handlers(self->radio, &self->radio_state_event_id, 1);
 | 
			
		||||
	ril_sim_card_remove_handlers(self->sim_card,
 | 
			
		||||
					&self->sim_status_event_id, 1);
 | 
			
		||||
	G_OBJECT_CLASS(ril_cell_info_parent_class)->dispose(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_finalize(GObject *object)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info *self = RIL_CELL_INFO(object);
 | 
			
		||||
	struct ril_cell_info_priv *priv = self->priv;
 | 
			
		||||
 | 
			
		||||
	DBG_(self, "");
 | 
			
		||||
	g_free(priv->log_prefix);
 | 
			
		||||
	grilio_channel_unref(priv->io);
 | 
			
		||||
	mce_display_unref(priv->display);
 | 
			
		||||
	ril_radio_unref(priv->radio);
 | 
			
		||||
	ril_sim_card_unref(priv->sim_card);
 | 
			
		||||
	g_slist_free_full(self->cells, g_free);
 | 
			
		||||
	g_free(self->log_prefix);
 | 
			
		||||
	grilio_channel_unref(self->io);
 | 
			
		||||
	ril_radio_unref(self->radio);
 | 
			
		||||
	ril_sim_card_unref(self->sim_card);
 | 
			
		||||
	g_slist_free_full(self->info.cells, ril_cell_free1);
 | 
			
		||||
	G_OBJECT_CLASS(ril_cell_info_parent_class)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -520,7 +594,6 @@ static void ril_cell_info_class_init(RilCellInfoClass *klass)
 | 
			
		||||
 | 
			
		||||
	object_class->dispose = ril_cell_info_dispose;
 | 
			
		||||
	object_class->finalize = ril_cell_info_finalize;
 | 
			
		||||
	g_type_class_add_private(klass, sizeof(struct ril_cell_info_priv));
 | 
			
		||||
	ril_cell_info_signals[SIGNAL_CELLS_CHANGED] =
 | 
			
		||||
		g_signal_new(SIGNAL_CELLS_CHANGED_NAME,
 | 
			
		||||
			G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2016 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2016-2019 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -17,41 +17,12 @@
 | 
			
		||||
#define RIL_CELL_INFO_H
 | 
			
		||||
 | 
			
		||||
#include "ril_types.h"
 | 
			
		||||
#include <mce_display.h>
 | 
			
		||||
 | 
			
		||||
struct ril_cell {
 | 
			
		||||
	enum ril_cell_info_type type;
 | 
			
		||||
	gboolean registered;
 | 
			
		||||
	union {
 | 
			
		||||
		struct ril_cell_info_gsm gsm;
 | 
			
		||||
		struct ril_cell_info_wcdma wcdma;
 | 
			
		||||
		struct ril_cell_info_lte lte;
 | 
			
		||||
	} info;
 | 
			
		||||
};
 | 
			
		||||
#include <sailfish_cell_info.h>
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info_priv;
 | 
			
		||||
struct ril_cell_info {
 | 
			
		||||
	GObject object;
 | 
			
		||||
	struct ril_cell_info_priv *priv;
 | 
			
		||||
	GSList *cells;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef void (*ril_cell_info_cb_t)(struct ril_cell_info *info, void *arg);
 | 
			
		||||
 | 
			
		||||
gint ril_cell_compare_func(gconstpointer v1, gconstpointer v2);
 | 
			
		||||
gint ril_cell_compare_location(const struct ril_cell *c1,
 | 
			
		||||
					const struct ril_cell *c2);
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info *ril_cell_info_new(GRilIoChannel *io,
 | 
			
		||||
		const char *log_prefix, MceDisplay *display,
 | 
			
		||||
		struct ril_radio *radio, struct ril_sim_card *sim_card);
 | 
			
		||||
struct ril_cell_info *ril_cell_info_ref(struct ril_cell_info *info);
 | 
			
		||||
void ril_cell_info_unref(struct ril_cell_info *info);
 | 
			
		||||
struct ril_cell *ril_cell_find_cell(struct ril_cell_info *info,
 | 
			
		||||
						const struct ril_cell *cell);
 | 
			
		||||
gulong ril_cell_info_add_cells_changed_handler(struct ril_cell_info *info,
 | 
			
		||||
					ril_cell_info_cb_t cb, void *arg);
 | 
			
		||||
void ril_cell_info_remove_handler(struct ril_cell_info *info, gulong id);
 | 
			
		||||
struct sailfish_cell_info *ril_cell_info_new(GRilIoChannel *io,
 | 
			
		||||
			const char *log_prefix, struct ril_radio *radio,
 | 
			
		||||
			struct ril_sim_card *sim_card);
 | 
			
		||||
 | 
			
		||||
#endif /* RIL_CELL_INFO_H */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,586 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2016 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "ril_plugin.h"
 | 
			
		||||
#include "ril_cell_info.h"
 | 
			
		||||
#include "ril_log.h"
 | 
			
		||||
 | 
			
		||||
#include <ofono/dbus.h>
 | 
			
		||||
 | 
			
		||||
#include <gdbus.h>
 | 
			
		||||
 | 
			
		||||
struct ril_cell_entry {
 | 
			
		||||
	guint cell_id;
 | 
			
		||||
	char *path;
 | 
			
		||||
	struct ril_cell cell;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info_dbus {
 | 
			
		||||
	struct ril_modem *md;
 | 
			
		||||
	struct ril_cell_info *info;
 | 
			
		||||
	DBusConnection *conn;
 | 
			
		||||
	char *path;
 | 
			
		||||
	gulong handler_id;
 | 
			
		||||
	guint next_cell_id;
 | 
			
		||||
	GSList *entries;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define RIL_CELL_INFO_DBUS_INTERFACE             "org.nemomobile.ofono.CellInfo"
 | 
			
		||||
#define RIL_CELL_INFO_DBUS_CELLS_ADDED_SIGNAL    "CellsAdded"
 | 
			
		||||
#define RIL_CELL_INFO_DBUS_CELLS_REMOVED_SIGNAL  "CellsRemoved"
 | 
			
		||||
 | 
			
		||||
#define RIL_CELL_DBUS_INTERFACE_VERSION          (1)
 | 
			
		||||
#define RIL_CELL_DBUS_INTERFACE                  "org.nemomobile.ofono.Cell"
 | 
			
		||||
#define RIL_CELL_DBUS_REGISTERED_CHANGED_SIGNAL  "RegisteredChanged"
 | 
			
		||||
#define RIL_CELL_DBUS_PROPERTY_CHANGED_SIGNAL    "PropertyChanged"
 | 
			
		||||
#define RIL_CELL_DBUS_REMOVED_SIGNAL             "Removed"
 | 
			
		||||
 | 
			
		||||
struct ril_cell_property {
 | 
			
		||||
	const char *name;
 | 
			
		||||
	glong off;
 | 
			
		||||
	int flag;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define RIL_CELL_GSM_PROPERTY(value,name) \
 | 
			
		||||
	{ #name, G_STRUCT_OFFSET(struct ril_cell_info_gsm,name), value }
 | 
			
		||||
#define RIL_CELL_WCDMA_PROPERTY(value,name) \
 | 
			
		||||
	{ #name, G_STRUCT_OFFSET(struct ril_cell_info_wcdma,name), value }
 | 
			
		||||
#define RIL_CELL_LTE_PROPERTY(value,name) \
 | 
			
		||||
	{ #name, G_STRUCT_OFFSET(struct ril_cell_info_lte,name), value }
 | 
			
		||||
 | 
			
		||||
static const struct ril_cell_property ril_cell_gsm_properties [] = {
 | 
			
		||||
	RIL_CELL_GSM_PROPERTY(0x01,mcc),
 | 
			
		||||
	RIL_CELL_GSM_PROPERTY(0x02,mnc),
 | 
			
		||||
	RIL_CELL_GSM_PROPERTY(0x04,lac),
 | 
			
		||||
	RIL_CELL_GSM_PROPERTY(0x08,cid),
 | 
			
		||||
	RIL_CELL_GSM_PROPERTY(0x10,signalStrength),
 | 
			
		||||
	RIL_CELL_GSM_PROPERTY(0x20,bitErrorRate)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct ril_cell_property ril_cell_wcdma_properties [] = {
 | 
			
		||||
	RIL_CELL_WCDMA_PROPERTY(0x01,mcc),
 | 
			
		||||
	RIL_CELL_WCDMA_PROPERTY(0x02,mnc),
 | 
			
		||||
	RIL_CELL_WCDMA_PROPERTY(0x04,lac),
 | 
			
		||||
	RIL_CELL_WCDMA_PROPERTY(0x08,cid),
 | 
			
		||||
	RIL_CELL_WCDMA_PROPERTY(0x10,psc),
 | 
			
		||||
	RIL_CELL_WCDMA_PROPERTY(0x20,signalStrength),
 | 
			
		||||
	RIL_CELL_WCDMA_PROPERTY(0x40,bitErrorRate)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct ril_cell_property ril_cell_lte_properties [] = {
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x001,mcc),
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x002,mnc),
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x004,ci),
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x008,pci),
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x010,tac),
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x020,signalStrength),
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x040,rsrp),
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x080,rsrq),
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x100,rssnr),
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x200,cqi),
 | 
			
		||||
	RIL_CELL_LTE_PROPERTY(0x400,timingAdvance)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define RIL_CELL_PROPERTY_REGISTERED 0x1000
 | 
			
		||||
 | 
			
		||||
typedef void (*ril_cell_info_dbus_append_fn)(DBusMessageIter *it,
 | 
			
		||||
					const struct ril_cell_entry *entry);
 | 
			
		||||
 | 
			
		||||
static const char *ril_cell_info_dbus_cell_type_str(enum ril_cell_info_type t)
 | 
			
		||||
{
 | 
			
		||||
	switch (t) {
 | 
			
		||||
	case RIL_CELL_INFO_TYPE_GSM:
 | 
			
		||||
		return "gsm";
 | 
			
		||||
	case RIL_CELL_INFO_TYPE_CDMA:
 | 
			
		||||
		return "cdma";
 | 
			
		||||
	case RIL_CELL_INFO_TYPE_LTE:
 | 
			
		||||
		return "lte";
 | 
			
		||||
	case RIL_CELL_INFO_TYPE_WCDMA:
 | 
			
		||||
		return "wcdma";
 | 
			
		||||
	case RIL_CELL_INFO_TYPE_TD_SCDMA:
 | 
			
		||||
		return "tdscdma";
 | 
			
		||||
	case RIL_CELL_INFO_TYPE_NONE:
 | 
			
		||||
	default:
 | 
			
		||||
		return "unknown";
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct ril_cell_property *ril_cell_info_dbus_cell_properties(
 | 
			
		||||
				enum ril_cell_info_type type, int *count)
 | 
			
		||||
{
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case RIL_CELL_INFO_TYPE_GSM:
 | 
			
		||||
		*count = G_N_ELEMENTS(ril_cell_gsm_properties);
 | 
			
		||||
		return ril_cell_gsm_properties;
 | 
			
		||||
	case RIL_CELL_INFO_TYPE_WCDMA:
 | 
			
		||||
		*count = G_N_ELEMENTS(ril_cell_wcdma_properties);
 | 
			
		||||
		return ril_cell_wcdma_properties;
 | 
			
		||||
	case RIL_CELL_INFO_TYPE_LTE:
 | 
			
		||||
		*count = G_N_ELEMENTS(ril_cell_lte_properties);
 | 
			
		||||
		return ril_cell_lte_properties;
 | 
			
		||||
	default:
 | 
			
		||||
		*count = 0;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_destroy_entry(struct ril_cell_entry *entry)
 | 
			
		||||
{
 | 
			
		||||
	if (entry) {
 | 
			
		||||
		g_free(entry->path);
 | 
			
		||||
		g_free(entry);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DBusMessage *ril_cell_info_dbus_reply(DBusMessage *msg,
 | 
			
		||||
				const struct ril_cell_entry *entry,
 | 
			
		||||
				ril_cell_info_dbus_append_fn append)
 | 
			
		||||
{
 | 
			
		||||
	DBusMessage *reply = dbus_message_new_method_return(msg);
 | 
			
		||||
	DBusMessageIter it;
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_init_append(reply, &it);
 | 
			
		||||
	append(&it, entry);
 | 
			
		||||
	return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_dbus_append_version(DBusMessageIter *it,
 | 
			
		||||
					const struct ril_cell_entry *entry)
 | 
			
		||||
{
 | 
			
		||||
	dbus_int32_t version = RIL_CELL_DBUS_INTERFACE_VERSION;
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_append_basic(it, DBUS_TYPE_INT32, &version);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_dbus_append_type(DBusMessageIter *it,
 | 
			
		||||
					const struct ril_cell_entry *entry)
 | 
			
		||||
{
 | 
			
		||||
	const char *type = ril_cell_info_dbus_cell_type_str(entry->cell.type);
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_dbus_append_registered(DBusMessageIter *it,
 | 
			
		||||
					const struct ril_cell_entry *entry)
 | 
			
		||||
{
 | 
			
		||||
	dbus_bool_t registered = entry->cell.registered;
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_append_basic(it, DBUS_TYPE_BOOLEAN, ®istered);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_dbus_append_properties(DBusMessageIter *it,
 | 
			
		||||
					const struct ril_cell_entry *entry)
 | 
			
		||||
{
 | 
			
		||||
	int i, n;
 | 
			
		||||
	DBusMessageIter dict;
 | 
			
		||||
	const struct ril_cell *cell = &entry->cell;
 | 
			
		||||
	const struct ril_cell_property *prop =
 | 
			
		||||
		ril_cell_info_dbus_cell_properties(cell->type, &n);
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, "{sv}", &dict);
 | 
			
		||||
	for (i = 0; i < n; i++) {
 | 
			
		||||
		gint32 value = G_STRUCT_MEMBER(int, &cell->info, prop[i].off);
 | 
			
		||||
		if (value != INT_MAX) {
 | 
			
		||||
			ofono_dbus_dict_append(&dict, prop[i].name,
 | 
			
		||||
						DBUS_TYPE_INT32, &value);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	dbus_message_iter_close_container(it, &dict);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_dbus_append_all(DBusMessageIter *it,
 | 
			
		||||
					const struct ril_cell_entry *entry)
 | 
			
		||||
{
 | 
			
		||||
	ril_cell_info_dbus_append_version(it, entry);
 | 
			
		||||
	ril_cell_info_dbus_append_type(it, entry);
 | 
			
		||||
	ril_cell_info_dbus_append_registered(it, entry);
 | 
			
		||||
	ril_cell_info_dbus_append_properties(it, entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DBusMessage *ril_cell_info_dbus_cell_get_all(DBusConnection *conn,
 | 
			
		||||
						DBusMessage *msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return ril_cell_info_dbus_reply(msg, (struct ril_cell_entry*)data,
 | 
			
		||||
				ril_cell_info_dbus_append_all);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DBusMessage *ril_cell_info_dbus_cell_get_version(DBusConnection *conn,
 | 
			
		||||
						DBusMessage *msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return ril_cell_info_dbus_reply(msg, (struct ril_cell_entry*)data,
 | 
			
		||||
				ril_cell_info_dbus_append_version);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DBusMessage *ril_cell_info_dbus_cell_get_type(DBusConnection *conn,
 | 
			
		||||
						DBusMessage *msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return ril_cell_info_dbus_reply(msg, (struct ril_cell_entry*)data,
 | 
			
		||||
				ril_cell_info_dbus_append_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DBusMessage *ril_cell_info_dbus_cell_get_registered(DBusConnection *conn,
 | 
			
		||||
						DBusMessage *msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return ril_cell_info_dbus_reply(msg, (struct ril_cell_entry*)data,
 | 
			
		||||
				ril_cell_info_dbus_append_registered);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DBusMessage *ril_cell_info_dbus_cell_get_properties(DBusConnection *conn,
 | 
			
		||||
						DBusMessage *msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	return ril_cell_info_dbus_reply(msg, (struct ril_cell_entry*)data,
 | 
			
		||||
				ril_cell_info_dbus_append_properties);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GDBusMethodTable ril_cell_info_dbus_cell_methods[] = {
 | 
			
		||||
	{ GDBUS_METHOD("GetAll", NULL,
 | 
			
		||||
			GDBUS_ARGS({ "version", "i" },
 | 
			
		||||
			           { "type", "s" },
 | 
			
		||||
			           { "registered", "b" },
 | 
			
		||||
			           { "properties", "a{sv}" }),
 | 
			
		||||
			ril_cell_info_dbus_cell_get_all) },
 | 
			
		||||
	{ GDBUS_METHOD("GetInterfaceVersion", NULL,
 | 
			
		||||
			GDBUS_ARGS({ "version", "i" }),
 | 
			
		||||
			ril_cell_info_dbus_cell_get_version) },
 | 
			
		||||
	{ GDBUS_METHOD("GetType", NULL,
 | 
			
		||||
			GDBUS_ARGS({ "type", "s" }),
 | 
			
		||||
			ril_cell_info_dbus_cell_get_type) },
 | 
			
		||||
	{ GDBUS_METHOD("GetRegistered", NULL,
 | 
			
		||||
			GDBUS_ARGS({ "registered", "b" }),
 | 
			
		||||
			ril_cell_info_dbus_cell_get_registered) },
 | 
			
		||||
	{ GDBUS_METHOD("GetProperties", NULL,
 | 
			
		||||
			GDBUS_ARGS({ "properties", "a{sv}" }),
 | 
			
		||||
			ril_cell_info_dbus_cell_get_properties) },
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const GDBusSignalTable ril_cell_info_dbus_cell_signals[] = {
 | 
			
		||||
	{ GDBUS_SIGNAL(RIL_CELL_DBUS_REGISTERED_CHANGED_SIGNAL,
 | 
			
		||||
			GDBUS_ARGS({ "registered", "b" })) },
 | 
			
		||||
	{ GDBUS_SIGNAL(RIL_CELL_DBUS_PROPERTY_CHANGED_SIGNAL,
 | 
			
		||||
			GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
 | 
			
		||||
	{ GDBUS_SIGNAL(RIL_CELL_DBUS_REMOVED_SIGNAL,
 | 
			
		||||
			GDBUS_ARGS({})) },
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct ril_cell_entry *ril_cell_info_dbus_find_id(
 | 
			
		||||
		struct ril_cell_info_dbus *dbus, guint id)
 | 
			
		||||
{
 | 
			
		||||
	GSList *l;
 | 
			
		||||
	for (l = dbus->entries; l; l = l->next) {
 | 
			
		||||
		struct ril_cell_entry *entry = l->data;
 | 
			
		||||
		if (entry->cell_id == id) {
 | 
			
		||||
			return entry;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static guint ril_cell_info_dbus_next_cell_id(struct ril_cell_info_dbus *dbus)
 | 
			
		||||
{
 | 
			
		||||
	while (ril_cell_info_dbus_find_id(dbus, dbus->next_cell_id)) {
 | 
			
		||||
		dbus->next_cell_id++;
 | 
			
		||||
	}
 | 
			
		||||
	return dbus->next_cell_id++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ril_cell_entry *ril_cell_info_dbus_find_cell(
 | 
			
		||||
		struct ril_cell_info_dbus *dbus, const struct ril_cell *cell)
 | 
			
		||||
{
 | 
			
		||||
	if (cell) {
 | 
			
		||||
		GSList *l;
 | 
			
		||||
		for (l = dbus->entries; l; l = l->next) {
 | 
			
		||||
			struct ril_cell_entry *entry = l->data;
 | 
			
		||||
			if (!ril_cell_compare_location(&entry->cell, cell)) {
 | 
			
		||||
				return entry;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_dbus_emit_path_list(struct ril_cell_info_dbus *dbus,
 | 
			
		||||
					const char *name, GPtrArray *list)
 | 
			
		||||
{
 | 
			
		||||
	guint i;
 | 
			
		||||
	DBusMessageIter it, array;
 | 
			
		||||
	DBusMessage *signal = dbus_message_new_signal(dbus->path,
 | 
			
		||||
					RIL_CELL_INFO_DBUS_INTERFACE, name);
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_init_append(signal, &it);
 | 
			
		||||
	dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "o", &array);
 | 
			
		||||
	for (i = 0; i < list->len; i++) {
 | 
			
		||||
		const char* path = list->pdata[i];
 | 
			
		||||
		dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
 | 
			
		||||
								&path);
 | 
			
		||||
	}
 | 
			
		||||
	dbus_message_iter_close_container(&it, &array);
 | 
			
		||||
 | 
			
		||||
	g_dbus_send_message(dbus->conn, signal);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ril_cell_info_dbus_compare(const struct ril_cell *c1,
 | 
			
		||||
					const struct ril_cell *c2)
 | 
			
		||||
{
 | 
			
		||||
	if (c1->type == c2->type) {
 | 
			
		||||
		int i, n, mask = 0;
 | 
			
		||||
		const struct ril_cell_property *prop =
 | 
			
		||||
			ril_cell_info_dbus_cell_properties(c1->type, &n);
 | 
			
		||||
 | 
			
		||||
		if (c1->registered != c2->registered) {
 | 
			
		||||
			mask |= RIL_CELL_PROPERTY_REGISTERED;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < n; i++) {
 | 
			
		||||
			const glong offset = prop[i].off;
 | 
			
		||||
			gint32 v1 = G_STRUCT_MEMBER(int, &c1->info, offset);
 | 
			
		||||
			gint32 v2 = G_STRUCT_MEMBER(int, &c2->info, offset);
 | 
			
		||||
			if (v1 != v2) {
 | 
			
		||||
				mask |= prop[i].flag;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return mask;
 | 
			
		||||
	} else {
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_dbus_property_changed(struct ril_cell_info_dbus *dbus,
 | 
			
		||||
				const struct ril_cell_entry *entry, int mask)
 | 
			
		||||
{
 | 
			
		||||
	int i, n;
 | 
			
		||||
	const struct ril_cell *cell = &entry->cell;
 | 
			
		||||
	const struct ril_cell_property *prop =
 | 
			
		||||
		ril_cell_info_dbus_cell_properties(cell->type, &n);
 | 
			
		||||
 | 
			
		||||
	if (mask & RIL_CELL_PROPERTY_REGISTERED) {
 | 
			
		||||
		dbus_bool_t registered = cell->registered;
 | 
			
		||||
		g_dbus_emit_signal(dbus->conn, entry->path,
 | 
			
		||||
			RIL_CELL_DBUS_INTERFACE,
 | 
			
		||||
			RIL_CELL_DBUS_REGISTERED_CHANGED_SIGNAL,
 | 
			
		||||
			DBUS_TYPE_BOOLEAN, ®istered, DBUS_TYPE_INVALID);
 | 
			
		||||
		mask &= ~RIL_CELL_PROPERTY_REGISTERED;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < n && mask; i++) {
 | 
			
		||||
		if (mask & prop[i].flag) {
 | 
			
		||||
			ofono_dbus_signal_property_changed(dbus->conn,
 | 
			
		||||
				entry->path, RIL_CELL_DBUS_INTERFACE,
 | 
			
		||||
				prop[i].name, DBUS_TYPE_INT32,
 | 
			
		||||
				G_STRUCT_MEMBER_P(&cell->info, prop[i].off));
 | 
			
		||||
			mask &= ~prop[i].flag;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_dbus_update_entries(struct ril_cell_info_dbus *dbus,
 | 
			
		||||
							gboolean emit_signals)
 | 
			
		||||
{
 | 
			
		||||
	GSList *l;
 | 
			
		||||
	GPtrArray* added = NULL;
 | 
			
		||||
	GPtrArray* removed = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Remove non-existent cells */
 | 
			
		||||
	l = dbus->entries;
 | 
			
		||||
	while (l) {
 | 
			
		||||
		GSList *next = l->next;
 | 
			
		||||
		struct ril_cell_entry *entry = l->data;
 | 
			
		||||
		if (!g_slist_find_custom(dbus->info->cells, &entry->cell,
 | 
			
		||||
						ril_cell_compare_func)) {
 | 
			
		||||
			DBG("%s removed", entry->path);
 | 
			
		||||
			dbus->entries = g_slist_delete_link(dbus->entries, l);
 | 
			
		||||
			g_dbus_emit_signal(dbus->conn, entry->path,
 | 
			
		||||
					RIL_CELL_DBUS_INTERFACE,
 | 
			
		||||
					RIL_CELL_DBUS_REMOVED_SIGNAL,
 | 
			
		||||
					DBUS_TYPE_INVALID);
 | 
			
		||||
			g_dbus_unregister_interface(dbus->conn, entry->path,
 | 
			
		||||
						RIL_CELL_DBUS_INTERFACE);
 | 
			
		||||
			if (emit_signals) {
 | 
			
		||||
				if (!removed) {
 | 
			
		||||
					removed =
 | 
			
		||||
						g_ptr_array_new_with_free_func(
 | 
			
		||||
								g_free);
 | 
			
		||||
				}
 | 
			
		||||
				/* Steal the path */
 | 
			
		||||
				g_ptr_array_add(removed, entry->path);
 | 
			
		||||
				entry->path = NULL;
 | 
			
		||||
			}
 | 
			
		||||
			ril_cell_info_destroy_entry(entry);
 | 
			
		||||
		}
 | 
			
		||||
		l = next;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Add new cells */
 | 
			
		||||
	for (l = dbus->info->cells; l; l = l->next) {
 | 
			
		||||
		const struct ril_cell *cell = l->data;
 | 
			
		||||
		struct ril_cell_entry *entry =
 | 
			
		||||
			ril_cell_info_dbus_find_cell(dbus, cell);
 | 
			
		||||
 | 
			
		||||
		if (entry) {
 | 
			
		||||
			if (emit_signals) {
 | 
			
		||||
				int diff = ril_cell_info_dbus_compare(cell,
 | 
			
		||||
								&entry->cell);
 | 
			
		||||
				entry->cell = *cell;
 | 
			
		||||
				ril_cell_info_dbus_property_changed(dbus,
 | 
			
		||||
								entry, diff);
 | 
			
		||||
			} else {
 | 
			
		||||
				entry->cell = *cell;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			entry = g_new0(struct ril_cell_entry, 1);
 | 
			
		||||
			entry->cell = *cell;
 | 
			
		||||
			entry->cell_id = ril_cell_info_dbus_next_cell_id(dbus);
 | 
			
		||||
			entry->path = g_strdup_printf("%s/cell_%u", dbus->path,
 | 
			
		||||
							entry->cell_id);
 | 
			
		||||
			dbus->entries = g_slist_append(dbus->entries, entry);
 | 
			
		||||
			DBG("%s added", entry->path);
 | 
			
		||||
			g_dbus_register_interface(dbus->conn, entry->path,
 | 
			
		||||
					RIL_CELL_DBUS_INTERFACE,
 | 
			
		||||
					ril_cell_info_dbus_cell_methods,
 | 
			
		||||
					ril_cell_info_dbus_cell_signals, NULL,
 | 
			
		||||
					entry, NULL);
 | 
			
		||||
			if (emit_signals) {
 | 
			
		||||
				if (!added) {
 | 
			
		||||
					added = g_ptr_array_new();
 | 
			
		||||
				}
 | 
			
		||||
				g_ptr_array_add(added, entry->path);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (removed) {
 | 
			
		||||
		ril_cell_info_dbus_emit_path_list(dbus,
 | 
			
		||||
			RIL_CELL_INFO_DBUS_CELLS_REMOVED_SIGNAL, removed);
 | 
			
		||||
		g_ptr_array_free(removed, TRUE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (added) {
 | 
			
		||||
		ril_cell_info_dbus_emit_path_list(dbus,
 | 
			
		||||
			RIL_CELL_INFO_DBUS_CELLS_ADDED_SIGNAL, added);
 | 
			
		||||
		g_ptr_array_free(added, TRUE);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_cell_info_dbus_cells_changed_cb(struct ril_cell_info *info,
 | 
			
		||||
								void *arg)
 | 
			
		||||
{
 | 
			
		||||
	DBG("");
 | 
			
		||||
	ril_cell_info_dbus_update_entries((struct ril_cell_info_dbus *)arg,
 | 
			
		||||
								TRUE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DBusMessage *ril_cell_info_dbus_get_cells(DBusConnection *conn,
 | 
			
		||||
						DBusMessage *msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info_dbus *dbus = data;
 | 
			
		||||
	DBusMessage *reply = dbus_message_new_method_return(msg);
 | 
			
		||||
	DBusMessageIter it, array;
 | 
			
		||||
	GSList *l;
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_init_append(reply, &it);
 | 
			
		||||
	dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "o", &array);
 | 
			
		||||
	for (l = dbus->entries; l; l = l->next) {
 | 
			
		||||
		const struct ril_cell_entry *entry = l->data;
 | 
			
		||||
		dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
 | 
			
		||||
								&entry->path);
 | 
			
		||||
	}
 | 
			
		||||
	dbus_message_iter_close_container(&it, &array);
 | 
			
		||||
	return reply;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GDBusMethodTable ril_cell_info_dbus_methods[] = {
 | 
			
		||||
	{ GDBUS_METHOD("GetCells", NULL,
 | 
			
		||||
			GDBUS_ARGS({ "paths", "ao" }),
 | 
			
		||||
			ril_cell_info_dbus_get_cells) },
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const GDBusSignalTable ril_cell_info_dbus_signals[] = {
 | 
			
		||||
	{ GDBUS_SIGNAL(RIL_CELL_INFO_DBUS_CELLS_ADDED_SIGNAL,
 | 
			
		||||
			GDBUS_ARGS({ "paths", "ao" })) },
 | 
			
		||||
	{ GDBUS_SIGNAL(RIL_CELL_INFO_DBUS_CELLS_REMOVED_SIGNAL,
 | 
			
		||||
			GDBUS_ARGS({ "paths", "ao" })) },
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info_dbus *ril_cell_info_dbus_new(struct ril_modem *md,
 | 
			
		||||
						struct ril_cell_info *info)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_cell_info_dbus *dbus = g_new0(struct ril_cell_info_dbus, 1);
 | 
			
		||||
 | 
			
		||||
	DBG("%s", ril_modem_get_path(md));
 | 
			
		||||
	dbus->md = md;
 | 
			
		||||
	dbus->path = g_strdup(ril_modem_get_path(md));
 | 
			
		||||
	dbus->conn = dbus_connection_ref(ofono_dbus_get_connection());
 | 
			
		||||
	dbus->info = ril_cell_info_ref(info);
 | 
			
		||||
	dbus->handler_id = ril_cell_info_add_cells_changed_handler(info,
 | 
			
		||||
				ril_cell_info_dbus_cells_changed_cb, dbus);
 | 
			
		||||
 | 
			
		||||
	/* Register D-Bus interface */
 | 
			
		||||
	if (g_dbus_register_interface(dbus->conn, dbus->path,
 | 
			
		||||
			RIL_CELL_INFO_DBUS_INTERFACE, ril_cell_info_dbus_methods,
 | 
			
		||||
			ril_cell_info_dbus_signals, NULL, dbus, NULL)) {
 | 
			
		||||
		ofono_modem_add_interface(md->ofono,
 | 
			
		||||
						RIL_CELL_INFO_DBUS_INTERFACE);
 | 
			
		||||
		ril_cell_info_dbus_update_entries(dbus, FALSE);
 | 
			
		||||
		return dbus;
 | 
			
		||||
	} else {
 | 
			
		||||
		ofono_error("RIL D-Bus register failed");
 | 
			
		||||
		ril_cell_info_dbus_free(dbus);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ril_cell_info_dbus_free(struct ril_cell_info_dbus *dbus)
 | 
			
		||||
{
 | 
			
		||||
	if (dbus) {
 | 
			
		||||
		GSList *l;
 | 
			
		||||
 | 
			
		||||
		DBG("%s", dbus->path);
 | 
			
		||||
		g_dbus_unregister_interface(dbus->conn, dbus->path,
 | 
			
		||||
						RIL_CELL_INFO_DBUS_INTERFACE);
 | 
			
		||||
		ofono_modem_remove_interface(dbus->md->ofono,
 | 
			
		||||
						RIL_CELL_INFO_DBUS_INTERFACE);
 | 
			
		||||
 | 
			
		||||
		/* Unregister cells */
 | 
			
		||||
		l = dbus->entries;
 | 
			
		||||
		while (l) {
 | 
			
		||||
			struct ril_cell_entry *entry = l->data;
 | 
			
		||||
			g_dbus_unregister_interface(dbus->conn, entry->path,
 | 
			
		||||
						RIL_CELL_DBUS_INTERFACE);
 | 
			
		||||
			ril_cell_info_destroy_entry(entry);
 | 
			
		||||
			l = l->next;
 | 
			
		||||
		}
 | 
			
		||||
		g_slist_free(dbus->entries);
 | 
			
		||||
 | 
			
		||||
		dbus_connection_unref(dbus->conn);
 | 
			
		||||
 | 
			
		||||
		ril_cell_info_remove_handler(dbus->info, dbus->handler_id);
 | 
			
		||||
		ril_cell_info_unref(dbus->info);
 | 
			
		||||
 | 
			
		||||
		g_free(dbus->path);
 | 
			
		||||
		g_free(dbus);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 8
 | 
			
		||||
 * indent-tabs-mode: t
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2015-2016 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2015-2020 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2019-2020 Open Mobile Platform LLC.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -14,10 +15,20 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "ril_config.h"
 | 
			
		||||
#include "ril_util.h"
 | 
			
		||||
#include "ril_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_intarray.h>
 | 
			
		||||
#include <gutil_ints.h>
 | 
			
		||||
#include <gutil_misc.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
/* Utilities for parsing ril_subscription.conf */
 | 
			
		||||
 | 
			
		||||
char* ril_config_get_string(GKeyFile *file, const char *group, const char *key)
 | 
			
		||||
char *ril_config_get_string(GKeyFile *file, const char *group, const char *key)
 | 
			
		||||
{
 | 
			
		||||
	char *val = g_key_file_get_string(file, group, key, NULL);
 | 
			
		||||
 | 
			
		||||
@@ -29,6 +40,31 @@ char* ril_config_get_string(GKeyFile *file, const char *group, const char *key)
 | 
			
		||||
	return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char **ril_config_get_strings(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key, char delimiter)
 | 
			
		||||
{
 | 
			
		||||
	char *str = ril_config_get_string(file, group, key);
 | 
			
		||||
 | 
			
		||||
	if (str) {
 | 
			
		||||
		char **strv, **p;
 | 
			
		||||
		char delimiter_str[2];
 | 
			
		||||
 | 
			
		||||
		delimiter_str[0] = delimiter;
 | 
			
		||||
		delimiter_str[1] = 0;
 | 
			
		||||
		strv = g_strsplit(str, delimiter_str, -1);
 | 
			
		||||
 | 
			
		||||
		/* Strip whitespaces */
 | 
			
		||||
		for (p = strv; *p; p++) {
 | 
			
		||||
			*p = g_strstrip(*p);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		g_free(str);
 | 
			
		||||
		return strv;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean ril_config_get_integer(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key, int *out_value)
 | 
			
		||||
{
 | 
			
		||||
@@ -106,6 +142,168 @@ gboolean ril_config_get_flag(GKeyFile *file, const char *group,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean ril_config_get_enum(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key, int *result,
 | 
			
		||||
					const char *name, int value, ...)
 | 
			
		||||
{
 | 
			
		||||
	char *str = ril_config_get_string(file, group, key);
 | 
			
		||||
 | 
			
		||||
	if (str) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Some people are thinking that # is a comment
 | 
			
		||||
		 * anywhere on the line, not just at the beginning
 | 
			
		||||
		 */
 | 
			
		||||
		char *comment = strchr(str, '#');
 | 
			
		||||
 | 
			
		||||
		if (comment) *comment = 0;
 | 
			
		||||
		g_strstrip(str);
 | 
			
		||||
		if (strcasecmp(str, name)) {
 | 
			
		||||
			va_list args;
 | 
			
		||||
			va_start(args, value);
 | 
			
		||||
			while ((name = va_arg(args, char*)) != NULL) {
 | 
			
		||||
				value = va_arg(args, int);
 | 
			
		||||
				if (!strcasecmp(str, name)) {
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			va_end(args);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!name) {
 | 
			
		||||
			ofono_error("Invalid %s config value (%s)", key, str);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		g_free(str);
 | 
			
		||||
 | 
			
		||||
		if (name) {
 | 
			
		||||
			if (result) {
 | 
			
		||||
				*result = value;
 | 
			
		||||
			}
 | 
			
		||||
			return TRUE;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gboolean ril_config_get_mask(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key, int *result,
 | 
			
		||||
					const char *name, int value, ...)
 | 
			
		||||
{
 | 
			
		||||
	char *str = ril_config_get_string(file, group, key);
 | 
			
		||||
	gboolean ok = FALSE;
 | 
			
		||||
	
 | 
			
		||||
	if (result) {
 | 
			
		||||
		*result = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (str) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Some people are thinking that # is a comment
 | 
			
		||||
		 * anywhere on the line, not just at the beginning
 | 
			
		||||
		 */
 | 
			
		||||
		char *comment = strchr(str, '#');
 | 
			
		||||
		char **values, **ptr;
 | 
			
		||||
 | 
			
		||||
		if (comment) *comment = 0;
 | 
			
		||||
		values = g_strsplit(str, "+", -1);
 | 
			
		||||
 | 
			
		||||
		for (ok = TRUE, ptr = values; *ptr && ok; ptr++) {
 | 
			
		||||
			const char* found_str = NULL;
 | 
			
		||||
			const char* s = g_strstrip(*ptr);
 | 
			
		||||
 | 
			
		||||
			if (!strcasecmp(s, name)) {
 | 
			
		||||
				found_str = name;
 | 
			
		||||
				if (result) {
 | 
			
		||||
					*result |= value;
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				va_list args;
 | 
			
		||||
				const char* known;
 | 
			
		||||
 | 
			
		||||
				va_start(args, value);
 | 
			
		||||
				while ((known = va_arg(args, char*)) != NULL) {
 | 
			
		||||
					const int bit = va_arg(args, int);
 | 
			
		||||
 | 
			
		||||
					if (!strcasecmp(s, known)) {
 | 
			
		||||
						found_str = known;
 | 
			
		||||
						if (result) {
 | 
			
		||||
							*result |= bit;
 | 
			
		||||
						}
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				va_end(args);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!found_str) {
 | 
			
		||||
				ofono_error("Unknown bit '%s' in %s", s, key);
 | 
			
		||||
				ok = FALSE;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		g_strfreev(values);
 | 
			
		||||
		g_free(str);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!ok && result) {
 | 
			
		||||
		*result = 0;
 | 
			
		||||
	}
 | 
			
		||||
	return ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GUtilInts *ril_config_get_ints(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key)
 | 
			
		||||
{
 | 
			
		||||
	char *value = ril_config_get_string(file, group, key);
 | 
			
		||||
 | 
			
		||||
	if (value) {
 | 
			
		||||
		GUtilIntArray *array = gutil_int_array_new();
 | 
			
		||||
		char **values, **ptr;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Some people are thinking that # is a comment
 | 
			
		||||
		 * anywhere on the line, not just at the beginning
 | 
			
		||||
		 */
 | 
			
		||||
		char *comment = strchr(value, '#');
 | 
			
		||||
 | 
			
		||||
		if (comment) *comment = 0;
 | 
			
		||||
		values = g_strsplit(value, ",", -1);
 | 
			
		||||
		ptr = values;
 | 
			
		||||
 | 
			
		||||
		while (*ptr) {
 | 
			
		||||
			int val;
 | 
			
		||||
 | 
			
		||||
			if (gutil_parse_int(*ptr++, 0, &val)) {
 | 
			
		||||
				gutil_int_array_append(array, val);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		g_free(value);
 | 
			
		||||
		g_strfreev(values);
 | 
			
		||||
		return gutil_int_array_free_to_ints(array);
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char *ril_config_ints_to_string(GUtilInts *ints, char separator)
 | 
			
		||||
{
 | 
			
		||||
	if (ints) {
 | 
			
		||||
		guint i, n;
 | 
			
		||||
		const int *data = gutil_ints_get_data(ints, &n);
 | 
			
		||||
		GString *buf = g_string_new(NULL);
 | 
			
		||||
 | 
			
		||||
		for (i=0; i<n; i++) {
 | 
			
		||||
			if (buf->len > 0) {
 | 
			
		||||
				g_string_append_c(buf, separator);
 | 
			
		||||
			}
 | 
			
		||||
			g_string_append_printf(buf, "%d", data[i]);
 | 
			
		||||
		}
 | 
			
		||||
		return g_string_free(buf, FALSE);
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2015-2016 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2015-2020 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2019-2020 Open Mobile Platform LLC.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -22,13 +23,27 @@
 | 
			
		||||
 | 
			
		||||
#define RILCONF_SETTINGS_GROUP      "Settings"
 | 
			
		||||
 | 
			
		||||
char* ril_config_get_string(GKeyFile *file, const char *group, const char *key);
 | 
			
		||||
char *ril_config_get_string(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key);
 | 
			
		||||
char **ril_config_get_strings(GKeyFile *file, const char *group,
 | 
			
		||||
			      const char *key, char delimiter);
 | 
			
		||||
gboolean ril_config_get_integer(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key, int *value);
 | 
			
		||||
gboolean ril_config_get_boolean(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key, gboolean *value);
 | 
			
		||||
gboolean ril_config_get_flag(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key, int flag, int *flags);
 | 
			
		||||
gboolean ril_config_get_enum(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key, int *result,
 | 
			
		||||
					const char *name, int value, ...)
 | 
			
		||||
					G_GNUC_NULL_TERMINATED;
 | 
			
		||||
gboolean ril_config_get_mask(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key, int *result,
 | 
			
		||||
					const char *name, int value, ...)
 | 
			
		||||
					G_GNUC_NULL_TERMINATED;
 | 
			
		||||
GUtilInts *ril_config_get_ints(GKeyFile *file, const char *group,
 | 
			
		||||
					const char *key);
 | 
			
		||||
char *ril_config_ints_to_string(GUtilInts *ints, char separator);
 | 
			
		||||
 | 
			
		||||
#endif /* RIL_CONFIG_H */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										622
									
								
								ofono/drivers/ril/ril_connman.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										622
									
								
								ofono/drivers/ril/ril_connman.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,622 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2019 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "ril_connman.h"
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
 | 
			
		||||
#include <gdbus.h>
 | 
			
		||||
 | 
			
		||||
#include <gutil_macros.h>
 | 
			
		||||
#include <gutil_misc.h>
 | 
			
		||||
 | 
			
		||||
#include <glib-object.h>
 | 
			
		||||
 | 
			
		||||
#define CONNMAN_BUS DBUS_BUS_SYSTEM
 | 
			
		||||
#define CONNMAN_SERVICE "net.connman"
 | 
			
		||||
#define CONNMAN_PATH "/"
 | 
			
		||||
 | 
			
		||||
#define CONNMAN_GET_PROPERTIES "GetProperties"
 | 
			
		||||
#define CONNMAN_GET_TECHNOLOGIES "GetTechnologies"
 | 
			
		||||
#define CONNMAN_PROPERTY_CHANGED "PropertyChanged"
 | 
			
		||||
#define CONNMAN_TECH_CONNECTED "Connected"
 | 
			
		||||
#define CONNMAN_TECH_TETHERING "Tethering"
 | 
			
		||||
 | 
			
		||||
#define CONNMAN_INTERFACE_(name) "net.connman." name
 | 
			
		||||
#define CONNMAN_MANAGER_INTERFACE CONNMAN_INTERFACE_("Manager")
 | 
			
		||||
#define CONNMAN_TECH_INTERFACE CONNMAN_INTERFACE_("Technology")
 | 
			
		||||
 | 
			
		||||
#define CONNMAN_TECH_PATH_(name) "/net/connman/technology/" name
 | 
			
		||||
#define CONNMAN_TECH_PATH_WIFI CONNMAN_TECH_PATH_("wifi")
 | 
			
		||||
 | 
			
		||||
#define CONNMAN_TECH_CONNECTED_BIT (0x01)
 | 
			
		||||
#define CONNMAN_TECH_TETHERING_BIT (0x02)
 | 
			
		||||
#define CONNMAN_TECH_ALL_PROPERTY_BITS (\
 | 
			
		||||
	CONNMAN_TECH_CONNECTED_BIT | \
 | 
			
		||||
	CONNMAN_TECH_TETHERING_BIT)
 | 
			
		||||
 | 
			
		||||
typedef GObjectClass ConnManObjectClass;
 | 
			
		||||
 | 
			
		||||
typedef struct connman_tech ConnManTech;
 | 
			
		||||
 | 
			
		||||
typedef struct connman_object {
 | 
			
		||||
	GObject object;
 | 
			
		||||
	struct ril_connman pub;
 | 
			
		||||
	guint32 pending_signals;
 | 
			
		||||
	DBusConnection *connection;
 | 
			
		||||
	DBusPendingCall *call;
 | 
			
		||||
	guint service_watch;
 | 
			
		||||
	guint signal_watch;
 | 
			
		||||
	GHashTable *techs;
 | 
			
		||||
	ConnManTech *wifi;
 | 
			
		||||
} ConnManObject;
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(ConnManObject, connman_object, G_TYPE_OBJECT)
 | 
			
		||||
#define CONNMAN_OBJECT_TYPE (connman_object_get_type())
 | 
			
		||||
#define CONNMAN_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
 | 
			
		||||
	CONNMAN_OBJECT_TYPE, ConnManObject))
 | 
			
		||||
 | 
			
		||||
struct connman_tech {
 | 
			
		||||
	ConnManObject *obj;
 | 
			
		||||
	const char *path;
 | 
			
		||||
	gboolean connected;
 | 
			
		||||
	gboolean tethering;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct connman_closure {
 | 
			
		||||
	GCClosure cclosure;
 | 
			
		||||
	ril_connman_property_cb_t callback;
 | 
			
		||||
	gpointer user_data;
 | 
			
		||||
} ConnManClosure;
 | 
			
		||||
 | 
			
		||||
#define connman_closure_new() ((ConnManClosure *) \
 | 
			
		||||
    g_closure_new_simple(sizeof(ConnManClosure), NULL))
 | 
			
		||||
 | 
			
		||||
#define SIGNAL_PROPERTY_CHANGED_NAME    "ril-connman-property-changed"
 | 
			
		||||
#define SIGNAL_PROPERTY_DETAIL          "%x"
 | 
			
		||||
#define SIGNAL_PROPERTY_DETAIL_MAX_LEN  (8)
 | 
			
		||||
 | 
			
		||||
#define SIGNAL_BIT(property) (1 << (property - 1))
 | 
			
		||||
#define SIGNAL_BIT_(name) SIGNAL_BIT(RIL_CONNMAN_PROPERTY_##name)
 | 
			
		||||
 | 
			
		||||
enum connman_object_signal {
 | 
			
		||||
	SIGNAL_PROPERTY_CHANGED,
 | 
			
		||||
	SIGNAL_COUNT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static guint connman_object_signals[SIGNAL_COUNT];
 | 
			
		||||
static GQuark connman_object_property_quarks[RIL_CONNMAN_PROPERTY_COUNT - 1];
 | 
			
		||||
 | 
			
		||||
static inline ConnManObject *connman_object_cast(struct ril_connman *connman)
 | 
			
		||||
{
 | 
			
		||||
	return G_LIKELY(connman) ?
 | 
			
		||||
		CONNMAN_OBJECT(G_CAST(connman, ConnManObject, pub)) :
 | 
			
		||||
		NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline const char *connman_iter_get_string(DBusMessageIter *it)
 | 
			
		||||
{
 | 
			
		||||
	const char *str = NULL;
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_get_basic(it, &str);
 | 
			
		||||
	return str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GQuark connman_object_property_quark(enum ril_connman_property p)
 | 
			
		||||
{
 | 
			
		||||
	/* For ANY property this function is expected to return zero */
 | 
			
		||||
	if (p > RIL_CONNMAN_PROPERTY_ANY && p < RIL_CONNMAN_PROPERTY_COUNT) {
 | 
			
		||||
		const int i = p - 1;
 | 
			
		||||
 | 
			
		||||
		if (G_UNLIKELY(!connman_object_property_quarks[i])) {
 | 
			
		||||
			char buf[SIGNAL_PROPERTY_DETAIL_MAX_LEN + 1];
 | 
			
		||||
 | 
			
		||||
			snprintf(buf, sizeof(buf), SIGNAL_PROPERTY_DETAIL, p);
 | 
			
		||||
			buf[sizeof(buf) - 1] = 0;
 | 
			
		||||
			connman_object_property_quarks[i] =
 | 
			
		||||
				g_quark_from_string(buf);
 | 
			
		||||
		}
 | 
			
		||||
		return connman_object_property_quarks[i];
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_object_property_changed(ConnManObject *self,
 | 
			
		||||
		enum ril_connman_property property, ConnManClosure *closure)
 | 
			
		||||
{
 | 
			
		||||
	closure->callback(&self->pub, property, closure->user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_object_emit_property_change(ConnManObject *self,
 | 
			
		||||
						enum ril_connman_property p)
 | 
			
		||||
{
 | 
			
		||||
	self->pending_signals &= ~SIGNAL_BIT(p);
 | 
			
		||||
	g_signal_emit(self, connman_object_signals[SIGNAL_PROPERTY_CHANGED],
 | 
			
		||||
		connman_object_property_quark(p), p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_object_emit_pending_signals(ConnManObject *self)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_connman *connman = &self->pub;
 | 
			
		||||
	gboolean valid_changed, present_changed;
 | 
			
		||||
	enum ril_connman_property p;
 | 
			
		||||
 | 
			
		||||
	/* Handlers could drop their references to us */
 | 
			
		||||
	g_object_ref(self);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * PRESENT and VALID are the last signals to be emitted if the object
 | 
			
		||||
	 * BECOMES present and/or valid.
 | 
			
		||||
	 */
 | 
			
		||||
	if ((self->pending_signals & SIGNAL_BIT_(VALID)) &&
 | 
			
		||||
						connman->valid) {
 | 
			
		||||
		self->pending_signals &= ~SIGNAL_BIT_(VALID);
 | 
			
		||||
		valid_changed = TRUE;
 | 
			
		||||
	} else {
 | 
			
		||||
		valid_changed = FALSE;
 | 
			
		||||
	}
 | 
			
		||||
	if ((self->pending_signals & SIGNAL_BIT_(PRESENT)) &&
 | 
			
		||||
						connman->present) {
 | 
			
		||||
		self->pending_signals &= ~SIGNAL_BIT_(PRESENT);
 | 
			
		||||
		present_changed = TRUE;
 | 
			
		||||
	} else {
 | 
			
		||||
		present_changed = FALSE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Emit the signals. Not that in case if valid has become FALSE,
 | 
			
		||||
	 * then VALID is emitted first, otherwise it's emitted last.
 | 
			
		||||
	 * Same thing with PRESENT.
 | 
			
		||||
	 */
 | 
			
		||||
	for (p = RIL_CONNMAN_PROPERTY_ANY + 1;
 | 
			
		||||
		p < RIL_CONNMAN_PROPERTY_COUNT && self->pending_signals;
 | 
			
		||||
		p++) {
 | 
			
		||||
		if (self->pending_signals & SIGNAL_BIT(p)) {
 | 
			
		||||
			connman_object_emit_property_change(self, p);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Then emit PRESENT and VALID if necessary */
 | 
			
		||||
	if (present_changed) {
 | 
			
		||||
		connman_object_emit_property_change(self,
 | 
			
		||||
			RIL_CONNMAN_PROPERTY_PRESENT);
 | 
			
		||||
	}
 | 
			
		||||
	if (valid_changed) {
 | 
			
		||||
		connman_object_emit_property_change(self,
 | 
			
		||||
			RIL_CONNMAN_PROPERTY_VALID);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* And release the temporary reference */
 | 
			
		||||
	g_object_unref(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_cancel_call(ConnManObject *self)
 | 
			
		||||
{
 | 
			
		||||
	if (self->call) {
 | 
			
		||||
		dbus_pending_call_cancel(self->call);
 | 
			
		||||
		dbus_pending_call_unref(self->call);
 | 
			
		||||
		self->call = NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ConnManTech *connman_tech_new(ConnManObject *self, const char *path)
 | 
			
		||||
{
 | 
			
		||||
	ConnManTech *tech = g_new0(ConnManTech, 1);
 | 
			
		||||
	char *key = g_strdup(path);
 | 
			
		||||
 | 
			
		||||
	tech->obj = self;
 | 
			
		||||
	tech->path = key;
 | 
			
		||||
	g_hash_table_replace(self->techs, key, tech);
 | 
			
		||||
	return tech;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_invalidate(ConnManObject *self)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_connman *connman = &self->pub;
 | 
			
		||||
 | 
			
		||||
	if (connman->valid) {
 | 
			
		||||
		connman->valid = FALSE;
 | 
			
		||||
		self->pending_signals |= SIGNAL_BIT_(VALID);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_update_valid(ConnManObject *self)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_connman *connman = &self->pub;
 | 
			
		||||
	const gboolean valid = (connman->present && !self->call);
 | 
			
		||||
 | 
			
		||||
	if (connman->valid != valid) {
 | 
			
		||||
		connman->valid = valid;
 | 
			
		||||
		self->pending_signals |= SIGNAL_BIT_(VALID);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean connman_update_tethering(ConnManObject *self)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_connman *connman = &self->pub;
 | 
			
		||||
	gboolean tethering = FALSE;
 | 
			
		||||
	GHashTableIter it;
 | 
			
		||||
	gpointer value;
 | 
			
		||||
 | 
			
		||||
	g_hash_table_iter_init(&it, self->techs);
 | 
			
		||||
	while (g_hash_table_iter_next(&it, NULL, &value)) {
 | 
			
		||||
		const ConnManTech *tech = value;
 | 
			
		||||
 | 
			
		||||
		if (tech->tethering) {
 | 
			
		||||
			tethering = TRUE;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (connman->tethering != tethering) {
 | 
			
		||||
		connman->tethering = tethering;
 | 
			
		||||
		self->pending_signals |= SIGNAL_BIT_(TETHERING);
 | 
			
		||||
		return TRUE;
 | 
			
		||||
	} else {
 | 
			
		||||
		return FALSE;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_set_tech_tethering(ConnManTech *tech, gboolean tethering)
 | 
			
		||||
{
 | 
			
		||||
	if (tech->tethering != tethering) {
 | 
			
		||||
		ConnManObject *self = tech->obj;
 | 
			
		||||
 | 
			
		||||
		tech->tethering = tethering;
 | 
			
		||||
		DBG(CONNMAN_TECH_TETHERING " %s for %s",
 | 
			
		||||
				tethering ? "on" : "off", tech->path);
 | 
			
		||||
		if (tethering) {
 | 
			
		||||
			struct ril_connman *connman = &self->pub;
 | 
			
		||||
 | 
			
		||||
			if (G_LIKELY(!connman->tethering)) {
 | 
			
		||||
				/* Definitely tethering now */
 | 
			
		||||
				connman->tethering = TRUE;
 | 
			
		||||
				self->pending_signals |= SIGNAL_BIT_(TETHERING);
 | 
			
		||||
				DBG("Tethering on");
 | 
			
		||||
			}
 | 
			
		||||
		} else if (connman_update_tethering(self)) {
 | 
			
		||||
			/* Not tethering anymore */
 | 
			
		||||
			DBG("Tethering off");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_set_tech_connected(ConnManTech *tech, gboolean connected)
 | 
			
		||||
{
 | 
			
		||||
	if (tech->connected != connected) {
 | 
			
		||||
		ConnManObject *self = tech->obj;
 | 
			
		||||
 | 
			
		||||
		tech->connected = connected;
 | 
			
		||||
		DBG(CONNMAN_TECH_CONNECTED " %s for %s",
 | 
			
		||||
				connected ? "on" : "off", tech->path);
 | 
			
		||||
		if (tech == self->wifi) {
 | 
			
		||||
			struct ril_connman *connman = &self->pub;
 | 
			
		||||
 | 
			
		||||
			connman->wifi_connected = connected;
 | 
			
		||||
			self->pending_signals |= SIGNAL_BIT_(WIFI_CONNECTED);
 | 
			
		||||
			DBG("WiFi %sconnected", connected ? "" : "dis");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int connman_tech_set_property(ConnManTech *tech, DBusMessageIter *it)
 | 
			
		||||
{
 | 
			
		||||
	DBusMessageIter var;
 | 
			
		||||
	DBusBasicValue value;
 | 
			
		||||
	const char *key = connman_iter_get_string(it);
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_next(it);
 | 
			
		||||
	dbus_message_iter_recurse(it, &var);
 | 
			
		||||
	dbus_message_iter_get_basic(&var, &value);
 | 
			
		||||
	if (!g_ascii_strcasecmp(key, CONNMAN_TECH_CONNECTED)) {
 | 
			
		||||
		if (dbus_message_iter_get_arg_type(&var) == DBUS_TYPE_BOOLEAN) {
 | 
			
		||||
			connman_set_tech_connected(tech, value.bool_val);
 | 
			
		||||
			return CONNMAN_TECH_CONNECTED_BIT;
 | 
			
		||||
		}
 | 
			
		||||
	} else if (!g_ascii_strcasecmp(key, CONNMAN_TECH_TETHERING)) {
 | 
			
		||||
		if (dbus_message_iter_get_arg_type(&var) == DBUS_TYPE_BOOLEAN) {
 | 
			
		||||
			connman_set_tech_tethering(tech, value.bool_val);
 | 
			
		||||
			return CONNMAN_TECH_TETHERING_BIT;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_tech_set_properties(ConnManTech *tech, DBusMessageIter *it)
 | 
			
		||||
{
 | 
			
		||||
	DBusMessageIter dict;
 | 
			
		||||
	int handled = 0;
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_recurse(it, &dict);
 | 
			
		||||
	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
 | 
			
		||||
		DBusMessageIter entry;
 | 
			
		||||
 | 
			
		||||
		dbus_message_iter_recurse(&dict, &entry);
 | 
			
		||||
		handled |= connman_tech_set_property(tech, &entry);
 | 
			
		||||
		if (handled == CONNMAN_TECH_ALL_PROPERTY_BITS) {
 | 
			
		||||
			/* Ignore the rest */
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		dbus_message_iter_next(&dict);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean connman_tech_property_changed(DBusConnection *conn,
 | 
			
		||||
					DBusMessage *msg, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	const char *path = dbus_message_get_path(msg);
 | 
			
		||||
	ConnManObject *self = CONNMAN_OBJECT(user_data);
 | 
			
		||||
	ConnManTech *tech = g_hash_table_lookup(self->techs, path);
 | 
			
		||||
	DBusMessageIter it;
 | 
			
		||||
 | 
			
		||||
	if (tech && dbus_message_has_signature(msg, "sv") &&
 | 
			
		||||
					dbus_message_iter_init(msg, &it)) {
 | 
			
		||||
		const char* name = connman_iter_get_string(&it);
 | 
			
		||||
 | 
			
		||||
		if (!connman_tech_set_property(tech, &it)) {
 | 
			
		||||
			DBG("%s changed for %s", name, path);
 | 
			
		||||
		}
 | 
			
		||||
		connman_object_emit_pending_signals(self);
 | 
			
		||||
	}
 | 
			
		||||
	return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_set_techs(ConnManObject *self, DBusMessageIter *it)
 | 
			
		||||
{
 | 
			
		||||
	DBusMessageIter list;
 | 
			
		||||
 | 
			
		||||
	dbus_message_iter_recurse(it, &list);
 | 
			
		||||
	while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) {
 | 
			
		||||
		DBusMessageIter entry;
 | 
			
		||||
		const char *path;
 | 
			
		||||
		ConnManTech *tech;
 | 
			
		||||
 | 
			
		||||
		dbus_message_iter_recurse(&list, &entry);
 | 
			
		||||
		path = connman_iter_get_string(&entry);
 | 
			
		||||
		tech = connman_tech_new(self, path);
 | 
			
		||||
 | 
			
		||||
		DBG("%s", path);
 | 
			
		||||
		if (!g_strcmp0(path, CONNMAN_TECH_PATH_WIFI)) {
 | 
			
		||||
			/* WiFi is a special case */
 | 
			
		||||
			self->wifi = tech;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dbus_message_iter_next(&entry);
 | 
			
		||||
		connman_tech_set_properties(tech, &entry);
 | 
			
		||||
		dbus_message_iter_next(&list);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_techs_reply(DBusPendingCall *call, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	ConnManObject *self = CONNMAN_OBJECT(user_data);
 | 
			
		||||
	DBusMessage *reply = dbus_pending_call_steal_reply(call);
 | 
			
		||||
	DBusError error;
 | 
			
		||||
	DBusMessageIter array;
 | 
			
		||||
 | 
			
		||||
	dbus_error_init(&error);
 | 
			
		||||
	if (dbus_set_error_from_message(&error, reply)) {
 | 
			
		||||
		DBG("Failed to get technologies: %s", error.message);
 | 
			
		||||
		dbus_error_free(&error);
 | 
			
		||||
	} else if (dbus_message_has_signature(reply, "a(oa{sv})") &&
 | 
			
		||||
				dbus_message_iter_init(reply, &array)) {
 | 
			
		||||
		connman_set_techs(self, &array);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbus_message_unref(reply);
 | 
			
		||||
	dbus_pending_call_unref(self->call);
 | 
			
		||||
	self->call = NULL;
 | 
			
		||||
	connman_update_valid(self);
 | 
			
		||||
	connman_object_emit_pending_signals(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_get_techs(ConnManObject *self)
 | 
			
		||||
{
 | 
			
		||||
	DBusMessage *msg = dbus_message_new_method_call(CONNMAN_SERVICE,
 | 
			
		||||
				CONNMAN_PATH, CONNMAN_MANAGER_INTERFACE,
 | 
			
		||||
				CONNMAN_GET_TECHNOLOGIES);
 | 
			
		||||
 | 
			
		||||
	connman_cancel_call(self);
 | 
			
		||||
	if (g_dbus_send_message_with_reply(self->connection, msg,
 | 
			
		||||
					&self->call, DBUS_TIMEOUT_INFINITE)) {
 | 
			
		||||
		/* Not valid while any request is pending */
 | 
			
		||||
		connman_invalidate(self);
 | 
			
		||||
		dbus_pending_call_set_notify(self->call, connman_techs_reply,
 | 
			
		||||
								self, NULL);
 | 
			
		||||
	}
 | 
			
		||||
	dbus_message_unref(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_appeared(DBusConnection *conn, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	ConnManObject *self = CONNMAN_OBJECT(user_data);
 | 
			
		||||
	struct ril_connman *connman = &self->pub;
 | 
			
		||||
 | 
			
		||||
	if (!connman->present) {
 | 
			
		||||
		DBG("connman is there");
 | 
			
		||||
		connman->present = TRUE;
 | 
			
		||||
		self->pending_signals |= SIGNAL_BIT_(PRESENT);
 | 
			
		||||
		connman_get_techs(self);
 | 
			
		||||
		connman_object_emit_pending_signals(self);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_vanished(DBusConnection *conn, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	ConnManObject *self = CONNMAN_OBJECT(user_data);
 | 
			
		||||
	struct ril_connman *connman = &self->pub;
 | 
			
		||||
 | 
			
		||||
	if (connman->present) {
 | 
			
		||||
 | 
			
		||||
		DBG("connman has disappeared");
 | 
			
		||||
		g_hash_table_remove_all(self->techs);
 | 
			
		||||
		self->wifi = NULL;
 | 
			
		||||
		connman->present = FALSE;
 | 
			
		||||
		self->pending_signals |= SIGNAL_BIT_(PRESENT);
 | 
			
		||||
		if (connman->wifi_connected) {
 | 
			
		||||
			connman->wifi_connected = FALSE;
 | 
			
		||||
			self->pending_signals |= SIGNAL_BIT_(WIFI_CONNECTED);
 | 
			
		||||
		}
 | 
			
		||||
		if (connman->tethering) {
 | 
			
		||||
			connman->tethering = FALSE;
 | 
			
		||||
			self->pending_signals |= SIGNAL_BIT_(TETHERING);
 | 
			
		||||
		}
 | 
			
		||||
		connman_object_emit_pending_signals(self);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_init(ConnManObject *self, DBusConnection *connection)
 | 
			
		||||
{
 | 
			
		||||
	self->connection = dbus_connection_ref(connection);
 | 
			
		||||
	self->service_watch = g_dbus_add_service_watch(self->connection,
 | 
			
		||||
		CONNMAN_SERVICE, connman_appeared, connman_vanished,
 | 
			
		||||
		self, NULL);
 | 
			
		||||
	self->signal_watch = g_dbus_add_signal_watch(self->connection,
 | 
			
		||||
		CONNMAN_SERVICE, NULL, CONNMAN_TECH_INTERFACE,
 | 
			
		||||
		CONNMAN_PROPERTY_CHANGED, connman_tech_property_changed,
 | 
			
		||||
		self, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ril_connman *ril_connman_new()
 | 
			
		||||
{
 | 
			
		||||
	static ConnManObject *instance = NULL;
 | 
			
		||||
 | 
			
		||||
	if (instance) {
 | 
			
		||||
		g_object_ref(instance);
 | 
			
		||||
		return &instance->pub;
 | 
			
		||||
	} else {
 | 
			
		||||
		DBusError error;
 | 
			
		||||
		DBusConnection *connection;
 | 
			
		||||
 | 
			
		||||
		dbus_error_init(&error);
 | 
			
		||||
		connection = dbus_bus_get(CONNMAN_BUS, NULL);
 | 
			
		||||
 | 
			
		||||
		if (connection) {
 | 
			
		||||
			instance = g_object_new(CONNMAN_OBJECT_TYPE, NULL);
 | 
			
		||||
			connman_init(instance, connection);
 | 
			
		||||
			dbus_connection_unref(connection);
 | 
			
		||||
			g_object_add_weak_pointer(G_OBJECT(instance),
 | 
			
		||||
				(gpointer*)(&instance));
 | 
			
		||||
			return &instance->pub;
 | 
			
		||||
		} else {
 | 
			
		||||
			ofono_error("Unable to attach to connman bus: %s",
 | 
			
		||||
				error.message);
 | 
			
		||||
			dbus_error_free(&error);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ril_connman *ril_connman_ref(struct ril_connman *connman)
 | 
			
		||||
{
 | 
			
		||||
	ConnManObject *self = connman_object_cast(connman);
 | 
			
		||||
 | 
			
		||||
	if (G_LIKELY(self)) {
 | 
			
		||||
		g_object_ref(self);
 | 
			
		||||
		return connman;
 | 
			
		||||
	} else {
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ril_connman_unref(struct ril_connman *connman)
 | 
			
		||||
{
 | 
			
		||||
	ConnManObject *self = connman_object_cast(connman);
 | 
			
		||||
 | 
			
		||||
	if (G_LIKELY(self)) {
 | 
			
		||||
		g_object_unref(self);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gulong ril_connman_add_property_changed_handler(struct ril_connman *connman,
 | 
			
		||||
	enum ril_connman_property p, ril_connman_property_cb_t cb, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	ConnManObject *self = connman_object_cast(connman);
 | 
			
		||||
 | 
			
		||||
	if (G_LIKELY(self) && G_LIKELY(cb)) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * We can't directly connect the provided callback because
 | 
			
		||||
		 * it expects the first parameter to point to public part
 | 
			
		||||
		 * of the object but glib will call it with ConnManObject
 | 
			
		||||
		 * as the first parameter. connman_object_property_changed()
 | 
			
		||||
		 * will do the conversion.
 | 
			
		||||
		 */
 | 
			
		||||
		ConnManClosure *closure = connman_closure_new();
 | 
			
		||||
		GCClosure *cc = &closure->cclosure;
 | 
			
		||||
 | 
			
		||||
		cc->closure.data = closure;
 | 
			
		||||
		cc->callback = G_CALLBACK(connman_object_property_changed);
 | 
			
		||||
		closure->callback = cb;
 | 
			
		||||
		closure->user_data = arg;
 | 
			
		||||
 | 
			
		||||
		return g_signal_connect_closure_by_id(self,
 | 
			
		||||
			connman_object_signals[SIGNAL_PROPERTY_CHANGED],
 | 
			
		||||
			connman_object_property_quark(p), &cc->closure, FALSE);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ril_connman_remove_handler(struct ril_connman *connman, gulong id)
 | 
			
		||||
{
 | 
			
		||||
	if (G_LIKELY(id)) {
 | 
			
		||||
		ConnManObject *self = connman_object_cast(connman);
 | 
			
		||||
 | 
			
		||||
		if (G_LIKELY(self)) {
 | 
			
		||||
			g_signal_handler_disconnect(self, id);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ril_connman_remove_handlers(struct ril_connman *connman, gulong *ids,
 | 
			
		||||
								int n)
 | 
			
		||||
{
 | 
			
		||||
	gutil_disconnect_handlers(connman_object_cast(connman), ids, n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_object_init(ConnManObject *self)
 | 
			
		||||
{
 | 
			
		||||
	self->techs = g_hash_table_new_full(g_str_hash, g_str_equal,
 | 
			
		||||
							g_free, g_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_object_finalize(GObject *object)
 | 
			
		||||
{
 | 
			
		||||
	ConnManObject *self = CONNMAN_OBJECT(object);
 | 
			
		||||
 | 
			
		||||
	connman_cancel_call(self);
 | 
			
		||||
	g_hash_table_destroy(self->techs);
 | 
			
		||||
	g_dbus_remove_watch(self->connection, self->service_watch);
 | 
			
		||||
	g_dbus_remove_watch(self->connection, self->signal_watch);
 | 
			
		||||
	dbus_connection_unref(self->connection);
 | 
			
		||||
	G_OBJECT_CLASS(connman_object_parent_class)->finalize(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void connman_object_class_init(ConnManObjectClass *klass)
 | 
			
		||||
{
 | 
			
		||||
	G_OBJECT_CLASS(klass)->finalize = connman_object_finalize;
 | 
			
		||||
	connman_object_signals[SIGNAL_PROPERTY_CHANGED] =
 | 
			
		||||
		g_signal_new(SIGNAL_PROPERTY_CHANGED_NAME,
 | 
			
		||||
			G_OBJECT_CLASS_TYPE(klass),
 | 
			
		||||
			G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
 | 
			
		||||
			0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 8
 | 
			
		||||
 * indent-tabs-mode: t
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										61
									
								
								ofono/drivers/ril/ril_connman.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								ofono/drivers/ril/ril_connman.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2019 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef RIL_CONNMAN_H
 | 
			
		||||
#define RIL_CONNMAN_H
 | 
			
		||||
 | 
			
		||||
#include <gutil_misc.h>
 | 
			
		||||
 | 
			
		||||
struct ril_connman {
 | 
			
		||||
	gboolean valid;          /* TRUE if other fields are valid */
 | 
			
		||||
	gboolean present;        /* ConnMan is present on D-Bus */
 | 
			
		||||
	gboolean tethering;      /* At least one technology is tethering */
 | 
			
		||||
	gboolean wifi_connected; /* WiFi network is connected */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_connman_property {
 | 
			
		||||
	RIL_CONNMAN_PROPERTY_ANY,
 | 
			
		||||
	RIL_CONNMAN_PROPERTY_VALID,
 | 
			
		||||
	RIL_CONNMAN_PROPERTY_PRESENT,
 | 
			
		||||
	RIL_CONNMAN_PROPERTY_TETHERING,
 | 
			
		||||
	RIL_CONNMAN_PROPERTY_WIFI_CONNECTED,
 | 
			
		||||
	RIL_CONNMAN_PROPERTY_COUNT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef void (*ril_connman_property_cb_t)(struct ril_connman *connman,
 | 
			
		||||
			enum ril_connman_property property, void *arg);
 | 
			
		||||
 | 
			
		||||
struct ril_connman *ril_connman_new(void);
 | 
			
		||||
struct ril_connman *ril_connman_ref(struct ril_connman *connman);
 | 
			
		||||
void ril_connman_unref(struct ril_connman *connman);
 | 
			
		||||
 | 
			
		||||
gulong ril_connman_add_property_changed_handler(struct ril_connman *connman,
 | 
			
		||||
	enum ril_connman_property p, ril_connman_property_cb_t cb, void *arg);
 | 
			
		||||
void ril_connman_remove_handler(struct ril_connman *connman, gulong id);
 | 
			
		||||
void ril_connman_remove_handlers(struct ril_connman *connman, gulong *ids,
 | 
			
		||||
								int n);
 | 
			
		||||
 | 
			
		||||
#define ril_connman_remove_all_handlers(connman, ids) \
 | 
			
		||||
	ril_connman_remove_handlers(connman, ids, G_N_ELEMENTS(ids))
 | 
			
		||||
 | 
			
		||||
#endif /* RIL_CONNMAN_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 8
 | 
			
		||||
 * indent-tabs-mode: t
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
@@ -1,11 +1,6 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  RIL constants adopted from AOSP's header:
 | 
			
		||||
 *
 | 
			
		||||
 *  /hardware/ril/reference_ril/ril.h
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2013 Canonical Ltd.
 | 
			
		||||
 *  Copyright (C) 2013-2016 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2013-2020 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -20,47 +15,9 @@
 | 
			
		||||
#ifndef __RIL_CONSTANTS_H
 | 
			
		||||
#define __RIL_CONSTANTS_H 1
 | 
			
		||||
 | 
			
		||||
/* Error Codes */
 | 
			
		||||
#define RIL_E_SUCCESS 0
 | 
			
		||||
#define RIL_E_RADIO_NOT_AVAILABLE 1
 | 
			
		||||
#define RIL_E_GENERIC_FAILURE 2
 | 
			
		||||
#define RIL_E_PASSWORD_INCORRECT 3
 | 
			
		||||
#define RIL_E_SIM_PIN2 4
 | 
			
		||||
#define RIL_E_SIM_PUK2 5
 | 
			
		||||
#define RIL_E_REQUEST_NOT_SUPPORTED 6
 | 
			
		||||
#define RIL_E_CANCELLED 7
 | 
			
		||||
#define RIL_E_OP_NOT_ALLOWED_DURING_VOICE_CALL 8
 | 
			
		||||
#define RIL_E_OP_NOT_ALLOWED_BEFORE_REG_TO_NW 9
 | 
			
		||||
#define RIL_E_SMS_SEND_FAIL_RETRY 10
 | 
			
		||||
#define RIL_E_SIM_ABSENT 11
 | 
			
		||||
#define RIL_E_SUBSCRIPTION_NOT_AVAILABLE 12
 | 
			
		||||
#define RIL_E_MODE_NOT_SUPPORTED 13
 | 
			
		||||
#define RIL_E_FDN_CHECK_FAILURE 14
 | 
			
		||||
#define RIL_E_ILLEGAL_SIM_OR_ME 15
 | 
			
		||||
#define RIL_E_UNUSED 16
 | 
			
		||||
#define RIL_E_DIAL_MODIFIED_TO_USSD 17
 | 
			
		||||
#define RIL_E_DIAL_MODIFIED_TO_SS 18
 | 
			
		||||
#define RIL_E_DIAL_MODIFIED_TO_DIAL 19
 | 
			
		||||
#define RIL_E_USSD_MODIFIED_TO_DIAL 20
 | 
			
		||||
#define RIL_E_USSD_MODIFIED_TO_SS 21
 | 
			
		||||
#define RIL_E_USSD_MODIFIED_TO_USSD 22
 | 
			
		||||
#define RIL_E_SS_MODIFIED_TO_DIAL 23
 | 
			
		||||
#define RIL_E_SS_MODIFIED_TO_USSD 24
 | 
			
		||||
#define RIL_E_SS_MODIFIED_TO_SS 25
 | 
			
		||||
#define RIL_E_SUBSCRIPTION_NOT_SUPPORTED 26
 | 
			
		||||
#define RIL_E_MISSING_RESOURCE 27
 | 
			
		||||
#define RIL_E_NO_SUCH_ELEMENT 28
 | 
			
		||||
#define RIL_E_INVALID_PARAMETER 29
 | 
			
		||||
#include <ofono/ril-constants.h>
 | 
			
		||||
 | 
			
		||||
/* call states */
 | 
			
		||||
enum ril_call_state {
 | 
			
		||||
	RIL_CALL_ACTIVE   = 0,
 | 
			
		||||
	RIL_CALL_HOLDING  = 1,
 | 
			
		||||
	RIL_CALL_DIALING  = 2,
 | 
			
		||||
	RIL_CALL_ALERTING = 3,
 | 
			
		||||
	RIL_CALL_INCOMING = 4,
 | 
			
		||||
	RIL_CALL_WAITING  = 5
 | 
			
		||||
};
 | 
			
		||||
#define RIL_MAX_UUID_LENGTH 64
 | 
			
		||||
 | 
			
		||||
/* Radio state */
 | 
			
		||||
enum ril_radio_state {
 | 
			
		||||
@@ -114,75 +71,185 @@ enum ril_radio_tech {
 | 
			
		||||
	RADIO_TECH_HSPAP    = 15,
 | 
			
		||||
	RADIO_TECH_GSM      = 16,
 | 
			
		||||
	RADIO_TECH_TD_SCDMA = 17,
 | 
			
		||||
	RADIO_TECH_IWLAN    = 18
 | 
			
		||||
	RADIO_TECH_IWLAN    = 18,
 | 
			
		||||
	RADIO_TECH_LTE_CA   = 19
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Radio capabilities */
 | 
			
		||||
enum ril_radio_access_family {
 | 
			
		||||
	RAF_GPRS     = (1 << RADIO_TECH_GPRS),
 | 
			
		||||
	RAF_EDGE     = (1 << RADIO_TECH_EDGE),
 | 
			
		||||
	RAF_UMTS     = (1 << RADIO_TECH_UMTS),
 | 
			
		||||
	RAF_IS95A    = (1 << RADIO_TECH_IS95A),
 | 
			
		||||
	RAF_IS95B    = (1 << RADIO_TECH_IS95B),
 | 
			
		||||
	RAF_1xRTT    = (1 << RADIO_TECH_1xRTT),
 | 
			
		||||
	RAF_EVDO_0   = (1 << RADIO_TECH_EVDO_0),
 | 
			
		||||
	RAF_EVDO_A   = (1 << RADIO_TECH_EVDO_A),
 | 
			
		||||
	RAF_HSDPA    = (1 << RADIO_TECH_HSDPA),
 | 
			
		||||
	RAF_HSUPA    = (1 << RADIO_TECH_HSUPA),
 | 
			
		||||
	RAF_HSPA     = (1 << RADIO_TECH_HSPA),
 | 
			
		||||
	RAF_EVDO_B   = (1 << RADIO_TECH_EVDO_B),
 | 
			
		||||
	RAF_EHRPD    = (1 << RADIO_TECH_EHRPD),
 | 
			
		||||
	RAF_LTE      = (1 << RADIO_TECH_LTE),
 | 
			
		||||
	RAF_HSPAP    = (1 << RADIO_TECH_HSPAP),
 | 
			
		||||
	RAF_GSM      = (1 << RADIO_TECH_GSM),
 | 
			
		||||
	RAF_TD_SCDMA = (1 << RADIO_TECH_TD_SCDMA),
 | 
			
		||||
	RAF_LTE_CA   = (1 << RADIO_TECH_LTE_CA)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_radio_capability_phase {
 | 
			
		||||
	RC_PHASE_CONFIGURED = 0,
 | 
			
		||||
	RC_PHASE_START      = 1,
 | 
			
		||||
	RC_PHASE_APPLY      = 2,
 | 
			
		||||
	RC_PHASE_UNSOL_RSP  = 3,
 | 
			
		||||
	RC_PHASE_FINISH     = 4
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_radio_capability_status {
 | 
			
		||||
	RC_STATUS_NONE      = 0,
 | 
			
		||||
	RC_STATUS_SUCCESS   = 1,
 | 
			
		||||
	RC_STATUS_FAIL      = 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define RIL_RADIO_CAPABILITY_VERSION 1
 | 
			
		||||
 | 
			
		||||
struct ril_radio_capability {
 | 
			
		||||
	int version;
 | 
			
		||||
	int session;
 | 
			
		||||
	enum ril_radio_capability_phase phase;
 | 
			
		||||
	enum ril_radio_access_family rat;
 | 
			
		||||
	char logicalModemUuid[RIL_MAX_UUID_LENGTH];
 | 
			
		||||
	int status;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_uicc_subscription_action {
 | 
			
		||||
	RIL_UICC_SUBSCRIPTION_DEACTIVATE = 0,
 | 
			
		||||
	RIL_UICC_SUBSCRIPTION_ACTIVATE = 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* See RIL_REQUEST_LAST_CALL_FAIL_CAUSE */
 | 
			
		||||
#define CALL_FAIL_UNOBTAINABLE_NUMBER 1
 | 
			
		||||
#define CALL_FAIL_NORMAL 16
 | 
			
		||||
#define CALL_FAIL_BUSY 17
 | 
			
		||||
#define CALL_FAIL_CONGESTION 34
 | 
			
		||||
#define CALL_FAIL_ACM_LIMIT_EXCEEDED 68
 | 
			
		||||
#define CALL_FAIL_CALL_BARRED 240
 | 
			
		||||
#define CALL_FAIL_FDN_BLOCKED 241
 | 
			
		||||
#define CALL_FAIL_IMSI_UNKNOWN_IN_VLR 242
 | 
			
		||||
#define CALL_FAIL_IMEI_NOT_ACCEPTED 243
 | 
			
		||||
#define CALL_FAIL_DIAL_MODIFIED_TO_USSD 244
 | 
			
		||||
#define CALL_FAIL_DIAL_MODIFIED_TO_SS 245
 | 
			
		||||
#define CALL_FAIL_DIAL_MODIFIED_TO_DIAL 246
 | 
			
		||||
#define CALL_FAIL_CDMA_LOCKED_UNTIL_POWER_CYCLE 1000
 | 
			
		||||
#define CALL_FAIL_CDMA_DROP 1001
 | 
			
		||||
#define CALL_FAIL_CDMA_INTERCEPT 1002
 | 
			
		||||
#define CALL_FAIL_CDMA_REORDER 1003
 | 
			
		||||
#define CALL_FAIL_CDMA_SO_REJECT 1004
 | 
			
		||||
#define CALL_FAIL_CDMA_RETRY_ORDER 1005
 | 
			
		||||
#define CALL_FAIL_CDMA_ACCESS_FAILURE 1006
 | 
			
		||||
#define CALL_FAIL_CDMA_PREEMPTED 1007
 | 
			
		||||
#define CALL_FAIL_CDMA_NOT_EMERGENCY 1008
 | 
			
		||||
#define CALL_FAIL_CDMA_ACCESS_BLOCKED 1009
 | 
			
		||||
#define CALL_FAIL_ERROR_UNSPECIFIED 0xffff
 | 
			
		||||
enum ril_call_fail_cause {
 | 
			
		||||
	CALL_FAIL_UNOBTAINABLE_NUMBER = 1,
 | 
			
		||||
	CALL_FAIL_NO_ROUTE_TO_DESTINATION = 3,
 | 
			
		||||
	CALL_FAIL_CHANNEL_UNACCEPTABLE = 6,
 | 
			
		||||
	CALL_FAIL_OPERATOR_DETERMINED_BARRING = 8,
 | 
			
		||||
	CALL_FAIL_NORMAL = 16,
 | 
			
		||||
	CALL_FAIL_BUSY = 17,
 | 
			
		||||
	CALL_FAIL_NO_USER_RESPONDING = 18,
 | 
			
		||||
	CALL_FAIL_NO_ANSWER_FROM_USER = 19,
 | 
			
		||||
	CALL_FAIL_CALL_REJECTED = 21,
 | 
			
		||||
	CALL_FAIL_NUMBER_CHANGED = 22,
 | 
			
		||||
	CALL_FAIL_DESTINATION_OUT_OF_ORDER = 27,
 | 
			
		||||
	CALL_FAIL_INVALID_NUMBER_FORMAT = 28,
 | 
			
		||||
	CALL_FAIL_FACILITY_REJECTED = 29,
 | 
			
		||||
	CALL_FAIL_RESP_TO_STATUS_ENQUIRY = 30,
 | 
			
		||||
	CALL_FAIL_NORMAL_UNSPECIFIED = 31,
 | 
			
		||||
	CALL_FAIL_CONGESTION = 34,
 | 
			
		||||
	CALL_FAIL_NETWORK_OUT_OF_ORDER = 38,
 | 
			
		||||
	CALL_FAIL_TEMPORARY_FAILURE = 41,
 | 
			
		||||
	CALL_FAIL_SWITCHING_EQUIPMENT_CONGESTION = 42,
 | 
			
		||||
	CALL_FAIL_ACCESS_INFORMATION_DISCARDED = 43,
 | 
			
		||||
	CALL_FAIL_REQUESTED_CIRCUIT_OR_CHANNEL_NOT_AVAILABLE = 44,
 | 
			
		||||
	CALL_FAIL_RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47,
 | 
			
		||||
	CALL_FAIL_QOS_UNAVAILABLE = 49,
 | 
			
		||||
	CALL_FAIL_REQUESTED_FACILITY_NOT_SUBSCRIBED = 50,
 | 
			
		||||
	CALL_FAIL_INCOMING_CALLS_BARRED_WITHIN_CUG = 55,
 | 
			
		||||
	CALL_FAIL_BEARER_CAPABILITY_NOT_AUTHORIZED = 57,
 | 
			
		||||
	CALL_FAIL_BEARER_CAPABILITY_UNAVAILABLE = 58,
 | 
			
		||||
	CALL_FAIL_SERVICE_OPTION_NOT_AVAILABLE = 63,
 | 
			
		||||
	CALL_FAIL_BEARER_SERVICE_NOT_IMPLEMENTED = 65,
 | 
			
		||||
	CALL_FAIL_ACM_LIMIT_EXCEEDED = 68,
 | 
			
		||||
	CALL_FAIL_REQUESTED_FACILITY_NOT_IMPLEMENTED = 69,
 | 
			
		||||
	CALL_FAIL_ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70,
 | 
			
		||||
	CALL_FAIL_SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79,
 | 
			
		||||
	CALL_FAIL_INVALID_TRANSACTION_IDENTIFIER = 81,
 | 
			
		||||
	CALL_FAIL_USER_NOT_MEMBER_OF_CUG = 87,
 | 
			
		||||
	CALL_FAIL_INCOMPATIBLE_DESTINATION = 88,
 | 
			
		||||
	CALL_FAIL_INVALID_TRANSIT_NW_SELECTION = 91,
 | 
			
		||||
	CALL_FAIL_SEMANTICALLY_INCORRECT_MESSAGE = 95,
 | 
			
		||||
	CALL_FAIL_INVALID_MANDATORY_INFORMATION = 96,
 | 
			
		||||
	CALL_FAIL_MESSAGE_TYPE_NON_IMPLEMENTED = 97,
 | 
			
		||||
	CALL_FAIL_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98,
 | 
			
		||||
	CALL_FAIL_INFORMATION_ELEMENT_NON_EXISTENT = 99,
 | 
			
		||||
	CALL_FAIL_CONDITIONAL_IE_ERROR = 100,
 | 
			
		||||
	CALL_FAIL_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101,
 | 
			
		||||
	CALL_FAIL_RECOVERY_ON_TIMER_EXPIRED = 102,
 | 
			
		||||
	CALL_FAIL_PROTOCOL_ERROR_UNSPECIFIED = 111,
 | 
			
		||||
	CALL_FAIL_INTERWORKING_UNSPECIFIED = 127,
 | 
			
		||||
	CALL_FAIL_CALL_BARRED = 240,
 | 
			
		||||
	CALL_FAIL_FDN_BLOCKED = 241,
 | 
			
		||||
	CALL_FAIL_IMSI_UNKNOWN_IN_VLR = 242,
 | 
			
		||||
	CALL_FAIL_IMEI_NOT_ACCEPTED = 243,
 | 
			
		||||
	CALL_FAIL_DIAL_MODIFIED_TO_USSD = 244,
 | 
			
		||||
	CALL_FAIL_DIAL_MODIFIED_TO_SS = 245,
 | 
			
		||||
	CALL_FAIL_DIAL_MODIFIED_TO_DIAL = 246,
 | 
			
		||||
	CALL_FAIL_ERROR_UNSPECIFIED = 0xffff,
 | 
			
		||||
 | 
			
		||||
/* Not defined in ril.h but valid 3GPP specific cause values
 | 
			
		||||
 * for call control. See 3GPP TS 24.008 Annex H. */
 | 
			
		||||
#define CALL_FAIL_NO_ROUTE_TO_DESTINATION 3
 | 
			
		||||
#define CALL_FAIL_CHANNEL_UNACCEPTABLE 6
 | 
			
		||||
#define CALL_FAIL_OPERATOR_DETERMINED_BARRING 8
 | 
			
		||||
#define CALL_FAIL_NO_USER_RESPONDING 18
 | 
			
		||||
#define CALL_FAIL_USER_ALERTING_NO_ANSWER 19
 | 
			
		||||
#define CALL_FAIL_CALL_REJECTED 21
 | 
			
		||||
#define CALL_FAIL_NUMBER_CHANGED 22
 | 
			
		||||
#define CALL_FAIL_ANONYMOUS_CALL_REJECTION 24
 | 
			
		||||
#define CALL_FAIL_PRE_EMPTION 25
 | 
			
		||||
#define CALL_FAIL_DESTINATION_OUT_OF_ORDER 27
 | 
			
		||||
#define CALL_FAIL_INCOMPLETE_NUMBER 28
 | 
			
		||||
#define CALL_FAIL_FACILITY_REJECTED 29
 | 
			
		||||
#define CALL_FAIL_NORMAL_UNSPECIFIED 31
 | 
			
		||||
	CALL_FAIL_ANONYMOUS_CALL_REJECTION = 24,
 | 
			
		||||
	CALL_FAIL_PRE_EMPTION = 25
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_data_call_fail_cause {
 | 
			
		||||
    PDP_FAIL_NONE = 0,
 | 
			
		||||
    PDP_FAIL_OPERATOR_BARRED = 0x08,
 | 
			
		||||
    PDP_FAIL_INSUFFICIENT_RESOURCES = 0x1A,
 | 
			
		||||
    PDP_FAIL_MISSING_UKNOWN_APN = 0x1B,
 | 
			
		||||
    PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE = 0x1C,
 | 
			
		||||
    PDP_FAIL_USER_AUTHENTICATION = 0x1D,
 | 
			
		||||
    PDP_FAIL_ACTIVATION_REJECT_GGSN = 0x1E,
 | 
			
		||||
    PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED = 0x1F,
 | 
			
		||||
    PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED = 0x20,
 | 
			
		||||
    PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED = 0x21,
 | 
			
		||||
    PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER = 0x22,
 | 
			
		||||
    PDP_FAIL_NSAPI_IN_USE = 0x23,
 | 
			
		||||
    PDP_FAIL_REGULAR_DEACTIVATION = 0x24,
 | 
			
		||||
    PDP_FAIL_ONLY_IPV4_ALLOWED = 0x32,
 | 
			
		||||
    PDP_FAIL_ONLY_IPV6_ALLOWED = 0x33,
 | 
			
		||||
    PDP_FAIL_ONLY_SINGLE_BEARER_ALLOWED = 0x34,
 | 
			
		||||
    PDP_FAIL_PROTOCOL_ERRORS = 0x6F,
 | 
			
		||||
    PDP_FAIL_VOICE_REGISTRATION_FAIL = -1,
 | 
			
		||||
    PDP_FAIL_DATA_REGISTRATION_FAIL = -2,
 | 
			
		||||
    PDP_FAIL_SIGNAL_LOST = -3,
 | 
			
		||||
    PDP_FAIL_PREF_RADIO_TECH_CHANGED = -4,
 | 
			
		||||
    PDP_FAIL_RADIO_POWER_OFF = -5,
 | 
			
		||||
    PDP_FAIL_TETHERED_CALL_ACTIVE = -6,
 | 
			
		||||
    PDP_FAIL_ERROR_UNSPECIFIED = 0xffff
 | 
			
		||||
	PDP_FAIL_NONE = 0,
 | 
			
		||||
	PDP_FAIL_OPERATOR_BARRED = 0x08,
 | 
			
		||||
	PDP_FAIL_INSUFFICIENT_RESOURCES = 0x1A,
 | 
			
		||||
	PDP_FAIL_MISSING_UKNOWN_APN = 0x1B,
 | 
			
		||||
	PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE = 0x1C,
 | 
			
		||||
	PDP_FAIL_USER_AUTHENTICATION = 0x1D,
 | 
			
		||||
	PDP_FAIL_ACTIVATION_REJECT_GGSN = 0x1E,
 | 
			
		||||
	PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED = 0x1F,
 | 
			
		||||
	PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED = 0x20,
 | 
			
		||||
	PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED = 0x21,
 | 
			
		||||
	PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER = 0x22,
 | 
			
		||||
	PDP_FAIL_NSAPI_IN_USE = 0x23,
 | 
			
		||||
	PDP_FAIL_REGULAR_DEACTIVATION = 0x24,
 | 
			
		||||
	PDP_FAIL_QOS_NOT_ACCEPTED = 0x25,
 | 
			
		||||
	PDP_FAIL_NETWORK_FAILURE = 0x26,
 | 
			
		||||
	PDP_FAIL_UMTS_REACTIVATION_REQ = 0x27,
 | 
			
		||||
	PDP_FAIL_FEATURE_NOT_SUPP = 0x28,
 | 
			
		||||
	PDP_FAIL_TFT_SEMANTIC_ERROR = 0x29,
 | 
			
		||||
	PDP_FAIL_TFT_SYTAX_ERROR = 0x2A,
 | 
			
		||||
	PDP_FAIL_UNKNOWN_PDP_CONTEXT = 0x2B,
 | 
			
		||||
	PDP_FAIL_FILTER_SEMANTIC_ERROR = 0x2C,
 | 
			
		||||
	PDP_FAIL_FILTER_SYTAX_ERROR = 0x2D,
 | 
			
		||||
	PDP_FAIL_PDP_WITHOUT_ACTIVE_TFT = 0x2E,
 | 
			
		||||
	PDP_FAIL_ONLY_IPV4_ALLOWED = 0x32,
 | 
			
		||||
	PDP_FAIL_ONLY_IPV6_ALLOWED = 0x33,
 | 
			
		||||
	PDP_FAIL_ONLY_SINGLE_BEARER_ALLOWED = 0x34,
 | 
			
		||||
	PDP_FAIL_ESM_INFO_NOT_RECEIVED = 0x35,
 | 
			
		||||
	PDP_FAIL_PDN_CONN_DOES_NOT_EXIST = 0x36,
 | 
			
		||||
	PDP_FAIL_MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37,
 | 
			
		||||
	PDP_FAIL_MAX_ACTIVE_PDP_CONTEXT_REACHED = 0x41,
 | 
			
		||||
	PDP_FAIL_UNSUPPORTED_APN_IN_CURRENT_PLMN = 0x42,
 | 
			
		||||
	PDP_FAIL_INVALID_TRANSACTION_ID = 0x51,
 | 
			
		||||
	PDP_FAIL_MESSAGE_INCORRECT_SEMANTIC = 0x5F,
 | 
			
		||||
	PDP_FAIL_INVALID_MANDATORY_INFO = 0x60,
 | 
			
		||||
	PDP_FAIL_MESSAGE_TYPE_UNSUPPORTED = 0x61,
 | 
			
		||||
	PDP_FAIL_MSG_TYPE_NONCOMPATIBLE_STATE = 0x62,
 | 
			
		||||
	PDP_FAIL_UNKNOWN_INFO_ELEMENT = 0x63,
 | 
			
		||||
	PDP_FAIL_CONDITIONAL_IE_ERROR = 0x64,
 | 
			
		||||
	PDP_FAIL_MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 0x65,
 | 
			
		||||
	PDP_FAIL_PROTOCOL_ERRORS = 0x6F,
 | 
			
		||||
	PDP_FAIL_APN_TYPE_CONFLICT = 0x70,
 | 
			
		||||
	PDP_FAIL_INVALID_PCSCF_ADDR = 0x71,
 | 
			
		||||
	PDP_FAIL_INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 0x72,
 | 
			
		||||
	PDP_FAIL_EMM_ACCESS_BARRED = 0x73,
 | 
			
		||||
	PDP_FAIL_EMERGENCY_IFACE_ONLY = 0x74,
 | 
			
		||||
	PDP_FAIL_IFACE_MISMATCH = 0x75,
 | 
			
		||||
	PDP_FAIL_COMPANION_IFACE_IN_USE = 0x76,
 | 
			
		||||
	PDP_FAIL_IP_ADDRESS_MISMATCH = 0x77,
 | 
			
		||||
	PDP_FAIL_IFACE_AND_POL_FAMILY_MISMATCH = 0x78,
 | 
			
		||||
	PDP_FAIL_EMM_ACCESS_BARRED_INFINITE_RETRY = 0x79,
 | 
			
		||||
	PDP_FAIL_AUTH_FAILURE_ON_EMERGENCY_CALL = 0x7A,
 | 
			
		||||
	PDP_FAIL_VOICE_REGISTRATION_FAIL = -1,
 | 
			
		||||
	PDP_FAIL_DATA_REGISTRATION_FAIL = -2,
 | 
			
		||||
	PDP_FAIL_SIGNAL_LOST = -3,
 | 
			
		||||
	PDP_FAIL_PREF_RADIO_TECH_CHANGED = -4,
 | 
			
		||||
	PDP_FAIL_RADIO_POWER_OFF = -5,
 | 
			
		||||
	PDP_FAIL_TETHERED_CALL_ACTIVE = -6,
 | 
			
		||||
	PDP_FAIL_ERROR_UNSPECIFIED = 0xffff
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* RIL_REQUEST_DEACTIVATE_DATA_CALL parameter */
 | 
			
		||||
@@ -200,6 +267,12 @@ enum ril_data_profile {
 | 
			
		||||
	RIL_DATA_PROFILE_INVALID = 0xFFFFFFFF
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_profile_type {
 | 
			
		||||
	RIL_PROFILE_COMMON = 0,
 | 
			
		||||
	RIL_PROFILE_3GPP = 1,
 | 
			
		||||
	RIL_PROFILE_3GPP2 = 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_auth {
 | 
			
		||||
	RIL_AUTH_NONE = 0,
 | 
			
		||||
	RIL_AUTH_PAP = 1,
 | 
			
		||||
@@ -287,222 +360,14 @@ enum ril_cell_info_type {
 | 
			
		||||
	RIL_CELL_INFO_TYPE_TD_SCDMA = 5
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info_gsm {
 | 
			
		||||
	int mcc;            /* Mobile Country Code (0..999) */
 | 
			
		||||
	int mnc;            /* Mobile Network Code (0..999) */
 | 
			
		||||
	int lac;            /* Location Area Code (0..65535) */
 | 
			
		||||
	int cid;            /* GSM Cell Identity (0..65535) TS 27.007 */
 | 
			
		||||
	int signalStrength; /* (0-31, 99) TS 27.007 */
 | 
			
		||||
	int bitErrorRate;   /* (0-7, 99) TS 27.007 */
 | 
			
		||||
enum ril_restricted_state {
 | 
			
		||||
	RIL_RESTRICTED_STATE_NONE         = 0x00,
 | 
			
		||||
	RIL_RESTRICTED_STATE_CS_EMERGENCY = 0x01,
 | 
			
		||||
	RIL_RESTRICTED_STATE_CS_NORMAL    = 0x02,
 | 
			
		||||
	RIL_RESTRICTED_STATE_CS_ALL       = 0x04,
 | 
			
		||||
	RIL_RESTRICTED_STATE_PS_ALL       = 0x10
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info_wcdma {
 | 
			
		||||
	int mcc;            /* Mobile Country Code (0..999) */
 | 
			
		||||
	int mnc;            /* Mobile Network Code (0..999) */
 | 
			
		||||
	int lac;            /* Location Area Code (0..65535) */
 | 
			
		||||
	int cid;            /* UMTS Cell Identity (0..268435455) TS 25.331 */
 | 
			
		||||
	int psc;            /* Primary Scrambling Code (0..511) TS 25.331) */
 | 
			
		||||
	int signalStrength; /* (0-31, 99) TS 27.007 */
 | 
			
		||||
	int bitErrorRate;   /* (0-7, 99) TS 27.007 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ril_cell_info_lte {
 | 
			
		||||
	int mcc;            /* Mobile Country Code (0..999) */
 | 
			
		||||
	int mnc;            /* Mobile Network Code (0..999) */
 | 
			
		||||
	int ci;             /* Cell Identity */
 | 
			
		||||
	int pci;            /* Physical cell id (0..503) */
 | 
			
		||||
	int tac;            /* Tracking area code */
 | 
			
		||||
	int signalStrength; /* (0-31, 99) TS 27.007 8.5 */
 | 
			
		||||
	int rsrp;           /* Reference Signal Receive Power TS 36.133 */
 | 
			
		||||
	int rsrq;           /* Reference Signal Receive Quality TS 36.133 */
 | 
			
		||||
	int rssnr;          /* Reference Signal-to-Noise Ratio TS 36.101*/
 | 
			
		||||
	int cqi;            /* Channel Quality Indicator TS 36.101 */
 | 
			
		||||
	int timingAdvance;  /* (Distance = 300m/us) TS 36.321 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* RIL Request Messages */
 | 
			
		||||
#define RIL_REQUEST_GET_SIM_STATUS 1
 | 
			
		||||
#define RIL_REQUEST_ENTER_SIM_PIN 2
 | 
			
		||||
#define RIL_REQUEST_ENTER_SIM_PUK 3
 | 
			
		||||
#define RIL_REQUEST_ENTER_SIM_PIN2 4
 | 
			
		||||
#define RIL_REQUEST_ENTER_SIM_PUK2 5
 | 
			
		||||
#define RIL_REQUEST_CHANGE_SIM_PIN 6
 | 
			
		||||
#define RIL_REQUEST_CHANGE_SIM_PIN2 7
 | 
			
		||||
#define RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION 8
 | 
			
		||||
#define RIL_REQUEST_GET_CURRENT_CALLS 9
 | 
			
		||||
#define RIL_REQUEST_DIAL 10
 | 
			
		||||
#define RIL_REQUEST_GET_IMSI 11
 | 
			
		||||
#define RIL_REQUEST_HANGUP 12
 | 
			
		||||
#define RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND 13
 | 
			
		||||
#define RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND 14
 | 
			
		||||
#define RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE 15
 | 
			
		||||
#define RIL_REQUEST_CONFERENCE 16
 | 
			
		||||
#define RIL_REQUEST_UDUB 17
 | 
			
		||||
#define RIL_REQUEST_LAST_CALL_FAIL_CAUSE 18
 | 
			
		||||
#define RIL_REQUEST_SIGNAL_STRENGTH 19
 | 
			
		||||
#define RIL_REQUEST_VOICE_REGISTRATION_STATE 20
 | 
			
		||||
#define RIL_REQUEST_DATA_REGISTRATION_STATE 21
 | 
			
		||||
#define RIL_REQUEST_OPERATOR 22
 | 
			
		||||
#define RIL_REQUEST_RADIO_POWER 23
 | 
			
		||||
#define RIL_REQUEST_DTMF 24
 | 
			
		||||
#define RIL_REQUEST_SEND_SMS 25
 | 
			
		||||
#define RIL_REQUEST_SEND_SMS_EXPECT_MORE 26
 | 
			
		||||
#define RIL_REQUEST_SETUP_DATA_CALL 27
 | 
			
		||||
#define RIL_REQUEST_SIM_IO 28
 | 
			
		||||
#define RIL_REQUEST_SEND_USSD 29
 | 
			
		||||
#define RIL_REQUEST_CANCEL_USSD 30
 | 
			
		||||
#define RIL_REQUEST_GET_CLIR 31
 | 
			
		||||
#define RIL_REQUEST_SET_CLIR 32
 | 
			
		||||
#define RIL_REQUEST_QUERY_CALL_FORWARD_STATUS 33
 | 
			
		||||
#define RIL_REQUEST_SET_CALL_FORWARD 34
 | 
			
		||||
#define RIL_REQUEST_QUERY_CALL_WAITING 35
 | 
			
		||||
#define RIL_REQUEST_SET_CALL_WAITING 36
 | 
			
		||||
#define RIL_REQUEST_SMS_ACKNOWLEDGE  37
 | 
			
		||||
#define RIL_REQUEST_GET_IMEI 38
 | 
			
		||||
#define RIL_REQUEST_GET_IMEISV 39
 | 
			
		||||
#define RIL_REQUEST_ANSWER 40
 | 
			
		||||
#define RIL_REQUEST_DEACTIVATE_DATA_CALL 41
 | 
			
		||||
#define RIL_REQUEST_QUERY_FACILITY_LOCK 42
 | 
			
		||||
#define RIL_REQUEST_SET_FACILITY_LOCK 43
 | 
			
		||||
#define RIL_REQUEST_CHANGE_BARRING_PASSWORD 44
 | 
			
		||||
#define RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE 45
 | 
			
		||||
#define RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC 46
 | 
			
		||||
#define RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL 47
 | 
			
		||||
#define RIL_REQUEST_QUERY_AVAILABLE_NETWORKS 48
 | 
			
		||||
#define RIL_REQUEST_DTMF_START 49
 | 
			
		||||
#define RIL_REQUEST_DTMF_STOP 50
 | 
			
		||||
#define RIL_REQUEST_BASEBAND_VERSION 51
 | 
			
		||||
#define RIL_REQUEST_SEPARATE_CONNECTION 52
 | 
			
		||||
#define RIL_REQUEST_SET_MUTE 53
 | 
			
		||||
#define RIL_REQUEST_GET_MUTE 54
 | 
			
		||||
#define RIL_REQUEST_QUERY_CLIP 55
 | 
			
		||||
#define RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE 56
 | 
			
		||||
#define RIL_REQUEST_DATA_CALL_LIST 57
 | 
			
		||||
#define RIL_REQUEST_RESET_RADIO 58
 | 
			
		||||
#define RIL_REQUEST_OEM_HOOK_RAW 59
 | 
			
		||||
#define RIL_REQUEST_OEM_HOOK_STRINGS 60
 | 
			
		||||
#define RIL_REQUEST_SCREEN_STATE 61
 | 
			
		||||
#define RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION 62
 | 
			
		||||
#define RIL_REQUEST_WRITE_SMS_TO_SIM 63
 | 
			
		||||
#define RIL_REQUEST_DELETE_SMS_ON_SIM 64
 | 
			
		||||
#define RIL_REQUEST_SET_BAND_MODE 65
 | 
			
		||||
#define RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE 66
 | 
			
		||||
#define RIL_REQUEST_STK_GET_PROFILE 67
 | 
			
		||||
#define RIL_REQUEST_STK_SET_PROFILE 68
 | 
			
		||||
#define RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND 69
 | 
			
		||||
#define RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE 70
 | 
			
		||||
#define RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM 71
 | 
			
		||||
#define RIL_REQUEST_EXPLICIT_CALL_TRANSFER 72
 | 
			
		||||
#define RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE 73
 | 
			
		||||
#define RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE 74
 | 
			
		||||
#define RIL_REQUEST_GET_NEIGHBORING_CELL_IDS 75
 | 
			
		||||
#define RIL_REQUEST_SET_LOCATION_UPDATES 76
 | 
			
		||||
#define RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE 77
 | 
			
		||||
#define RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE 78
 | 
			
		||||
#define RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE 79
 | 
			
		||||
#define RIL_REQUEST_SET_TTY_MODE 80
 | 
			
		||||
#define RIL_REQUEST_QUERY_TTY_MODE 81
 | 
			
		||||
#define RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE 82
 | 
			
		||||
#define RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE 83
 | 
			
		||||
#define RIL_REQUEST_CDMA_FLASH 84
 | 
			
		||||
#define RIL_REQUEST_CDMA_BURST_DTMF 85
 | 
			
		||||
#define RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY 86
 | 
			
		||||
#define RIL_REQUEST_CDMA_SEND_SMS 87
 | 
			
		||||
#define RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE 88
 | 
			
		||||
#define RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG 89
 | 
			
		||||
#define RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG 90
 | 
			
		||||
#define RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION 91
 | 
			
		||||
#define RIL_REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG 92
 | 
			
		||||
#define RIL_REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG 93
 | 
			
		||||
#define RIL_REQUEST_CDMA_SMS_BROADCAST_ACTIVATION 94
 | 
			
		||||
#define RIL_REQUEST_CDMA_SUBSCRIPTION 95
 | 
			
		||||
#define RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM 96
 | 
			
		||||
#define RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM 97
 | 
			
		||||
#define RIL_REQUEST_DEVICE_IDENTITY 98
 | 
			
		||||
#define RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE 99
 | 
			
		||||
#define RIL_REQUEST_GET_SMSC_ADDRESS 100
 | 
			
		||||
#define RIL_REQUEST_SET_SMSC_ADDRESS 101
 | 
			
		||||
#define RIL_REQUEST_REPORT_SMS_MEMORY_STATUS 102
 | 
			
		||||
#define RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING 103
 | 
			
		||||
#define RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE 104
 | 
			
		||||
#define RIL_REQUEST_ISIM_AUTHENTICATION 105
 | 
			
		||||
#define RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU 106
 | 
			
		||||
#define RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS 107
 | 
			
		||||
#define RIL_REQUEST_VOICE_RADIO_TECH 108
 | 
			
		||||
#define RIL_REQUEST_GET_CELL_INFO_LIST 109
 | 
			
		||||
#define RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE 110
 | 
			
		||||
#define RIL_REQUEST_SET_INITIAL_ATTACH_APN 111
 | 
			
		||||
#define RIL_REQUEST_IMS_REGISTRATION_STATE 112
 | 
			
		||||
#define RIL_REQUEST_IMS_SEND_SMS 113
 | 
			
		||||
#define RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC 114
 | 
			
		||||
#define RIL_REQUEST_SIM_OPEN_CHANNEL 115
 | 
			
		||||
#define RIL_REQUEST_SIM_CLOSE_CHANNEL 116
 | 
			
		||||
#define RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL 117
 | 
			
		||||
#define RIL_REQUEST_NV_READ_ITEM 118
 | 
			
		||||
#define RIL_REQUEST_NV_WRITE_ITEM 119
 | 
			
		||||
#define RIL_REQUEST_NV_WRITE_CDMA_PRL 120
 | 
			
		||||
#define RIL_REQUEST_NV_RESET_CONFIG 121
 | 
			
		||||
/* SET_UICC_SUBSCRIPTION was 115 in v9 and 122 in v10 and later */
 | 
			
		||||
#define RIL_REQUEST_V9_SET_UICC_SUBSCRIPTION  115
 | 
			
		||||
#define RIL_REQUEST_SET_UICC_SUBSCRIPTION  122
 | 
			
		||||
#define RIL_REQUEST_ALLOW_DATA  123
 | 
			
		||||
#define RIL_REQUEST_GET_HARDWARE_CONFIG 124
 | 
			
		||||
#define RIL_REQUEST_SIM_AUTHENTICATION 125
 | 
			
		||||
#define RIL_REQUEST_GET_DC_RT_INFO 126
 | 
			
		||||
#define RIL_REQUEST_SET_DC_RT_INFO_RATE 127
 | 
			
		||||
#define RIL_REQUEST_SET_DATA_PROFILE 128
 | 
			
		||||
#define RIL_REQUEST_SHUTDOWN 129
 | 
			
		||||
#define RIL_REQUEST_GET_RADIO_CAPABILITY 130
 | 
			
		||||
#define RIL_REQUEST_SET_RADIO_CAPABILITY 131
 | 
			
		||||
 | 
			
		||||
/* RIL Unsolicited Messages */
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_BASE 1000
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED 1000
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED 1001
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED 1002
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_NEW_SMS 1003
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT 1004
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM 1005
 | 
			
		||||
#define RIL_UNSOL_ON_USSD 1006
 | 
			
		||||
#define RIL_UNSOL_ON_USSD_REQUEST 1007
 | 
			
		||||
#define RIL_UNSOL_NITZ_TIME_RECEIVED  1008
 | 
			
		||||
#define RIL_UNSOL_SIGNAL_STRENGTH  1009
 | 
			
		||||
#define RIL_UNSOL_DATA_CALL_LIST_CHANGED 1010
 | 
			
		||||
#define RIL_UNSOL_SUPP_SVC_NOTIFICATION 1011
 | 
			
		||||
#define RIL_UNSOL_STK_SESSION_END 1012
 | 
			
		||||
#define RIL_UNSOL_STK_PROACTIVE_COMMAND 1013
 | 
			
		||||
#define RIL_UNSOL_STK_EVENT_NOTIFY 1014
 | 
			
		||||
#define RIL_UNSOL_STK_CALL_SETUP 1015
 | 
			
		||||
#define RIL_UNSOL_SIM_SMS_STORAGE_FULL 1016
 | 
			
		||||
#define RIL_UNSOL_SIM_REFRESH 1017
 | 
			
		||||
#define RIL_UNSOL_CALL_RING 1018
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED 1019
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_CDMA_NEW_SMS 1020
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS 1021
 | 
			
		||||
#define RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL 1022
 | 
			
		||||
#define RIL_UNSOL_RESTRICTED_STATE_CHANGED 1023
 | 
			
		||||
#define RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE 1024
 | 
			
		||||
#define RIL_UNSOL_CDMA_CALL_WAITING 1025
 | 
			
		||||
#define RIL_UNSOL_CDMA_OTA_PROVISION_STATUS 1026
 | 
			
		||||
#define RIL_UNSOL_CDMA_INFO_REC 1027
 | 
			
		||||
#define RIL_UNSOL_OEM_HOOK_RAW 1028
 | 
			
		||||
#define RIL_UNSOL_RINGBACK_TONE 1029
 | 
			
		||||
#define RIL_UNSOL_RESEND_INCALL_MUTE 1030
 | 
			
		||||
#define RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED 1031
 | 
			
		||||
#define RIL_UNSOL_CDMA_PRL_CHANGED 1032
 | 
			
		||||
#define RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE 1033
 | 
			
		||||
#define RIL_UNSOL_RIL_CONNECTED 1034
 | 
			
		||||
#define RIL_UNSOL_VOICE_RADIO_TECH_CHANGED 1035
 | 
			
		||||
#define RIL_UNSOL_CELL_INFO_LIST 1036
 | 
			
		||||
#define RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED 1037
 | 
			
		||||
#define RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED 1038
 | 
			
		||||
#define RIL_UNSOL_SRVCC_STATE_NOTIFY 1039
 | 
			
		||||
#define RIL_UNSOL_HARDWARE_CONFIG_CHANGED 1040
 | 
			
		||||
#define RIL_UNSOL_DC_RT_INFO_CHANGED 1041
 | 
			
		||||
#define RIL_UNSOL_RADIO_CAPABILITY 1042
 | 
			
		||||
#define RIL_UNSOL_ON_SS 1043
 | 
			
		||||
#define RIL_UNSOL_STK_CC_ALPHA_NOTIFY 1044
 | 
			
		||||
 | 
			
		||||
/* Suplementary services Service class*/
 | 
			
		||||
#define SERVICE_CLASS_NONE 0
 | 
			
		||||
 | 
			
		||||
@@ -510,6 +375,19 @@ struct ril_cell_info_lte {
 | 
			
		||||
#define RIL_FACILITY_UNLOCK "0"
 | 
			
		||||
#define RIL_FACILITY_LOCK "1"
 | 
			
		||||
 | 
			
		||||
/* See RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER (RIL_VERSION >= 15) */
 | 
			
		||||
enum  ril_unsolicited_response_filter {
 | 
			
		||||
	RIL_UR_SIGNAL_STRENGTH            = 0x01,
 | 
			
		||||
	RIL_UR_FULL_NETWORK_STATE         = 0x02,
 | 
			
		||||
	RIL_UR_DATA_CALL_DORMANCY_CHANGED = 0x04
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE result */
 | 
			
		||||
enum ril_network_selection_mode {
 | 
			
		||||
	RIL_NETWORK_SELECTION_MODE_AUTO	  = 0,
 | 
			
		||||
	RIL_NETWORK_SELECTION_MODE_MANUAL = 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /*__RIL_CONSTANTS_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,7 +1,8 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2016 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2016-2021 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2019 Open Mobile Platform LLC.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -17,8 +18,11 @@
 | 
			
		||||
#define RIL_DATA_H
 | 
			
		||||
 | 
			
		||||
#include "ril_types.h"
 | 
			
		||||
 | 
			
		||||
#include <ofono/gprs-context.h>
 | 
			
		||||
 | 
			
		||||
#include <glib-object.h>
 | 
			
		||||
 | 
			
		||||
enum ril_data_call_active {
 | 
			
		||||
	RIL_DATA_CALL_INACTIVE = 0,
 | 
			
		||||
	RIL_DATA_CALL_LINK_DOWN = 1,
 | 
			
		||||
@@ -36,6 +40,7 @@ struct ril_data_call {
 | 
			
		||||
	char **dnses;
 | 
			
		||||
	char **gateways;
 | 
			
		||||
	char **addresses;
 | 
			
		||||
	char **pcscf;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ril_data_call_list {
 | 
			
		||||
@@ -51,13 +56,14 @@ struct ril_data {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_data_manager_flags {
 | 
			
		||||
	RIL_DATA_MANAGER_3GLTE_HANDOVER = 0x01
 | 
			
		||||
	RIL_DATA_MANAGER_3GLTE_HANDOVER = 0x01,
 | 
			
		||||
	RIL_DATA_MANAGER_FORCE_GSM_ON_OTHER_SLOTS = 0x02
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_data_allow_data_opt {
 | 
			
		||||
	RIL_ALLOW_DATA_AUTO,
 | 
			
		||||
	RIL_ALLOW_DATA_ON,
 | 
			
		||||
	RIL_ALLOW_DATA_OFF
 | 
			
		||||
	RIL_ALLOW_DATA_ENABLED,
 | 
			
		||||
	RIL_ALLOW_DATA_DISABLED
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_data_call_format {
 | 
			
		||||
@@ -74,16 +80,12 @@ struct ril_data_options {
 | 
			
		||||
	unsigned int data_call_retry_delay_ms;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ril_data_role {
 | 
			
		||||
	RIL_DATA_ROLE_NONE,    /* Data not allowed */
 | 
			
		||||
	RIL_DATA_ROLE_MMS,     /* Data is allowed at any speed */
 | 
			
		||||
	RIL_DATA_ROLE_INTERNET /* Data is allowed at full speed */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ril_data_manager;
 | 
			
		||||
struct ril_data_manager *ril_data_manager_new(enum ril_data_manager_flags flg);
 | 
			
		||||
struct ril_data_manager *ril_data_manager_ref(struct ril_data_manager *dm);
 | 
			
		||||
void ril_data_manager_unref(struct ril_data_manager *dm);
 | 
			
		||||
void ril_data_manager_check_data(struct ril_data_manager *dm);
 | 
			
		||||
void ril_data_manager_assert_data_on(struct ril_data_manager *dm);
 | 
			
		||||
 | 
			
		||||
typedef void (*ril_data_cb_t)(struct ril_data *data, void *arg);
 | 
			
		||||
typedef void (*ril_data_call_setup_cb_t)(struct ril_data *data,
 | 
			
		||||
@@ -94,10 +96,13 @@ typedef void (*ril_data_call_deactivate_cb_t)(struct ril_data *data,
 | 
			
		||||
 | 
			
		||||
struct ril_data *ril_data_new(struct ril_data_manager *dm, const char *name,
 | 
			
		||||
		struct ril_radio *radio, struct ril_network *network,
 | 
			
		||||
		GRilIoChannel *io, const struct ril_data_options *options);
 | 
			
		||||
		GRilIoChannel *io, const struct ril_data_options *options,
 | 
			
		||||
		const struct ril_slot_config *config,
 | 
			
		||||
		struct ril_vendor *vendor);
 | 
			
		||||
struct ril_data *ril_data_ref(struct ril_data *data);
 | 
			
		||||
void ril_data_unref(struct ril_data *data);
 | 
			
		||||
gboolean ril_data_allowed(struct ril_data *data);
 | 
			
		||||
void ril_data_poll_call_state(struct ril_data *data);
 | 
			
		||||
 | 
			
		||||
gulong ril_data_add_allow_changed_handler(struct ril_data *data,
 | 
			
		||||
					ril_data_cb_t cb, void *arg);
 | 
			
		||||
@@ -110,17 +115,25 @@ void ril_data_allow(struct ril_data *data, enum ril_data_role role);
 | 
			
		||||
struct ril_data_request;
 | 
			
		||||
struct ril_data_request *ril_data_call_setup(struct ril_data *data,
 | 
			
		||||
				const struct ofono_gprs_primary_context *ctx,
 | 
			
		||||
				enum ofono_gprs_context_type context_type,
 | 
			
		||||
				ril_data_call_setup_cb_t cb, void *arg);
 | 
			
		||||
struct ril_data_request *ril_data_call_deactivate(struct ril_data *data,
 | 
			
		||||
			int cid, ril_data_call_deactivate_cb_t cb, void *arg);
 | 
			
		||||
void ril_data_request_detach(struct ril_data_request *req);
 | 
			
		||||
void ril_data_request_cancel(struct ril_data_request *req);
 | 
			
		||||
 | 
			
		||||
gboolean ril_data_call_grab(struct ril_data *data, int cid, void *cookie);
 | 
			
		||||
void ril_data_call_release(struct ril_data *data, int cid, void *cookie);
 | 
			
		||||
 | 
			
		||||
void ril_data_call_free(struct ril_data_call *call);
 | 
			
		||||
struct ril_data_call *ril_data_call_dup(const struct ril_data_call *call);
 | 
			
		||||
struct ril_data_call *ril_data_call_find(struct ril_data_call_list *list,
 | 
			
		||||
								int cid);
 | 
			
		||||
 | 
			
		||||
/* Constructors of various kinds of RIL requests */
 | 
			
		||||
GRilIoRequest *ril_request_allow_data_new(gboolean allow);
 | 
			
		||||
GRilIoRequest *ril_request_deactivate_data_call_new(int cid);
 | 
			
		||||
 | 
			
		||||
#endif /* RIL_DATA_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2015-2016 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2015-2017 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
@@ -17,16 +17,24 @@
 | 
			
		||||
#include "ril_util.h"
 | 
			
		||||
#include "ril_log.h"
 | 
			
		||||
 | 
			
		||||
#include <gutil_idlequeue.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * TODO: No public RIL api to query manufacturer or model.
 | 
			
		||||
 * Check where to get, could /system/build.prop be updated to have good values?
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
enum ril_devinfo_cb_tag {
 | 
			
		||||
	DEVINFO_QUERY_SERIAL = 1,
 | 
			
		||||
	DEVINFO_QUERY_SVN
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ril_devinfo {
 | 
			
		||||
	struct ofono_devinfo *info;
 | 
			
		||||
	GRilIoQueue *q;
 | 
			
		||||
	guint register_id;
 | 
			
		||||
	guint imei_id;
 | 
			
		||||
	GUtilIdleQueue *iq;
 | 
			
		||||
	char *log_prefix;
 | 
			
		||||
	char *imeisv;
 | 
			
		||||
	char *imei;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -36,6 +44,7 @@ struct ril_devinfo_cbd {
 | 
			
		||||
	gpointer data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
 | 
			
		||||
#define ril_devinfo_cbd_free g_free
 | 
			
		||||
 | 
			
		||||
static inline struct ril_devinfo *ril_devinfo_get_data(
 | 
			
		||||
@@ -62,7 +71,7 @@ static void ril_devinfo_query_unsupported(struct ofono_devinfo *info,
 | 
			
		||||
	cb(ril_error_failure(&error), "", data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_devinfo_query_cb(GRilIoChannel *io, int status,
 | 
			
		||||
static void ril_devinfo_query_revision_cb(GRilIoChannel *io, int status,
 | 
			
		||||
			const void *data, guint len, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
@@ -73,7 +82,7 @@ static void ril_devinfo_query_cb(GRilIoChannel *io, int status,
 | 
			
		||||
		GRilIoParser rilp;
 | 
			
		||||
		grilio_parser_init(&rilp, data, len);
 | 
			
		||||
		res = grilio_parser_get_utf8(&rilp);
 | 
			
		||||
		DBG("%s", res);
 | 
			
		||||
		DBG_(cbd->di, "%s", res);
 | 
			
		||||
		cbd->cb(ril_error_ok(&error), res ? res : "", cbd->data);
 | 
			
		||||
		g_free(res);
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -86,23 +95,46 @@ static void ril_devinfo_query_revision(struct ofono_devinfo *info,
 | 
			
		||||
{
 | 
			
		||||
	struct ril_devinfo *di = ril_devinfo_get_data(info);
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
	grilio_queue_send_request_full(di->q, NULL, RIL_REQUEST_BASEBAND_VERSION,
 | 
			
		||||
				ril_devinfo_query_cb, ril_devinfo_cbd_free,
 | 
			
		||||
	DBG_(di, "");
 | 
			
		||||
	grilio_queue_send_request_full(di->q, NULL,
 | 
			
		||||
				RIL_REQUEST_BASEBAND_VERSION,
 | 
			
		||||
				ril_devinfo_query_revision_cb,
 | 
			
		||||
				ril_devinfo_cbd_free,
 | 
			
		||||
				ril_devinfo_cbd_new(di, cb, data));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean ril_devinfo_query_serial_cb(void *user_data)
 | 
			
		||||
static void ril_devinfo_query_serial_cb(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_devinfo_cbd *cbd = user_data;
 | 
			
		||||
	struct ril_devinfo *di = cbd->di;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
 | 
			
		||||
	GASSERT(di->imei_id);
 | 
			
		||||
	di->imei_id = 0;
 | 
			
		||||
 | 
			
		||||
	DBG_(di, "%s", di->imei);
 | 
			
		||||
	cbd->cb(ril_error_ok(&error), di->imei, cbd->data);
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_devinfo_query_svn_cb(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_devinfo_cbd *cbd = user_data;
 | 
			
		||||
	struct ril_devinfo *di = cbd->di;
 | 
			
		||||
	struct ofono_error error;
 | 
			
		||||
 | 
			
		||||
	DBG_(di, "%s", di->imeisv);
 | 
			
		||||
	if (di->imeisv && di->imeisv[0]) {
 | 
			
		||||
		cbd->cb(ril_error_ok(&error), di->imeisv, cbd->data);
 | 
			
		||||
	} else {
 | 
			
		||||
		cbd->cb(ril_error_failure(&error), "", cbd->data);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_devinfo_query(struct ril_devinfo *di,
 | 
			
		||||
			enum ril_devinfo_cb_tag tag, GUtilIdleFunc fn,
 | 
			
		||||
			ofono_devinfo_query_cb_t cb, void *data)
 | 
			
		||||
{
 | 
			
		||||
	GVERIFY_FALSE(gutil_idle_queue_cancel_tag(di->iq, tag));
 | 
			
		||||
	gutil_idle_queue_add_tag_full(di->iq, tag, fn,
 | 
			
		||||
					ril_devinfo_cbd_new(di, cb, data),
 | 
			
		||||
					ril_devinfo_cbd_free);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_devinfo_query_serial(struct ofono_devinfo *info,
 | 
			
		||||
@@ -111,29 +143,28 @@ static void ril_devinfo_query_serial(struct ofono_devinfo *info,
 | 
			
		||||
{
 | 
			
		||||
	struct ril_devinfo *di = ril_devinfo_get_data(info);
 | 
			
		||||
 | 
			
		||||
	GASSERT(!di->imei_id);
 | 
			
		||||
	if (di->imei_id) {
 | 
			
		||||
		g_source_remove(di->imei_id);
 | 
			
		||||
		di->imei_id = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DBG("%s", di->imei);
 | 
			
		||||
	di->imei_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
 | 
			
		||||
					ril_devinfo_query_serial_cb,
 | 
			
		||||
					ril_devinfo_cbd_new(di, cb, data),
 | 
			
		||||
					ril_devinfo_cbd_free);
 | 
			
		||||
	DBG_(di, "");
 | 
			
		||||
	ril_devinfo_query(di, DEVINFO_QUERY_SERIAL,
 | 
			
		||||
				ril_devinfo_query_serial_cb, cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean ril_devinfo_register(gpointer user_data)
 | 
			
		||||
static void ril_devinfo_query_svn(struct ofono_devinfo *info,
 | 
			
		||||
				ofono_devinfo_query_cb_t cb,
 | 
			
		||||
				void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_devinfo *di = ril_devinfo_get_data(info);
 | 
			
		||||
 | 
			
		||||
	DBG_(di, "");
 | 
			
		||||
	ril_devinfo_query(di, DEVINFO_QUERY_SVN,
 | 
			
		||||
				ril_devinfo_query_svn_cb, cb, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_devinfo_register(gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_devinfo *di = user_data;
 | 
			
		||||
 | 
			
		||||
	DBG("");
 | 
			
		||||
	di->register_id = 0;
 | 
			
		||||
	DBG_(di, "");
 | 
			
		||||
	ofono_devinfo_register(di->info);
 | 
			
		||||
 | 
			
		||||
	/* This makes the timeout a single-shot */
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ril_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
 | 
			
		||||
@@ -142,13 +173,18 @@ static int ril_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
 | 
			
		||||
	struct ril_modem *modem = data;
 | 
			
		||||
	struct ril_devinfo *di = g_new0(struct ril_devinfo, 1);
 | 
			
		||||
 | 
			
		||||
	DBG("%s %s %p", ril_modem_get_path(modem), modem->imei, di);
 | 
			
		||||
	di->log_prefix = (modem->log_prefix && modem->log_prefix[0]) ?
 | 
			
		||||
		g_strconcat(modem->log_prefix, " ", NULL) : g_strdup("");
 | 
			
		||||
 | 
			
		||||
	DBG_(di, "%s", modem->imei);
 | 
			
		||||
	GASSERT(modem->imei);
 | 
			
		||||
 | 
			
		||||
	di->q = grilio_queue_new(ril_modem_io(modem));
 | 
			
		||||
	di->info = info;
 | 
			
		||||
	di->imeisv = g_strdup(modem->imeisv);
 | 
			
		||||
	di->imei = g_strdup(modem->imei);
 | 
			
		||||
 | 
			
		||||
	di->register_id = g_idle_add(ril_devinfo_register, di);
 | 
			
		||||
	di->iq = gutil_idle_queue_new();
 | 
			
		||||
	gutil_idle_queue_add(di->iq, ril_devinfo_register, di);
 | 
			
		||||
	ofono_devinfo_set_data(info, di);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -157,19 +193,14 @@ static void ril_devinfo_remove(struct ofono_devinfo *info)
 | 
			
		||||
{
 | 
			
		||||
	struct ril_devinfo *di = ril_devinfo_get_data(info);
 | 
			
		||||
 | 
			
		||||
	DBG("%p", di);
 | 
			
		||||
	DBG_(di, "");
 | 
			
		||||
	ofono_devinfo_set_data(info, NULL);
 | 
			
		||||
 | 
			
		||||
	if (di->register_id > 0) {
 | 
			
		||||
		g_source_remove(di->register_id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (di->imei_id > 0) {
 | 
			
		||||
		g_source_remove(di->imei_id);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gutil_idle_queue_cancel_all(di->iq);
 | 
			
		||||
	gutil_idle_queue_unref(di->iq);
 | 
			
		||||
	grilio_queue_cancel_all(di->q, FALSE);
 | 
			
		||||
	grilio_queue_unref(di->q);
 | 
			
		||||
	g_free(di->log_prefix);
 | 
			
		||||
	g_free(di->imeisv);
 | 
			
		||||
	g_free(di->imei);
 | 
			
		||||
	g_free(di);
 | 
			
		||||
}
 | 
			
		||||
@@ -178,10 +209,11 @@ const struct ofono_devinfo_driver ril_devinfo_driver = {
 | 
			
		||||
	.name                   = RILMODEM_DRIVER,
 | 
			
		||||
	.probe                  = ril_devinfo_probe,
 | 
			
		||||
	.remove                 = ril_devinfo_remove,
 | 
			
		||||
	.query_manufacturer     = ril_devinfo_query_unsupported,
 | 
			
		||||
	/* query_revision won't be called if query_model is missing */
 | 
			
		||||
	.query_model            = ril_devinfo_query_unsupported,
 | 
			
		||||
	.query_revision         = ril_devinfo_query_revision,
 | 
			
		||||
	.query_serial           = ril_devinfo_query_serial
 | 
			
		||||
	.query_serial           = ril_devinfo_query_serial,
 | 
			
		||||
	.query_svn              = ril_devinfo_query_svn
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								ofono/drivers/ril/ril_devmon.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								ofono/drivers/ril/ril_devmon.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2019 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "ril_devmon.h"
 | 
			
		||||
 | 
			
		||||
struct ril_devmon_io *ril_devmon_start_io(struct ril_devmon *devmon,
 | 
			
		||||
		GRilIoChannel *channel, struct sailfish_cell_info *cell_info)
 | 
			
		||||
{
 | 
			
		||||
	return devmon ? devmon->start_io(devmon, channel, cell_info) : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ril_devmon_io_free(struct ril_devmon_io *devmon_io)
 | 
			
		||||
{
 | 
			
		||||
	if (devmon_io) {
 | 
			
		||||
		devmon_io->free(devmon_io);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ril_devmon_free(struct ril_devmon *devmon)
 | 
			
		||||
{
 | 
			
		||||
	if (devmon) {
 | 
			
		||||
		devmon->free(devmon);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 8
 | 
			
		||||
 * indent-tabs-mode: t
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										80
									
								
								ofono/drivers/ril/ril_devmon.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								ofono/drivers/ril/ril_devmon.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2019-2020 Jolla Ltd.
 | 
			
		||||
 *  Copyright (C) 2020 Open Mobile Platform LLC.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef RIL_DEVMON_H
 | 
			
		||||
#define RIL_DEVMON_H
 | 
			
		||||
 | 
			
		||||
#include "ril_cell_info.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Separate instance of ril_devmon is created for each modem.
 | 
			
		||||
 * Device monitor is started after RIL has been connected.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
struct ril_devmon_io {
 | 
			
		||||
	void (*free)(struct ril_devmon_io *devmon_io);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ril_devmon {
 | 
			
		||||
	void (*free)(struct ril_devmon *devmon);
 | 
			
		||||
	struct ril_devmon_io *(*start_io)(struct ril_devmon *devmon,
 | 
			
		||||
		GRilIoChannel *channel, struct sailfish_cell_info *cell_info);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Legacy Device Monitor uses RIL_REQUEST_SCREEN_STATE to tell
 | 
			
		||||
 * the modem when screen turns on and off.
 | 
			
		||||
 */
 | 
			
		||||
struct ril_devmon *ril_devmon_ss_new(const struct ril_slot_config *config);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This Device Monitor uses RIL_REQUEST_SEND_DEVICE_STATE to let
 | 
			
		||||
 * the modem choose the right power saving strategy. It basically
 | 
			
		||||
 * mirrors the logic of Android's DeviceStateMonitor class.
 | 
			
		||||
 */
 | 
			
		||||
struct ril_devmon *ril_devmon_ds_new(const struct ril_slot_config *config);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This Device Monitor implementation controls network state updates
 | 
			
		||||
 * by sending SET_UNSOLICITED_RESPONSE_FILTER.
 | 
			
		||||
 */
 | 
			
		||||
struct ril_devmon *ril_devmon_ur_new(const struct ril_slot_config *config);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This one selects the type based on the RIL version.
 | 
			
		||||
 */
 | 
			
		||||
struct ril_devmon *ril_devmon_auto_new(const struct ril_slot_config *config);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This one combines several methods. Takes ownership of ril_devmon objects.
 | 
			
		||||
 */
 | 
			
		||||
struct ril_devmon *ril_devmon_combine(struct ril_devmon *devmon[], guint n);
 | 
			
		||||
 | 
			
		||||
/* Utilities (NULL tolerant) */
 | 
			
		||||
struct ril_devmon_io *ril_devmon_start_io(struct ril_devmon *devmon,
 | 
			
		||||
		GRilIoChannel *channel, struct sailfish_cell_info *cell_info);
 | 
			
		||||
void ril_devmon_io_free(struct ril_devmon_io *devmon_io);
 | 
			
		||||
void ril_devmon_free(struct ril_devmon *devmon);
 | 
			
		||||
 | 
			
		||||
#endif /* RIL_CONNMAN_H */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 8
 | 
			
		||||
 * indent-tabs-mode: t
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
							
								
								
									
										92
									
								
								ofono/drivers/ril/ril_devmon_auto.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								ofono/drivers/ril/ril_devmon_auto.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  oFono - Open Source Telephony - RIL-based devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2019 Jolla Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 *  it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 *  published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is distributed in the hope that it will be useful,
 | 
			
		||||
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
			
		||||
 *  GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "ril_devmon.h"
 | 
			
		||||
 | 
			
		||||
#include <ofono/log.h>
 | 
			
		||||
 | 
			
		||||
#include <grilio_channel.h>
 | 
			
		||||
 | 
			
		||||
typedef struct ril_devmon_ds {
 | 
			
		||||
	struct ril_devmon pub;
 | 
			
		||||
	struct ril_devmon *ss;
 | 
			
		||||
	struct ril_devmon *ds;
 | 
			
		||||
} DevMon;
 | 
			
		||||
 | 
			
		||||
static inline DevMon *ril_devmon_auto_cast(struct ril_devmon *pub)
 | 
			
		||||
{
 | 
			
		||||
	return G_CAST(pub, DevMon, pub);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct ril_devmon_io *ril_devmon_auto_start_io(struct ril_devmon *devmon,
 | 
			
		||||
		GRilIoChannel *io, struct sailfish_cell_info *cell_info)
 | 
			
		||||
{
 | 
			
		||||
	DevMon *self = ril_devmon_auto_cast(devmon);
 | 
			
		||||
 | 
			
		||||
	if (!self->ss) {
 | 
			
		||||
		/* We have already chosen SEND_DEVICE_STATE method */
 | 
			
		||||
		return ril_devmon_start_io(self->ds, io, cell_info);
 | 
			
		||||
	} else if (!self->ds) {
 | 
			
		||||
		/* We have already chosen SCREEN_STATE method */
 | 
			
		||||
		return ril_devmon_start_io(self->ss, io, cell_info);
 | 
			
		||||
	} else if (io->ril_version > 14 /* Covers binder implementation */) {
 | 
			
		||||
		/* Choose SEND_DEVICE_STATE method */
 | 
			
		||||
		DBG("%s: Will use SEND_DEVICE_STATE method", io->name);
 | 
			
		||||
		ril_devmon_free(self->ss);
 | 
			
		||||
		self->ss = NULL;
 | 
			
		||||
		return ril_devmon_start_io(self->ds, io, cell_info);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Choose legacy SCREEN_STATE method */
 | 
			
		||||
		DBG("%s: Will use SCREEN_STATE method", io->name);
 | 
			
		||||
		ril_devmon_free(self->ds);
 | 
			
		||||
		self->ds = NULL;
 | 
			
		||||
		return ril_devmon_start_io(self->ss, io, cell_info);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ril_devmon_auto_free(struct ril_devmon *devmon)
 | 
			
		||||
{
 | 
			
		||||
	DevMon *self = ril_devmon_auto_cast(devmon);
 | 
			
		||||
 | 
			
		||||
	ril_devmon_free(self->ss);
 | 
			
		||||
	ril_devmon_free(self->ds);
 | 
			
		||||
	g_free(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ril_devmon *ril_devmon_auto_new(const struct ril_slot_config *config)
 | 
			
		||||
{
 | 
			
		||||
	DevMon *self = g_new0(DevMon, 1);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Allocate both implementations at startup. We need to do that
 | 
			
		||||
	 * early so that connections to D-Bus daemon and services are
 | 
			
		||||
	 * established before we drop privileges. This isn't much of
 | 
			
		||||
	 * an overhead because those implementation don't do much until
 | 
			
		||||
	 * we actually start the I/O (at which point we drop one of those).
 | 
			
		||||
	 */
 | 
			
		||||
	self->pub.free = ril_devmon_auto_free;
 | 
			
		||||
	self->pub.start_io = ril_devmon_auto_start_io;
 | 
			
		||||
	self->ss = ril_devmon_ss_new(config);
 | 
			
		||||
	self->ds = ril_devmon_ds_new(config);
 | 
			
		||||
	return &self->pub;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Local Variables:
 | 
			
		||||
 * mode: C
 | 
			
		||||
 * c-basic-offset: 8
 | 
			
		||||
 * indent-tabs-mode: t
 | 
			
		||||
 * End:
 | 
			
		||||
 */
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user