ofono-1.17.bzr6912+16.04.20160314.3/0000755000015600001650000000000012671500304016400 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/bootstrap-configure0000755000015600001650000000050212671500024022316 0ustar pbuserpbgroup00000000000000#!/bin/sh if [ -f config.status ]; then make maintainer-clean fi ./bootstrap && \ ./configure --enable-maintainer-mode \ --enable-debug \ --prefix=/usr \ --mandir=/usr/share/man \ --sysconfdir=/etc \ --localstatedir=/var \ --enable-test \ --enable-tools \ --enable-dundee \ --disable-datafiles $* ofono-1.17.bzr6912+16.04.20160314.3/doc/0000755000015600001650000000000012671500304017145 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/doc/cell-broadcast-api.txt0000644000015600001650000000412212671500024023332 0ustar pbuserpbgroup00000000000000Cell broadcast hierarchy ======================== Service org.ofono Interface org.ofono.CellBroadcast Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns properties for the cell broadcast object. See the properties section for available 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 name, variant value) This signal indicates a changed value of the given property. IncomingBroadcast(string text, uint16 topic) This signal is emitted whenever a new cell broadcast is received. The string text contains contents of the broadcast and topic contains the channel this broadcast was received on. Please note that base station name broadcasts are handled by the NetworkRegistration interface. EmergencyBroadcast(string text, dict properties) This signal is emitted whenever an ETWS cell broadcast is received. The string text contains contents of the broadcast. The dict is made up of the following entries: EmergencyType - string value, possible values include: "Earthquake", "Tsunami", "Earthquake+Tsunami", "Other" EmergencyAlert - boolean value hinting whether an extra emergency indicator should be activated (e.g. vibrate mode, emergency alert mode.) Popup - boolean value hinting whether the UI should popup a message box with the emergency information. Properties boolean Powered [readwrite] Boolean representing the power state of the cell broadcast service. If powered is False, then no Cell Broadcast information is received. string Topics [readwrite] Returns a list of topics currently subscribed to by this modem. If the list is empty, then only emergency broadcasts will ever be received. ofono-1.17.bzr6912+16.04.20160314.3/doc/phonesim-control-api.txt0000644000015600001650000000165612671500024023764 0ustar pbuserpbgroup00000000000000Phonesim control hierarchy ========================== Service org.ofono Interface org.ofono.phonesim.Manager Object path / Methods Add(string name, string address, string port) Instantiates a new modem in phonesim with the given specifications. RemoveAll() Removes all modems from phonesim. Reset() Removes all modems and instantiates all those modems listed in the configuration file. Equivalent to stopping and restarting the daemon. Examples To add a new phonesim modem on the fly do the following: - Start new phonesim instance * ./phonesim -p 12346 your_very_own.xml - Call the [interface].Add("myname", "127.0.0.1", "12346") * dbus-send --system --print-reply --dest=org.ofono / \ org.ofono.phonesim.Manager.Add string:myname string:127.0.0.1 string:12346 * dbus-send --system --print-reply --dest=org.ofono \ /myname org.ofono.Modem.SetProperty string:Powered variant:boolean:true ofono-1.17.bzr6912+16.04.20160314.3/doc/usat-certification-status.txt0000644000015600001650000003430612671500024025031 0ustar pbuserpbgroup00000000000000This document describes the status of the latest GCF test results for USIM Application Toolkit. The tests are described in 3GPP 31.124 and ETSI 102.384. All tests were performed using the test-stk-menu script and any additional test scripts on as needed basis. The modem used is an Intel Mobile Communications XMM6260 27.22.4.1 Display Text (30/30 pass, 4 not applicable) 27.22.4.1.1_1 PASS 27.22.4.1.1_2 PASS 27.22.4.1.1_3 PASS 27.22.4.1.1_4 PASS 27.22.4.1.1_5 PASS 27.22.4.1.1_6 PASS 27.22.4.1.1_7 PASS 27.22.4.1.1_8 PASS 27.22.4.1.1_9 PASS 27.22.4.1.2_1 PASS 27.22.4.1.3_1 PASS 27.22.4.1.4_1 PASS 27.22.4.1.4_2 PASS 27.22.4.1.4_3 PASS 27.22.4.1.5_1A PASS 28.22.4.1.5_1B N/A * 27.22.4.1.5_2A PASS 27.22.4.1.5_2B N/A * 27.22.4.1.5_3A PASS 27.22.4.1.5_3B N/A * 27.22.4.1.6.1_1 PASS 27.22.4.1.5_1A N/A ** 27.22.4.1.8.1_1 PASS 27.22.4.1.8.1_2 PASS 27.22.4.1.8.1_3 PASS 27.22.4.1.8.1_4 PASS 27.22.4.1.8.1_5 PASS 27.22.4.1.8.1_6 PASS 27.22.4.1.8.1_7 PASS 27.22.4.1.8.1_8 PASS 27.22.4.1.8.1_9 PASS 27.22.4.1.8.1_10 PASS 27.22.4.1.9.1_1 PASS 27.22.4.1.10.1_1 PASS * Icon always displayed ** Variable timeout not supported by the modem firmware 27.22.4.2 Get Inkey (31/31 pass, 6 not applicable) 27.22.4.2.1_1 PASS 27.22.4.2.1_2 PASS 27.22.4.2.1_3 PASS 27.22.4.2.1_4 PASS 27.22.4.2.1_5 PASS 27.22.4.2.1_6 PASS 27.22.4.2.2_1 PASS 27.22.4.2.3_1 PASS 27.22.4.2.3_2 PASS 27.22.4.2.4_1 PASS 27.22.4.2.5_1 PASS 27.22.4.2.6_1A PASS 27.22.4.2.6_1B N/A * 27.22.4.2.6_2A PASS 27.22.4.2.6_2B N/A * 27.22.4.2.6_3A PASS 27.22.4.2.6_3B N/A * 27.22.4.2.6_4A PASS 27.22.4.2.6_4B N/A * 27.22.4.2.7_1 N/A ** 27.22.4.2.8_1 N/A *** 27.22.4.2.9_1 PASS 27.22.4.2.9_2 PASS 27.22.4.2.9_3 PASS 27.22.4.2.9_4 PASS 27.22.4.2.9_5 PASS 27.22.4.2.9_6 PASS 27.22.4.2.9_7 PASS 27.22.4.2.9_8 PASS 27.22.4.2.9_9 PASS 27.22.4.2.9_10 PASS 27.22.4.2.10_1 PASS 27.22.4.2.10_2 PASS 27.22.4.2.11_1 PASS 27.22.4.2.12_1 PASS 27.22.4.2.12_2 PASS 27.22.4.2.13_1 PASS * Icon always displayed ** Help information not supported by oFono *** Variable timeout not supported by the modem firmware 27.22.4.3 Get Input (39/39 pass, 5 not applicable) 27.22.4.3.1_1 PASS 27.22.4.3.1_2 PASS 27.22.4.3.1_3 PASS 27.22.4.3.1_4 PASS 27.22.4.3.1_5 PASS 27.22.4.3.1_6 PASS 27.22.4.3.1_7 PASS 27.22.4.3.1_8 PASS 27.22.4.3.1_9 PASS 27.22.4.3.1_10 PASS 27.22.4.3.2_1 PASS 27.22.4.3.3_1 PASS 27.22.4.3.3_2 PASS 27.22.4.3.4_1 PASS 27.22.4.3.4_2 PASS 27.22.4.3.5_1 PASS 27.22.4.3.5_2 PASS 27.22.4.3.6_1A PASS 27.22.4.3.6_1B N/A * 27.22.4.3.6_2A PASS 27.22.4.3.6_2B N/A * 27.22.4.3.6_3A PASS 27.22.4.3.6_3B N/A * 27.22.4.3.6_4A PASS 27.22.4.3.6_4B N/A * 27.22.4.3.7_1 N/A ** 27.22.4.3.8_1 PASS 27.22.4.3.8_2 PASS 27.22.4.3.8_3 PASS 27.22.4.3.8_4 PASS 27.22.4.3.8_5 PASS 27.22.4.3.8_6 PASS 27.22.4.3.8_7 PASS 27.22.4.3.8_8 PASS 27.22.4.3.8_9 PASS 27.22.4.3.8_10 PASS 27.22.4.3.9_1 PASS 27.22.4.3.9_2 PASS 27.22.4.3.10_1 PASS 27.22.4.3.10_2 PASS 27.22.4.3.11_1 PASS 27.22.4.3.11_2 PASS 27.22.4.3.12_1 PASS 27.22.4.3.12_2 PASS * Icon always displayed ** Help information not supported by oFono 27.22.4.5 Play Tone (18/18 pass, 4 not applicable) 27.22.4.5.1_1 PASS 27.22.4.5.2_1 PASS 27.22.4.5.3_1A PASS 27.22.4.5.3_1B N/A * 27.22.4.5.3_2A PASS 27.22.4.5.3_2B N/A * 27.22.4.5.3_3A PASS 27.22.4.5.3_3B N/A * 27.22.4.5.3_4A PASS 27.22.4.5.3_4B N/A * 27.22.4.5.4_1 PASS 27.22.4.5.4_2 PASS 27.22.4.5.4_3 PASS 27.22.4.5.4_4 PASS 27.22.4.5.4_5 PASS 27.22.4.5.4_6 PASS 27.22.4.5.4_7 PASS 27.22.4.5.4_8 PASS 27.22.4.5.4_9 PASS 27.22.4.5.4_10 PASS 27.22.4.5.5_1 PASS 27.22.4.5.6_1 PASS * Icon always displayed 27.22.4.7 SIM Refresh (2/2 pass, 3 not applicable) 27.22.4.7.1_1 PASS * 27.22.4.7.1_2 N/A * 27.22.4.7.1_3 PASS ** 27.22.4.7.1_4 N/A * 27.22.4.7.1_6 N/A * * FDN not supported by oFono ** Modem sends wrong terminal response 27.22.4.8 Set Up Menu (18/18 pass, 4 not applicable) 27.22.4.8.1_1 PASS 27.22.4.8.1_2 PASS 27.22.4.8.2_1 N/A * 27.22.4.8.3_1 PASS 27.22.4.8.4_1A PASS 27.22.4.8.4_1B N/A ** 27.22.4.8.4_2A PASS 27.22.4.8.4_2B N/A ** 27.22.4.8.5_1 N/A *** 27.22.4.8.6_1 PASS 27.22.4.8.6_2 PASS 27.22.4.8.6_3 PASS 27.22.4.8.6_4 PASS 27.22.4.8.6_5 PASS 27.22.4.8.6_6 PASS 27.22.4.8.6_7 PASS 27.22.4.8.6_8 PASS 27.22.4.8.6_9 PASS 27.22.4.8.6_10 PASS 27.22.4.8.7_1 PASS 27.22.4.8.8_1 PASS 27.22.4.8.9_1 PASS * Help information not supported by oFono ** Icon always displayed *** Soft keys not supported by oFono 27.22.4.9 Select Item (30/30 pass, 4 not applicable) 27.22.4.9.1_1 PASS 27.22.4.9.1_2 PASS 27.22.4.9.1_3 PASS 27.22.4.9.1_4 PASS 27.22.4.9.1_5 PASS 27.22.4.9.1_6 PASS 27.22.4.9.2_1 PASS 27.22.4.9.3_1 PASS 27.22.4.9.4_1 N/A * 27.22.4.9.5_1A PASS 27.22.4.9.5_1B N/A ** 27.22.4.9.5_2A PASS 27.22.4.9.5_2B N/A ** 27.22.4.9.6_1 PASS 27.22.4.9.6_2 PASS 27.22.4.9.7_1 N/A *** 27.22.4.9.8_1 PASS 27.22.4.9.9_1 PASS 27.22.4.9.9_2 PASS 27.22.4.9.9_3 PASS 27.22.4.9.9_4 PASS 27.22.4.9.9_5 PASS 27.22.4.9.9_6 PASS 27.22.4.9.9_7 PASS 27.22.4.9.9_8 PASS 27.22.4.9.9_9 PASS 27.22.4.9.9_10 PASS 27.22.4.9.10_1 PASS 27.22.4.9.10_2 PASS 27.22.4.9.10_3 PASS 27.22.4.9.11_1 PASS 27.22.4.9.12_1 PASS 27.22.4.9.12_2 PASS 27.22.4.9.12_3 PASS * Help information not supported by oFono ** Icon always displayed *** Soft keys not supported by oFono 27.22.4.10 Send SMS (1/1 pass) 27.22.4.10.1_9 PASS 27.22.4.11 Send SS (10/10 pass, 6 not applicable) 27.22.4.11.1_1a N/A * 27.22.4.11.1_1b PASS 27.22.4.11.1_2 PASS 27.22.4.11.1_3 PASS 27.22.4.11.1_4a N/A * 27.22.4.11.1_4b PASS 27.22.4.11.1/5 PASS 27.22.4.11.1/6a N/A * 27.22.4.11.1/6b PASS 27.22.4.11.2/1a PASS ** 27.22.4.11.2/1b N/A *** 27.22.4.11.2/2a PASS ** 27.22.4.11.2/2b N/A *** 27.22.4.11.2/3a PASS ** 27.22.4.11.2/3b N/A *** 27.22.4.11.2/4 PASS ** * UE does not support A.1/63 O_FTN ** Register B, A.1/63 O_FTN not supported *** Icon always displayed 27.22.4.12 Send USSD (8/8 pass) 27.22.4.12.1_1 PASS 27.22.4.12.1_2 PASS 27.22.4.12.1_3 PASS 27.22.4.12.1_4 PASS 27.22.4.12.1_5 PASS 27.22.4.12.1_6 PASS 27.22.4.12.1_7 PASS 27.22.4.12.1_8 PASS 27.22.4.13 Set Up Call (11/11 pass, 1 not applicable) 27.22.4.13.1_1 PASS 27.22.4.13.1_2 PASS 27.22.4.13.1_4 PASS 27.22.4.13.1_5 PASS 27.22.4.13.1_6 PASS 27.22.4.13.1_7 PASS 27.22.4.13.1_8 PASS 27.22.4.13.1_9 PASS 27.22.4.13.1_10 PASS 27.22.4.13.1_11a PASS 27.22.4.13.1_11b N/A * 27.22.4.13.1_12 PASS * Subaddress not support 27.22.4.15 Provide Local Information (2/2 pass, 3 not applicable) 27.22.4.15.1_1 N/A * 27.22.4.15.1_2 N/A * 27.22.4.15.1_3 N/A *,** 27.22.4.15.1_4 PASS 27.22.4.15.1_5 PASS * Handled by modem firmware ** Invalid terminal response sent 27.22.4.22 Set Up Idle Mode Text (24/24 pass, 3 not applicable) 27.22.4.22.1_1 PASS 27.22.4.22.1_2 PASS 27.22.4.22.1_3 PASS 27.22.4.22.1_4 PASS 27.22.4.22.1_5 PASS 27.22.4.22.1_6 PASS 27.22.4.22.1_7 PASS 27.22.4.22.2_1A PASS 27.22.4.22.2_1B N/A * 27.22.4.22.2_2A PASS 27.22.4.22.2_2B N/A * 27.22.4.22.2_3A PASS 27.22.4.22.2_3B N/A * 27.22.4.22.2_4 PASS 27.22.4.22.3_1 PASS 27.22.4.22.4_1 PASS 27.22.4.22.4_2 PASS 27.22.4.22.4_3 PASS 27.22.4.22.4_4 PASS 27.22.4.22.4_5 PASS 27.22.4.22.4_6 PASS 27.22.4.22.4_7 PASS 27.22.4.22.4_8 PASS 27.22.4.22.4_9 PASS 27.22.4.22.4_10 PASS 27.22.4.22.5_1 PASS 27.22.4.22.6_1 PASS * Icon always displayed 27.22.4.24 Send DTMF (2/2 pass) 27.22.4.24.1_1 PASS 27.22.4.24.1_4 PASS 27.22.4.26 Launch Browser (6/6 pass, 1 not applicable) 27.22.4.26.1_1 PASS 27.22.4.26.1_2 PASS 27.22.4.26.1_3 PASS 27.22.4.26.1_4 N/A * 27.22.4.26.2_1 PASS 27.22.4.26.2_2 PASS 27.22.4.26.2_3 PASS * Overriding Proxy information is not supported by oFono 27.22.6 Call Control by USIM (14/16 pass, 2 fail, 1 not applicable) 27.22.6.1_1 PASS 27.22.6.1_2 PASS 27.22.6.1_3a PASS 27.22.6.1_3b PASS 27.22.6.1_4 PASS 27.22.6.1_5a N/A 27.22.6.1_5b PASS 27.22.6.1_6 PASS 27.22.6.1_7a PASS 27.22.6.1_7b PASS 27.22.6.1_8 FAIL * 27.22.6.1_9 FAIL ** 27.22.6.1_10 PASS 27.22.6.1_11 PASS 27.22.6.1_12 PASS 27.22.6.1_13 PASS 27.22.6.1_14 PASS * Not recognized as an emergency call Nov 29 22:41:50 localhost ofonod[8435]: Voice: > ATD+01234567890123456789;\r Nov 29 22:41:51 localhost ofonod[8435]: Aux: < \r\n+STKCTRLIND: 0,2,,"112",129\r\n Nov 29 22:41:51 localhost ofonod[8435]: Voice: < \r\nOK\r\n Nov 29 22:41:51 localhost ofonod[8435]: src/voicecall.c:dial_handle_result() Registering new call: 1 Nov 29 22:41:51 localhost ofonod[8435]: Voice: < \r\n+XCALLSTAT: 1,2\r\n Nov 29 22:41:51 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Got a voicecall event, status: 2, id: 1, number: called_number: , called_name Nov 29 22:41:51 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Found call with id: 1 Nov 29 22:41:51 localhost ofonod[8435]: Voice: < \r\n+XEMC:1\r\n Nov 29 22:41:52 localhost ofonod[8435]: Voice: < \r\n+XPROGRESS: 1, 1\r\n Nov 29 22:41:52 localhost ofonod[8435]: Voice: < \r\n+XCALLSTAT: 1,3\r\n Nov 29 22:41:52 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Got a voicecall event, status: 3, id: 1, number: called_number: , called_name Nov 29 22:41:52 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Found call with id: 1 Nov 29 22:41:55 localhost ofonod[8435]: Voice: < \r\n+XCOLP: 1,"112",129\r\n Nov 29 22:41:55 localhost ofonod[8435]: drivers/ifxmodem/voicecall.c:xcolp_notify() xcolp_notify: 1 112 129 Nov 29 22:41:55 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Got a voicecall event, status: 3, id: 1, number: 112 called_number: , called_name Nov 29 22:41:55 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Found call with id: 1 Nov 29 22:41:55 localhost ofonod[8435]: Voice: < \r\n+XCALLSTAT: 1,7\r\n\r\n+XCALLSTAT: 1,0\r\n Nov 29 22:41:55 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Got a voicecall event, status: 0, id: 1, number: 112 called_number: , called_name Nov 29 22:41:55 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Found call with id: 1 Nov 29 22:41:55 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Got a voicecall event, status: 0, id: 1, number: 112 called_number: , called_name Nov 29 22:41:55 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Found call with id: 1 Nov 29 22:42:02 localhost ofonod[8435]: Voice: < \r\n+XPROGRESS: 1, 11\r\n Nov 29 22:42:02 localhost ofonod[8435]: src/audio-settings.c:ofono_audio_settings_active_notify() active 0 Nov 29 22:42:02 localhost ofonod[8435]: Voice: < \r\n+XEMC:0\r\n\r\nNO CARRIER\r\n\r\n+XCALLSTAT: 1,6\r\n Nov 29 22:42:02 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_disconnected() Got disconnection event for id: 1, reason: 2 Nov 29 22:42:02 localhost ofonod[8435]: emergency mode is already deactivated!!! Nov 29 22:42:02 localhost ofonod[8435]: Call Ended on modem: 0x768340 Nov 29 22:42:02 localhost ofonod[8435]: Voice Call, Outgoing Nov 29 22:42:02 localhost ofonod[8435]: To: 112 Nov 29 22:42:02 localhost ofonod[8435]: Name from Network: Nov 29 22:42:02 localhost ofonod[8435]: StartTime: 2012-11-29T22:41:51-0600 Nov 29 22:42:02 localhost ofonod[8435]: EndTime: 2012-11-29T22:42:02-0600 ** Emergency mode should not be toggled at call end Nov 29 22:46:22 localhost ofonod[8435]: Voice: > ATD+01234567890123456789;\r Nov 29 22:46:23 localhost ofonod[8435]: Aux: < \r\n+STKCTRLIND: 0,2,,"1020",129\r\n Nov 29 22:46:23 localhost ofonod[8435]: Voice: < \r\nOK\r\n Nov 29 22:46:23 localhost ofonod[8435]: src/voicecall.c:dial_handle_result() Registering new call: 1 Nov 29 22:46:23 localhost ofonod[8435]: Voice: < \r\n+XCALLSTAT: 1,2\r\n Nov 29 22:46:23 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Got a voicecall event, status: 2, id: 1, number: called_number: , called_name Nov 29 22:46:23 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Found call with id: 1 Nov 29 22:46:24 localhost ofonod[8435]: Voice: < \r\n+XPROGRESS: 1, 1\r\n Nov 29 22:46:24 localhost ofonod[8435]: Voice: < \r\n+XCALLSTAT: 1,3\r\n Nov 29 22:46:24 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Got a voicecall event, status: 3, id: 1, number: called_number: , called_name Nov 29 22:46:24 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Found call with id: 1 Nov 29 22:46:27 localhost ofonod[8435]: Voice: < \r\n+XPROGRESS: 1, 7\r\n Nov 29 22:46:27 localhost ofonod[8435]: src/audio-settings.c:ofono_audio_settings_active_notify() active 1 Nov 29 22:46:27 localhost ofonod[8435]: Voice: < \r\n+XCOLP: 1,"1020",129\r\n Nov 29 22:46:27 localhost ofonod[8435]: drivers/ifxmodem/voicecall.c:xcolp_notify() xcolp_notify: 1 1020 129 Nov 29 22:46:27 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Got a voicecall event, status: 3, id: 1, number: 1020 called_number: , called_name Nov 29 22:46:27 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Found call with id: 1 Nov 29 22:46:27 localhost ofonod[8435]: Voice: < \r\n+XCALLSTAT: 1,7\r\n\r\n+XCALLSTAT: 1,0\r\n Nov 29 22:46:27 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Got a voicecall event, status: 0, id: 1, number: 1020 called_number: , called_name Nov 29 22:46:27 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Found call with id: 1 Nov 29 22:46:27 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Got a voicecall event, status: 0, id: 1, number: 1020 called_number: , called_name Nov 29 22:46:27 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_notify() Found call with id: 1 Nov 29 22:46:32 localhost ofonod[8435]: Voice: < \r\n+XPROGRESS: 1, 11\r\n Nov 29 22:46:32 localhost ofonod[8435]: src/audio-settings.c:ofono_audio_settings_active_notify() active 0 Nov 29 22:46:33 localhost ofonod[8435]: Voice: < \r\nNO CARRIER\r\n\r\n+XCALLSTAT: 1,6\r\n Nov 29 22:46:33 localhost ofonod[8435]: src/voicecall.c:ofono_voicecall_disconnected() Got disconnection event for id: 1, reason: 2 Nov 29 22:46:33 localhost ofonod[8435]: emergency mode is already deactivated!!! Nov 29 22:46:33 localhost ofonod[8435]: Call Ended on modem: 0x768340 Nov 29 22:46:33 localhost ofonod[8435]: Voice Call, Outgoing Nov 29 22:46:33 localhost ofonod[8435]: To: 1020 Nov 29 22:46:33 localhost ofonod[8435]: Name from Network: Nov 29 22:46:33 localhost ofonod[8435]: StartTime: 2012-11-29T22:46:23-0600 Nov 29 22:46:33 localhost ofonod[8435]: EndTime: 2012-11-29T22:46:33-0600 ofono-1.17.bzr6912+16.04.20160314.3/doc/stk-api.txt0000644000015600001650000002346712671500024021271 0ustar pbuserpbgroup00000000000000SimToolkit Hierarchy =============== Service org.ofono Interface org.ofono.SimToolkit Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns properties for the SimToolkit object. See the properties section for available properties. void SelectItem(byte item, object agent) Selects an item from the main menu, thus triggering a new user session. The agent parameter specifies a new agent to be used for the duration of the user session. Possible Errors: [service].Error.InProgress [service].Error.NotSupported [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void RegisterAgent(object path) Registers a default agent to be used for SIM initiated actions such as Display Text, Get Inkey or Get Input. These can typically occur when a special SMS is received and might not involve interaction from the user. Possible Errors: [service].Error.InProgress [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void UnregisterAgent(object path) Unregisters the default agent. If no agent is registered then unsolicited commands from the SIM are rejected. Possible Errors: [service].Error.InvalidArguments [service].Error.Failed Signals PropertyChanged(string property, variant value) Signal is emitted whenever a property has changed. The new value is passed as the signal argument. Properties string IdleModeText [readonly] Contains the text to be used when the home screen is idle. This text is set by the SIM and can change at any time. byte IdleModeIcon [readonly] Contains the identifier of the icon accompanying the idle mode text. array{struct{string, byte}} MainMenu [readonly] Contains the items that make up the main menu. This is populated by the SIM when it sends the Setup Menu Proactive Command. The main menu is always available, but its contents can be changed at any time. Each item contains the item label and icon identifier. string MainMenuTitle [readonly] Contains the title of the main menu. string MainMenuIcon [readonly] Contains the identifier of the icon for the main menu. SimToolkitAgent Hierarchy =============== Service unique name Interface org.ofono.SimToolkitAgent Object path freely definable Methods byte RequestSelection(string title, byte icon_id, array{struct(string, byte)} items, int16 default) Tells the agent to ask the user to select an item from the menu. The default is set the the default item index or -1 if no default is provided. This function should return the index of the item or an error given below. Possible Errors: [service].Error.SimToolkit.GoBack [service].Error.SimToolkit.EndSession Implementation notes: - Data / Navigation type not indicated - Soft key preferred not indicated - Help available ignored void DisplayText(string text, byte icon_id, boolean urgent) Tells the agent to display text from the SIM. The boolean urgent parameter tells the agent whether this is an urgent message and all other messages should be cleared prior to the display of this text. Possible Errors: [service].Error.SimToolkit.GoBack [service].Error.SimToolkit.EndSession [service].Error.SimToolkit.Busy Implementation notes: - High / normal priority indicated by urgent - The clear message after delay / wait for user to clear flags are handled as follows. If the wait for user flag is set, then oFono sets a higher timeout for the Agent DisplayText method call. It will then call Cancel() on the agent after this timeout expires and the "No response from user" terminal response is sent. If the agent returns earlier from this method call, a terminal response "Command "performed successfully" is sent. It might be required to pass a flag to the UI to hint it that allocation of an 'acknowledgement' button is required in the case of a longer timeout. However, the UI can figure this out itself as well based on a timer threshold. More input needed. - Immediate Response indication is handled internally, terminal response is sent immediately and no other indication is given to the user / agent. Response from this method call is ignored and a short timeout is used for the method call. Once another proactive command arrives, and the DisplayText is still pending, Cancel() is called. string RequestInput(string alpha, byte icon_id, string default, byte min, byte max, boolean hide_typing) Tells the agent to request an input string from the user. The alpha parameter and icon_id gives context to the user. The default string contains the suggested default by the SIM. The min and max parameters contain how many characters the user should enter. The parameter hide_typing indicates whether user's typing should be opaque. Possible Errors: [service].Error.SimToolkit.GoBack [service].Error.SimToolkit.EndSession Implementation notes: - It is still unclear how to handle gsm vs ucs2 accepted alphabet selection. Can the reduced character set functionality from SMS be applied here? If not, then an extra gsm vs ucs2 acceptance argument needs to be added. string RequestDigits(string alpha, byte icon_id, string default, byte min, byte max, boolean hide_typing) Same as GetInput but only digit characters (0-9, *#+) are expected. Possible Errors: [service].Error.SimToolkit.GoBack [service].Error.SimToolkit.EndSession string RequestKey(string alpha, byte icon_id) Tells the agent to request a single input key from the user. The alpha parameter contains the context for the request. Possible Errors: [service].Error.SimToolkit.GoBack [service].Error.SimToolkit.EndSession string RequestDigit(string alpha, byte icon_id) Same as above, but only digits (0-9, *#+) are expected. Possible Errors: [service].Error.SimToolkit.GoBack [service].Error.SimToolkit.EndSession string RequestQuickDigit(string alpha, byte icon_id) Same as above but the entered digit shall not be displayed and the response shall be sent immediately after the key press. "+" is not allowed for user input. Possible Errors: [service].Error.SimToolkit.GoBack [service].Error.SimToolkit.EndSession boolean RequestConfirmation(string alpha, byte icon_id) Asks the agent to get confirmation from the user. Possible Errors: [service].Error.SimToolkit.GoBack [service].Error.SimToolkit.EndSession boolean ConfirmCallSetup(string information, byte icon_id) Asks the agent to request user to confirm an outgoing call setup. If confirmed, the next new outgoing call reported by VoiceCallManager will have the Information and Icon properties set to inform the user. Hanging up before the call is connected will cause EndSession reply to be sent. Possible Errors: [service].Error.SimToolkit.EndSession void PlayTone(string tone, string text, byte icon_id) Tells the agent to play an audio tone once. The method should return once the tone is finished playing. The text parameter contains an optional text to be displayed to the user. The following tones are defined: "dial-tone" "busy" "congestion" "radio-path-acknowledge" "radio-path-not-available" "error" "call-waiting" "ringing-tone" "general-beep" "positive-acknowledgement" "negative-acknowledgement" "user-ringing-tone" "user-sms-alert" "critical" (high priority) "vibrate" "happy" "sad" "urgent-action" "question" "message-received" Possible Errors: [service].Error.SimToolkit.EndSession void LoopTone(string tone, string text, byte icon_id) Tells the agent to reproduce an audio tone in a loop until the method call is cancelled. See PlayTone() above for the list of possible tone names. The text parameter contains an optional text to be displayed to the user. Possible Errors: [service].Error.SimToolkit.EndSession void DisplayActionInformation(string text, byte icon_id) Supplies a text string and/or icon concerning the current activity in the terminal and UICC. The text should be displayed to the user on screen until the call is canceled using Cancel(). If the method returns it is assumed that the user has explicitly dismissed the dialog and no Cancel() is sent. boolean ConfirmLaunchBrowser(string information, byte icon_id, string url) Asks the agent to request user to confirm launch browser. If confirmed, then the agent should send confirmation message to oFono and then should open the launch browser with the given url. void DisplayAction(string text, byte icon_id) Supplies a text string and/or icon concerning the current activity in the terminal and UICC. The text should be displayed to the user on screen until the call is canceled using Cancel() or until the user decides to end the session. Possible Errors: [service].Error.SimToolkit.EndSession boolean ConfirmOpenChannel(string information, byte icon_id) [experimental] Asks the agent to request user to confirm the channel set-up. Possible Errors: [service].Error.SimToolkit.EndSession void Cancel() [noreply] Asks the agent to cancel any ongoing operation in progress. This is usually either because the agent is taking too long to respond, the Sim Application has terminated the session or a task has finished. void Release() [noreply] Agent is being released, possibly because of oFono terminating, SimToolkit interface torn down or modem off. If the agent is registered as a global agent, no UnregisterAgent call is expected. ofono-1.17.bzr6912+16.04.20160314.3/doc/call-settings-api.txt0000644000015600001650000001027412671500024023231 0ustar pbuserpbgroup00000000000000Call Settings hierarchy =============== Service org.ofono Interface org.ofono.CallSettings Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Contains the properties for this object. Possible Errors: [service].Error.InProgress void SetProperty(string property, variant value) Sets the given property value to that specified in call parameter. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed Signals PropertyChanged(string property, variant value) Signal is emitted whenever a property has changed. The new value is passed as the signal argument. Properties string CallingLinePresentation [readonly] Contains the value of the calling line identification presentation property. The value indicates the state of the CLIP supplementary service in the network. If enabled, the network will provide the number of the calling party for incoming calls. Possible values are: "disabled", "enabled", "unknown" string CalledLinePresentation [readonly] Contains the value of the called line identification presentation property. The value indicates the state of the CDIP supplementary service in the network. If enabled, when receiving a call the network will provide the subscriber's line dialed. This is useful for subscribers which have a multiple line service with their network provider and would like to know what line the call is coming in on. Possible values are: "disabled", "enabled", "unknown" string CallingNamePresentation [readonly] Contains the value of the calling name identification presentation property. The value indicates the state of the CNAP supplementary service in the network. If enabled, the network will provide the name of the calling party for incoming calls. Possible values are: "disabled", "enabled", "unknown" string ConnectedLinePresentation [readonly] Contains the value of the connected line identification presentation property. The value indicates the state of the COLP supplementary service in the network. If enabled, the network will attempt to provide the number of the connected party for outgoing calls. Possible values are: "disabled", "enabled", "unknown" string ConnectedLineRestriction [readonly] Contains the value of the connected line identification restriction property. The value indicates the state of the COLR supplementary service in the network. If enabled, the network will withhold subscriber number information from the calling party on incoming calls. The possible values are: "disabled", "enabled", "unknown" Not all modems can report this information. string CallingLineRestriction [readonly] Contains the value of the calling line identification restriction property. The value indicates the state of the CLIR supplementary service in the network. If enabled permanently or temporarily the restriction is in effect, the subscriber number information will be withheld from the called party on outgoing calls unless the value is overriden using the HideCallerId property or on a per call basis. The possible values are: "disabled" "permanent" "unknown", "on", "off" string HideCallerId [readwrite] Sets whether the ID of the caller will should be provided or withheld for outgoing calls. This setting is also modified by the status of the CLIR supplementary service in the network (see the CallingLineRestriction property). The three possible values are: "default" - Uses the network setting "enabled" - CLIR invoked, caller id is withheld "disabled" - CLIR suppressed, caller id is provided This setting can also be changed on a per-call basis, see the VoiceCallManager Dial method documentation. string VoiceCallWaiting [readwrite] Contains the call waiting status for Voice calls. If enabled, the call waiting status will be presented to the subscriber for voice calls. Possible values are: "disabled", "enabled", ofono-1.17.bzr6912+16.04.20160314.3/doc/siri-api.txt0000644000015600001650000000207312671500024021424 0ustar pbuserpbgroup00000000000000Siri hierarchy [experimental] ======================== Service org.ofono Interface org.ofono.Siri Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns all Siri properties. See the properties section for available properties. Possible Errors: [service].Error.InProgress [service].Error.Failed void SetProperty(string name, variant value) Changes the value of the specified property. Only properties that are listed as read-write are changeable. On success a PropertyChanged signal will be emitted. Possible Errors: [service].Error.InvalidArguments [service].Error.InProgress [service].Error.Failed Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. Properties boolean Enabled [readonly] This property indicates whether Siri is available on the device. string EyesFreeMode [readwrite] This property indicates the state of Siri Eyes Free Mode. The current possible values are: "enabled" and "disabled" ofono-1.17.bzr6912+16.04.20160314.3/doc/features.txt0000644000015600001650000010216512671500024021530 0ustar pbuserpbgroup00000000000000oFono - Open Source Telephony ***************************** Purpose ======= The purpose of this document is to enumerate all major functionality areas of oFono. In effect, this document will serve as the primary, up to date source of oFono feature information. It is intended for developers, managers and users alike to quickly gauge the progress of the project and feature availability. Sim Toolkit =========== Supported Proactive Commands: - Display Text proactive command support. Upon receiving the proactive command notification oFono decodes it, including performing character conversion from packed/unpacked GSM 7bit and UCS2 to UTF-8 encoded text. The registered agent is then called using the DisplayText method on the SimToolkitAgent interface to handle the user interaction aspects. SIM-specified duration are handled. If immediate response to the SIM is required, oFono sends a terminal response immediately. DisplayText method is still executed normally, until a timeout occurs or a new proactive command arrives from the SIM. - Get Inkey proactive command support. When this command is received, oFono decodes it and checks what "flavor" it is. Depending on this, the SimToolkitAgent is called with a different method call: * If the Get Inkey flavor indicates that this is a simple Yes/No confirmation, then the RequestConfirmation method is called. * If the Get Inkey flavor indicates that the SIM only expects digits, then the RequestDigit method is called. * Otherwise the RequestKey method is called SIM specified durations are handled, if the user agent does not respond in the time allowed, then the 'No Response' Terminal Response is generated automatically. - Get Input proactive command support. When this command is received, oFono decodes it and checks what "flavor" it is. Depending on this, the SimToolkitAgent is called with a different method call: * If the Get Input flavor indicates that the SIM only expects digits, then the RequestDigits method is called. * Otherwise the RequestInput method is called - More Time proactive command support. This command is intended to notify that the SIM is still busy processing a command. For oFono, this proactive command is a no-op. It is always replied to successfully. - Setup Menu proactive command support. When this command is received, oFono parses the new main menu structure and updates its MainMenu and MainMenuTitle properties which reflect the items, icons and the alpha text of the proactive command. Soft key support and menu help system are ignored by oFono. - Select Item proactive command support. When this command is received, oFono decodes it and dispatches it to the SimToolkitAgent by calling the RequestSelection method. This method is passed the menu selection title, the selectable items and the default, if any. - Timer Management proactive command support. oFono supports starting, stopping and querying timer state flavors of this command. Up to eight timers are supported. This proactive command is handled completely inside oFono and no external user interaction is required. - Set Up Idle Mode Text proactive command support. Whenever oFono receives this proactive command, it updates the IdleText property on the main SimToolkit interface. Indications that this property has changed are handled by the usual means. - Send DTMF proactive command. Whenever oFono receives the Send DTMF command, it checks that there are calls in progress and DTMF is possible. If so, DTMF characters are passed to the voicecall atom to be transmitted to the modem. The appropriate terminal response is sent to the SIM once the DTMF tones have been played or the call has been disconnected. NOTE: This command can also be handled by the modem. - Play Tone proactive command. Whenever oFono receives a Play Tone proactive command it checks whether the tone is to be continuous/looped or played once. It then calls the SimToolkitAgent PlayTone or LoopTone method as appropriate. The sound that will be played will be determined based on the sound type that is passed to the agent. It is up to the system integrator to provide the appropriate sounds. - Send USSD proactive command. Whenever oFono receives a Send USSD proactive command it checks whether there are any USSD / SS operations in progress. If an operation is in progress, the appropriate terminal response is sent without performing the Send USSD operation. Otherwise the USSD string is sent to the network, and the response is sent back to the SIM in the terminal response. NOTE: This command can also be handled by the modem. - Language Notification proactive command. Whenever oFono receives a Language Notification proactive command, it prints the language code and sends terminal response to the SIM immediately. - Provide Local Information proactive command. Whenever oFono receives a Provide Local Information proactive command, it checks the type of the information requested. If the information is about the time/date or the language of the terminal, it responds to the command with the appropriate terminal response. The time/date information is obtained using localtime(). The language information is obtained by checking the LANG environment variable. All other information requests are expected to be handled by the modem. - Send Short Message proactive command. Whenever oFono receives a Send SMS proactive command, it parses the PDU and submits it to the outgoing SMS queue. A terminal response is sent to the SIM When the raw PDU has been sent, or failed to be sent. NOTE: This command can also be handled by the modem. - Set Up Call proactive command. When oFono receives a Set Up Call proactive command, it checks whether the UICC has indicated that the user should be informed. In this case the SimToolkitAgent is called with the ConfirmCallSetup method. If the user has authorized the operation, or if the user's confirmation was not indicated oFono will setup the call and optionally inform the user. The information sent by the SIM will be available on the resulting voice call object. The relevant properties are Information and Icon. NOTE: This command can also be handled by the modem. - Refresh proactive command. The specification defines 7 types of Refresh requests: - NAA Initialization - NAA File Change Notification - NAA Initialization and File Change Notification - NAA Initialization and Full File Change Notification - UICC Reset - NAA Application Reset (2G only) - NAA Session Reset (3G only) oFono can fully perform the the first four types of Refresh. The remaining three must be handled by the modem or its driver with a notification sent to ofono. Regardless of whether the command is handled by the modem or not, oFono will check whether there are any calls or ussd operations active. If there are, the appropriate response will be sent (e.g. busy on call or screen busy terminal response.) Otherwise a positive response will be sent to the driver. In the case of a 'UICC Reset' the driver / modem can interpret this that it is safe to reset the UICC. Alternatively, the driver / modem can notify the core of the SIM removal / SIM insertion events without using the Refresh proactive command. It is up to the driver / modem to perform a warm reset. In particular, 3GPP 31.111 mandates that any change to EFimsi is done by using 'UICC Reset', 'NAA Application Reset' or 'NAA Session Reset'. Please see 3GPP 31.111 Section 6.4.7.1. Other types are handled by oFono flushing the EF cache of the files affected (or the entire SIM cache in case of Full File Change Notifications) and re-reading the affected files. Any properties derived from these Elementary Files will be updated and signaled using PropertyChanged. NOTE: This command can also be handled by the modem. - Sim icon support. oFono supports icons that are stored on the SIM. If the SIM notifies oFono that an icon is available for a particular proactive command, oFono passes this information to the UI. The UI is able to obtain the icons by using GetIcon method on the SimManager interface. The icons are read from the SIM and converted into XPM format. - Text attribute support. Whenever oFono detects that text attributes have been given to any text or alpha string, it applies them and converts the resulting text to HTML. The UI is expected to be able to display such HTML formatted text. Envelopes: - Timer Expiration envelope support. Whenever a timer expires (as started by the Timer Management proactive command) oFono forwards, this envelope type to the SIM. No external user interaction is required. - Menu Selection envelope support. The user can initiate a proactive command session by selecting something from the Sim Toolkit main menu. This is done by using the SimToolkit's SelectItem method. As a result, oFono will send the Menu Selection envelope to the SIM. - CBS-PP Download envelope support. When oFono receives a cell broadcast and on a channel listed in EFcbmid, it is dispatched to the SIM using the CBS-PP Download envelope. No user interaction is required or signaled whenever this occurs. - SMS-PP Download envelope support. When oFono receives an sms message addressed to the SIM, it is dispatched to the SIM using the SMS-PP Download envelope. No user interaction is required or signaled whenever this occurs. Please note that many current modems do not support returning RP-ACK and RP-ERROR acknowledgement PDUs back to the network. This is required by the CAT specification for SMS-PP Download. E.g. the sim response to the SMS-PP Download Envelope is to be stored in an RP-ACK / RP-ERROR PDU and returned to the network. It is thus anticipated that modems will transparently handle this feature in the firmware. The following commands are expected to be handled by the modem: - Send SS proactive command. oFono does not explicitly support this proactive command since AT modems do not provide the low level information necessary for oFono to generate a valid response. The modem (or driver) shall handle this command. Optionally the modem (or driver) can inform oFono that the proactive command has been received and is being handled by the modem, as well as when the terminal response has been sent by the modem. oFono will display the necessary user information for this time period if this information is included in the proactive command. ----------------------------------------------------------------------- | Feature | Support | Implemented by | | | | | ----------------------------------------------------------------------- |Profile Download | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |SMS-PP Data Download | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |Cell Broadcast data Download | YES | BASEBAND or ME | | | | | ----------------------------------------------------------------------- |CALL CONTROL BY SIM | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |DISPLAY TEXT | YES | ME | | | | | ----------------------------------------------------------------------- |GET INPUT | YES | ME | | | | | ----------------------------------------------------------------------- |GET INKEY | YES | ME | | | | | ----------------------------------------------------------------------- |MORE TIME | YES | ME | | | | | ----------------------------------------------------------------------- |PLAY TONE | YES | ME | | | | | ----------------------------------------------------------------------- |POLL INTERVAL | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |POLLING OFF | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |REFRESH | YES | BASEBAND-ME | | | | | ----------------------------------------------------------------------- |SELECT ITEM | YES | ME | | | | | ----------------------------------------------------------------------- |SEND SHORT MESSAGE | YES | BASEBAND-ME | | | | | ----------------------------------------------------------------------- |SEND SS | YES | BASEBAND-ME | | | | | ----------------------------------------------------------------------- |SEND USSD | YES | BASEBAND-ME | | | | | ----------------------------------------------------------------------- |SET UP CALL | YES | BASEBAND-ME | | | | | ----------------------------------------------------------------------- |SET UP MENU | YES | ME | | | | | ----------------------------------------------------------------------- |PROVIDE LOCAL INFORMATION | YES | BASEBAND | |(MCC, MNC, LAC, cellId & | | | |IMEI) | | | | | | | ----------------------------------------------------------------------- |PROVIDE LOCAL INFORMATION | YES | BASEBAND | |(NMR) | | | | | | | ----------------------------------------------------------------------- |SET UP EVENT LIST | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |EVENT: MT CALL | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |EVENT: CALL CONNECTED | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |EVENT: CALL DISCONNECTED | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |EVENT: LOCATION STATUS | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |EVENT: USER ACTIVITY | NO | | | | | | ----------------------------------------------------------------------- |EVENT: IDLE SCREEN AVAILABLE | NO | | | | | | ----------------------------------------------------------------------- |EVENT: CARD READER STATUS | NO | | | | | | ----------------------------------------------------------------------- |EVENT: LANGUAGE SELECTION | NO | | | | | | ----------------------------------------------------------------------- |EVENT: BROWSER TERMINATION | NO | | | | | | ----------------------------------------------------------------------- |EVENT: DATA AVAILABLE | NO | | | | | | ----------------------------------------------------------------------- |EVENT: CHANNEL STATUS | NO | | | | | | ----------------------------------------------------------------------- |EVENT: ACCESS TECHNOLOGY | YES | BASEBAND | | CHANGE | | | ----------------------------------------------------------------------- |EVENT: DISPLAY PARAMETERS | NO | | | CHANGED | | | | | | | ----------------------------------------------------------------------- |EVENT: LOCAL CONNECTION | NO | | | | | | ----------------------------------------------------------------------- |EVENT: NETWORK SEARCH MODE | YES | BASEBAND | | CHANGE | | | | | | | ----------------------------------------------------------------------- |POWER ON CARD | NO | | | | | | ----------------------------------------------------------------------- |POWER OFF CARD | NO | | | | | | ----------------------------------------------------------------------- |PERFORM CARD APDU | NO | | | | | | ----------------------------------------------------------------------- |GET READER STATUS | NO | | |(Card reader status) | | | | | | | ----------------------------------------------------------------------- |GET READER STATUS | NO | | |(Card reader identifier) | | | | | | | ----------------------------------------------------------------------- |TIMER MANAGEMENT | YES | BASEBAND-ME | | | | | ----------------------------------------------------------------------- |PROVIDE LOCAL INFORMATION | YES | ME | |(Date, Time, & Time Zone) | | | | | | | ----------------------------------------------------------------------- |SET UP IDLE MODE TEXT | YES | ME | | | | | ----------------------------------------------------------------------- |RUN AT COMMAND | YES | BASEBAND | | | | | ----------------------------------------------------------------------- |SEND DTMF | YES | BASEBAND-ME | | | | | ----------------------------------------------------------------------- |PROVIDE LOCAL INFORMATION | YES | ME | |(Language) | | | | | | | ----------------------------------------------------------------------- |PROVIDE LOCAL INFORMATION | YES | BASEBAND | |(Timing Advance) | | | | | | | ----------------------------------------------------------------------- |Language Notification | YES | ME | | | | | ----------------------------------------------------------------------- |LAUNCH BROWSER | YES | ME | | | (MIN) | | ----------------------------------------------------------------------- |PROVIDE LOCAL INFORMATION | YES | BASEBAND | |(ACCESS TECHNOLOGY) | | | | | | | ----------------------------------------------------------------------- |OPEN CHANNEL | NO | | | | | | ----------------------------------------------------------------------- |CLOSE CHANNEL | NO | | | | | | ----------------------------------------------------------------------- |RECEIVE DATA | NO | | | | | | ----------------------------------------------------------------------- |SEND DATA | NO | | | | | | ----------------------------------------------------------------------- |GET CHANNEL STATUS | NO | | | | | | ----------------------------------------------------------------------- |SERVICE SEARCH | NO | | | | | | ----------------------------------------------------------------------- |GET SERVICE INFORMATION | NO | | | | | | ----------------------------------------------------------------------- |DECLARE SERVICE | NO | | | | | | ----------------------------------------------------------------------- |Text Attributes | YES | ME | | | | | ----------------------------------------------------------------------- |ICONS | YES | ME | | | | | ----------------------------------------------------------------------- |Bearer Independent Protocol | NO | | | | | | ----------------------------------------------------------------------- |VARIABLE TIMEOUT | YES | ME | | | | | ----------------------------------------------------------------------- |PROVIDE LOCAL INFORMATION | YES | BASEBAND | |(IMEISV) | | | | | | | ----------------------------------------------------------------------- |PROVIDE LOCAL INFORMATION | YES | BASEBAND | |(SEARCH MODE CHANGE) | | | | | | | ----------------------------------------------------------------------- |Extended Launch Browser | NO | | |Capability | | | | | | | ----------------------------------------------------------------------- |PROVIDE LOCAL INFORMATION | YES | BASEBAND | |(battery state) | | | | | | | ----------------------------------------------------------------------- |RETRIEVE MULTIMEDIA MESSAGE | NO | | | | | | ----------------------------------------------------------------------- |SUBMIT MULTIMEDIA MESSAGE | NO | | | | | | ----------------------------------------------------------------------- |DISPLAY MULTIMEDIA MESSAGE | NO | | | | | | ----------------------------------------------------------------------- |SET FRAMES | NO | | | | | | ----------------------------------------------------------------------- |GET FRAMES STATUS | NO | | | | | | ----------------------------------------------------------------------- |PROVIDE LOCAL INFORMATION | YES | BASEBAND | |(NMR(UTRAN)) | | | | | | | ----------------------------------------------------------------------- |USSD Data Download and | NO | | |application mode | | | | | | | ----------------------------------------------------------------------- |EVENT: BROWSING STATUS | NO | | | | | | ----------------------------------------------------------------------- |EVENT: MMS TRANSFER STATUS | NO | | | | | | ----------------------------------------------------------------------- Modem ===== - 'silent' modem reset. Sometimes modems get in a bad state and have to reset itself. Now oFono can be notified when a reset happens and do proper work to put the modem back to work restoring the state before the reset. - Lockdown support. Through the Lockdown property on the Modem D-Bus API, one can power down the modem and lock it disallowing any other application to use that modem. Useful for firmware update and similar stuff. Short Messaging Service ======================= - Unique identifier for incoming and outgoing SMS messages. Whenever a message is received or sent by oFono, a unique ID is generated for that message. The id is relevant to all segments of an SMS and in the case of incoming messages is only generated when the SMS is fully assembled. The hash used is SHA1. This unique identifier is used to identify the SMS message to history plugins as well. - SMS Status Report support. oFono allows requesting of SMS Status Reports via the MessageManager UseDeliveryReports property. If enabled, oFono will set the SRR bit and process incoming status reports. oFono takes care of collating the status reports for multi-fragment messages. Once all status reports are received, the UI is notified either via DBus or history plugin API. - Source / Destination port addressing scheme from 3GPP 23.040. A special header is used to indicate the source / destination port of the application this SMS message belongs to. oFono provides a handler registration framework where plugins can handle the reception of such messages. The handler can be registered to receive messages which contain a specific source and destination port, or a wildcard. When such messages are received, they are matched against the registered handlers and dispatched appropriately. oFono takes care of de-fragmentation of such SMS messages, so the handler is informed only once the entire message has been received, and the data payload has been extracted. - Smart Messaging Service - vCard support. oFono provides the ability to send and receive vCard objects through the SmartMessaging interface. vCards can be sent using the SendBusinessCard method and received using the SmartMessagingAgent framework. - Smart Messaging Service - vCalendar support. oFono provides the ability to send and receive vCalendar objects through the SmartMessaging interface. vCalendars can be sent using the SendAppointment method and received using the SmartMessagingAgent framework. - WAP PUSH notification support. oFono allows the reception of WAP PUSH messages via SMS through the use of the PushNotification interface and the PushNotificationAgent framework. - Persisting of outgoing SMS messages. Whenever oFono is asked to send an SMS message, it adds it to an internal queue and persists it on disk. The queue is persistent across reboots of oFono and allows to queue SMS messages even while the modem is offline. GPRS ==== - GPRS suspension event support. The packet data service may be temporarily suspended while a circuit switched service such as voice call or SMS is active. - GPRS context protocol support. oFono supports the followig types of GPRS contexts: - IPv4 - IPv6 - Dual Stack (IPv4 + IPv6) - GPRS provisioning support. oFono will automatically configure GPRS contexts if no prior configuration (e.g. user or provisioning) has been detected. If the GPRS atom detects that provisioning is required it will query the inserted sim card for the Service Provider Name and call each registered provisioning plugin with the MCC, MNC, SPN of the inserted SIM card. If the provisioning information is provided, then it is used to populate the initial context configuration. - GPRS provisioning via 'Mobile Broadband Provider Info'. oFono supports provisioning of GPRS contexts via Mobile Broadband Provider Info project's database. Location Reporting ================== - GPS support. Many modem manufacturers provide a GPS unit with their modem hardware. Upon client request oFono can turn this unit on or off and pass a file descriptor in which client may receive the desired location reporting data. SIM === - Fixed Dialing support. oFono reads the necessary bits from the SIM to check if FDN support is allocated and enabled in the SIM. If enabled, oFono halts the SIM initialization procedure and the modem remains in the PRESIM state. In this state oFono will only allow emergency calls. - Barred Dialing support. oFono reads the necessary bits from the SIM to check if BDN support is allocated and enabled in the SIM. If enabled, oFono halts the SIM initialization procedure and the modem remains in the PRESIM state. In this state oFono will only allow emergency calls. - Read / Write EFcfis / EFcphs-cff. oFono reads EFcfis/EFcphs-cff SIM files to check if VoiceUnconditional call forwarding rule is enabled. If enabled, ForwardingFlagOnSim will be set and VoiceUnconditional may contain the "forwarded to" number if the number is available. - Support SIM retry counters. oFono exports all the retry counters available on SIM, e.g., PIN, PIN2, PUK and PUK2. Whenever an action changes them, a signal is sent with the updated values, so user can keep track of how many times he/she can still give a wrong pin before the SIM locking down. Radio settings ============== - Fast dormancy support. A fast dormancy feature can be enabled in the cellular modem to conserve power when the end user is not actively using the device but some networking applications are online using packet data. - Frequency Band Selection support. This feature allows the user to limit the frequency bands in which the cellular modem can operate. Text Telephony ============== - TTY (hearing impaired) support, also known as Cellular Text Modem (CTM). In case it's supported by the modem, oFono allows the user to enabled or disable it through the TextTelephony interface. Emergency Calls =============== - Emergency number reporting. During SIM initialization phase oFono reads EFecc in order to bootstrap the emergency calling codes provided by the SIM. Emergency number list is exposed via the EmergencyNumbers property on the VoicecallManager interface. If SIM is present, list is the union of default emergency numbers(112, 911), numbers in EFecc and Network / Modem reported emergency numbers. If SIM is not present or EFecc has not been read yet, list is the union of default emergency numbers(112, 911) and emergency numbers without SIM(119, 118, 999, 110, 08 and 000). - Emergency call reporting. When a voicecall is made to a number present on the EmergencyNumbers list it is automatically flagged as an emergency call. This is done by setting the Emergency property on the Voicecall interface to TRUE. - Emergency Mode. oFono supports a concept of an 'Emergency Mode'. This mode is activated when any emergency procedure is ongoing and restricts certain actions (e.g. setting the modem offline). Emergency procedures are e.g. ongoing emergency calls, or network initiated location requests related to the emergency call. The state of the emergency mode is exposed via the Emergency property on the org.ofono.Modem interface. This property is intended to be used by power management daemons and other entities which need to be aware of the ongoing emergency operation. Supplementary Services ====================== - CNAP support. The Calling Name Presentation supplementary service is supported by oFono. One can query whether the service is provisioned in the network by using the CallSettings interface. If the network reports the calling name, it is presented on the Voicecall interface using the Name property. If no calling name is given, the Name is empty. - CDIP support. The Called Line Presentation is supported by oFono. One can query whether the service is provisioned in the network by using the CallSettings interface. If the network supports this service, the number dialed by the remote party is presented through the Voicecall interface using the IncomingLine property. Voice Calls =========== - Long phone number support. oFono supports dialing of phone numbers up to 80 digits long. - Supplementary service notifications related to mobile originated (MO) and mobile terminated (MT) calls. oFono supports certain supplementary service notifications, typically reported by CSSU and CSSI, that are related to MT/MO calls: - outgoing call has been forwarded (+CSSI: 2) - outgoing calls are barred (+CSSI: 5) - outgoing call barred due to call barring on remote party (+CSSI: 6) - incoming call is a forwarded call (+CSSU: 0) - call has been put on hold by the remote party (+CSSU: 2) - call has been retrieved by the remote party (+CSSU: 3) - call has been added to a mpty call by the remote party (+CSSU: 4) Flight Mode =========== - Flight Mode support. oFono uses the 'Online' property on the Modem interface that controls the state of the radio. When Online state is False, all interfaces that depend on the radio being on are removed. Other interfaces enter reduced functionality mode where only certain actions are available that do not depend on the radio. E.g. setting various settings on the local store or the SIM. Network Registration ==================== - Support for PLMN_MODE bit from CPHS Customer Service Profile (CSP) within the 'Value Added Services' service group. oFono reads this file when the network registration atom has been initialized. If EFcsp indicates that manual network registration is not allowed, oFono enters into 'auto-only' registration mode. Updates to this file via STK Refresh is also supported. - Support for 3GPP Service Provider Name (EFspn), CPHS Operator Name String (ONS) and CPHS Short Operator Name String fields. oFono always tries to read the EFspn field first. If this is not available, then oFono tries to read the CPHS variant. If neither are available and the appropriate bits are set in the CPHS Information field, oFono tries to read the CPHS Short ONS field. oFono then reports the network name via the 'Name' property. PPP Stack ========= - Support for Protocol Field Compression (PFC) packets. The oFono PPP stack supports both receiving and sending of packets with PFC enabled. The user can also control whether PFC capability is advertised to the peer, and used during transmission. - Support for Address & Control Field Compression (ACFC) packets. The oFono PPP stack supports both receiving and sending of packets with ACFC enabled. The user can also control whether ACFC capability is advertised to the peer, and used during transmission. Modem Emulator ============== - Support for Bluetooth HandsFree Profile Audio Gateway (HFP AG). oFono supports the underlying AT command protocol specified by BT HFP version 1.6. Supported features include 3-way calling, ability to reject a call, enhanced call status, enhanced call control, report of extended error results code and indicator activation. Audio management is assumed to be performed in another system component, e.g. PulseAudio. - Support for Bluetooth DUN profile. oFono supports the Dial Up Networking profile and all mandatory commands specified by BT DUN 1.1. For a list of supported commands please see doc/dialup-command-set.txt. CDMA Connection Manager ======================= - Support Network Initiated disconnection of Packet Data Service over CDMA (1xRTT and 1xEV-DO) systems. CDMA Network Acquisition ======================== - Support reporting of the received signal strength indicator (RSSI) measurement for the currently acquired CDMA network. - Support reporting of the received signal strength indicator (RSSI) measurement for the currently acquired 1xEV-DO data network. Bluetooth Sim Access Profile ============================ - oFono supports certain modems that can utilize remote sim cards (e.g. via SAP). This is done transparently to the user and all of oFono's APIs are supported on such devices (assuming the device itself supports the required features). Today the Telit UC864-G is supported in this mode. Bluetooth Handsfree Profile =========================== - Voicecall support. oFono supports the use of Bluetooth Handsfree capable devices to make voicecalls. All features are supported, including 3-way calls, multiparty calls, call waiting, etc. - Support for Handsfree specific protocol features. oFono clients can gain access to Bluetooth HFP specific features via the oFono Handsfree interface. These features include voice recognition activation, last number redial, etc. Apple Siri ========== - Siri feature. oFono can query availability of Siri on an iOS device. oFono uses 'EyesFreeMode' property on the Siri interface to control the state of the Siri Eyes Free Mode. When EyesFreeMode state is enabled, the iOS device’s screen won’t light up during a voice recognition session and an incoming call. ofono-1.17.bzr6912+16.04.20160314.3/doc/connman-api.txt0000644000015600001650000002134712671500024022114 0ustar pbuserpbgroup00000000000000Connection Manager hierarchy ================= Service org.ofono Interface org.ofono.ConnectionManager Object path [variable] Methods dict GetProperties() Returns all global system properties. See the properties section for available properties. void SetProperty(string property, variant value) Sets the property to a desired value Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.Failed void DeactivateAll() Deactivates all active contexts. Possible Errors: [service].Error.InProgress [service].Error.InvalidArguments [service].Error.Failed array{object,dict} GetContexts() Get array of context objects and properties. The method should only be call once per application. Further changes shall be monitored via ContextAdded ContextRemoved signals. object AddContext(string type) Creates a new Primary context. The type contains the intended purpose of the context. For possible values of the type parameter see the Type documentation of ConnectionContext interface. Returns the object path of the created context. Possible Errors: [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void RemoveContext(object context) Removes a primary context. All secondary contexts, if any, associated with the primary context are also removed. Possible Errors: [service].Error.InProgress [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.NotFound [service].Error.Failed void ResetContexts() Removes all contexts and re-provisions from the APN database. Contexts must all be deactivated for this method to work, and the atom must not be powered. Possible Errors: [service].Error.InProgress [service].Error.InvalidArguments [service].Error.NotAllowed Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. ContextAdded(object path, dict properties) Signal that gets emitted when a new context has been created. It contains the context object path and its properties. ContextRemoved(object path) Signal that gets emitted when a context has been removed. The object path of the context is only included for reference. Its properties are no longer accessible at this point. Properties boolean Attached [readonly] Contains whether the Packet Radio Service is attached. The attach state might change dynamically based on availability of network resources. If this value changes to false, the user can assume that all contexts have been deactivated. If the modem is detached, certain features will not be available, e.g. receiving SMS over packet radio or network initiated PDP activation. string Bearer [readonly, optional] Contains the data bearer technology as reported by the GPRS service registration (if known). Possible values are: "none", "gsm", "edge", "umts", "hsdpa", "hsupa", "hspa" (HSDPA and HSUPA at the same time) and "lte" boolean Suspended [readonly, optional] Contains whether the GPRS service is suspended. During suspended state the modem is attached to the GPRS service and all contexts remain established, however, data transfer is not possible. The suspended state may be entered if the modem is temporarily out of network coverage. GPRS class B modems will suspend GPRS whenever a voice call is active at the same time. GPRS may also be suspended if the network does not support simultaneous packet data and voice. Various signalling procedures may also cause GPRS to be briefly suspended. As the suspension may be brief, clients should wait for an appropriate time for GPRS service to resume before taking corrective action. boolean RoamingAllowed [readwrite] Contains whether data roaming is allowed. In the off setting, if the packet radio registration state indicates that the modem is roaming, oFono will automatically detach and no further connection establishment will be possible. boolean Powered [readwrite] Controls whether packet radio use is allowed. Setting this value to off detaches the modem from the Packet Domain network. Connection Context hierarchy ================= Service org.ofono Interface org.ofono.ConnectionContext Object path [variable] Methods dict GetProperties() Returns all properties for the context object. void SetProperty(string property, variant value) Sets the property to a desired value Possible Errors: [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed [service].Error.InProgress [service].Error.NotAttached [service].Error.AttachInProgress [service].Error.NotImplemented Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. Properties boolean Active [readwrite] Holds whether the context is activated. This value can be set to activate / deactivate the context. string AccessPointName [readwrite] Holds the name of the access point. This is abbreviated as APN. This value cannot be changed when the context is active. string Type [readwrite] Contains the intended usage type for this context. The currently supported values are: "internet" - General internet connectivity "mms" - Used by MMS related services "wap" - Used by WAP related services "ims" - Used by IMS related services "ia" - Used by IA related services string AuthenticationMethod [readwrite] Holds the PPP authentication method to use. Valid values are "pap" and "chap". Defaults to "chap". string Username [readwrite] Holds the username to be used for authentication purposes. This value cannot be changed when the context is active. string Password [readwrite] Holds the password to be used for authentication purposes. This value cannot be changed when the context is active. string Protocol [readwrite] Holds the protocol for this context. Valid values are: "ip", "ipv6" and "dual". string Name [readwrite] The name is a free form string that describes this context. The name should not be empty and limited to a short string for display purposes. boolean Preferred [readwrite] Holds a boolean. This value is meant to be used by clients to store an indication on whether this context is preferred compared to others. ofono just stores and shows this value, but does not use it internally. The exact way in which this is used depends on the clients. dict Settings [readonly, optional] Holds all the IP network settings string Interface [readonly, optional] Holds the interface of the network interface used by this context (e.g. "ppp0" "usb0") string Method [readonly, optional] Holds the IP network config method "static"- Set IP network statically "dhcp" - Set IP network through DHCP string Address [readonly, optional] Holds the IP address for this context. string Netmask [readonly, optional] Holds the Netmask for this context. array{string} DomainNameServers [readonly, optional] Holds the list of domain name servers for this context. string Gateway [readonly, optional] Holds the gateway IP for this connection. string Proxy [readonly, MMS only] Holds the current proxy information for using this context. In combination with the Interface value this allows access to the services offered by this context. It is possible that this reflects just the MessageProxy property if such a routing can be set up. However this can also be pointing to a local proxy on 127.0.0.1 and then using the loopback interace lo for it. Users of this context should bind to the provided interface and only attempt access via this proxy. All other values are left out in this case. dict IPv6.Settings [readonly, optional] Holds all the IPv6 network settings string Interface [readonly, optional] Holds the interface of the network interface used by this context (e.g. "ppp0" "usb0") string Address [readonly, optional] Holds the IP address for this context. byte PrefixLength [readonly, optional] Holds the prefix length. array{string} DomainNameServers [readonly, optional] Holds the list of domain name servers for this context. string Gateway [readonly, optional] Holds the gateway IP for this connection. string MessageProxy [readwrite, MMS only] Holds the MMS Proxy setting. string MessageCenter [readwrite, MMS only] Holds the MMSC setting. ofono-1.17.bzr6912+16.04.20160314.3/doc/manager-api.txt0000644000015600001650000000141012671500024022062 0ustar pbuserpbgroup00000000000000Manager hierarchy ================= Service org.ofono Interface org.ofono.Manager Object path / Methods array{object,dict} GetModems() Get an array of modem objects and properties that represents the currently attached modems. This method call should only be used once when an application starts up. Further modem additions and removal shall be monitored via ModemAdded and ModemRemoved signals. Signals ModemAdded(object path, dict properties) Signal that is sent when a new modem is added. It contains the object path of new modem and also its properties. ModemRemoved(object path) Signal that is sent when a modem has been removed. The object path is no longer accessible after this signal and only emitted for reference. ofono-1.17.bzr6912+16.04.20160314.3/doc/dialup-overview.txt0000644000015600001650000000511012671500024023024 0ustar pbuserpbgroup00000000000000DialUp Networking diagram ========================= This diagram explains how oFono and ConnMan are interacting to handle a data call from a DUN client. 1) GAtServer receive ATD*99#. 2) The GAtPPP server is setup. 3) oFono through GAtPPP notify ConnMan that he needs a TUN/TAP interface. 4) oFono is notified that TUN/TAP interface is created and can start exchanging PPP packets with DUN client. Those PPP packets are converted into IP stream and transmitted to/received from TUN/TAP interface. 5) ConnMan is selecting which interface to send IP packets (WiFi, 3G, ...). *----------------* *----------------* | | ATD*99# | | | DUN Client |----------->| GAtServer | | |<---* | | *----------------* | *----------------* | | PPP | | setup_ppp() Packets | | | | | V Need | *----------------* /dev/net/tun *----------------* *--->| | interface | | | GAtPPP |<-------------->| ConnMan | *--->| | interface | | | *----------------* created *----------------* IP | | Stream | |Create | *------------------------* |/dev/net/tun | | | |interface *--->| TUN/TAP interface |<-----------* | | *------------------------* IP | Stream routed with | ConnMan rules *---------------------*--------------------* | | | | | | V V V *-------------* *-------------* *-------------* | | | | | | | WiFi | | Modem | | EthX | | | | | | | *-------------* *-------------* *-------------* ofono-1.17.bzr6912+16.04.20160314.3/doc/dundee-api.txt0000644000015600001650000000425012671500024021721 0ustar pbuserpbgroup00000000000000 Manager hierarchy ================= Service org.ofono.dundee Interface org.ofono.dundee.Manager Object path / Methods array{object,dict} GetDevices() Get an array of device objects and properties that represent the currently attached devices. This method call should only be used once when an application starts up. Further device additions and removal shall be monitored via DeviceAdded and DeviceRemoved signals. Signals DeviceAdded(object path, dict properties) Signal that is sent when a new device is added. It contains the object path of new device and its properties. DeviceRemoved(object path) Signal that is sent when a device has been removed. The object path is no longer accessible after this signal and only emitted for reference. Device hierarchy ================ Service org.ofono.dundee Interface org.ofono.dundee.Device Object path /{device0,device1,...} Methods dict GetProperties() Returns properties for the device object. See the properties section for available 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.Timedout [service].Error.InvalidArguments [service].Error.Failed Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given property. Properties string Name [readonly] Friendly name of the device. boolean Active [readwrite] Holds whether the device is connected. A connection will be established when this value is set to true. A existing connection will be teared down when set to false. dict Settings [readonly] Holds all the IP network settings. string Interface [readonly, optional] Holds the interface of the network interface used by this connection (e.g. "ppp0" "usb0") string Address [readonly, optional] Holds the IP address for this connection. array{string} DomainNameServers [readonly, optional] Holds the list of domain name servers for this connection. ofono-1.17.bzr6912+16.04.20160314.3/doc/message-api.txt0000644000015600001650000000143412671500024022102 0ustar pbuserpbgroup00000000000000Message hierarchy =============== Service org.ofono Interface org.ofono.Message Object path [variable prefix]/{modem0,modem1,...}/{message_01,...} Methods dict GetProperties() Returns properties for the message object. See the properties section for available properties. void Cancel() Cancel a message that was previously sent. Only messages that are waiting on queue can be cancelled and it's not possible to cancel messages that already had some parts sent. Possible Errors: [service].Error.AccessDenied Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given property. Properties string State Contains the state of the message object. Possible values are: "pending", "sent", "failed" ofono-1.17.bzr6912+16.04.20160314.3/doc/telit-modem.txt0000644000015600001650000000122312671500024022123 0ustar pbuserpbgroup00000000000000oFono - Open Source Telephony ***************************** Purpose ======= The purpose of this document is to identify issues and configuration requirements with Telit's modems. HE910 ===== GPS: To enable location reporting on the Telit HE910 the modem needs to be switched to Port Configuration #8. Please refer to Telit's 'HE910 UE910 Family Ports Arrangements' section 4.1.3 for rationale and 'AT Commands Reference Guide' section 3.5.7.1.96 for specific AT command. 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'. ofono-1.17.bzr6912+16.04.20160314.3/doc/call-volume-api.txt0000644000015600001650000000223212671500024022673 0ustar pbuserpbgroup00000000000000CallVolume hierarchy =============== Service org.ofono Interface org.ofono.CallVolume Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns properties for the CallVolume object. See the properties section for available 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.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given property. Properties boolean Muted [readwrite] Boolean representing whether the microphone is muted. byte SpeakerVolume [readwrite] Represents the current volume of the speaker in percentage points. Valid values are 0-100. byte MicrophoneVolume [readwrite] Represents the current volume of the microphone in percentage points. Valid values are 0-100. ofono-1.17.bzr6912+16.04.20160314.3/doc/mmi-codes.txt0000644000015600001650000000737712671500024021600 0ustar pbuserpbgroup00000000000000Man-Machine Interface (MMI) Codes and oFono =========================================== 1.0 Introduction 3GPP 22.030 describes the structure of MMI codes for User Equipment. All user equipment that wishes to be compliant with the Global Certification Forum (GCF) must recognize such codes in the dialer application. This document describes the basic design principles for how to handle MMI codes with oFono. 2.0 Basic MMI structure The MMI codes fall into two broad categories: codes that are followed by and standalone codes. oFono handles all codes that are followed by while the UI is expected to handle standalone codes. 2.1 Supplementary Service Control Codes The following codes are followed by and are handled by oFono. The general structure of the codes is as follows: * Activation - '*SC*SI#' * Registration - '*SC*SI#' and '**SC*SI#' * Erasure - '##SC*SI#' * Deactivation - '#SC*SI#' * Interrogation - '*#SC*SI#' Please refer to 3GPP 22.030 for detailed explanation of the structure of SI and SC. oFono currently handles the following SCs: * 33 - Call Barring - All Outgoing * 331 - Call Barring - Outgoing International * 332 - Call Barring - Outgoing International except Home Country * 35 - Call Barring - All Incoming * 351 - Call Barring - All Incoming when Roaming * 330 - Call Barring - All Barrring Services * 333 - Call Barring - All Outgoing Services (e.g. 33, 331, 332) * 335 - Call Barring - All Incoming Services (e.g. 35, 351) * 21 - Unconditional Call Forwarding * 67 - Call Forwarding on Busy * 61 - Call Forwarding on No Reply * 62 - Call Forwarding on Unreachable * 002 - Call Forwarding All Conditional * 004 - Call Forwarding All * 30 - CLIP * 31 - CLIR * 76 - COLP * 77 - COLR * 43 - Call Waiting * 300 - CNAP 2.2 Registration of a new password The following password change strings are followed by and are recognized by oFono: * 03 * ZZ * OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD # ** 03 * ZZ * OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD # * 03 ** OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD # ** 03 ** OLD_PASSWORD * NEW_PASSWORD * NEW_PASSWORD # NOTE: ZZ is the Call Barring supplementary service code. 2.3 Change of PIN/PIN2 The following string allows the user to change the PIN: PIN1: **04*OLD_PIN*NEW_PIN*NEW_PIN# PIN2: **042*OLD-PIN2*NEW_PIN2*NEW_PIN2# The following string allows the user to unblock the PIN: PIN1: **05*PIN_UNBLOCKING_KEY*NEW_PIN*NEW_PIN# PIN2: **052*PIN2_UNBLOCKING_KEY*NEW_PIN2*NEW_PIN2# Please note that this procedure is not followed by . It is up to the dialer to recognize this string and act accordingly by using the appropriate method on the SimManager interface. 2.4 IMEI Display The following string can be used to obtain the IMEI: *#06# Please note that this procedure is not followed by . It is up to the dialer to recognize this string and display the SerialNumber property of the Modem Interface. 3.0 General Application Guidelines When the application is taking user input it is expected to match the input against all possible strings that are not to be followed by . At a minimum the PIN change or unlock and the IMEI display strings must be handled by the application. Any additional manufacturer-specific strings are also to be handled by the application. Once the user presses the request should be sent to the SupplementaryServices.Initiate() method. If the string is recognized as a control string, then the return value will be interpreted according to structure specified in doc/supplementaryservices-api.txt. If the error NotRecognized is returned, then the string is not recognized as a supplementary service string and should be treated as a call setup request instead. In this case the application should forward the string to VoiceCallManager.Dial() method. ofono-1.17.bzr6912+16.04.20160314.3/doc/release-faq.txt0000644000015600001650000000375412671500024022103 0ustar pbuserpbgroup00000000000000oFono release FAQ ***************** What is the release cycle? ========================== There is no clear specified release cycle. The project follows the general open source paradigm of release early and release often. Historically the release cadence has been about two to three weeks. The two weeks release cycle is a general rule of thumb. It will never be precise down to an exact day. The decision to release is driven by the code flow and changes made during that cycle. When a closed set of features has been merged, then a new release is considered. When a lot changes are merged, then the release cycle can be decreased down to weekly releases. And in case not many changes have been made and no new features are merged, it can happen that the release cycle becomes as long as one month. In addition to the normal releases during a cycle, there could be also so called brown-paper-bag type of releases. The releases are fixing critical issues with the previous release. Normally they happen in case a serious bug or regression slipped into the source and where it makes sense to fix it right away. The goal is to keep this type of releases to a bare minimum, but they do happen every now and then. From past releases they are to be expected around three times per year. What does the release number mean? ================================== Every release contains a major and a minor version. The major version only indicates an API version and it should not change until the D-Bus API is backwards incompatible. The same major version can contain new version of the API as long as it is backward compatible. This allows for extensions of the D-Bus API within the same major version. The minor version number is just an increasing number and has itself no special meaning. The minor version will increase until a new backwards incompatible API is required and a new major number is used. There is no stable API guarantee for the internal plugin API. The release numbers are not covering this. ofono-1.17.bzr6912+16.04.20160314.3/doc/assisted-satellite-navigation-api.txt0000644000015600001650000000357412671500024026425 0ustar pbuserpbgroup00000000000000Assisted Satellite Navigation hierarchy [experimental] ========================================================== Service org.ofono Interface org.ofono.AssistedSatelliteNavigation Object path [variable prefix]/{modem0,modem1,...} Methods void SendPositioningElement(string xml_element) Send an XML element conforming to the XML DTD for as defined in 3GPP 27.007 Table 8.55-2. This xml is used for transferring data associated with positioning requests received via control plane from the network. This includes assistance data requests and the results of positioning procedures. This method maps directly to the 3GPP 27.007 AT+CPOS command. void RegisterPositioningRequestAgent(object path) Registers an agent which will be called whenever a CPOSR AT response is received. The Agent must respond to requests using SendPositioningElement. void UnregisterPositioningRequestAgent(object path) Un-registers the agent. PositioningRequestAgent hierarchy ================================== Service unique name Interface org.ofono.PositioningRequestAgent Object path freely definable Methods void Request(string xml_element) Receive an XML element conforming to the XML DTD for in 3GPP 27.007. This xml is used for transferring data associated with positioning requests received, via control plane, from the network. This includes measurement requests and assistance data. This method maps directly to the 3GPP defined +CPOSR unsolicited result code. void ResetAssistanceData() A request has been received from the network that all assistance data should be reset. This is used for 3gpp performance tests. void Release() Agent is being released, possibly because of oFono terminating, AssistedSatelliteNavigation interface is being torn down or modem off. No UnregisterPositioningRequestAgent call is needed. ofono-1.17.bzr6912+16.04.20160314.3/doc/ofono-paper.txt0000644000015600001650000002007112671500024022132 0ustar pbuserpbgroup00000000000000oFono - Open Source Telephony ******************************************************************************* 1.0 Introduction Linux and other open source components are now used extensively on both desktop and mobile embedded devices. They provide networking, power management, database and other core OS infrastructure. However, up to this point no viable open source solution for mobile telephony existed. oFono aims to change that; it is a telephony host stack specifically targeted at both mobile embedded and desktop systems. Launched on May 11, 2009 oFono aims to provide a solid framework for builidng 3GPP GSM/UMTS User Equipment (UE) standard compliant devices. Support for CDMA/EVDO technologies is also planned. The goal of oFono is to provide an easy to use, high-level API for applications. This is accomplished by keeping the core logic within the daemon, taking care of standards compliance and exposing only the need-to-know aspects to the application. The license for oFono was chosen as GPLv2. This means that all core services and plugins for oFono must be Open Source. oFono accepts GPLv2 or any GPL-compatible BSD license. However, since oFono provides a D-Bus API, user interface applications can be of any license. 2.0 Design Philosophy 2.1 Modern oFono aims to be a modern implementation, ready for the 21st century. From the very beginning oFono was designed with support of multiple technologies and device types in mind. It is also designed to support multiple active devices simultenously. This enables greater flexibility and enables usecases not possible with current solutions. oFono explicitly chooses not to support some of the more archaic features of GSM. Specifically only limited use of the SIM for Phonebook support is enabled. SIM storage for incoming and outgoing Short Messages (SMS) is also not supported. The use of these features does not make sense on the current generation of devices, and introduces unnessary complexity. 2.2 Fast and Light One of the main constraints for oFono's design was to make it extremely performant on resource-constrainted embedded devices. This means that high-level languages like Python could not be used and library dependencies had to be kept to a minimum. oFono is thus implemented in C and has minimial dependencies: libdbus, glib. The reference drivers introduce two other library dependencies, gatchat and gisi, which are linked statically. 2.3 Standards Compliant oFono is meant to be used in commercial systems, so standards compliance is a primary consideration from the very beginning. Whenever possible oFono takes care of the gory details. This includes the particulars of SMS decoding, de-fragmentation and duplicate detection; network operator name display; message waiting indicator processing and reporting; emergency dialing numbers, service numbers and subscriber number management; supplementary service control via strings defined in 3GPP TS 22.030. 3.0 Architecture oFono provides a flexible, modular and extensible architecture with four main components: core daemon, oFono atoms, drivers and plugins. 3.1 Core Daemon Core daemon provides base level services within oFono, namely the loading of plugins and drivers; utility APIs for decoding, encoding and fragmentation of binary SMS pdus; utility APIs for reading and writing to the SIM, and interpreting the contents of the low-level Element File (EF) contents; utility APIs for character set conversion; utility APIs for decoding, duplicate detection and pagination of cell broadcasts; and detection of and communication between oFono atoms. A big part of the core daemon is the modem device abstraction. Each device is managed independently, and several devices can be present and active in the system at the same time. The technologies for each device are not restricted in any way, and can be customized via the use of drivers. 3.2 oFono Atoms oFono atoms provide a clear abstraction API for the applications based on D-Bus. There are currently over a dozen atoms within oFono, providing access to core functionality like voice calls, supplementary services, short message service (SMS), cell broadcast (CBS) and sim management. Atoms can detect the presence of other atoms and use information provided by other atoms to provide extra functionality. For instance, the Network Registration atom will read low-level EF files whenever a SIM is present, and provide enhanced operator information if the SIM is thus provisioned. 3.3 Drivers oFono provides a way to integrate multiple device technologies through its driver mechanism. All modem devices and atoms provide an abstract interface for low-level operations. This interface is based on 3GPP TS 27.007 "AT command set for User Equipment" and 3GPP TS 27.005 "DTE-DCE interface for SMS and CBS". oFono assumes that all operations are fully asynchronous. This means that oFono can accommodate a wide variety of devices, including full-featured modems (AT command based and otherwise), data-only cards, and modem like devices (e.g. Bluetooth Handsfree and Sim Access Profile devices, etc.) oFono provides a reference AT command driver, which should work for the majority of AT command based modems in the market. oFono also includes an ISI protocol based driver, which will enable the majority of Nokia devices to be used. Finally a Bluetooth Handsfree Profile (HFP) driver is also planned. 3.4 Plugins Plugins provide a final piece of the puzzle. These are used to provide device drivers and atom drivers. They can also be used to extend oFono or interact with other system services. For example, Moblin uses oFono plugins to store all call history information within Evolution Data Server. 4.0 D-Bus API Much thought has been given to how user interface applications will interact with oFono. The goal of the oFono API is to make the User Interface (UI) application writer's job as easy as possible. This is accomplished in two ways: exposing only the essential details to the application and provide a high level API. To accomplish this, oFono sticks to the following four basic principles of API design: Consistent, Minimal, Complete and Easy to Use. 4.1 Consistent As mentioned previously, each atom provides a high-level D-Bus API, which is referred to as an interface. Each interface has a well-defined set of properties and two special methods for managing them: GetProperties and SetProperty. All names within oFono are CamelCased and this naming convention is strictly enforced. This means that once the application writer is comfortable using one Interface, they should be able to quickly pick up others. 4.2 Minimal & Complete A common pitfal of API design is exposing too much and assuming that the user has the same level of knowledge as the designer. Almost always these assumptions are incorrect and lead to incorrect and inefficient use of the API. This in turn leads to applications that are hard to write, maintain and replace. Instead the API should be minimal; it should make it easy and apparent to the user how to accomplish a particular task he or she is interested in. oFono accomplishes this by doing as much as possible within the core and only exposing the information which is actually required to be shown to the user. 4.3 Easy to Use While the above three principles generally provide good results, a process of refinement can still be applied. oFono works with user interface designers and implementers to continually improve its API. This means that if a particular feature is found to be inefficient in practice, it refined and improved as quickly as possible. 5.0 Conclusion oFono provides a full host protocol stack for telephony aware applications. Today, it enables most of the commonly used features defined by 3GPP standards, including voicecalls, sms, cbs, supplementary services and network registration. Data connections using GPRS and 3G features are being actively worked on. It thus provides a viable, open source solution to system implementors seeking to add telephony capabilities to Linux desktop and mobile devices. 6.0 Resources Website: http://ofono.org Mailing List: ofono@ofono.org IRC: #ofono on freenode ofono-1.17.bzr6912+16.04.20160314.3/doc/radio-settings-api.txt0000644000015600001650000001203612671500024023412 0ustar pbuserpbgroup00000000000000Radio settings hierarchy ======================== Service org.ofono Interface org.ofono.RadioSettings Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns all radio access properties. See the properties section for available properties. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed void SetProperty(string name, 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.NotImplemented [service].Error.InvalidArguments [service].Error.Failed Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. Properties string TechnologyPreference [readwrite] The current radio access selection mode, also known as network preference. The possible values are: "any" Radio access technology selection is done automatically, based on reception and availability. "gsm" Only GSM used for radio access. "umts" Only UMTS used for radio access. "lte" Only LTE used for radio access. NOTE: In Ubuntu for Phones branch the description above is no longer valid. Usage of "any" is deprecated and, after setting the property, RAT selection will be done automatically based on reception and availability among the selected technology *and* less advanced technologies. For instance, if preference is "umts", at a given moment we could be registered in a gsm or umts network, but never in an lte one. array{string} AvailableTechnologies [readonly, optional] List of values for TechnologyPreference property supported by the modem. string GsmBand [readwrite, optional] Frequency band in which the modem is allowed to operate when using "gsm" mode. Setting this property has an immediate effect on modem only if TechnologyPreference is set to "gsm" or "any". Otherwise the value is kept and applied whenever modem uses this mode. The possible values are: "any" Frequency band is selected automatically by modem. "850" Operate only on 850 MHz. "900P" Operate only on 900 MHz, known as Primary GSM-900 Band "900E" Operate only on 900 MHz, known as Extended GSM-900 Band. "1800" Operate only on 1800 MHz, known as DCS. "1900" Operate only on 1900 MHz, known as PCS. string UmtsBand [readwrite, optional] Frequency band in which the modem is allowed to operate when using "umts" mode. Setting this property has an immediate effect on modem only if TechnologyPreference is set to "umts" or "any". Otherwise the value is kept and applied whenever modem uses this mode. The possible values are: "any" Frequency band is selected automatically by modem. "850" Operate only on 850 MHz, known as CLR (class V). "900" Operate only on 900 MHz, known as GSM (class VIII). "1700AWS" Operate only on 1700 MHz, known as AWS (class IV). "1900" Operate only on 1900 MHz, known as PCS (class II). "2100" Operate only on 2100 MHz, known as IMT (class I). boolean FastDormancy [readwrite, optional] This property will enable or disable the fast dormancy feature in the modem. Fast dormancy refers to a modem feature that allows the modem to quickly release radio resources after a burst of data transfer has ended. Normally, radio resources are released by the network after a timeout configured by the network. Fast dormancy allows the modem to release the radio resources more quickly. Fast dormancy is a major power-saving feature for mobile devices. Typically, fast dormancy would be enabled when the device is not being interactively used by a human user and only networking applications with keep-alive traffic are active (e.g. mail client or a presence application). In this case it is desirable to release radio resources quickly after a keep-alive transaction has ended, since typically no network traffic will occur until the next keep-alive transaction. Fast dormancy should not be enabled during interactive use because the release and setup of radio resources introduces perceivable delay for the end user. The fast dormancy implementation in the modem is vendor specific. The implementation should try to release radio resources more quickly, when the situation allows it, but should also take care not to increase the signalling load on the cellular network by releasing and re-establishing radio resources too often. The modem should adjust its behaviour to the 3GPP release supported by the network and the parameters set by the operator. Fast dormancy can be ignored for externally powered modems such as USB sticks or PCI devices. If the modem does not support such a feature the property should never be exposed to the user. ofono-1.17.bzr6912+16.04.20160314.3/doc/handsfree-api.txt0000644000015600001650000000534012671500024022415 0ustar pbuserpbgroup00000000000000Handsfree hierarchy =================== Service org.ofono Interface org.ofono.Handsfree Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns properties for the Handsfree Interface. See the properties section for available 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.NotImplemented [service].Error.NotSupported string RequestPhoneNumber() Request a phone number from the AG, corresponding to the last voice tag recorded in the HF. The AG may accept or reject this request depending on its internal state. This functionality is generally implemented by using the +BINP=1 AT command. Possible Errors: [service].Error.InProgress [service].Error.InvalidArguments [service].Error.Failed Signals PropertyChanged(string property, variant value) Signal is emitted whenever a property has changed. The new value is passed as the signal argument. Properties array{string} Features [readonly] List of features supported by the AG. The currently supported values are: "voice-recognition" "attach-voice-tag" "echo-canceling-and-noise-reduction" "three-way-calling" "release-all-held" "release-specified-active-call" "private-chat" "create-multiparty" "transfer" "hf-indicators" boolean InbandRinging [readonly] Boolean representing whether inband ringing is enabled. boolean VoiceRecognition [readwrite] Boolean representing whether voice recognition is currently active in the AG. This property may be written to activate or deactivate the function from the HF, or the AG could autonomously initiate it. boolean EchoCancelingNoiseReduction [readwrite, optional] Non-persistent Boolean property representing whether echo canceling and noise reduction is enabled in the AG. This feature can only be disabled once from the HF unit; the current specification does not allow the HF unit from enabling this feature on the AG once it has been disabled. byte BatteryChargeLevel [readonly] The current charge level of the battery. The value can be between 0 and 5 respectively. array{string} SubscriberNumbers [readonly] List of subscriber numbers provided by the AG. boolean DistractedDrivingReduction [readwrite, optional] Non-persistent property representing whether distracted driving reduction mode should be enabled in the AG. Support for this feature is optional on the AG. ofono-1.17.bzr6912+16.04.20160314.3/doc/hfp-overview.txt0000644000015600001650000000443712671500024022336 0ustar pbuserpbgroup00000000000000HandsFree Profile Audio Gateway diagram ======================================= This diagram explains how oFono manages HFP AG. AT commands and unsolicited results are managed in their related atom (eg. ATA is managed in voicecall atom). The emulator atom is managing AT commands or unsolicited results that are not falling into a specific atom. 1) HFP AG plugin registers a HFP AG server as soon as a voicecall atom exist. 2) When a connection occurs on this server, HFP AG plugin creates and registers an emulator atom. 3) Emulator atom will start a GAtServer and registers non-atom-specific AT commands to it. 4) On emulator atom registration, voice call related atoms (voicecall, network and sim) register AT callbacks they managed through emulator atom. *===========* *-----------------* | | Register | | | BlueZ |<---------------| HFP AG plugin | | | SDP record | | *===========* *-----------------* | | Start emulator on connection | V *-----------------* | | *-------->| Emulator Atom | | | | | *-----------------* | | Register | | Register AT commands AT | V callbacks | *-----------------* | | | | | GAtServer | | | | | *-----------------* | *---------------------*-------------------* | | | *----------------* *--------------* *-------------* | | | | | | | Voicecall atom | | Network atom | | SIM atom | | | | | | | *----------------* *--------------* *-------------* ofono-1.17.bzr6912+16.04.20160314.3/doc/hardware-support.txt0000644000015600001650000000471112671500024023217 0ustar pbuserpbgroup00000000000000Hardware support **************** Voice and data modems ===================== - Infineon (IFX) Fully supported modem with voice calls, text messaging, supplementary services, data connections, SIM Toolkit etc. Supports multiple GPRS connections with RawIP interface. - ST-Ericsson (STE) Fully supported modem with voice calls, text messaging, supplementary service, data connections, SIM Toolkit etc. Supports multiple GPRS connections via CAIF subsystem. - Nokia Phonet/ISI Supports majority of phone features used on the N900 phone from Nokia. Supports multiple GPRS connections via Phonet pipes. - Calypso / Openmoko Freerunner Fully supported modem with voice calls, text messaging, supplementary services, data connections, SIM Toolkit etc. GPRS connection support is limited due to hardware design. Data only modems ================ - Ericsson MBM Fully supported data only modem with extra support for text messaging, USSD and SIM Toolkit. Support for one high-speed CDC Ethernet GPRS connection and one PPP connection. Sony-Ericsson MD-300 Toshiba F3607gw Lenovo F3507g and F3607gw Dell 5530, F3607gw and F3307 - Option HSO Fully supported data only modem with extra support for text messaging and USSD. Support for one high-speed point-to-point GRPS connection. - Huawei Support for one PPP based GPRS connection with extra support for text messaging and USSD. Modems based on EM770 have voice call support. Newer Qualcomm QMI based versions are also supported. - Novatel Support for one PPP based GPRS connection with extra support for text messaging and USSD. Newer Qualcomm QMI based versions are also supported. - ZTE Support for one PPP based GPRS connection with extra support for text messaging and USSD. Newer Qualcomm QMI based versions are also supported. - Sierra Support for PPP based GPRS connection still work in progress and it has limited support for text messaging and USSD. Devices with DirectIP are supported and have extra support for text messaging and USSD. - Nvidia Icera Fully support data only modem with extra support for text messaging and USSD. - Qualcomm Gobi Fully supported data only modem with extra support for text messaging and GPS location reporting. Support for voice calls, USSD and SS is work in progress. Other modems ============ - Phonesim Fully supported emulator for testing. - Bluetooth Handsfree Special Bluetooth Handsfree client support. ofono-1.17.bzr6912+16.04.20160314.3/doc/call-barring-api.txt0000644000015600001650000000500212671500024023006 0ustar pbuserpbgroup00000000000000Call Barring hierarchy ====================== Service org.ofono Interface org.ofono.CallBarring Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Contains the properties for this object. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented void ChangePassword(string old_password, string new_password) Register new network password for the barring services. Possible Errors: [service].Error.InProgress [service].Error.InvalidFormat [service].Error.Failed void DisableAll(string password) Disables all call barrings. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void DisableAllIncoming(string password) Disables barrings for incoming calls. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void DisableAllOutgoing(string password) Disables barrings for outgoing calls. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void SetProperty(string property, variant value, string pin2) Sets the given property value to that specified in call parameter. For all properties, the password (typically PIN2) must be provided. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed Signals PropertyChanged(string property, variant value) Signal is emitted whenever a property has changed. The new value is passed as the signal argument. Properties string VoiceIncoming [readwrite] Contains the value of the barrings for the incoming voice calls. The possible values are: - "always" bar all incoming voice calls - "whenroaming" bar incoming voice calls when roaming, - "disabled" if no barring is active string VoiceOutgoing [readwrite] Contains the value of the barrings for the outgoing voice calls. The possible values are: - "all" bar all outgoing calls - "international" bar all outgoing international calls - "internationalnothome" bar all outgoing international calls except to home country - "disabled" if no barring is active ofono-1.17.bzr6912+16.04.20160314.3/doc/supplementaryservices-api.txt0000644000015600001650000001247112671500024025135 0ustar pbuserpbgroup00000000000000SupplementaryServices hierarchy ========================== Service org.ofono Interface org.ofono.SupplementaryServices Object path [variable prefix]/{modem0,modem1,...} Methods string, variant Initiate(string command) If the command is a recognized supplementary service control string, the corresponding SS request is made and the result is returned. Otherwise the command is sent to the network initiating a USSD session. When the request is handled by the appropriate node of the network, the method returns the response or an appropriate error. The network may be awaiting further response from the ME after returning from this method and no new command can be initiated until this one is cancelled or ended. The output arguments are described in section "Initiate method outptut arguments" below. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.NotSupported [service].Error.NotAllowed [service].Error.IncorrectPassword [service].Error.NotRegistered [service].Error.Timedout [service].Error.AccessDenied [service].Error.Failed [service].Error.NotRecognized [service].Error.Terminated string Respond(string reply) Send a response to the network either when it is awaiting further input after Initiate() was called or after a network-initiated request. Possible Errors: [service].Error.InProgress [service].Error.NotActive [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed [service].Error.Terminated void Cancel() Cancel an ongoing USSD session, mobile- or network-initiated. Possible Errors: [service].Error.NotActive [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed dict GetProperties() Returns Supplementary Services related properties. See the properties section for available properties. Signals NotificationReceived(string message) Signal is emitted on a network-initiated USSD request for which no response is needed. RequestReceived(string message) Signal is emitted on a network-initiated USSD request for which a response must be sent using the Respond method unless it is cancelled or the request is not supported. PropertyChanged(string property, variant value) Signal is emitted whenever a property has changed. The new value is passed as the signal argument. Properties string State [readonly] Reflects the state of current USSD session. The values have the following meanings: "idle" No active USSD session. "active" A session is active between the network and the ME, the ME is waiting for a reply from the network. "user-response" The network is waiting for the user's response, client must call Respond(). Initiate method output arguments ================================ The first return argument and the corresponding second return argument are: "USSD" string ussd_response "CallBarring" (string ss_op, string cb_service, dict cb_dict) "CallForwarding" (string ss_op, string cf_service, dict cf_dict) "CallWaiting" (string ss_op, dict cw_dict) "CallingLinePresentation" (string ss_op, string status) "ConnectedLinePresentation" (string ss_op, string status) "CallingLineRestriction" (string ss_op, string clir_status) "ConnectedLineRestriction" (string ss_op, string status) ss_op contains the supplementary service operation: "activation" "registration" "interrogation" "deactivation" "erasure" cb_service contains the call barring service for which the operation was requested: "AllOutgoing" "InternationalOutgoing" "InternationalOutgoingExceptHome" "AllIncoming" "IncomingWhenRoaming" "AllBarringServices" "AllOutgoingServices" "AllIncomingServices" cf_service contains the call forwarding service for which the operation was requested: "Unconditional" "Busy" "NoReply" "NotReachable" "All" "AllConditional" cb_dict contains basic service/call barring service combinations that were affected by SS operation and their current status ("enabled" or "disabled"). The basic services are: "Voice" "Data" "Fax" "Sms" "DataSync" "DataAsync" "DataPad" "DataPacket" To those the name of call barring service is appended, so the property and value is for example: "FaxIncomingWhenRoaming" : "disabled" cf_dict contains call forwarding properties affected by the operation. Propery names are formed from basic service name and call forwarding service name, for example: "VoiceNoReply" : "+12345678" The property value is the phone number to which the call is forwarded. For "NoReply" service, there is also a timeout property, holding the timeout in seconds, for example: "VoiceNoReplyTimeout" : 20 cw_dict contains basic services with "CallWaiting" suffix that were affected by call waiting operation and their current status ("enabled" or "disabled"), for example: "VoiceCallWaiting" : "enabled" status can be "disabled" or "enabled". clir_status can be "disabled", "permanent", "on" or "off". More information about supplementary services is provided in call-barring-api.txt, call-forwarding-api.txt and call-settings-api.txt ofono-1.17.bzr6912+16.04.20160314.3/doc/phonebook-api.txt0000644000015600001650000000077512671500024022451 0ustar pbuserpbgroup00000000000000Phonebook hierarchy =============== Service org.ofono Interface org.ofono.Phonebook Object path [variable prefix]/{modem0,modem1,...} Methods string Import() Returns the contents of the SIM and ME phonebook in VCard 3.0 format. If several entries are determined to be related to the same contact, then they are merged into a single VCard entry. The phonebook is returned as a single UTF8 encoded string with zero or more VCard entries. Possible Errors: [service].Error.InProgress ofono-1.17.bzr6912+16.04.20160314.3/doc/smartmessaging-api.txt0000644000015600001650000000357412671500024023511 0ustar pbuserpbgroup00000000000000Smart Messaging hierarchy =============== Service org.ofono Interface org.ofono.SmartMessaging Object path [variable prefix]/{modem0,modem1,...} Methods object SendAppointment(string to, array{bytes} appointment) Sends a vCalendar object in appointment to the number in to. The object in appointment is not interpreted by oFono in any way. If the object is too large to fit into a single SMS, it is fragmented as appropriate. This method call returns the object path of the queued SMS. object SendBusinessCard(string to, array{bytes} card) Sends a vCard object in card to the number in to. The object in card is not interpreted by oFono in any way. If the object is too large to fit into a single SMS, it is fragmented as appropriate. This method call returns the object path of the queued SMS. void RegisterAgent(object path) Registers an agent which will be called whenever a new Smart Messaging based SMS arrives. void UnregisterAgent(object path) Unregisters an agent. SmartMessagingAgent Hierarchy [experimental] =============== Service unique name Interface org.ofono.SmartMessagingAgent Object path freely definable Methods void ReceiveAppointment(array{byte} appointment, dict info) Requests the agent to process a new SMS that has arrived containing a vCalendar object. The info dictionary contains 'Sender', 'LocalSentTime' and 'SentTime' properties. Possible Errors: None void ReceiveBusinessCard(array{byte} card, dict info) Requests the agent to process a new SMS that has arrived containing a vCard object. The info dictionary contains 'Sender', 'LocalSentTime' and 'SentTime' properties. Possible Errors: None void Release() [noreply] Agent is being released, possibly because of oFono terminating, SMS interface is being torn down or modem off. No UnregisterAgent call is needed. ofono-1.17.bzr6912+16.04.20160314.3/doc/certification.txt0000644000015600001650000000672412671500024022541 0ustar pbuserpbgroup00000000000000oFono Certification Testing *************************** Introduction ============ The aim of this document is to briefly describe the GCF certification process and how the oFono projects intends to help make obtaining the relevant certifications easier. GCF certification ================= All european network operators require a device to be certified for their network prior to being sold. While each operator defines their own set of requirements, a common set is defined by Global Certification Forum, or GCF. In North America, these requirements are defined by PTCRB. Certification is achieved by successfully passing a set of test cases, which are defined in various 3GPP specifications. The testing methodology is to perform end-to-end testing. The tests are effectively testing the modem hardware, the telephony stack and the applications simultaneously. In effect, any change to the above components requires the testing to be performed again. There is no consideration for separate component testing. The Goal ======== While it is not possible to certify oFono directly as a GCF compliant software stack, it is possible to alleviate some of the work required for obtaining such certifications in an end-product. To accomplish this, oFono team will begin running all 3GPP test cases that are deemed relevant to achieving the above goal. The short-term approach is to perform manual test case runs to establish a baseline and fix any issues discovered. In the longer term, we will be building more advanced testing tools. The goals are automated testing to detect regressions and a full SIM toolkit test suite. Test case relevance and selection ================================= Many of the defined test cases are not relevant to the telephony stack and applications; such test cases include testing of the modem roaming behavior, particular radio frequency details, etc. These cases are not considered here; only test cases directly applicable to the telephony stack or applications will be considered. All other test cases are assumed to be covered by the modem hardware manufacturer or device manufacturer as part of the full product certification cycle. Test cases considered applicable ================================ Below is a list of conformance test cases considered relevant to this effort: - 3GPP 51.010-1: 2G conformance specification section 26.7.6.1, 44.2.9.1.1, 44.2.9.1.2: NITZ (network identity and time zone) section 26.8.x : Tests related to circuit switched call control section 26.9.x: Structured procedures (MO/MT calls, emergency calls) section 31.x: Test of supplementary services section 34.x: Short Message Service section 44.2.1: Test case requirements for GPRS mobility management section 44.2.2: Attach/Detach procedure - 3GPP 34.123-1: Protocol conformance specification section 6.1.1.x: PLMN selection section 6.1.2.6: Emergency calls section 10.x: Circuit Switched Call Control (CC) section 11.x: Session Management Procedures section 12.2.x, 12.3.x: PS attach/detach procedure section 13.x: General tests (Emergency calls) section 16.x: Short message services - 3GPP 31.121: USIM application test specification section 6.x: Security related Tests - 3GPP 51.010-4: SIM application toolkit conformance specification section 27.22.x : SIM Application Toolkit - 3GPP 31.124: USAT conformance test specification section 27.22.x: USAT Application Toolkit NOTE: Many of the tests related to (U)SAT are described in ETSI TS 102.384 and not in the above 3GPP specifications. ofono-1.17.bzr6912+16.04.20160314.3/doc/networktime-api.txt0000644000015600001650000000224212671500024023024 0ustar pbuserpbgroup00000000000000Network time hierarchy ====================== Service org.ofono Interface org.ofono.NetworkTime Object path [variable prefix]/{modem0,modem1,...} Methods dict GetNetworkTime() Returns the current time and date as notified by the cellular network. The dictionary contains the following possible keys: int64 UTC [optional] Network time in seconds from epoch. Receiving entity obtains current real time by adding the value from monotonic clock e.g. clock_gettime(CLOCK_MONOTONIC,...) and substracting "Received" key value. int32 Timezone [optional] Current timezone offset in seconds from UTC. uint32 DST [optional] Current daylight saving setting in hours. int64 Received [optional] Monotonic time as returned by clock_gettime(CLOCK_MONOTONIC,...) when the network time is received. string MobileCountryCode The Mobile country code of the current network operator. string MobileNetworkCode The Mobile network code of the current network operator. Signals NetworkTimeChanged(dict) Returns a dictionary with the same keys as GetNetworkTime() when a NITZ event is received. ofono-1.17.bzr6912+16.04.20160314.3/doc/standards.txt0000644000015600001650000001552712671500024021702 0ustar pbuserpbgroup00000000000000Referencing standards in the source =================================== When referencing standard documents use raw numbers xx.xxx for 3GPP documents or xxx.xxx for ETSI document (eg: 23.040). If needing to point to an specific section/subsection, explicitly say "Section foo" 3GPP specs can be found in http://3gpp.org/ftp/Specs. Core 3GPP Specifications ======================== - 22.030: Man-Machine Interface (MMI) of the User Equipment (UE) Describes the various supplementary service magic strings, how Abbreviated Dialing Numbers are used and general UI interaction. This spec is particularly important for its description of how calls are put on hold, transferred, swapped, etc. - 22.038: Alphabets and Language Specific Information Describes the GSM 7-bit character set, bit packing for SMS, CBS and USSD. Also describes UCS2 and how it is encoded for SMS, CBS and USSD. - 27.007: AT command set for User Equipment (UE) Describes the AT command set for all GSM modems. oFono atom driver APIs are largely based on the AT commands defined in this document. - 27.005: Short Message Service (SMS) & Cell Broadcast Service (CBS) Describes the AT command set for SMS and CBS interaction. - 23.040: Technical realization of the Short Message Service (SMS) Describes the SMS service in detail, including the various PDUs, headers, EMS messages, MWI messages and other aspects of SMS. - 23.041: Technical realization of Cell Broadcast Service (CBS) Describes the CBS service in detail, including the PDU structure, ETWS and other aspects. - 31.102: Characteristics of the (USIM) application Describes the contents of the SIM, SIM initialization procedures, elementary file permissions and formats. - 31.111: Universal Subscriber Identity Module (USIM) Application Toolkit (USAT) Describes 3GPP specific aspects of Card Application Toolkit (CAT) / STK. - 31.124: USAT conformance test specification Describes the testing parameters and test cases for 31.111. Security Specifications ======================= - 33.220 Generic bootstrapping architecture (GBA) Describes the generic bootstrapping architecture used to leverage SIM-based authentication. - 24.109 Bootstrapping interface (Ub) and NAF interface (Ua) Describes how the GBA authentication is used with, e.g., HTTP. 3GPP Specific Services ====================== - 22.072: Call Deflection - 22.081: Line Identification - 22.082: Call Forwarding - 22.083: Call Waiting and Call Hold - 22.084: Multiparty (MPTY) - 22.085: Closed User Group - 22.086: Advice of Charge - 22.088: Call Barring - 22.090: Unstructured Supplementary Service Data (USSD) - 22.091: Explicit Call Transfer ETSI Card Application Toolkit (Sim Toolkit) =========================================== - 102.223: Card Application Toolkit (CAT) Describes the core functionality of CAT, but does not describe network technology specific features (e.g. UMTS/CDMA). - 102.384: Card Application Toolkit (CAT) conformance specification Describes test methodology and test cases for 102.223. Core 3GPP2 Specifications ======================== - C.R1001-G: Administration of Parameter Value Assignments for cdma2000 Spread Spectrum Standards - Release G Describes the value of various parameters that defined in other specifications - C.S0015-B: Short Message Service (SMS) for Wideband Spread Spectrum Systems Describes the SMS service(include broadcast short message)in detail. providing delivery of text and numeric information for paging, messaging, and voice mail notification. - C.S0023-D: Removable User Identity Module for Spread Spectrum Systems Describes the contents of the R-UIM, R-UIM initialization procedures, functions, commands, file architecture and the coding of elementary files. - C.S0035-A: CDMA Card Application Toolkit (CCAT) Describes the core functionality of CCAT. - S.R0006-000 Wireless Features Description Describes the general definitions and concepts of a subset of wireless features. - S.R0006-100 Wireless Features Description: General Background and Assumptions Describes the general background and assumption of wireless features. 3GPP2 Wireless Features Description =================================== - S.R0006-501: Call Delivery - S.R0006-502: Call Forwarding--Busy - S.R0006-503: Call Forwarding--Default - S.R0006-504: Call Forwarding--No Answer - S.R0006-505: Call Forwarding--Unconditional - S.R0006-506: Call Transfer - S.R0006-507: Call Waiting - S.R0006-508: Calling Number Identification Presentation - S.R0006-509: Calling Number Identification Restriction - S.R0006-510: Conference Calling - S.R0006-511: Do Not Disturb - S.R0006-512: Flexible Alerting - S.R0006-513: Message Waiting Notification - S.R0006-514: Mobile Access Hunting - S.R0006-515: Password Call Acceptance - S.R0006-516: Preferred Language - S.R0006-517: Priority Access and Channel Assignment - S.R0006-518: Remote Feature Control - S.R0006-519: Selective Call Acceptance - S.R0006-520: Subscriber PIN Access - S.R0006-521: Subscriber PIN Intercept - S.R0006-522: Three-Way Calling - S.R0006-523: Voice Message Retrieval - S.R0006-524: Voice Privacy - S.R0006-525: Asynchronous Data Service - S.R0006-526: Calling Name Presentation - S.R0006-527: Calling Name Restriction - S.R0006-528: Data Privacy - S.R0006-529: Emergency Services - S.R0006-530: Group 3 Facsimile Service - S.R0006-531: Network Directed System Selection - S.R0006-532: Non-Public Service Mode - S.R0006-533: Over-the-Air Service Provisioning - S.R0006-534: Service Negotiation - S.R0006-535: User Group - S.R0006-536: Group 3 Analog Facsimile Service - S.R0006-601: Short Message Delivery - Point-to-Point Bearer Service - S.R0006-602: Wireless Features Description: Wireless Messaging Teleservice - S.R0006-603: Wireless Features Description: Wireless Paging Teleservice - S.R0006-701: Wireless Features Description: Mobile Station Functionality - S.R0006-801: Wireless Features Description: System Functionality - S.R0006-802: Wireless Features Description: Subscriber Confidentiality - S.R0006-803: Wireless Features Description: Network Services - S.R0006-804: Wireless Features Description: Enhanced Security Services - S.R0006-805: Wireless Features Description: CDMA Packet Data Service - S.R0006-806: Wireless Features Description: Over-the-Air Parameter Administration - S.R0006-807: Wireless Features Description: Generic Broadcast Teleservice Transport Capability: Network Perspective - S.R0006-808: Wireless Features Description: Circuit Switched Call Precedence Over CDMA Packet Data Session Common PCN Handset Specification (CPHS) ======================================= This specification includes certain pre-standard extensions to GSM standards. oFono implements some of the features found in the Phase 2 specification, version 4.2. The specification itself is not publicly available. Bluetooth Specifications ======================== - Dial-up Networking Profile - Bluetooth specification version 1.1 - 22 February 2001 ofono-1.17.bzr6912+16.04.20160314.3/doc/dialup-command-set.txt0000644000015600001650000000277112671500024023377 0ustar pbuserpbgroup00000000000000This document specifies the AT command set used in the bluetooth ofono plugins. Bluetooth Dial-up Networking Profile Features Description ========================================================= (Ref. document: Dial-up Networking Profile - Bluetooth specification version 1.1 - 22 February 2001) - AT COMMAND SET USED: Commands: &C Circuit 109 (DCD) Control &D Circuit 108 (DTR) Response &F Set to Factory Defined Configuration +GCAP Request Complete Capabilities List +GMI Request Manufacturer Identification +GMM Read Model Identification +GMR Read Revision Identification A Answer Incoming Call D Dial E Command Echo H Hang Up L Monitor Speaker Loudness M Monitor Speaker Control O Return to Online Data Mode P Select Pulse Dialling Q Result Code Suppression S0 Automatic Answer Control S10 Automatic Disconnect Delay Control S3 Command Line Termination Character S4 Response Formatting Character S5 Command Line Editing Character (BACKSPACE) S6 Blind Dial Delay Control S7 Connection Completion Timeout S8 Comma Dial Modifier Delay Control T Select Tone Dialling V DCE Response Format X Call Progress Monitoring Control Z Reset to Default Configuration Result codes: OK Acknowledge execution of a command CONNECT Connection has been established RING The DCE has detected an incoming call signal from the network NO CARRIER The connection has been terminated, or attempt to establish a connection failed ERROR Error NO DIALTONE No dial-tone detected BUSY Busy signal detected ofono-1.17.bzr6912+16.04.20160314.3/doc/audio-settings-api.txt0000644000015600001650000000151312671500024023413 0ustar pbuserpbgroup00000000000000Audio settings hierarchy ======================== Service org.ofono Interface org.ofono.AudioSettings Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns all audio settings properties. See the properties section for available properties. Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. Properties boolean Active [readonly] [EXPERIMENTAL] Indicates if an audio PCM stream is active or not. This is not supported by all modems. Only modems where the audio data can be routed to the host application processor will support this. string Mode [readonly, optional] [EXPERIMENTAL] Indicates the audio mode setting. This is highly modem specific audio string. Every modem might use different ones. ofono-1.17.bzr6912+16.04.20160314.3/doc/text-telephony-api.txt0000644000015600001650000000213112671500024023442 0ustar pbuserpbgroup00000000000000Text Telephony hierarchy ======================== Service org.ofono Interface org.ofono.TextTelephony Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns all Text Telephony properties. See the properties section for available properties. Possible Errors: [service].Error.InProgress [service].Error.Failed void SetProperty(string name, variant value) Changes the value of the specified property. Only properties that are listed as read-write are changeable. On success a PropertyChanged signal will be emitted. Possible Errors: [service].Error.InvalidArguments [service].Error.InProgress [service].Error.Failed Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. Properties boolean Enabled [readwrite] This property will enable or disable the text telephony feature in the modem. Text telephony (TTY), also known as Cellular Text Modem (CTM), is a feature present in some modems that allow them to be used by hearing impaired people. ofono-1.17.bzr6912+16.04.20160314.3/doc/cdma-voicecall-manager-api.txt0000644000015600001650000000707112671500024024734 0ustar pbuserpbgroup00000000000000CDMA VoiceCallManager hierarchy [experimental] =============================== Service org.ofono Interface org.ofono.cdma.VoiceCallManager Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns properties for the VoiceCallManager Interface. See the properties section for available properties. void Dial(string number) Initiates a new outgoing call. This is usually implemented using the ATD AT command. Possible Errors: [service].Error.InProgress [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.NotImplemented [service].Error.Failed void Hangup() Hangup all active calls. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed void Answer() Answer the incoming call. This only affects the incoming call. void SendFlash(string flash_string) Sends the flash string to the network. void SendTones(string tones) Sends the DTMF tones to the network. The tones have a fixed duration. Tones can be one of: '0' - '9', '*', '#', 'A', 'B', 'C', 'D'. The last four are typically not used in normal circumstances. 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.InvalidArguments [service].Error.DoesNotExist Signals PropertyChanged(string property, variant value) Signal is emitted whenever a property has changed. The new value is passed as the signal argument. DisconnectReason(string reason) This signal is emitted when the modem manager can provide extra information about why the call was released. The possible reason values are: "local" - The call was release due to local user action "remote" - Remote party released the call "network" - Network released the call, most likely due to low signal or other network failure Not all implementations are able to provide this information, so applications should treat the emission of this signal as optional. This signal will be emitted before the PropertyChanged signal. Properties string State [readonly] Contains the state of the current call. The state can be one of: - "active" - The call is active - "dialing" - The call is being dialed - "alerting" - The remote party is being alerted - "incoming" - Incoming call in progress - "disconnected" - No call is connected boolean CallWaiting [readonly] Contains whether a call is waiting. string CallWaitingNumber [readonly, optional] Contains the call waiting number. string ToneDuration [readwrite] Contains the length of the DTMF tone duration. The currently supported values are: "short", "long" string LineIdentification [readonly] Contains the Line Identification information returned by the network, if present. For incoming calls this is effectively the CLIP. For outgoing calls this attribute will hold the dialed number. Please note that after sending flash this property will be empty. string StartTime [readonly, optional] Contains the starting time of the call. The time is stamped when the call enters the "active" state. Client applications can use this to infer somewhat reliable call duration information. Please note that after sending Flash, there will not be any StartTime property change for the Flash string/number. ofono-1.17.bzr6912+16.04.20160314.3/doc/pushnotification-api.txt0000644000015600001650000000226412671500024024046 0ustar pbuserpbgroup00000000000000Push Notification hierarchy =============== Service org.ofono Interface org.ofono.PushNotification Object path [variable prefix]/{modem0,modem1,...} Methods void RegisterAgent(object path) Registers an agent which will be called whenever a new Smart Messaging based SMS arrives. Possible Errors: [service].Error.InProgress [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void UnregisterAgent(object path) Unregisters an agent. Possible Errors: [service].Error.InvalidArguments [service].Error.Failed PushNotificationAgent Hierarchy [experimental] =============== Service unique name Interface org.ofono.PushNotificationAgent Object path freely definable Methods void ReceiveNotification(array{byte} notification, dict info) Requests the agent to process a new SMS that has arrived containing a WAP PUSH. The dictionary info contains 'Sender', 'LocalSentTime' and 'SentTime' properties. Possible Errors: None void Release() [noreply] Agent is being released, possibly because of oFono terminating, SMS interface is being torn down or modem off. No UnregisterAgent call is needed. ofono-1.17.bzr6912+16.04.20160314.3/doc/overview.txt0000644000015600001650000001466112671500024021563 0ustar pbuserpbgroup00000000000000oFono - Open Source Telephony ***************************** Copyright (C) 2008-2011 Intel Corporation. All rights reserved. Mission statement ================= The overall goal of the oFono project is to create a telephony host stack for embedded/mobile and desktop systems based on Linux. It currently targets GSM/UMTS User Equipment (UE) based on 3GPP standards, but is of course not limited to that. Extensions for other telephony systems like CDMA/EVDO are more than welcome. Within oFono there is clear abstraction between the application interfaces based on D-Bus, the hardware level (via drivers) and the integration with other system components (plugins). The whole architecture is modular and flexible. Telephony features ================== The oFono stack will support the majority of the features from the 3GPP specification, but not all of them. The whole standard is pretty complex and some features are not used in any mobile network so far, some of them are outdated and have no relevance anymore. This paragraph tries to give some insights on what has been implemented so far, what is coming in the future and especially what will not be part of oFono. Current implemented features: Modem abstraction Currently there are two modem drivers available. The "atmodem" driver handles hardware based on the 3GPP TS 27.007 standard. The "isimodem" driver handles Nokia based PhoNet modems. Network registration The network registration interface contains support for network detection. It also handles the automatic or manual registration to a mobile network. Additional information about the current network can be easily retrieved and displayed to the user. Network time indications oFono includes support for receiving Network Identity and Timezone (NITZ) indications, and handles processing of this information via system-specific plugins. An example network time plugin is provided that simply prints out the received time information. A more advanced, real-world plugin could automatically set the system time based on the received information. Voice call handling The voice call interface handles simple call creation and termination. It also supports 3way-calling and multi-party features. This is only for voice control. It doesn't contain support for the voice data path/routing. Advanced voice call control Features like COLR/CLIR/CLIP/COLP are supported by the voice call handling interface. The support for call forwarding and call waiting is also present. Interfaces for call barring and advice of charge do exist, but highly depend on if the operator supports them. Call history The call history is realized via a plugin interface and not handled directly. This allows an easy integration with storage systems like Evolution-Data-Server. Phonebook support The storage of the SIM card is not used. The only supported feature is to export the contacts stored on the SIM card to some third-part entity. SIM card storage is limited, slow and not flexible enough for modem telephony applications. Short message service The SMS support for text messages is available. oFono supports concatenated messages of up to 255 segments, UCS2 and GSM alphabets as well as extended alphabets. Delivery confirmations (Status Reports) are also supported. Selection of SMS bearer settings is supported through the 'Bearer' property on the SmsManager interface. SIM PIN handling SIM PIN locks, network and service provider locks are supported. The SIM Manager also handles retry counter for PIN, PIN2, PUK and PUK2. Cell Broadcast Cell broadcasts should be fully supported by oFono, but have not been well tested. Base station name ids have been confirmed to work. GPRS GPRS data connections are fully supported by oFono. Multiple active GPRS contexts are supported. IPv6 context support is in progress. Radio Access Settings The radio settings interface contains support for selecting the access selection mode and used frequency bands, and allows enabling and disabling fast dormancy on hardware that support this feature. Sim Toolkit The Sim Toolkit interface handles the SAT commands. Sim Toolkit relies on the SimToolkit agent not only to get confirmation from the user but also to inform user of the SAT initiated operation status. Information on the Sim Toolkit feature list can be found in features.txt. Supplementary Services The Supplementary Services interface handles both recognized supplementary service control string and user/network initiated unstructured supplementary service data (USSD). GPS/Location Services oFono provides a Location Reporting interface that enables taking advantage of on-board GPS capabilities of modern modems. oFono also provides an Assisted Satellite Navigation interface that allows feeding assistance data to the GPS unit from the network as well as E911 services. Work in progress features: GPRS IPv6 PS context support is under development. Modem Emulator To enable DialUp Networking (over Bluetooth or USB), and to allow Bluetooth HandsFree / Headset support, oFono will expose some 'fake' modem to enable the communication with car kits, or other devices. Not implemented features: SIM card storage The SIM card storage will not be used by oFono. It is slow, limited in size and flexibility. It is an outdated interface that makes no sense in a modern telephony system. Export of stored contacts from the SIM card is supported for legacy reasons and to allow a smooth transition. WAP support The WAP feature is outdated and using full Internet access with an embedded browser is the future. MMS support The MMS support should not be part of oFono itself. The best idea is to implement a MMS service that runs in the user session and uses oFono's SMS interface to listen for notifications. Similar to the split between BlueZ and OBEX daemon. Especially when it comes to image conversion and other tasks that MMS support requires it is important to NOT do this as a system daemon. EMS support This is an Ericsson specific standard and not widely spread across the handset manufactures. Video telephony Currently there are no plans to support this. The support from the networks and available handsets are still limited. This needs re-evaluation once such a service becomes more prominent. A similar split like with MMS might be a good idea. ofono-1.17.bzr6912+16.04.20160314.3/doc/emergency-call-handling.txt0000644000015600001650000000754112671500024024365 0ustar pbuserpbgroup00000000000000This document explains what is expected from applications and what oFono will do for an emergency call request in different states. Case 1: Call in offline and SIM present state Expected from UI/applications: - Online property of org.ofono.Modem interface should be set to TRUE. - Dial method should be called with the dialled number. What oFono will do: - Modem will be set to online. - Post online atoms will be created. - Upon reception of Dial request, Emergency mode is activated. - Once the call is ended, Emergency mode is deactivated. - Modem remains in online mode with full funcationality. Case 2: Call in SIM Present and PIN required state Expected from UI/applications: - If the user enters emergency number in the PIN entry dialog, then Online property on org.ofono.Modem interface should be set to TRUE. - List of Emergency numbers can be known from the EmergencyNumbers property on the org.ofono.VoiceCallManager interface. - Dial method should be called with the dialled number What oFono will do: - Modem will be set to online. - Upon reception of Dial request, Emergency mode is activated. - Once the call is ended, Emergency mode is deactivated. - Modem remains in online mode but the functionalities will be limited. Case 3: Call in SIM Present and PIN required state - Dial cancelled by user Expected from UI/applications: - If the user enters emergency number in the PIN entry dialog, then Online property on org.ofono.Modem interface should be set to TRUE. - List of Emergency numbers can be known from the EmergencyNumbers property on the org.ofono.VoiceCallManager interface. - Dial method should be called with the dialled number - Upon dial cancellation by user, HangupAll should be called. What oFono will do: - Modem will be set to online. - Post SIM and Post online atoms are not created. - Upon reception of Dial request, Emergency mode is activated. - Upon dial cancellation(HangupAll request), Emergency mode is deactivated. - Modem remains in online mode but the functionalities will be limited. Case 4: Call in No SIM state Expected from UI/applications: - Online property on org.ofono.Modem interface should be set to TRUE. - Dial method should be called with the dialled number What oFono will do: - Modem will be set to online. - Post SIM and Post online atoms not created. - If the dialed number is an Emergeny number, Emergency mode is activated. - Once the call is ended, Emergency mode is deactivated. - Modem remains in online mode but the functionalities will be limited. Case 5: Call in No SIM state - PIN disabled SIM inserted during emergency call Expected from UI/applications: - Online property on org.ofono.Modem interface should be set to TRUE. - Dial method should be called with the dialled number What oFono will do: - Modem will be set to online. - Post SIM and Post online atoms are not created. - If the dialed number is an Emergeny number, Emergency mode is activated. - Upon PIN disabled SIM detection, Post SIM and Post online atoms are created. - Once the call is ended, Emergency mode is deactivated. - Modem remains in online mode with full functionality. Case 6: Call in No SIM state - PIN enabled SIM inserted during emergency call Expected from UI/applications: - Online property on org.ofono.Modem interface should be set to TRUE. - Dial method should be called with the dialled number What oFono will do: - Modem will be set to online. - Post SIM and Post online atoms are not created. - If the dialed number is an Emergeny number, Emergency mode is activated. - Upon PIN enabled SIM detection, applications will be informed of the pin status via PinRequired property on the org.ofono.SimManager interface. - Once the call is ended, Emergency mode is deactivated. - Modem remains in online mode but the functionalities will be limited. ofono-1.17.bzr6912+16.04.20160314.3/doc/location-reporting-api.txt0000644000015600001650000000174712671500024024304 0ustar pbuserpbgroup00000000000000Location Reporting Hierarchy [experimental] ================= Service org.ofono Interface org.ofono.LocationReporting Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns all LocationReporting properties. See the properties section for available properties. filedescriptor Request() Asks to turn ON the NMEA stream and supplies the gps device file descriptor. The external cliend should use the file descriptor to receive the NMEA data. Possible Errors: [service].Error.InProgress [service].Error.InUse [service].Error.Failed void Release() Releases the gps device file descriptor and turns OFF the NMEA stream. Possible Errors: [service].Error.InProgress [service].Error.NotAvailable [service].Error.Failed Properties boolean Enabled [readonly] Boolean representing the state of the NMEA stream. string Type [readonly] Holds the type of the device. Currently only NMEA is supported. ofono-1.17.bzr6912+16.04.20160314.3/doc/messagemanager-api.txt0000644000015600001650000000701512671500024023436 0ustar pbuserpbgroup00000000000000Message Manager hierarchy =============== Service org.ofono Interface org.ofono.MessageManager Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns properties for the manager object. See the properties section for available properties. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented array{object,dict} GetMessages() Get an array of message object paths and properties that represents the currently pending messages. This method call should only be used once when an application starts up. Further message additions and removal shall be monitored via MessageAdded and MessageRemoved signals. void SetProperty(string name, 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.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed object SendMessage(string to, string text) Send the message in text to the number in to. If the message could be queued successfully, this method returns an object path to the created Message object. Possible Errors: [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given property. ImmediateMessage(string message, dict info) New immediate (class 0) SMS received. Info has Sender, LocalSentTime, and SentTime information. Sender address is given in string format. LocalSentTime and SentTime are given in string form using ISO8601 format. IncomingMessage(string message, dict info) New incoming text SMS received. Info has Sender, LocalSentTime, and SentTime information. MessageAdded(object path, dict properties) This signal is emitted whenever a new Message object has been created. MessageRemoved(object path) This signal is emitted whenever a Message object has been removed, e.g. when it reaches a final state. StatusReport(object path, boolean delivered) This signal is emitted whenever a SMS Status Report is received for a previously sent message (note that the Message object already has been removed at this point). Possible 'delivered' values are: true - successfully delivered false - delivery failed Properties string ServiceCenterAddress Contains the number of the SMS service center. boolean UseDeliveryReports This property controls whether SMS Status Reports, sometimes better known as Delivery Reports are to be used. If enabled, all outgoing SMS messages will be flagged to request a status report from the SMSC. string Bearer Contains the bearer to use for SMS messages. Possible values are: "cs-only" - Circuit Switched only "ps-only" - Packet Domain only "cs-preferred" - Use PS if CS is unavailable "ps-preferred" - Use CS if PS is unavailable By default oFono uses "cs-preferred" setting. string Alphabet Contains the alphabet setting for outgoing SMSs. Possible values are: "default" - Default GSM alphabet "turkish" - Turkish alphabet "spanish" - Spanish alphabet "portuguese" - Portuguese alphabet The standard, language-specific alphabets are defined in 3GPP TS23.038, Annex A. By default, oFono uses the "default" setting. ofono-1.17.bzr6912+16.04.20160314.3/doc/cdma-messagemanager-api.txt0000644000015600001650000000600512671500024024336 0ustar pbuserpbgroup00000000000000CDMA Message Manager hierarchy [experimental] ============================== Service org.ofono Interface org.ofono.cdma.MessageManager Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns properties for the manager object. See the properties section for available properties. Possible Errors: [service].Error.InvalidArguments array{object,dict} GetMessages() Get an array of message object paths and properties that represents the currently pending messages. This method call should only be used once when an application starts up. Further message additions and removal shall be monitored via MessageAdded and MessageRemoved signals. void SetProperty(string name, 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.InvalidArguments [service].Error.DoesNotExist object SendMessage(dict message_info) The dictionary can contain the following keys: string "To" - Address of the receiver string "Text" - The text to send string "Priority" - The value can be one of: "normal", "interactive", "urgent", "emergency", TODO: Figure out where this is really needed string "Privacy" - The value can be one of: "not restricted", "restricted", "confidential", "secret" TODO: Figure out where this is really needed If the message could be queued successfully, this method returns an object path to the created Message object. Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given property. ImmediateMessage(string message, dict info) New immediate SMS received. Info has Sender, LocalSentTime, SentTime, Priority, Privacy and CallbackNumber information. Sender address is given in string format. LocalSentTime and SentTime are given in string form using ISO8601 format. IncomingMessage(string message, dict info) New incoming text SMS received. Info has Sender, LocalSentTime, SentTime, Priority, Privacy, and CallbackNumber. MessageAdded(object path, dict properties) This signal is emitted whenever a new Message object has been created. MessageRemoved(object path) This signal is emitted whenever a Message object has been removed, e.g. when it reaches a final state. Properties boolean UseDeliveryAcknowledgement Request to be notified when the SMSC has delivered the message to the receiving user. In effect this is the same as the GSM Status Report. boolean UseUserAcknowledgement Request to be notified when the receiving user has acknowledged the message. boolean UseReadAcknowledgement Request to be notified when the receiving User has read the message. string CallbackNumber The call back number for the user. If the number is empty, then the optional field is not included in the encoded PDU. ofono-1.17.bzr6912+16.04.20160314.3/doc/ofonod.80000644000015600001650000000210612671500024020520 0ustar pbuserpbgroup00000000000000.\" .\" ofonod(8) .\" .\" Copyright (C) 2009 Collabora Ltd. .TH ofonod 8 "Jul 2009" .SH NAME ofonod \- oFono mobile telephony daemon .SH SYNOPSIS .B "ofonod [options]" .SH DESCRIPTION .B ofonod is a daemon which provides an oFono stack for interfacing mobile telephony devices. oFono is controlled through \fID-Bus\fP; for example, one can tell .B ofonod to send AT commands over /dev/rfcomm0 by calling the \fID-Bus\fP method org.ofono.at.Manager.Create. .I "/etc/dbus-1/system.d/ofono.conf" is used to manage \fID-Bus\fP permissions for oFono. .SH OPTIONS .TP .B --debug, -d Enable debug information output. Note multiple arguments to \-d can be specified, colon, comma or space separated. The arguments are relative source code filenames for which debugging output should be enabled; output shell-style globs are accepted (e.g.: "plugins/*:src/main.c"). .TP .B --nodetach, -n Don't run as daemon in background. .TP .SH SEE ALSO .PP \&\fIdbus-send\fR\|(1) .SH FILES .BR /etc/dbus-1/system.d/ofono.conf .SH AUTHOR .br This man page was written by Andres Salomon . ofono-1.17.bzr6912+16.04.20160314.3/doc/sim900-modem.txt0000644000015600001650000000062312671500024022026 0ustar pbuserpbgroup00000000000000SIM900 modem usage =================== To enable SIM900 module support you need to put the following udev rule into appropriate file in /{etc,lib}/udev/rules.d: KERNEL=="gsmtty3", ENV{OFONO_DRIVER}="sim900" On the i-Tetra tracking device, the SIM900 is accessed via N_GSM mux device. We use ofono as SMS message service and incoming voice calls service, so we use /dev/gsmtty1 provided by N_GSM mux. ofono-1.17.bzr6912+16.04.20160314.3/doc/coding-style.txt0000644000015600001650000002017112671500024022307 0ustar pbuserpbgroup00000000000000Every project has its coding style, and oFono is not an exception. This document describes the preferred coding style for oFono code, in order to keep some level of consistency among developers so that code can be easily understood and maintained, and also to help your code survive under maintainer's fastidious eyes so that you can get a passport for your patch ASAP. First of all, oFono coding style must follow every rule for Linux kernel (http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool named checkpatch.pl to help you check the compliance with it. Just type "checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need to clean up all the warnings and errors except this one: "ERROR: Missing Signed-off-by: line(s)". oFono does not used Signed-Off lines, so including them is actually an error. In certain circumstances one can ignore the 80 character per line limit. This is generally only allowed if the alternative would make the code even less readable. 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 ============================================================ There should be a blank line before if statement unless the if is nested and not preceded by an expression or variable declaration. Example: 1) a = 1; if (b) { // wrong 2) a = 1 if (b) { } a = 2; // wrong 3) if (a) { if (b) // correct 4) b = 2; if (a) { // correct } b = 3; The only exception to this rule applies when a variable is being allocated: array = g_try_new0(int, 20); if (array == NULL) // Correct return; M2: Multiple line comment ========================= If your comments have more then one line, please start it from the second line. Example: /* * first line comment // correct * ... * last line comment */ M3: Space before and after operator =================================== There should be a space before and after each operator. Example: a + b; // correct M4: Wrap long lines =================== If your condition in if, while, for statement or a function declaration is too long to fit in one line, the new line needs to be indented not aligned with the body. Example: 1) if (call->status == CALL_STATUS_ACTIVE || call->status == CALL_STATUS_HELD) { // wrong ofono_dbus_dict_append(); 2) if (call->status == CALL_STATUS_ACTIVE || call->status == CALL_STATUS_HELD) { // correct ofono_dbus_dict_append(); 3) gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len, num sim_ust_service index) // wrong { int a; ... } 4) gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len, enum sim_ust_service index) // correct { int a; ... } If the line being wrapped is a function call or function declaration, the preferred style is to indent at least past the opening parenthesis. Indenting further is acceptable as well (as long as you don't hit the 80 character limit). If this is not possible due to hitting the 80 character limit, then indenting as far as possible to the right without hitting the limit is preferred. Example: 1) gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len, enum sim_ust_service index); // worse 2) gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len, enum sim_ust_service index); // better M5: Git commit message 50/72 formatting ======================================= The commit message header should be within 50 characters. And if you have detailed explanatory text, wrap it to 72 character. M6: Space when doing type casting ================================= There should be a space between new type and variable. Example: 1) a = (int *)b; // wrong 2) a = (int *) b; // correct M7: Don't initialize variable unnecessarily =========================================== When declaring a variable, try not to initialize it unless necessary. Example: int i = 1; // wrong for (i = 0; i < 3; i++) { } M8: Use g_try_malloc instead of g_malloc ======================================== When g_malloc fails, the whole program would exit. Most of time, this is not the expected behavior, and you may want to use g_try_malloc instead. Example: additional = g_try_malloc(len - 1); // correct if (additional == NULL) return FALSE; M9: Follow the order of include header elements =============================================== When writing an include header the various elements should be in the following order: - #includes - forward declarations - #defines - enums - typedefs - function declarations and inline function definitions M10: Internal headers must not use include guards ================================================= Any time when creating a new header file with non-public API, that header must not contain include guards. M11: Naming of enums ==================== Enums must have a descriptive name. The enum type should be small caps and it should not be typedef-ed. Enum contents should be in CAPITAL letters and prefixed by the enum type name. Example: enum animal_type { ANIMAL_TYPE_FOUR_LEGS, ANIMAL_TYPE_EIGHT_LEGS, ANIMAL_TYPE_TWO_LEGS, }; If the enum contents have values (e.g. from specification) the formatting should be as follows: enum animal_type { ANIMAL_TYPE_FOUR_LEGS = 4, ANIMAL_TYPE_EIGHT_LEGS = 8, ANIMAL_TYPE_TWO_LEGS = 2, }; M12: Enum as switch variable ==================== If the variable of a switch is an enum, you must not include a default in switch body. The reason for this is: If later on you modify the enum by adding a new type, and forget to change the switch accordingly, the compiler will complain the new added type hasn't been handled. Example: enum animal_type { ANIMAL_TYPE_FOUR_LEGS = 4, ANIMAL_TYPE_EIGHT_LEGS = 8, ANIMAL_TYPE_TWO_LEGS = 2, }; enum animal_type t; switch (t) { case ANIMAL_TYPE_FOUR_LEGS: ... break; case ANIMAL_TYPE_EIGHT_LEGS: ... break; case ANIMAL_TYPE_TWO_LEGS: ... break; default: // wrong break; } However if the enum comes from an external header file outside ofono we cannot make any assumption of how the enum is defined and this rule might not apply. M13: Check for pointer being NULL ================================= When checking if a pointer or a return value is NULL, explicitly compare to NULL rather than use the shorter check with "!" operator. Example: 1) array = g_try_new0(int, 20); if (!array) // Wrong return; 2) if (!g_at_chat_get_slave(chat)) // Wrong return -EINVAL; 3) array = g_try_new0(int, 20); if (array == NULL) // Correct return; M14: Always use parenthesis with sizeof ======================================= The expression argument to the sizeof operator should always be in parenthesis, too. Example: 1) memset(stuff, 0, sizeof(*stuff)); 2) memset(stuff, 0, sizeof *stuff); // Wrong M15: Use void if function has no parameters =========================================================== A function with no parameters must use void in the parameter list. Example: 1) void foo(void) { } 2) void foo() // Wrong { } M16: Don't use hex value with shift operators ============================================== The expression argument to the shift operators should not be in hex. Example: 1) 1 << y 2) 0x1 << y // Wrong O1: Shorten the name ==================== Better to use abbreviation, rather than full name, to name a variable, function, struct, etc. Example: supplementary_service // too long ss // better O2: Try to avoid complex if body ================================ It's better not to have a complicated statement for if. You may judge its contrary condition and return | break | continue | goto ASAP. Example: 1) if (a) { // worse struct voicecall *v; call = synthesize_outgoing_call(vc, vc->pending); v = voicecall_create(vc, call); v->detect_time = time(NULL); DBG("Registering new call: %d", call->id); voicecall_dbus_register(v); } else return; 2) if (!a) return; struct voicecall *v; call = synthesize_outgoing_call(vc, vc->pending); v = voicecall_create(vc, call); v->detect_time = time(NULL); DBG("Registering new call: %d", call->id); voicecall_dbus_register(v); ofono-1.17.bzr6912+16.04.20160314.3/doc/sim-api.txt0000644000015600001650000001365412671500024021255 0ustar pbuserpbgroup00000000000000SimManager hierarchy =============== Service org.ofono Interface org.ofono.SimManager Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns SIM properties for the modem object. See the properties section for available properties. void ChangePin(string type, string oldpin, string newpin) Changes the pin given by string type. Possible Errors: [service].Error.NotImplemented [service].Error.InProgress [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void EnterPin(string type, string pin) Enters the currently pending pin. The type value must match the pin type being asked in the PinRequired property. Possible Errors: [service].Error.NotImplemented [service].Error.InProgress [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void ResetPin(string type, string puk, string newpin) Provides the unblock key to the modem and if correct resets the pin to the new value of newpin. Possible Errors: [service].Error.NotImplemented [service].Error.InProgress [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void LockPin(string type, string pin) Activates the lock for the particular pin type. The device will ask for a PIN automatically next time the device is turned on or the SIM is removed and re-inserted. The current PIN is required for the operation to succeed. Possible Errors: [service].Error.NotImplemented [service].Error.InProgress [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void UnlockPin(string type, string pin) Deactivates the lock for the particular pin type. The current PIN is required for the operation to succeed. Possible Errors: [service].Error.NotImplemented [service].Error.InProgress [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed array{byte} GetIcon(byte id) Obtain the icon given by id. Only ids greater than 1 are valid. XPM format is currently used to return the icon data. Possible Errors: [service].Error.NotImplemented [service].Error.InProgress [service].Error.InvalidArguments [service].Error.Failed Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given property. Properties boolean Present [readonly] True if a SIM card is detected. There are no other properties if false. string SubscriberIdentity [readonly, optional] Contains the IMSI of the SIM, if available. string MobileCountryCode [readonly, optional] Contains the Mobile Country Code (MCC) of the home network (not to be confused with the currently registered network reported on NetworkRegistration interface) and is read directly from the SIM if available. string MobileNetworkCode [readonly, optional] Contains the Mobile Network Code (MNC) of the home network (not to be confused with the currently registered network reported on NetworkRegistration interface) and is read directly from the SIM if available. array{string} SubscriberNumbers [readwrite] Contains the list of subscriber numbers. This is usually stored in the EFmsisdn sim file. array{string} PreferredLanguages [readonly, optional] Contains the list of preferred languages from the SIM, if available. dict{string,string} ServiceNumbers [readonly, optional] Contains a dictionary of service dialing numbers from the SIM, if available. string PinRequired [readonly] Contains the string type of the pin required by the modem. The possible values are: "none" - Nothing is required "pin" - SIM PIN is required "phone" - Phone-to-SIM PIN is required "firstphone" - Phone-to-very-first SIM PIN is required "pin2" - SIM PIN2 is required "network" - Network Personalization password is required "netsub" - Network subset personalization password is required "service" - Service Provider personalization password is required "corp" - Corporate personalization password is required "puk" - SIM PUK is required "firstphonepuk" - Phone-to-very-first SIM PUK is required "puk2" - SIM PUK2 is required "networkpuk" - Network personalization unblocking password is required "netsubpuk" - Network subset personalization unblocking password is required "servicepuk" - Service provider personalization unblocking password is required "corppuk" - Corporate personalization unblocking password is required array{string} LockedPins [readonly] Contains the pins that are currently locked and will require the user to enter the password at startup. Using LockPin and UnlockPin will result in changes to this property. The list contains elements of the same format as the PinRequired property. string CardIdentifier [readonly] Contains the Integrated Circuit Card Identifer (ICCID) which is read directly from the SIM. boolean FixedDialing [readonly] True if Fixed Dialing service is enabled in SIM card. If FDN is enabled, oFono halts the SIM initialization procedure and only emergency calls are allowed. boolean BarredDialing [readonly] True if Barred Dialing service is enabled in SIM card. If BDN is enabled, oFono halts the SIM initialization procedure and only emergency calls are allowed. dict{string,byte} Retries [readonly] Contains all the retry counters available. The possible values for the first field are the same as in PinRequired property. The second field contains is the counter for that pin type. This property is updated after each operation that might have changed the retry counters, i.e. calls to ChangePin(), EnterPin(), ResetPin() LockPin(), UnlockPin(). ofono-1.17.bzr6912+16.04.20160314.3/doc/network-api.txt0000644000015600001650000001413612671500024022152 0ustar pbuserpbgroup00000000000000Network registration hierarchy ============================== Service org.ofono Interface org.ofono.NetworkRegistration Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns all network registration properties. See the properties section for available properties. void Register() Attempts to register to the default network. The default network is normally selected by the settings from the SIM card. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed [service].Error.AccessDenied array{object,dict} GetOperators() Retrieve array of operator object and properties. This method can be used to retrieve the current operator list. This is either an empty list (when not registered to any network) or a list with one or more operators (when registered). This list will also return cached values of previously seen networks. Manual updates to list can only be done via the Scan method call. array{object,dict} Scan() Runs a network operator scan to discover the currently available operators. This operation can take several seconds, and up to several minutes on some modems. This can be used to help the user determine what is the best operator to use if forced to roam on a foreign network. NOTE: The operator scan can interfere with any active GPRS contexts. Expect the context to be unavailable for the duration of the operator scan. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed [service].Error.AccessDenied Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. Properties string Mode [readonly] The current registration mode. The default of this is "auto", but can be changed during operation. This property will change to "manual" if the Register() method of an operator is called. The possible values are: "auto" Network registration is performed automatically. "auto-only" Network registration is performed automatically, and manual selection is disabled. "manual" Network operator is selected manually. If the operator is currently not selected, registration is not attempted. string Status [readonly] The current registration status of a modem. The possible values are: "unregistered" Not registered to any network "registered" Registered to home network "searching" Not registered, but searching "denied" Registration has been denied "unknown" Status is unknown "roaming" Registered, but roaming uint16 LocationAreaCode [readonly, optional] Contains the current location area code. TODO: Agent based location signalling would be better. uint32 CellId [readonly, optional] Contains the current network cell id. TODO: Agent based location signalling would be better. string MobileCountryCode [readonly, optional] Contains the Mobile Country Code (MCC). This is repeated here for convenience. It can also be obtained from the NetworkOperator interface. string MobileNetworkCode [readonly, optional] Contains the Mobile Network Code (MNC). This is repeated here for convenience. It can also be obtained from the NetworkOperator interface. string Technology [readonly, optional] Contains the technology of the current network. The possible values are: "gsm", "edge", "umts", "hspa", "lte" string Name [readonly] Contains the current operator name, suitable for display on the idle screen or an empty string if not registered to a network. byte Strength [readonly, optional] Contains the current signal strength as a percentage between 0-100 percent. string BaseStation [readonly, optional] If the Cell Broadcast service is available and properly configured, this attribute will contain the name of the current service cell. This is typically a descriptive name of the local area, e.g. name of the suburb. If no name is provided or becomes unavailable, this property will not be returned by GetProperties or will be set to an empty string. Network operator hierarchy ========================== Service org.ofono Interface org.ofono.NetworkOperator Object path [variable prefix]/{modem0,modem1,...}/{operator0,operator1,...} Methods dict GetProperties() Returns all network operator properties. See the properties section for available properties. void Register() Attempts to register to this network operator. The method will return immediately, the result should be observed by tracking the NetworkRegistration Status property. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed [service].Error.AccessDenied Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. Properties string Name [readonly] Contains the name of the operator, suitable for using as a string handle in a UI widget used for manual network selection. For a proper display name of the current operator, use the Name property of the NetworkRegistration interface instead. string Status [readonly] Contains the status of the operator. The possible values are: "unknown", "available", "current" and "forbidden" string MobileCountryCode [readonly, optional] Contains the Mobile Country Code (MCC). string MobileNetworkCode [readonly, optional] Contains the Mobile Network Code (MNC) array{string} Technologies [readonly, optional] Contains a list of technologies currently available from this network provider. The possible values are: "gsm", "edge", "umts", "hspa", "lte" string AdditionalInformation [readonly, optional] Contains a short description of the operator. This is typically stored on the SIM and might be available only for select operators. ofono-1.17.bzr6912+16.04.20160314.3/doc/modem-api.txt0000644000015600001650000001223712671500024021562 0ustar pbuserpbgroup00000000000000Modem hierarchy =============== Service org.ofono Interface org.ofono.Modem Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns properties for the modem object. See the properties section for available 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.NotImplemented [service].Error.InvalidArguments [service].Error.NotAvailable [service].Error.AccessDenied [service].Error.Failed Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given property. Properties boolean Powered [readwrite] Boolean representing the power state of the modem device. boolean Online [readwrite] Boolean representing the rf state of the modem. Online is false in flight mode. boolean Lockdown [readwrite] Boolean representing the lock state of the modem. Setting it to true, makes the calling application hold the modem lock and power it down. Setting to false makes the it release the modem lock. Only the application that holds the lock can power up the modem. If the the application exits Lockdown is set to false. boolean Emergency [readonly, optional, experimental] Boolean representing the emergency mode of the modem. The Emergency is true if an emergency call or related operation is currently active. string Name [readonly, optional] Friendly name of the modem device. In the case of Bluetooth devices (e.g. Type="sap" or "hfp") this corresponds to the remote device name or it's alias. string Manufacturer [readonly, optional] String representing the manufacturer of the modem device. This is usually obtained by the +CGMI AT command. string Model [readonly, optional] String representing the model of the modem device. This is usually obtained by the +CGMM AT command. string Revision [readonly, optional] String representing the revision of the modem device. This is usually obtained by using the +CGMR AT command. string Serial [readonly, optional] String represeting the serial number of the modem device. This is usually obtained by using the +CGSN AT command. In the case of Bluetooth devices (e.g. Type="sap" or "hfp") this corresponds to the Bluetooth Device Address of the remote device. array{string} Features [readonly] List of currently enabled features. It uses simple string abbreviations like "sms", "sim" etc. Currently available features are: "net", "rat", "cbs", "sms", "sim", "stk", "ussd", "gprs", "tty", "gps". Please note that this is a convenience property, equivalent results can be obtained by reading the Interfaces property directly. array{string} Interfaces [readonly] Set of interfaces currently supported by the mode device. The set depends on the state of the device (registration status, SIM inserted status, network capabilities, device capabilities, etc.) Each string in the array is an interface from the set supported by oFono by modem objects. The set includes: org.ofono.AssistedSatelliteNavigation org.ofono.AudioSettings org.ofono.CallBarring org.ofono.CallForwarding org.ofono.CallMeter org.ofono.CallSettings org.ofono.CallVolume org.ofono.CellBroadcast org.ofono.Handsfree org.ofono.LocationReporting org.ofono.MessageManager org.ofono.MessageWaiting org.ofono.NetworkRegistration org.ofono.Phonebook org.ofono.PushNotification org.ofono.RadioSettings org.ofono.SimManager org.ofono.SmartMessaging org.ofono.SimToolkit org.ofono.SupplementaryServices org.ofono.TextTelephony org.ofono.VoiceCallManager It is possible for extension interfaces (e.g. APIs that are not part of the oFono standard API) to be available in this list. Also note that child object interfaces, such as org.ofono.Message, org.ofono.VoiceCall, org.ofono.NetworkOperator, will never be part of this list. Please note that the set of Interfaces can and does change frequently (e.g. due to change in Powered and Online properties.) If a given interface is no longer available, then the application should assume that all data for that interface has become invalid, e.g. calls have been dropped, network registration lost, etc. The set of possible interfaces supported is also dependent on the modem hardware and driver support. For example, HFP devices only support org.ofono.VoiceCallManager, org.ofono.NetworkRegistration, org.ofono.Handsfree and org.ofono.CallVolume interfaces. string Type [readonly] Indicates whether the modem is virtual or a real hardware one. This information should only be used to identify which componet (e.g. ConnMan or a phone dialer) should take control over the modem. It does not give any hints on which Interfaces will be supported by this modem. Possible values are "test", "hfp", "sap" and "hardware". ofono-1.17.bzr6912+16.04.20160314.3/doc/calypso-modem.txt0000644000015600001650000000135712671500024022464 0ustar pbuserpbgroup00000000000000Calypso modem usage =================== On the Openmoko Freerunner phone, the Calypso modem is presented by the kernel as serial device /dev/ttySAC0. To allow oFono to autodetect and use this, a simple udev rule is needed: KERNEL=="ttySAC0", ENV{OFONO_DRIVER}="calypso" You can put this in any file in /lib/udev/rules.d. A logical choice is the "55-openmoko-gta01-gta02.rules" file, if it exists in your Freerunner distribution. With this rule in place: - oFono will detect the Calypso modem when it starts up, and the `list-modems' test script will show this, but will not power it up - the `enable-modem' test script can be used to power it up - a subsequent `list-modems' will show lots of information, including network registration. ofono-1.17.bzr6912+16.04.20160314.3/doc/call-meter-api.txt0000644000015600001650000000513612671500024022506 0ustar pbuserpbgroup00000000000000Call Meter hierarchy =============== Service org.ofono Interface org.ofono.CallMeter Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Contains the properties for this object. Possible Errors: [service].Error.InProgress void SetProperty(string property, variant value, string password) Sets the property to value specified in the call parameter. The last parameter is used to pass the SIM PIN2 code which may be required by the SIM. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void Reset(string password) Attempts to reset the Accumulated Call Meter. Reseting this value requires SIM PIN2, provided by the password parameter. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed Signals PropertyChanged(string property, variant value) Signal is emitted whenever a property has changed. The new value is passed as the signal argument. NearMaximumWarning() Emitted shortly before the ACM (Accumulated Call Meter) maximum values is reached. The warning is issued approximately when 30 seconds call time remains or when starting a call with less than 30 seconds remaining. Properties uint32 CallMeter [readonly] Contains the current call meter value from the ME. The values are in 24-bit range, counted in home units. uint32 AccumulatedCallMeter [readonly] Contains the Accumulated Call Meter (ACM) value from the SIM. When the AccumulatedCallMeter value reaches AccumulatedCallMeterMaximum value, no further calls can be made until the ACM value is reset. Reset is accomplished using the Reset() function. The values are in 24-bit range. uint32 AccumulatedCallMeterMaximum [readwrite] Contains the Accumulated Call Meter maximum value on reaching which, calls are prohibited. This is effectively the maximum number of home units allowed to be consumed by subscriber. According to the GSM specification, setting the value to 0, turns off this feature. The values are in 24-bit range. double PricePerUnit [readwrite] Contains price-per-unit conversion value. This information can be used to convert the home units into currency units. string Currency [readwrite] Contains three-character currency code. This information can be used to convert the home units into currency units. ofono-1.17.bzr6912+16.04.20160314.3/doc/handsfree-audio-api.txt0000644000015600001650000000563112671500024023517 0ustar pbuserpbgroup00000000000000Handsfree Audio Manager hierarchy [experimental] ================================= Service org.ofono Interface org.ofono.HandsfreeAudioManager Object path / Methods array{object,dict} GetCards() Get an array of card objects and properties that represent the currently attached devices. This method call should only be used once when an application starts up. Further device additions and removal shall be monitored via CardAdded and CardRemoved signals. void Register(object path, array{byte}) Registers a Handsfree Audio agent with a specific path (freely selectable by the audio subsystem) and list of supported codecs. Available codec identifiers: 1 CVSD 2 mSBC Possible Errors: [service].Error.InvalidArguments [service].Error.InUse void Unregister(object path) Unregisters a Handsfree Audio agent registered through the Register method. Possible Errors: [service].Error.NotFound [service].Error.InvalidArguments [service].Error.NotAllowed Signals CardAdded(object path, dict properties) Signal that is sent when a new card is added. It contains the object path of new card and its properties. CardRemoved(object path) Signal that is sent when a card has been removed. The object path is no longer accessible after this signal and only emitted for reference. Handsfree Audio Card hierarchy [experimental] ============================== Service org.ofono Interface org.ofono.HandsfreeAudioCard Object path /{device0,device1,...} Methods dict GetProperties() Returns properties for the device object. See the properties section for available properties. void Connect() Attempts to establish the SCO audio connection. The Agent NewConnection() method will be called whenever the SCO audio link has been established. If the audio connection could not be established, this method will return an error. 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 property. Properties string RemoteAddress [readonly] Bluetooth address of the remote peer. string LocalAddress [readonly] Bluetooth address of the local adapter. string Type [readonly] Type of the card. Valid values are "gateway" or "handsfree". Handsfree Audio Agent hierarchy [experimental] =============================== Service Interface org.ofono.HandsfreeAudioAgent Object Methods void NewConnection(object card, fd sco, byte codec) Notifies the handler that a new SCO connection is available. Returning an error will cause oFono to disconnect the SCO connection. void Release() Notifies the Agent that it is no longer registered to oFono. ofono-1.17.bzr6912+16.04.20160314.3/doc/call-forwarding-api.txt0000644000015600001650000000441312671500024023531 0ustar pbuserpbgroup00000000000000Call Forwarding hierarchy =============== Service org.ofono Interface org.ofono.CallForwarding Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Contains the properties for this object. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented void DisableAll(string type) Disables all call forwarding rules for type. Type can be one of: "all" or "" - Disables all rules "conditional" - Disables all conditional rules, e.g. busy, no reply and not reachable. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void SetProperty(string property, variant value) Sets the given property value to that specified in call parameter. Possible Errors: [service].Error.NotAvailable [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed Signals PropertyChanged(string property, variant value) Signal is emitted whenever a property has changed. The new value is passed as the signal argument. Properties string VoiceUnconditional [readwrite] Contains the value of the voice unconditional call forwarding property. If the value is an empty string, then this call forwarding rule is not active. Otherwise the rule is active with the string value as the phone number. string VoiceBusy [readwrite] Contains the value of the voice "Busy" call forwarding rule. string VoiceNoReply [readwrite] Contains the value of the voice "No Reply" call forwarding rule. uint16 VoiceNoReplyTimeout [readwrite] Contains the value of the voice "No Reply" timeout in seconds. The timeout can be between 1 and 30 seconds. Please note that it is not possible to set this property's value if GetProperties() has not been previously called or the VoiceNoReply property has not been set. string VoiceNotReachable [readwrite] Contains the value of the voice "Not Reachable" call forwarding rule. boolean ForwardingFlagOnSim [readonly] Boolean representing the voice unconditional call forwarding rule status. ofono-1.17.bzr6912+16.04.20160314.3/doc/cdma-network-api.txt0000644000015600001650000000410212671500024023044 0ustar pbuserpbgroup00000000000000CDMA Network registration hierarchy [experimental] =================================== Service org.ofono Interface org.ofono.cdma.NetworkRegistration Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns all network registration properties. See the properties section for available properties. Possible Errors: [service].Error.InvalidArguments void SetProperty(string name, 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.InvalidArguments [service].Error.DoesNotExist Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. Properties string Status [readonly] The current registration status of a modem. The possible values are: "unregistered" Not registered "registered" Registered to home network "roaming" Roaming byte Strength [readonly] Contains the current signal strength as a percentage between 0-100 percent. byte DataStrength [readonly] Contains the current signal strength of the High Data Rate network. This is a percentage value between 0-100 percent. uint16 SystemIdentifier [readonly, optional] Contains the system identifier of the currently selected network. uint16 NetworkIdentifier [readonly, optional] Contains the network identifier of the currently selected network. uint16 MobileCountryCode [readonly, optional] Contains the Mobile Country Code (MCC). uint16 MobileNetworkCode [readonly, optional] Contains the Mobile Network Code (MNC). string Name [readonly, optional] Contains the name of the current network. string RoamingPreference [readwrite] Contains the roaming preference used in the network selection. The possible values are: "home" Home networks only "roamonly" Roaming networks only "affiliated" Affiliated networks only "any" Any network ofono-1.17.bzr6912+16.04.20160314.3/doc/voicecallmanager-api.txt0000644000015600001650000002112012671500024023744 0ustar pbuserpbgroup00000000000000VoiceCallManager hierarchy ========================== Service org.ofono Interface org.ofono.VoiceCallManager Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns properties for the VoiceCallManager Interface. See the properties section for available properties. array{object,dict} GetCalls() Get an array of call object paths and properties that represents the currently present calls. This method call should only be used once when an application starts up. Further call additions and removal shall be monitored via CallAdded and CallRemoved signals. object Dial(string number, string hide_callerid) Initiates a new outgoing call. Returns the object path to the newly created call. The hide_callerid variable holds the CLIR override for this call. The defines values are: "" or "default" - Default (Network) CLIR mode is used "enabled" - Hides callerid, CLIR Invocation is used "disabled" - Shows callerid, CLIR Suppression is used The number must be a string in the following format: [+][0-9*#]{1,80} In other words, it must be a non-empty string optionally prefixed with a '+' followed by 1 to 80 characters. The character set can contain numbers, '*' and '#'. Besides this sanity checking no further number validation is performed. It is assumed the network will perform further validation. The optional '+' denotes an international number format. For example: +15551234567 - International format 5551234567 - National / Uknown format This method is usually implemented using the ATD AT command. NOTE: If an active call (single or multiparty) exists, then it is automatically put on hold if the dial procedure is successful. 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 on network support) and Held calls together and disconnects both calls. In effect transferring one party to the other. This procedure requires an Active and Held call and the Explicit Call Transfer (ECT) supplementary service to be active. This functionality is generally implemented by using the +CHLD=4 AT command. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed void SwapCalls() Swaps Active and Held calls. The effect of this is that all calls (0 or more including calls in a multi-party conversation) that were Active are now Held, and all calls (0 or more) that were Held are now Active. GSM specification does not allow calls to be swapped in the case where Held, Active and Waiting calls exist. Some modems implement this anyway, thus it is manufacturer specific whether this method will succeed in the case of Held, Active and Waiting calls. This functionality is generally implemented by using the +CHLD=2 AT command. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed void ReleaseAndAnswer() Releases currently active call (0 or more) and answers the currently waiting call. Please note that if the current call is a multiparty call, then all parties in the multi-party call will be released. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed void ReleaseAndSwap() Releases currently active call (0 or more) and activates any currently held calls. Please note that if the current call is a multiparty call, then all parties in the multi-party call will be released. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed void HoldAndAnswer() Puts the current call (including multi-party calls) on hold and answers the currently waiting call. Calling this function when a user already has a both Active and Held calls is invalid, since in GSM a user can have only a single Held call at a time. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed void HangupAll() Releases all calls except waiting calls. This includes multiparty calls. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed array{object} PrivateChat(object call) Places the multi-party call on hold and makes desired call active. This is used to accomplish private chat functionality. Note that if there are only two calls (three parties) in the multi-party call the result will be two regular calls, one held and one active. The Multiparty call will need to be setup again by using the CreateMultiparty method. Returns the new list of calls participating in the multiparty call. This is usually implemented using the +CHLD=2X command. Possible Errors: [service].Error.InProgress [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.NotFound [service].Error.NotImplemented [service].Error.Failed array{object} CreateMultiparty() Joins active and held calls together into a multi-party call. If one of the calls is already a multi-party call, then the other call is added to the multiparty conversation. Returns the new list of calls participating in the multiparty call. There can only be one subscriber controlled multi-party call according to the GSM specification. This is usually implemented using the +CHLD=3 AT command. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed void HangupMultiparty() Hangs up the multi-party call. All participating calls are released. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed void SendTones(string tones) Sends the DTMF tones to the network. The tones have a fixed duration. Tones can be one of: '0' - '9', '*', '#', 'A', 'B', 'C', 'D'. The last four are typically not used in normal circumstances. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed Signals CallAdded(object path, dict properties) Signal that is sent when a new call is added. It contains the object path of the new voice call and also its properties. Applications get the whole properties via this signal and don't need to call GetProperties on the voice call object. CallRemoved(object path) Signal that is sent when a voice call has been released. The object path is no longer accessible after this signal and only emitted for reference. NOTE: If the VoiceCallManager interface becomes unavailable, this signal is not guaranteed to be emitted for remaining calls. The applications are expected to monitor changes in Modem.Interfaces property. PropertyChanged(string property, variant value) Signal is emitted whenever a property has changed. The new value is passed as the signal argument. BarringActive(string type) [experimental] Signal emitted when an outgoing voice call is made and the call has been barred by the network due to the remote party's "Call Barring" Supplementary Services settings for incoming calls. In this case the type parameter in the signal set to "remote". The signal is also emitted when an outgoing voice call is made and the call has been barred by the network due to the local "Call Barring" Supplementary Services settings for outgoing calls. In this case the type parameter in the signal is set to "local". Forwarded(string type) [experimental] Signal emitted when an outgoing voice call is made and the call has been redirected to another number due to the remote party's "Call Forwarding" Supplementary Services settings. In this case the type parameter in the signal is set to "outgoing". The signal is also emitted when the incoming voice call is a redirected call due to a call forwarding operation. In this case the type parameter in the signal is set to "incoming". Properties array{string} EmergencyNumbers [readonly] Contains the list of emergency numbers recognized by oFono. This list is based on the default set of numbers provided by the specification and any extra numbers provisioned by the carrier on the SIM. ofono-1.17.bzr6912+16.04.20160314.3/doc/message-waiting-api.txt0000644000015600001650000000271612671500024023546 0ustar pbuserpbgroup00000000000000MessageWaiting hierarchy =============== Service org.ofono Interface org.ofono.MessageWaiting Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns properties for the MessageWaiting object. See the properties section for available 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.InvalidArguments [service].Error.InvalidFormat [service].Error.NotSupported [service].Error.SimNotReady [service].Error.Failed Signals PropertyChanged(string name, variant value) This signal indicates a changed value of the given property. Properties boolean VoicemailWaiting [readonly] Boolean representing whether there is a voicemail message waiting for the user on the voicemail server. byte VoicemailMessageCount [readonly] The total number of voicemail messages waiting. Values of 255 indicate 255 messages or more. Value 0 when VoicemailWaiting is true indicates that the mailbox is not empty and the message count is not known. string VoicemailMailboxNumber [readwrite] String containing the dialing number to be used for voice mailbox access. This number is generally pre-provisioned on the SIM. However, the user can change this number if required. ofono-1.17.bzr6912+16.04.20160314.3/doc/mtk-settings-api.txt0000644000015600001650000000307212671500024023107 0ustar pbuserpbgroup00000000000000MediaTek settings hierarchy =========================== Service org.ofono Interface org.ofono.MtkSettings Object path [variable prefix]/{modem0,modem1,...} Methods dict GetProperties() Returns all MtkSettings properties. See the properties section for available properties. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.Failed void SetProperty(string name, 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.NotImplemented [service].Error.InvalidArguments [service].Error.Failed Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. Properties boolean Has3G [readwrite] If true, the modem has 3G capabilities, otherwise it is just 2G. Only one of the modems can have this set at a time (note that in fact the modem hardware is shared among all ofono MTK modems, and that each ofono MTK modem represents one physical slot in the phone). When we set the property to TRUE, all MTK modems are reset, and after that this property will be false for all modems except for the one for which we set the property. Note that setting this property to FALSE is not allowed to avoid confusion, as one and only one of the modems must have it set to true and we can have the case of more than two modems. ofono-1.17.bzr6912+16.04.20160314.3/doc/cdma-connman-api.txt0000644000015600001650000000352112671500024023010 0ustar pbuserpbgroup00000000000000CDMA Connection Manager hierarchy [experimental] ================================= Service org.ofono Interface org.ofono.cdma.ConnectionManager Object path [variable] Methods dict GetProperties() Returns all global system properties. See the properties section for available properties. Possible Errors: [service].Error.InvalidArguments void SetProperty(string property, variant value) Sets the property to a desired value Possible Errors: [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed Signals PropertyChanged(string property, variant value) This signal indicates a changed value of the given property. Properties boolean Powered [readwrite] Controls whether the CDMA data connection is enabled. boolean Dormant [readonly] Contains whether the connection is dormant. Will always be false if the connection is not powered. string Username [readwrite] Holds the username to be used for authentication purposes. string Password [readwrite] Holds the password to be used for authentication purposes. dict Settings [readonly, optional] Holds all the IP network settings string Interface [readonly, optional] Holds the interface of the network interface used by this context (e.g. "ppp0" "usb0") string Method [readonly, optional] Holds the IP network config method "static"- Set IP network statically "dhcp" - Set IP network through DHCP string Address [readonly, optional] Holds the IP address for this context. string Netmask [readonly, optional] Holds the Netmask for this context. array{string} DomainNameServers [readonly, optional] Holds the list of domain name servers for this context. string Gateway [readonly, optional] Holds the gateway IP for this connection. ofono-1.17.bzr6912+16.04.20160314.3/doc/voicecall-api.txt0000644000015600001650000001413312671500024022417 0ustar pbuserpbgroup00000000000000VoiceCall hierarchy =================== Service org.ofono Interface org.ofono.VoiceCall Object path [variable prefix]/{modem0,modem1,...}/{voicecall01,voicecall02,...} Methods dict GetProperties() Returns all properties for this object. See the properties section for available properties. void Deflect(string number) Deflects the incoming or waiting call to number given in the argument. This method is only valid if the call is in "incoming" or "waiting" state and the Call Deflection supplementary service is subscribed to. This functionality is generally implemented by using the +CHLD=4 * NUMBER command. This method should not be confused with the Transfer() method. Possible Errors: [service].Error.InProgress [service].Error.NotImplemented [service].Error.InvalidArguments [service].Error.InvalidFormat [service].Error.Failed void Hangup() Hangs up the voice call. For an incoming call, the call is hung up using ATH or equivalent. For a waiting call, the remote party is notified by using the User Determined User Busy (UDUB) condition. This is generally implemented using CHLD=0. Please note that the GSM specification does not allow the release of a held call when a waiting call exists. This is because 27.007 allows CHLD=1X to operate only on active calls. Hence a held call cannot be hung up without affecting the state of the incoming call (e.g. using other CHLD alternatives). Most manufacturers provide vendor extensions that do allow the state of the held call to be modified using CHLD=1X or equivalent. It should be noted that Bluetooth HFP specifies the classic 27.007 behavior and does not allow CHLD=1X to modify the state of held calls. Based on the discussion above, it should also be noted that releasing a particular party of a held multiparty call might not be possible on some implementations. It is recommended for the applications to structure their UI accordingly. NOTE: Releasing active calls does not produce side-effects. That is the state of held or waiting calls is not affected. As an exception, in the case where a single active call and a waiting call are present, releasing the active call will result in the waiting call transitioning to the 'incoming' state. Possible Errors: [service].Error.InProgress [service].Error.Failed [service].Error.NotImplemented void Answer() Answers the incoming call. Only valid if the state of the call is "incoming." This functionality is generally implemented by ATA AT command. Possible Errors: [service].Error.InProgress [service].Error.Failed [service].Error.NotImplemented Signals PropertyChanged(string property, variant value) Signal is emitted whenever a property has changed. The new value is passed as the signal argument. DisconnectReason(string reason) This signal is emitted when the modem manager can provide extra information about why this call was released. The possible reason values are: "local" - The call was release due to local user action "remote" - Remote party released the call "network" - Network released the call, most likely due to low signal or other network failure Not all implementations are able to provide this information, so applications should treat the emission of this signal as optional. This signal will be emitted before the PropertyChanged signal. Properties string LineIdentification [readonly] Contains the Line Identification information returned by the network, if present. For incoming calls this is effectively the CLIP. For outgoing calls this attribute will hold the dialed number, or the COLP if received by the underlying implementation. Please note that COLP may be different from the dialed number. A special "withheld" value means the remote party refused to provide caller ID and the "override category" option was not provisioned for the current subscriber. string IncomingLine [readonly, optional] Contains the Called Line Identification information returned by the network. This is only available for incoming calls and indicates the local subscriber number which was dialed by the remote party. This is useful for subscribers which have a multiple line service with their network provider and would like to know what line the call is coming in on. string Name [readonly] Contains the Name Identification information returned by the network, if present. boolean Multiparty [readonly] Contains the indication if the voice call is part of a multiparty call or not. Notifications if a call becomes part or leaves a multipart call are sent. string State [readonly] Contains the state of the current call. The state can be one of: - "active" - The call is active - "held" - The call is on hold - "dialing" - The call is being dialed - "alerting" - The remote party is being alerted - "incoming" - Incoming call in progress - "waiting" - Call is waiting - "disconnected" - No further use of this object is allowed, it will be destroyed shortly string StartTime [readonly, optional] Contains the starting time of the call. The time is stamped when the call enters the "active" state. Client applications can use this to infer somewhat reliable call duration information. string Information [readonly, optional] Contains information related to the call for the user. Currently this property is set for calls initiated by SIM Toolkit applications. byte Icon [readonly, optional] Icon identifier to be used instead of or together with the text information. boolean Emergency [readonly] Contains the indication if the voice call is an emergency call or not. boolean RemoteHeld [experimental] Contains the indication whether the voice call is put on hold by the remote party or not. boolean RemoteMultiparty [experimental] Contains the indication whether the voice call is joined in a multiparty call by the remote party or not. ofono-1.17.bzr6912+16.04.20160314.3/.gitignore0000644000015600001650000000173712671500073020403 0ustar pbuserpbgroup00000000000000*.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 test-driver 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-grilreply unit/test-grilrequest unit/test-grilunsol unit/test-mnclength unit/test-mtkreply unit/test-mtkrequest unit/test-mtkunsol unit/test-rilmodem-cs unit/test-rilmodem-sms unit/test-rilmodem-cb 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 ofono-1.17.bzr6912+16.04.20160314.3/plugins/0000755000015600001650000000000012671500304020061 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/plugins/provision.c0000644000015600001650000000703612671500024022262 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include "mbpi.h" static int provision_get_settings(const char *mcc, const char *mnc, const char *spn, const char *imsi, const char *gid1, struct ofono_gprs_provision_data **settings, int *count) { GSList *l; GSList *apns; GError *error = NULL; int ap_count; int i; ofono_info("Provisioning for MCC %s, MNC %s, SPN '%s', IMSI '%s', " "GID1 '%s'", mcc, mnc, spn, imsi, gid1); /* * TODO: review with upstream. Default behavior was to * disallow duplicate APN entries, which unfortunately exist * in the mobile-broadband-provider-info db. */ apns = mbpi_lookup_apn(mcc, mnc, OFONO_GPRS_CONTEXT_TYPE_INTERNET, TRUE, &error); if (apns == NULL) { if (error != NULL) { ofono_error("%s", error->message); g_error_free(error); } return -ENOENT; } ap_count = g_slist_length(apns); ofono_info("GPRS Provisioning found %d matching APNs for " "SPN: %s MCC: %s MNC: %s", ap_count, spn, mcc, mnc); /* * Only keep the first APN found. * * This allows auto-provisioning to work most of the time vs. * passing FALSE to mbpi_lookup_apn() which would return an * an empty list if duplicates are found. */ if (ap_count > 1) ap_count = 1; *settings = g_try_new0(struct ofono_gprs_provision_data, ap_count); if (*settings == NULL) { ofono_error("Provisioning failed: %s", g_strerror(errno)); for (l = apns; l; l = l->next) mbpi_ap_free(l->data); g_slist_free(apns); return -ENOMEM; } *count = ap_count; for (l = apns, i = 0; l; l = l->next, i++) { struct ofono_gprs_provision_data *ap = l->data; /* * Only create a data context for the first matching APN. * See comment above that restricts restricts apn_count. */ if (i == 0) { ofono_info("Name: '%s'", ap->name); ofono_info("APN: '%s'", ap->apn); ofono_info("Type: %s", mbpi_ap_type(ap->type)); ofono_info("Username: '%s'", ap->username); ofono_info("Password: '%s'", ap->password); memcpy(*settings + i, ap, sizeof(struct ofono_gprs_provision_data)); } g_free(ap); } g_slist_free(apns); return 0; } static struct ofono_gprs_provision_driver provision_driver = { .name = "Provisioning", .get_settings = provision_get_settings }; static int provision_init(void) { return ofono_gprs_provision_driver_register(&provision_driver); } static void provision_exit(void) { ofono_gprs_provision_driver_unregister(&provision_driver); } OFONO_PLUGIN_DEFINE(provision, "Provisioning Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, provision_init, provision_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/c-ares-dns-client.c0000644000015600001650000005636112671500024023447 0ustar pbuserpbgroup00000000000000/* * * DNS Client - provides an asynchronous DNS resolution client. * * The client is implemented using the c-ares library, and integrates with * glib's main event loop. It was originally written to be used by flimflam, * and it has been modified to integrate with ofono. See http://c-ares.haxx.se * and http://developer.gnome.org/glib for c-ares and glib documentation. * * This file originally created by Google, Inc. * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. * Copyright (c) 2015 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include /* Structure representing a pending asynchronous name resolution request. */ struct ares_request { char *hostname; /* hostname that we're resolving */ char *interface; /* interface to use for queries */ struct ares_addr_node *servers; struct timeval timeout; /* caller-specified timeout */ struct timeval start_time; /* time at which request was started */ ofono_dns_client_callback_t cb; /* client-provided callback */ void *data; /* user data */ ares_channel channel; /* opaque, used by c-ares library */ GHashTable *ares_watches; /* fds that we're monitoring for c-ares */ guint timeout_source_id; /* glib source id for our ares timeout */ gboolean running; /* stopped requests are eligible for deletion */ }; /* * Structure representing a file descriptor that we're monitoring within our * glib event loop for c-ares. */ struct ares_watch { struct ares_request *request; /* backpointer to our owner */ int fd; /* file descriptor that we're watching */ GIOChannel *gio_channel; /* glib IO channel */ GIOCondition gio_condition; /* events in which we're interested */ guint g_source_id; /* glib source id */ }; /* * List of pending asynchronous name resolution requests. We expect the number * of pending requests to be small, hence the use of a linked list. */ static GList *pending_requests; /* * Table of number of requests per device. Key is a pointer to a string, value * is a pointer to an integer. Used to know whether we need to (de)activate the * device rp filter or not. */ static GTree *req_per_dev_cnt; /* * Total number of requests that have specified a specific device. Used to know * whether we need to (de)activate the generic rp filter or not. */ static int req_using_dev_cnt; /* * ares requests are often stopped from within ares callbacks. In these cases, * we defer deletion of the ares_request struct to the idle loop. This is the * glib source id associated with the deferred deletion task. */ static guint deferred_deletion_g_source_id; static void reset_ares_timeout(struct ares_request *request, gboolean destroy_old_source); static void stop_ares_request(struct ares_request *request); #define IFACE_ALL "all" /* * Set/unset rp_filter file content for an interface (from connman) */ static void rp_filter_set(const char *interface, gboolean enabled) { int fd; ssize_t cnt; char filename[PATH_MAX]; const char *str_value = (enabled == TRUE) ? "1" : "0"; snprintf(filename, sizeof(filename), "/proc/sys/net/ipv4/conf/%s/rp_filter", interface); fd = open(filename, O_WRONLY); if (fd == -1) return; cnt = write(fd, str_value, strlen(str_value)); if (cnt == -1) ofono_error("%s: cannot write (%s)", __func__, strerror(errno)); close(fd); } static void disable_rp_filter(const char *interface) { int *if_cnt; if_cnt = g_tree_lookup(req_per_dev_cnt, interface); if (if_cnt == NULL) { if_cnt = g_malloc0(sizeof(*if_cnt)); g_tree_insert(req_per_dev_cnt, g_strdup(interface), if_cnt); } /* * Both "all" and device specific rp_filter files have to be set to 0 to * disable rp filter for an interface. See * Documentation/networking/ip-sysctl.txt in kernel documentation. */ if ((*if_cnt)++ == 0) rp_filter_set(interface, FALSE); if (req_using_dev_cnt++ == 0) rp_filter_set(IFACE_ALL, FALSE); } static void enable_rp_filter(const char *interface) { int *if_cnt; if (--req_using_dev_cnt == 0) rp_filter_set(IFACE_ALL, TRUE); if_cnt = g_tree_lookup(req_per_dev_cnt, interface); if (if_cnt == NULL) { ofono_error("%s: %s interface not found!!!", __func__, interface); return; } if (--(*if_cnt) == 0) rp_filter_set(interface, TRUE); } /* * Callback invoked when it's time to give control back to c-ares. Controlled by * the glib source referred to by |timeout_source_id| in struct ares_request. */ static gboolean ares_timeout_cb(gpointer data) { struct ares_request *request = data; const gboolean destroy_old_source = FALSE; DBG("request %p: running = %d", request, request->running); if (!request->running) { request->timeout_source_id = 0; return FALSE; } ares_process_fd(request->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); /* * NOTE: We tell reset_ares_timeout not to destroy its old timer source * because we're calling it from within that source and it will be * destroyed by glib when we return FALSE below. */ reset_ares_timeout(request, destroy_old_source); /* * Return FALSE to get rid of our old glib source. We created a new * one during our call to reset_ares_timeout above. */ return FALSE; } /* * Determine how long c-ares is willing to wait until being given control and * schedule ares_timeout_cb to be invoked at that time. Any existing * timer is replaced. If |destroy_old_source| is TRUE, the old timer's glib * source will be destroyed. */ static void reset_ares_timeout(struct ares_request *request, gboolean destroy_old_source) { struct timeval ret_tv, now, elapsed, max_tv; struct timeval *tv; struct timeval *max = NULL; guint timeout_interval_msecs = 0; gboolean timeout_provided = FALSE; DBG("request %p: running = %d", request, request->running); if (!request->running) return; /* * Compute how much time has elapsed since the request started. * If the client provided a non-default timeout and we've timed out, * notify the client and stop the request. */ gettimeofday(&now, NULL); timersub(&now, &request->start_time, &elapsed); timeout_provided = request->timeout.tv_sec != 0 || request->timeout.tv_usec != 0; if (timeout_provided && timercmp(&elapsed, &request->timeout, >=)) { request->cb(request->data, OFONO_DNS_CLIENT_ERROR_TIMED_OUT, NULL); stop_ares_request(request); return; } /* * Tell c-ares how long we're willing to wait (max) and see if it wants * to regain control sooner than that. */ if (timeout_provided) { timersub(&request->timeout, &elapsed, &max_tv); max = &max_tv; } tv = ares_timeout(request->channel, max, &ret_tv); if (tv == NULL) { ofono_error("%s: ares_timeout failed", __func__); return; } /* * Reschedule our timeout to be the sooner of the ares-specified tiemout * and the client-specified timeout. */ if (request->timeout_source_id != 0 && destroy_old_source) { if (!g_source_remove(request->timeout_source_id)) ofono_error("%s: g_source_remove failed", __func__); } timeout_interval_msecs = tv->tv_sec * 1000 + tv->tv_usec / 1000; DBG("timeout interval = %u", timeout_interval_msecs); request->timeout_source_id = g_timeout_add(timeout_interval_msecs, ares_timeout_cb, request); } /* * Callback invoked by glib when there is activity on a file descriptor that * we're monitoring for c-ares. */ static gboolean ares_watch_io_cb(GIOChannel *source, GIOCondition condition, gpointer data) { struct ares_watch *watch = data; ares_socket_t read_fd = ARES_SOCKET_BAD; ares_socket_t write_fd = ARES_SOCKET_BAD; const gboolean destroy_old_source = TRUE; DBG("watch %p (fd %d): condition = 0x%x", watch, watch->fd, condition); if (!watch->request->running) { /* Destroy this source by returning FALSE. */ watch->g_source_id = 0; return FALSE; } if (condition & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) { ofono_error("%s: error condition on fd %d", __func__, watch->fd); watch->g_source_id = 0; return FALSE; } if (condition & G_IO_IN) read_fd = watch->fd; if (condition & G_IO_OUT) write_fd = watch->fd; /* Give control to c-ares. */ ares_process_fd(watch->request->channel, read_fd, write_fd); reset_ares_timeout(watch->request, destroy_old_source); return TRUE; } /* * Destroy an ares_watch structure. We register this as our value destroy * function when creating the ares_watches table, and it is called by glib * whenever we remove a value from the table or destroy the table. */ static void destroy_ares_watch(gpointer data) { struct ares_watch *watch = data; DBG("watch %p (fd %d)", watch, watch->fd); if (watch->g_source_id != 0) { if (!g_source_remove(watch->g_source_id)) { ofono_error("%s: g_source_remove failed for id %d", __func__, watch->g_source_id); } watch->g_source_id = 0; } g_io_channel_unref(watch->gio_channel); g_free(watch); } /* * Create an ares_watch for |fd| and store it in the ares_watches table for * |request|. Monitor for readability if |read| is TRUE. Monitor for writability * if |write| is TRUE. If there is already an entry for |fd| in the table, * update it according to the values of |read| and |write|. */ static gboolean init_ares_watch(struct ares_request *request, int fd, gboolean read, gboolean write) { struct ares_watch *watch; DBG("fd = %d, read = %d, write = %d", fd, read, write); /* * If there's an old watch in the table, destroy it. We'll replace it * with a new one below if c-ares is still interested in this fd. */ if (g_hash_table_lookup(request->ares_watches, &fd) != NULL) { /* This removal calls destroy_ares_watch on the old watch. */ g_hash_table_remove(request->ares_watches, &fd); } if (!read && !write) return TRUE; watch = g_malloc0(sizeof(struct ares_watch)); watch->request = request; watch->fd = fd; watch->g_source_id = 0; watch->gio_condition = G_IO_NVAL | G_IO_HUP | G_IO_ERR; if (read) watch->gio_condition |= G_IO_IN; if (write) watch->gio_condition |= G_IO_OUT; watch->gio_channel = g_io_channel_unix_new(fd); if (watch->gio_channel == NULL) { ofono_error("%s: could not create g_io_channel for fd %d", __func__, fd); g_free(watch); return FALSE; } g_io_channel_set_close_on_unref(watch->gio_channel, FALSE); g_hash_table_insert(request->ares_watches, &fd, watch); watch->g_source_id = g_io_add_watch(watch->gio_channel, watch->gio_condition, ares_watch_io_cb, watch); return TRUE; } /* * Destroy an ares_request struct, freeing the resources allocated in * init_ares_request. |request| must already have been removed from the * |pending_requests| list and must have been marked not running. */ static void destroy_ares_request(struct ares_request *request) { struct ares_addr_node *node, *next; DBG("request %p", request); ares_destroy(request->channel); g_free(request->hostname); if (request->interface) { enable_rp_filter(request->interface); g_free(request->interface); } for (node = request->servers; node != NULL; node = next) { next = node->next; g_free(node); } if (request->timeout_source_id != 0) g_source_remove(request->timeout_source_id); /* Hash table destruction calls destroy_ares_watch on all watches. */ g_hash_table_destroy(request->ares_watches); g_free(request); } /* * Callback invoked from the main loop to perform deferred deletion of stopped * ares_request objects. We do deferred deletion to avoid problems when we're in * an ares callback and want to delete an object that contains context * associated with that callback. */ static gboolean delete_stopped_ares_requests_cb(gpointer data) { GList *node, *next; struct ares_request *request; guint num_requests_deleted = 0; DBG("pending_requests list has length %u", g_list_length(pending_requests)); /* * Inspect each request in |pending_requests| and destroy it if it's * not running. */ for (node = pending_requests; node != NULL; node = next) { next = g_list_next(node); request = node->data; if (!request->running) { pending_requests = g_list_delete_link(pending_requests, node); destroy_ares_request(request); ++num_requests_deleted; } } DBG("deleted %u stopped requests", num_requests_deleted); deferred_deletion_g_source_id = 0; return FALSE; } /* * Stop an ares_request and schedule the deferred deletion task if it's * not already running. */ static void stop_ares_request(struct ares_request *request) { DBG(""); request->running = FALSE; if (deferred_deletion_g_source_id != 0) return; deferred_deletion_g_source_id = g_idle_add(delete_stopped_ares_requests_cb, NULL); if (deferred_deletion_g_source_id == 0) ofono_error("%s: g_idle_add failed", __func__); } /* * Callback that is invoked by c-ares to tell us which sockets it wants us to * monitor for readability and writability. */ static void ares_socket_state_cb(void *data, int s, int read, int write) { struct ares_request *request = (struct ares_request *) data; DBG(""); if (!request->running) return; DBG("socket %d: read = %d, write = %d", s, read, write); if (!init_ares_watch(request, s, read, write)) ofono_error("%s: couldn't create ares_watch for socket %d", __func__, s); } /* * Converts a c-ares status code to the corresponding dns_client status code. * We do this to completely encapsulate c-ares. In theory, we should be able to * replace it with a different asynchronous DNS library without changing our * clients. */ static ofono_dns_client_status_t status_from_ares_status(int ares_status) { switch (ares_status) { case ARES_SUCCESS: return OFONO_DNS_CLIENT_SUCCESS; case ARES_ENODATA: return OFONO_DNS_CLIENT_ERROR_NO_DATA; case ARES_EFORMERR: return OFONO_DNS_CLIENT_ERROR_FORM_ERR; case ARES_ESERVFAIL: return OFONO_DNS_CLIENT_ERROR_SERVER_FAIL; case ARES_ENOTFOUND: return OFONO_DNS_CLIENT_ERROR_NOT_FOUND; case ARES_ENOTIMP: return OFONO_DNS_CLIENT_ERROR_NOT_IMP; case ARES_EREFUSED: return OFONO_DNS_CLIENT_ERROR_REFUSED; case ARES_EBADQUERY: case ARES_EBADNAME: case ARES_EBADFAMILY: case ARES_EBADRESP: return OFONO_DNS_CLIENT_ERROR_BAD_QUERY; case ARES_ECONNREFUSED: return OFONO_DNS_CLIENT_ERROR_NET_REFUSED; case ARES_ETIMEOUT: return OFONO_DNS_CLIENT_ERROR_TIMED_OUT; default: return OFONO_DNS_CLIENT_ERROR_UNKNOWN; } } /* * Callback that is invoked by c-ares when an asynchronous name resolution * request that we have previously initiated is complete. */ static void ares_request_cb(void *arg, int ares_status, int timeouts, struct hostent *hostent) { struct sockaddr_in sin; struct sockaddr_in6 sin6; int addr_length; void *addr_buffer; char ip_addr_string[INET6_ADDRSTRLEN]; struct sockaddr *ip_addr; struct ares_request *request = (struct ares_request *)arg; DBG(""); if (!request->running) return; /* Stop the request. It will be deleted later from the idle loop. */ stop_ares_request(request); if (ares_status != ARES_SUCCESS) { DBG("ares request for '%s' failed: %s", request->hostname, ares_strerror(ares_status)); /* Notify client. */ request->cb(request->data, status_from_ares_status(ares_status), NULL); return; } if (hostent->h_addrtype != AF_INET && hostent->h_addrtype != AF_INET6) { ofono_error("%s: unsupported addrtype: %d", __func__, hostent->h_addrtype); request->cb(request->data, OFONO_DNS_CLIENT_ERROR_NO_DATA, NULL); return; } if (hostent->h_addrtype == AF_INET) { memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; addr_length = sizeof(sin.sin_addr.s_addr); addr_buffer = &sin.sin_addr.s_addr; ip_addr = (struct sockaddr *) &sin; } else { /* AF_INET6 */ memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; addr_length = sizeof(sin6.sin6_addr.s6_addr); addr_buffer = &sin6.sin6_addr.s6_addr; ip_addr = (struct sockaddr *) &sin6; } if (hostent->h_length > addr_length) { ofono_error("%s: address too large: %u bytes", __func__, hostent->h_length); request->cb(request->data, OFONO_DNS_CLIENT_ERROR_NO_DATA, NULL); return; } memcpy(addr_buffer, hostent->h_addr, hostent->h_length); if (inet_ntop(hostent->h_addrtype, addr_buffer, ip_addr_string, sizeof(ip_addr_string)) == NULL) { ofono_error("%s: could not convert address to string: %s", __func__, strerror(errno)); request->cb(request->data, OFONO_DNS_CLIENT_ERROR_NO_DATA, NULL); return; } DBG("ares request for '%s' succeeded with %d timeouts: %s", request->hostname, timeouts, ip_addr_string); request->cb(request->data, status_from_ares_status(ares_status), ip_addr); } /* Cancel all in-progress asynchronous name resolution requests. */ static void cancel_all_ares_requests() { GList *node; struct ares_request *request; DBG(""); while ((node = g_list_first(pending_requests)) != NULL) { request = node->data; pending_requests = g_list_delete_link(pending_requests, node); request->running = FALSE; /* don't trip assertion */ destroy_ares_request(request); } } static void set_request_servers(const char **servers, struct ares_request *request) { struct ares_addr_node *current; struct ares_addr_node **prev = &request->servers; const char **dns; int rc; for (dns = servers; *dns != NULL; ++dns) { current = g_malloc0(sizeof(*current)); rc = inet_pton(AF_INET, *dns, ¤t->addr.addr4); if (rc == 1) { current->family = AF_INET; } else { rc = inet_pton(AF_INET6, *dns, ¤t->addr.addr6); if (rc != 1) { ofono_error("%s: wrong dns address %s", __func__, *dns); g_free(current); continue; } current->family = AF_INET6; } *prev = current; prev = ¤t->next; } } /* Initiate an asynchronous name resolution request. */ static ofono_dns_client_request_t ofono_dns_client_submit_request(const char *hostname, const char *interface, const char **servers, int timeout_ms, ofono_dns_client_callback_t cb, void *data) { int ares_status; struct ares_request *request; struct ares_options options; int optmask; const gboolean destroy_old_source = TRUE; DBG(""); if (timeout_ms < 0) { DBG("invalid timeout value of %d ms", timeout_ms); return NULL; } request = g_malloc0(sizeof(struct ares_request)); request->running = TRUE; request->ares_watches = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, destroy_ares_watch); if (request->ares_watches == NULL) { ofono_error("%s: could not create ares_watches table", __func__); g_free(request); return NULL; } /* * Init a c-ares channel for this request. We set an option asking * c-ares to notify us via callback about which sockets it wants to * monitor for readability and writability. This allows us to * integrate c-ares activity into our glib main event loop. */ memset(&options, 0, sizeof(options)); options.sock_state_cb = ares_socket_state_cb; options.sock_state_cb_data = request; optmask = ARES_OPT_SOCK_STATE_CB; if (timeout_ms > 0) { options.timeout = timeout_ms; optmask |= ARES_OPT_TIMEOUTMS; } ares_status = ares_init_options(&request->channel, &options, optmask); if (ares_status != ARES_SUCCESS) { ofono_error("%s: failed to init c-ares channel: %s", __func__, ares_strerror(ares_status)); request->running = FALSE; /* don't trip assertion */ g_hash_table_destroy(request->ares_watches); g_free(request); return NULL; } if (servers != NULL) { set_request_servers(servers, request); ares_status = ares_set_servers(request->channel, request->servers); /* Log an error, but continue anyway */ if (ares_status != ARES_SUCCESS) ofono_error("%s: failed to set name servers: %s", __func__, ares_strerror(ares_status)); } /* * If the caller has provided a preferred interface, tell c-ares to send * requests out that interface (it uses internally SO_BINDTODEVICE to * force it), and unset rf filter (so we can receive packages which * source address is "unexpected" at this interface). This avoids the * need for routes to the DNS servers and forces using the provided * interface for the query. Unsetting the reverse path filter this way * works only for IPv4. TODO IPv6 uses ip6tables to enable/disable rp * filtering, and it is not clear if it is enabled by default. Needs * more investigation. */ if (interface != NULL) { DBG("caller has specified device %s", interface); request->interface = g_strdup(interface); ares_set_local_dev(request->channel, request->interface); disable_rp_filter(request->interface); } request->cb = cb; request->data = data; request->hostname = g_strdup(hostname); request->timeout.tv_sec = timeout_ms / 1000; request->timeout.tv_usec = (timeout_ms % 1000) * 1000; gettimeofday(&request->start_time, NULL); pending_requests = g_list_append(pending_requests, request); ares_gethostbyname(request->channel, hostname, AF_INET, ares_request_cb, request); reset_ares_timeout(request, destroy_old_source); return request; } /* Cancel an in-progress name resolution request. */ static gboolean ofono_dns_client_cancel_request(ofono_dns_client_request_t req) { struct ares_request *request = req; if (request == NULL || g_list_find(pending_requests, request) == NULL) return FALSE; DBG("request %p", request); if (!request->running) return TRUE; pending_requests = g_list_remove(pending_requests, request); request->running = FALSE; /* don't trip assertion */ destroy_ares_request(request); return TRUE; } static struct ofono_dns_client_driver dns_driver = { .name = "c-ares DNS client", .submit_request = ofono_dns_client_submit_request, .cancel_request = ofono_dns_client_cancel_request }; /* Intitialize this module. */ static int c_ares_init(void) { int ares_status = 0; DBG(""); ares_status = ares_library_init(ARES_LIB_INIT_ALL); if (ares_status != ARES_SUCCESS) { ofono_error("%s: Failed to init c-ares: %s", __func__, ares_strerror(ares_status)); return -1; } req_per_dev_cnt = g_tree_new_full((GCompareDataFunc) strcmp, NULL, g_free, g_free); return ofono_dns_client_driver_register(&dns_driver); } /* Clean up. */ static void c_ares_exit(void) { DBG(""); ofono_dns_client_driver_unregister(&dns_driver); g_tree_destroy(req_per_dev_cnt); req_per_dev_cnt = NULL; req_using_dev_cnt = 0; if (deferred_deletion_g_source_id != 0) { g_source_remove(deferred_deletion_g_source_id); deferred_deletion_g_source_id = 0; } cancel_all_ares_requests(); ares_library_cleanup(); } OFONO_PLUGIN_DEFINE(c_ares_dns_client, "c-ares DNS client", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, c_ares_init, c_ares_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/android-wakelock.c0000644000015600001650000001043712671500073023453 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - Android based wakelocks * * Copyright (C) 2016 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include "plugin.h" #include "log.h" #include "wakelock.h" #define ANDROID_WAKELOCK_LOCK_PATH "/sys/power/wake_lock" #define ANDROID_WAKELOCK_UNLOCK_PATH "/sys/power/wake_unlock" struct wakelock { char *name; unsigned int acquisitions; }; GSList *locks = NULL; static int file_exists(char const *filename) { struct stat st; return stat(filename, &st) == 0; } static int write_file(const char *file, const char *content) { int fd; unsigned int r = 0; fd = open(file, O_WRONLY); if (fd == -1) return -EIO; r = write(fd, content, strlen(content)); close(fd); if (r != strlen(content)) return -EIO; return 0; } static int wakelock_lock(const char *name) { return write_file(ANDROID_WAKELOCK_LOCK_PATH, name); } static int wakelock_unlock(const char *name) { return write_file(ANDROID_WAKELOCK_UNLOCK_PATH, name); } static int android_wakelock_acquire(struct wakelock *lock) { if (lock == NULL) return -EINVAL; if (lock->acquisitions > 0) { lock->acquisitions++; return 0; } if (wakelock_lock(lock->name) < 0) return -EIO; lock->acquisitions++; return 0; } static int android_wakelock_release(struct wakelock *lock) { if (lock == NULL) return -EINVAL; DBG("lock %p name %s acquisitions %d", lock, lock->name, lock->acquisitions); if (!lock->acquisitions) { ofono_warn("Attempted to release already released lock %s", lock->name); return -EINVAL; } if (lock->acquisitions > 1) { lock->acquisitions--; DBG("lock %s released acquisitions %d", lock->name, lock->acquisitions); return 0; } if (wakelock_unlock(lock->name) < 0) return -EIO; lock->acquisitions = 0; DBG("lock %s was released", lock->name); return 0; } static int android_wakelock_create(const char *name, struct wakelock **lock) { if (lock == NULL) return -EINVAL; *lock = g_new0(struct wakelock, 1); (*lock)->name = g_strdup(name); (*lock)->acquisitions = 0; locks = g_slist_prepend(locks, *lock); DBG("wakelock %s create", name); return 0; } static int android_wakelock_free(struct wakelock *lock) { if (lock == NULL) return -EINVAL; if (lock->acquisitions) { /* Need to force releasing the lock here */ lock->acquisitions = 1; android_wakelock_release(lock); } locks = g_slist_remove(locks, lock); DBG("Freeing lock %s", lock->name); g_free(lock->name); g_free(lock); return 0; } static ofono_bool_t android_wakelock_is_locked(struct wakelock *lock) { if (lock == NULL) return FALSE; return lock->acquisitions > 0; } struct wakelock_table driver = { .create = android_wakelock_create, .free = android_wakelock_free, .acquire = android_wakelock_acquire, .release = android_wakelock_release, .is_locked = android_wakelock_is_locked }; static int android_wakelock_init(void) { if (!file_exists(ANDROID_WAKELOCK_LOCK_PATH)) { ofono_warn("System does not support Android wakelocks."); return 0; } if (wakelock_plugin_register("android-wakelock", &driver) < 0) { ofono_error("Failed to register wakelock driver"); return -EIO; } return 0; } static void android_wakelock_exit(void) { wakelock_plugin_unregister(); } OFONO_PLUGIN_DEFINE(android_wakelock, "Android Wakelock driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, android_wakelock_init, android_wakelock_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/sap.c0000644000015600001650000001726512671500024021022 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010-2011 ProFUSION embedded systems * * 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 #endif #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include "bluez4.h" #include "util.h" #ifndef DBUS_TYPE_UNIX_FD #define DBUS_TYPE_UNIX_FD -1 #endif #define BLUEZ_SERIAL_INTERFACE BLUEZ_SERVICE ".Serial" static DBusConnection *connection; static GHashTable *modem_hash = NULL; static struct ofono_modem *sap_hw_modem = NULL; static struct bluetooth_sap_driver *sap_hw_driver = NULL; struct sap_data { struct ofono_modem *hw_modem; struct bluetooth_sap_driver *sap_driver; DBusPendingCall *call; }; int bluetooth_sap_client_register(struct bluetooth_sap_driver *sap, struct ofono_modem *modem) { if (sap_hw_modem != NULL) return -EPERM; sap_hw_modem = modem; sap_hw_driver = sap; bluetooth_get_properties(); return 0; } void bluetooth_sap_client_unregister(struct ofono_modem *modem) { GHashTableIter iter; gpointer key, value; if (sap_hw_modem == NULL) return; g_hash_table_iter_init(&iter, modem_hash); while (g_hash_table_iter_next(&iter, &key, &value)) { g_hash_table_iter_remove(&iter); ofono_modem_remove(value); } sap_hw_modem = NULL; sap_hw_driver = NULL; } static int sap_probe(struct ofono_modem *modem) { struct sap_data *data; DBG("%p", modem); data = g_try_new0(struct sap_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void sap_remove(struct ofono_modem *modem) { struct sap_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (data->call != NULL) dbus_pending_call_cancel(data->call); g_free(data); ofono_modem_set_data(modem, NULL); } static void sap_connect_reply(DBusPendingCall *call, gpointer user_data) { struct ofono_modem *modem = user_data; struct sap_data *data = ofono_modem_get_data(modem); DBusError derr; DBusMessage *reply; int fd, err; DBG(""); reply = dbus_pending_call_steal_reply(call); data->call = NULL; if (ofono_modem_get_powered(modem)) goto done; dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { DBG("Connect reply: %s", derr.message); dbus_error_free(&derr); goto done; } if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID)) goto done; data->hw_modem = sap_hw_modem; data->sap_driver = sap_hw_driver; err = data->sap_driver->enable(data->hw_modem, modem, fd); if (!err || err == -EINPROGRESS) { dbus_message_unref(reply); return; } done: ofono_modem_set_powered(modem, FALSE); dbus_message_unref(reply); } /* power up hardware */ static int sap_enable(struct ofono_modem *modem) { struct sap_data *data = ofono_modem_get_data(modem); DBusPendingCall *call; int status; const char *str = "sap"; const char *server_path = ofono_modem_get_string(modem, "ServerPath"); DBG("%p", modem); status = bluetooth_send_with_reply(server_path, BLUEZ_SERIAL_INTERFACE, "ConnectFD", &call, sap_connect_reply, modem, NULL, DBUS_TIMEOUT, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID); if (status < 0) return -EINVAL; data->call = call; return -EINPROGRESS; } static int sap_disable(struct ofono_modem *modem) { struct sap_data *data = ofono_modem_get_data(modem); DBG("%p", modem); return data->sap_driver->disable(data->hw_modem); } static void sap_pre_sim(struct ofono_modem *modem) { struct sap_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->sap_driver->pre_sim(data->hw_modem); } static void sap_post_sim(struct ofono_modem *modem) { struct sap_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->sap_driver->post_sim(data->hw_modem); } static void sap_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct sap_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->sap_driver->set_online(data->hw_modem, online, cb, user_data); } static void sap_post_online(struct ofono_modem *modem) { struct sap_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->sap_driver->post_online(data->hw_modem); } static int bluetooth_sap_probe(const char *device, const char *dev_addr, const char *adapter_addr, const char *alias) { struct ofono_modem *modem; char buf[256]; if (sap_hw_modem == NULL) return -ENODEV; /* We already have this device in our hash, ignore */ if (g_hash_table_lookup(modem_hash, device) != NULL) return -EALREADY; ofono_info("Using device: %s, devaddr: %s, adapter: %s", device, dev_addr, adapter_addr); strcpy(buf, "sap/"); bluetooth_create_path(dev_addr, adapter_addr, buf + 4, sizeof(buf) - 4); modem = ofono_modem_create(buf, "sap"); if (modem == NULL) return -ENOMEM; ofono_modem_set_string(modem, "ServerPath", device); ofono_modem_set_name(modem, alias); ofono_modem_register(modem); g_hash_table_insert(modem_hash, g_strdup(device), modem); return 0; } static void bluetooth_sap_remove(const char *prefix) { GHashTableIter iter; gpointer key, value; DBG("%s", prefix); if (modem_hash == NULL) return; g_hash_table_iter_init(&iter, modem_hash); while (g_hash_table_iter_next(&iter, &key, &value)) { if (prefix && g_str_has_prefix((char *)key, prefix) == FALSE) continue; g_hash_table_iter_remove(&iter); ofono_modem_remove(value); } } static void bluetooth_sap_set_alias(const char *device, const char *alias) { struct ofono_modem *modem; if (device == NULL || alias == NULL) return; modem = g_hash_table_lookup(modem_hash, device); if (modem == NULL) return; ofono_modem_set_name(modem, alias); } static struct ofono_modem_driver sap_driver = { .name = "sap", .modem_type = OFONO_MODEM_TYPE_SAP, .probe = sap_probe, .remove = sap_remove, .enable = sap_enable, .disable = sap_disable, .pre_sim = sap_pre_sim, .post_sim = sap_post_sim, .set_online = sap_set_online, .post_online = sap_post_online, }; static struct bluetooth_profile sap = { .name = "sap", .probe = bluetooth_sap_probe, .remove = bluetooth_sap_remove, .set_alias = bluetooth_sap_set_alias, }; static int sap_init(void) { int err; if (DBUS_TYPE_UNIX_FD < 0) return -EBADF; connection = ofono_dbus_get_connection(); err = ofono_modem_driver_register(&sap_driver); if (err < 0) return err; err = bluetooth_register_uuid(SAP_UUID, &sap); if (err < 0) { ofono_modem_driver_unregister(&sap_driver); return err; } modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); return 0; } static void sap_exit(void) { DBG(""); bluetooth_unregister_uuid(SAP_UUID); ofono_modem_driver_unregister(&sap_driver); g_hash_table_destroy(modem_hash); modem_hash = NULL; } OFONO_PLUGIN_DEFINE(sap, "Sim Access Profile Plugins", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, sap_init, sap_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/bluez5.h0000644000015600001650000000335412671500024021444 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 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 BLUEZ_SERVICE "org.bluez" #define BLUEZ_MANAGER_PATH "/" #define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1" #define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device1" #define BLUEZ_ERROR_INTERFACE BLUEZ_SERVICE ".Error" #define DUN_GW_UUID "00001103-0000-1000-8000-00805f9b34fb" #define HFP_HS_UUID "0000111e-0000-1000-8000-00805f9b34fb" #define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb" int bt_register_profile(DBusConnection *conn, const char *uuid, uint16_t version, const char *name, const char *object, const char *role, uint16_t features); void bt_unregister_profile(DBusConnection *conn, const char *object); typedef void (*bt_finish_cb)(gboolean success, gpointer user_data); void bt_connect_profile(DBusConnection *conn, const char *device, const char *uuid, bt_finish_cb cb, gpointer user_data); void bt_disconnect_profile(DBusConnection *conn, const char *device, const char *uuid, bt_finish_cb cb, gpointer user_data); ofono-1.17.bzr6912+16.04.20160314.3/plugins/isiusb.c0000644000015600001650000002645112671500024021532 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drivers/isimodem/isimodem.h" #include "drivers/isimodem/isiutil.h" #include "drivers/isimodem/mtc.h" #include "drivers/isimodem/debug.h" struct isi_data { char const *ifname; GIsiModem *modem; GIsiClient *client; GIsiPhonetNetlink *link; enum GIsiPhonetLinkState linkstate; unsigned interval; int reported; ofono_bool_t online; struct isi_cb_data *online_cbd; }; static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid) { if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", strerror(-g_isi_msg_error(msg))); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", mtc_message_id_name(g_isi_msg_id(msg))); return FALSE; } return TRUE; } static void report_powered(struct ofono_modem *modem, struct isi_data *isi, ofono_bool_t powered) { if (powered == isi->reported) return; isi->reported = powered; ofono_modem_set_powered(modem, powered); } static void report_online(struct isi_data *isi, ofono_bool_t online) { struct isi_cb_data *cbd = isi->online_cbd; ofono_modem_online_cb_t cb = cbd->cb; isi->online_cbd = NULL; if (isi->online == online) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void set_power_by_mtc_state(struct ofono_modem *modem, struct isi_data *isi, int mtc_state) { if (isi->online_cbd) report_online(isi, mtc_state == MTC_NORMAL); switch (mtc_state) { case MTC_STATE_NONE: case MTC_POWER_OFF: case MTC_CHARGING: case MTC_SELFTEST_FAIL: report_powered(modem, isi, FALSE); break; case MTC_RF_INACTIVE: case MTC_NORMAL: default: report_powered(modem, isi, TRUE); } } static void mtc_state_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_modem *modem = data; struct isi_data *isi = ofono_modem_get_data(modem); uint8_t state; uint8_t action; if (!isi || g_isi_msg_id(msg) != MTC_STATE_INFO_IND) return; if (!g_isi_msg_data_get_byte(msg, 0, &state) || !g_isi_msg_data_get_byte(msg, 1, &action)) return; switch (action) { case MTC_START: DBG("target modem state: %s (0x%02X)", mtc_modem_state_name(state), state); break; case MTC_READY: DBG("current modem state: %s (0x%02X)", mtc_modem_state_name(state), state); set_power_by_mtc_state(modem, isi, state); break; } } static void mtc_query_cb(const GIsiMessage *msg, void *data) { struct ofono_modem *modem = data; struct isi_data *isi = ofono_modem_get_data(modem); uint8_t current; uint8_t target; if (!check_response_status(msg, MTC_STATE_QUERY_RESP)) return; if (!g_isi_msg_data_get_byte(msg, 0, ¤t) || !g_isi_msg_data_get_byte(msg, 1, &target)) return; DBG("Modem state: current=%s (0x%02X) target=%s (0x%02X)", mtc_modem_state_name(current), current, mtc_modem_state_name(target), target); if (current == target) set_power_by_mtc_state(modem, isi, current); } static gboolean bootstrap_current_state(gpointer user) { struct ofono_modem *om = user; struct isi_data *isi = ofono_modem_get_data(om); const uint8_t req[] = { MTC_STATE_QUERY_REQ, 0x00, 0x00 /* Filler */ }; size_t len = sizeof(req); g_isi_client_send(isi->client, req, len, mtc_query_cb, om, NULL); return FALSE; } static void reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_modem *om = data; struct isi_data *isi = ofono_modem_get_data(om); if (g_isi_msg_error(msg) < 0) return; ISI_RESOURCE_DBG(msg); g_isi_client_ind_subscribe(isi->client, MTC_STATE_INFO_IND, mtc_state_ind_cb, om); /* * FIXME: There is a theoretical race condition here: * g_isi_client_ind_subscribe() adds the actual message * sending for committing changes to subscriptions in idle * loop, which may or may not preserve ordering. Thus, we * might miss a state indication if the bootstrap request ends * up being sent first. */ g_idle_add(bootstrap_current_state, om); } static void phonet_status_cb(GIsiModem *modem, enum GIsiPhonetLinkState st, char const *ifname, void *data) { struct ofono_modem *om = data; struct isi_data *isi = ofono_modem_get_data(om); DBG("Link %s (%u) is %s", isi->ifname, g_isi_modem_index(isi->modem), st == PN_LINK_REMOVED ? "removed" : st == PN_LINK_DOWN ? "down" : "up"); isi->linkstate = st; if (st == PN_LINK_UP) g_isi_client_verify(isi->client, reachable_cb, om, NULL); else if (st == PN_LINK_DOWN) set_power_by_mtc_state(om, isi, MTC_STATE_NONE); } static int isiusb_probe(struct ofono_modem *modem) { const char *ifname = ofono_modem_get_string(modem, "Interface"); unsigned address = ofono_modem_get_integer(modem, "Address"); GIsiModem *isimodem; GIsiClient *client = NULL; GIsiPhonetNetlink *link = NULL; struct isi_data *isi = NULL; if (!ifname) return -EINVAL; DBG("(%p) with %s", modem, ifname); isimodem = g_isi_modem_create_by_name(ifname); if (!isimodem) { DBG("Interface=%s: %s", ifname, strerror(errno)); return -errno; } g_isi_modem_set_userdata(isimodem, modem); g_isi_modem_set_flags(isimodem, GISI_MODEM_FLAG_USE_LEGACY_SUBSCRIBE); if (getenv("OFONO_ISI_DEBUG")) g_isi_modem_set_debug(isimodem, ofono_debug); if (getenv("OFONO_ISI_TRACE")) g_isi_modem_set_trace(isimodem, isi_trace); if (g_isi_pn_netlink_by_modem(isimodem)) { DBG("%s: %s", ifname, strerror(EBUSY)); errno = EBUSY; goto error; } link = g_isi_pn_netlink_start(isimodem, phonet_status_cb, modem); if (link == NULL) { DBG("%s: %s", ifname, strerror(errno)); goto error; } if (address) { int error = g_isi_pn_netlink_set_address(isimodem, address); if (error && error != -EEXIST) { DBG("g_isi_pn_netlink_set_address(): %s\n", strerror(-error)); errno = -error; goto error; } } isi = g_try_new0(struct isi_data, 1); if (!isi) { errno = ENOMEM; goto error; } client = g_isi_client_create(isimodem, PN_MTC); if (!client) goto error; isi->modem = isimodem; isi->ifname = ifname; isi->link = link; isi->reported = -1; isi->client = client; ofono_modem_set_data(modem, isi); return 0; error: g_isi_pn_netlink_stop(link); g_isi_client_destroy(client); g_isi_modem_destroy(isimodem); g_free(isi); return -errno; } static void isiusb_remove(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); ofono_modem_set_data(modem, NULL); if (isi == NULL) return; g_isi_pn_netlink_stop(isi->link); g_isi_client_destroy(isi->client); g_isi_modem_destroy(isi->modem); g_free(isi); } static void mtc_state_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; struct ofono_modem *modem = cbd->user; ofono_modem_online_cb_t cb = cbd->cb; struct isi_data *isi = ofono_modem_get_data(modem); uint8_t cause; if (!check_response_status(msg, MTC_STATE_RESP)) goto error; if (!g_isi_msg_data_get_byte(msg, 0, &cause)) goto error; DBG("MTC cause: %s (0x%02X)", mtc_isi_cause_name(cause), cause); if (cause == MTC_OK || cause == MTC_STATE_TRANSITION_GOING_ON) { isi->online_cbd = cbd; return; } if (cause == MTC_ALREADY_ACTIVE) { CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); return; } error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void isiusb_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *data) { struct isi_data *isi = ofono_modem_get_data(modem); struct isi_cb_data *cbd = isi_cb_data_new(modem, cb, data); const uint8_t req[] = { MTC_STATE_REQ, online ? MTC_NORMAL : MTC_RF_INACTIVE, 0x00 }; DBG("(%p) with %s", modem, isi->ifname); if (cbd == NULL || isi == NULL) goto error; if (g_isi_client_send_with_timeout(isi->client, req, sizeof(req), MTC_STATE_REQ_TIMEOUT, mtc_state_cb, cbd, NULL)) { isi->online = online; return; } error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void isiusb_pre_sim(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); DBG("(%p) with %s", modem, isi->ifname); ofono_sim_create(modem, 0, "isimodem", isi->modem); ofono_devinfo_create(modem, 0, "isimodem", isi->modem); ofono_voicecall_create(modem, 0, "isimodem", isi->modem); } static void isiusb_post_sim(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); DBG("(%p) with %s", modem, isi->ifname); ofono_phonebook_create(modem, 0, "isimodem", isi->modem); ofono_call_forwarding_create(modem, 0, "isimodem", isi->modem); ofono_radio_settings_create(modem, 0, "isimodem", isi->modem); } static void isiusb_post_online(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; DBG("(%p) with %s", modem, isi->ifname); ofono_netreg_create(modem, 0, "isimodem", isi->modem); ofono_sms_create(modem, 0, "isimodem", isi->modem); ofono_cbs_create(modem, 0, "isimodem", isi->modem); ofono_ussd_create(modem, 0, "isimodem", isi->modem); ofono_call_settings_create(modem, 0, "isimodem", isi->modem); ofono_call_barring_create(modem, 0, "isimodem", isi->modem); ofono_call_meter_create(modem, 0, "isimodem", isi->modem); ofono_gprs_create(modem, 0, "isimodem", isi->modem); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } static int isiusb_enable(struct ofono_modem *modem) { return 0; } static int isiusb_disable(struct ofono_modem *modem) { return 0; } static struct ofono_modem_driver driver = { .name = "isiusb", .probe = isiusb_probe, .remove = isiusb_remove, .set_online = isiusb_online, .pre_sim = isiusb_pre_sim, .post_sim = isiusb_post_sim, .post_online = isiusb_post_online, .enable = isiusb_enable, .disable = isiusb_disable, }; static int isiusb_init(void) { return ofono_modem_driver_register(&driver); } static void isiusb_exit(void) { ofono_modem_driver_unregister(&driver); } OFONO_PLUGIN_DEFINE(isiusb, "Generic modem driver for isi", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, isiusb_init, isiusb_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/nokia.c0000644000015600001650000001440712671500024021333 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; struct nokia_data { GAtChat *modem; GAtChat *aux; }; static int nokia_probe(struct ofono_modem *modem) { struct nokia_data *data; DBG("%p", modem); data = g_try_new0(struct nokia_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void nokia_remove(struct ofono_modem *modem) { struct nokia_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup after hot-unplug */ g_at_chat_unref(data->aux); g_free(data); } static void nokia_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GAtSyntax *syntax; GIOChannel *channel; GAtChat *chat; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, nokia_debug, debug); return chat; } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct nokia_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_unref(data->aux); data->aux = NULL; } ofono_modem_set_powered(modem, ok); } static int nokia_enable(struct ofono_modem *modem) { struct nokia_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->aux = open_device(modem, "Aux", "Aux: "); if (data->aux == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_send(data->modem, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL); /* * Ensure that the modem is using GSM character set and not IRA, * otherwise weirdness with umlauts and other non-ASCII characters * can result */ g_at_chat_send(data->modem, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CFUN=1", none_prefix, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct nokia_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->aux); data->aux = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int nokia_disable(struct ofono_modem *modem) { struct nokia_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->aux); g_at_chat_unregister_all(data->aux); g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix, cfun_disable, modem, NULL); return -EINPROGRESS; } static void nokia_pre_sim(struct ofono_modem *modem) { struct nokia_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->aux); sim = ofono_sim_create(modem, 0, "atmodem", data->aux); if (sim) ofono_sim_inserted_notify(sim, TRUE); } static void nokia_post_sim(struct ofono_modem *modem) { struct nokia_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_phonebook_create(modem, 0, "atmodem", data->aux); ofono_sms_create(modem, OFONO_VENDOR_OPTION_HSO, "atmodem", data->aux); gprs = ofono_gprs_create(modem, OFONO_VENDOR_NOKIA, "atmodem", data->aux); gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static void nokia_post_online(struct ofono_modem *modem) { struct nokia_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_netreg_create(modem, OFONO_VENDOR_NOKIA, "atmodem", data->aux); ofono_ussd_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->aux); } static struct ofono_modem_driver nokia_driver = { .name = "nokia", .probe = nokia_probe, .remove = nokia_remove, .enable = nokia_enable, .disable = nokia_disable, .pre_sim = nokia_pre_sim, .post_sim = nokia_post_sim, .post_online = nokia_post_online, }; static int nokia_init(void) { return ofono_modem_driver_register(&nokia_driver); } static void nokia_exit(void) { ofono_modem_driver_unregister(&nokia_driver); } OFONO_PLUGIN_DEFINE(nokia, "Nokia Datacard modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, nokia_init, nokia_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/u8500.c0000644000015600001650000003757612671500024021027 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 ST-Ericsson AB. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drivers/isimodem/isimodem.h" #include "drivers/isimodem/isiutil.h" #include "drivers/isimodem/mtc.h" #include "drivers/isimodem/debug.h" struct isi_data { char const *ifname; GIsiModem *modem; GIsiClient *client; GIsiPhonetNetlink *link; enum GIsiPhonetLinkState linkstate; unsigned interval; int reported; ofono_bool_t online; struct isi_cb_data *online_cbd; }; struct devinfo_data { GIsiClient *client; }; static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid) { if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", strerror(-g_isi_msg_error(msg))); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", mce_message_id_name(g_isi_msg_id(msg))); return FALSE; } return TRUE; } static void report_powered(struct ofono_modem *modem, struct isi_data *isi, ofono_bool_t powered) { if (powered == isi->reported) return; isi->reported = powered; ofono_modem_set_powered(modem, powered); } static void report_online(struct isi_data *isi, ofono_bool_t online) { struct isi_cb_data *cbd = isi->online_cbd; ofono_modem_online_cb_t cb = cbd->cb; isi->online_cbd = NULL; if (isi->online == online) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void set_power_by_mce_state(struct ofono_modem *modem, struct isi_data *isi, int mce_state) { switch (mce_state) { case MCE_POWER_OFF: report_powered(modem, isi, FALSE); break; case MCE_NORMAL: if (isi->online_cbd) report_online(isi, mce_state == MCE_NORMAL); default: report_powered(modem, isi, TRUE); } } static void mce_state_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_modem *modem = data; struct isi_data *isi = ofono_modem_get_data(modem); uint8_t state; uint8_t action; if (isi == NULL || g_isi_msg_id(msg) != MCE_MODEM_STATE_IND) return; if (!g_isi_msg_data_get_byte(msg, 0, &state) || !g_isi_msg_data_get_byte(msg, 1, &action)) return; switch (action) { case MCE_START: DBG("target modem state: %s (0x%02X)", mce_modem_state_name(state), state); break; case MCE_READY: DBG("current modem state: %s (0x%02X)", mce_modem_state_name(state), state); set_power_by_mce_state(modem, isi, state); break; default: break; } } static void mce_rf_state_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_modem *modem = data; struct isi_data *isi = ofono_modem_get_data(modem); uint8_t state; uint8_t action; if (isi == NULL || g_isi_msg_id(msg) != MCE_RF_STATE_IND) return; if (!g_isi_msg_data_get_byte(msg, 0, &state) || !g_isi_msg_data_get_byte(msg, 1, &action)) return; switch (action) { case MCE_READY: DBG("current rf state: %s (0x%02X)", mce_rf_state_name(state), state); if (isi->online_cbd) report_online(isi, state); break; case MCE_START: default: break; } } static void mce_query_cb(const GIsiMessage *msg, void *data) { struct ofono_modem *modem = data; struct isi_data *isi = ofono_modem_get_data(modem); uint8_t current; uint8_t target; if (!check_response_status(msg, MCE_MODEM_STATE_QUERY_RESP)) return; if (!g_isi_msg_data_get_byte(msg, 0, ¤t) || !g_isi_msg_data_get_byte(msg, 1, &target)) return; DBG("Modem state: current=%s (0x%02X) target=%s (0x%02X)", mce_modem_state_name(current), current, mce_modem_state_name(target), target); if (current == target) set_power_by_mce_state(modem, isi, current); } static gboolean bootstrap_current_state(gpointer user) { struct ofono_modem *om = user; struct isi_data *isi = ofono_modem_get_data(om); const uint8_t req[] = { MCE_MODEM_STATE_QUERY_REQ, 0x00, 0x00 /* Filler */ }; size_t len = sizeof(req); g_isi_client_send(isi->client, req, len, mce_query_cb, om, NULL); return FALSE; } static void reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_modem *om = data; struct isi_data *isi = ofono_modem_get_data(om); if (g_isi_msg_error(msg) < 0) return; ISI_RESOURCE_DBG(msg); g_isi_client_ind_subscribe(isi->client, MCE_MODEM_STATE_IND, mce_state_ind_cb, om); g_isi_client_ind_subscribe(isi->client, MCE_RF_STATE_IND, mce_rf_state_ind_cb, om); /* * FIXME: There is a theoretical race condition here: * g_isi_client_ind_subscribe() adds the actual message * sending for committing changes to subscriptions in idle * loop, which may or may not preserve ordering. Thus, we * might miss a state indication if the bootstrap request ends * up being sent first. */ g_idle_add(bootstrap_current_state, om); } static void phonet_status_cb(GIsiModem *modem, enum GIsiPhonetLinkState st, char const *ifname, void *data) { struct ofono_modem *om = data; struct isi_data *isi = ofono_modem_get_data(om); DBG("Link %s (%u) is %s", isi->ifname, g_isi_modem_index(isi->modem), st == PN_LINK_REMOVED ? "removed" : st == PN_LINK_DOWN ? "down" : "up"); isi->linkstate = st; if (st == PN_LINK_UP) g_isi_client_verify(isi->client, reachable_cb, om, NULL); else if (st == PN_LINK_DOWN) set_power_by_mce_state(om, isi, MCE_POWER_OFF); } static int u8500_probe(struct ofono_modem *modem) { const char *ifname = ofono_modem_get_string(modem, "Interface"); unsigned address = ofono_modem_get_integer(modem, "Address"); GIsiModem *isimodem; GIsiClient *client = NULL; GIsiPhonetNetlink *link = NULL; struct isi_data *isi = NULL; if (ifname == NULL) return -EINVAL; DBG("(%p) with %s", modem, ifname); isimodem = g_isi_modem_create_by_name(ifname); if (isimodem == NULL) { DBG("Interface=%s: %s", ifname, strerror(errno)); return -errno; } g_isi_modem_set_userdata(isimodem, modem); if (getenv("OFONO_ISI_DEBUG")) g_isi_modem_set_debug(isimodem, ofono_debug); if (getenv("OFONO_ISI_TRACE")) g_isi_modem_set_trace(isimodem, isi_trace); if (g_isi_pn_netlink_by_modem(isimodem)) { DBG("%s: %s", ifname, strerror(EBUSY)); errno = EBUSY; goto error; } link = g_isi_pn_netlink_start(isimodem, phonet_status_cb, modem); if (link == NULL) { DBG("%s: %s", ifname, strerror(errno)); goto error; } if (address) { int error = g_isi_pn_netlink_set_address(isimodem, address); if (error && error != -EEXIST) { DBG("g_isi_pn_netlink_set_address(): %s\n", strerror(-error)); errno = -error; goto error; } } isi = g_try_new0(struct isi_data, 1); if (isi == NULL) { errno = ENOMEM; goto error; } client = g_isi_client_create(isimodem, PN_MODEM_MCE); if (!client) goto error; g_isi_modem_set_device(isimodem, PN_DEV_MODEM); isi->modem = isimodem; isi->ifname = ifname; isi->link = link; isi->reported = -1; isi->client = client; ofono_modem_set_data(modem, isi); return 0; error: g_isi_pn_netlink_stop(link); g_isi_client_destroy(client); g_isi_modem_destroy(isimodem); g_free(isi); return -errno; } static void u8500_remove(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); ofono_modem_set_data(modem, NULL); if (isi == NULL) return; g_isi_pn_netlink_stop(isi->link); g_isi_client_destroy(isi->client); g_isi_modem_destroy(isi->modem); g_free(isi); } static void mce_state_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; struct ofono_modem *modem = cbd->user; ofono_modem_online_cb_t cb = cbd->cb; struct isi_data *isi = ofono_modem_get_data(modem); uint8_t cause; if (!check_response_status(msg, MCE_RF_STATE_RESP)) goto error; if (!g_isi_msg_data_get_byte(msg, 0, &cause)) goto error; DBG("MCE cause: %s (0x%02X)", mce_status_info(cause), cause); if (cause == MCE_OK) { isi->online_cbd = cbd; return; } if (cause == MCE_ALREADY_ACTIVE) { CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); return; } error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void u8500_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *data) { struct isi_data *isi = ofono_modem_get_data(modem); struct isi_cb_data *cbd = isi_cb_data_new(modem, cb, data); const uint8_t req[] = { MCE_RF_STATE_REQ, online ? MCE_RF_ON : MCE_RF_OFF, 0x00 }; DBG("(%p) with %s", modem, isi->ifname); if (cbd == NULL || isi == NULL) goto error; if (g_isi_client_send_with_timeout(isi->client, req, sizeof(req), MTC_STATE_REQ_TIMEOUT, mce_state_cb, cbd, NULL)) { isi->online = online; return; } error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void u8500_pre_sim(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); DBG("(%p) with %s", modem, isi->ifname); ofono_sim_create(modem, 0, "wgmodem2.5", isi->modem); ofono_devinfo_create(modem, 0, "u8500", isi->modem); ofono_voicecall_create(modem, 0, "isimodem", isi->modem); } static void u8500_post_sim(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); DBG("(%p) with %s", modem, isi->ifname); ofono_phonebook_create(modem, 0, "isimodem", isi->modem); ofono_call_forwarding_create(modem, 0, "isimodem", isi->modem); ofono_radio_settings_create(modem, 0, "isimodem", isi->modem); } static void u8500_post_online(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); DBG("(%p) with %s", modem, isi->ifname); ofono_netreg_create(modem, 0, "isimodem", isi->modem); ofono_sms_create(modem, 0, "isimodem", isi->modem); ofono_cbs_create(modem, 0, "isimodem", isi->modem); ofono_ussd_create(modem, 0, "isimodem", isi->modem); ofono_call_settings_create(modem, 0, "isimodem", isi->modem); ofono_call_barring_create(modem, 0, "isimodem", isi->modem); ofono_call_meter_create(modem, 0, "isimodem", isi->modem); ofono_gprs_create(modem, 0, "isimodem", isi->modem); } static int u8500_enable(struct ofono_modem *modem) { return 0; } static int u8500_disable(struct ofono_modem *modem) { return 0; } static void u8500_info_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_devinfo_query_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint8_t msgid; uint8_t status; msgid = g_isi_msg_id(msg); if (msgid != INFO_SERIAL_NUMBER_READ_RESP) goto error; if (g_isi_msg_error(msg) < 0) goto error; if (!g_isi_msg_data_get_byte(msg, 0, &status)) goto error; if (status != INFO_OK) goto error; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { uint8_t id = g_isi_sb_iter_get_id(&iter); uint8_t chars; char *info = NULL; if (id != INFO_SB_PRODUCT_INFO_MANUFACTURER && id != INFO_SB_PRODUCT_INFO_NAME && id != INFO_SB_MCUSW_VERSION && id != INFO_SB_SN_IMEI_PLAIN && id != INFO_SB_MODEMSW_VERSION) continue; if (g_isi_sb_iter_get_len(&iter) < 5) goto error; if (!g_isi_sb_iter_get_byte(&iter, &chars, 3)) goto error; if (!g_isi_sb_iter_get_latin_tag(&iter, &info, chars, 4)) goto error; CALLBACK_WITH_SUCCESS(cb, info, cbd->data); g_free(info); return; } error: CALLBACK_WITH_FAILURE(cb, "", cbd->data); } static void u8500_devinfo_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_devinfo *info = data; if (g_isi_msg_error(msg) < 0) return; ISI_RESOURCE_DBG(msg); ofono_devinfo_register(info); } static void u8500_query_manufacturer(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { CALLBACK_WITH_FAILURE(cb, "", data); } static void u8500_query_model(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { CALLBACK_WITH_FAILURE(cb, "", data); } static void u8500_query_revision(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct devinfo_data *dev = ofono_devinfo_get_data(info); struct isi_cb_data *cbd = isi_cb_data_new(dev, cb, data); const unsigned char msg[] = { INFO_SERIAL_NUMBER_READ_REQ, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* M_INFO_MODEMSW */ 0x00, 0x00 }; DBG(""); if (cbd == NULL || dev == NULL) goto error; if (g_isi_client_send(dev->client, msg, sizeof(msg), u8500_info_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, "", data); g_free(cbd); } static void u8500_query_serial(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { char imei[16]; /* IMEI 15 digits + 1 null*/ char numbers[] = "1234567890"; FILE *fp = fopen("/etc/imei", "r"); DBG(""); if (fp == NULL) { DBG("failed to open /etc/imei file"); goto error; } if (fgets(imei, 16, fp)) { DBG(" IMEI = %s", imei); if (15 == strspn(imei, numbers)) CALLBACK_WITH_SUCCESS(cb, imei, data); else { CALLBACK_WITH_FAILURE(cb, "", data); fclose(fp); goto error; } } fclose(fp); return; error: CALLBACK_WITH_FAILURE(cb, "", data); } static int u8500_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor, void *user) { GIsiModem *idx = user; struct devinfo_data *data = g_try_new0(struct devinfo_data, 1); if (data == NULL) return -ENOMEM; data->client = g_isi_client_create(idx, PN_MODEM_INFO); if (data->client == NULL) goto nomem; ofono_devinfo_set_data(info, data); g_isi_client_set_timeout(data->client, INFO_TIMEOUT); g_isi_client_verify(data->client, u8500_devinfo_reachable_cb, info, NULL); return 0; nomem: g_isi_client_destroy(data->client); g_free(data); return -ENOMEM; } static void u8500_devinfo_remove(struct ofono_devinfo *info) { struct devinfo_data *data = ofono_devinfo_get_data(info); ofono_devinfo_set_data(info, NULL); if (data == NULL) return; g_isi_client_destroy(data->client); g_free(data); } static struct ofono_modem_driver driver = { .name = "u8500", .probe = u8500_probe, .remove = u8500_remove, .set_online = u8500_online, .pre_sim = u8500_pre_sim, .post_sim = u8500_post_sim, .post_online = u8500_post_online, .enable = u8500_enable, .disable = u8500_disable, }; static struct ofono_devinfo_driver devinfo_driver = { .name = "u8500", .probe = u8500_devinfo_probe, .remove = u8500_devinfo_remove, .query_manufacturer = u8500_query_manufacturer, .query_model = u8500_query_model, .query_revision = u8500_query_revision, .query_serial = u8500_query_serial }; static int u8500_init(void) { int err; err = ofono_modem_driver_register(&driver); if (err < 0) return err; ofono_devinfo_driver_register(&devinfo_driver); return 0; } static void u8500_exit(void) { ofono_devinfo_driver_unregister(&devinfo_driver); ofono_modem_driver_unregister(&driver); } OFONO_PLUGIN_DEFINE(u8500, "ST-Ericsson U8500 modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, u8500_init, u8500_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/telit.c0000644000015600001650000003735512671500024021362 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bluez4.h" static const char *none_prefix[] = { NULL }; static const char *rsen_prefix[]= { "#RSEN:", NULL }; struct telit_data { GAtChat *chat; /* AT chat */ GAtChat *modem; /* Data port */ struct ofono_sim *sim; ofono_bool_t have_sim; ofono_bool_t sms_phonebook_added; struct ofono_modem *sap_modem; GIOChannel *bt_io; GIOChannel *hw_io; guint bt_watch; guint hw_watch; }; static void telit_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static void sap_close_io(struct ofono_modem *modem) { struct telit_data *data = ofono_modem_get_data(modem); if (data->bt_io != NULL) { int sk = g_io_channel_unix_get_fd(data->bt_io); shutdown(sk, SHUT_RDWR); g_io_channel_unref(data->bt_io); data->bt_io = NULL; } if (data->bt_watch > 0) g_source_remove(data->bt_watch); g_io_channel_unref(data->hw_io); data->hw_io = NULL; if (data->hw_watch > 0) g_source_remove(data->hw_watch); } static void bt_watch_remove(gpointer userdata) { struct ofono_modem *modem = userdata; struct telit_data *data = ofono_modem_get_data(modem); ofono_modem_set_powered(modem, FALSE); data->bt_watch = 0; } static gboolean bt_event_cb(GIOChannel *bt_io, GIOCondition condition, gpointer userdata) { struct ofono_modem *modem = userdata; struct telit_data *data = ofono_modem_get_data(modem); if (condition & G_IO_IN) { GIOStatus status; gsize bytes_read, bytes_written; gchar buf[300]; status = g_io_channel_read_chars(bt_io, buf, 300, &bytes_read, NULL); if (bytes_read > 0) g_io_channel_write_chars(data->hw_io, buf, bytes_read, &bytes_written, NULL); if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) return FALSE; return TRUE; } return FALSE; } static void hw_watch_remove(gpointer userdata) { struct ofono_modem *modem = userdata; struct telit_data *data = ofono_modem_get_data(modem); ofono_modem_set_powered(modem, FALSE); data->hw_watch = 0; } static gboolean hw_event_cb(GIOChannel *hw_io, GIOCondition condition, gpointer userdata) { struct ofono_modem *modem = userdata; struct telit_data *data = ofono_modem_get_data(modem); if (condition & G_IO_IN) { GIOStatus status; gsize bytes_read, bytes_written; gchar buf[300]; status = g_io_channel_read_chars(hw_io, buf, 300, &bytes_read, NULL); if (bytes_read > 0) g_io_channel_write_chars(data->bt_io, buf, bytes_read, &bytes_written, NULL); if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) return FALSE; return TRUE; } return FALSE; } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GAtSyntax *syntax; GIOChannel *channel; GAtChat *chat; GHashTable *options; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) return NULL; g_hash_table_insert(options, "Baud", "115200"); channel = g_at_tty_open(device, options); g_hash_table_destroy(options); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsmv1(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, telit_debug, debug); return chat; } static void switch_sim_state_status(struct ofono_modem *modem, int status) { struct telit_data *data = ofono_modem_get_data(modem); DBG("%p, SIM status: %d", modem, status); switch (status) { case 0: /* SIM not inserted */ if (data->have_sim == TRUE) { ofono_sim_inserted_notify(data->sim, FALSE); data->have_sim = FALSE; data->sms_phonebook_added = FALSE; } break; case 1: /* SIM inserted */ case 2: /* SIM inserted and PIN unlocked */ if (data->have_sim == FALSE) { ofono_sim_inserted_notify(data->sim, TRUE); data->have_sim = TRUE; } break; case 3: /* SIM inserted, SMS and phonebook ready */ if (data->sms_phonebook_added == FALSE) { ofono_phonebook_create(modem, 0, "atmodem", data->chat); ofono_sms_create(modem, 0, "atmodem", data->chat); data->sms_phonebook_added = TRUE; } break; default: ofono_warn("Unknown SIM state %d received", status); break; } } static void telit_qss_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; int status; GAtResultIter iter; DBG("%p", modem); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "#QSS:")) return; g_at_result_iter_next_number(&iter, &status); switch_sim_state_status(modem, status); } static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct telit_data *data = ofono_modem_get_data(modem); struct ofono_modem *m = data->sap_modem ? : modem; DBG("%p", modem); if (!ok) { g_at_chat_unref(data->chat); data->chat = NULL; ofono_modem_set_powered(m, FALSE); sap_close_io(modem); return; } /* * Switch data carrier detect signal off. * When the DCD is disabled the modem does not hangup anymore * after the data connection. */ g_at_chat_send(data->chat, "AT&C0", NULL, NULL, NULL, NULL); data->have_sim = FALSE; data->sms_phonebook_added = FALSE; ofono_modem_set_powered(m, TRUE); /* * Tell the modem not to automatically initiate auto-attach * proceedures on its own. */ g_at_chat_send(data->chat, "AT#AUTOATT=0", none_prefix, NULL, NULL, NULL); /* Follow sim state */ g_at_chat_register(data->chat, "#QSS:", telit_qss_notify, FALSE, modem, NULL); /* Enable sim state notification */ g_at_chat_send(data->chat, "AT#QSS=2", none_prefix, NULL, NULL, NULL); } static int telit_enable(struct ofono_modem *modem) { struct telit_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->chat = open_device(modem, "Aux", "Aux: "); if (data->chat == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_set_slave(data->modem, data->chat); /* * Disable command echo and * enable the Extended Error Result Codes */ g_at_chat_send(data->chat, "ATE0 +CMEE=1", none_prefix, NULL, NULL, NULL); /* * Disable sim state notification so that we sure get a notification * when we enable it again later and don't have to query it. */ g_at_chat_send(data->chat, "AT#QSS=0", none_prefix, NULL, NULL, NULL); /* Set phone functionality */ g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix, cfun_enable_cb, modem, NULL); return -EINPROGRESS; } static void telit_rsen_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct telit_data *data = ofono_modem_get_data(modem); int status; GAtResultIter iter; DBG("%p", modem); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "#RSEN:")) return; g_at_result_iter_next_number(&iter, &status); if (status == 0) { ofono_modem_set_powered(data->sap_modem, FALSE); sap_close_io(modem); return; } telit_enable(modem); } static void rsen_enable_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct telit_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (!ok) { ofono_modem_set_powered(data->sap_modem, FALSE); sap_close_io(modem); return; } } static void cfun_disable_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct telit_data *data = ofono_modem_get_data(modem); if(data->sap_modem) modem = data->sap_modem; DBG("%p", modem); g_at_chat_unref(data->chat); data->chat = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); data->sap_modem = NULL; } static int telit_disable(struct ofono_modem *modem) { struct telit_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->chat); g_at_chat_unregister_all(data->chat); /* Power down modem */ g_at_chat_send(data->chat, "AT+CFUN=0", none_prefix, cfun_disable_cb, modem, NULL); return -EINPROGRESS; } static void rsen_disable_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; DBG("%p", modem); sap_close_io(modem); telit_disable(modem); } static int telit_sap_open(void) { const char *device = "/dev/ttyUSB4"; struct termios ti; int fd; DBG("%s", device); fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd < 0) return -EINVAL; /* Switch TTY to raw mode */ memset(&ti, 0, sizeof(ti)); cfmakeraw(&ti); ti.c_cflag |= (B115200 | CLOCAL | CREAD); tcflush(fd, TCIOFLUSH); if (tcsetattr(fd, TCSANOW, &ti) < 0) { close(fd); return -EBADF; } return fd; } static int telit_sap_enable(struct ofono_modem *modem, struct ofono_modem *sap_modem, int bt_fd) { struct telit_data *data = ofono_modem_get_data(modem); int fd; DBG("%p", modem); fd = telit_sap_open(); if (fd < 0) goto error; data->hw_io = g_io_channel_unix_new(fd); if (data->hw_io == NULL) { close(fd); goto error; } g_io_channel_set_encoding(data->hw_io, NULL, NULL); g_io_channel_set_buffered(data->hw_io, FALSE); g_io_channel_set_close_on_unref(data->hw_io, TRUE); data->bt_io = g_io_channel_unix_new(bt_fd); if (data->bt_io == NULL) goto error; g_io_channel_set_encoding(data->bt_io, NULL, NULL); g_io_channel_set_buffered(data->bt_io, FALSE); g_io_channel_set_close_on_unref(data->bt_io, TRUE); data->hw_watch = g_io_add_watch_full(data->hw_io, G_PRIORITY_DEFAULT, G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN, hw_event_cb, modem, hw_watch_remove); data->bt_watch = g_io_add_watch_full(data->bt_io, G_PRIORITY_DEFAULT, G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN, bt_event_cb, modem, bt_watch_remove); data->sap_modem = sap_modem; g_at_chat_register(data->chat, "#RSEN:", telit_rsen_notify, FALSE, modem, NULL); g_at_chat_send(data->chat, "AT#NOPT=0", NULL, NULL, NULL, NULL); /* Set SAP functionality */ g_at_chat_send(data->chat, "AT#RSEN=1,1,0,2,0", rsen_prefix, rsen_enable_cb, modem, NULL); return -EINPROGRESS; error: shutdown(bt_fd, SHUT_RDWR); close(bt_fd); sap_close_io(modem); return -EINVAL; } static int telit_sap_disable(struct ofono_modem *modem) { struct telit_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_send(data->chat, "AT#RSEN=0", rsen_prefix, rsen_disable_cb, modem, NULL); return -EINPROGRESS; } static void telit_pre_sim(struct ofono_modem *modem) { struct telit_data *data = ofono_modem_get_data(modem); if (data->sap_modem) modem = data->sap_modem; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->chat); data->sim = ofono_sim_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat); ofono_voicecall_create(modem, 0, "atmodem", data->chat); } static void telit_post_sim(struct ofono_modem *modem) { struct telit_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; if (data->sap_modem) modem = data->sap_modem; DBG("%p", modem); gprs = ofono_gprs_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat); gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void telit_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct telit_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1,0" : "AT+CFUN=4,0"; DBG("modem %p %s", modem, online ? "online" : "offline"); g_at_chat_send(data->chat, command, none_prefix, set_online_cb, cbd, g_free); } static void telit_post_online(struct ofono_modem *modem) { struct telit_data *data = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; if(data->sap_modem) modem = data->sap_modem; DBG("%p", modem); ofono_netreg_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat); ofono_ussd_create(modem, 0, "atmodem", data->chat); ofono_call_forwarding_create(modem, 0, "atmodem", data->chat); ofono_call_settings_create(modem, 0, "atmodem", data->chat); ofono_call_meter_create(modem, 0, "atmodem", data->chat); ofono_call_barring_create(modem, 0, "atmodem", data->chat); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } static struct bluetooth_sap_driver sap_driver = { .name = "telit", .enable = telit_sap_enable, .pre_sim = telit_pre_sim, .post_sim = telit_post_sim, .set_online = telit_set_online, .post_online = telit_post_online, .disable = telit_sap_disable, }; static int telit_probe(struct ofono_modem *modem) { struct telit_data *data; DBG("%p", modem); data = g_try_new0(struct telit_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); bluetooth_sap_client_register(&sap_driver, modem); return 0; } static void telit_remove(struct ofono_modem *modem) { struct telit_data *data = ofono_modem_get_data(modem); DBG("%p", modem); bluetooth_sap_client_unregister(modem); ofono_modem_set_data(modem, NULL); /* Cleanup after hot-unplug */ g_at_chat_unref(data->chat); g_at_chat_unref(data->modem); g_free(data); } static struct ofono_modem_driver telit_driver = { .name = "telit", .probe = telit_probe, .remove = telit_remove, .enable = telit_enable, .disable = telit_disable, .set_online = telit_set_online, .pre_sim = telit_pre_sim, .post_sim = telit_post_sim, .post_online = telit_post_online, }; static int telit_init(void) { return ofono_modem_driver_register(&telit_driver); } static void telit_exit(void) { ofono_modem_driver_unregister(&telit_driver); } OFONO_PLUGIN_DEFINE(telit, "telit driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, telit_init, telit_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/ste.c0000644000015600001650000002763112671500024021030 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * * 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 #endif #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUM_CHAT 6 #define AT_DEFAULT 0 #define AT_NET 1 #define AT_VOICE 2 #define AT_GPRS 3 #define AT_SIM 4 #define AT_GNSS 5 #define MAX_PDP_CONTEXTS 4 static char *chat_prefixes[NUM_CHAT] = { "Default: ", "Net: ", "Voice: ", "GPRS: ", "SIM: ", "GNSS:" }; struct ste_data { GAtChat *chat[NUM_CHAT]; gboolean have_sim; struct ofono_sim *sim; }; enum ste_sim_state { SIM_STATE_NULL = 0, SIM_STATE_AWAITING_APP, SIM_STATE_BLOCKED, SIM_STATE_BLOCKED_FOREVER, SIM_STATE_WAIT_FOR_PIN, SIM_STATE_ACTIVE, SIM_STATE_TERMINATING, SIM_STATE_POWER_OFF }; static int ste_probe(struct ofono_modem *modem) { struct ste_data *data; DBG("%p", modem); data = g_try_new0(struct ste_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void ste_remove(struct ofono_modem *modem) { struct ste_data *data = ofono_modem_get_data(modem); int i; DBG("%p", modem); ofono_modem_set_data(modem, NULL); for (i = 0; i < NUM_CHAT; i++) g_at_chat_unref(data->chat[i]); g_free(data); } static void ste_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static void handle_sim_status(int status, struct ofono_modem *modem) { struct ste_data *data = ofono_modem_get_data(modem); DBG("SIM status:%d\n", status); switch (status) { case SIM_STATE_WAIT_FOR_PIN: case SIM_STATE_ACTIVE: case SIM_STATE_NULL: case SIM_STATE_AWAITING_APP: case SIM_STATE_BLOCKED: case SIM_STATE_BLOCKED_FOREVER: case SIM_STATE_TERMINATING: if (data->have_sim == FALSE) { if (data->sim) ofono_sim_inserted_notify(data->sim, TRUE); data->have_sim = TRUE; } break; case SIM_STATE_POWER_OFF: if (data->have_sim == TRUE) { if (data->sim) ofono_sim_inserted_notify(data->sim, FALSE); data->have_sim = FALSE; } break; } } static void handle_sim_state(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; int simnr, status; GAtResultIter iter; DBG("ok:%d", ok); if (!ok) return; g_at_result_iter_init(&iter, result); ofono_modem_set_powered(modem, TRUE); if (!g_at_result_iter_next(&iter, "*ESIMSR:")) return; if (!g_at_result_iter_next_number(&iter, &simnr)) return; if (!g_at_result_iter_next_number(&iter, &status)) return; handle_sim_status(status, modem); } static gboolean init_sim_reporting(gpointer user_data) { struct ofono_modem *modem = user_data; struct ste_data *data = ofono_modem_get_data(modem); data->have_sim = FALSE; g_at_chat_send(data->chat[AT_SIM], "AT*ESIMSR=1;*ESIMSR?", NULL, handle_sim_state, modem, NULL); return FALSE; } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct ste_data *data = ofono_modem_get_data(modem); int i; DBG(""); if (!ok) { ofono_modem_set_powered(modem, FALSE); for (i = 0; i < NUM_CHAT; i++) { g_at_chat_cancel_all(data->chat[i]); g_at_chat_unregister_all(data->chat[i]); g_at_chat_unref(data->chat[i]); data->chat[i] = NULL; } return; } init_sim_reporting(modem); } static GIOChannel *ste_create_channel(struct ofono_modem *modem) { GIOChannel *channel; const char *device; int fd; DBG("%p", modem); device = ofono_modem_get_string(modem, "Device"); if (device == NULL) { struct sockaddr_caif addr; int err; const char *interface; /* Create a CAIF socket for AT Service */ fd = socket(AF_CAIF, SOCK_STREAM, CAIFPROTO_AT); if (fd < 0) { ofono_error("Failed to create CAIF socket for AT"); return NULL; } /* Bind CAIF socket to specified interface */ interface = ofono_modem_get_string(modem, "Interface"); if (interface) { struct ifreq ifreq; memset(&ifreq, 0, sizeof(ifreq)); strcpy(ifreq.ifr_name, interface); err = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq)); if (err < 0) { ofono_error("Failed to bind caif socket " "to interface"); close(fd); return NULL; } } memset(&addr, 0, sizeof(addr)); addr.family = AF_CAIF; addr.u.at.type = CAIF_ATTYPE_PLAIN; /* Connect to the AT Service at the modem */ err = connect(fd, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { ofono_error("Failed to connect CAIF socket for AT"); close(fd); return NULL; } } else { fd = open(device, O_RDWR); if (fd < 0) { ofono_error("Failed to open device %s", device); return NULL; } } channel = g_io_channel_unix_new(fd); if (channel == NULL) { close(fd); return NULL; } g_io_channel_set_close_on_unref(channel, TRUE); return channel; } static void esimsr_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; int status; GAtResultIter iter; DBG(""); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "*ESIMSR:")) return; if (!g_at_result_iter_next_number(&iter, &status)) return; handle_sim_status(status, modem); } static int ste_enable(struct ofono_modem *modem) { struct ste_data *data = ofono_modem_get_data(modem); GIOChannel *channel; GAtSyntax *syntax; int i; for (i = 0; i < NUM_CHAT; i++) { channel = ste_create_channel(modem); syntax = g_at_syntax_new_gsm_permissive(); data->chat[i] = g_at_chat_new_blocking(channel, syntax); if (data->chat[i] == NULL) { g_io_channel_unref(channel); g_at_syntax_unref(syntax); DBG("Failed to create AT chat %s", chat_prefixes[i]); goto error; } if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(data->chat[i], ste_debug, chat_prefixes[i]); g_at_chat_send(data->chat[i], "AT&F E0 V1 X4 &C1 +CMEE=1", NULL, NULL, NULL, NULL); /* All STE modems support UTF-8 */ g_at_chat_send(data->chat[i], "AT+CSCS=\"UTF-8\"", NULL, NULL, NULL, NULL); g_io_channel_unref(channel); g_at_syntax_unref(syntax); } g_at_chat_send(data->chat[AT_DEFAULT], "AT+CFUN=4", NULL, cfun_enable, modem, NULL); g_at_chat_register(data->chat[AT_SIM], "*ESIMSR:", esimsr_notify, FALSE, modem, NULL); return -EINPROGRESS; error: /* Unref open chats if any */ while (i--) { g_at_chat_unref(data->chat[i]); data->chat[i] = NULL; } return -EIO; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct ste_data *data = ofono_modem_get_data(modem); int i; DBG(""); for (i = 0; i < NUM_CHAT; i++) { g_at_chat_unref(data->chat[i]); data->chat[i] = NULL; } if (ok) ofono_modem_set_powered(modem, FALSE); } static int ste_disable(struct ofono_modem *modem) { struct ste_data *data = ofono_modem_get_data(modem); int i; DBG("%p", modem); for (i = 0; i < NUM_CHAT; i++) { g_at_chat_cancel_all(data->chat[i]); g_at_chat_unregister_all(data->chat[i]); } g_at_chat_send(data->chat[AT_DEFAULT], "AT+CFUN=4", NULL, cfun_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; if (ok) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); } static void ste_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct ste_data *data = ofono_modem_get_data(modem); GAtChat *chat = data->chat[AT_DEFAULT]; struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4"; DBG("modem %p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(chat, command, NULL, set_online_cb, cbd, g_free)) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void ste_pre_sim(struct ofono_modem *modem) { struct ste_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]); data->sim = ofono_sim_create(modem, OFONO_VENDOR_MBM, "atmodem", data->chat[AT_SIM]); ofono_voicecall_create(modem, 0, "stemodem", data->chat[AT_VOICE]); } static void ste_post_sim(struct ofono_modem *modem) { struct ste_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_stk_create(modem, 0, "mbmmodem", data->chat[AT_SIM]); ofono_phonebook_create(modem, 0, "atmodem", data->chat[AT_SIM]); ofono_radio_settings_create(modem, 0, "stemodem", data->chat[AT_NET]); ofono_sms_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]); } static void ste_post_online(struct ofono_modem *modem) { struct ste_data *data = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; struct ofono_gprs *gprs; struct ofono_gprs_context *gc; int i; DBG("%p", modem); ofono_ussd_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]); ofono_call_forwarding_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]); ofono_call_settings_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]); ofono_netreg_create(modem, OFONO_VENDOR_MBM, "atmodem", data->chat[AT_NET]); ofono_call_meter_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]); ofono_call_barring_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]); ofono_call_volume_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]); ofono_cbs_create(modem, 0, "atmodem", data->chat[AT_DEFAULT]); ofono_gnss_create(modem, OFONO_VENDOR_STE, "atmodem", data->chat[AT_GNSS]); gprs = ofono_gprs_create(modem, OFONO_VENDOR_MBM, "atmodem", data->chat[AT_GPRS]); if (gprs) { for (i = 0; i < MAX_PDP_CONTEXTS; i++) { gc = ofono_gprs_context_create(modem, 0, "stemodem", data->chat[AT_GPRS]); if (gc == NULL) break; ofono_gprs_add_context(gprs, gc); } } mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } static struct ofono_modem_driver ste_driver = { .name = "ste", .probe = ste_probe, .remove = ste_remove, .enable = ste_enable, .disable = ste_disable, .set_online = ste_set_online, .pre_sim = ste_pre_sim, .post_sim = ste_post_sim, .post_online = ste_post_online, }; static int ste_init(void) { return ofono_modem_driver_register(&ste_driver); } static void ste_exit(void) { ofono_modem_driver_unregister(&ste_driver); } OFONO_PLUGIN_DEFINE(ste, "ST-Ericsson modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, ste_init, ste_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/mbpi.c0000644000015600001650000003674012671500024021165 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #ifndef MBPI_DATABASE #define MBPI_DATABASE "/usr/share/mobile-broadband-provider-info/" \ "serviceproviders.xml" #endif #include "mbpi.h" #define _(x) case x: return (#x) enum MBPI_ERROR { MBPI_ERROR_DUPLICATE, }; struct gsm_data { const char *match_mcc; const char *match_mnc; enum ofono_gprs_context_type match_type; GSList *apns; gboolean match_found; gboolean allow_duplicates; }; struct cdma_data { const char *match_sid; char *provider_name; gboolean match_found; }; const char *mbpi_ap_type(enum ofono_gprs_context_type type) { switch (type) { _(OFONO_GPRS_CONTEXT_TYPE_ANY); _(OFONO_GPRS_CONTEXT_TYPE_INTERNET); _(OFONO_GPRS_CONTEXT_TYPE_MMS); _(OFONO_GPRS_CONTEXT_TYPE_WAP); _(OFONO_GPRS_CONTEXT_TYPE_IMS); _(OFONO_GPRS_CONTEXT_TYPE_IA); } return "OFONO_GPRS_CONTEXT_TYPE_"; } static GQuark mbpi_error_quark(void) { return g_quark_from_static_string("ofono-mbpi-error-quark"); } void mbpi_ap_free(struct ofono_gprs_provision_data *ap) { g_free(ap->name); g_free(ap->apn); g_free(ap->username); g_free(ap->password); g_free(ap->message_proxy); g_free(ap->message_center); g_free(ap); } static void mbpi_g_set_error(GMarkupParseContext *context, GError **error, GQuark domain, gint code, const gchar *fmt, ...) { va_list ap; gint line_number, char_number; g_markup_parse_context_get_position(context, &line_number, &char_number); va_start(ap, fmt); *error = g_error_new_valist(domain, code, fmt, ap); va_end(ap); g_prefix_error(error, "%s:%d ", MBPI_DATABASE, line_number); } static void text_handler(GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer userdata, GError **error) { char **string = userdata; *string = g_strndup(text, text_len); } static const GMarkupParser text_parser = { NULL, NULL, text_handler, NULL, NULL, }; static void authentication_start(GMarkupParseContext *context, const gchar **attribute_names, const gchar **attribute_values, enum ofono_gprs_auth_method *auth_method, GError **error) { const char *text = NULL; int i; for (i = 0; attribute_names[i]; i++) if (g_str_equal(attribute_names[i], "method") == TRUE) text = attribute_values[i]; if (text == NULL) { mbpi_g_set_error(context, error, G_MARKUP_ERROR, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "Missing attribute: method"); return; } if (strcmp(text, "chap") == 0) *auth_method = OFONO_GPRS_AUTH_METHOD_CHAP; else if (strcmp(text, "pap") == 0) *auth_method = OFONO_GPRS_AUTH_METHOD_PAP; else mbpi_g_set_error(context, error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "Unknown authentication method: %s", text); } static void usage_start(GMarkupParseContext *context, const gchar **attribute_names, const gchar **attribute_values, enum ofono_gprs_context_type *type, GError **error) { const char *text = NULL; int i; for (i = 0; attribute_names[i]; i++) if (g_str_equal(attribute_names[i], "type") == TRUE) text = attribute_values[i]; if (text == NULL) { mbpi_g_set_error(context, error, G_MARKUP_ERROR, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "Missing attribute: type"); return; } if (strcmp(text, "internet") == 0) *type = OFONO_GPRS_CONTEXT_TYPE_INTERNET; else if (strcmp(text, "mms") == 0) *type = OFONO_GPRS_CONTEXT_TYPE_MMS; else if (strcmp(text, "wap") == 0) *type = OFONO_GPRS_CONTEXT_TYPE_WAP; else mbpi_g_set_error(context, error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "Unknown usage attribute: %s", text); } static void apn_start(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer userdata, GError **error) { struct ofono_gprs_provision_data *apn = userdata; if (g_str_equal(element_name, "name")) g_markup_parse_context_push(context, &text_parser, &apn->name); else if (g_str_equal(element_name, "username")) g_markup_parse_context_push(context, &text_parser, &apn->username); else if (g_str_equal(element_name, "password")) g_markup_parse_context_push(context, &text_parser, &apn->password); else if (g_str_equal(element_name, "authentication")) authentication_start(context, attribute_names, attribute_values, &apn->auth_method, error); else if (g_str_equal(element_name, "mmsc")) g_markup_parse_context_push(context, &text_parser, &apn->message_center); else if (g_str_equal(element_name, "mmsproxy")) g_markup_parse_context_push(context, &text_parser, &apn->message_proxy); else if (g_str_equal(element_name, "usage")) usage_start(context, attribute_names, attribute_values, &apn->type, error); } static void apn_end(GMarkupParseContext *context, const gchar *element_name, gpointer userdata, GError **error) { if (g_str_equal(element_name, "name") || g_str_equal(element_name, "username") || g_str_equal(element_name, "password") || g_str_equal(element_name, "mmsc") || g_str_equal(element_name, "mmsproxy")) g_markup_parse_context_pop(context); } static void apn_error(GMarkupParseContext *context, GError *error, gpointer userdata) { /* * Note that even if the error happened in a subparser, this will * be called. So we always perform cleanup of the allocated * provision data */ mbpi_ap_free(userdata); } static const GMarkupParser apn_parser = { apn_start, apn_end, NULL, NULL, apn_error, }; static const GMarkupParser skip_parser = { NULL, NULL, NULL, NULL, NULL, }; static void network_id_handler(GMarkupParseContext *context, struct gsm_data *gsm, const gchar **attribute_names, const gchar **attribute_values, GError **error) { const char *mcc = NULL, *mnc = NULL; int i; for (i = 0; attribute_names[i]; i++) { if (g_str_equal(attribute_names[i], "mcc") == TRUE) mcc = attribute_values[i]; if (g_str_equal(attribute_names[i], "mnc") == TRUE) mnc = attribute_values[i]; } if (mcc == NULL) { mbpi_g_set_error(context, error, G_MARKUP_ERROR, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "Missing attribute: mcc"); return; } if (mnc == NULL) { mbpi_g_set_error(context, error, G_MARKUP_ERROR, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "Missing attribute: mnc"); return; } if (g_str_equal(mcc, gsm->match_mcc) && g_str_equal(mnc, gsm->match_mnc)) gsm->match_found = TRUE; } static void apn_handler(GMarkupParseContext *context, struct gsm_data *gsm, const gchar **attribute_names, const gchar **attribute_values, GError **error) { struct ofono_gprs_provision_data *ap; const char *apn; int i; if (gsm->match_found == FALSE) { g_markup_parse_context_push(context, &skip_parser, NULL); return; } for (i = 0, apn = NULL; attribute_names[i]; i++) { if (g_str_equal(attribute_names[i], "value") == FALSE) continue; apn = attribute_values[i]; break; } if (apn == NULL) { mbpi_g_set_error(context, error, G_MARKUP_ERROR, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "APN attribute missing"); return; } ap = g_new0(struct ofono_gprs_provision_data, 1); ap->apn = g_strdup(apn); ap->type = OFONO_GPRS_CONTEXT_TYPE_INTERNET; ap->proto = OFONO_GPRS_PROTO_IP; ap->auth_method = OFONO_GPRS_AUTH_METHOD_CHAP; g_markup_parse_context_push(context, &apn_parser, ap); } static void sid_handler(GMarkupParseContext *context, struct cdma_data *cdma, const gchar **attribute_names, const gchar **attribute_values, GError **error) { const char *sid = NULL; int i; for (i = 0; attribute_names[i]; i++) { if (g_str_equal(attribute_names[i], "value") == FALSE) continue; sid = attribute_values[i]; break; } if (sid == NULL) { mbpi_g_set_error(context, error, G_MARKUP_ERROR, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "Missing attribute: sid"); return; } if (g_str_equal(sid, cdma->match_sid)) cdma->match_found = TRUE; } static void gsm_start(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer userdata, GError **error) { if (g_str_equal(element_name, "network-id")) { struct gsm_data *gsm = userdata; /* * For entries with multiple network-id elements, don't bother * searching if we already have a match */ if (gsm->match_found == TRUE) return; network_id_handler(context, userdata, attribute_names, attribute_values, error); } else if (g_str_equal(element_name, "apn")) apn_handler(context, userdata, attribute_names, attribute_values, error); } static void gsm_end(GMarkupParseContext *context, const gchar *element_name, gpointer userdata, GError **error) { struct gsm_data *gsm; struct ofono_gprs_provision_data *ap; if (!g_str_equal(element_name, "apn")) return; gsm = userdata; ap = g_markup_parse_context_pop(context); if (ap == NULL) return; if (gsm->allow_duplicates == FALSE) { GSList *l; for (l = gsm->apns; l; l = l->next) { struct ofono_gprs_provision_data *pd = l->data; if (pd->type != ap->type) continue; mbpi_g_set_error(context, error, mbpi_error_quark(), MBPI_ERROR_DUPLICATE, "Duplicate context detected"); mbpi_ap_free(ap); return; } } if (gsm->match_type == OFONO_GPRS_CONTEXT_TYPE_ANY || gsm->match_type == ap->type) gsm->apns = g_slist_append(gsm->apns, ap); } static const GMarkupParser gsm_parser = { gsm_start, gsm_end, NULL, NULL, NULL, }; static void cdma_start(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer userdata, GError **error) { if (g_str_equal(element_name, "sid")) { struct cdma_data *cdma = userdata; /* * For entries with multiple sid elements, don't bother * searching if we already have a match */ if (cdma->match_found == TRUE) return; sid_handler(context, cdma, attribute_names, attribute_values, error); } } static const GMarkupParser cdma_parser = { cdma_start, NULL, NULL, NULL, NULL, }; static void provider_start(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer userdata, GError **error) { if (g_str_equal(element_name, "name")) { struct cdma_data *cdma = userdata; g_free(cdma->provider_name); cdma->provider_name = NULL; g_markup_parse_context_push(context, &text_parser, &cdma->provider_name); } else if (g_str_equal(element_name, "gsm")) g_markup_parse_context_push(context, &skip_parser, NULL); else if (g_str_equal(element_name, "cdma")) g_markup_parse_context_push(context, &cdma_parser, userdata); } static void provider_end(GMarkupParseContext *context, const gchar *element_name, gpointer userdata, GError **error) { if (g_str_equal(element_name, "name") || g_str_equal(element_name, "gsm") || g_str_equal(element_name, "cdma")) g_markup_parse_context_pop(context); } static const GMarkupParser provider_parser = { provider_start, provider_end, NULL, NULL, NULL, }; static void toplevel_gsm_start(GMarkupParseContext *context, const gchar *element_name, const gchar **atribute_names, const gchar **attribute_values, gpointer userdata, GError **error) { struct gsm_data *gsm = userdata; if (g_str_equal(element_name, "gsm")) { gsm->match_found = FALSE; g_markup_parse_context_push(context, &gsm_parser, gsm); } else if (g_str_equal(element_name, "cdma")) g_markup_parse_context_push(context, &skip_parser, NULL); } static void toplevel_gsm_end(GMarkupParseContext *context, const gchar *element_name, gpointer userdata, GError **error) { if (g_str_equal(element_name, "gsm") || g_str_equal(element_name, "cdma")) g_markup_parse_context_pop(context); } static const GMarkupParser toplevel_gsm_parser = { toplevel_gsm_start, toplevel_gsm_end, NULL, NULL, NULL, }; static void toplevel_cdma_start(GMarkupParseContext *context, const gchar *element_name, const gchar **atribute_names, const gchar **attribute_values, gpointer userdata, GError **error) { struct cdma_data *cdma = userdata; if (g_str_equal(element_name, "provider") == FALSE) return; if (cdma->match_found == TRUE) g_markup_parse_context_push(context, &skip_parser, NULL); else g_markup_parse_context_push(context, &provider_parser, cdma); } static void toplevel_cdma_end(GMarkupParseContext *context, const gchar *element_name, gpointer userdata, GError **error) { if (g_str_equal(element_name, "provider")) g_markup_parse_context_pop(context); } static const GMarkupParser toplevel_cdma_parser = { toplevel_cdma_start, toplevel_cdma_end, NULL, NULL, NULL, }; static gboolean mbpi_parse(const GMarkupParser *parser, gpointer userdata, GError **error) { struct stat st; char *db; int fd; GMarkupParseContext *context; gboolean ret; fd = open(MBPI_DATABASE, O_RDONLY); if (fd < 0) { g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "open(%s) failed: %s", MBPI_DATABASE, g_strerror(errno)); return FALSE; } if (fstat(fd, &st) < 0) { close(fd); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "fstat(%s) failed: %s", MBPI_DATABASE, g_strerror(errno)); return FALSE; } db = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (db == MAP_FAILED) { close(fd); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "mmap(%s) failed: %s", MBPI_DATABASE, g_strerror(errno)); return FALSE; } context = g_markup_parse_context_new(parser, G_MARKUP_TREAT_CDATA_AS_TEXT, userdata, NULL); ret = g_markup_parse_context_parse(context, db, st.st_size, error); if (ret == TRUE) g_markup_parse_context_end_parse(context, error); munmap(db, st.st_size); close(fd); g_markup_parse_context_free(context); return ret; } GSList *mbpi_lookup_apn(const char *mcc, const char *mnc, enum ofono_gprs_context_type type, gboolean allow_duplicates, GError **error) { struct gsm_data gsm; GSList *l; memset(&gsm, 0, sizeof(gsm)); gsm.match_mcc = mcc; gsm.match_mnc = mnc; gsm.match_type = type; gsm.allow_duplicates = allow_duplicates; if (mbpi_parse(&toplevel_gsm_parser, &gsm, error) == FALSE) { for (l = gsm.apns; l; l = l->next) mbpi_ap_free(l->data); g_slist_free(gsm.apns); gsm.apns = NULL; } return gsm.apns; } char *mbpi_lookup_cdma_provider_name(const char *sid, GError **error) { struct cdma_data cdma; memset(&cdma, 0, sizeof(cdma)); cdma.match_sid = sid; if (mbpi_parse(&toplevel_cdma_parser, &cdma, error) == FALSE) { g_free(cdma.provider_name); cdma.provider_name = NULL; } return cdma.provider_name; } ofono-1.17.bzr6912+16.04.20160314.3/plugins/novatel.c0000644000015600001650000002073412671500024021702 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; static const char *nwdmat_prefix[] = { "$NWDMAT:", NULL }; struct novatel_data { GAtChat *modem; GAtChat *aux; gint dmat_mode; }; static int novatel_probe(struct ofono_modem *modem) { struct novatel_data *data; DBG("%p", modem); data = g_try_new0(struct novatel_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void novatel_remove(struct ofono_modem *modem) { struct novatel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup after hot-unplug */ g_at_chat_unref(data->aux); g_free(data); } static void novatel_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { GAtChat *chat; GAtSyntax *syntax; GIOChannel *channel; const char *device; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, novatel_debug, debug); return chat; } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct novatel_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_unref(data->aux); data->aux = NULL; } ofono_modem_set_powered(modem, ok); } static void nwdmat_action(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct novatel_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) goto error; data->dmat_mode = 1; data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) goto error; g_at_chat_send(data->modem, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL); /* Check for all supported technologies */ g_at_chat_send(data->aux, "AT$CNTI=2", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix, cfun_enable, modem, NULL); return; error: g_at_chat_unref(data->aux); data->aux = NULL; ofono_modem_set_powered(modem, FALSE); } static void nwdmat_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct novatel_data *data = ofono_modem_get_data(modem); GAtResultIter iter; gint dmat_mode; DBG(""); if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "$NWDMAT:")) goto error; if (!g_at_result_iter_next_number(&iter, &dmat_mode)) goto error; if (dmat_mode == 1) { nwdmat_action(TRUE, result, user_data); return; } g_at_chat_send(data->aux, "AT$NWDMAT=1", nwdmat_prefix, nwdmat_action, modem, NULL); return; error: g_at_chat_unref(data->aux); data->aux = NULL; ofono_modem_set_powered(modem, FALSE); } static int novatel_enable(struct ofono_modem *modem) { struct novatel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->aux = open_device(modem, "Aux", "Aux: "); if (data->aux == NULL) return -EIO; g_at_chat_blacklist_terminator(data->aux, G_AT_CHAT_TERMINATOR_NO_CARRIER); g_at_chat_send(data->aux, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL); /* Check mode of seconday port */ g_at_chat_send(data->aux, "AT$NWDMAT?", nwdmat_prefix, nwdmat_query, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct novatel_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->aux); data->aux = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int novatel_disable(struct ofono_modem *modem) { struct novatel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->aux); g_at_chat_unregister_all(data->aux); g_at_chat_send(data->aux, "AT$NWDMAT=0", nwdmat_prefix, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CFUN=0", none_prefix, cfun_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void novatel_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct novatel_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4"; DBG("modem %p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(data->aux, command, none_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void novatel_pre_sim(struct ofono_modem *modem) { struct novatel_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->aux); sim = ofono_sim_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->aux); if (sim) ofono_sim_inserted_notify(sim, TRUE); } static void novatel_post_sim(struct ofono_modem *modem) { struct novatel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_phonebook_create(modem, 0, "atmodem", data->aux); ofono_radio_settings_create(modem, 0, "nwmodem", data->aux); ofono_sms_create(modem, OFONO_VENDOR_NOVATEL, "atmodem", data->aux); } static void novatel_post_online(struct ofono_modem *modem) { struct novatel_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_netreg_create(modem, OFONO_VENDOR_NOVATEL, "atmodem", data->aux); ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->aux); ofono_ussd_create(modem, 0, "atmodem", data->aux); gprs = ofono_gprs_create(modem, OFONO_VENDOR_NOVATEL, "atmodem", data->aux); gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static struct ofono_modem_driver novatel_driver = { .name = "novatel", .probe = novatel_probe, .remove = novatel_remove, .enable = novatel_enable, .disable = novatel_disable, .set_online = novatel_set_online, .pre_sim = novatel_pre_sim, .post_sim = novatel_post_sim, .post_online = novatel_post_online, }; static int novatel_init(void) { return ofono_modem_driver_register(&novatel_driver); } static void novatel_exit(void) { ofono_modem_driver_unregister(&novatel_driver); } OFONO_PLUGIN_DEFINE(novatel, "Novatel Wireless modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, novatel_init, novatel_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/hfp_ag_bluez5.c0000644000015600001650000003012412671500024022736 0ustar pbuserpbgroup00000000000000/* * oFono - Open Source Telephony * * Copyright (C) 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 #endif #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include typedef struct GAtChat GAtChat; typedef struct GAtResult GAtResult; #include "drivers/atmodem/atutil.h" #include "hfp.h" #include "bluez5.h" #include "bluetooth.h" #ifndef DBUS_TYPE_UNIX_FD #define DBUS_TYPE_UNIX_FD -1 #endif #define HFP_AG_EXT_PROFILE_PATH "/bluetooth/profile/hfp_ag" #define BT_ADDR_SIZE 18 #define HFP_AG_DRIVER "hfp-ag-driver" static guint modemwatch_id; static GList *modems; static GHashTable *sim_hash = NULL; static GHashTable *connection_hash; static int hfp_card_probe(struct ofono_handsfree_card *card, unsigned int vendor, void *data) { DBG(""); return 0; } static void hfp_card_remove(struct ofono_handsfree_card *card) { DBG(""); } static void codec_negotiation_done_cb(int err, void *data) { struct cb_data *cbd = data; ofono_handsfree_card_connect_cb_t cb = cbd->cb; DBG("err %d", err); if (err < 0) { CALLBACK_WITH_FAILURE(cb, cbd->data); goto done; } /* * We don't have anything to do at this point as when the * codec negotiation succeeded the emulator internally * already triggered the SCO connection setup of the * handsfree card which also takes over the processing * of the pending dbus message */ done: g_free(cbd); } static void hfp_card_connect(struct ofono_handsfree_card *card, ofono_handsfree_card_connect_cb_t cb, void *data) { int err; struct ofono_emulator *em = ofono_handsfree_card_get_data(card); struct cb_data *cbd; DBG(""); cbd = cb_data_new(cb, data); /* * The emulator core will take care if the remote side supports * codec negotiation or not. */ err = ofono_emulator_start_codec_negotiation(em, codec_negotiation_done_cb, cbd); if (err < 0) { CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); return; } /* * We hand over to the emulator core here to establish the * SCO connection once the codec is negotiated * */ } static void hfp_sco_connected_hint(struct ofono_handsfree_card *card) { DBG(""); } static struct ofono_handsfree_card_driver hfp_ag_driver = { .name = HFP_AG_DRIVER, .probe = hfp_card_probe, .remove = hfp_card_remove, .connect = hfp_card_connect, .sco_connected_hint = hfp_sco_connected_hint, }; static void connection_destroy(gpointer data) { int fd = GPOINTER_TO_INT(data); DBG("fd %d", fd); close(fd); } static gboolean io_hup_cb(GIOChannel *io, GIOCondition cond, gpointer data) { char *device = data; DBG("Remove %s", device); g_hash_table_remove(connection_hash, device); return FALSE; } static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessageIter entry; const char *device; GIOChannel *io; int fd, fd_dup; struct sockaddr_rc saddr; socklen_t optlen; struct ofono_emulator *em; char local[BT_ADDR_SIZE], remote[BT_ADDR_SIZE]; struct ofono_handsfree_card *card; int err; DBG("Profile handler NewConnection"); if (dbus_message_iter_init(msg, &entry) == FALSE) goto invalid; if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_OBJECT_PATH) goto invalid; dbus_message_iter_get_basic(&entry, &device); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_UNIX_FD) goto invalid; dbus_message_iter_get_basic(&entry, &fd); if (fd < 0) goto invalid; dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY) { close(fd); goto invalid; } if (modems == NULL) { close(fd); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "No voice call capable modem"); } memset(&saddr, 0, sizeof(saddr)); optlen = sizeof(saddr); if (getsockname(fd, (struct sockaddr *) &saddr, &optlen) < 0) { err = errno; ofono_error("RFCOMM getsockname(): %s (%d)", strerror(err), err); close(fd); goto invalid; } bt_ba2str(&saddr.rc_bdaddr, local); memset(&saddr, 0, sizeof(saddr)); optlen = sizeof(saddr); if (getpeername(fd, (struct sockaddr *) &saddr, &optlen) < 0) { err = errno; ofono_error("RFCOMM getpeername(): %s (%d)", strerror(err), err); close(fd); goto invalid; } bt_ba2str(&saddr.rc_bdaddr, remote); em = ofono_emulator_create(modems, OFONO_EMULATOR_TYPE_HFP); if (em == NULL) { close(fd); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Not enough resources"); } ofono_emulator_register(em, fd); fd_dup = dup(fd); io = g_io_channel_unix_new(fd_dup); g_io_add_watch_full(io, G_PRIORITY_DEFAULT, G_IO_HUP, io_hup_cb, g_strdup(device), g_free); g_io_channel_unref(io); card = ofono_handsfree_card_create(0, OFONO_HANDSFREE_CARD_TYPE_GATEWAY, HFP_AG_DRIVER, em); ofono_handsfree_card_set_data(card, em); ofono_handsfree_card_set_local(card, local); ofono_handsfree_card_set_remote(card, remote); ofono_emulator_set_handsfree_card(em, card); g_hash_table_insert(connection_hash, g_strdup(device), GINT_TO_POINTER(fd_dup)); return dbus_message_new_method_return(msg); invalid: return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Invalid arguments in method call"); } static DBusMessage *profile_release(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBG("Profile handler Release"); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".NotImplemented", "Implementation not provided"); } static DBusMessage *profile_cancel(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBG("Profile handler Cancel"); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".NotImplemented", "Implementation not provided"); } static DBusMessage *profile_disconnection(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessageIter iter; const char *device; gpointer fd; DBG("Profile handler RequestDisconnection"); if (!dbus_message_iter_init(msg, &iter)) goto invalid; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) goto invalid; dbus_message_iter_get_basic(&iter, &device); DBG("%s", device); fd = g_hash_table_lookup(connection_hash, device); if (fd == NULL) goto invalid; shutdown(GPOINTER_TO_INT(fd), SHUT_RDWR); g_hash_table_remove(connection_hash, device); return dbus_message_new_method_return(msg); invalid: return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Invalid arguments in method call"); } static const GDBusMethodTable profile_methods[] = { { GDBUS_ASYNC_METHOD("NewConnection", GDBUS_ARGS({ "device", "o"}, { "fd", "h"}, { "fd_properties", "a{sv}" }), NULL, profile_new_connection) }, { GDBUS_METHOD("Release", NULL, NULL, profile_release) }, { GDBUS_METHOD("Cancel", NULL, NULL, profile_cancel) }, { GDBUS_METHOD("RequestDisconnection", GDBUS_ARGS({"device", "o"}), NULL, profile_disconnection) }, { } }; static void sim_state_watch(enum ofono_sim_state new_state, void *data) { struct ofono_modem *modem = data; DBusConnection *conn = ofono_dbus_get_connection(); if (new_state != OFONO_SIM_STATE_READY) { if (modems == NULL) return; modems = g_list_remove(modems, modem); if (modems != NULL) return; bt_unregister_profile(conn, HFP_AG_EXT_PROFILE_PATH); return; } if (__ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_VOICECALL) == NULL) return; modems = g_list_append(modems, modem); if (modems->next != NULL) return; bt_register_profile(conn, HFP_AG_UUID, HFP_VERSION_1_7, "hfp_ag", HFP_AG_EXT_PROFILE_PATH, NULL, 0); } static gboolean sim_watch_remove(gpointer key, gpointer value, gpointer user_data) { struct ofono_sim *sim = key; ofono_sim_remove_state_watch(sim, GPOINTER_TO_UINT(value)); return TRUE; } static void sim_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_sim *sim = __ofono_atom_get_data(atom); struct ofono_modem *modem = data; int watch; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { sim_state_watch(OFONO_SIM_STATE_NOT_PRESENT, modem); sim_watch_remove(sim, g_hash_table_lookup(sim_hash, sim), NULL); g_hash_table_remove(sim_hash, sim); return; } watch = ofono_sim_add_state_watch(sim, sim_state_watch, modem, NULL); g_hash_table_insert(sim_hash, sim, GUINT_TO_POINTER(watch)); sim_state_watch(ofono_sim_get_state(sim), modem); } static void voicecall_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_atom *sim_atom; struct ofono_sim *sim; struct ofono_modem *modem; DBusConnection *conn = ofono_dbus_get_connection(); if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) return; /* * This logic is only intended to handle voicecall atoms * registered in post_sim state or later */ modem = __ofono_atom_get_modem(atom); sim_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_SIM); if (sim_atom == NULL) return; sim = __ofono_atom_get_data(sim_atom); if (ofono_sim_get_state(sim) != OFONO_SIM_STATE_READY) return; modems = g_list_append(modems, modem); if (modems->next != NULL) return; bt_register_profile(conn, HFP_AG_UUID, HFP_VERSION_1_7, "hfp_ag", HFP_AG_EXT_PROFILE_PATH, NULL, 0); } static void modem_watch(struct ofono_modem *modem, gboolean added, void *user) { DBG("modem: %p, added: %d", modem, added); if (added == FALSE) return; __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SIM, sim_watch, modem, NULL); __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_VOICECALL, voicecall_watch, modem, NULL); } static void call_modemwatch(struct ofono_modem *modem, void *user) { modem_watch(modem, TRUE, user); } static int hfp_ag_init(void) { DBusConnection *conn = ofono_dbus_get_connection(); int err; if (DBUS_TYPE_UNIX_FD < 0) return -EBADF; /* Registers External Profile handler */ if (!g_dbus_register_interface(conn, HFP_AG_EXT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE, profile_methods, NULL, NULL, NULL, NULL)) { ofono_error("Register Profile interface failed: %s", HFP_AG_EXT_PROFILE_PATH); return -EIO; } err = ofono_handsfree_card_driver_register(&hfp_ag_driver); if (err < 0) { g_dbus_unregister_interface(conn, HFP_AG_EXT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE); return err; } sim_hash = g_hash_table_new(g_direct_hash, g_direct_equal); modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL); __ofono_modem_foreach(call_modemwatch, NULL); connection_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, connection_destroy); ofono_handsfree_audio_ref(); return 0; } static void hfp_ag_exit(void) { DBusConnection *conn = ofono_dbus_get_connection(); __ofono_modemwatch_remove(modemwatch_id); g_dbus_unregister_interface(conn, HFP_AG_EXT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE); ofono_handsfree_card_driver_unregister(&hfp_ag_driver); g_hash_table_destroy(connection_hash); g_list_free(modems); g_hash_table_foreach_remove(sim_hash, sim_watch_remove, NULL); g_hash_table_destroy(sim_hash); ofono_handsfree_audio_unref(); } OFONO_PLUGIN_DEFINE(hfp_ag_bluez5, "Hands-Free Audio Gateway Profile Plugins", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, hfp_ag_init, hfp_ag_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/caif.c0000644000015600001650000000312212671500024021124 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include static GSList *modem_list = NULL; static int caif_init(void) { #if 0 struct ofono_modem *modem; modem = ofono_modem_create("caif", "ste"); if (modem == NULL) return -EIO; modem_list = g_slist_prepend(modem_list, modem); ofono_modem_register(modem); #endif return 0; } static void caif_exit(void) { GSList *list; for (list = modem_list; list; list = list->next) { struct ofono_modem *modem = list->data; ofono_modem_remove(modem); } g_slist_free(modem_list); modem_list = NULL; } OFONO_PLUGIN_DEFINE(caif, "CAIF device detection", VERSION, OFONO_PLUGIN_PRIORITY_LOW, caif_init, caif_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/stemgr.c0000644000015600001650000002273312671500024021534 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 ST-Ericsson AB. * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include /* * ST-Ericsson's Modem Init Daemon is used for controlling the modem power * cycles and provides a dbus API for modem state and properties. */ #define MGR_SERVICE "com.stericsson.modeminit" #define MGR_INTERFACE MGR_SERVICE ".Manager" #define MGR_GET_MODEMS "GetModems" #define GET_MODEMS_TIMEOUT 5000 #define MGR_MODEM_INTERFACE MGR_SERVICE ".Modem" #define PROPERTY_CHANGED "PropertyChanged" enum ste_state { STE_STATE_OFF, STE_STATE_READY, STE_STATE_RESET }; enum ste_operation { STE_OP_STARTING, STE_OP_READY, STE_OP_RESTART, STE_OP_OFF }; struct ste_modem { char *path; struct ofono_modem *modem; enum ste_state state; char *serial; char *interface; }; static GHashTable *modem_list; static guint modem_daemon_watch; static guint property_changed_watch; static DBusConnection *connection; static void state_change(struct ste_modem *stemodem, enum ste_operation op) { switch (stemodem->state) { case STE_STATE_OFF: /* * The STE Modem is in state OFF and we're waiting for * the Modem Init Daemon to signal that modem is ready * in order to create and register the modem. */ switch (op) { case STE_OP_READY: stemodem->modem = ofono_modem_create(stemodem->serial, "ste"); if (stemodem->modem == NULL) { ofono_error("Could not create modem %s, %s", stemodem->path, stemodem->serial); return; } DBG("register modem %s, %s", stemodem->path, stemodem->serial); if (stemodem->interface != NULL) ofono_modem_set_string(stemodem->modem, "Interface", stemodem->interface); ofono_modem_register(stemodem->modem); stemodem->state = STE_STATE_READY; break; case STE_OP_STARTING: case STE_OP_RESTART: case STE_OP_OFF: break; } break; case STE_STATE_READY: /* * The STE Modem is ready and the modem has been created * and registered in oFono. In this state two things can * happen: Modem restarts or is turned off. Turning off * the modem is an exceptional situation e.g. high-temperature, * low battery or upgrade. In this scenario we remove the * STE modem from oFono. */ switch (op) { case STE_OP_READY: break; case STE_OP_STARTING: case STE_OP_RESTART: DBG("reset ongoing %s", stemodem->path); /* Note: Consider to power off modem here? */ stemodem->state = STE_STATE_RESET; break; case STE_OP_OFF: DBG("STE modem unregistering %s", stemodem->path); ofono_modem_remove(stemodem->modem); stemodem->modem = NULL; stemodem->state = STE_STATE_OFF; break; } break; case STE_STATE_RESET: /* * The STE Modem is resetting.In this state two things can * happen: Modem restarts succeeds, or modem is turned off. */ switch (op) { case STE_OP_STARTING: case STE_OP_RESTART: break; case STE_OP_READY: DBG("STE modem reset complete %s", stemodem->path); if (ofono_modem_get_powered(stemodem->modem)) ofono_modem_reset(stemodem->modem); stemodem->state = STE_STATE_READY; break; case STE_OP_OFF: DBG("STE modem unregistering %s", stemodem->path); ofono_modem_remove(stemodem->modem); stemodem->modem = NULL; stemodem->state = STE_STATE_OFF; break; } break; } } static void update_property(struct ste_modem *stemodem, const char *prop, DBusMessageIter *iter, enum ste_operation *op, gboolean *op_valid) { const char *value; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return; dbus_message_iter_get_basic(iter, &value); if (g_strcmp0(prop, "State") == 0) { *op_valid = TRUE; if (g_strcmp0(value, "booting") == 0) *op = STE_OP_STARTING; else if (g_strcmp0(value, "upgrading") == 0) *op = STE_OP_OFF; else if (g_strcmp0(value, "ready") == 0) *op = STE_OP_READY; else if (g_strcmp0(value, "off") == 0) *op = STE_OP_OFF; else if (g_strcmp0(value, "dumping") == 0) *op = STE_OP_RESTART; else *op_valid = FALSE; } else if (g_strcmp0(prop, "Interface") == 0) { g_free(stemodem->interface); stemodem->interface = g_strdup(value); } else if (g_strcmp0(prop, "Serial") == 0) { g_free(stemodem->serial); stemodem->serial = g_strdup(value); } } static void update_modem_properties(const char *path, DBusMessageIter *iter) { enum ste_operation operation; gboolean operation_valid; struct ste_modem *stemodem = g_hash_table_lookup(modem_list, path); if (stemodem == NULL) { stemodem = g_try_new0(struct ste_modem, 1); if (stemodem == NULL) return; stemodem->path = g_strdup(path); stemodem->state = STE_STATE_OFF; g_hash_table_insert(modem_list, stemodem->path, stemodem); } while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(iter, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); update_property(stemodem, key, &value, &operation, &operation_valid); dbus_message_iter_next(iter); } if (operation_valid) state_change(stemodem, operation); } static void get_modems_reply(DBusPendingCall *call, void *user_data) { DBusMessageIter iter, list; DBusError err; DBusMessage *reply = dbus_pending_call_steal_reply(call); dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply)) { ofono_error("%s: %s\n", err.name, err.message); dbus_error_free(&err); goto done; } if (!dbus_message_has_signature(reply, "a(oa{sv})")) goto done; if (!dbus_message_iter_init(reply, &iter)) goto done; dbus_message_iter_recurse(&iter, &list); while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) { DBusMessageIter entry, dict; const char *path; dbus_message_iter_recurse(&list, &entry); dbus_message_iter_get_basic(&entry, &path); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &dict); update_modem_properties(path, &dict); dbus_message_iter_next(&list); } done: dbus_message_unref(reply); } static void get_modems(void) { DBusMessage *message; DBusPendingCall *call; message = dbus_message_new_method_call(MGR_SERVICE, "/", MGR_INTERFACE, MGR_GET_MODEMS); if (message == NULL) { ofono_error("Unable to allocate new D-Bus message"); goto error; } dbus_message_set_auto_start(message, FALSE); if (!dbus_connection_send_with_reply(connection, message, &call, GET_MODEMS_TIMEOUT)) { ofono_error("Sending D-Bus message failed"); goto error; } if (call == NULL) { DBG("D-Bus connection not available"); goto error; } dbus_pending_call_set_notify(call, get_modems_reply, NULL, NULL); dbus_pending_call_unref(call); error: dbus_message_unref(message); } static gboolean property_changed(DBusConnection *conn, DBusMessage *message, void *user_data) { DBusMessageIter iter; struct ste_modem *stemodem; const char *key; enum ste_operation operation; gboolean operation_valid; stemodem = g_hash_table_lookup(modem_list, dbus_message_get_path(message)); if (stemodem == NULL) return TRUE; if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); update_property(stemodem, key, &iter, &operation, &operation_valid); if (operation_valid) state_change(stemodem, operation); return TRUE; } static void mgr_connect(DBusConnection *conn, void *user_data) { property_changed_watch = g_dbus_add_signal_watch(conn, MGR_SERVICE, NULL, MGR_MODEM_INTERFACE, PROPERTY_CHANGED, property_changed, NULL, NULL); get_modems(); } static void mgr_disconnect(DBusConnection *conn, void *user_data) { g_hash_table_remove_all(modem_list); g_dbus_remove_watch(conn, property_changed_watch); property_changed_watch = 0; } static void destroy_stemodem(gpointer data) { struct ste_modem *stemodem = data; ofono_modem_remove(stemodem->modem); g_free(stemodem->interface); g_free(stemodem->path); g_free(stemodem->serial); g_free(stemodem); } static int stemgr_init(void) { connection = ofono_dbus_get_connection(); modem_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, destroy_stemodem); modem_daemon_watch = g_dbus_add_service_watch(connection, MGR_SERVICE, mgr_connect, mgr_disconnect, NULL, NULL); return 0; } static void stemgr_exit(void) { g_hash_table_destroy(modem_list); g_dbus_remove_watch(connection, modem_daemon_watch); if (property_changed_watch > 0) g_dbus_remove_watch(connection, property_changed_watch); } OFONO_PLUGIN_DEFINE(stemgr, "ST-Ericsson Modem Init Daemon detection", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, stemgr_init, stemgr_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/speedup.c0000644000015600001650000002340412671500024021674 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *creg_prefix[] = { "+CREG:", NULL }; static const char *none_prefix[] = { NULL }; struct speedup_data { GAtChat *modem; GAtChat *aux; gboolean have_sim; guint online_poll_source; guint online_poll_count; struct cb_data *online_cbd; struct at_util_sim_state_query *sim_state_query; }; static int speedup_probe(struct ofono_modem *modem) { struct speedup_data *data; DBG("%p", modem); data = g_try_new0(struct speedup_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void speedup_remove(struct ofono_modem *modem) { struct speedup_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup potential SIM state polling */ at_util_sim_state_query_free(data->sim_state_query); /* Cleanup potential online enable polling */ if (data->online_poll_source > 0) { g_source_remove(data->online_poll_source); g_free(data->online_cbd); } /* Cleanup after hot-unplug */ g_at_chat_unref(data->aux); g_free(data); } static void speedup_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GIOChannel *channel; GAtSyntax *syntax; GAtChat *chat; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, speedup_debug, debug); return chat; } static void sim_state_cb(gboolean present, gpointer user_data) { struct ofono_modem *modem = user_data; struct speedup_data *data = ofono_modem_get_data(modem); at_util_sim_state_query_free(data->sim_state_query); data->sim_state_query = NULL; data->have_sim = present; ofono_modem_set_powered(modem, TRUE); /* AT&C0 needs to be send separate and on both channel */ g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT&C0", NULL, NULL, NULL, NULL); /* * Ensure that the modem is using GSM character set and not IRA, * otherwise weirdness with umlauts and other non-ASCII characters * can result */ g_at_chat_send(data->modem, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct speedup_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_unref(data->aux); data->aux = NULL; ofono_modem_set_powered(modem, FALSE); return; } data->sim_state_query = at_util_sim_state_query_new(data->aux, 2, 20, sim_state_cb, modem, NULL); } static int speedup_enable(struct ofono_modem *modem) { struct speedup_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->aux = open_device(modem, "Aux", "Aux: "); if (data->aux == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_send(data->modem, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CFUN=1", NULL, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct speedup_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->aux); data->aux = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int speedup_disable(struct ofono_modem *modem) { struct speedup_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->aux); g_at_chat_unregister_all(data->aux); g_at_chat_send(data->aux, "AT+CFUN=0", NULL, cfun_disable, modem, NULL); return -EINPROGRESS; } static gboolean creg_online_check(gpointer user_data); static void creg_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct speedup_data *data = user_data; ofono_modem_online_cb_t cb = data->online_cbd->cb; if (ok) { CALLBACK_WITH_SUCCESS(cb, data->online_cbd->data); goto done; } data->online_poll_count++; if (data->online_poll_count > 15) goto failure; data->online_poll_source = g_timeout_add_seconds(2, creg_online_check, data); return; failure: CALLBACK_WITH_FAILURE(cb, data->online_cbd->data); done: g_free(data->online_cbd); data->online_cbd = NULL; } static gboolean creg_online_check(gpointer user_data) { struct speedup_data *data = user_data; data->online_poll_source = 0; g_at_chat_send(data->aux, "AT+CREG=?", creg_prefix, creg_online_cb, data, NULL); return FALSE; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct speedup_data *data = ofono_modem_get_data(modem); if (!ok) { ofono_modem_online_cb_t cb = data->online_cbd->cb; CALLBACK_WITH_FAILURE(cb, data->online_cbd->data); g_free(data->online_cbd); data->online_cbd = NULL; return; } data->online_poll_count = 0; creg_online_check(data); } static void set_offline_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void speedup_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct speedup_data *data = ofono_modem_get_data(modem); DBG("modem %p %s", modem, online ? "online" : "offline"); if (online == TRUE) { data->online_cbd = cb_data_new(cb, user_data); if (g_at_chat_send(data->aux, "AT+CFUN=1", none_prefix, set_online_cb, modem, NULL) > 0) return; g_free(data->online_cbd); data->online_cbd = NULL; } else { struct cb_data *cbd = cb_data_new(cb, user_data); if (g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix, set_offline_cb, cbd, g_free) > 0) return; g_free(cbd); } CALLBACK_WITH_FAILURE(cb, user_data); } static void speedup_pre_sim(struct ofono_modem *modem) { struct speedup_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->aux); sim = ofono_sim_create(modem, OFONO_VENDOR_SPEEDUP, "atmodem", data->aux); if (sim && data->have_sim == TRUE) ofono_sim_inserted_notify(sim, TRUE); } static void speedup_post_sim(struct ofono_modem *modem) { struct speedup_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_phonebook_create(modem, 0, "atmodem", data->aux); ofono_sms_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->aux); gprs = ofono_gprs_create(modem, OFONO_VENDOR_SPEEDUP, "atmodem", data->aux); gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static void speedup_post_online(struct ofono_modem *modem) { struct speedup_data *data = ofono_modem_get_data(modem); ofono_netreg_create(modem, OFONO_VENDOR_SPEEDUP, "atmodem", data->aux); ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->aux); ofono_ussd_create(modem, 0, "speedupmodem", data->aux); } static struct ofono_modem_driver speedup_driver = { .name = "speedup", .probe = speedup_probe, .remove = speedup_remove, .enable = speedup_enable, .disable = speedup_disable, .set_online = speedup_set_online, .pre_sim = speedup_pre_sim, .post_sim = speedup_post_sim, .post_online = speedup_post_online, }; static int speedup_init(void) { return ofono_modem_driver_register(&speedup_driver); } static void speedup_exit(void) { ofono_modem_driver_unregister(&speedup_driver); } OFONO_PLUGIN_DEFINE(speedup, "Speed Up modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, speedup_init, speedup_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/bluez4.c0000644000015600001650000005632212671500024021441 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ProFUSION embedded systems * Copyright (C) 2010 Gustavo F. Padovan * * 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 #endif #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include "bluez4.h" static DBusConnection *connection; static GHashTable *uuid_hash = NULL; static GHashTable *adapter_address_hash = NULL; static gint bluetooth_refcount; static GSList *server_list = NULL; static const char *adapter_any_name = "any"; static char *adapter_any_path; #define TIMEOUT 60 /* Timeout for user response (seconds) */ struct server { guint8 channel; char *sdp_record; guint32 handle; GIOChannel *io; ConnectFunc connect_cb; gpointer user_data; }; struct cb_data { struct server *server; char *path; guint source; GIOChannel *io; }; void bluetooth_create_path(const char *dev_addr, const char *adapter_addr, char *buf, int size) { int i, j; for (i = 0, j = 0; adapter_addr[j] && i < size - 1; j++) if (adapter_addr[j] >= '0' && adapter_addr[j] <= '9') buf[i++] = adapter_addr[j]; else if (adapter_addr[j] >= 'A' && adapter_addr[j] <= 'F') buf[i++] = adapter_addr[j]; if (i < size - 1) buf[i++] = '_'; for (j = 0; dev_addr[j] && i < size - 1; j++) if (dev_addr[j] >= '0' && dev_addr[j] <= '9') buf[i++] = dev_addr[j]; else if (dev_addr[j] >= 'A' && dev_addr[j] <= 'F') buf[i++] = dev_addr[j]; buf[i] = '\0'; } int bluetooth_send_with_reply(const char *path, const char *interface, const char *method, DBusPendingCall **call, DBusPendingCallNotifyFunction cb, void *user_data, DBusFreeFunction free_func, int timeout, int type, ...) { DBusMessage *msg; DBusPendingCall *c; va_list args; int err; msg = dbus_message_new_method_call(BLUEZ_SERVICE, path, interface, method); if (msg == NULL) { ofono_error("Unable to allocate new D-Bus %s message", method); err = -ENOMEM; goto fail; } va_start(args, type); if (!dbus_message_append_args_valist(msg, type, args)) { va_end(args); err = -EIO; goto fail; } va_end(args); if (timeout > 0) timeout *= 1000; if (!dbus_connection_send_with_reply(connection, msg, &c, timeout)) { ofono_error("Sending %s failed", method); err = -EIO; goto fail; } if (call != NULL) *call = c; dbus_pending_call_set_notify(c, cb, user_data, free_func); dbus_pending_call_unref(c); dbus_message_unref(msg); return 0; fail: if (free_func && user_data) free_func(user_data); if (msg) dbus_message_unref(msg); return err; } typedef void (*PropertyHandler)(DBusMessageIter *iter, gpointer user_data); struct property_handler { const char *property; PropertyHandler callback; gpointer user_data; }; static gint property_handler_compare(gconstpointer a, gconstpointer b) { const struct property_handler *handler = a; const char *property = b; return strcmp(handler->property, property); } void bluetooth_parse_properties(DBusMessage *reply, const char *property, ...) { va_list args; GSList *prop_handlers = NULL; DBusMessageIter array, dict; va_start(args, property); while (property != NULL) { struct property_handler *handler = g_new0(struct property_handler, 1); handler->property = property; handler->callback = va_arg(args, PropertyHandler); handler->user_data = va_arg(args, gpointer); property = va_arg(args, const char *); prop_handlers = g_slist_prepend(prop_handlers, handler); } va_end(args); if (dbus_message_iter_init(reply, &array) == FALSE) goto done; if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) goto done; dbus_message_iter_recurse(&array, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; GSList *l; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) goto done; dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) goto done; dbus_message_iter_recurse(&entry, &value); l = g_slist_find_custom(prop_handlers, key, property_handler_compare); if (l) { struct property_handler *handler = l->data; handler->callback(&value, handler->user_data); } dbus_message_iter_next(&dict); } done: g_slist_foreach(prop_handlers, (GFunc) g_free, NULL); g_slist_free(prop_handlers); } static void parse_uuids(DBusMessageIter *array, gpointer user_data) { GSList **uuids = user_data; DBusMessageIter value; if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(array, &value); while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { const char *uuid; dbus_message_iter_get_basic(&value, &uuid); *uuids = g_slist_prepend(*uuids, (char *) uuid); dbus_message_iter_next(&value); } } static void parse_string(DBusMessageIter *iter, gpointer user_data) { char **str = user_data; int arg_type = dbus_message_iter_get_arg_type(iter); if (arg_type != DBUS_TYPE_OBJECT_PATH && arg_type != DBUS_TYPE_STRING) return; dbus_message_iter_get_basic(iter, str); } static void bluetooth_probe(GSList *uuids, const char *path, const char *device, const char *adapter, const char *alias) { for (; uuids; uuids = uuids->next) { struct bluetooth_profile *driver; const char *uuid = uuids->data; int err; driver = g_hash_table_lookup(uuid_hash, uuid); if (driver == NULL) continue; err = driver->probe(path, device, adapter, alias); if (err == 0 || err == -EALREADY) continue; ofono_error("%s probe: %s (%d)", driver->name, strerror(-err), -err); } } static void device_properties_cb(DBusPendingCall *call, gpointer user_data) { DBusMessage *reply; const char *path = user_data; const char *adapter = NULL; const char *adapter_addr = NULL; const char *device_addr = NULL; const char *alias = NULL; struct DBusError derr; GSList *uuids = NULL; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { ofono_error("Device.GetProperties replied an error: %s, %s", derr.name, derr.message); dbus_error_free(&derr); goto done; } DBG(""); bluetooth_parse_properties(reply, "UUIDs", parse_uuids, &uuids, "Adapter", parse_string, &adapter, "Address", parse_string, &device_addr, "Alias", parse_string, &alias, NULL); if (adapter) adapter_addr = g_hash_table_lookup(adapter_address_hash, adapter); if (!device_addr || !adapter_addr) goto done; bluetooth_probe(uuids, path, device_addr, adapter_addr, alias); done: g_slist_free(uuids); dbus_message_unref(reply); } static void parse_devices(DBusMessageIter *array, gpointer user_data) { DBusMessageIter value; GSList **device_list = user_data; DBG(""); if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(array, &value); while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) { const char *path; dbus_message_iter_get_basic(&value, &path); *device_list = g_slist_prepend(*device_list, (gpointer) path); dbus_message_iter_next(&value); } } static gboolean property_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *property; DBusMessageIter iter; dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return FALSE; dbus_message_iter_get_basic(&iter, &property); if (g_str_equal(property, "UUIDs") == TRUE) { GSList *uuids = NULL; const char *path = dbus_message_get_path(msg); DBusMessageIter variant; if (!dbus_message_iter_next(&iter)) return FALSE; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return FALSE; dbus_message_iter_recurse(&iter, &variant); parse_uuids(&variant, &uuids); /* We need the full set of properties to be able to create * the modem properly, including Adapter and Alias, so * refetch everything again */ if (uuids) bluetooth_send_with_reply(path, BLUEZ_DEVICE_INTERFACE, "GetProperties", NULL, device_properties_cb, g_strdup(path), g_free, -1, DBUS_TYPE_INVALID); } else if (g_str_equal(property, "Alias") == TRUE) { const char *path = dbus_message_get_path(msg); struct bluetooth_profile *profile; const char *alias = NULL; DBusMessageIter variant; GHashTableIter hash_iter; gpointer key, value; if (!dbus_message_iter_next(&iter)) return FALSE; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return FALSE; dbus_message_iter_recurse(&iter, &variant); parse_string(&variant, &alias); g_hash_table_iter_init(&hash_iter, uuid_hash); while (g_hash_table_iter_next(&hash_iter, &key, &value)) { profile = value; if (profile->set_alias) profile->set_alias(path, alias); } } return TRUE; } static void adapter_properties_cb(DBusPendingCall *call, gpointer user_data) { const char *path = user_data; DBusMessage *reply; DBusError derr; GSList *device_list = NULL; GSList *l; const char *addr; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { ofono_error("Adapter.GetProperties replied an error: %s, %s", derr.name, derr.message); dbus_error_free(&derr); goto done; } /* * Adapter might have been removed before the callback, for instance in * case the SIM state changes due to a modem reset (see * hfp_ag.c:sim_state_watch()) */ if (adapter_address_hash == NULL) goto done; DBG(""); bluetooth_parse_properties(reply, "Devices", parse_devices, &device_list, "Address", parse_string, &addr, NULL); DBG("Adapter Address: %s, Path: %s", addr, path); g_hash_table_insert(adapter_address_hash, g_strdup(path), g_strdup(addr)); for (l = device_list; l; l = l->next) { const char *device = l->data; bluetooth_send_with_reply(device, BLUEZ_DEVICE_INTERFACE, "GetProperties", NULL, device_properties_cb, g_strdup(device), g_free, -1, DBUS_TYPE_INVALID); } done: g_slist_free(device_list); dbus_message_unref(reply); } static void get_adapter_properties(const char *path, const char *handle, gpointer user_data) { bluetooth_send_with_reply(path, BLUEZ_ADAPTER_INTERFACE, "GetProperties", NULL, adapter_properties_cb, g_strdup(path), g_free, -1, DBUS_TYPE_INVALID); } static void remove_record(struct server *server) { DBusMessage *msg; if (server->handle == 0) return; msg = dbus_message_new_method_call(BLUEZ_SERVICE, adapter_any_path, BLUEZ_SERVICE_INTERFACE, "RemoveRecord"); if (msg == NULL) { ofono_error("Unable to allocate D-Bus RemoveRecord message"); return; } dbus_message_append_args(msg, DBUS_TYPE_UINT32, &server->handle, DBUS_TYPE_INVALID); g_dbus_send_message(connection, msg); ofono_info("Unregistered handle for channel %d: 0x%x", server->channel, server->handle); } static void cb_data_destroy(gpointer data) { struct cb_data *cb_data = data; if (cb_data->source != 0) g_source_remove(cb_data->source); g_free(cb_data->path); g_free(cb_data); } static void cancel_authorization(struct cb_data *user_data) { DBusMessage *msg; msg = dbus_message_new_method_call(BLUEZ_SERVICE, user_data->path, BLUEZ_SERVICE_INTERFACE, "CancelAuthorization"); if (msg == NULL) { ofono_error("Unable to allocate D-Bus CancelAuthorization" " message"); return; } g_dbus_send_message(connection, msg); } static gboolean client_event(GIOChannel *chan, GIOCondition cond, gpointer data) { struct cb_data *cb_data = data; cancel_authorization(cb_data); cb_data->source = 0; return FALSE; } static void auth_cb(DBusPendingCall *call, gpointer user_data) { struct cb_data *cb_data = user_data; struct server *server = cb_data->server; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError derr; GError *err = NULL; dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { ofono_error("RequestAuthorization error: %s, %s", derr.name, derr.message); if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY)) cancel_authorization(cb_data); dbus_error_free(&derr); } else { ofono_info("RequestAuthorization succeeded"); if (!bt_io_accept(cb_data->io, server->connect_cb, server->user_data, NULL, &err)) { ofono_error("%s", err->message); g_error_free(err); } } dbus_message_unref(reply); } static void new_connection(GIOChannel *io, gpointer user_data) { struct server *server = user_data; struct cb_data *cbd; const char *addr; GError *err = NULL; char laddress[18], raddress[18]; guint8 channel; GHashTableIter iter; gpointer key, value; const char *path; bt_io_get(io, BT_IO_RFCOMM, &err, BT_IO_OPT_SOURCE, laddress, BT_IO_OPT_DEST, raddress, BT_IO_OPT_CHANNEL, &channel, BT_IO_OPT_INVALID); if (err) { ofono_error("%s", err->message); g_error_free(err); return; } ofono_info("New connection for %s on channel %u from: %s,", laddress, channel, raddress); path = NULL; g_hash_table_iter_init(&iter, adapter_address_hash); while (g_hash_table_iter_next(&iter, &key, &value)) { if (g_str_equal(laddress, value) == TRUE) { path = key; break; } } if (path == NULL) return; cbd = g_try_new0(struct cb_data, 1); if (cbd == NULL) { ofono_error("Unable to allocate client cb_data structure"); return; } cbd->path = g_strdup(path); cbd->server = server; cbd->io = io; addr = raddress; if (bluetooth_send_with_reply(path, BLUEZ_SERVICE_INTERFACE, "RequestAuthorization", NULL, auth_cb, cbd, cb_data_destroy, TIMEOUT, DBUS_TYPE_STRING, &addr, DBUS_TYPE_UINT32, &server->handle, DBUS_TYPE_INVALID) < 0) { ofono_error("Request Bluetooth authorization failed"); return; } ofono_info("RequestAuthorization(%s, 0x%x)", raddress, server->handle); cbd->source = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, client_event, cbd); } static void remove_service_handle(gpointer data, gpointer user_data) { struct server *server = data; server->handle = 0; } static void add_record_cb(DBusPendingCall *call, gpointer user_data) { struct server *server = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError derr; dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { ofono_error("Replied with an error: %s, %s", derr.name, derr.message); dbus_error_free(&derr); goto done; } dbus_message_get_args(reply, NULL, DBUS_TYPE_UINT32, &server->handle, DBUS_TYPE_INVALID); ofono_info("Registered handle for channel %d: 0x%x", server->channel, server->handle); done: dbus_message_unref(reply); } static void add_record(gpointer data, gpointer user_data) { struct server *server = data; if (server->sdp_record == NULL) return; bluetooth_send_with_reply(adapter_any_path, BLUEZ_SERVICE_INTERFACE, "AddRecord", NULL, add_record_cb, server, NULL, -1, DBUS_TYPE_STRING, &server->sdp_record, DBUS_TYPE_INVALID); } static void find_adapter_cb(DBusPendingCall *call, gpointer user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError derr; const char *path; dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { ofono_error("Replied with an error: %s, %s", derr.name, derr.message); dbus_error_free(&derr); goto done; } dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); adapter_any_path = g_strdup(path); g_slist_foreach(server_list, (GFunc) add_record, NULL); done: dbus_message_unref(reply); } static gboolean adapter_added(DBusConnection *conn, DBusMessage *message, void *user_data) { const char *path; dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); bluetooth_send_with_reply(path, BLUEZ_ADAPTER_INTERFACE, "GetProperties", NULL, adapter_properties_cb, g_strdup(path), g_free, -1, DBUS_TYPE_INVALID); return TRUE; } static void bluetooth_remove(gpointer key, gpointer value, gpointer user_data) { struct bluetooth_profile *profile = value; profile->remove(user_data); } static gboolean adapter_removed(DBusConnection *conn, DBusMessage *message, void *user_data) { const char *path; if (dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) == FALSE) return FALSE; g_hash_table_foreach(uuid_hash, bluetooth_remove, (gpointer) path); g_hash_table_remove(adapter_address_hash, path); return TRUE; } static gboolean device_removed(DBusConnection *conn, DBusMessage *message, void *user_data) { const char *path; if (dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) == FALSE) return FALSE; g_hash_table_foreach(uuid_hash, bluetooth_remove, (gpointer) path); return TRUE; } static void parse_adapters(DBusMessageIter *array, gpointer user_data) { DBusMessageIter value; DBG(""); if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(array, &value); while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) { const char *path; dbus_message_iter_get_basic(&value, &path); DBG("Calling GetProperties on %s", path); bluetooth_send_with_reply(path, BLUEZ_ADAPTER_INTERFACE, "GetProperties", NULL, adapter_properties_cb, g_strdup(path), g_free, -1, DBUS_TYPE_INVALID); dbus_message_iter_next(&value); } } static void manager_properties_cb(DBusPendingCall *call, gpointer user_data) { DBusMessage *reply; DBusError derr; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { ofono_error("Manager.GetProperties() replied an error: %s, %s", derr.name, derr.message); dbus_error_free(&derr); goto done; } DBG(""); bluetooth_parse_properties(reply, "Adapters", parse_adapters, NULL, NULL); done: dbus_message_unref(reply); } static void bluetooth_connect(DBusConnection *conn, void *user_data) { bluetooth_send_with_reply("/", BLUEZ_MANAGER_INTERFACE, "GetProperties", NULL, manager_properties_cb, NULL, NULL, -1, DBUS_TYPE_INVALID); bluetooth_send_with_reply("/", BLUEZ_MANAGER_INTERFACE, "FindAdapter", NULL, find_adapter_cb, NULL, NULL, -1, DBUS_TYPE_STRING, &adapter_any_name, DBUS_TYPE_INVALID); } static void bluetooth_disconnect(DBusConnection *conn, void *user_data) { if (uuid_hash == NULL) return; g_hash_table_foreach(uuid_hash, bluetooth_remove, NULL); g_slist_foreach(server_list, (GFunc) remove_service_handle, NULL); } static guint bluetooth_watch; static guint adapter_added_watch; static guint adapter_removed_watch; static guint device_removed_watch; static guint property_watch; static void bluetooth_ref(void) { if (bluetooth_refcount > 0) goto increment; connection = ofono_dbus_get_connection(); bluetooth_watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE, bluetooth_connect, bluetooth_disconnect, NULL, NULL); adapter_added_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL, BLUEZ_MANAGER_INTERFACE, "AdapterAdded", adapter_added, NULL, NULL); adapter_removed_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL, BLUEZ_MANAGER_INTERFACE, "AdapterRemoved", adapter_removed, NULL, NULL); device_removed_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL, BLUEZ_ADAPTER_INTERFACE, "DeviceRemoved", device_removed, NULL, NULL); property_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL, BLUEZ_DEVICE_INTERFACE, "PropertyChanged", property_changed, NULL, NULL); if (bluetooth_watch == 0 || adapter_added_watch == 0 || adapter_removed_watch == 0 || property_watch == 0) { goto remove; } uuid_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); adapter_address_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); increment: g_atomic_int_inc(&bluetooth_refcount); return; remove: g_dbus_remove_watch(connection, bluetooth_watch); g_dbus_remove_watch(connection, adapter_added_watch); g_dbus_remove_watch(connection, adapter_removed_watch); g_dbus_remove_watch(connection, property_watch); } static void bluetooth_unref(void) { if (g_atomic_int_dec_and_test(&bluetooth_refcount) == FALSE) return; g_free(adapter_any_path); adapter_any_path = NULL; g_dbus_remove_watch(connection, bluetooth_watch); g_dbus_remove_watch(connection, adapter_added_watch); g_dbus_remove_watch(connection, adapter_removed_watch); g_dbus_remove_watch(connection, property_watch); g_hash_table_destroy(uuid_hash); uuid_hash = NULL; g_hash_table_destroy(adapter_address_hash); adapter_address_hash = NULL; } void bluetooth_get_properties() { g_hash_table_foreach(adapter_address_hash, (GHFunc) get_adapter_properties, NULL); } int bluetooth_register_uuid(const char *uuid, struct bluetooth_profile *profile) { bluetooth_ref(); g_hash_table_insert(uuid_hash, g_strdup(uuid), profile); g_hash_table_foreach(adapter_address_hash, (GHFunc) get_adapter_properties, NULL); return 0; } void bluetooth_unregister_uuid(const char *uuid) { g_hash_table_remove(uuid_hash, uuid); bluetooth_unref(); } struct server *bluetooth_register_server(guint8 channel, const char *sdp_record, ConnectFunc cb, gpointer user_data) { struct server *server; GError *err = NULL; server = g_try_new0(struct server, 1); if (!server) return NULL; server->channel = channel; server->io = bt_io_listen(BT_IO_RFCOMM, NULL, new_connection, server, NULL, &err, BT_IO_OPT_CHANNEL, server->channel, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID); if (server->io == NULL) { g_error_free(err); g_free(server); return NULL; } bluetooth_ref(); if (sdp_record != NULL) server->sdp_record = g_strdup(sdp_record); server->connect_cb = cb; server->user_data = user_data; server_list = g_slist_prepend(server_list, server); if (adapter_any_path != NULL) add_record(server, NULL); return server; } void bluetooth_unregister_server(struct server *server) { server_list = g_slist_remove(server_list, server); remove_record(server); if (server->io != NULL) { g_io_channel_shutdown(server->io, TRUE, NULL); g_io_channel_unref(server->io); server->io = NULL; } g_free(server->sdp_record); g_free(server); bluetooth_unref(); } OFONO_PLUGIN_DEFINE(bluez4, "Bluetooth Utils Plugins", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, NULL, NULL) ofono-1.17.bzr6912+16.04.20160314.3/plugins/nettime.c0000644000015600001650000001671012671500024021676 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2012-2013 Jolla Ltd. * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "ofono.h" #include "common.h" struct nt_data { gboolean time_available; gboolean time_pending; time_t nw_time_utc; time_t received; int dst; int time_zone; char mcc[OFONO_MAX_MCC_LENGTH + 1]; char mnc[OFONO_MAX_MNC_LENGTH + 1]; const char *path; }; static void init_time(struct ofono_nettime_context *context) { struct nt_data *nt_data = g_new0(struct nt_data, 1); nt_data->time_available = FALSE; nt_data->time_pending = FALSE; nt_data->dst = 0; nt_data->time_zone = 0; context->data = nt_data; } static gboolean encode_time_format(const struct ofono_network_time *time, struct tm *tm) { if (time->year < 0) return FALSE; memset(tm, 0, sizeof(struct tm)); tm->tm_year = time->year - 1900; tm->tm_mon = time->mon - 1; tm->tm_mday = time->mday; tm->tm_hour = time->hour; tm->tm_min = time->min; tm->tm_sec = time->sec; tm->tm_gmtoff = time->utcoff; tm->tm_isdst = time->dst; return TRUE; } static time_t get_monotonic_time() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec; } static int fill_time_notification(DBusMessage *msg, struct nt_data *ntd) { DBusMessageIter iter, iter_array; dbus_int64_t utc_long, received; dbus_int32_t dst, timezone; dbus_message_iter_init_append(msg, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_array); if (ntd->time_pending) { if (ntd->time_available) { utc_long = (dbus_int64_t) ntd->nw_time_utc; ofono_dbus_dict_append(&iter_array, "UTC", DBUS_TYPE_INT64, &utc_long); dst = (dbus_int32_t) ntd->dst; ofono_dbus_dict_append(&iter_array, "DST", DBUS_TYPE_UINT32, &dst); timezone = (dbus_int32_t) ntd->time_zone; ofono_dbus_dict_append(&iter_array, "Timezone", DBUS_TYPE_INT32, &timezone); received = (dbus_int64_t) ntd->received; ofono_dbus_dict_append(&iter_array, "Received", DBUS_TYPE_INT64, &received); } if (ntd->mcc[0] != '\0') { char *mcc = ntd->mcc; ofono_dbus_dict_append(&iter_array, "MobileCountryCode", DBUS_TYPE_STRING, &mcc); } if (ntd->mnc[0] != '\0') { char *mnc = ntd->mnc; ofono_dbus_dict_append(&iter_array, "MobileNetworkCode", DBUS_TYPE_STRING, &mnc); } } else { DBG("fill_time_notification: time not available"); } dbus_message_iter_close_container(&iter, &iter_array); return 0; } static DBusMessage *get_network_time(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessage *reply; struct ofono_nettime_context *context = data; struct nt_data *nt_data = context->data; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; fill_time_notification(reply, nt_data); return reply; } static const GDBusMethodTable nettime_methods[] = { { GDBUS_METHOD("GetNetworkTime", NULL, GDBUS_ARGS({ "time", "a{sv}" }), get_network_time) }, { } }; static const GDBusSignalTable nettime_signals[] = { { GDBUS_SIGNAL("NetworkTimeChanged", GDBUS_ARGS({ "time", "a{sv}" })) }, { } }; static int nettime_probe(struct ofono_nettime_context *context) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = ofono_modem_get_path(context->modem); DBG("Network time probe for modem: %p (%s)", context->modem, path); init_time(context); if (!g_dbus_register_interface(conn, path, OFONO_NETWORK_TIME_INTERFACE, nettime_methods, nettime_signals, NULL, /* properties */ context, /* user data */ NULL)) { ofono_error("Network time: Could not register interface %s, " "path %s", OFONO_NETWORK_TIME_INTERFACE, path); return 1; } else { ofono_info("Network time: Registered inteface %s, path %s", OFONO_NETWORK_TIME_INTERFACE, path); } ofono_modem_add_interface(context->modem, OFONO_NETWORK_TIME_INTERFACE); return 0; } static void nettime_remove(struct ofono_nettime_context *context) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = ofono_modem_get_path(context->modem); DBG("Network time remove for modem: %p (%s)", context->modem, path); if (!g_dbus_unregister_interface(conn, path, OFONO_NETWORK_TIME_INTERFACE)) ofono_error("Network time: could not unregister interface %s, " "path %s", OFONO_NETWORK_TIME_INTERFACE, path); ofono_modem_remove_interface(context->modem, OFONO_NETWORK_TIME_INTERFACE); g_free(context->data); } static void send_signal(struct nt_data *ntd) { DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *signal = dbus_message_new_signal(ntd->path, OFONO_NETWORK_TIME_INTERFACE, "NetworkTimeChanged"); fill_time_notification(signal, ntd); g_dbus_send_message(conn, signal); } static void nettime_info_received(struct ofono_nettime_context *context, struct ofono_network_time *info) { struct ofono_netreg *netreg; struct tm t; struct nt_data *ntd = context->data; const char *mcc; const char *mnc; if (info == NULL) return; netreg = __ofono_atom_get_data(__ofono_modem_find_atom( context->modem, OFONO_ATOM_TYPE_NETREG)); ntd->path = ofono_modem_get_path(context->modem); mcc = ofono_netreg_get_mcc(netreg); mnc = ofono_netreg_get_mnc(netreg); if (mcc == NULL) ntd->mcc[0] = '\0'; else strcpy(ntd->mcc, mcc); if (mnc == NULL) ntd->mnc[0] = '\0'; else strcpy(ntd->mnc, mnc); ntd->received = get_monotonic_time(); ntd->time_pending = TRUE; ntd->dst = info->dst; ntd->time_zone = info->utcoff; ntd->time_available = encode_time_format(info, &t); if (ntd->time_available == TRUE) ntd->nw_time_utc = timegm(&t); send_signal(ntd); DBG("modem: %p (%s)", context->modem, ofono_modem_get_path(context->modem)); DBG("time: %04d-%02d-%02d %02d:%02d:%02d%c%02d:%02d (DST=%d)", info->year, info->mon, info->mday, info->hour, info->min, info->sec, info->utcoff > 0 ? '+' : '-', abs(info->utcoff) / 3600, (abs(info->utcoff) % 3600) / 60, info->dst); DBG("UTC timestamp: %li, Received (monotonic time): %li", ntd->nw_time_utc, ntd->received); DBG("MCC: %s, MNC: %s", ntd->mcc, ntd->mnc); } static struct ofono_nettime_driver driver = { .name = "Network Time", .probe = nettime_probe, .remove = nettime_remove, .info_received = nettime_info_received, }; static int nettime_init(void) { return ofono_nettime_driver_register(&driver); } static void nettime_exit(void) { ofono_nettime_driver_unregister(&driver); } OFONO_PLUGIN_DEFINE(nettime, "Network Time Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, nettime_init, nettime_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/mtk.c0000644000015600001650000013135612671500024021030 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL-based devices * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2014 Canonical Ltd. * Copyright (C) 2013 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. * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofono.h" #include #include #include #include #include #include "drivers/rilmodem/rilmodem.h" #include "drivers/rilmodem/vendor.h" #include "drivers/mtkmodem/mtkmodem.h" #include "drivers/mtkmodem/mtk_constants.h" #include "drivers/mtkmodem/mtkutil.h" #include "drivers/mtkmodem/mtkrequest.h" #include "drivers/mtkmodem/mtkreply.h" #include "drivers/mtkmodem/mtkunsol.h" #include "drivers/mtkmodem/mtksettings.h" #define MAX_SIM_STATUS_RETRIES 15 #define MULTISIM_SLOT_0 0 #define MULTISIM_SLOT_1 1 #define SIM_1_ACTIVE 1 #define SIM_2_ACTIVE 2 #define NO_SIM_ACTIVE 0 #define SOCKET_NUM_FOR_DBG_0 -1 #define SOCKET_NUM_FOR_DBG_1 -2 /* this gives 30s for rild to initialize */ #define RILD_MAX_CONNECT_RETRIES 5 #define RILD_CONNECT_RETRY_TIME_S 5 #define T_WAIT_DISCONN_MS 1000 #define T_SIM_SWITCH_FAILSAFE_MS 1000 #define INVALID_SUSPEND_ID -1 #define T_FW_SWITCH_S 60 enum mtk_plmn_type { MTK_PLMN_TYPE_UNKNOWN = 0, MTK_PLMN_TYPE_1, MTK_PLMN_TYPE_2, MTK_PLMN_TYPE_3 }; static const char hex_slot_0[] = "Slot 0: "; static const char hex_slot_1[] = "Slot 1: "; typedef void (*pending_cb_t)(struct cb_data *cbd); struct mtk_data { GRil *ril; int sim_status_retries; ofono_bool_t ofono_online; ofono_bool_t ofono_online_target; int radio_state; struct ofono_sim *sim; /* pending_* are used in case we are disconnected from the socket */ pending_cb_t pending_cb; struct cb_data *pending_cbd; int slot; struct ril_sim_data sim_data; struct ofono_devinfo *devinfo; struct ofono_voicecall *voicecall; struct ofono_call_volume *callvolume; struct cb_data *pending_online_cbd; ofono_bool_t pending_online; ofono_bool_t gprs_attach; int rild_connect_retries; struct ofono_sms *sms; struct ofono_netreg *netreg; struct ofono_ussd *ussd; struct ofono_call_settings *call_settings; struct ofono_call_forwarding *call_forwarding; struct ofono_call_barring *call_barring; struct ofono_phonebook *phonebook; struct ofono_gprs *gprs; struct ofono_message_waiting *message_waiting; struct ofono_modem *modem; ofono_bool_t has_3g; struct mtk_settings_data *mtk_settings; int fw_type; ofono_modem_online_cb_t online_cb; void *online_data; enum mtk_plmn_type sim_plmn_type; enum mtk_plmn_type sensed_plmn_type; int suspend_id; ofono_bool_t trm_pending; unsigned netreg_watch; unsigned status_watch; int netreg_status; guint switch_fw_id; }; /* * MTK dual SIM sockets are not completely symmetric: some requests (essentially * those related for radio power management and SIM slot enablement) can be sent * only through the socket for slot 0. So we need a pointer to the main socket. * Also, we need to access information of one channel from the other channel. */ static struct mtk_data *mtk_data_0; static struct mtk_data *mtk_data_1; /* Some variables control global state of the modem and are then static */ static gboolean disconnect_expected; static guint not_disconn_cb_id; struct socket_data { GRil *ril; const char *path; int radio_state; guint radio_state_ev_id; }; static struct socket_data *sock_0, *sock_1; static int create_gril(struct ofono_modem *modem); static gboolean mtk_connected(gpointer user_data); static void mtk_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t callback, void *data); static void query_3g_caps(struct socket_data *sock); static void socket_disconnected(gpointer user_data); static void start_slot(struct mtk_data *md, struct socket_data *sock, const char *hex_prefix); static void exec_pending_online(struct mtk_data *md); static void mtk_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static struct mtk_data *mtk_data_complement(struct mtk_data *md) { if (md->slot == MULTISIM_SLOT_0) return mtk_data_1; else return mtk_data_0; } static struct socket_data *socket_complement(struct socket_data *sock) { if (sock == sock_0) return sock_1; else return sock_0; } /* * mtk_set_attach_state and mtk_detach_received are called by mtkmodem's gprs * driver. They are needed to solve an issue with data attachment: in case * org.ofono.ConnectionManager Powered property is set for, say, slot 1 while * slot 0 has that property also set, slot 1 will not change the data * registration state even after slot 0 data connection is finally dropped. To * force slot 1 to try to attach we need to send an additional * MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE. The way to know when to do this is to * detect when slot 0 has finally detached. This is done listening for * MTK_RIL_UNSOL_GPRS_DETACH events, but unfortunately these events are received * in the modem that does not need to know about them, so we have to pass them * to the mtk plugin (which has knowledge of both modems) that will take proper * action in the other modem. */ void mtk_set_attach_state(struct ofono_modem *modem, ofono_bool_t attached) { struct mtk_data *md = ofono_modem_get_data(modem); md->gprs_attach = attached; } static void detach_received_cb(struct ril_msg *message, gpointer user_data) { struct mtk_data *md = user_data; if (message->error == RIL_E_SUCCESS) g_ril_print_response_no_args(md->ril, message); else ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); } void mtk_detach_received(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); struct mtk_data *md_c = mtk_data_complement(md); if (md_c != NULL && md_c->gprs_attach) { struct parcel rilp; g_mtk_request_set_gprs_connect_type(md_c->ril, md_c->gprs_attach, &rilp); if (g_ril_send(md_c->ril, MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE, &rilp, detach_received_cb, md_c, NULL) == 0) ofono_error("%s: send failed", __func__); } } static void radio_state_changed(struct ril_msg *message, gpointer user_data) { struct socket_data *sock = user_data; int radio_state = g_ril_unsol_parse_radio_state_changed(sock->ril, message); if (radio_state != sock->radio_state) { struct socket_data *sock_c = socket_complement(sock); ofono_info("%s, %s, state: %s", __func__, sock->path, ril_radio_state_to_string(radio_state)); /* * If there is just one slot, just start it. Otherwise, we ask * who owns the 3G capabilities in case both slots have already * radio state different from UNAVAILABLE. */ if (mtk_data_1 == NULL) { mtk_data_0->has_3g = TRUE; start_slot(mtk_data_0, sock, hex_slot_0); } else if (sock->radio_state == RADIO_STATE_UNAVAILABLE && sock_c != NULL && sock_c->radio_state != RADIO_STATE_UNAVAILABLE) { query_3g_caps(sock); } sock->radio_state = radio_state; } } static void exec_online_callback(struct mtk_data *md) { if (md->online_cb != NULL) { /* * We assume success, as the sim switch request was successful * too. Also, the SIM_* states can be returned independently of * the radio state, so we cannot reliably use it to know if the * sim switch command really did what was requested. */ CALLBACK_WITH_SUCCESS(md->online_cb, md->online_data); md->online_cb = NULL; md->online_data = NULL; if (md->ofono_online) md->mtk_settings = mtk_settings_create(md->modem, md->ril, md->has_3g); else mtk_settings_remove(md->mtk_settings); exec_pending_online(md); } } static void mtk_radio_state_changed(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = user_data; struct mtk_data *md = ofono_modem_get_data(modem); int radio_state = g_ril_unsol_parse_radio_state_changed(md->ril, message); ofono_info("%s, slot %d: state: %s md->ofono_online: %d", __func__, md->slot, ril_radio_state_to_string(radio_state), md->ofono_online); md->radio_state = radio_state; switch (radio_state) { case RADIO_STATE_ON: /* RADIO_STATE_SIM_* are deprecated in AOSP, but still used by MTK */ case RADIO_STATE_SIM_NOT_READY: case RADIO_STATE_SIM_LOCKED_OR_ABSENT: case RADIO_STATE_SIM_READY: case RADIO_STATE_UNAVAILABLE: case RADIO_STATE_OFF: break; default: /* Malformed parcel; no radio state == broken rild */ g_assert(FALSE); } exec_online_callback(md); } static void resume_reg_cb(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = user_data; struct mtk_data *md = ofono_modem_get_data(modem); if (message->error != RIL_E_SUCCESS) { ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); return; } g_ril_print_response_no_args(md->ril, message); } static void resume_reg(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); struct parcel rilp; if (md->suspend_id == INVALID_SUSPEND_ID) return; g_mtk_request_resume_registration(md->ril, md->suspend_id, &rilp); if (g_ril_send(md->ril, MTK_RIL_REQUEST_RESUME_REGISTRATION, &rilp, resume_reg_cb, modem, NULL) == 0) ofono_error("%s: failure sending request", __func__); md->suspend_id = INVALID_SUSPEND_ID; } static void sim_removed(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = (struct ofono_modem *) user_data; struct mtk_data *md = ofono_modem_get_data(modem); DBG(""); g_ril_print_unsol_no_args(md->ril, message); md->sim_plmn_type = MTK_PLMN_TYPE_UNKNOWN; ofono_modem_set_powered(modem, FALSE); g_idle_add(mtk_connected, modem); } static void sim_inserted(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = (struct ofono_modem *) user_data; struct mtk_data *md = ofono_modem_get_data(modem); DBG(""); g_ril_print_unsol_no_args(md->ril, message); if (getenv("OFONO_RIL_HOT_SIM_SWAP")) { ofono_modem_set_powered(modem, FALSE); g_idle_add(mtk_connected, modem); } } static void set_trm_cb(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = user_data; struct mtk_data *md = ofono_modem_get_data(modem); if (message->error != RIL_E_SUCCESS) { ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); return; } g_ril_print_response_no_args(md->ril, message); } static void store_type_cb(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = user_data; struct mtk_data *md = ofono_modem_get_data(modem); struct parcel rilp; int trm = 0; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); md->trm_pending = FALSE; return; } g_ril_print_response_no_args(md->ril, message); /* * Send SET_TRM, which reloads the FW. We do not know the meaning of the * magic TRM numbers (no source code for muxreport daemon). */ if (md->fw_type == MTK_MD_TYPE_LWG) { trm = 11; } else if (md->fw_type == MTK_MD_TYPE_LTG) { trm = 12; } else { ofono_error("%s: wrong modem type %d", __func__, md->fw_type); g_assert(FALSE); } g_mtk_request_set_trm(md->ril, trm, &rilp); if (g_ril_send(md->ril, MTK_RIL_REQUEST_SET_TRM, &rilp, set_trm_cb, modem, NULL) == 0) ofono_error("%s: failure sending request", __func__); /* * rild will close now the socket, and the response to SET_TRM might be * received before that or not. * Measurements showed that rild takes around 5 seconds to re-start, but * we use an 8 seconds timeout as times can vary and that value is also * compatible with krillin modem. */ } static gboolean find_in_table(const char *str, const char **table, int num_elem) { int i; for (i = 0; i < num_elem; ++i) if (strncmp(str, table[i], strlen(table[i])) == 0) return TRUE; return FALSE; } static enum mtk_plmn_type get_plmn_type(const char *code) { /* China Mobile (CMCC) MCC/MNC codes */ const char *table_type1[] = { "46000", "46002", "46007" }; /* China Unicom (CU) and China Telecom MCC/MNC codes */ const char *table_type3[] = { "46001", "46006", "46009", "45407", "46005", "45502" }; if (find_in_table(code, table_type1, G_N_ELEMENTS(table_type1))) return MTK_PLMN_TYPE_1; if (find_in_table(code, table_type3, G_N_ELEMENTS(table_type3))) return MTK_PLMN_TYPE_3; return MTK_PLMN_TYPE_2; } static int select_modem_fw(enum mtk_plmn_type sim_plmn_type, enum mtk_plmn_type sensed_plmn_type) { DBG("PLMN (sim, sensed)=(%d, %d)", sim_plmn_type, sensed_plmn_type); /* * Outside China (sensed t2) -> FDD always * In China (sensed t1, t3) -> sim t1 is TDD, t2 or t3 is FDD, * or wait for imsi to know sim type * Unknown country -> sim t2 or t3 are FDD, wait network if t1, wait * network/imsi events if both unknown */ switch (sensed_plmn_type) { case MTK_PLMN_TYPE_2: return MTK_MD_TYPE_LWG; case MTK_PLMN_TYPE_1: case MTK_PLMN_TYPE_3: switch (sim_plmn_type) { case MTK_PLMN_TYPE_1: return MTK_MD_TYPE_LTG; case MTK_PLMN_TYPE_2: case MTK_PLMN_TYPE_3: return MTK_MD_TYPE_LWG; case MTK_PLMN_TYPE_UNKNOWN: return MTK_MD_TYPE_INVALID; }; case MTK_PLMN_TYPE_UNKNOWN: switch (sim_plmn_type) { case MTK_PLMN_TYPE_2: case MTK_PLMN_TYPE_3: return MTK_MD_TYPE_LWG; case MTK_PLMN_TYPE_1: case MTK_PLMN_TYPE_UNKNOWN: return MTK_MD_TYPE_INVALID; }; }; /* We should never arrive here */ ofono_error("%s: UNREACHABLE POINT REACHED, aborting", __func__); g_assert(FALSE); return MTK_MD_TYPE_INVALID; } static void set_fw_type(struct ofono_modem *modem, int type) { ofono_bool_t lte_cap; struct mtk_data *md = ofono_modem_get_data(modem); md->fw_type = type; lte_cap = (type >= MTK_MD_TYPE_LWG) ? TRUE : FALSE; ofono_modem_set_boolean(modem, MODEM_PROP_LTE_CAPABLE, lte_cap); } static const char *fw_type_to_str(int fw_type) { switch (fw_type) { case MTK_MD_TYPE_2G: return "2G"; case MTK_MD_TYPE_3G: return "3G"; case MTK_MD_TYPE_WG: return "WG"; case MTK_MD_TYPE_TG: return "TG"; case MTK_MD_TYPE_LWG: return "LWG"; case MTK_MD_TYPE_LTG: return "LTG"; case MTK_MD_TYPE_LTNG: return "LTNG"; } return ""; } static void switch_fw(struct ofono_modem *modem, int fw_type) { struct mtk_data *md = ofono_modem_get_data(modem); struct parcel rilp; ofono_info("Switching modem FW from %s to %s", fw_type_to_str(md->fw_type), fw_type_to_str(fw_type)); md->trm_pending = TRUE; set_fw_type(modem, fw_type); g_mtk_request_store_modem_type(md->ril, fw_type, &rilp); if (g_ril_send(md->ril, MTK_RIL_REQUEST_STORE_MODEM_TYPE, &rilp, store_type_cb, modem, NULL) == 0) { ofono_error("%s: failure sending request", __func__); md->trm_pending = FALSE; } } static void check_modem_fw(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); int best_fw; /* We handle only LWG <-> LTG modem fw changes (arale case) */ if (md->fw_type != MTK_MD_TYPE_LTG && md->fw_type != MTK_MD_TYPE_LWG) return; if (md->trm_pending) { DBG("TRM pending, returning"); return; } /* Right modem fw type for our SIM/sensed PLMN */ best_fw = select_modem_fw(md->sim_plmn_type, md->sensed_plmn_type); DBG("Modem type selected is %d", best_fw); /* Best FW not known yet: wait for imsi/sensed network */ if (best_fw == MTK_MD_TYPE_INVALID) return; if (md->fw_type == best_fw) { resume_reg(modem); return; } /* We need to reload FW and reset transactionally */ switch_fw(modem, best_fw); } static void plmn_changed(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = user_data; struct mtk_data *md = ofono_modem_get_data(modem); struct parcel_str_array *plmns; plmns = g_mtk_unsol_parse_plmn_changed(md->ril, message); if (plmns == NULL) { ofono_error("%s: parse error", __func__); return; } md->sensed_plmn_type = get_plmn_type(plmns->str[0]); DBG("Best PLMN is %s (type %d)", plmns->str[0], md->sensed_plmn_type); parcel_free_str_array(plmns); check_modem_fw(modem); } static void reg_suspended(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = user_data; struct mtk_data *md = ofono_modem_get_data(modem); int suspend_id; suspend_id = g_mtk_unsol_parse_registration_suspended(md->ril, message); if (suspend_id < 0) { ofono_error("%s: parse error", __func__); return; } md->suspend_id = suspend_id; check_modem_fw(modem); } static gboolean tout_not_registered(gpointer user_data) { struct ofono_modem *modem = user_data; struct mtk_data *md = ofono_modem_get_data(modem); if (md->trm_pending) goto end; DBG("type was %d", md->fw_type); if (md->fw_type == MTK_MD_TYPE_LTG) switch_fw(modem, MTK_MD_TYPE_LWG); else switch_fw(modem, MTK_MD_TYPE_LTG); end: md->switch_fw_id = 0; return FALSE; } static void cancel_fw_load_timer(struct mtk_data *md) { if (md->switch_fw_id) { g_source_remove(md->switch_fw_id); md->switch_fw_id = 0; } } static void netreg_status_update(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); if (md->sim_plmn_type != MTK_PLMN_TYPE_1) return; if (md->fw_type != MTK_MD_TYPE_LTG && md->fw_type != MTK_MD_TYPE_LWG) return; DBG("%d", md->netreg_status); if (md->switch_fw_id) { if (md->netreg_status == NETWORK_REGISTRATION_STATUS_REGISTERED || md->netreg_status == NETWORK_REGISTRATION_STATUS_ROAMING) cancel_fw_load_timer(md); return; } if (md->netreg_status == NETWORK_REGISTRATION_STATUS_NOT_REGISTERED) md->switch_fw_id = g_timeout_add_seconds(T_FW_SWITCH_S, tout_not_registered, modem); } static void netreg_status_changed(int status, int lac, int ci, int tech, const char *mcc, const char *mnc, void *data) { struct ofono_modem *modem = data; struct mtk_data *md = ofono_modem_get_data(modem); if (md->netreg_status == status) return; md->netreg_status = status; netreg_status_update(modem); } static void netreg_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_modem *modem = data; struct mtk_data *md = ofono_modem_get_data(modem); void *netreg; DBG("%d", cond); if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { cancel_fw_load_timer(md); md->status_watch = 0; return; } netreg = __ofono_atom_get_data(atom); md->netreg_status = ofono_netreg_get_status(netreg); md->status_watch = __ofono_netreg_add_status_watch(netreg, netreg_status_changed, modem, NULL); netreg_status_update(modem); } static int mtk_probe(struct ofono_modem *modem) { struct mtk_data *md = g_try_new0(struct mtk_data, 1); if (md == NULL) { errno = ENOMEM; goto error; } md->ofono_online = FALSE; md->radio_state = RADIO_STATE_UNAVAILABLE; md->suspend_id = INVALID_SUSPEND_ID; md->slot = ofono_modem_get_integer(modem, "Slot"); if (md->slot == MULTISIM_SLOT_0) mtk_data_0 = md; else mtk_data_1 = md; DBG("slot %d", md->slot); md->modem = modem; ofono_modem_set_data(modem, md); return 0; error: g_free(md); return -errno; } static void mtk_remove(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); ofono_modem_set_data(modem, NULL); if (!md) return; g_ril_unref(md->ril); g_free(md); } static void mtk_pre_sim(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); DBG("slot %d", md->slot); } static void mtk_post_sim(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); DBG("slot %d", md->slot); } /* * sim_state_watch listens to SIM state changes and creates/removes atoms * accordingly. This is needed because we cannot rely on the modem core code, * which handles modem state transitions, to do this due to the SIM not being * accessible in the offline state for mtk modems. This causes a mismatch * between what the core thinks it can do in some states and what the mtk modem * can really do in those. This is a workaround to solve that. */ static void sim_state_watch(enum ofono_sim_state new_state, void *data) { struct ofono_modem *modem = data; struct mtk_data *md = ofono_modem_get_data(modem); if (new_state == OFONO_SIM_STATE_READY) { struct ofono_gprs_context *gc; struct ril_gprs_driver_data gprs_data = { md->ril, modem }; struct ril_gprs_context_data inet_ctx = { md->ril, modem, OFONO_GPRS_CONTEXT_TYPE_INTERNET }; struct ril_gprs_context_data mms_ctx = { md->ril, modem, OFONO_GPRS_CONTEXT_TYPE_MMS }; DBG("SIM ready, creating more atoms"); /* * TODO: this function should setup: * - stk ( SIM toolkit ) */ md->sms = ofono_sms_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, md->ril); /* netreg needs access to the SIM (SPN, SPDI) */ md->netreg = ofono_netreg_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, md->ril); md->ussd = ofono_ussd_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, md->ril); md->call_settings = ofono_call_settings_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, md->ril); md->call_forwarding = ofono_call_forwarding_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, md->ril); md->call_barring = ofono_call_barring_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, md->ril); md->phonebook = ofono_phonebook_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, modem); md->gprs = ofono_gprs_create(modem, OFONO_RIL_VENDOR_MTK, MTKMODEM, &gprs_data); gc = ofono_gprs_context_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, &inet_ctx); if (gc) { ofono_gprs_context_set_type(gc, OFONO_GPRS_CONTEXT_TYPE_INTERNET); ofono_gprs_add_context(md->gprs, gc); } gc = ofono_gprs_context_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, &mms_ctx); if (gc) { ofono_gprs_context_set_type(gc, OFONO_GPRS_CONTEXT_TYPE_MMS); ofono_gprs_add_context(md->gprs, gc); } md->message_waiting = ofono_message_waiting_create(modem); if (md->message_waiting) ofono_message_waiting_register(md->message_waiting); /* * Now that we can access IMSI, see if a FW change is needed. */ md->sim_plmn_type = get_plmn_type(ofono_sim_get_imsi(md->sim)); check_modem_fw(modem); } else if (new_state == OFONO_SIM_STATE_LOCKED_OUT) { DBG("SIM locked, removing atoms"); if (md->message_waiting) { ofono_message_waiting_remove(md->message_waiting); md->message_waiting = NULL; } if (md->gprs) { ofono_gprs_remove(md->gprs); md->gprs = NULL; } if (md->phonebook) { ofono_phonebook_remove(md->phonebook); md->phonebook = NULL; } if (md->call_barring) { ofono_call_barring_remove(md->call_barring); md->call_barring = NULL; } if (md->call_forwarding) { ofono_call_forwarding_remove(md->call_forwarding); md->call_forwarding = NULL; } if (md->call_settings) { ofono_call_settings_remove(md->call_settings); md->call_settings = NULL; } if (md->ussd) { ofono_ussd_remove(md->ussd); md->ussd = NULL; } if (md->netreg) { ofono_netreg_remove(md->netreg); md->netreg = NULL; } if (md->sms) { ofono_sms_remove(md->sms); md->sms = NULL; } } } static void create_online_atoms(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); struct ril_radio_settings_driver_data rs_data = { md->ril, modem }; md->sim_data.gril = md->ril; md->sim_data.modem = modem; md->sim_data.ril_state_watch = sim_state_watch; md->sim = ofono_sim_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, &md->sim_data); g_assert(md->sim != NULL); /* Radio settings does not depend on the SIM */ ofono_radio_settings_create(modem, OFONO_RIL_VENDOR_MTK, MTKMODEM, &rs_data); if (md->netreg_watch == 0) md->netreg_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_NETREG, netreg_watch, modem, NULL); } static void query_type_cb(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = user_data; struct mtk_data *md = ofono_modem_get_data(modem); int type; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); return; } type = g_mtk_reply_parse_query_modem_type(md->ril, message); if (type < 0) { ofono_error("%s: parse error", __func__); return; } set_fw_type(modem, type); create_online_atoms(modem); } static void mtk_post_online(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); DBG("slot %d", md->slot); /* With modem powered we can query the type in krillin */ if (g_ril_send(md->ril, MTK_RIL_REQUEST_QUERY_MODEM_TYPE, NULL, query_type_cb, md->modem, NULL) == 0) ofono_error("%s: failure sending QUERY_MODEM_TYPE", __func__); /* Register for changes in SIM insertion */ g_ril_register(md->ril, MTK_RIL_UNSOL_SIM_PLUG_OUT, sim_removed, modem); g_ril_register(md->ril, MTK_RIL_UNSOL_SIM_PLUG_IN, sim_inserted, modem); } static void exec_pending_online(struct mtk_data *md) { struct mtk_data *md_c; DBG(""); mtk_data_0->pending_cb = NULL; /* Execute possible pending operation on the other modem */ md_c = mtk_data_complement(md); if (md_c != NULL && md_c->pending_online_cbd) { struct cb_data *pending_cbd = md_c->pending_online_cbd; ofono_modem_online_cb_t pending_cb = pending_cbd->cb; mtk_set_online(pending_cbd->user, md_c->pending_online, pending_cb, pending_cbd->data); g_free(md_c->pending_online_cbd); md_c->pending_online_cbd = NULL; } } static gboolean sim_switch_failsafe(gpointer user_data) { struct mtk_data *md = user_data; exec_online_callback(md); return FALSE; } static void mtk_sim_mode_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_modem *modem = cbd->user; struct mtk_data *md = ofono_modem_get_data(modem); if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(md->ril, message); /* * Although the request was successful, radio state might not * have changed yet. So we wait for the upcoming radio event, * otherwise RIL requests that depend on radio state will fail. * If we are switching the 3G slot, we cannot really trust this * behaviour, so we add a failsafe timer. */ md->online_cb = cb; md->online_data = cbd->data; g_timeout_add(T_SIM_SWITCH_FAILSAFE_MS, sim_switch_failsafe, md); } else { ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); CALLBACK_WITH_FAILURE(cb, cbd->data); exec_pending_online(md); } } static int sim_state() { int state = mtk_data_0->ofono_online ? SIM_1_ACTIVE : NO_SIM_ACTIVE; if (mtk_data_1 && mtk_data_1->ofono_online) state |= SIM_2_ACTIVE; return state; } static void mtk_send_sim_mode(GRilResponseFunc func, gpointer user_data) { struct parcel rilp; struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = NULL; GDestroyNotify notify = NULL; int sim_mode; if (cbd != NULL) { notify = g_free; cb = cbd->cb; } /* Case of modems with just one slot */ if (mtk_data_1 == NULL) { mtk_data_0->pending_cb = NULL; if (cbd != NULL) { CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); } return; } sim_mode = sim_state(); if (sim_mode == NO_SIM_ACTIVE) sim_mode = MTK_SWITCH_MODE_ALL_INACTIVE; g_mtk_request_dual_sim_mode_switch(mtk_data_0->ril, sim_mode, &rilp); /* This request is always sent through the main socket */ if (g_ril_send(mtk_data_0->ril, MTK_RIL_REQUEST_DUAL_SIM_MODE_SWITCH, &rilp, func, cbd, notify) == 0 && cbd != NULL) { ofono_error("%s: failure sending request", __func__); mtk_data_0->pending_cb = NULL; if (cbd != NULL) { CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } } } static gboolean no_disconnect_case(gpointer user_data) { struct cb_data *cbd = user_data; ofono_info("%s: Execute pending sim mode switch", __func__); not_disconn_cb_id = 0; mtk_send_sim_mode(mtk_sim_mode_cb, cbd); return FALSE; } static void poweron_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_modem *modem = cbd->user; struct mtk_data *md = ofono_modem_get_data(modem); ofono_modem_online_cb_t cb = cbd->cb; DBG(""); /* * MTK's rild behavior when a POWERON is sent to it is different * depending on whether a previous POWEROFF had been sent. When * the modem is initialized during device startup, POWERON is * sent without a prior POWEROFF, rild responds with an OK reply, * and the modem is brought up. Any subsequent POWERON requests * are sent whenever both modems have been offlined before ( meaning a * POWEROFF has been sent prior ). rild may respond to the POWERON * request, but will usually ( always? ) trigger a socket disconnect in * this case. * * This means there's a race condition between the POWERON reply * callback and the socket disconnect function ( which triggers a * SIM_MODE_SWITCH request ). In some cases rild is slower than * usual closing the socket, so we add a timeout to avoid following * the code path used when there is not a disconnection. Otherwise, * there would be a race and some requests would return errors due to * having been sent through the about-to-be-disconnected socket, leaving * ofono in an inconsistent state. So, we delay sending the * SIM_MODE_SWITCH for 1s, to allow the disconnect to happen when we * know that we have sent previously a POWEROFF. * * Also, I saw once that sending SIM_MODE while the * socket was being disconnected provoked a crash due to SIGPIPE being * issued. The timeout should also fix this. */ if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(md->ril, message); if (disconnect_expected) { if (not_disconn_cb_id != 0) g_source_remove(not_disconn_cb_id); not_disconn_cb_id = g_timeout_add(T_WAIT_DISCONN_MS, no_disconnect_case, cbd); } else { mtk_send_sim_mode(mtk_sim_mode_cb, cbd); } } else { ofono_error("%s RADIO_POWERON error %s", __func__, ril_error_to_string(message->error)); CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } } static void poweron_disconnect(struct cb_data *cbd) { DBG("Execute pending sim mode switch"); mtk_send_sim_mode(mtk_sim_mode_cb, cbd); } static void poweroff_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_modem *modem = cbd->user; struct mtk_data *md = ofono_modem_get_data(modem); if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(md->ril, message); mtk_settings_remove(md->mtk_settings); CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); CALLBACK_WITH_FAILURE(cb, cbd->data); } } static int power_on_off(GRil *ril, gboolean on, struct cb_data *cbd) { int cancel_id; int req; struct parcel rilp; struct parcel *p_rilp; GRilResponseFunc resp; GDestroyNotify notify; ofono_modem_online_cb_t cb = cbd->cb; /* Case of modems with just one slot */ if (mtk_data_1 == NULL) { /* Fall back to generic RIL_REQUEST_RADIO_POWER */ req = RIL_REQUEST_RADIO_POWER; g_ril_request_power(ril, on, &rilp); p_rilp = &rilp; } else { req = on ? MTK_RIL_REQUEST_RADIO_POWERON : MTK_RIL_REQUEST_RADIO_POWEROFF; p_rilp = NULL; } if (on) { resp = poweron_cb; notify = NULL; } else { resp = poweroff_cb; notify = g_free; } cancel_id = g_ril_send(ril, req, p_rilp, resp, cbd, notify); if (cancel_id == 0) { ofono_error("%s: failure sending request", __func__); CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); return 0; } return cancel_id; } static void mtk_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t callback, void *data) { struct mtk_data *md = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(callback, data, modem); ofono_modem_online_cb_t cb = cbd->cb; int current_state, next_state; /* * Serialize online requests to avoid incoherent states. When changing * the online state of *one* of the modems, we need to send a * DUAL_SIM_MODE_SWITCH request, which affects *both* modems. Also, when * we want to online one modem and at that time both modems are * offline a RADIO_POWERON needs to be sent before DUAL_SIM_MODE_SWITCH, * with the additional complexity of being disconnected from the rild * socket while doing the sequence. This can take some time, and we * cannot change the state of the other modem while the sequence is * happenig, as DUAL_SIM_MODE_SWITCH affects both states. Therefore, we * need to do this serialization, which is different from the one done * per modem by ofono core. */ if (mtk_data_0->pending_cb != NULL) { md->pending_online_cbd = cbd; md->pending_online = online; return; } current_state = sim_state(); md->ofono_online = online; /* Changes as md points to either mtk_data_0 or mtk_data_1 variables */ next_state = sim_state(); DBG("setting md_%d->ofono_online to: %d (from %d to %d)", md->slot, online, current_state, next_state); if (current_state == next_state) { CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); return; } /* Reset mtk_data variables */ if (online == FALSE) md->sim_status_retries = 0; if (current_state == NO_SIM_ACTIVE) { /* Old state was off, need to power on the modem */ if (power_on_off(mtk_data_0->ril, TRUE, cbd)) { /* Socket might disconnect... failsafe */ mtk_data_0->pending_cb = poweron_disconnect; mtk_data_0->pending_cbd = cbd; } } else if (next_state == NO_SIM_ACTIVE) { /* Disconnection expected for dual SIM only */ if (power_on_off(mtk_data_0->ril, FALSE, cbd) && mtk_data_1 != NULL) disconnect_expected = TRUE; } else { mtk_send_sim_mode(mtk_sim_mode_cb, cbd); } } static void set_online_cb(const struct ofono_error *error, void *data) { if (mtk_data_1->ofono_online_target && !mtk_data_1->ofono_online) mtk_set_online(mtk_data_1->modem, TRUE, set_online_cb, NULL); } static void set_offline_cb(const struct ofono_error *error, void *data) { if (mtk_data_1->ofono_online) mtk_set_online(mtk_data_1->modem, FALSE, set_offline_cb, NULL); else if (mtk_data_0->ofono_online_target) mtk_set_online(mtk_data_0->modem, TRUE, set_online_cb, NULL); else mtk_set_online(mtk_data_1->modem, TRUE, set_online_cb, NULL); } void mtk_reset_all_modems(void) { if (!mtk_data_0->ofono_online && !mtk_data_1->ofono_online) return; mtk_data_0->ofono_online_target = mtk_data_0->ofono_online; mtk_data_1->ofono_online_target = mtk_data_1->ofono_online; ofono_modem_set_powered(mtk_data_0->modem, FALSE); ofono_modem_set_powered(mtk_data_1->modem, FALSE); if (mtk_data_0->ofono_online) mtk_set_online(mtk_data_0->modem, FALSE, set_offline_cb, NULL); else mtk_set_online(mtk_data_1->modem, FALSE, set_offline_cb, NULL); } void mtk_reset_modem(struct ofono_modem *modem) { if (ofono_modem_get_powered(modem) == FALSE) return; ofono_modem_set_powered(modem, FALSE); g_idle_add(mtk_connected, modem); } static void create_atoms_on_connection(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); struct ril_voicecall_driver_data vc_data = { md->ril, modem }; md->devinfo = ofono_devinfo_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, md->ril); /* Create interfaces useful for emergency calls */ md->voicecall = ofono_voicecall_create(modem, OFONO_RIL_VENDOR_MTK, MTKMODEM, &vc_data); md->callvolume = ofono_call_volume_create(modem, OFONO_RIL_VENDOR_MTK, RILMODEM, md->ril); } static void remove_atoms_on_disconnection(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); if (__ofono_modem_find_atom(modem, OFONO_ATOM_TYPES_CALL_VOLUME)) ofono_call_volume_remove(md->callvolume); md->callvolume = NULL; if (__ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_VOICECALL)) ofono_voicecall_remove(md->voicecall); md->voicecall = NULL; if (__ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_DEVINFO)) ofono_devinfo_remove(md->devinfo); md->devinfo = NULL; } static void start_slot(struct mtk_data *md, struct socket_data *sock, const char *hex_prefix) { ofono_info("Physical slot %d in socket %s", md->slot, sock->path); md->ril = sock->ril; md->radio_state = sock->radio_state; g_ril_set_slot(md->ril, md->slot); if (getenv("OFONO_RIL_TRACE")) g_ril_set_trace(md->ril, TRUE); if (getenv("OFONO_RIL_HEX_TRACE")) g_ril_set_debugf(md->ril, mtk_debug, (char *) hex_prefix); g_ril_set_disconnect_function(md->ril, socket_disconnected, md->modem); g_ril_unregister(sock->ril, sock->radio_state_ev_id); g_ril_register(md->ril, RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, mtk_radio_state_changed, md->modem); mtk_connected(md->modem); } static void query_3g_caps_cb(struct ril_msg *message, gpointer user_data) { struct socket_data *sock = user_data; struct socket_data *sock_for_md_0, *sock_for_md_1; int slot_3g; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: error %s", __func__, ril_error_to_string(message->error)); return; } slot_3g = g_mtk_reply_parse_get_3g_capability(sock->ril, message); /* * The socket at sock_slot_0 always connects to the slot with 3G * capabilities, while sock_slot_1 connects to the slot that is just 2G. * However, the physical slot that owns the 3G capabilities can be * changed dynamically using a RILd request, so the sockets can connect * to different physical slots depending on the current configuration. * We want to keep the relationship between the physical slots and * the modem names in DBus (so /ril_0 and /ril_1 always refer to the * same physical slots), so here we assign the sockets needed by * mtk_data_0 and mtk_data_1 structures to make sure that happens. */ if (slot_3g == MULTISIM_SLOT_0) { sock_for_md_0 = sock_0; sock_for_md_1 = sock_1; mtk_data_0->has_3g = TRUE; mtk_data_1->has_3g = FALSE; } else { sock_for_md_0 = sock_1; sock_for_md_1 = sock_0; mtk_data_0->has_3g = FALSE; mtk_data_1->has_3g = TRUE; } start_slot(mtk_data_0, sock_for_md_0, hex_slot_0); start_slot(mtk_data_1, sock_for_md_1, hex_slot_1); g_free(sock_0); sock_0 = NULL; g_free(sock_1); sock_1 = NULL; } static void query_3g_caps(struct socket_data *sock) { if (g_ril_send(sock->ril, MTK_RIL_REQUEST_GET_3G_CAPABILITY, NULL, query_3g_caps_cb, sock, NULL) <= 0) ofono_error("%s Error querying 3G capabilities", __func__); } static gboolean mtk_connected(gpointer user_data) { struct ofono_modem *modem = (struct ofono_modem *) user_data; struct mtk_data *md = ofono_modem_get_data(modem); ofono_info("[slot %d] CONNECTED", md->slot); DBG("calling set_powered(TRUE)"); if (!ofono_modem_get_powered(modem)) ofono_modem_set_powered(modem, TRUE); create_atoms_on_connection(modem); if (md->pending_cb) md->pending_cb(md->pending_cbd); /* Call the function just once */ return FALSE; } static gboolean reconnect_rild(gpointer user_data) { struct ofono_modem *modem = (struct ofono_modem *) user_data; struct mtk_data *md = ofono_modem_get_data(modem); ofono_info("[slot %d] trying to reconnect", md->slot); if (create_gril(modem) < 0) return TRUE; /* Reconnected: do not call this again */ return FALSE; } #define WAIT_FOR_RILD_TO_RESTART_MS 8000 /* Milliseconds */ static void socket_disconnected(gpointer user_data) { struct ofono_modem *modem = user_data; struct mtk_data *md = ofono_modem_get_data(modem); DBG("slot %d", md->slot); /* Atoms use old gril object, remove and recreate later */ remove_atoms_on_disconnection(modem); g_ril_unref(md->ril); md->ril = NULL; md->sensed_plmn_type = MTK_PLMN_TYPE_UNKNOWN; md->suspend_id = INVALID_SUSPEND_ID; if (md->trm_pending) { md->ofono_online = FALSE; ofono_modem_set_powered(md->modem, FALSE); md->trm_pending = FALSE; } /* Disconnection happened so we do not call failsafe function */ if (not_disconn_cb_id != 0) { g_source_remove(not_disconn_cb_id); not_disconn_cb_id = 0; } /* The disconnection happens because rild is re-starting, wait for it */ g_timeout_add(WAIT_FOR_RILD_TO_RESTART_MS, reconnect_rild, modem); } static const char sock_slot_0[] = "/dev/socket/rild"; static const char sock_slot_1[] = "/dev/socket/rild2"; static int create_gril(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); struct socket_data *sock; int sock_num; DBG("slot %d", md->slot); if (md->ril != NULL) return 0; sock = g_try_malloc0(sizeof(*sock)); if (sock == NULL) { ofono_error("%s: Cannot allocate socket_data", __func__); return -ENOMEM; } if (md->slot == MULTISIM_SLOT_0) { sock_num = SOCKET_NUM_FOR_DBG_0; sock->path = sock_slot_0; } else { sock_num = SOCKET_NUM_FOR_DBG_1; sock->path = sock_slot_1; } /* Opens the socket to RIL */ sock->ril = g_ril_new(sock->path, OFONO_RIL_VENDOR_MTK); /* * NOTE: Since AT modems open a tty, and then call * g_at_chat_new(), they're able to return -EIO if * the first fails, and -ENOMEM if the second fails. * in our case, we already return -EIO if the ril_new * fails. If this is important, we can create a ril_socket * abstraction... ( probaby not a bad idea ). */ if (sock->ril == NULL) { ofono_error("g_ril_new() failed to connect to %s!", sock->path); g_free(sock); return -EIO; } else if (md->slot == MULTISIM_SLOT_0) { sock_0 = sock; } else { sock_1 = sock; } sock->radio_state = RADIO_STATE_UNAVAILABLE; sock->radio_state_ev_id = g_ril_register(sock->ril, RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, radio_state_changed, sock); g_ril_register(sock->ril, MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED, plmn_changed, modem); g_ril_register(sock->ril, MTK_RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED, reg_suspended, modem); /* sock_num is negative to avoid confusion with physical slots */ g_ril_set_slot(sock->ril, sock_num); g_ril_set_vendor_print_msg_id_funcs(sock->ril, mtk_request_id_to_string, mtk_unsol_request_to_string); if (getenv("OFONO_RIL_TRACE")) g_ril_set_trace(sock->ril, TRUE); if (getenv("OFONO_RIL_HEX_TRACE")) g_ril_set_debugf(sock->ril, mtk_debug, (char *) sock->path); return 0; } static gboolean connect_rild(gpointer user_data) { struct ofono_modem *modem = (struct ofono_modem *) user_data; struct mtk_data *md = ofono_modem_get_data(modem); ofono_info("Trying to reconnect to slot %d...", md->slot); if (md->rild_connect_retries++ < RILD_MAX_CONNECT_RETRIES) { if (create_gril(modem) < 0) return TRUE; } else { ofono_error("Exiting, can't connect to rild."); exit(0); } return FALSE; } static int mtk_enable(struct ofono_modem *modem) { int ret; /* We handle SIM states due to MTK peculiarities */ ofono_modem_set_driver_watches_sim(modem, TRUE); ret = create_gril(modem); if (ret < 0) g_timeout_add_seconds(RILD_CONNECT_RETRY_TIME_S, connect_rild, modem); /* * We will mark the modem as powered when we receive an event that * confirms that the radio is in a state different from unavailable */ return -EINPROGRESS; } static int mtk_disable(struct ofono_modem *modem) { struct mtk_data *md = ofono_modem_get_data(modem); DBG("%p", modem); if (md->slot == MULTISIM_SLOT_0 && not_disconn_cb_id != 0) { g_source_remove(not_disconn_cb_id); not_disconn_cb_id = 0; } if (md->ofono_online) { md->ofono_online = FALSE; mtk_send_sim_mode(NULL, NULL); } return 0; } static ofono_bool_t mtk_is_standby(struct ofono_modem *modem) { return TRUE; } static struct ofono_modem_driver mtk_driver = { .name = "mtk", .probe = mtk_probe, .remove = mtk_remove, .enable = mtk_enable, .disable = mtk_disable, .pre_sim = mtk_pre_sim, .post_sim = mtk_post_sim, .post_online = mtk_post_online, .set_online = mtk_set_online, .is_standby = mtk_is_standby, }; static int mtk_init(void) { int retval = ofono_modem_driver_register(&mtk_driver); if (retval != 0) DBG("ofono_modem_driver_register returned: %d", retval); return retval; } static void mtk_exit(void) { DBG(""); ofono_modem_driver_unregister(&mtk_driver); } OFONO_PLUGIN_DEFINE(mtk, "MTK modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, mtk_init, mtk_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/upower.c0000644000015600001650000002103612671500073021553 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2015 Canonical 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. * * 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 #endif #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" #define UPOWER_SERVICE "org.freedesktop.UPower" #define UPOWER_PATH "/org/freedesktop/UPower" #define UPOWER_INTERFACE UPOWER_SERVICE #define UPOWER_DEVICE_INTERFACE UPOWER_SERVICE ".Device" static guint modem_watch; static guint upower_daemon_watch; static DBusConnection *connection; static int last_battery_level; static char *battery_device_path; static void emulator_battery_cb(struct ofono_atom *atom, void *data) { int val = GPOINTER_TO_INT(data); DBG("calling set_indicator: %d", val); ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_BATTERY, val); } static void update_modem_battery_indicator(struct ofono_modem *modem, void *data) { __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_battery_cb, data); } static void update_battery_level(double percentage_val) { int battery_level; if (percentage_val <= 1.00) { battery_level = 0; } else if (percentage_val > 1.00 && percentage_val <= 100.00) { battery_level = ((int) percentage_val - 1) / 20 + 1; } else { ofono_error("%s: Invalid value for battery level: %f", __func__, percentage_val); return; } DBG("last_battery_level: %d battery_level: %d (%f)", last_battery_level, battery_level, percentage_val); if (last_battery_level == battery_level) return; last_battery_level = battery_level; __ofono_modem_foreach(update_modem_battery_indicator, GINT_TO_POINTER(battery_level)); } static gboolean battery_props_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *iface; DBusMessageIter iter, dict; double percentage_val; gboolean percentage_found = FALSE; gboolean retval = FALSE; DBG(""); dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { ofono_error("%s: iface != TYPE_STRING!", __func__); goto done; } dbus_message_iter_get_basic(&iter, &iface); if (g_str_equal(iface, UPOWER_DEVICE_INTERFACE) != TRUE) { ofono_error("%s: wrong iface: %s!", __func__, iface); goto done; } if (!dbus_message_iter_next(&iter)) { ofono_error("%s: advance iter failed!", __func__); goto done; } if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { ofono_error("%s: type != ARRAY!", __func__); goto done; } dbus_message_iter_recurse(&iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, val; const char *key; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) { ofono_error("%s: key type != STRING!", __func__); goto done; } dbus_message_iter_get_basic(&entry, &key); if (g_str_equal(key, "Percentage") != TRUE) { dbus_message_iter_next(&dict); continue; } dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) { ofono_error("%s: 'Percentage' val != VARIANT", __func__); goto done; } dbus_message_iter_recurse(&entry, &val); if (dbus_message_iter_get_arg_type(&val) != DBUS_TYPE_DOUBLE) { ofono_error("%s: 'Percentage' val != DOUBLE", __func__); goto done; } dbus_message_iter_get_basic(&val, &percentage_val); percentage_found = TRUE; break; } /* No errors found during parsing, so don't trigger cb removal */ retval = TRUE; if (percentage_found == FALSE) goto done; update_battery_level(percentage_val); done: return retval; } static void emulator_hfp_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) { DBG("REGISTERED; calling set_indicator: %d", last_battery_level); ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_BATTERY, last_battery_level); return; } DBG("UNREGISTERED"); } static void modemwatch(struct ofono_modem *modem, gboolean added, void *user) { const char *path = ofono_modem_get_path(modem); DBG("modem: %s, added: %d", path, added); if (added) __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_hfp_watch, NULL, NULL); } static void call_modemwatch(struct ofono_modem *modem, void *user) { modemwatch(modem, TRUE, user); } static gboolean parse_devices_reply(DBusMessage *reply) { DBusMessageIter array, iter; const char *path; DBG(""); if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { ofono_error("%s: ERROR reply to EnumerateDevices", __func__); return FALSE; } if (dbus_message_iter_init(reply, &array) == FALSE) { ofono_error("%s: error initializing array iter", __func__); return FALSE; } if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) { ofono_error("%s: type != ARRAY!", __func__); return FALSE; } dbus_message_iter_recurse(&array, &iter); while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_OBJECT_PATH) { dbus_message_iter_get_basic(&iter, &path); if (g_strrstr(path, "/battery_")) { ofono_info("%s: found 1st battery device: %s", __func__, path); battery_device_path = g_strdup(path); break; } if (!dbus_message_iter_next(&iter)) break; } return TRUE; } static void enum_devices_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply; DBG(""); reply = dbus_pending_call_steal_reply(call); if (reply == NULL) { ofono_error("%s: dbus_message_new_method failed", __func__); goto done; } if (parse_devices_reply(reply) == FALSE) goto done; DBG("parse_devices_reply OK"); /* TODO: handle removable batteries */ if (battery_device_path == NULL) { ofono_error("%s: no battery detected", __func__); goto done; } /* Always listen to PropertiesChanged for battery */ g_dbus_add_signal_watch(connection, UPOWER_SERVICE, battery_device_path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", battery_props_changed, NULL, NULL); modem_watch = __ofono_modemwatch_add(modemwatch, NULL, NULL); __ofono_modem_foreach(call_modemwatch, NULL); done: if (reply) dbus_message_unref(reply); dbus_pending_call_unref(call); } static void upower_connect(DBusConnection *conn, void *user_data) { DBusPendingCall *call; DBusMessage *msg; DBG("upower connect"); msg = dbus_message_new_method_call(UPOWER_SERVICE, UPOWER_PATH, UPOWER_INTERFACE, "EnumerateDevices"); if (msg == NULL) { ofono_error("%s: dbus_message_new_method failed", __func__); return; } if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) { ofono_error("%s: Sending EnumerateDevices failed", __func__); goto done; } dbus_pending_call_set_notify(call, enum_devices_reply, NULL, NULL); done: dbus_message_unref(msg); } static void upower_disconnect(DBusConnection *conn, void *user_data) { DBG("upower disconnect"); if (modem_watch) { __ofono_modemwatch_remove(modem_watch); modem_watch = 0; } if (battery_device_path) { g_free(battery_device_path); battery_device_path = NULL; } } static int upower_init(void) { DBG("upower init"); connection = ofono_dbus_get_connection(); upower_daemon_watch = g_dbus_add_service_watch(connection, UPOWER_SERVICE, upower_connect, upower_disconnect, NULL, NULL); return 0; } static void upower_exit(void) { if (upower_daemon_watch) g_dbus_remove_watch(connection, upower_daemon_watch); if (modem_watch) __ofono_modemwatch_remove(modem_watch); if (battery_device_path) g_free(battery_device_path); } OFONO_PLUGIN_DEFINE(upower, "upower battery monitor", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, upower_init, upower_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/hfp_hf_bluez5.c0000644000015600001650000005067012671500024022754 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 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 #endif #include #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bluetooth.h" #include "hfp.h" #include "bluez5.h" #ifndef DBUS_TYPE_UNIX_FD #define DBUS_TYPE_UNIX_FD -1 #endif #define HFP_EXT_PROFILE_PATH "/bluetooth/profile/hfp_hf" #define HFP16_HF_DRIVER "hfp16-hf-driver" struct hfp { struct hfp_slc_info info; DBusMessage *msg; struct ofono_handsfree_card *card; unsigned int bcc_id; }; static const char *none_prefix[] = { NULL }; static GDBusClient *bluez = NULL; static void hfp_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static void slc_established(gpointer userdata) { struct ofono_modem *modem = userdata; struct hfp *hfp = ofono_modem_get_data(modem); DBusMessage *msg; ofono_modem_set_powered(modem, TRUE); msg = dbus_message_new_method_return(hfp->msg); g_dbus_send_message(ofono_dbus_get_connection(), msg); dbus_message_unref(hfp->msg); hfp->msg = NULL; ofono_info("Service level connection established"); ofono_handsfree_card_register(hfp->card); } static void slc_failed(gpointer userdata) { struct ofono_modem *modem = userdata; struct hfp *hfp = ofono_modem_get_data(modem); struct hfp_slc_info *info = &hfp->info; DBusMessage *msg; msg = g_dbus_create_error(hfp->msg, BLUEZ_ERROR_INTERFACE ".Failed", "HFP Handshake failed"); g_dbus_send_message(ofono_dbus_get_connection(), msg); dbus_message_unref(hfp->msg); hfp->msg = NULL; ofono_error("Service level connection failed"); ofono_modem_set_powered(modem, FALSE); ofono_handsfree_card_remove(hfp->card); hfp->card = NULL; g_at_chat_unref(info->chat); info->chat = NULL; } static void hfp_disconnected_cb(gpointer user_data) { struct ofono_modem *modem = user_data; struct hfp *hfp = ofono_modem_get_data(modem); struct hfp_slc_info *info = &hfp->info; DBG("HFP disconnected"); ofono_modem_set_powered(modem, FALSE); ofono_handsfree_card_remove(hfp->card); hfp->card = NULL; g_at_chat_unref(info->chat); info->chat = NULL; } static int service_level_connection(struct ofono_modem *modem, int fd, guint16 version) { struct hfp *hfp = ofono_modem_get_data(modem); struct hfp_slc_info *info = &hfp->info; GIOChannel *io; GAtSyntax *syntax; GAtChat *chat; io = g_io_channel_unix_new(fd); syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_set_close_on_unref(io, TRUE); g_io_channel_unref(io); if (chat == NULL) return -ENOMEM; g_at_chat_set_disconnect_function(chat, hfp_disconnected_cb, modem); if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, hfp_debug, ""); hfp_slc_info_init(info, version); info->chat = chat; hfp_slc_establish(info, slc_established, slc_failed, modem); return -EINPROGRESS; } static struct ofono_modem *modem_register(const char *device, const char *device_address, const char *alias) { struct ofono_modem *modem; char *path; path = g_strconcat("hfp", device, NULL); modem = ofono_modem_create(path, "hfp"); g_free(path); if (modem == NULL) return NULL; ofono_modem_set_string(modem, "Remote", device_address); ofono_modem_set_string(modem, "DevicePath", device); ofono_modem_set_name(modem, alias); ofono_modem_register(modem); return modem; } static int hfp_probe(struct ofono_modem *modem) { struct hfp *hfp; DBG("modem: %p", modem); hfp = g_new0(struct hfp, 1); ofono_modem_set_data(modem, hfp); return 0; } static void hfp_remove(struct ofono_modem *modem) { struct hfp *hfp = ofono_modem_get_data(modem); struct hfp_slc_info *info = &hfp->info; DBG("modem: %p", modem); if (hfp->msg) dbus_message_unref(hfp->msg); g_at_chat_unref(info->chat); g_free(hfp); ofono_modem_set_data(modem, NULL); } static void connect_cb(gboolean success, gpointer user_data) { struct ofono_modem *modem = user_data; if (success) return; ofono_modem_set_powered(modem, FALSE); } /* power up hardware */ static int hfp_enable(struct ofono_modem *modem) { const char *path; DBG("%p", modem); path = ofono_modem_get_string(modem, "DevicePath"); /* * We call Device1.ConnectProfile() with our UUID, and we hope for the * NewConnection() method to be called, if ConnectProfile() fails we * force the modem to powered off */ bt_connect_profile(ofono_dbus_get_connection(), path, HFP_AG_UUID, connect_cb, modem); return -EINPROGRESS; } static int hfp_disable(struct ofono_modem *modem) { struct hfp *hfp = ofono_modem_get_data(modem); struct hfp_slc_info *info = &hfp->info; GIOChannel *channel; int fd; DBG("%p", modem); /* * Instead of triggering two round trips to BlueZ (DisconnectProfile, * RequestDisconnection) simply kill the connection on the RFCOMM fd * we already have. But for this we have to call shutdown(). */ channel = g_at_chat_get_channel(info->chat); fd = g_io_channel_unix_get_fd(channel); shutdown(fd, SHUT_RDWR); ofono_handsfree_card_remove(hfp->card); hfp->card = NULL; g_at_chat_unref(info->chat); info->chat = NULL; return 0; } static void hfp_pre_sim(struct ofono_modem *modem) { struct hfp *hfp = ofono_modem_get_data(modem); char *address = (char *) ofono_modem_get_string(modem, "Remote"); DBG("%p", modem); ofono_devinfo_create(modem, 0, "hfpmodem", address); ofono_voicecall_create(modem, 0, "hfpmodem", &hfp->info); ofono_netreg_create(modem, 0, "hfpmodem", &hfp->info); ofono_handsfree_create(modem, 0, "hfpmodem", &hfp->info); ofono_call_volume_create(modem, 0, "hfpmodem", &hfp->info); ofono_siri_create(modem, 0, "hfpmodem", &hfp->info); } static void hfp_post_sim(struct ofono_modem *modem) { DBG("%p", modem); } static struct ofono_modem_driver hfp_driver = { .name = "hfp", .modem_type = OFONO_MODEM_TYPE_HFP, .probe = hfp_probe, .remove = hfp_remove, .enable = hfp_enable, .disable = hfp_disable, .pre_sim = hfp_pre_sim, .post_sim = hfp_post_sim, }; static void bcs_notify(GAtResult *result, gpointer user_data) { struct hfp *hfp = user_data; struct hfp_slc_info *info = &hfp->info; GAtResultIter iter; char str[32]; int value; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+BCS:")) return; if (!g_at_result_iter_next_number(&iter, &value)) return; if (ofono_handsfree_card_set_codec(hfp->card, value) == FALSE) { /* Unsupported codec, re-send our codecs */ if (ofono_handsfree_audio_has_wideband()) sprintf(str, "AT+BAC=%d,%d", HFP_CODEC_CVSD, HFP_CODEC_MSBC); else sprintf(str, "AT+BAC=%d", HFP_CODEC_CVSD); goto done; } /* Confirm the codec */ sprintf(str, "AT+BCS=%d", value); done: g_at_chat_send(info->chat, str, none_prefix, NULL, NULL, NULL); } static int hfp16_card_probe(struct ofono_handsfree_card *card, unsigned int vendor, void *data) { struct hfp *hfp = data; struct hfp_slc_info *info = &hfp->info; g_at_chat_register(info->chat, "+BCS:", bcs_notify, FALSE, hfp, NULL); return 0; } static void hfp16_card_remove(struct ofono_handsfree_card *card) { } static void bcc_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_handsfree_card_connect_cb_t cb = cbd->cb; struct ofono_handsfree_card *card = cbd->user; struct hfp *hfp = ofono_handsfree_card_get_data(card); struct ofono_error error; hfp->bcc_id = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void hfp16_card_connect(struct ofono_handsfree_card *card, ofono_handsfree_card_connect_cb_t cb, void *data) { struct hfp *hfp = ofono_handsfree_card_get_data(card); struct hfp_slc_info *info = &hfp->info; if (info->hf_features & HFP_HF_FEATURE_CODEC_NEGOTIATION && info->ag_features & HFP_AG_FEATURE_CODEC_NEGOTIATION) { struct cb_data *cbd = cb_data_new(cb, data); cbd->user = card; hfp->bcc_id = g_at_chat_send(info->chat, "AT+BCC", none_prefix, bcc_cb, cbd, g_free); return; } /* * If any side (remote or local) doesn't support codec negotiation, * fallback to direct SCO connection. Calling connect_sco() * hands the connection responsibility to the core, so no need * to call the callback */ ofono_handsfree_card_connect_sco(card); } static void hfp16_sco_connected_hint(struct ofono_handsfree_card *card) { struct hfp *hfp = ofono_handsfree_card_get_data(card); struct hfp_slc_info *info = &hfp->info; struct cb_data *cbd; ofono_handsfree_card_connect_cb_t cb; /* * SCO has just been connected, probably initiated by the AG. * If we have any outstanding BCC requests, then lets cancel these * as they're no longer needed */ if (hfp->bcc_id == 0) return; cbd = g_at_chat_get_userdata(info->chat, hfp->bcc_id); if (cbd == NULL) return; cb = cbd->cb; CALLBACK_WITH_SUCCESS(cb, cbd->data); /* cbd will be freed once cancel is processed */ g_at_chat_cancel(info->chat, hfp->bcc_id); hfp->bcc_id = 0; } static struct ofono_handsfree_card_driver hfp16_hf_driver = { .name = HFP16_HF_DRIVER, .probe = hfp16_card_probe, .remove = hfp16_card_remove, .connect = hfp16_card_connect, .sco_connected_hint = hfp16_sco_connected_hint, }; static ofono_bool_t device_path_compare(struct ofono_modem *modem, void *userdata) { const char *path = userdata; const char *value = ofono_modem_get_string(modem, "DevicePath"); if (value == NULL) return FALSE; return g_str_equal(path, value); } static int get_version(DBusMessageIter *iter, uint16_t *version) { DBusMessageIter dict, entry, valiter; const char *key; uint16_t value; /* Points to dict */ dbus_message_iter_recurse(iter, &dict); /* For each entry in this dict */ while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) { /* I want to access the entry's contents */ dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) return -EINVAL; /* If the current key isn't "Version", keep looking */ dbus_message_iter_get_basic(&entry, &key); if (!g_str_equal("Version", key)) { dbus_message_iter_next(&dict); continue; } dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) return -EINVAL; dbus_message_iter_recurse(&entry, &valiter); dbus_message_iter_get_basic(&valiter, &value); if (version) *version = value; return 0; } return -ENOENT; } static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct hfp *hfp; struct ofono_modem *modem; struct sockaddr_rc saddr; socklen_t optlen; DBusMessageIter entry; const char *device, *driver; char local[18], remote[18]; uint16_t version = HFP_VERSION_1_5; int fd, err; DBG("Profile handler NewConnection"); if (dbus_message_iter_init(msg, &entry) == FALSE) goto invalid; if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_OBJECT_PATH) goto invalid; dbus_message_iter_get_basic(&entry, &device); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_UNIX_FD) goto invalid; dbus_message_iter_get_basic(&entry, &fd); if (fd < 0) goto invalid; dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY) goto invalid; if (get_version(&entry, &version) < 0) goto invalid; DBG("version: %hd", version); modem = ofono_modem_find(device_path_compare, (void *) device); if (modem == NULL) { close(fd); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Unknown Bluetooth device"); } err = service_level_connection(modem, fd, version); if (err < 0 && err != -EINPROGRESS) { close(fd); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Not enough resources"); } memset(&saddr, 0, sizeof(saddr)); optlen = sizeof(saddr); if (getsockname(fd, (struct sockaddr *) &saddr, &optlen) < 0) { err = errno; ofono_error("RFCOMM getsockname(): %s (%d)", strerror(err), err); close(fd); goto invalid; } bt_ba2str(&saddr.rc_bdaddr, local); memset(&saddr, 0, sizeof(saddr)); optlen = sizeof(saddr); if (getpeername(fd, (struct sockaddr *) &saddr, &optlen) < 0) { err = errno; ofono_error("RFCOMM getpeername(): %s (%d)", strerror(err), err); close(fd); goto invalid; } bt_ba2str(&saddr.rc_bdaddr, remote); hfp = ofono_modem_get_data(modem); hfp->msg = dbus_message_ref(msg); driver = NULL; if (version >= HFP_VERSION_1_6) driver = HFP16_HF_DRIVER; hfp->card = ofono_handsfree_card_create(0, OFONO_HANDSFREE_CARD_TYPE_HANDSFREE, driver, hfp); ofono_handsfree_card_set_data(hfp->card, hfp); ofono_handsfree_card_set_local(hfp->card, local); ofono_handsfree_card_set_remote(hfp->card, remote); return NULL; invalid: return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Invalid arguments in method call"); } static DBusMessage *profile_release(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBG("Profile handler Release"); return NULL; } static DBusMessage *profile_disconnection(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct ofono_modem *modem; struct hfp *hfp; const char *device; struct hfp_slc_info *info; DBusMessageIter entry; DBG("Profile handler RequestDisconnection"); if (dbus_message_iter_init(msg, &entry) == FALSE) goto error; if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_OBJECT_PATH) goto error; dbus_message_iter_get_basic(&entry, &device); modem = ofono_modem_find(device_path_compare, (void *) device); if (modem == NULL) goto error; ofono_modem_set_powered(modem, FALSE); hfp = ofono_modem_get_data(modem); info = &hfp->info; ofono_handsfree_card_remove(hfp->card); hfp->card = NULL; g_at_chat_unref(info->chat); info->chat = NULL; return dbus_message_new_method_return(msg); error: return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Invalid arguments in method call"); } static const GDBusMethodTable profile_methods[] = { { GDBUS_ASYNC_METHOD("NewConnection", GDBUS_ARGS({ "device", "o"}, { "fd", "h"}, { "fd_properties", "a{sv}" }), NULL, profile_new_connection) }, { GDBUS_NOREPLY_METHOD("Release", NULL, NULL, profile_release) }, { GDBUS_METHOD("RequestDisconnection", GDBUS_ARGS({"device", "o"}), NULL, profile_disconnection) }, { } }; static void connect_handler(DBusConnection *conn, void *user_data) { uint16_t features = HFP_SDP_HF_FEATURE_ECNR | HFP_SDP_HF_FEATURE_3WAY | HFP_SDP_HF_FEATURE_CLIP | HFP_SDP_HF_FEATURE_REMOTE_VOLUME_CONTROL; /* * Assuming that if defer_setup is supported, then SCO transparent * mode is also supported */ if (ofono_handsfree_audio_has_transparent_sco()) features |= HFP_SDP_HF_FEATURE_WIDEBAND_SPEECH; DBG("Registering External Profile handler ..."); bt_register_profile(conn, HFP_HS_UUID, HFP_VERSION_1_7, "hfp_hf", HFP_EXT_PROFILE_PATH, NULL, features); } static gboolean has_hfp_ag_uuid(DBusMessageIter *array) { DBusMessageIter value; if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) return FALSE; dbus_message_iter_recurse(array, &value); while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { const char *uuid; dbus_message_iter_get_basic(&value, &uuid); if (g_str_equal(uuid, HFP_AG_UUID) == TRUE) return TRUE; dbus_message_iter_next(&value); } return FALSE; } static void modem_removed(GDBusProxy *proxy, void *user_data) { struct ofono_modem *modem = user_data; ofono_modem_remove(modem); } static void alias_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { const char *alias; struct ofono_modem *modem = user_data; if (g_str_equal("Alias", name) == FALSE) return; dbus_message_iter_get_basic(iter, &alias); ofono_modem_set_name(modem, alias); } static void modem_register_from_proxy(GDBusProxy *proxy, const char *path) { const char *alias, *remote; DBusMessageIter iter; dbus_bool_t paired; struct ofono_modem *modem; if (g_dbus_proxy_get_property(proxy, "Paired", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &paired); if (paired == FALSE) { modem = ofono_modem_find(device_path_compare, (void *) path); if (modem != NULL) { ofono_modem_remove(modem); g_dbus_proxy_set_removed_watch(proxy, NULL, NULL); g_dbus_proxy_set_property_watch(proxy, NULL, NULL); } return; } if (g_dbus_proxy_get_property(proxy, "UUIDs", &iter) == FALSE) return; if (has_hfp_ag_uuid(&iter) == FALSE) return; if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &alias); if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE) return; dbus_message_iter_get_basic(&iter, &remote); modem = modem_register(path, remote, alias); g_dbus_proxy_set_property_watch(proxy, alias_changed, modem); g_dbus_proxy_set_removed_watch(proxy, modem_removed, modem); } static void proxy_added(GDBusProxy *proxy, void *user_data) { const char *interface, *path; interface = g_dbus_proxy_get_interface(proxy); path = g_dbus_proxy_get_path(proxy); if (g_str_equal(BLUEZ_DEVICE_INTERFACE, interface) == FALSE) return; modem_register_from_proxy(proxy, path); } static void property_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { const char *interface, *path; interface = g_dbus_proxy_get_interface(proxy); path = g_dbus_proxy_get_path(proxy); if (g_str_equal(BLUEZ_DEVICE_INTERFACE, interface) == FALSE) return; if (g_str_equal("Paired", name) != TRUE) return; modem_register_from_proxy(proxy, path); } static int hfp_init(void) { DBusConnection *conn = ofono_dbus_get_connection(); int err; if (DBUS_TYPE_UNIX_FD < 0) return -EBADF; /* Registers External Profile handler */ if (!g_dbus_register_interface(conn, HFP_EXT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE, profile_methods, NULL, NULL, NULL, NULL)) { ofono_error("Register Profile interface failed: %s", HFP_EXT_PROFILE_PATH); return -EIO; } err = ofono_handsfree_card_driver_register(&hfp16_hf_driver); if (err < 0) { g_dbus_unregister_interface(conn, HFP_EXT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE); return err; } err = ofono_modem_driver_register(&hfp_driver); if (err < 0) { g_dbus_unregister_interface(conn, HFP_EXT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE); return err; } bluez = g_dbus_client_new(conn, BLUEZ_SERVICE, BLUEZ_MANAGER_PATH); if (bluez == NULL) { g_dbus_unregister_interface(conn, HFP_EXT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE); ofono_modem_driver_unregister(&hfp_driver); return -ENOMEM; } g_dbus_client_set_connect_watch(bluez, connect_handler, NULL); g_dbus_client_set_proxy_handlers(bluez, proxy_added, NULL, property_changed, NULL); ofono_handsfree_audio_ref(); return 0; } static void hfp_exit(void) { DBusConnection *conn = ofono_dbus_get_connection(); bt_unregister_profile(conn, HFP_EXT_PROFILE_PATH); g_dbus_unregister_interface(conn, HFP_EXT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE); ofono_handsfree_card_driver_unregister(&hfp16_hf_driver); ofono_modem_driver_unregister(&hfp_driver); g_dbus_client_unref(bluez); ofono_handsfree_audio_unref(); } OFONO_PLUGIN_DEFINE(hfp_bluez5, "External Hands-Free Profile Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, hfp_init, hfp_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/he910.c0000644000015600001650000002271412671500024021060 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2014 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 #endif #include #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; static const char *qss_prefix[] = { "#QSS:", NULL }; struct he910_data { GAtChat *chat; /* AT chat */ GAtChat *modem; /* Data port */ struct ofono_sim *sim; ofono_bool_t have_sim; ofono_bool_t sms_phonebook_added; }; static void he910_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GAtSyntax *syntax; GIOChannel *channel; GAtChat *chat; GHashTable *options; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) return NULL; g_hash_table_insert(options, "Baud", "115200"); channel = g_at_tty_open(device, options); g_hash_table_destroy(options); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, he910_debug, debug); return chat; } static void switch_sim_state_status(struct ofono_modem *modem, int status) { struct he910_data *data = ofono_modem_get_data(modem); DBG("%p, SIM status: %d", modem, status); switch (status) { case 0: /* SIM not inserted */ if (data->have_sim == TRUE) { ofono_sim_inserted_notify(data->sim, FALSE); data->have_sim = FALSE; data->sms_phonebook_added = FALSE; } break; case 1: /* SIM inserted */ case 2: /* SIM inserted and PIN unlocked */ if (data->have_sim == FALSE) { ofono_sim_inserted_notify(data->sim, TRUE); data->have_sim = TRUE; } break; case 3: /* SIM inserted, SMS and phonebook ready */ if (data->sms_phonebook_added == FALSE) { ofono_phonebook_create(modem, 0, "atmodem", data->chat); ofono_sms_create(modem, 0, "atmodem", data->chat); data->sms_phonebook_added = TRUE; } break; default: ofono_warn("Unknown SIM state %d received", status); break; } } static void he910_qss_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; int status; GAtResultIter iter; DBG("%p", modem); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "#QSS:")) return; g_at_result_iter_next_number(&iter, &status); switch_sim_state_status(modem, status); } static void qss_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; int status, mode; GAtResultIter iter; DBG("%p", modem); if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "#QSS:")) return; if (!g_at_result_iter_next_number(&iter, &mode)) return; if (!g_at_result_iter_next_number(&iter, &status)) return; switch_sim_state_status(modem, status); } static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct he910_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (!ok) { g_at_chat_unref(data->chat); data->chat = NULL; g_at_chat_unref(data->modem); data->modem = NULL; ofono_modem_set_powered(modem, FALSE); return; } /* * Switch data carrier detect signal off. * When the DCD is disabled the modem does not hangup anymore * after the data connection. */ g_at_chat_send(data->chat, "AT&C0", NULL, NULL, NULL, NULL); data->have_sim = FALSE; data->sms_phonebook_added = FALSE; ofono_modem_set_powered(modem, TRUE); /* * Tell the modem not to automatically initiate auto-attach * proceedures on its own. */ g_at_chat_send(data->chat, "AT#AUTOATT=0", none_prefix, NULL, NULL, NULL); /* Follow sim state */ g_at_chat_register(data->chat, "#QSS:", he910_qss_notify, FALSE, modem, NULL); /* Enable sim state notification */ g_at_chat_send(data->chat, "AT#QSS=2", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT#QSS?", qss_prefix, qss_query_cb, modem, NULL); } static int he910_enable(struct ofono_modem *modem) { struct he910_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->chat = open_device(modem, "Aux", "Aux: "); if (data->chat == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_set_slave(data->modem, data->chat); /* * Disable command echo and * enable the Extended Error Result Codes */ g_at_chat_send(data->chat, "ATE0 +CMEE=1", none_prefix, NULL, NULL, NULL); /* Set phone functionality */ g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix, cfun_enable_cb, modem, NULL); return -EINPROGRESS; } static void cfun_disable_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct he910_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_unref(data->chat); data->chat = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int he910_disable(struct ofono_modem *modem) { struct he910_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->chat); g_at_chat_unregister_all(data->chat); /* Power down modem */ g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix, cfun_disable_cb, modem, NULL); return -EINPROGRESS; } static void he910_pre_sim(struct ofono_modem *modem) { struct he910_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->chat); data->sim = ofono_sim_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat); ofono_location_reporting_create(modem, 0, "telitmodem", data->chat); } static void he910_post_online(struct ofono_modem *modem) { struct he910_data *data = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_voicecall_create(modem, 0, "atmodem", data->chat); ofono_netreg_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat); ofono_ussd_create(modem, 0, "atmodem", data->chat); ofono_call_forwarding_create(modem, 0, "atmodem", data->chat); ofono_call_settings_create(modem, 0, "atmodem", data->chat); ofono_call_meter_create(modem, 0, "atmodem", data->chat); ofono_call_barring_create(modem, 0, "atmodem", data->chat); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); gprs = ofono_gprs_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat); gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static int he910_probe(struct ofono_modem *modem) { struct he910_data *data; DBG("%p", modem); data = g_try_new0(struct he910_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void he910_remove(struct ofono_modem *modem) { struct he910_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup after hot-unplug */ g_at_chat_unref(data->chat); g_at_chat_unref(data->modem); g_free(data); } static struct ofono_modem_driver he910_driver = { .name = "he910", .probe = he910_probe, .remove = he910_remove, .enable = he910_enable, .disable = he910_disable, .pre_sim = he910_pre_sim, .post_online = he910_post_online, }; static int he910_init(void) { DBG(""); return ofono_modem_driver_register(&he910_driver); } static void he910_exit(void) { ofono_modem_driver_unregister(&he910_driver); } OFONO_PLUGIN_DEFINE(he910, "Telit HE910 driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, he910_init, he910_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/bluez5.c0000644000015600001650000001433712671500024021442 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "bluez5.h" #define BLUEZ_PROFILE_MGMT_INTERFACE BLUEZ_SERVICE ".ProfileManager1" struct finish_callback { bt_finish_cb cb; gpointer user_data; char *member; }; static void profile_register_cb(DBusPendingCall *call, gpointer user_data) { DBusMessage *reply; DBusError derr; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { ofono_error("RegisterProfile() replied an error: %s, %s", derr.name, derr.message); dbus_error_free(&derr); goto done; } DBG(""); done: dbus_message_unref(reply); } static void unregister_profile_cb(DBusPendingCall *call, gpointer user_data) { DBusMessage *reply; DBusError derr; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { ofono_error("UnregisterProfile() replied an error: %s, %s", derr.name, derr.message); dbus_error_free(&derr); goto done; } DBG(""); done: dbus_message_unref(reply); } int bt_register_profile(DBusConnection *conn, const char *uuid, uint16_t version, const char *name, const char *object, const char *role, uint16_t features) { DBusMessageIter iter, dict; DBusPendingCall *c; DBusMessage *msg; DBG("Bluetooth: Registering %s (%s) profile", uuid, name); msg = dbus_message_new_method_call(BLUEZ_SERVICE, "/org/bluez", BLUEZ_PROFILE_MGMT_INTERFACE, "RegisterProfile"); dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &object); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &uuid); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); ofono_dbus_dict_append(&dict, "Name", DBUS_TYPE_STRING, &name); ofono_dbus_dict_append(&dict, "Version", DBUS_TYPE_UINT16, &version); if (role) ofono_dbus_dict_append(&dict, "Role", DBUS_TYPE_STRING, &role); if (features) ofono_dbus_dict_append(&dict, "Features", DBUS_TYPE_UINT16, &features); dbus_message_iter_close_container(&iter, &dict); if (!dbus_connection_send_with_reply(conn, msg, &c, -1)) { ofono_error("Sending RegisterProfile failed"); dbus_message_unref(msg); return -EIO; } dbus_pending_call_set_notify(c, profile_register_cb, NULL, NULL); dbus_pending_call_unref(c); dbus_message_unref(msg); return 0; } void bt_unregister_profile(DBusConnection *conn, const char *object) { DBusMessageIter iter; DBusPendingCall *c; DBusMessage *msg; DBG("Bluetooth: Unregistering profile %s", object); msg = dbus_message_new_method_call(BLUEZ_SERVICE, "/org/bluez", BLUEZ_PROFILE_MGMT_INTERFACE, "UnregisterProfile"); dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &object); if (!dbus_connection_send_with_reply(conn, msg, &c, -1)) { ofono_error("Sending UnregisterProfile failed"); dbus_message_unref(msg); return; } dbus_pending_call_set_notify(c, unregister_profile_cb, NULL, NULL); dbus_pending_call_unref(c); dbus_message_unref(msg); } static void finish_profile_cb(DBusPendingCall *call, gpointer user_data) { struct finish_callback *callback = user_data; DBusMessage *reply; DBusError derr; gboolean success; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); success = TRUE; if (dbus_set_error_from_message(&derr, reply)) { success = FALSE; ofono_error("%s() replied an error: %s, %s", callback->member, derr.name, derr.message); dbus_error_free(&derr); } if (callback->cb) callback->cb(success, callback->user_data); dbus_message_unref(reply); } static void finish_callback_free(void *data) { struct finish_callback *callback = data; g_free(callback->member); g_free(callback); } static void device_send_message(DBusConnection *conn, const char *device, const char *member, const char *uuid, bt_finish_cb cb, gpointer user_data) { struct finish_callback *callback; DBusMessageIter iter; DBusPendingCall *c; DBusMessage *msg; DBG("Bluetooth: sending %s for %s on %s", member, uuid, device); msg = dbus_message_new_method_call(BLUEZ_SERVICE, device, BLUEZ_DEVICE_INTERFACE, member); dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &uuid); if (!dbus_connection_send_with_reply(conn, msg, &c, -1)) { ofono_error("Sending %s failed", member); dbus_message_unref(msg); return; } callback = g_new0(struct finish_callback, 1); callback->cb = cb; callback->user_data = user_data; callback->member = g_strdup(dbus_message_get_member(msg)); dbus_pending_call_set_notify(c, finish_profile_cb, callback, finish_callback_free); dbus_pending_call_unref(c); dbus_message_unref(msg); } void bt_connect_profile(DBusConnection *conn, const char *device, const char *uuid, bt_finish_cb cb, gpointer user_data) { device_send_message(conn, device, "ConnectProfile", uuid, cb, user_data); } void bt_disconnect_profile(DBusConnection *conn, const char *device, const char *uuid, bt_finish_cb cb, gpointer user_data) { device_send_message(conn, device, "DisconnectProfile", uuid, cb, user_data); } OFONO_PLUGIN_DEFINE(bluez5, "BlueZ 5 Utils Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, NULL, NULL) ofono-1.17.bzr6912+16.04.20160314.3/plugins/connman.c0000644000015600001650000001561012671500024021660 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #ifndef DBUS_TYPE_UNIX_FD #define DBUS_TYPE_UNIX_FD -1 #endif #define CONNMAN_SERVICE "net.connman" #define CONNMAN_PATH "/net/connman" #define CONNMAN_MANAGER_INTERFACE CONNMAN_SERVICE ".Manager" #define CONNMAN_MANAGER_PATH "/" static DBusConnection *connection; static GHashTable *requests; static unsigned int id; struct connman_req { int uid; DBusPendingCall *pending; ofono_private_network_cb_t cb; void *data; gboolean redundant; char *path; }; static void send_release(const char *path) { DBusMessage *message; message = dbus_message_new_method_call(CONNMAN_SERVICE, CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE, "ReleasePrivateNetwork"); if (message == NULL) return; dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); dbus_message_set_no_reply(message, TRUE); dbus_connection_send(connection, message, NULL); dbus_message_unref(message); } static void connman_release(int uid) { struct connman_req *req; DBG(""); req = g_hash_table_lookup(requests, &uid); if (req == NULL) return; if (req->pending) { /* * We want to cancel the request but we have to wait * the response of ConnMan. So we mark request as * redundant until we get the response, then we remove * it from hash table. */ req->redundant = TRUE; return; } send_release(req->path); g_hash_table_remove(requests, &req->uid); } static gboolean parse_reply(DBusMessage *reply, const char **path, struct ofono_private_network_settings *pns) { DBusMessageIter array, dict, entry; if (!reply) return FALSE; if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) return FALSE; if (dbus_message_iter_init(reply, &array) == FALSE) return FALSE; if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_OBJECT_PATH) return FALSE; dbus_message_iter_get_basic(&array, path); dbus_message_iter_next(&array); if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) return FALSE; dbus_message_iter_recurse(&array, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter iter; const char *key; int type; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &iter); type = dbus_message_iter_get_arg_type(&iter); if (type != DBUS_TYPE_STRING) break; if (g_str_equal(key, "ServerIPv4") && type == DBUS_TYPE_STRING) dbus_message_iter_get_basic(&iter, &pns->server_ip); else if (g_str_equal(key, "PeerIPv4") && type == DBUS_TYPE_STRING) dbus_message_iter_get_basic(&iter, &pns->peer_ip); else if (g_str_equal(key, "PrimaryDNS") && type == DBUS_TYPE_STRING) dbus_message_iter_get_basic(&iter, &pns->primary_dns); else if (g_str_equal(key, "SecondaryDNS") && type == DBUS_TYPE_STRING) dbus_message_iter_get_basic(&iter, &pns->secondary_dns); dbus_message_iter_next(&dict); } dbus_message_iter_next(&array); if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_UNIX_FD) return FALSE; dbus_message_iter_get_basic(&array, &pns->fd); return TRUE; } static void request_reply(DBusPendingCall *call, void *user_data) { struct connman_req *req = user_data; DBusMessage *reply; const char *path = NULL; struct ofono_private_network_settings pns; DBG(""); req->pending = NULL; memset(&pns, 0, sizeof(pns)); pns.fd = -1; reply = dbus_pending_call_steal_reply(call); if (reply == NULL) goto badreply; if (parse_reply(reply, &path, &pns) == FALSE) goto error; DBG("fd: %d, path: %s", pns.fd, path); if (req->redundant == TRUE) goto redundant; if (pns.server_ip == NULL || pns.peer_ip == NULL || pns.primary_dns == NULL || pns.secondary_dns == NULL || pns.fd < 0) { ofono_error("Error while reading dictionary...\n"); goto error; } req->path = g_strdup(path); req->cb(&pns, req->data); dbus_message_unref(reply); dbus_pending_call_unref(call); return; error: redundant: if (pns.fd != -1) close(pns.fd); if (path != NULL) send_release(path); dbus_message_unref(reply); badreply: if (req->redundant == FALSE) req->cb(NULL, req->data); g_hash_table_remove(requests, &req->uid); dbus_pending_call_unref(call); } static int connman_request(ofono_private_network_cb_t cb, void *data) { DBusMessage *message; DBusPendingCall *call; struct connman_req *req; DBG(""); if (DBUS_TYPE_UNIX_FD < 0) return -EBADF; req = g_try_new(struct connman_req, 1); if (req == NULL) return -ENOMEM; message = dbus_message_new_method_call(CONNMAN_SERVICE, CONNMAN_MANAGER_PATH, CONNMAN_MANAGER_INTERFACE, "RequestPrivateNetwork"); if (message == NULL) { g_free(req); return -ENOMEM; } if (dbus_connection_send_with_reply(connection, message, &call, 5000) == FALSE) { g_free(req); dbus_message_unref(message); return -EIO; } id++; req->pending = call; req->cb = cb; req->data = data; req->uid = id; req->redundant = FALSE; req->path = NULL; dbus_pending_call_set_notify(call, request_reply, req, NULL); g_hash_table_insert(requests, &req->uid, req); dbus_message_unref(message); return req->uid; } static struct ofono_private_network_driver pn_driver = { .name = "ConnMan Private Network", .request = connman_request, .release = connman_release, }; static void request_free(gpointer user_data) { struct connman_req *req = user_data; g_free(req->path); g_free(req); } static int connman_init(void) { DBG(""); connection = ofono_dbus_get_connection(); requests = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, request_free); return ofono_private_network_driver_register(&pn_driver); } static void connman_exit(void) { g_hash_table_destroy(requests); ofono_private_network_driver_unregister(&pn_driver); } OFONO_PLUGIN_DEFINE(connman, "ConnMan plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, connman_init, connman_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/nokia-gpio.h0000644000015600001650000000242012671500024022264 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ enum power_state { POWER_STATE_NONE, POWER_STATE_ON_STARTED, POWER_STATE_ON, POWER_STATE_ON_RESET, POWER_STATE_ON_FAILED, POWER_STATE_OFF_STARTED, POWER_STATE_OFF_WAITING, POWER_STATE_OFF, }; typedef void (*gpio_finished_cb_t)(enum power_state value, void *opaque); int gpio_probe(GIsiModem *idx, unsigned addr, gpio_finished_cb_t cb, void *data); int gpio_enable(void *opaque); int gpio_disable(void *opaque); int gpio_remove(void *opaque); char const *gpio_power_state_name(enum power_state value); ofono-1.17.bzr6912+16.04.20160314.3/plugins/mbpi.h0000644000015600001650000000213712671500024021163 0ustar pbuserpbgroup00000000000000/* * * 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 * */ const char *mbpi_ap_type(enum ofono_gprs_context_type type); void mbpi_ap_free(struct ofono_gprs_provision_data *data); GSList *mbpi_lookup_apn(const char *mcc, const char *mnc, enum ofono_gprs_context_type type, gboolean allow_duplicates, GError **error); char *mbpi_lookup_cdma_provider_name(const char *sid, GError **error); ofono-1.17.bzr6912+16.04.20160314.3/plugins/ifx.c0000644000015600001650000004243012671500024021015 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUM_DLC 6 #define VOICE_DLC 0 #define NETREG_DLC 1 #define GPRS1_DLC 2 #define GPRS2_DLC 3 #define GPRS3_DLC 4 #define AUX_DLC 5 static char *dlc_prefixes[NUM_DLC] = { "Voice: ", "Net: ", "GPRS1: ", "GPRS2: ", "GPRS3: ", "Aux: " }; static const char *dlc_nodes[NUM_DLC] = { "/dev/ttyGSM1", "/dev/ttyGSM2", "/dev/ttyGSM3", "/dev/ttyGSM4", "/dev/ttyGSM5", "/dev/ttyGSM6" }; static const char *none_prefix[] = { NULL }; static const char *xgendata_prefix[] = { "+XGENDATA:", NULL }; static const char *xsimstate_prefix[] = { "+XSIMSTATE:", NULL }; struct ifx_data { GIOChannel *device; GAtMux *mux; GAtChat *dlcs[NUM_DLC]; guint dlc_poll_count; guint dlc_poll_source; guint dlc_init_source; guint mux_init_timeout; guint frame_size; int mux_ldisc; int saved_ldisc; struct ofono_sim *sim; }; static void ifx_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static int ifx_probe(struct ofono_modem *modem) { struct ifx_data *data; DBG("%p", modem); data = g_try_new0(struct ifx_data, 1); if (data == NULL) return -ENOMEM; data->mux_ldisc = -1; data->saved_ldisc = -1; ofono_modem_set_data(modem, data); return 0; } static void ifx_remove(struct ofono_modem *modem) { struct ifx_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); g_free(data); } static void ifx_set_sim_state(struct ifx_data *data, int state) { DBG("state %d", state); switch (state) { case 0: /* SIM not present */ case 6: /* SIM Error */ case 8: /* SIM Technical Problem */ case 9: /* SIM Removed */ ofono_sim_inserted_notify(data->sim, FALSE); break; case 1: /* PIN verification needed */ case 4: /* PUK verification needed */ case 5: /* SIM permanently blocked */ case 7: /* ready for attach (+COPS) */ ofono_sim_inserted_notify(data->sim, TRUE); break; case 2: /* PIN verification not needed – Ready */ case 3: /* PIN verified – Ready */ /* * State 3 is handled in the SIM atom driver * while for state 2 we should be waiting for state 7 */ break; case 10: /* SIM Reactivating */ case 11: /* SIM Reactivated */ case 12: /* SIM SMS Caching Completed */ case 99: /* SIM State Unknown */ ofono_warn("Unhandled SIM state %d received", state); break; default: ofono_warn("Unknown SIM state %d received", state); break; } } static void xsim_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct ifx_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int state; if (data->sim == NULL) return; 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; ifx_set_sim_state(data, state); } static void xsimstate_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct ifx_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int mode; int state; DBG(""); if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+XSIMSTATE:")) return; if (!g_at_result_iter_next_number(&iter, &mode)) return; if (!g_at_result_iter_next_number(&iter, &state)) return; ifx_set_sim_state(data, state); } static void shutdown_device(struct ifx_data *data) { int i, fd; DBG(""); if (data->dlc_init_source > 0) { g_source_remove(data->dlc_init_source); data->dlc_init_source = 0; } for (i = 0; i < NUM_DLC; i++) { if (data->dlcs[i] == NULL) continue; g_at_chat_unref(data->dlcs[i]); data->dlcs[i] = NULL; } if (data->mux) { g_at_mux_shutdown(data->mux); g_at_mux_unref(data->mux); data->mux = NULL; goto done; } fd = g_io_channel_unix_get_fd(data->device); if (ioctl(fd, TIOCSETD, &data->saved_ldisc) < 0) ofono_warn("Failed to restore line discipline"); done: g_io_channel_unref(data->device); data->device = NULL; } static void dlc_disconnect(gpointer user_data) { struct ofono_modem *modem = user_data; struct ifx_data *data = ofono_modem_get_data(modem); DBG(""); ofono_warn("Disconnect of modem channel"); shutdown_device(data); } static GAtChat *create_chat(GIOChannel *channel, struct ofono_modem *modem, char *debug) { GAtSyntax *syntax; GAtChat *chat; if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsmv1(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, ifx_debug, debug); g_at_chat_set_disconnect_function(chat, dlc_disconnect, modem); return chat; } static void xgendata_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct ifx_data *data = ofono_modem_get_data(modem); GAtResultIter iter; const char *gendata; DBG(""); if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+XGENDATA:")) goto error; if (!g_at_result_iter_next_string(&iter, &gendata)) goto error; DBG("\n%s", gendata); /* switch to GSM character set instead of IRA */ g_at_chat_send(data->dlcs[AUX_DLC], "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); /* disable UART for power saving */ g_at_chat_send(data->dlcs[AUX_DLC], "AT+XPOW=0,0,0", none_prefix, NULL, NULL, NULL); /* notify that the modem is ready so that pre_sim gets called */ ofono_modem_set_powered(modem, TRUE); g_at_chat_register(data->dlcs[AUX_DLC], "+XSIM:", xsim_notify, FALSE, modem, NULL); /* enable XSIM and XLOCK notifications */ g_at_chat_send(data->dlcs[AUX_DLC], "AT+XSIMSTATE=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->dlcs[AUX_DLC], "AT+XSIMSTATE?", xsimstate_prefix, xsimstate_query, modem, NULL); return; error: shutdown_device(data); ofono_modem_set_powered(modem, FALSE); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct ifx_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { shutdown_device(data); ofono_modem_set_powered(modem, FALSE); return; } g_at_chat_send(data->dlcs[AUX_DLC], "AT+XGENDATA", xgendata_prefix, xgendata_query, modem, NULL); } static gboolean dlc_setup(gpointer user_data) { struct ofono_modem *modem = user_data; struct ifx_data *data = ofono_modem_get_data(modem); int i; DBG(""); for (i = 0; i < NUM_DLC; i++) g_at_chat_send(data->dlcs[i], "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_set_slave(data->dlcs[GPRS1_DLC], data->dlcs[NETREG_DLC]); g_at_chat_set_slave(data->dlcs[GPRS2_DLC], data->dlcs[NETREG_DLC]); g_at_chat_set_slave(data->dlcs[GPRS3_DLC], data->dlcs[NETREG_DLC]); g_at_chat_send(data->dlcs[AUX_DLC], "AT+CFUN=4", NULL, cfun_enable, modem, NULL); data->dlc_init_source = 0; return FALSE; } static gboolean dlc_ready_check(gpointer user_data) { struct ofono_modem *modem = user_data; struct ifx_data *data = ofono_modem_get_data(modem); struct stat st; int i; DBG(""); data->dlc_poll_count++; if (stat(dlc_nodes[AUX_DLC], &st) < 0) { /* only possible error is ENOENT */ if (data->dlc_poll_count > 6) goto error; return TRUE; } for (i = 0; i < NUM_DLC; i++) { GIOChannel *channel = g_at_tty_open(dlc_nodes[i], NULL); data->dlcs[i] = create_chat(channel, modem, dlc_prefixes[i]); if (data->dlcs[i] == NULL) { ofono_error("Failed to open %s", dlc_nodes[i]); goto error; } } data->dlc_poll_source = 0; /* iterate through mainloop */ data->dlc_init_source = g_timeout_add_seconds(0, dlc_setup, modem); return FALSE; error: data->dlc_poll_source = 0; shutdown_device(data); ofono_modem_set_powered(modem, FALSE); return FALSE; } static void setup_internal_mux(struct ofono_modem *modem) { struct ifx_data *data = ofono_modem_get_data(modem); int i; DBG(""); data->mux = g_at_mux_new_gsm0710_basic(data->device, data->frame_size); if (data->mux == NULL) goto error; if (getenv("OFONO_MUX_DEBUG")) g_at_mux_set_debug(data->mux, ifx_debug, "MUX: "); g_at_mux_start(data->mux); for (i = 0; i < NUM_DLC; i++) { GIOChannel *channel = g_at_mux_create_channel(data->mux); data->dlcs[i] = create_chat(channel, modem, dlc_prefixes[i]); if (data->dlcs[i] == NULL) { ofono_error("Failed to create channel"); goto error; } } /* wait for DLC creation to settle */ data->dlc_init_source = g_timeout_add(500, dlc_setup, modem); return; error: shutdown_device(data); ofono_modem_set_powered(modem, FALSE); } static void mux_setup_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct ifx_data *data = ofono_modem_get_data(modem); int fd; DBG(""); if (data->mux_init_timeout > 0) { g_source_remove(data->mux_init_timeout); data->mux_init_timeout = 0; } g_at_chat_unref(data->dlcs[AUX_DLC]); data->dlcs[AUX_DLC] = NULL; if (!ok) goto error; if (data->mux_ldisc < 0) { ofono_info("Using internal multiplexer"); setup_internal_mux(modem); return; } fd = g_io_channel_unix_get_fd(data->device); if (ioctl(fd, TIOCGETD, &data->saved_ldisc) < 0) { ofono_error("Failed to get current line discipline"); goto error; } if (ioctl(fd, TIOCSETD, &data->mux_ldisc) < 0) { ofono_error("Failed to set multiplexer line discipline"); goto error; } data->dlc_poll_count = 0; data->dlc_poll_source = g_timeout_add_seconds(1, dlc_ready_check, modem); return; error: data->saved_ldisc = -1; g_io_channel_unref(data->device); data->device = NULL; ofono_modem_set_powered(modem, FALSE); } static gboolean mux_timeout_cb(gpointer user_data) { struct ofono_modem *modem = user_data; struct ifx_data *data = ofono_modem_get_data(modem); ofono_error("Timeout with multiplexer setup"); data->mux_init_timeout = 0; g_at_chat_unref(data->dlcs[AUX_DLC]); data->dlcs[AUX_DLC] = NULL; g_io_channel_unref(data->device); data->device = NULL; ofono_modem_set_powered(modem, FALSE); return FALSE; } static int ifx_enable(struct ofono_modem *modem) { struct ifx_data *data = ofono_modem_get_data(modem); const char *device, *ldisc; GAtSyntax *syntax; GAtChat *chat; DBG("%p", modem); device = ofono_modem_get_string(modem, "Device"); if (device == NULL) return -EINVAL; DBG("%s", device); ldisc = ofono_modem_get_string(modem, "LineDiscipline"); if (ldisc != NULL) { data->mux_ldisc = atoi(ldisc); ofono_info("Using multiplexer line discipline %d", data->mux_ldisc); } data->device = g_at_tty_open(device, NULL); if (data->device == NULL) return -EIO; syntax = g_at_syntax_new_gsmv1(); chat = g_at_chat_new(data->device, syntax); g_at_syntax_unref(syntax); if (chat == NULL) { g_io_channel_unref(data->device); return -EIO; } if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, ifx_debug, "Master: "); g_at_chat_send(chat, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); /* Enable multiplexer */ data->frame_size = 1509; g_at_chat_send(chat, "AT+CMUX=0,0,,1509,10,3,30,,", NULL, mux_setup_cb, modem, NULL); data->mux_init_timeout = g_timeout_add_seconds(5, mux_timeout_cb, modem); data->dlcs[AUX_DLC] = chat; return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct ifx_data *data = ofono_modem_get_data(modem); DBG(""); if (data->dlc_poll_source > 0) { g_source_remove(data->dlc_poll_source); data->dlc_poll_source = 0; } shutdown_device(data); if (ok) ofono_modem_set_powered(modem, FALSE); } static int ifx_disable(struct ofono_modem *modem) { struct ifx_data *data = ofono_modem_get_data(modem); int i; DBG("%p", modem); for (i = 0; i < NUM_DLC; i++) { g_at_chat_cancel_all(data->dlcs[i]); g_at_chat_unregister_all(data->dlcs[i]); } g_at_chat_send(data->dlcs[AUX_DLC], "AT+CFUN=0", NULL, cfun_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void ifx_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct ifx_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4"; DBG("%p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(data->dlcs[AUX_DLC], command, none_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void ifx_pre_sim(struct ofono_modem *modem) { struct ifx_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); data->sim = ofono_sim_create(modem, OFONO_VENDOR_IFX, "atmodem", data->dlcs[AUX_DLC]); ofono_voicecall_create(modem, 0, "ifxmodem", data->dlcs[VOICE_DLC]); ofono_audio_settings_create(modem, 0, "ifxmodem", data->dlcs[VOICE_DLC]); ofono_ctm_create(modem, 0, "ifxmodem", data->dlcs[AUX_DLC]); } static void ifx_post_sim(struct ofono_modem *modem) { struct ifx_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_stk_create(modem, 0, "ifxmodem", data->dlcs[AUX_DLC]); ofono_phonebook_create(modem, OFONO_VENDOR_IFX, "atmodem", data->dlcs[AUX_DLC]); ofono_call_forwarding_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_radio_settings_create(modem, 0, "ifxmodem", data->dlcs[AUX_DLC]); ofono_sms_create(modem, OFONO_VENDOR_IFX, "atmodem", data->dlcs[AUX_DLC]); gprs = ofono_gprs_create(modem, OFONO_VENDOR_IFX, "atmodem", data->dlcs[NETREG_DLC]); if (gprs == NULL) return; if (data->mux_ldisc < 0) { gc = ofono_gprs_context_create(modem, 0, "ifxmodem", data->dlcs[GPRS1_DLC]); if (gc) ofono_gprs_add_context(gprs, gc); gc = ofono_gprs_context_create(modem, 0, "ifxmodem", data->dlcs[GPRS2_DLC]); if (gc) ofono_gprs_add_context(gprs, gc); gc = ofono_gprs_context_create(modem, 0, "ifxmodem", data->dlcs[GPRS3_DLC]); if (gc) ofono_gprs_add_context(gprs, gc); } } static void ifx_post_online(struct ofono_modem *modem) { struct ifx_data *data = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; DBG("%p", modem); ofono_netreg_create(modem, OFONO_VENDOR_IFX, "atmodem", data->dlcs[NETREG_DLC]); ofono_cbs_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_ussd_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_gnss_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_call_settings_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_call_meter_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_call_barring_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_call_volume_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } static struct ofono_modem_driver ifx_driver = { .name = "ifx", .probe = ifx_probe, .remove = ifx_remove, .enable = ifx_enable, .disable = ifx_disable, .set_online = ifx_set_online, .pre_sim = ifx_pre_sim, .post_sim = ifx_post_sim, .post_online = ifx_post_online, }; static int ifx_init(void) { return ofono_modem_driver_register(&ifx_driver); } static void ifx_exit(void) { ofono_modem_driver_unregister(&ifx_driver); } OFONO_PLUGIN_DEFINE(ifx, "Infineon modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, ifx_init, ifx_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/zte.c0000644000015600001650000002305012671500024021026 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; struct zte_data { GAtChat *modem; GAtChat *aux; gboolean have_sim; struct at_util_sim_state_query *sim_state_query; }; static int zte_probe(struct ofono_modem *modem) { struct zte_data *data; DBG("%p", modem); data = g_try_new0(struct zte_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void zte_remove(struct ofono_modem *modem) { struct zte_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup potential SIM state polling */ at_util_sim_state_query_free(data->sim_state_query); /* Cleanup after hot-unplug */ g_at_chat_unref(data->aux); g_free(data); } static void zte_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GIOChannel *channel; GAtSyntax *syntax; GAtChat *chat; GHashTable *options; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) return NULL; g_hash_table_insert(options, "Baud", "115200"); g_hash_table_insert(options, "Parity", "none"); g_hash_table_insert(options, "StopBits", "1"); g_hash_table_insert(options, "DataBits", "8"); g_hash_table_insert(options, "XonXoff", "off"); g_hash_table_insert(options, "RtsCts", "on"); g_hash_table_insert(options, "Local", "on"); g_hash_table_insert(options, "Read", "on"); channel = g_at_tty_open(device, options); g_hash_table_destroy(options); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, zte_debug, debug); return chat; } static void zoprt_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct zte_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_unref(data->aux); data->aux = NULL; ofono_modem_set_powered(modem, FALSE); return; } /* AT&C0 needs to be send separate and on both channel */ g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT&C0", NULL, NULL, NULL, NULL); /* * Ensure that the modem is using GSM character set and not IRA, * otherwise weirdness with umlauts and other non-ASCII characters * can result */ g_at_chat_send(data->modem, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); /* Read PCB information */ g_at_chat_send(data->aux, "AT+ZPCB?", none_prefix, NULL, NULL, NULL); ofono_modem_set_powered(modem, TRUE); } static void sim_state_cb(gboolean present, gpointer user_data) { struct ofono_modem *modem = user_data; struct zte_data *data = ofono_modem_get_data(modem); at_util_sim_state_query_free(data->sim_state_query); data->sim_state_query = NULL; data->have_sim = present; /* Switch device into offline mode now */ g_at_chat_send(data->aux, "AT+ZOPRT=6", none_prefix, zoprt_enable, modem, NULL); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct zte_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_unref(data->aux); data->aux = NULL; ofono_modem_set_powered(modem, FALSE); return; } data->sim_state_query = at_util_sim_state_query_new(data->aux, 2, 20, sim_state_cb, modem, NULL); } static int zte_enable(struct ofono_modem *modem) { struct zte_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->aux = open_device(modem, "Aux", "Aux: "); if (data->aux == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_set_slave(data->modem, data->aux); g_at_chat_blacklist_terminator(data->aux, G_AT_CHAT_TERMINATOR_NO_CARRIER); g_at_chat_send(data->modem, "ATZ E0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); /* Switch device on first */ g_at_chat_send(data->aux, "AT+CFUN=1", NULL, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct zte_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->aux); data->aux = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static void zoprt_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct zte_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_send(data->aux, "AT+CFUN=0", NULL, cfun_disable, modem, NULL); } static int zte_disable(struct ofono_modem *modem) { struct zte_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->aux); g_at_chat_unregister_all(data->aux); /* Switch to offline mode first */ g_at_chat_send(data->aux, "AT+ZOPRT=6", none_prefix, zoprt_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void zte_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct zte_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+ZOPRT=5" : "AT+ZOPRT=6"; DBG("modem %p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(data->aux, command, none_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void zte_pre_sim(struct ofono_modem *modem) { struct zte_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->aux); sim = ofono_sim_create(modem, OFONO_VENDOR_ZTE, "atmodem", data->aux); if (sim && data->have_sim == TRUE) ofono_sim_inserted_notify(sim, TRUE); } static void zte_post_sim(struct ofono_modem *modem) { struct zte_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_phonebook_create(modem, 0, "atmodem", data->aux); ofono_radio_settings_create(modem, 0, "ztemodem", data->aux); ofono_sms_create(modem, OFONO_VENDOR_ZTE, "atmodem", data->aux); gprs = ofono_gprs_create(modem, OFONO_VENDOR_ZTE, "atmodem", data->aux); gc = ofono_gprs_context_create(modem, OFONO_VENDOR_ZTE, "atmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static void zte_post_online(struct ofono_modem *modem) { struct zte_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_netreg_create(modem, OFONO_VENDOR_ZTE, "atmodem", data->aux); ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->aux); ofono_ussd_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->aux); } static struct ofono_modem_driver zte_driver = { .name = "zte", .probe = zte_probe, .remove = zte_remove, .enable = zte_enable, .disable = zte_disable, .set_online = zte_set_online, .pre_sim = zte_pre_sim, .post_sim = zte_post_sim, .post_online = zte_post_online, }; static int zte_init(void) { return ofono_modem_driver_register(&zte_driver); } static void zte_exit(void) { ofono_modem_driver_unregister(&zte_driver); } OFONO_PLUGIN_DEFINE(zte, "ZTE modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, zte_init, zte_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/palmpre.c0000644000015600001650000001255112671500024021670 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include struct palmpre_data { GAtChat *chat; }; static int palmpre_probe(struct ofono_modem *modem) { struct palmpre_data *data; DBG("%p", modem); data = g_try_new0(struct palmpre_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void palmpre_remove(struct ofono_modem *modem) { struct palmpre_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); g_at_chat_unref(data->chat); g_free(data); } static void palmpre_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static void cfun_set_on_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; DBG(""); ofono_modem_set_powered(modem, ok); } static int palmpre_enable(struct ofono_modem *modem) { struct palmpre_data *data = ofono_modem_get_data(modem); GIOChannel *io; GAtSyntax *syntax; const char *device; GHashTable *options; DBG("%p", modem); device = ofono_modem_get_string(modem, "Device"); if (device == NULL) device = "/dev/modem0"; options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) return -ENOMEM; g_hash_table_insert(options, "Baud", "115200"); io = g_at_tty_open(device, options); g_hash_table_destroy(options); if (io == NULL) return -EIO; syntax = g_at_syntax_new_gsm_permissive(); data->chat = g_at_chat_new(io, syntax); g_io_channel_unref(io); g_at_syntax_unref(syntax); if (data->chat == NULL) return -ENOMEM; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(data->chat, palmpre_debug, ""); /* Ensure terminal is in a known state */ g_at_chat_send(data->chat, "ATZ E0 +CMEE=1", NULL, NULL, NULL, NULL); /* Power modem up */ g_at_chat_send(data->chat, "AT+CFUN=1", NULL, cfun_set_on_cb, modem, NULL); return 0; } static void cfun_set_off_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct palmpre_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->chat); data->chat = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int palmpre_disable(struct ofono_modem *modem) { struct palmpre_data *data = ofono_modem_get_data(modem); DBG("%p", modem); /* Power modem down */ g_at_chat_cancel_all(data->chat); g_at_chat_unregister_all(data->chat); g_at_chat_send(data->chat, "AT+CFUN=0", NULL, cfun_set_off_cb, modem, NULL); return -EINPROGRESS; } static void palmpre_pre_sim(struct ofono_modem *modem) { struct palmpre_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->chat); sim = ofono_sim_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->chat); ofono_voicecall_create(modem, 0, "atmodem", data->chat); if (sim) ofono_sim_inserted_notify(sim, TRUE); } static void palmpre_post_sim(struct ofono_modem *modem) { struct palmpre_data *data = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_netreg_create(modem, 0, "atmodem", data->chat); ofono_sms_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->chat); ofono_phonebook_create(modem, 0, "atmodem", data->chat); gprs = ofono_gprs_create(modem, 0, "atmodem", data->chat); gc = ofono_gprs_context_create(modem, 0, "atmodem", data->chat); if (gprs && gc) ofono_gprs_add_context(gprs, gc); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } static struct ofono_modem_driver palmpre_driver = { .name = "palmpre", .probe = palmpre_probe, .remove = palmpre_remove, .enable = palmpre_enable, .disable = palmpre_disable, .pre_sim = palmpre_pre_sim, .post_sim = palmpre_post_sim }; static int palmpre_init(void) { return ofono_modem_driver_register(&palmpre_driver); } static void palmpre_exit(void) { ofono_modem_driver_unregister(&palmpre_driver); } OFONO_PLUGIN_DEFINE(palmpre, "Palm Pre driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, palmpre_init, palmpre_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/cinterion.c0000644000015600001650000001437412671500024022227 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int cinterion_probe(struct ofono_modem *modem) { return 0; } static void cinterion_remove(struct ofono_modem *modem) { } static void cinterion_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static int cinterion_enable(struct ofono_modem *modem) { GAtChat *chat; GIOChannel *channel; GAtSyntax *syntax; GHashTable *options; const char *device; DBG("%p", modem); options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) return -ENOMEM; device = ofono_modem_get_string(modem, "Device"); if (device == NULL) return -EINVAL; g_hash_table_insert(options, "Baud", "115200"); g_hash_table_insert(options, "StopBits", "1"); g_hash_table_insert(options, "DataBits", "8"); g_hash_table_insert(options, "Parity", "none"); g_hash_table_insert(options, "XonXoff", "off"); g_hash_table_insert(options, "RtsCts", "on"); g_hash_table_insert(options, "Local", "on"); g_hash_table_insert(options, "Read", "on"); channel = g_at_tty_open(device, options); g_hash_table_destroy(options); if (channel == NULL) return -EIO; /* * (Cinterion plugin is based on tc65 plugin. Comment left in but may * not be applicable in the general case) * * TC65 works almost as the 27.007 says. But for example after * AT+CRSM the modem replies with the data in the queried EF and * writes three pairs of after the data and before OK. */ syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return -ENOMEM; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, cinterion_debug, ""); ofono_modem_set_data(modem, chat); return 0; } static int cinterion_disable(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); g_at_chat_send(chat, "AT+CFUN=7", NULL, NULL, NULL, NULL); g_at_chat_unref(chat); return 0; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void cinterion_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { GAtChat *chat = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=7"; DBG("modem %p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(chat, command, NULL, set_online_cb, cbd, g_free)) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void cinterion_pre_sim(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", chat); sim = ofono_sim_create(modem, 0, "atmodem", chat); ofono_voicecall_create(modem, 0, "atmodem", chat); if (sim) ofono_sim_inserted_notify(sim, TRUE); } static void cinterion_post_sim(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); DBG("%p", modem); ofono_phonebook_create(modem, 0, "atmodem", chat); ofono_sms_create(modem, 0, "atmodem", chat); } static void cinterion_post_online(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_ussd_create(modem, 0, "atmodem", chat); ofono_call_forwarding_create(modem, 0, "atmodem", chat); ofono_call_settings_create(modem, 0, "atmodem", chat); ofono_netreg_create(modem, OFONO_VENDOR_CINTERION, "atmodem", chat); ofono_call_meter_create(modem, 0, "atmodem", chat); ofono_call_barring_create(modem, 0, "atmodem", chat); gprs = ofono_gprs_create(modem, 0, "atmodem", chat); gc = ofono_gprs_context_create(modem, 0, "atmodem", chat); if (gprs && gc) ofono_gprs_add_context(gprs, gc); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } static struct ofono_modem_driver cinterion_driver = { .name = "cinterion", .probe = cinterion_probe, .remove = cinterion_remove, .enable = cinterion_enable, .disable = cinterion_disable, .set_online = cinterion_set_online, .pre_sim = cinterion_pre_sim, .post_sim = cinterion_post_sim, .post_online = cinterion_post_online, }; static int cinterion_init(void) { return ofono_modem_driver_register(&cinterion_driver); } static void cinterion_exit(void) { ofono_modem_driver_unregister(&cinterion_driver); } OFONO_PLUGIN_DEFINE(cinterion, "Cinterion driver plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, cinterion_init, cinterion_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/calypso.c0000644000015600001650000003376612671500024021715 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CALYPSO_POWER_PATH "/sys/bus/platform/devices/gta02-pm-gsm.0/power_on" #define CALYPSO_RESET_PATH "/sys/bus/platform/devices/gta02-pm-gsm.0/reset" enum powercycle_state { POWERCYCLE_STATE_POWER0 = 0, POWERCYCLE_STATE_RESET0, POWERCYCLE_STATE_POWER1, POWERCYCLE_STATE_RESET1, POWERCYCLE_STATE_FINISHED, }; #define NUM_DLC 4 #define VOICE_DLC 0 #define NETREG_DLC 1 #define SMS_DLC 2 #define AUX_DLC 3 #define SETUP_DLC 3 static char *debug_prefixes[NUM_DLC] = { "Voice: ", "Net: ", "SMS: ", "Aux: " }; struct calypso_data { GAtMux *mux; GAtChat *dlcs[NUM_DLC]; enum powercycle_state state; gboolean phonebook_added; gboolean sms_added; gboolean have_sim; struct ofono_sim *sim; }; static const char *cpin_prefix[] = { "+CPIN:", NULL }; static const char *none_prefix[] = { NULL }; static void calypso_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static int calypso_probe(struct ofono_modem *modem) { const char *device; struct calypso_data *data; DBG("%p", modem); device = ofono_modem_get_string(modem, "Device"); if (device == NULL) return -EINVAL; DBG("%s", device); data = g_new0(struct calypso_data, 1); ofono_modem_set_data(modem, data); return 0; } static void calypso_remove(struct ofono_modem *modem) { struct calypso_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_free(data); } static void cstat_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct calypso_data *data = ofono_modem_get_data(modem); GAtResultIter iter; const char *stat; int enabled; DBG("phonebook: %d, sms: %d", data->phonebook_added, data->sms_added); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "%CSTAT:")) return; if (!g_at_result_iter_next_unquoted_string(&iter, &stat)) return; if (!g_at_result_iter_next_number(&iter, &enabled)) return; DBG("stat: %s, enabled: %d", stat, enabled); if (!g_strcmp0(stat, "PHB") && enabled == 1 && !data->phonebook_added) { data->phonebook_added = TRUE; ofono_phonebook_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); } if (!g_strcmp0(stat, "SMS") && enabled == 1 && !data->sms_added) { data->sms_added = TRUE; ofono_sms_create(modem, 0, "atmodem", data->dlcs[SMS_DLC]); } } static void simind_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct calypso_data *data = ofono_modem_get_data(modem); GAtResultIter iter; if (data->sim == NULL) return; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "%SIMREM:")) ofono_sim_inserted_notify(data->sim, FALSE); else if (g_at_result_iter_next(&iter, "%SIMINS:")) ofono_sim_inserted_notify(data->sim, TRUE); } static void setup_modem(struct ofono_modem *modem) { struct calypso_data *data = ofono_modem_get_data(modem); int i; /* Generate unsolicited notifications as soon as they're generated */ for (i = 0; i < NUM_DLC; i++) { g_at_chat_send(data->dlcs[i], "ATE0", NULL, NULL, NULL, NULL); g_at_chat_send(data->dlcs[i], "AT%CUNS=0", NULL, NULL, NULL, NULL); g_at_chat_send(data->dlcs[i], "AT+CMEE=1", NULL, NULL, NULL, NULL); } /* CSTAT tells us when SMS & Phonebook are ready to be used */ g_at_chat_register(data->dlcs[SETUP_DLC], "%CSTAT:", cstat_notify, FALSE, modem, NULL); g_at_chat_send(data->dlcs[SETUP_DLC], "AT%CSTAT=1", NULL, NULL, NULL, NULL); /* audio side tone: set to minimum */ g_at_chat_send(data->dlcs[SETUP_DLC], "AT@ST=\"-26\"", NULL, NULL, NULL, NULL); /* Disable deep sleep */ g_at_chat_send(data->dlcs[SETUP_DLC], "AT%SLEEP=2", NULL, NULL, NULL, NULL); /* Enable SIM removed/inserted notifications */ g_at_chat_register(data->dlcs[SETUP_DLC], "%SIMREM:", simind_notify, FALSE, modem, NULL); g_at_chat_register(data->dlcs[SETUP_DLC], "%SIMINS:", simind_notify, FALSE, modem, NULL); g_at_chat_send(data->dlcs[SETUP_DLC], "AT%SIMIND=1", NULL, NULL, NULL, NULL); } static void simpin_check_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct calypso_data *data = ofono_modem_get_data(modem); DBG(""); /* Modem returns ERROR if there is no SIM in slot. */ data->have_sim = ok; setup_modem(modem); ofono_modem_set_powered(modem, TRUE); } static void init_simpin_check(struct ofono_modem *modem) { struct calypso_data *data = ofono_modem_get_data(modem); /* * Check for SIM presence by seeing if AT+CPIN? succeeds. * The SIM can not be practically inserted/removed without * restarting the device so there's no need to check more * than once. */ g_at_chat_send(data->dlcs[SETUP_DLC], "AT+CPIN?", cpin_prefix, simpin_check_cb, modem, NULL); } static void mux_setup(GAtMux *mux, gpointer user_data) { struct ofono_modem *modem = user_data; struct calypso_data *data = ofono_modem_get_data(modem); GIOChannel *io; GAtSyntax *syntax; int i; DBG("%p", mux); if (mux == NULL) { ofono_modem_set_powered(modem, FALSE); return; } data->mux = mux; if (getenv("OFONO_AT_DEBUG")) g_at_mux_set_debug(data->mux, calypso_debug, "MUX: "); g_at_mux_start(mux); for (i = 0; i < NUM_DLC; i++) { io = g_at_mux_create_channel(mux); syntax = g_at_syntax_new_gsm_permissive(); data->dlcs[i] = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(data->dlcs[i], calypso_debug, debug_prefixes[i]); g_at_chat_set_wakeup_command(data->dlcs[i], "AT\r", 500, 5000); } init_simpin_check(modem); } static void modem_initialize(struct ofono_modem *modem) { GAtSyntax *syntax; GAtChat *chat; const char *device; GIOChannel *io; GHashTable *options; DBG(""); device = ofono_modem_get_string(modem, "Device"); options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) goto error; g_hash_table_insert(options, "Baud", "115200"); g_hash_table_insert(options, "Parity", "none"); g_hash_table_insert(options, "StopBits", "1"); g_hash_table_insert(options, "DataBits", "8"); g_hash_table_insert(options, "XonXoff", "on"); g_hash_table_insert(options, "Local", "on"); g_hash_table_insert(options, "RtsCts", "on"); io = g_at_tty_open(device, options); g_hash_table_destroy(options); if (io == NULL) goto error; /* Calypso is normally compliant to 27.007, except the vendor-specific * notifications (like %CSTAT) are not prefixed by \r\n */ syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); if (chat == NULL) goto error; if (getenv("OFONO_AT_DEBUG") != NULL) g_at_chat_set_debug(chat, calypso_debug, "Setup: "); g_at_chat_set_wakeup_command(chat, "AT\r", 500, 5000); g_at_chat_send(chat, "ATE0", NULL, NULL, NULL, NULL); g_at_mux_setup_gsm0710(chat, mux_setup, modem, NULL); g_at_chat_unref(chat); return; error: ofono_modem_set_powered(modem, FALSE); } static gboolean write_file(const char *file, gboolean on) { int fd; int r; fd = open(file, O_WRONLY); if (fd == -1) return FALSE; DBG("%s, %s", file, on ? "1" : "0"); if (on) r = write(fd, "1\n", 2); else r = write(fd, "0\n", 2); close(fd); return r > 0 ? TRUE : FALSE; } static gboolean poweron_cycle(gpointer user_data) { struct ofono_modem *modem = user_data; struct calypso_data *data = ofono_modem_get_data(modem); switch (data->state) { case POWERCYCLE_STATE_POWER0: if (write_file(CALYPSO_RESET_PATH, FALSE)) { data->state = POWERCYCLE_STATE_RESET0; return TRUE; } break; case POWERCYCLE_STATE_RESET0: if (write_file(CALYPSO_POWER_PATH, TRUE)) { data->state = POWERCYCLE_STATE_POWER1; return TRUE; } break; case POWERCYCLE_STATE_POWER1: if (write_file(CALYPSO_RESET_PATH, TRUE)) { data->state = POWERCYCLE_STATE_RESET1; return TRUE; } break; case POWERCYCLE_STATE_RESET1: if (write_file(CALYPSO_RESET_PATH, FALSE)) { data->state = POWERCYCLE_STATE_FINISHED; return TRUE; } break; case POWERCYCLE_STATE_FINISHED: modem_initialize(modem); return FALSE; default: break; }; ofono_modem_set_powered(modem, FALSE); return FALSE; } /* power up hardware */ static int calypso_enable(struct ofono_modem *modem) { struct calypso_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (write_file(CALYPSO_POWER_PATH, FALSE) == FALSE) return -EINVAL; data->state = POWERCYCLE_STATE_POWER0; g_timeout_add_seconds(1, poweron_cycle, modem); return -EINPROGRESS; } static int calypso_disable(struct ofono_modem *modem) { struct calypso_data *data = ofono_modem_get_data(modem); int i; DBG("%p", modem); for (i = 0; i < NUM_DLC; i++) { g_at_chat_unref(data->dlcs[i]); data->dlcs[i] = NULL; } g_at_mux_shutdown(data->mux); g_at_mux_unref(data->mux); data->mux = NULL; data->phonebook_added = FALSE; data->sms_added = FALSE; if (write_file(CALYPSO_POWER_PATH, FALSE)) return 0; return -EINVAL; } static void calypso_pre_sim(struct ofono_modem *modem) { struct calypso_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); data->sim = ofono_sim_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_voicecall_create(modem, 0, "calypsomodem", data->dlcs[VOICE_DLC]); /* * The STK atom is only useful after SIM has been initialised, * so really it belongs in post_sim. However, the order of the * following three actions is adapted to work around different * issues with the Calypso's firmware in its different versions * (may have been fixed starting at some version, but this order * should work with any version). * * To deal with PIN-enabled and PIN-disabled SIM cards, the order * needs to be as follows: * * AT%SATC="..." * ... * AT+CFUN=1 * ... * AT+CPIN="..." * * %SATC comes before the other two actions because it provides * the Terminal Profile data to the modem, which will be used * during the Profile Download either during +CFUN=1 (on * unprotected cards) or +CPIN="..." (on protected cards). * The STK atom needs to be present at this time because the * card may start issuing proactive commands immediately after * the Download. * * +CFUN=1 appears before PIN entry because switching from +CFUN * mode 0 later, on the Calypso has side effects at least on some * versions of the firmware: * * mode 0 -> 1 transition forces PIN re-authentication. * mode 0 -> 4 doesn't work at all. * mode 1 -> 4 and * mode 4 -> 1 transitions work and have no side effects. * * So in order to switch to Offline mode at startup, * AT+CFUN=1;+CFUN=4 would be needed. * * Additionally AT+CFUN=1 response is not checked: on PIN-enabled * cards, it will in most situations return "+CME ERROR: SIM PIN * required" (CME ERROR 11) even though the switch to mode 1 * succeeds. It will not perform Profile Download on those cards * though, until another +CPIN command. */ if (data->have_sim && data->sim) ofono_stk_create(modem, 0, "calypsomodem", data->dlcs[AUX_DLC]); g_at_chat_send(data->dlcs[AUX_DLC], "AT+CFUN=1", none_prefix, NULL, NULL, NULL); if (data->have_sim && data->sim) ofono_sim_inserted_notify(data->sim, TRUE); } static void calypso_post_sim(struct ofono_modem *modem) { struct calypso_data *data = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; DBG("%p", modem); ofono_ussd_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_call_forwarding_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_call_settings_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_netreg_create(modem, OFONO_VENDOR_CALYPSO, "atmodem", data->dlcs[NETREG_DLC]); ofono_call_meter_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_call_barring_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); ofono_call_volume_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } static struct ofono_modem_driver calypso_driver = { .name = "calypso", .probe = calypso_probe, .remove = calypso_remove, .enable = calypso_enable, .disable = calypso_disable, .pre_sim = calypso_pre_sim, .post_sim = calypso_post_sim, }; static int calypso_init(void) { return ofono_modem_driver_register(&calypso_driver); } static void calypso_exit(void) { ofono_modem_driver_unregister(&calypso_driver); } OFONO_PLUGIN_DEFINE(calypso, "TI Calypso modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, calypso_init, calypso_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/ril.c0000644000015600001650000002747312671500073021033 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL-based devices * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2014 Canonical Ltd. * Copyright (C) 2013 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. * * 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 #endif #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofono.h" #include #include #include #include "ril.h" #include "drivers/rilmodem/rilmodem.h" #include "drivers/rilmodem/vendor.h" #include "drivers/qcommsimmodem/qcom_msim_modem.h" #define MAX_SIM_STATUS_RETRIES 15 /* this gives 30s for rild to initialize */ #define RILD_MAX_CONNECT_RETRIES 5 #define RILD_CONNECT_RETRY_TIME_S 5 struct ril_data { GRil *ril; enum ofono_ril_vendor vendor; int sim_status_retries; ofono_bool_t connected; ofono_bool_t ofono_online; int radio_state; struct ofono_sim *sim; struct ofono_radio_settings *radio_settings; int rild_connect_retries; }; static void ril_debug(const char *str, void *user_data) { struct ril_data *rd = user_data; ofono_info("Device %d: %s", g_ril_get_slot(rd->ril), str); } static void ril_radio_state_changed(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = user_data; struct ril_data *rd = ofono_modem_get_data(modem); int radio_state = g_ril_unsol_parse_radio_state_changed(rd->ril, message); if (radio_state != rd->radio_state) { ofono_info("%s: state: %s rd->ofono_online: %d", __func__, ril_radio_state_to_string(radio_state), rd->ofono_online); rd->radio_state = radio_state; switch (radio_state) { case RADIO_STATE_ON: if (rd->radio_settings == NULL) { char *rs_driver; struct ril_radio_settings_driver_data rs_data = { rd->ril, modem }; if (rd->vendor == OFONO_RIL_VENDOR_QCOM_MSIM) rs_driver = QCOMMSIMMODEM; else rs_driver = RILMODEM; rd->radio_settings = ofono_radio_settings_create(modem, rd->vendor, rs_driver, &rs_data); } break; case RADIO_STATE_UNAVAILABLE: case RADIO_STATE_OFF: /* * Unexpected radio state change, as we are supposed to * be online. UNAVAILABLE has been seen occassionally * when powering off the phone. We wait 5 secs to avoid * too fast re-spawns, then exit with error to make * upstart re-start ofono. */ if (rd->ofono_online) { ofono_error("%s: radio self-powered off!", __func__); sleep(5); exit(1); } break; default: /* Malformed parcel; no radio state == broken rild */ g_assert(FALSE); } } } int ril_create(struct ofono_modem *modem, enum ofono_ril_vendor vendor) { ofono_bool_t lte_cap; struct ril_data *rd = g_try_new0(struct ril_data, 1); if (rd == NULL) { errno = ENOMEM; goto error; } DBG(""); rd->vendor = vendor; rd->ofono_online = FALSE; rd->radio_state = RADIO_STATE_UNAVAILABLE; lte_cap = getenv("OFONO_RIL_RAT_LTE") ? TRUE : FALSE; ofono_modem_set_boolean(modem, MODEM_PROP_LTE_CAPABLE, lte_cap); ofono_modem_set_data(modem, rd); return 0; error: g_free(rd); return -errno; } static int ril_probe(struct ofono_modem *modem) { return ril_create(modem, OFONO_RIL_VENDOR_AOSP); } void ril_remove(struct ofono_modem *modem) { struct ril_data *rd = ofono_modem_get_data(modem); ofono_modem_set_data(modem, NULL); if (!rd) return; g_ril_unref(rd->ril); g_free(rd); } void ril_pre_sim(struct ofono_modem *modem) { struct ril_data *rd = ofono_modem_get_data(modem); struct ril_voicecall_driver_data vc_data = { rd->ril, modem }; struct ril_sim_data sim_data; DBG(""); ofono_devinfo_create(modem, rd->vendor, RILMODEM, rd->ril); ofono_voicecall_create(modem, rd->vendor, RILMODEM, &vc_data); ofono_call_volume_create(modem, rd->vendor, RILMODEM, rd->ril); sim_data.gril = rd->ril; sim_data.modem = modem; sim_data.ril_state_watch = NULL; rd->sim = ofono_sim_create(modem, rd->vendor, RILMODEM, &sim_data); g_assert(rd->sim != NULL); } void ril_post_sim(struct ofono_modem *modem) { struct ril_data *rd = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; struct ofono_message_waiting *mw; struct ril_gprs_driver_data gprs_data = { rd->ril, modem }; struct ril_gprs_context_data inet_ctx = { rd->ril, modem, OFONO_GPRS_CONTEXT_TYPE_INTERNET }; struct ril_gprs_context_data mms_ctx = { rd->ril, modem, OFONO_GPRS_CONTEXT_TYPE_MMS }; /* TODO: this function should setup: * - phonebook * - stk ( SIM toolkit ) * - radio_settings */ ofono_sms_create(modem, rd->vendor, RILMODEM, rd->ril); gprs = ofono_gprs_create(modem, rd->vendor, RILMODEM, &gprs_data); gc = ofono_gprs_context_create(modem, rd->vendor, RILMODEM, &inet_ctx); if (gc) { ofono_gprs_context_set_type(gc, OFONO_GPRS_CONTEXT_TYPE_INTERNET); ofono_gprs_add_context(gprs, gc); } gc = ofono_gprs_context_create(modem, rd->vendor, RILMODEM, &mms_ctx); if (gc) { ofono_gprs_context_set_type(gc, OFONO_GPRS_CONTEXT_TYPE_MMS); ofono_gprs_add_context(gprs, gc); } mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); ofono_call_forwarding_create(modem, rd->vendor, RILMODEM, rd->ril); ofono_phonebook_create(modem, rd->vendor, RILMODEM, modem); } void ril_post_online(struct ofono_modem *modem) { struct ril_data *rd = ofono_modem_get_data(modem); ofono_netreg_create(modem, rd->vendor, RILMODEM, rd->ril); ofono_ussd_create(modem, rd->vendor, RILMODEM, rd->ril); ofono_call_settings_create(modem, rd->vendor, RILMODEM, rd->ril); ofono_call_barring_create(modem, rd->vendor, RILMODEM, rd->ril); } static void ril_set_online_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ril_data *rd = cbd->user; ofono_modem_online_cb_t cb = cbd->cb; if (message != NULL && message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(rd->ril, message); DBG("%s: set_online OK: rd->ofono_online: %d", __func__, rd->ofono_online); CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { ofono_error("%s: set_online: %d failed", __func__, rd->ofono_online); CALLBACK_WITH_FAILURE(cb, cbd->data); } g_free(cbd); } static void ril_send_power(struct ril_data *rd, ofono_bool_t online, GRilResponseFunc func, gpointer user_data) { struct parcel rilp; DBG("(online = 1, offline = 0)): %i", online); g_ril_request_power(rd->ril, (const gboolean) online, &rilp); if (g_ril_send(rd->ril, RIL_REQUEST_RADIO_POWER, &rilp, func, user_data, NULL) == 0 && func != NULL) { func(NULL, user_data); } } void ril_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t callback, void *data) { struct ril_data *rd = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(callback, data, rd); rd->ofono_online = online; DBG("setting rd->ofono_online to: %d", online); ril_send_power(rd, online, ril_set_online_cb, cbd); } static void ril_set_powered_off_cb(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = user_data; struct ril_data *rd = ofono_modem_get_data(modem); if (message != NULL && message->error == RIL_E_SUCCESS) g_ril_print_response_no_args(rd->ril, message); DBG("calling set_powered(TRUE)"); ofono_modem_set_powered(modem, TRUE); } static void ril_connected(struct ril_msg *message, gpointer user_data) { struct ofono_modem *modem = (struct ofono_modem *) user_data; struct ril_data *rd = ofono_modem_get_data(modem); int version; version = g_ril_unsol_parse_connected(rd->ril, message); g_ril_set_version(rd->ril, version); ofono_info("[%d,UNSOL]< %s, version %d", g_ril_get_slot(rd->ril), g_ril_unsol_request_to_string(rd->ril, message->req), version); /* TODO: need a disconnect function to restart things! */ rd->connected = TRUE; DBG("calling set_powered(FALSE) on connected"); ril_send_power(rd, FALSE, ril_set_powered_off_cb, modem); } static int create_gril(struct ofono_modem *modem) { struct ril_data *rd = ofono_modem_get_data(modem); int slot_id = ofono_modem_get_integer(modem, "Slot"); const gchar *socket = ofono_modem_get_string(modem, "Socket"); ofono_info("Using %s as socket for slot %d.", socket, slot_id); rd->ril = g_ril_new(socket, rd->vendor); /* NOTE: Since AT modems open a tty, and then call * g_at_chat_new(), they're able to return -EIO if * the first fails, and -ENOMEM if the second fails. * in our case, we already return -EIO if the ril_new * fails. If this is important, we can create a ril_socket * abstraction... ( probaby not a bad idea ). */ if (rd->ril == NULL) { ofono_error("g_ril_new() failed to create modem!"); return -EIO; } g_ril_set_slot(rd->ril, slot_id); if (getenv("OFONO_RIL_TRACE")) g_ril_set_trace(rd->ril, TRUE); if (getenv("OFONO_RIL_HEX_TRACE")) g_ril_set_debugf(rd->ril, ril_debug, rd); g_ril_register(rd->ril, RIL_UNSOL_RIL_CONNECTED, ril_connected, modem); g_ril_register(rd->ril, RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, ril_radio_state_changed, modem); return 0; } static gboolean connect_rild(gpointer user_data) { struct ofono_modem *modem = (struct ofono_modem *) user_data; struct ril_data *rd = ofono_modem_get_data(modem); ofono_info("Trying to reconnect to rild..."); if (rd->rild_connect_retries++ < RILD_MAX_CONNECT_RETRIES) { if (create_gril(modem) < 0) return TRUE; } else { ofono_error("Exiting, can't connect to rild."); exit(0); } return FALSE; } int ril_enable(struct ofono_modem *modem) { int ret; DBG(""); ret = create_gril(modem); if (ret < 0) g_timeout_add_seconds(RILD_CONNECT_RETRY_TIME_S, connect_rild, modem); return -EINPROGRESS; } int ril_disable(struct ofono_modem *modem) { struct ril_data *rd = ofono_modem_get_data(modem); DBG("%p", modem); ril_send_power(rd, FALSE, NULL, NULL); return 0; } static struct ofono_modem_driver ril_driver = { .name = "ril", .probe = ril_probe, .remove = ril_remove, .enable = ril_enable, .disable = ril_disable, .pre_sim = ril_pre_sim, .post_sim = ril_post_sim, .post_online = ril_post_online, .set_online = ril_set_online, }; /* * This plugin is a generic ( aka default ) device plugin for RIL-based devices. * The plugin 'rildev' is used to determine which RIL plugin should be loaded * based upon an environment variable. */ static int ril_init(void) { int retval = ofono_modem_driver_register(&ril_driver); if (retval != 0) DBG("ofono_modem_driver_register returned: %d", retval); return retval; } static void ril_exit(void) { DBG(""); ofono_modem_driver_unregister(&ril_driver); } OFONO_PLUGIN_DEFINE(ril, "RIL modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, ril_init, ril_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/hso.c0000644000015600001650000002664312671500024021030 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; static const char *opmn_prefix[] = { "_OPMN:", NULL }; static const char *obls_prefix[] = { "_OBLS:", NULL }; static const char *opcm_prefix[] = { "_OPCMENABLE:", NULL }; struct hso_data { GAtChat *app; GAtChat *control; GAtChat *modem; guint sim_poll_source; guint sim_poll_count; ofono_bool_t have_sim; ofono_bool_t have_voice; }; static int hso_probe(struct ofono_modem *modem) { struct hso_data *data; DBG("%p", modem); data = g_try_new0(struct hso_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void hso_remove(struct ofono_modem *modem) { struct hso_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); g_at_chat_unref(data->control); if (data->sim_poll_source > 0) g_source_remove(data->sim_poll_source); g_free(data); } static void hso_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static void opcm_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct hso_data *data = ofono_modem_get_data(modem); GAtResultIter iter; DBG(""); if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "_OPCMENABLE:")) return; g_at_chat_send(data->app, "AT_OPCMENABLE=1", none_prefix, NULL, NULL, NULL); } static void opcm_support(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct hso_data *data = ofono_modem_get_data(modem); GAtResultIter iter; DBG(""); if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "_OPCMENABLE:")) return; data->have_voice = TRUE; g_at_chat_send(data->app, "AT_OPCMENABLE?", opcm_prefix, opcm_query, modem, NULL); } static gboolean init_sim_check(gpointer user_data); static void sim_status(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct hso_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int sim, pb, sms; DBG(""); if (data->sim_poll_source > 0) { g_source_remove(data->sim_poll_source); data->sim_poll_source = 0; } if (!ok) { ofono_modem_set_powered(modem, FALSE); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "_OBLS:")) { ofono_modem_set_powered(modem, FALSE); return; } if (!g_at_result_iter_next_number(&iter, &sim)) { ofono_modem_set_powered(modem, FALSE); return; } if (!g_at_result_iter_next_number(&iter, &pb)) { ofono_modem_set_powered(modem, FALSE); return; } if (!g_at_result_iter_next_number(&iter, &sms)) { ofono_modem_set_powered(modem, FALSE); return; } DBG("status sim %d pb %d sms %d", sim, pb, sms); switch (sim) { case 0: /* not ready */ data->have_sim = FALSE; if (data->sim_poll_count++ < 5) { data->sim_poll_source = g_timeout_add_seconds(1, init_sim_check, modem); return; } break; case 1: /* SIM card ready */ data->have_sim = TRUE; break; case 2: /* no SIM card */ data->have_sim = FALSE; break; default: data->have_sim = FALSE; break; } data->sim_poll_count = 0; ofono_modem_set_powered(modem, data->have_sim); if (data->have_sim == FALSE) return; /* * Ensure that the modem is using GSM character set and not IRA, * otherwise weirdness with umlauts and other non-ASCII characters * can result */ g_at_chat_send(data->control, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->app, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); /* * Option has the concept of Speech Service versus * Data Service. Problem is that in Data Service mode * the card will reject all voice calls. This is a * problem for Multi-SIM cards where one of the SIM * cards is used in a mobile phone and thus incoming * calls would be not signalled on the phone. * * 0 = Speech Service enabled * 1 = Data Service only mode */ g_at_chat_send(data->app, "AT_ODO?", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->app, "AT_ODO=0", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->app, "AT_OPCMENABLE=?", opcm_prefix, opcm_support, modem, NULL); } static gboolean init_sim_check(gpointer user_data) { struct ofono_modem *modem = user_data; struct hso_data *data = ofono_modem_get_data(modem); data->sim_poll_source = 0; g_at_chat_send(data->control, "AT_OBLS", obls_prefix, sim_status, modem, NULL); return FALSE; } static void check_model(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; GAtResultIter iter; char const *model; DBG(""); if (!ok) goto done; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "_OPMN:")) goto done; if (g_at_result_iter_next_unquoted_string(&iter, &model)) ofono_info("Model is %s", model); done: init_sim_check(modem); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct hso_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { ofono_modem_set_powered(modem, FALSE); return; } g_at_chat_send(data->control, "AT_OPMN", opmn_prefix, check_model, modem, NULL); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GIOChannel *channel; GAtSyntax *syntax; GAtChat *chat; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, hso_debug, debug); return chat; } static int hso_enable(struct ofono_modem *modem) { struct hso_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->control = open_device(modem, "Control", "Control: "); if (data->control == NULL) return -EINVAL; data->app = open_device(modem, "Application", "App: "); if (data->app == NULL) { g_at_chat_unref(data->control); data->control = NULL; return -EIO; } data->modem = open_device(modem, "Modem", "Modem: "); g_at_chat_send(data->control, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->app, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->control, "AT+CFUN=4", none_prefix, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct hso_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->control); data->control = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int hso_disable(struct ofono_modem *modem) { struct hso_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (data->control == NULL) return 0; g_at_chat_cancel_all(data->control); g_at_chat_unregister_all(data->control); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_unref(data->app); data->app = NULL; g_at_chat_send(data->control, "AT+CFUN=0", none_prefix, cfun_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; if (ok) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); } static void hso_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct hso_data *data = ofono_modem_get_data(modem); GAtChat *chat = data->control; struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4"; DBG("modem %p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(chat, command, NULL, set_online_cb, cbd, g_free)) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void hso_pre_sim(struct ofono_modem *modem) { struct hso_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->control); sim = ofono_sim_create(modem, OFONO_VENDOR_OPTION_HSO, "atmodem", data->control); if (sim && data->have_sim == TRUE) ofono_sim_inserted_notify(sim, TRUE); } static void hso_post_sim(struct ofono_modem *modem) { struct hso_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (data->have_voice == TRUE) { ofono_voicecall_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->app); } ofono_phonebook_create(modem, 0, "atmodem", data->app); ofono_radio_settings_create(modem, 0, "hsomodem", data->app); ofono_sms_create(modem, OFONO_VENDOR_OPTION_HSO, "atmodem", data->app); } static void hso_post_online(struct ofono_modem *modem) { struct hso_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_netreg_create(modem, OFONO_VENDOR_OPTION_HSO, "atmodem", data->app); ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->app); ofono_ussd_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->app); gprs = ofono_gprs_create(modem, 0, "atmodem", data->app); gc = ofono_gprs_context_create(modem, 0, "hsomodem", data->control); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static struct ofono_modem_driver hso_driver = { .name = "hso", .probe = hso_probe, .remove = hso_remove, .enable = hso_enable, .disable = hso_disable, .set_online = hso_set_online, .pre_sim = hso_pre_sim, .post_sim = hso_post_sim, .post_online = hso_post_online, }; static int hso_init(void) { return ofono_modem_driver_register(&hso_driver); } static void hso_exit(void) { ofono_modem_driver_unregister(&hso_driver); } OFONO_PLUGIN_DEFINE(hso, "Option HSO modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, hso_init, hso_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/bluez4.h0000644000015600001650000000611112671500024021435 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Gustavo F. Padovan * * 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 #include #define BLUEZ_SERVICE "org.bluez" #define BLUEZ_MANAGER_INTERFACE BLUEZ_SERVICE ".Manager" #define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter" #define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device" #define BLUEZ_SERVICE_INTERFACE BLUEZ_SERVICE ".Service" #define BLUEZ_SERIAL_INTERFACE BLUEZ_SERVICE ".Serial" #define DBUS_TIMEOUT 60 #define DUN_GW_UUID "00001103-0000-1000-8000-00805f9b34fb" #define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb" #define HFP_HS_UUID "0000111e-0000-1000-8000-00805f9b34fb" #define SAP_UUID "0000112d-0000-1000-8000-00805f9b34fb" struct bluetooth_profile { const char *name; int (*probe)(const char *device, const char *dev_addr, const char *adapter_addr, const char *alias); void (*remove)(const char *prefix); void (*set_alias)(const char *device, const char *); }; struct bluetooth_sap_driver { const char *name; int (*enable) (struct ofono_modem *modem, struct ofono_modem *sap_modem, int bt_fd); void (*pre_sim) (struct ofono_modem *modem); void (*post_sim) (struct ofono_modem *modem); void (*set_online) (struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data); void (*post_online) (struct ofono_modem *modem); int (*disable) (struct ofono_modem *modem); }; struct server; typedef void (*ConnectFunc)(GIOChannel *io, GError *err, gpointer user_data); void bluetooth_get_properties(); int bluetooth_register_uuid(const char *uuid, struct bluetooth_profile *profile); void bluetooth_unregister_uuid(const char *uuid); struct server *bluetooth_register_server(guint8 channel, const char *sdp_record, ConnectFunc cb, gpointer user_data); void bluetooth_unregister_server(struct server *server); void bluetooth_create_path(const char *dev_addr, const char *adapter_addr, char *buf, int size); int bluetooth_send_with_reply(const char *path, const char *interface, const char *method, DBusPendingCall **call, DBusPendingCallNotifyFunction cb, void *user_data, DBusFreeFunction free_func, int timeout, int type, ...); void bluetooth_parse_properties(DBusMessage *reply, const char *property, ...); int bluetooth_sap_client_register(struct bluetooth_sap_driver *sap, struct ofono_modem *modem); void bluetooth_sap_client_unregister(struct ofono_modem *modem); ofono-1.17.bzr6912+16.04.20160314.3/plugins/mbm.c0000644000015600001650000002754212671500024021011 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *cfun_prefix[] = { "+CFUN:", NULL }; static const char *none_prefix[] = { NULL }; enum mbm_variant { MBM_GENERIC, MBM_DELL_D5530, /* OEM of F3507g */ }; #define MBM_FLAG_HAVE_SIM 0x1 #define MBM_FLAG_SAW_EMRDY 0x2 struct mbm_data { GAtChat *modem_port; GAtChat *data_port; unsigned int flags; struct ofono_location_reporting *lr; enum mbm_variant variant; struct at_util_sim_state_query *sim_state_query; }; static int mbm_probe(struct ofono_modem *modem) { struct mbm_data *data; DBG("%p", modem); data = g_try_new0(struct mbm_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void mbm_remove(struct ofono_modem *modem) { struct mbm_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup potential SIM state polling */ at_util_sim_state_query_free(data->sim_state_query); g_at_chat_unref(data->data_port); g_at_chat_unref(data->modem_port); g_free(data); } static void mbm_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static void d5530_notify(GAtResult *result, gpointer user_data) { DBG("D5530"); } static void mbm_quirk_d5530(struct ofono_modem *modem) { struct mbm_data *data = ofono_modem_get_data(modem); data->variant = MBM_DELL_D5530; /* This Dell modem sends some unsolicated messages when it boots. */ /* Try to ignore them. */ g_at_chat_register(data->modem_port, "D5530", d5530_notify, FALSE, NULL, NULL); g_at_chat_register(data->modem_port, "+GCAP:", d5530_notify, FALSE, NULL, NULL); } static void sim_state_cb(gboolean present, gpointer user_data) { struct ofono_modem *modem = user_data; struct mbm_data *data = ofono_modem_get_data(modem); at_util_sim_state_query_free(data->sim_state_query); data->sim_state_query = NULL; if (present) data->flags |= MBM_FLAG_HAVE_SIM; ofono_modem_set_powered(modem, TRUE); } static void check_model(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct mbm_data *data = ofono_modem_get_data(modem); GAtResultIter iter; char const *model; DBG(""); if (!ok) goto done; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, NULL)) { if (!g_at_result_iter_next_unquoted_string(&iter, &model)) continue; if (g_str_equal(model, "D5530")) mbm_quirk_d5530(modem); } done: data->sim_state_query = at_util_sim_state_query_new(data->modem_port, 1, 5, sim_state_cb, modem, NULL); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct mbm_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { ofono_modem_set_powered(modem, FALSE); return; } g_at_chat_send(data->modem_port, "AT+CGMM", NULL, check_model, modem, NULL); } static void cfun_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct mbm_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int status; DBG("%d", ok); if (!ok) return; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "+CFUN:") == FALSE) return; g_at_result_iter_next_number(&iter, &status); g_at_chat_send(data->modem_port, "AT&F E0 V1 X4 &C0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->data_port, "AT&F E0 V1 X4 &C0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->modem_port, "AT*E2CFUN=1", none_prefix, NULL, NULL, NULL); if (status != 4) { g_at_chat_send(data->modem_port, "AT+CFUN=4", none_prefix, cfun_enable, modem, NULL); return; } cfun_enable(TRUE, NULL, modem); } static void emrdy_notifier(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct mbm_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int status; DBG(""); if (data->flags & MBM_FLAG_SAW_EMRDY) return; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "*EMRDY:") == FALSE) return; g_at_result_iter_next_number(&iter, &status); if (status != 1) return; data->flags |= MBM_FLAG_SAW_EMRDY; g_at_chat_send(data->modem_port, "AT+CFUN?", cfun_prefix, cfun_query, modem, NULL); } static void emrdy_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct mbm_data *data = ofono_modem_get_data(modem); DBG("%d", ok); if (ok) return; /* Sometimes we query EMRDY just as the EMRDY notifier is fired */ if (data->flags & MBM_FLAG_SAW_EMRDY) return; /* On some MBM hardware the EMRDY cannot be queried, so if this fails * we try to run CFUN? to check the state. CFUN? will fail unless * EMRDY: 1 has been sent, in which case the emrdy_notifier should be * triggered eventually and we send CFUN? again. */ g_at_chat_send(data->modem_port, "AT+CFUN?", cfun_prefix, cfun_query, modem, NULL); } static GAtChat *create_port(const char *device) { GAtSyntax *syntax; GIOChannel *channel; GAtChat *chat; GHashTable *options; options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) return NULL; g_hash_table_insert(options, "Baud", "115200"); channel = g_at_tty_open(device, options); g_hash_table_destroy(options); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; return chat; } static int mbm_enable(struct ofono_modem *modem) { struct mbm_data *data = ofono_modem_get_data(modem); const char *modem_dev; const char *data_dev; DBG("%p", modem); modem_dev = ofono_modem_get_string(modem, "ModemDevice"); data_dev = ofono_modem_get_string(modem, "DataDevice"); DBG("%s, %s", modem_dev, data_dev); if (modem_dev == NULL || data_dev == NULL) return -EINVAL; data->modem_port = create_port(modem_dev); if (data->modem_port == NULL) return -EIO; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(data->modem_port, mbm_debug, "Modem: "); data->data_port = create_port(data_dev); if (data->data_port == NULL) { g_at_chat_unref(data->modem_port); data->modem_port = NULL; return -EIO; } if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(data->data_port, mbm_debug, "Data: "); g_at_chat_register(data->modem_port, "*EMRDY:", emrdy_notifier, FALSE, modem, NULL); g_at_chat_send(data->modem_port, "AT*EMRDY?", none_prefix, emrdy_query, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct mbm_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->modem_port); data->modem_port = NULL; g_at_chat_unref(data->data_port); data->data_port = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int mbm_disable(struct ofono_modem *modem) { struct mbm_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (data->modem_port == NULL) return 0; g_at_chat_cancel_all(data->modem_port); g_at_chat_unregister_all(data->modem_port); g_at_chat_send(data->modem_port, "AT+CFUN=4", NULL, cfun_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void mbm_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct mbm_data *data = ofono_modem_get_data(modem); GAtChat *chat = data->modem_port; struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4"; DBG("modem %p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(chat, command, none_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void mbm_pre_sim(struct ofono_modem *modem) { struct mbm_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->modem_port); sim = ofono_sim_create(modem, OFONO_VENDOR_MBM, "atmodem", data->modem_port); if ((data->flags & MBM_FLAG_HAVE_SIM) && sim) ofono_sim_inserted_notify(sim, TRUE); } static void mbm_post_sim(struct ofono_modem *modem) { struct mbm_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_stk_create(modem, 0, "mbmmodem", data->modem_port); ofono_radio_settings_create(modem, 0, "stemodem", data->modem_port); ofono_sms_create(modem, 0, "atmodem", data->modem_port); } static void mbm_post_online(struct ofono_modem *modem) { struct mbm_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; const char *gps_dev; DBG("%p", modem); gps_dev = ofono_modem_get_string(modem, "GPSDevice"); if (gps_dev) data->lr = ofono_location_reporting_create(modem, 0, "mbmmodem", data->modem_port); ofono_netreg_create(modem, OFONO_VENDOR_MBM, "atmodem", data->modem_port); switch (data->variant) { case MBM_GENERIC: ofono_cbs_create(modem, 0, "atmodem", data->modem_port); break; case MBM_DELL_D5530: /* DELL D5530 crashes when it processes CBSs */ break; } ofono_ussd_create(modem, 0, "atmodem", data->modem_port); gprs = ofono_gprs_create(modem, OFONO_VENDOR_MBM, "atmodem", data->modem_port); if (gprs == NULL) return; gc = ofono_gprs_context_create(modem, 0, "mbmmodem", data->modem_port); if (gc) { ofono_gprs_context_set_type(gc, OFONO_GPRS_CONTEXT_TYPE_INTERNET); ofono_gprs_add_context(gprs, gc); } gc = ofono_gprs_context_create(modem, 0, "atmodem", data->data_port); if (gc) { ofono_gprs_context_set_type(gc, OFONO_GPRS_CONTEXT_TYPE_MMS); ofono_gprs_add_context(gprs, gc); } } static struct ofono_modem_driver mbm_driver = { .name = "mbm", .probe = mbm_probe, .remove = mbm_remove, .enable = mbm_enable, .disable = mbm_disable, .set_online = mbm_set_online, .pre_sim = mbm_pre_sim, .post_sim = mbm_post_sim, .post_online = mbm_post_online, }; static int mbm_init(void) { return ofono_modem_driver_register(&mbm_driver); } static void mbm_exit(void) { ofono_modem_driver_unregister(&mbm_driver); } OFONO_PLUGIN_DEFINE(mbm, "Ericsson MBM modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, mbm_init, mbm_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/icera.c0000644000015600001650000002347312671500024021320 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; static const char *siminit_prefix[] = { "%ISIMINIT:", NULL }; static const char *ussdmode_prefix[] = { "%IUSSDMODE:", NULL }; struct icera_data { GAtChat *chat; struct ofono_sim *sim; gboolean have_sim; gboolean have_ussdmode; }; static int icera_probe(struct ofono_modem *modem) { struct icera_data *data; DBG("%p", modem); data = g_try_new0(struct icera_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void icera_remove(struct ofono_modem *modem) { struct icera_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup after hot-unplug */ g_at_chat_unref(data->chat); g_free(data); } static void icera_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { GAtChat *chat; GAtSyntax *syntax; GIOChannel *channel; GHashTable *options; const char *device; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) return NULL; g_hash_table_insert(options, "Baud", "115200"); channel = g_at_tty_open(device, options); g_hash_table_destroy(options); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, icera_debug, debug); return chat; } static void ussdmode_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct icera_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int mode; if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "%IUSSDMODE:")) return; if (!g_at_result_iter_next_number(&iter, &mode)) return; DBG("mode %d", mode); if (mode == 1) data->have_ussdmode = TRUE; } static void ussdmode_support(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct icera_data *data = ofono_modem_get_data(modem); GAtResultIter iter; if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "%IUSSDMODE:")) return; g_at_chat_send(data->chat, "AT%IUSSDMODE?", ussdmode_prefix, ussdmode_query, modem, NULL); } static void icera_set_sim_state(struct icera_data *data, int state) { DBG("state %d", state); switch (state) { case 1: if (data->have_sim == FALSE) { ofono_sim_inserted_notify(data->sim, TRUE); data->have_sim = TRUE; } break; case 0: case 2: if (data->have_sim == TRUE) { ofono_sim_inserted_notify(data->sim, FALSE); data->have_sim = FALSE; } break; default: ofono_warn("Unknown SIM state %d received", state); break; } } static void siminit_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct icera_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int state; if (data->sim == NULL) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "%ISIMINIT:")) return; if (!g_at_result_iter_next_number(&iter, &state)) return; icera_set_sim_state(data, state); } static void siminit_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct icera_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int state; DBG(""); if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "%ISIMINIT:")) return; if (!g_at_result_iter_next_number(&iter, &state)) return; icera_set_sim_state(data, state); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct icera_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->chat); data->chat = NULL; ofono_modem_set_powered(modem, FALSE); return; } /* switch to GSM character set instead of IRA */ g_at_chat_send(data->chat, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); data->have_sim = FALSE; /* notify that the modem is ready so that pre_sim gets called */ ofono_modem_set_powered(modem, TRUE); /* register for SIM init notifications */ g_at_chat_register(data->chat, "%ISIMINIT:", siminit_notify, FALSE, modem, NULL); g_at_chat_send(data->chat, "AT%ISIMINIT=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT%ISIMINIT", siminit_prefix, siminit_query, modem, NULL); g_at_chat_send(data->chat, "AT%IAIRCRAFT?", none_prefix, NULL, NULL, NULL); } static int icera_enable(struct ofono_modem *modem) { struct icera_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->chat = open_device(modem, "Aux", "Aux: "); if (data->chat == NULL) return -EIO; g_at_chat_send(data->chat, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT%IFWR", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT%ISWIN", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT%IUSSDMODE=?", ussdmode_prefix, ussdmode_support, modem, NULL); g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct icera_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->chat); data->chat = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int icera_disable(struct ofono_modem *modem) { struct icera_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->chat); g_at_chat_unregister_all(data->chat); g_at_chat_send(data->chat, "AT+CFUN=0", none_prefix, cfun_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void icera_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct icera_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4"; DBG("%p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(data->chat, command, none_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void icera_pre_sim(struct ofono_modem *modem) { struct icera_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->chat); data->sim = ofono_sim_create(modem, OFONO_VENDOR_ICERA, "atmodem", data->chat); } static void icera_post_sim(struct ofono_modem *modem) { struct icera_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_radio_settings_create(modem, 0, "iceramodem", data->chat); ofono_sms_create(modem, OFONO_VENDOR_ICERA, "atmodem", data->chat); gprs = ofono_gprs_create(modem, OFONO_VENDOR_ICERA, "atmodem", data->chat); gc = ofono_gprs_context_create(modem, 0, "iceramodem", data->chat); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static void icera_post_online(struct ofono_modem *modem) { struct icera_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_netreg_create(modem, OFONO_VENDOR_ICERA, "atmodem", data->chat); if (data->have_ussdmode == TRUE) ofono_ussd_create(modem, 0, "huaweimodem", data->chat); else ofono_ussd_create(modem, 0, "atmodem", data->chat); } static struct ofono_modem_driver icera_driver = { .name = "icera", .probe = icera_probe, .remove = icera_remove, .enable = icera_enable, .disable = icera_disable, .set_online = icera_set_online, .pre_sim = icera_pre_sim, .post_sim = icera_post_sim, .post_online = icera_post_online, }; static int icera_init(void) { return ofono_modem_driver_register(&icera_driver); } static void icera_exit(void) { ofono_modem_driver_unregister(&icera_driver); } OFONO_PLUGIN_DEFINE(icera, "Icera modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, icera_init, icera_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/n900.c0000644000015600001650000003154612671500024020723 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drivers/isimodem/isimodem.h" #include "drivers/isimodem/isiutil.h" #include "drivers/isimodem/infoserver.h" #include "drivers/isimodem/mtc.h" #include "drivers/isimodem/debug.h" #include "nokia-gpio.h" struct isi_data { const char *ifname; GIsiModem *modem; GIsiClient *client; struct isi_infoserver *infoserver; ofono_bool_t enabled; ofono_bool_t online; ofono_bool_t reported; enum power_state power_state; int mtc_state; guint timeout; struct isi_cb_data *online_cbd; }; static void mtc_power_off(struct isi_data *isi); static gboolean mtc_power_off_poll(gpointer user); static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid) { if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", strerror(-g_isi_msg_error(msg))); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", mtc_message_id_name(g_isi_msg_id(msg))); return FALSE; } return TRUE; } static void report_powered(struct ofono_modem *modem, struct isi_data *isi, ofono_bool_t powered) { if (powered == isi->reported) return; DBG("%s", powered ? "Powered on" : isi->enabled ? "Reset" : "Powered off"); isi->reported = powered; ofono_modem_set_powered(modem, powered); } static void report_online(struct isi_data *isi, ofono_bool_t online) { struct isi_cb_data *cbd = isi->online_cbd; ofono_modem_online_cb_t cb = cbd->cb; isi->online_cbd = NULL; if (isi->online == online) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void set_power_by_mtc_state(struct ofono_modem *modem, struct isi_data *isi, int mtc_state) { isi->mtc_state = mtc_state; if (isi->online_cbd) report_online(isi, mtc_state == MTC_NORMAL); switch (mtc_state) { case MTC_STATE_NONE: case MTC_POWER_OFF: case MTC_CHARGING: case MTC_SELFTEST_FAIL: report_powered(modem, isi, FALSE); break; case MTC_RF_INACTIVE: case MTC_NORMAL: default: report_powered(modem, isi, TRUE); } } static void mtc_state_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_modem *modem = data; struct isi_data *isi = ofono_modem_get_data(modem); uint8_t action; uint8_t state; if (g_isi_msg_error(msg) < 0) return; if (g_isi_msg_id(msg) != MTC_STATE_INFO_IND) return; if (!g_isi_msg_data_get_byte(msg, 0, &state) || !g_isi_msg_data_get_byte(msg, 1, &action)) return; if (action == MTC_START) { DBG("target modem state: %s (0x%02X)", mtc_modem_state_name(state), state); if (state == MTC_POWER_OFF) { isi->power_state = POWER_STATE_OFF_STARTED; mtc_power_off_poll(isi); } } else if (action == MTC_READY) { DBG("current modem state: %s (0x%02X)", mtc_modem_state_name(state), state); set_power_by_mtc_state(modem, isi, state); } } static void mtc_startup_synq_cb(const GIsiMessage *msg, void *data) { check_response_status(msg, MTC_STARTUP_SYNQ_RESP); } static void mtc_startup_synq(struct isi_data *isi) { const uint8_t msg[] = { MTC_STARTUP_SYNQ_REQ, 0, 0, }; g_isi_client_send(isi->client, msg, sizeof(msg), mtc_startup_synq_cb, NULL, NULL); } static void mtc_query_cb(const GIsiMessage *msg, void *data) { struct ofono_modem *modem = data; struct isi_data *isi = ofono_modem_get_data(modem); uint8_t current; uint8_t target; if (!check_response_status(msg, MTC_STATE_QUERY_RESP)) return; if (!g_isi_msg_data_get_byte(msg, 0, ¤t) || !g_isi_msg_data_get_byte(msg, 1, &target)) return; DBG("Modem state: current=%s (0x%02X) target=%s (0x%02X)", mtc_modem_state_name(current), current, mtc_modem_state_name(target), target); set_power_by_mtc_state(modem, isi, current); mtc_startup_synq(isi); } static void mtc_state_query(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); const uint8_t msg[] = { MTC_STATE_QUERY_REQ, 0, 0, }; if (!isi) return; g_isi_client_send(isi->client, msg, sizeof(msg), mtc_query_cb, modem, NULL); } static void mtc_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_modem *modem = data; struct isi_data *isi = ofono_modem_get_data(modem); if (g_isi_msg_error(msg) < 0) return; ISI_RESOURCE_DBG(msg); g_isi_client_ind_subscribe(isi->client, MTC_STATE_INFO_IND, mtc_state_ind_cb, modem); mtc_state_query(modem); } static void mtc_shutdown_sync(struct isi_data *isi) { const uint8_t msg[] = { MTC_SHUTDOWN_SYNC_REQ, 0, 0, }; g_isi_client_send(isi->client, msg, sizeof(msg), NULL, NULL, NULL); } static gboolean mtc_power_off_poll(gpointer user) { struct isi_data *isi = user; isi->timeout = 0; if (isi->power_state == POWER_STATE_ON_STARTED || isi->power_state == POWER_STATE_OFF || isi->power_state == POWER_STATE_OFF_WAITING) return FALSE; mtc_shutdown_sync(isi); isi->timeout = g_timeout_add(200, mtc_power_off_poll, user); return FALSE; } static void mtc_power_off_cb(const GIsiMessage *msg, void *data) { struct isi_data *isi = data; if (!check_response_status(msg, MTC_POWER_OFF_RESP)) { if (isi->power_state == POWER_STATE_OFF_STARTED) mtc_power_off(isi); return; } /* power off poll is started by mtc_state_ind_cb() */ } static void mtc_power_off(struct isi_data *isi) { const uint8_t msg[] = { MTC_POWER_OFF_REQ, 0, 0, }; g_isi_client_send(isi->client, msg, sizeof(msg), mtc_power_off_cb, isi, NULL); } static void n900_power_cb(enum power_state state, void *data) { struct ofono_modem *modem = data; struct isi_data *isi = ofono_modem_get_data(modem); DBG("power state %s", gpio_power_state_name(state)); isi->power_state = state; if (state == POWER_STATE_OFF_STARTED) mtc_power_off(isi); else if (isi->timeout) g_source_remove(isi->timeout); if (state == POWER_STATE_ON) g_isi_client_verify(isi->client, mtc_reachable_cb, modem, NULL); else if (isi->enabled) /* If enabled, report modem crash */ set_power_by_mtc_state(modem, isi, MTC_STATE_NONE); else if (state == POWER_STATE_OFF || state == POWER_STATE_ON_FAILED) /* If being disabled, report powered off only when safe */ report_powered(modem, isi, 0); else isi->mtc_state = MTC_STATE_NONE; } static int n900_probe(struct ofono_modem *modem) { char const *ifname = ofono_modem_get_string(modem, "Interface"); unsigned address = ofono_modem_get_integer(modem, "Address"); struct isi_data *isi = NULL; GIsiModem *isimodem; GIsiClient *client; if (!ifname) return -EINVAL; DBG("(%p) with %s", modem, ifname); isimodem = g_isi_modem_create_by_name(ifname); if (isimodem == NULL) { DBG("Interface=%s: %s", ifname, strerror(errno)); return -errno; } g_isi_modem_set_userdata(isimodem, modem); g_isi_modem_set_flags(isimodem, GISI_MODEM_FLAG_USE_LEGACY_SUBSCRIBE); if (getenv("OFONO_ISI_DEBUG")) g_isi_modem_set_debug(isimodem, ofono_debug); if (getenv("OFONO_ISI_TRACE")) g_isi_modem_set_trace(isimodem, isi_trace); if (gpio_probe(isimodem, address, n900_power_cb, modem) != 0) { DBG("gpio for %s: %s", ifname, strerror(errno)); goto error; } isi = g_try_new0(struct isi_data, 1); if (isi == NULL) { errno = ENOMEM; goto error; } client = g_isi_client_create(isimodem, PN_MTC); if (!client) goto error; isi->modem = isimodem; isi->ifname = ifname; isi->client = client; ofono_modem_set_data(modem, isi); return 0; error: g_isi_modem_destroy(isimodem); gpio_remove(modem); g_free(isi); return -errno; } static void n900_remove(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); ofono_modem_set_data(modem, NULL); if (!isi) return; gpio_remove(modem); if (isi->timeout) g_source_remove(isi->timeout); g_isi_client_destroy(isi->client); g_isi_modem_destroy(isi->modem); g_free(isi); } static void mtc_state_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; struct ofono_modem *modem = cbd->user; ofono_modem_online_cb_t cb = cbd->cb; struct isi_data *isi = ofono_modem_get_data(modem); uint8_t cause; if (!check_response_status(msg, MTC_STATE_RESP)) goto error; if (!g_isi_msg_data_get_byte(msg, 0, &cause)) goto error; DBG("MTC cause: %s (0x%02X)", mtc_isi_cause_name(cause), cause); if (cause == MTC_OK) { isi->online_cbd = cbd; return; } if (cause == MTC_ALREADY_ACTIVE) { CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); return; } error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void n900_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *data) { struct isi_data *isi = ofono_modem_get_data(modem); struct isi_cb_data *cbd = isi_cb_data_new(modem, cb, data); const uint8_t req[] = { MTC_STATE_REQ, online ? MTC_NORMAL : MTC_RF_INACTIVE, 0 }; DBG("(%p) with %s", modem, isi->ifname); if (cbd == NULL || isi == NULL) goto error; if (isi->power_state != POWER_STATE_ON) goto error; if (isi->mtc_state == MTC_SELFTEST_FAIL) goto error; if (g_isi_client_send_with_timeout(isi->client, req, sizeof(req), MTC_STATE_REQ_TIMEOUT, mtc_state_cb, cbd, NULL)) { isi->online = online; return; } error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void n900_pre_sim(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); DBG("(%p) with %s", modem, isi->ifname); isi->infoserver = isi_infoserver_create(modem, isi->modem); ofono_sim_create(modem, 0, "isimodem", isi->modem); ofono_devinfo_create(modem, 0, "isimodem", isi->modem); ofono_voicecall_create(modem, 0, "isimodem", isi->modem); ofono_audio_settings_create(modem, 0, "isimodem", isi->modem); } static void n900_post_sim(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); DBG("(%p) with %s", modem, isi->ifname); ofono_phonebook_create(modem, 0, "isimodem", isi->modem); ofono_call_forwarding_create(modem, 0, "isimodem", isi->modem); ofono_radio_settings_create(modem, 0, "isimodem", isi->modem); } static void n900_post_online(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); DBG("(%p) with %s", modem, isi->ifname); ofono_netreg_create(modem, 0, "isimodem", isi->modem); ofono_sms_create(modem, 0, "isimodem", isi->modem); ofono_cbs_create(modem, 0, "isimodem", isi->modem); ofono_ussd_create(modem, 0, "isimodem", isi->modem); ofono_call_settings_create(modem, 0, "isimodem", isi->modem); ofono_call_barring_create(modem, 0, "isimodem", isi->modem); ofono_call_meter_create(modem, 0, "isimodem", isi->modem); ofono_gprs_create(modem, 0, "isimodem", isi->modem); } static int n900_enable(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); DBG("modem=%p with %p", modem, isi ? isi->ifname : NULL); isi->enabled = TRUE; return gpio_enable(modem); } static int n900_disable(struct ofono_modem *modem) { struct isi_data *isi = ofono_modem_get_data(modem); DBG("modem=%p with %p", modem, isi ? isi->ifname : NULL); isi->enabled = FALSE; return gpio_disable(modem); } static struct ofono_modem_driver n900_driver = { .name = "n900", .probe = n900_probe, .remove = n900_remove, .enable = n900_enable, .disable = n900_disable, .set_online = n900_set_online, .pre_sim = n900_pre_sim, .post_sim = n900_post_sim, .post_online = n900_post_online, }; static int n900_init(void) { return ofono_modem_driver_register(&n900_driver); } static void n900_exit(void) { ofono_modem_driver_unregister(&n900_driver); } OFONO_PLUGIN_DEFINE(n900, "Nokia N900 modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, n900_init, n900_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/infineon.c0000644000015600001650000000375612671500024022044 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL-based devices: infineon modems * * Copyright (C) 2014 Canonical 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. * * 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 #endif #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include "ofono.h" #include "drivers/rilmodem/vendor.h" #include "ril.h" static int inf_probe(struct ofono_modem *modem) { return ril_create(modem, OFONO_RIL_VENDOR_INFINEON); } static struct ofono_modem_driver infineon_driver = { .name = "infineon", .probe = inf_probe, .remove = ril_remove, .enable = ril_enable, .disable = ril_disable, .pre_sim = ril_pre_sim, .post_sim = ril_post_sim, .post_online = ril_post_online, .set_online = ril_set_online, }; /* * This plugin is a device plugin for infineon modems that use RIL interface. * The plugin 'rildev' is used to determine which RIL plugin should be loaded * based upon an environment variable. */ static int inf_init(void) { int retval = 0; retval = ofono_modem_driver_register(&infineon_driver); if (retval != 0) DBG("ofono_modem_driver_register returned: %d", retval); return retval; } static void inf_exit(void) { DBG(""); ofono_modem_driver_unregister(&infineon_driver); } OFONO_PLUGIN_DEFINE(infineon, "Infineon modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, inf_init, inf_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/smshistory.c0000644000015600001650000000746612671500024022465 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2013 Jolla Ltd. All rights reserved. * Copyright (C) 2013 Canonical 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. * * 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 #endif #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "ofono.h" #include "common.h" static const char msg_prefix[] = "/message_"; static int sms_history_probe(struct ofono_history_context *context) { ofono_debug("SMS History Probe for modem: %p", context->modem); return 0; } static void sms_history_remove(struct ofono_history_context *context) { ofono_debug("SMS History Remove for modem: %p", context->modem); } static void sms_history_sms_send_status( struct ofono_history_context *context, const struct ofono_uuid *uuid, time_t when, enum ofono_history_sms_status s) { if ((s == OFONO_HISTORY_SMS_STATUS_DELIVERED) || (s == OFONO_HISTORY_SMS_STATUS_DELIVER_FAILED)) { DBusMessage *signal; DBusMessageIter iter; DBusMessageIter dict; const char *uuid_str; char *msg_uuid_str; size_t msg_len; struct ofono_atom *atom; const char *path; DBusConnection *conn; int delivered; atom = __ofono_modem_find_atom(context->modem, OFONO_ATOM_TYPE_SMS); if (atom == NULL) return; path = __ofono_atom_get_path(atom); if (path == NULL) return; conn = ofono_dbus_get_connection(); if (conn == NULL) return; delivered = (s == OFONO_HISTORY_SMS_STATUS_DELIVERED); uuid_str = ofono_uuid_to_str(uuid); /* sizeof adds extra space for one '\0' */ msg_len = strlen(path) + sizeof(msg_prefix) + strlen(uuid_str); msg_uuid_str = g_try_malloc(msg_len); if (msg_uuid_str == NULL) return; /* modem path + msg_prefix + UUID as string */ snprintf(msg_uuid_str, msg_len, "%s%s%s", path, msg_prefix, uuid_str); DBG("SMS %s delivery success: %d", msg_uuid_str, delivered); signal = dbus_message_new_signal(path, OFONO_MESSAGE_MANAGER_INTERFACE, "StatusReport"); if (signal != NULL) { dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &msg_uuid_str); dbus_message_iter_open_container( &iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "Delivered", DBUS_TYPE_BOOLEAN, &delivered); dbus_message_iter_close_container(&iter, &dict); g_dbus_send_message(conn, signal); } g_free(msg_uuid_str); } } static struct ofono_history_driver smshistory_driver = { .name = "SMS History", .probe = sms_history_probe, .remove = sms_history_remove, .sms_send_status = sms_history_sms_send_status, }; static int sms_history_init(void) { DBG(""); return ofono_history_driver_register(&smshistory_driver); } static void sms_history_exit(void) { DBG(""); ofono_history_driver_unregister(&smshistory_driver); } OFONO_PLUGIN_DEFINE(smshistory, "SMS History Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, sms_history_init, sms_history_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/alcatel.c0000644000015600001650000001437412671500024021642 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include struct alcatel_data { GAtChat *modem; GAtChat *aux; gboolean have_sim; struct at_util_sim_state_query *sim_state_query; }; static int alcatel_probe(struct ofono_modem *modem) { struct alcatel_data *data; DBG("%p", modem); data = g_try_new0(struct alcatel_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void alcatel_remove(struct ofono_modem *modem) { struct alcatel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup potential SIM state polling */ at_util_sim_state_query_free(data->sim_state_query); /* Cleanup after hot-unplug */ g_at_chat_unref(data->aux); g_free(data); } static void alcatel_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GIOChannel *channel; GAtSyntax *syntax; GAtChat *chat; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, alcatel_debug, debug); return chat; } static void sim_state_cb(gboolean present, gpointer user_data) { struct ofono_modem *modem = user_data; struct alcatel_data *data = ofono_modem_get_data(modem); at_util_sim_state_query_free(data->sim_state_query); data->sim_state_query = NULL; data->have_sim = present; ofono_modem_set_powered(modem, TRUE); /* AT&C0 needs to be send separate and on both channel */ g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT&C0", NULL, NULL, NULL, NULL); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct alcatel_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_unref(data->aux); data->aux = NULL; ofono_modem_set_powered(modem, FALSE); return; } data->sim_state_query = at_util_sim_state_query_new(data->aux, 2, 20, sim_state_cb, modem, NULL); } static int alcatel_enable(struct ofono_modem *modem) { struct alcatel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->aux = open_device(modem, "Aux", "Aux: "); if (data->aux == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_send(data->modem, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CFUN=1", NULL, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct alcatel_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->aux); data->aux = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int alcatel_disable(struct ofono_modem *modem) { struct alcatel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->aux); g_at_chat_unregister_all(data->aux); g_at_chat_send(data->aux, "AT+CFUN=0", NULL, cfun_disable, modem, NULL); return -EINPROGRESS; } static void alcatel_pre_sim(struct ofono_modem *modem) { struct alcatel_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->aux); sim = ofono_sim_create(modem, OFONO_VENDOR_ALCATEL, "atmodem", data->aux); if (sim && data->have_sim == TRUE) ofono_sim_inserted_notify(sim, TRUE); } static void alcatel_post_sim(struct ofono_modem *modem) { struct alcatel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_phonebook_create(modem, 0, "atmodem", data->aux); } static void alcatel_post_online(struct ofono_modem *modem) { struct alcatel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_netreg_create(modem, 0, "atmodem", data->aux); } static struct ofono_modem_driver alcatel_driver = { .name = "alcatel", .probe = alcatel_probe, .remove = alcatel_remove, .enable = alcatel_enable, .disable = alcatel_disable, .pre_sim = alcatel_pre_sim, .post_sim = alcatel_post_sim, .post_online = alcatel_post_online, }; static int alcatel_init(void) { return ofono_modem_driver_register(&alcatel_driver); } static void alcatel_exit(void) { ofono_modem_driver_unregister(&alcatel_driver); } OFONO_PLUGIN_DEFINE(alcatel, "Alcatel modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, alcatel_init, alcatel_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/cdma-provision.c0000644000015600001650000000374112671500024023163 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include "mbpi.h" static int cdma_provision_get_provider_name(const char *sid, char **name) { GError *error = NULL; DBG("Search provider name for SID %s", sid); *name = mbpi_lookup_cdma_provider_name(sid, &error); if (*name == NULL) { if (error != NULL) { ofono_error("%s", error->message); g_error_free(error); } return -ENOENT; } DBG("Found provider name: %s", *name); return 0; } static struct ofono_cdma_provision_driver provision_driver = { .name = "CDMA provisioning", .get_provider_name = cdma_provision_get_provider_name }; static int cdma_provision_init(void) { return ofono_cdma_provision_driver_register(&provision_driver); } static void cdma_provision_exit(void) { ofono_cdma_provision_driver_unregister(&provision_driver); } OFONO_PLUGIN_DEFINE(cdma_provision, "CDMA provisioning Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, cdma_provision_init, cdma_provision_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/ofono.rules0000644000015600001650000000170112671500024022253 0ustar pbuserpbgroup00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change", GOTO="ofono_end" # ISI/Phonet drivers SUBSYSTEM!="net", GOTO="ofono_isi_end" ATTRS{type}!="820", GOTO="ofono_isi_end" KERNELS=="gadget", GOTO="ofono_isi_end" # Nokia N900 modem SUBSYSTEMS=="hsi", ENV{OFONO_DRIVER}="n900", ENV{OFONO_ISI_ADDRESS}="108" KERNEL=="phonet*", ENV{OFONO_DRIVER}="n900", ENV{OFONO_ISI_ADDRESS}="108" # STE u8500 KERNEL=="shrm0", ENV{OFONO_DRIVER}="u8500" LABEL="ofono_isi_end" SUBSYSTEM!="usb", GOTO="ofono_end" ENV{DEVTYPE}!="usb_device", GOTO="ofono_end" # Ignore fake serial number ATTRS{serial}=="1234567890ABCDEF", ENV{ID_SERIAL_SHORT}="" # Nokia CDMA Device ATTRS{idVendor}=="0421", ATTRS{idProduct}=="023e", ENV{OFONO_DRIVER}="nokiacdma" ATTRS{idVendor}=="0421", ATTRS{idProduct}=="00b6", ENV{OFONO_DRIVER}="nokiacdma" # Lenovo H5321gw 0bdb:1926 ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1926", ENV{OFONO_DRIVER}="mbm" LABEL="ofono_end" ofono-1.17.bzr6912+16.04.20160314.3/plugins/ubuntu-apndb.c0000644000015600001650000003441612671500024022640 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * 2013 Simon Busch * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include "ubuntu-apndb.h" #ifndef SYSTEM_APNDB_PATH #define SYSTEM_APNDB_PATH "/system/etc/apns-conf.xml" #define CUSTOM_APNDB_PATH "/custom/etc/apns-conf.xml" #endif struct apndb_data { const char *match_mcc; const char *match_mnc; const char *match_imsi; const char *match_spn; const char *match_gid1; GSList *apns; gboolean allow_duplicates; gboolean mvno_found; }; void ubuntu_apndb_ap_free(gpointer data) { struct apndb_provision_data *ap = data; g_free(ap->gprs_data.name); g_free(ap->gprs_data.apn); g_free(ap->gprs_data.username); g_free(ap->gprs_data.password); g_free(ap->gprs_data.message_proxy); g_free(ap->gprs_data.message_center); g_free(ap); } static gboolean imsi_match(const char *imsi, const char *match) { gboolean result = FALSE; size_t imsi_len = strlen(imsi); size_t match_len = strlen(match); unsigned int i; DBG("imsi %s match %s", imsi, match); if (match_len == 0 || imsi_len < match_len) goto done; for (i = 0; i < match_len; i++) { if (*(imsi + i) == *(match + i)) continue; else if (*(match + i) == 'x') continue; else if (*(match + i) == 'X') continue; else goto done; } result = TRUE; done: return result; } static void strip_non_mvno_apns(GSList **apns) { GSList *l = NULL; unsigned int ap_count = g_slist_length(*apns); struct apndb_provision_data *ap; DBG(""); for (l = *apns; l;) { ap = l->data; l = l->next; if (ap->mvno == FALSE) { DBG("Removing: %s", ap->gprs_data.apn); *apns = g_slist_remove(*apns, (gconstpointer) ap); ubuntu_apndb_ap_free(ap); ap_count--; } } } static GSList *merge_apn_lists(GSList *custom_apns, GSList *base_apns) { GSList *l = NULL; GSList *l2 = NULL; gboolean found = FALSE; struct apndb_provision_data *ap; DBG(""); if (custom_apns == NULL) return base_apns; for (l = custom_apns; l; l = l->next, found = FALSE) { struct apndb_provision_data *ap2 = l->data; if (ap2->gprs_data.apn == NULL) { ofono_error("%s: invalid custom apn entry - %s found", __func__, ap2->gprs_data.name); continue; } for (l2 = base_apns; l2; l2 = l2->next) { ap = l2->data; if (ap->gprs_data.apn != NULL && ap->gprs_data.type == ap2->gprs_data.type && g_strcmp0(ap2->gprs_data.apn, ap->gprs_data.apn) == 0) { found = TRUE; break; } } if (found == TRUE) { DBG("found=TRUE; removing '%s'", ap->gprs_data.apn); base_apns = g_slist_remove(base_apns, ap); ubuntu_apndb_ap_free(ap); } } custom_apns = g_slist_reverse(custom_apns); for (l = custom_apns; l; l = l->next) { struct ap2 *ap2 = l->data; base_apns = g_slist_prepend(base_apns, ap2); } g_slist_free(custom_apns); return base_apns; } static enum ofono_gprs_context_type determine_apn_type(const char *types) { /* * The database contains entries with the following type field contents: * - default * - default,mms * - default,supl * - defualt,supl,dun * - default,supl,mms * - mms */ /* Default apns can be used for mms and ia, mms can be used for ia */ if (types == NULL || g_strcmp0(types, "*") == 0 || strstr(types, "default") != NULL) return OFONO_GPRS_CONTEXT_TYPE_INTERNET; else if (strstr(types, "mms") != NULL) return OFONO_GPRS_CONTEXT_TYPE_MMS; else if (strstr(types, "ia") != NULL) return OFONO_GPRS_CONTEXT_TYPE_IA; else return OFONO_GPRS_CONTEXT_TYPE_ANY; } static char *ubuntu_apndb_sanitize_ipv4_address(const char *address) { char **numbers = NULL; char *sanitized_numbers[4]; unsigned int count = 0; char *result = NULL; char *numeral; /* * As src/gprs.c expects MMS proxies to always be * specified using IPV4 numbers-and-dot notation, * we need to strip any leading "0"s from the * individual numeric components, otherwise they * will be treated as octal numbers * ( see 'man inet_aton' for details ). */ if (g_ascii_isdigit(*address) == FALSE) goto done; numbers = g_strsplit(address, ".", 4); for (; (numeral = *(numbers+count)); count++) { if (count > 3) goto done; for (; *numeral; numeral++) { if (g_ascii_isdigit(*numeral) == FALSE) goto done; else if (*numeral == '0') continue; else break; } if (*numeral) sanitized_numbers[count] = numeral; else sanitized_numbers[count] = "0"; } if (count != 4) goto done; result = g_strdup_printf("%s.%s.%s.%s", sanitized_numbers[0], sanitized_numbers[1], sanitized_numbers[2], sanitized_numbers[3]); done: if (numbers != NULL) g_strfreev(numbers); return result; } static void toplevel_apndb_start(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer userdata, GError **error) { struct apndb_data *apndb = userdata; struct apndb_provision_data *ap = NULL; int i; const gchar *carrier = NULL; const gchar *mcc = NULL; const gchar *mnc = NULL; const gchar *apn = NULL; const gchar *username = NULL; const gchar *password = NULL; const gchar *types = NULL; const gchar *protocol = NULL; const gchar *mmsproxy = NULL; const gchar *mmsport = NULL; const gchar *mmscenter = NULL; const gchar *mvnomatch = NULL; const gchar *mvnotype = NULL; enum ofono_gprs_proto proto = OFONO_GPRS_PROTO_IP; enum ofono_gprs_context_type type; if (g_strcmp0(element_name, "apn") != 0) return; for (i = 0; attribute_names[i]; i++) { if (g_strcmp0(attribute_names[i], "carrier") == 0) carrier = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "mcc") == 0) mcc = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "mnc") == 0) mnc = attribute_values[i]; } if (mcc == NULL) { ofono_error("%s: apn for %s missing 'mcc' attribute", __func__, carrier); return; } if (mnc == NULL) { ofono_error("%s: apn for %s missing 'mnc' attribute", __func__, carrier); return; } if (g_strcmp0(mcc, apndb->match_mcc) != 0 || g_strcmp0(mnc, apndb->match_mnc) != 0) return; for (i = 0; attribute_names[i]; i++) { if (g_strcmp0(attribute_names[i], "apn") == 0) apn = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "user") == 0) username = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "password") == 0) password = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "type") == 0) types = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "protocol") == 0) protocol = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "mmsc") == 0) mmscenter = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "mmsproxy") == 0) mmsproxy = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "mmsport") == 0) mmsport = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "mvno_match_data") == 0) mvnomatch = attribute_values[i]; else if (g_strcmp0(attribute_names[i], "mvno_type") == 0) mvnotype = attribute_values[i]; } if (apn == NULL) { ofono_error("%s: apn for %s missing 'apn' attribute", __func__, carrier); return; } if (protocol != NULL) { if (g_strcmp0(protocol, "IP") == 0) { proto = OFONO_GPRS_PROTO_IP; } else if (g_strcmp0(protocol, "IPV6") == 0) { /* TODO: Use OFONO_GPRS_PROTO_IPV6 when supported */ proto = OFONO_GPRS_PROTO_IP; } else if (g_strcmp0(protocol, "IPV4V6") == 0) { /* TODO: Use OFONO_GPRS_PROTO_IPV4V6 when supported */ proto = OFONO_GPRS_PROTO_IP; } else { ofono_error("%s: APN %s has invalid protocol=%s" "attribute", __func__, carrier, protocol); return; } } if (mvnotype != NULL && mvnomatch != NULL) { if (g_strcmp0(mvnotype, "imsi") == 0) { DBG("APN %s is mvno_type 'imsi'", carrier); if (apndb->match_imsi == NULL || imsi_match(apndb->match_imsi, mvnomatch) == FALSE) { DBG("Skipping MVNO 'imsi' APN %s with" " match_data: %s", carrier, mvnomatch); return; } } else if (g_strcmp0(mvnotype, "spn") == 0) { DBG("APN %s is mvno_type 'spn'", carrier); if (g_strcmp0(mvnomatch, apndb->match_spn) != 0) { DBG("Skipping mvno 'spn' APN %s with" " match_data: %s", carrier, mvnomatch); return; } } else if (g_strcmp0(mvnotype, "gid") == 0) { int match_len = strlen(mvnomatch); DBG("APN %s is mvno_type 'gid'", carrier); /* Check initial part of GID1 against match data */ if (apndb->match_gid1 == NULL || g_ascii_strncasecmp(mvnomatch, apndb->match_gid1, match_len) != 0) { DBG("Skipping mvno 'gid' APN %s with" " match_data: %s", carrier, mvnomatch); return; } } } type = determine_apn_type(types); if (type == OFONO_GPRS_CONTEXT_TYPE_ANY || (type == OFONO_GPRS_CONTEXT_TYPE_MMS && mmscenter == NULL)) { DBG("Skipping %s context; types: %s", apn, types ? types : "(null)"); return; } ap = g_try_new0(struct apndb_provision_data, 1); if (ap == NULL) { ofono_error("%s: out-of-memory trying to provision APN - %s", __func__, carrier); return; } ap->gprs_data.type = type; if (carrier != NULL) ap->gprs_data.name = g_strdup(carrier); if (apn != NULL) ap->gprs_data.apn = g_strdup(apn); if (username != NULL) ap->gprs_data.username = g_strdup(username); if (password != NULL) ap->gprs_data.password = g_strdup(password); if (mmscenter != NULL && strlen(mmscenter) > 0) ap->gprs_data.message_center = g_strdup(mmscenter); if (mmsproxy != NULL && strlen(mmsproxy) > 0) { char *tmp = ubuntu_apndb_sanitize_ipv4_address(mmsproxy); if (tmp != NULL) mmsproxy = tmp; if (mmsport != NULL) ap->gprs_data.message_proxy = g_strdup_printf("%s:%s", mmsproxy, mmsport); else ap->gprs_data.message_proxy = g_strdup(mmsproxy); g_free(tmp); } ap->gprs_data.proto = proto; if (mvnotype != NULL) { ap->mvno = TRUE; apndb->mvno_found = TRUE; } apndb->apns = g_slist_append(apndb->apns, ap); } static void toplevel_apndb_end(GMarkupParseContext *context, const gchar *element_name, gpointer userdata, GError **error) { } static const GMarkupParser toplevel_apndb_parser = { toplevel_apndb_start, toplevel_apndb_end, NULL, NULL, NULL, }; static gboolean ubuntu_apndb_parse(const GMarkupParser *parser, gpointer userdata, const char *apndb_path, GError **error) { struct stat st; char *db; int fd; GMarkupParseContext *context; gboolean ret; fd = open(apndb_path, O_RDONLY); if (fd < 0) { g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "open(%s) failed: %s", apndb_path, g_strerror(errno)); return FALSE; } if (fstat(fd, &st) < 0) { close(fd); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "fstat(%s) failed: %s", apndb_path, g_strerror(errno)); return FALSE; } db = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (db == MAP_FAILED) { close(fd); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "mmap(%s) failed: %s", apndb_path, g_strerror(errno)); return FALSE; } context = g_markup_parse_context_new(parser, G_MARKUP_TREAT_CDATA_AS_TEXT, userdata, NULL); ret = g_markup_parse_context_parse(context, db, st.st_size, error); if (ret == TRUE) g_markup_parse_context_end_parse(context, error); munmap(db, st.st_size); close(fd); g_markup_parse_context_free(context); return ret; } GSList *ubuntu_apndb_lookup_apn(const char *mcc, const char *mnc, const char *spn, const char *imsi, const char *gid1, GError **error) { struct apndb_data apndb = { NULL }; struct apndb_data custom_apndb = { NULL }; const char *apndb_path; GSList *merged_apns; /* * Lookup /custom apns first, if mvno apns found, * strip non-mvno apns from list * * Lookup /system next, apply same mvno logic... * * Merge both lists, any custom apns that match the type * and apn fields of a /system apn replace it. */ custom_apndb.match_mcc = mcc; custom_apndb.match_mnc = mnc; custom_apndb.match_spn = spn; custom_apndb.match_imsi = imsi; custom_apndb.match_gid1 = gid1; apndb_path = getenv("OFONO_CUSTOM_APNDB_PATH"); if (apndb_path == NULL) apndb_path = CUSTOM_APNDB_PATH; if (ubuntu_apndb_parse(&toplevel_apndb_parser, &custom_apndb, apndb_path, error) == FALSE) { g_slist_free_full(custom_apndb.apns, ubuntu_apndb_ap_free); custom_apndb.apns = NULL; if (*error) { if ((*error)->domain != G_FILE_ERROR) ofono_error("%s: custom apn_lookup error -%s", __func__, (*error)->message); g_error_free(*error); *error = NULL; } } if (custom_apndb.apns && custom_apndb.mvno_found) strip_non_mvno_apns(&custom_apndb.apns); DBG("custom_apndb: found '%d' APNs", g_slist_length(custom_apndb.apns)); apndb.match_mcc = mcc; apndb.match_mnc = mnc; apndb.match_spn = spn; apndb.match_imsi = imsi; apndb.match_gid1 = gid1; apndb_path = getenv("OFONO_SYSTEM_APNDB_PATH"); if (apndb_path == NULL) apndb_path = SYSTEM_APNDB_PATH; if (ubuntu_apndb_parse(&toplevel_apndb_parser, &apndb, apndb_path, error) == FALSE) { g_slist_free_full(apndb.apns, ubuntu_apndb_ap_free); apndb.apns = NULL; } DBG("apndb: found '%d' APNs", g_slist_length(apndb.apns)); if (apndb.apns && apndb.mvno_found) strip_non_mvno_apns(&apndb.apns); merged_apns = merge_apn_lists(custom_apndb.apns, apndb.apns); return merged_apns; } ofono-1.17.bzr6912+16.04.20160314.3/plugins/ofono-speedup.rules0000644000015600001650000000252712671500024023725 0ustar pbuserpbgroup00000000000000# do not edit this file, it will be overwritten on update ACTION!="add|change", GOTO="ofono_speedup_end" SUBSYSTEM!="tty", GOTO="ofono_speedup_end" KERNEL!="ttyUSB[0-9]*", GOTO="ofono_speedup_end" # SpeedUp 7300 ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9e00", ENV{ID_USB_INTERFACE_NUM}=="00", ENV{OFONO_LABEL}="modem" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9e00", ENV{ID_USB_INTERFACE_NUM}=="03", ENV{OFONO_LABEL}="aux" # SpeedUp ATTRS{idVendor}=="2020", ATTRS{idProduct}=="1005", ENV{ID_USB_INTERFACE_NUM}=="03", ENV{OFONO_LABEL}="modem" ATTRS{idVendor}=="2020", ATTRS{idProduct}=="1005", ENV{ID_USB_INTERFACE_NUM}=="01", ENV{OFONO_LABEL}="aux" ATTRS{idVendor}=="2020", ATTRS{idProduct}=="1008", ENV{ID_USB_INTERFACE_NUM}=="03", ENV{OFONO_LABEL}="modem" ATTRS{idVendor}=="2020", ATTRS{idProduct}=="1008", ENV{ID_USB_INTERFACE_NUM}=="01", ENV{OFONO_LABEL}="aux" # SpeedUp 9800 ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9800", ENV{ID_USB_INTERFACE_NUM}=="01", ENV{OFONO_LABEL}="modem" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9800", ENV{ID_USB_INTERFACE_NUM}=="02", ENV{OFONO_LABEL}="aux" # SpeedUp U3501 ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{ID_USB_INTERFACE_NUM}=="03", ENV{OFONO_LABEL}="modem" ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{ID_USB_INTERFACE_NUM}=="01", ENV{OFONO_LABEL}="aux" LABEL="ofono_speedup_end" ofono-1.17.bzr6912+16.04.20160314.3/plugins/huawei.c0000644000015600001650000005154112671500024021514 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; static const char *gcap_prefix[] = { "+GCAP:", NULL }; static const char *rfswitch_prefix[] = { "^RFSWITCH:", NULL }; static const char *sysinfo_prefix[] = { "^SYSINFO:", NULL }; static const char *ussdmode_prefix[] = { "^USSDMODE:", NULL }; static const char *dialmode_prefix[] = { "^DIALMODE:", NULL }; static const char *cvoice_prefix[] = { "^CVOICE:", NULL }; enum { SIM_STATE_INVALID_OR_LOCKED = 0, SIM_STATE_VALID = 1, SIM_STATE_INVALID_CS = 2, SIM_STATE_INVALID_PS = 3, SIM_STATE_INVALID_PS_AND_CS = 4, SIM_STATE_ROMSIM = 240, SIM_STATE_NOT_EXISTENT = 255, }; struct huawei_data { GAtChat *modem; GAtChat *pcui; gboolean have_sim; int sim_state; guint sysinfo_poll_source; guint sysinfo_poll_count; struct cb_data *online_cbd; const char *offline_command; gboolean have_voice; gboolean have_gsm; gboolean have_cdma; gboolean have_ndis; gboolean have_ussdmode; }; static int huawei_probe(struct ofono_modem *modem) { struct huawei_data *data; DBG("%p", modem); data = g_try_new0(struct huawei_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void huawei_remove(struct ofono_modem *modem) { struct huawei_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup after potential enable polling */ if (data->sysinfo_poll_source > 0) g_source_remove(data->sysinfo_poll_source); /* Cleanup after hot-unplug */ g_at_chat_unref(data->pcui); g_free(data); } static void huawei_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static void ussdmode_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct huawei_data *data = user_data; GAtResultIter iter; gint ussdmode; if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^USSDMODE:")) return; if (!g_at_result_iter_next_number(&iter, &ussdmode)) return; if (ussdmode == 1) data->have_ussdmode = TRUE; } static void ussdmode_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct huawei_data *data = user_data; GAtResultIter iter; if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^USSDMODE:")) return; /* Query current USSD mode */ g_at_chat_send(data->pcui, "AT^USSDMODE?", ussdmode_prefix, ussdmode_query_cb, data, NULL); } static void dialmode_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct huawei_data *data = user_data; GAtResultIter iter; gint dialmode, cdc_spec; const char *str = "unknown"; if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^DIALMODE:")) return; if (!g_at_result_iter_next_number(&iter, &dialmode)) return; if (g_at_result_iter_next_number(&iter, &cdc_spec)) { switch (cdc_spec) { case 0: str = "none"; break; case 1: str = "Modem port"; break; case 2: str = "NDIS port"; break; case 3: str = "Modem and NDIS port"; break; } } switch (dialmode) { case 0: ofono_info("Modem support (CDC support: %s)", str); data->have_ndis = FALSE; break; case 1: ofono_info("NDIS support (CDC support: %s)", str); data->have_ndis = TRUE; break; case 2: ofono_info("Modem and NDIS support (CDC support: %s)", str); data->have_ndis = TRUE; break; } } static void dialmode_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct huawei_data *data = user_data; GAtResultIter iter; if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^DIALMODE:")) return; /* Query current NDIS mode */ g_at_chat_send(data->pcui, "AT^DIALMODE?", dialmode_prefix, dialmode_query_cb, data, NULL); } static void cvoice_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); GAtResultIter iter; gint mode, rate, bits, period; if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^CVOICE:")) return; if (!g_at_result_iter_next_number(&iter, &mode)) return; if (!g_at_result_iter_next_number(&iter, &rate)) return; if (!g_at_result_iter_next_number(&iter, &bits)) return; if (!g_at_result_iter_next_number(&iter, &period)) return; data->have_voice = TRUE; ofono_info("Voice channel: %d Hz, %d bits, %dms period", rate, bits, period); /* Check available voice ports */ g_at_chat_send(data->pcui, "AT^DDSETEX=?", none_prefix, NULL, NULL, NULL); } static void cvoice_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); GAtResultIter iter; if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^CVOICE:")) return; /* Query current voice setting */ g_at_chat_send(data->pcui, "AT^CVOICE?", cvoice_prefix, cvoice_query_cb, modem, NULL); } static void simst_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int sim_state; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^SIMST:")) return; if (!g_at_result_iter_next_number(&iter, &sim_state)) return; DBG("%d -> %d", data->sim_state, sim_state); data->sim_state = sim_state; } static gboolean parse_sysinfo_result(GAtResult *result, int *srv_status, int *srv_domain, int *sim_state) { GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^SYSINFO:")) return FALSE; if (!g_at_result_iter_next_number(&iter, srv_status)) return FALSE; if (!g_at_result_iter_next_number(&iter, srv_domain)) return FALSE; if (!g_at_result_iter_skip_next(&iter)) return FALSE; if (!g_at_result_iter_skip_next(&iter)) return FALSE; if (!g_at_result_iter_next_number(&iter, sim_state)) return FALSE; return TRUE; } static void shutdown_device(struct huawei_data *data) { g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->pcui); g_at_chat_unregister_all(data->pcui); g_at_chat_unref(data->pcui); data->pcui = NULL; } static void cfun_offline(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { shutdown_device(data); ofono_modem_set_powered(modem, FALSE); return; } ofono_modem_set_powered(modem, TRUE); } static gboolean sysinfo_enable_check(gpointer user_data); static void sysinfo_enable_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); int srv_status, srv_domain, sim_state; if (!ok) goto failure; if (parse_sysinfo_result(result, &srv_status, &srv_domain, &sim_state) == FALSE) goto failure; DBG("%d -> %d", data->sim_state, sim_state); data->sim_state = sim_state; if (sim_state == SIM_STATE_NOT_EXISTENT) { data->sysinfo_poll_count++; if (data->sysinfo_poll_count > 5) goto failure; data->sysinfo_poll_source = g_timeout_add_seconds(1, sysinfo_enable_check, modem); return; } data->have_sim = TRUE; /* Switch data carrier detect signal off */ g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL); g_at_chat_send(data->pcui, "AT&C0", NULL, NULL, NULL, NULL); /* * Ensure that the modem is using GSM character set and not IRA, * otherwise weirdness with umlauts and other non-ASCII characters * can result */ g_at_chat_send(data->modem, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->pcui, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); /* Query current device settings */ g_at_chat_send(data->pcui, "AT^U2DIAG?", none_prefix, NULL, NULL, NULL); /* Query current port settings */ g_at_chat_send(data->pcui, "AT^GETPORTMODE", none_prefix, NULL, NULL, NULL); /* Check USSD mode support */ g_at_chat_send(data->pcui, "AT^USSDMODE=?", ussdmode_prefix, ussdmode_support_cb, data, NULL); /* Check NDIS mode support */ g_at_chat_send(data->pcui, "AT^DIALMODE=?", dialmode_prefix, dialmode_support_cb, data, NULL); /* Check for voice support */ g_at_chat_send(data->pcui, "AT^CVOICE=?", cvoice_prefix, cvoice_support_cb, modem, NULL); /* For CDMA we use AlwaysOnline so we leave the modem online. */ if (data->have_gsm == FALSE && data->have_cdma == TRUE) { ofono_modem_set_boolean(modem, "AlwaysOnline", TRUE); ofono_modem_set_powered(modem, TRUE); return; } if (g_at_chat_send(data->pcui, data->offline_command, none_prefix, cfun_offline, modem, NULL) > 0) return; failure: shutdown_device(data); ofono_modem_set_powered(modem, FALSE); } static gboolean sysinfo_enable_check(gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); data->sysinfo_poll_source = 0; g_at_chat_send(data->pcui, "AT^SYSINFO", sysinfo_prefix, sysinfo_enable_cb, modem, NULL); return FALSE; } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { shutdown_device(data); ofono_modem_set_powered(modem, FALSE); return; } /* Follow sim state changes */ g_at_chat_register(data->pcui, "^SIMST:", simst_notify, FALSE, modem, NULL); data->sysinfo_poll_count = 0; sysinfo_enable_check(modem); } static void rfswitch_support(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); if (data->have_gsm == FALSE && data->have_cdma == TRUE) { data->offline_command = "AT+CFUN=5"; goto done; } if (!ok) data->offline_command = "AT+CFUN=5"; else data->offline_command = "AT+CFUN=7"; done: g_at_chat_send(data->pcui, "AT+CFUN=1", none_prefix, cfun_enable, modem, NULL); } static void gcap_support(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); GAtResultIter iter; const char *gcap; if (!ok) goto done; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+GCAP:")) goto done; while (g_at_result_iter_next_unquoted_string(&iter, &gcap)) { if (*gcap == '\0') break; if (!strcmp(gcap, "+CGSM")) data->have_gsm = TRUE; else if (!strcmp(gcap, "+CIS707-A")) data->have_cdma = TRUE; } done: g_at_chat_send(data->pcui, "AT^RFSWITCH=?", rfswitch_prefix, rfswitch_support, modem, NULL); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GIOChannel *channel; GAtSyntax *syntax; GAtChat *chat; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; g_at_chat_add_terminator(chat, "COMMAND NOT SUPPORT", -1, FALSE); g_at_chat_add_terminator(chat, "TOO MANY PARAMETERS", -1, FALSE); if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, huawei_debug, debug); return chat; } static int huawei_enable(struct ofono_modem *modem) { struct huawei_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->pcui = open_device(modem, "Pcui", "PCUI: "); if (data->pcui == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_set_slave(data->modem, data->pcui); g_at_chat_send(data->modem, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->pcui, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL); data->sim_state = SIM_STATE_NOT_EXISTENT; /* Check for GSM capabilities */ g_at_chat_send(data->pcui, "ATI", gcap_prefix, gcap_support, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->pcui); data->pcui = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int huawei_disable(struct ofono_modem *modem) { struct huawei_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->pcui); g_at_chat_unregister_all(data->pcui); /* Cleanup potential online enable polling */ if (data->sysinfo_poll_source > 0) { g_source_remove(data->sysinfo_poll_source); data->sysinfo_poll_source = 0; g_free(data->online_cbd); data->online_cbd = NULL; } g_at_chat_send(data->pcui, "AT+CFUN=0", none_prefix, cfun_disable, modem, NULL); return -EINPROGRESS; } static gboolean sysinfo_online_check(gpointer user_data); static void sysinfo_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct huawei_data *data = user_data; ofono_modem_online_cb_t cb = data->online_cbd->cb; int srv_status, srv_domain, sim_state; if (!ok) goto failure; if (parse_sysinfo_result(result, &srv_status, &srv_domain, &sim_state) == FALSE) goto failure; DBG("%d -> %d", data->sim_state, sim_state); data->sim_state = sim_state; /* Valid service status and at minimum PS domain */ if (srv_status > 0 && srv_domain > 1) { CALLBACK_WITH_SUCCESS(cb, data->online_cbd->data); goto done; } switch (sim_state) { case SIM_STATE_VALID: case SIM_STATE_INVALID_CS: case SIM_STATE_INVALID_PS: case SIM_STATE_INVALID_PS_AND_CS: case SIM_STATE_ROMSIM: CALLBACK_WITH_SUCCESS(cb, data->online_cbd->data); goto done; } data->sysinfo_poll_count++; if (data->sysinfo_poll_count > 15) goto failure; data->sysinfo_poll_source = g_timeout_add_seconds(2, sysinfo_online_check, data); return; failure: CALLBACK_WITH_FAILURE(cb, data->online_cbd->data); done: g_free(data->online_cbd); data->online_cbd = NULL; } static gboolean sysinfo_online_check(gpointer user_data) { struct huawei_data *data = user_data; data->sysinfo_poll_source = 0; g_at_chat_send(data->pcui, "AT^SYSINFO", sysinfo_prefix, sysinfo_online_cb, data, NULL); return FALSE; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct huawei_data *data = ofono_modem_get_data(modem); if (!ok) { ofono_modem_online_cb_t cb = data->online_cbd->cb; CALLBACK_WITH_FAILURE(cb, data->online_cbd->data); g_free(data->online_cbd); data->online_cbd = NULL; return; } data->sysinfo_poll_count = 0; sysinfo_online_check(data); } static void set_offline_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void huawei_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct huawei_data *data = ofono_modem_get_data(modem); DBG("modem %p %s", modem, online ? "online" : "offline"); if (online == TRUE) { data->online_cbd = cb_data_new(cb, user_data); if (g_at_chat_send(data->pcui, "AT+CFUN=1", none_prefix, set_online_cb, modem, NULL) > 0) return; g_free(data->online_cbd); data->online_cbd = NULL; } else { struct cb_data *cbd = cb_data_new(cb, user_data); if (g_at_chat_send(data->pcui, data->offline_command, none_prefix, set_offline_cb, cbd, g_free) > 0) return; g_free(cbd); } CALLBACK_WITH_FAILURE(cb, user_data); } static void huawei_pre_sim(struct ofono_modem *modem) { struct huawei_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim = NULL; DBG("%p", modem); if (data->have_gsm == TRUE) { ofono_devinfo_create(modem, 0, "atmodem", data->pcui); sim = ofono_sim_create(modem, OFONO_VENDOR_HUAWEI, "atmodem", data->pcui); } else if (data->have_cdma == TRUE) { ofono_devinfo_create(modem, 0, "cdmamodem", data->pcui); /* Create SIM atom only if SIM is not embedded */ if (data->sim_state != SIM_STATE_ROMSIM) sim = ofono_sim_create(modem, OFONO_VENDOR_HUAWEI, "atmodem-noef", data->pcui); } if (sim && data->have_sim == TRUE) ofono_sim_inserted_notify(sim, TRUE); } static void huawei_post_sim(struct ofono_modem *modem) { struct huawei_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (data->have_voice == TRUE) { ofono_voicecall_create(modem, 0, "huaweimodem", data->pcui); ofono_audio_settings_create(modem, 0, "huaweimodem", data->pcui); } if (data->have_gsm == TRUE) { struct ofono_gprs *gprs; struct ofono_gprs_context *gc; ofono_phonebook_create(modem, 0, "atmodem", data->pcui); ofono_radio_settings_create(modem, 0, "huaweimodem", data->pcui); ofono_sms_create(modem, OFONO_VENDOR_HUAWEI, "atmodem", data->pcui); gprs = ofono_gprs_create(modem, OFONO_VENDOR_HUAWEI, "atmodem", data->pcui); gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } } static void huawei_post_online(struct ofono_modem *modem) { struct huawei_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (data->have_gsm == TRUE) { ofono_netreg_create(modem, OFONO_VENDOR_HUAWEI, "atmodem", data->pcui); ofono_cbs_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->pcui); if (data->have_ussdmode == TRUE) ofono_ussd_create(modem, 0, "huaweimodem", data->pcui); else ofono_ussd_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", data->pcui); } else if (data->have_cdma == TRUE) { ofono_cdma_netreg_create(modem, 0, "huaweimodem", data->pcui); ofono_cdma_connman_create(modem, OFONO_VENDOR_HUAWEI, "cdmamodem", data->modem); } if (data->have_voice == TRUE) { struct ofono_message_waiting *mw; ofono_call_forwarding_create(modem, 0, "atmodem", data->pcui); ofono_call_settings_create(modem, 0, "atmodem", data->pcui); ofono_call_barring_create(modem, 0, "atmodem", data->pcui); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } } static struct ofono_modem_driver huawei_driver = { .name = "huawei", .probe = huawei_probe, .remove = huawei_remove, .enable = huawei_enable, .disable = huawei_disable, .set_online = huawei_set_online, .pre_sim = huawei_pre_sim, .post_sim = huawei_post_sim, .post_online = huawei_post_online, }; static int huawei_init(void) { return ofono_modem_driver_register(&huawei_driver); } static void huawei_exit(void) { ofono_modem_driver_unregister(&huawei_driver); } OFONO_PLUGIN_DEFINE(huawei, "HUAWEI Mobile modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, huawei_init, huawei_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/g1.c0000644000015600001650000001167012671500024020540 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2009 Collabora Ltd. 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void g1_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } /* Detect hardware, and initialize if found */ static int g1_probe(struct ofono_modem *modem) { DBG(""); return 0; } static void g1_remove(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); DBG(""); if (chat) { g_at_chat_unref(chat); ofono_modem_set_data(modem, NULL); } } static void cfun_set_on_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; DBG(""); if (ok) ofono_modem_set_powered(modem, TRUE); } /* power up hardware */ static int g1_enable(struct ofono_modem *modem) { GAtSyntax *syntax; GIOChannel *channel; GAtChat *chat; const char *device; DBG(""); device = ofono_modem_get_string(modem, "Device"); if (device == NULL) return -EINVAL; channel = g_at_tty_open(device, NULL); if (channel == NULL) return -EIO; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_io_channel_unref(channel); g_at_syntax_unref(syntax); if (chat == NULL) return -EIO; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, g1_debug, ""); ofono_modem_set_data(modem, chat); /* ensure modem is in a known state; verbose on, echo/quiet off */ g_at_chat_send(chat, "ATE0Q0V1", NULL, NULL, NULL, NULL); /* power up modem */ g_at_chat_send(chat, "AT+CFUN=1", NULL, cfun_set_on_cb, modem, NULL); return 0; } static void cfun_set_off_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; GAtChat *chat = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(chat); ofono_modem_set_data(modem, NULL); if (ok) ofono_modem_set_powered(modem, FALSE); } static int g1_disable(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); DBG(""); /* power down modem */ g_at_chat_cancel_all(chat); g_at_chat_unregister_all(chat); g_at_chat_send(chat, "AT+CFUN=0", NULL, cfun_set_off_cb, modem, NULL); return -EINPROGRESS; } static void g1_pre_sim(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG(""); ofono_devinfo_create(modem, 0, "atmodem", chat); sim = ofono_sim_create(modem, 0, "atmodem", chat); ofono_voicecall_create(modem, 0, "atmodem", chat); if (sim) ofono_sim_inserted_notify(sim, TRUE); } static void g1_post_sim(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; DBG(""); ofono_ussd_create(modem, 0, "atmodem", chat); ofono_call_forwarding_create(modem, 0, "atmodem", chat); ofono_call_settings_create(modem, 0, "atmodem", chat); ofono_netreg_create(modem, 0, "atmodem", chat); ofono_call_meter_create(modem, 0, "atmodem", chat); ofono_call_barring_create(modem, 0, "atmodem", chat); ofono_sms_create(modem, OFONO_VENDOR_QUALCOMM_MSM, "atmodem", chat); ofono_phonebook_create(modem, 0, "atmodem", chat); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } static struct ofono_modem_driver g1_driver = { .name = "g1", .probe = g1_probe, .remove = g1_remove, .enable = g1_enable, .disable = g1_disable, .pre_sim = g1_pre_sim, .post_sim = g1_post_sim, }; static int g1_init(void) { return ofono_modem_driver_register(&g1_driver); } static void g1_exit(void) { ofono_modem_driver_unregister(&g1_driver); } OFONO_PLUGIN_DEFINE(g1, "HTC G1 modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, g1_init, g1_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/sim900.c0000644000015600001650000002177312671500024021257 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUM_DLC 5 #define VOICE_DLC 0 #define NETREG_DLC 1 #define SMS_DLC 2 #define GPRS_DLC 3 #define SETUP_DLC 4 static char *dlc_prefixes[NUM_DLC] = { "Voice: ", "Net: ", "SMS: ", "GPRS: " , "Setup: "}; static const char *none_prefix[] = { NULL }; struct sim900_data { GIOChannel *device; GAtMux *mux; GAtChat * dlcs[NUM_DLC]; guint frame_size; }; static int sim900_probe(struct ofono_modem *modem) { struct sim900_data *data; DBG("%p", modem); data = g_try_new0(struct sim900_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void sim900_remove(struct ofono_modem *modem) { struct sim900_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); g_free(data); } static void sim900_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { struct sim900_data *data = ofono_modem_get_data(modem); const char *device; GAtSyntax *syntax; GIOChannel *channel; GAtChat *chat; GHashTable *options; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) return NULL; g_hash_table_insert(options, "Baud", "115200"); g_hash_table_insert(options, "Parity", "none"); g_hash_table_insert(options, "StopBits", "1"); g_hash_table_insert(options, "DataBits", "8"); g_hash_table_insert(options, "XonXoff", "off"); g_hash_table_insert(options, "Local", "off"); g_hash_table_insert(options, "RtsCts", "off"); g_hash_table_insert(options, "Read", "on"); channel = g_at_tty_open(device, options); g_hash_table_destroy(options); if (channel == NULL) return NULL; data->device = channel; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); if (chat == NULL) { g_io_channel_unref(data->device); data->device = NULL; return NULL; } if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, sim900_debug, debug); return chat; } static GAtChat *create_chat(GIOChannel *channel, struct ofono_modem *modem, char *debug) { GAtSyntax *syntax; GAtChat *chat; if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsmv1(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, sim900_debug, debug); return chat; } static void shutdown_device(struct sim900_data *data) { int i; DBG(""); for (i = 0; i < NUM_DLC; i++) { if (data->dlcs[i] == NULL) continue; g_at_chat_unref(data->dlcs[i]); data->dlcs[i] = NULL; } if (data->mux) { g_at_mux_shutdown(data->mux); g_at_mux_unref(data->mux); data->mux = NULL; } g_io_channel_unref(data->device); data->device = NULL; } static void setup_internal_mux(struct ofono_modem *modem) { struct sim900_data *data = ofono_modem_get_data(modem); int i; DBG(""); data->frame_size = 128; data->mux = g_at_mux_new_gsm0710_basic(data->device, data->frame_size); if (data->mux == NULL) goto error; if (getenv("OFONO_MUX_DEBUG")) g_at_mux_set_debug(data->mux, sim900_debug, "MUX: "); if (!g_at_mux_start(data->mux)) { g_at_mux_shutdown(data->mux); g_at_mux_unref(data->mux); goto error; } for (i = 0; i < NUM_DLC; i++) { GIOChannel *channel = g_at_mux_create_channel(data->mux); data->dlcs[i] = create_chat(channel, modem, dlc_prefixes[i]); if (data->dlcs[i] == NULL) { ofono_error("Failed to create channel"); goto error; } } ofono_modem_set_powered(modem, TRUE); return; error: shutdown_device(data); ofono_modem_set_powered(modem, FALSE); } static void mux_setup_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct sim900_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->dlcs[SETUP_DLC]); data->dlcs[SETUP_DLC] = NULL; if (!ok) goto error; setup_internal_mux(modem); return; error: shutdown_device(data); ofono_modem_set_powered(modem, FALSE); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct sim900_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->dlcs[SETUP_DLC]); data->dlcs[SETUP_DLC] = NULL; ofono_modem_set_powered(modem, FALSE); return; } g_at_chat_send(data->dlcs[SETUP_DLC], "AT+CMUX=0,0,5,128,10,3,30,10,2", NULL, mux_setup_cb, modem, NULL); } static int sim900_enable(struct ofono_modem *modem) { struct sim900_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->dlcs[SETUP_DLC] = open_device(modem, "Device", "Setup: "); if (data->dlcs[SETUP_DLC] == NULL) return -EINVAL; g_at_chat_send(data->dlcs[SETUP_DLC], "ATE0", NULL, NULL, NULL, NULL); /* For obtain correct sms service number */ g_at_chat_send(data->dlcs[SETUP_DLC], "AT+CSCS=\"GSM\"", NULL, NULL, NULL, NULL); g_at_chat_send(data->dlcs[SETUP_DLC], "AT+CFUN=1", none_prefix, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct sim900_data *data = ofono_modem_get_data(modem); DBG(""); shutdown_device(data); if (ok) ofono_modem_set_powered(modem, FALSE); } static int sim900_disable(struct ofono_modem *modem) { struct sim900_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_send(data->dlcs[SETUP_DLC], "AT+CFUN=4", none_prefix, cfun_disable, modem, NULL); return -EINPROGRESS; } static void sim900_pre_sim(struct ofono_modem *modem) { struct sim900_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->dlcs[VOICE_DLC]); sim = ofono_sim_create(modem, OFONO_VENDOR_SIMCOM, "atmodem", data->dlcs[VOICE_DLC]); if (sim) ofono_sim_inserted_notify(sim, TRUE); } static void sim900_post_sim(struct ofono_modem *modem) { struct sim900_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_phonebook_create(modem, 0, "atmodem", data->dlcs[VOICE_DLC]); ofono_sms_create(modem, OFONO_VENDOR_SIMCOM, "atmodem", data->dlcs[SMS_DLC]); gprs = ofono_gprs_create(modem, 0, "atmodem", data->dlcs[GPRS_DLC]); if (gprs == NULL) return; gc = ofono_gprs_context_create(modem, OFONO_VENDOR_SIMCOM_SIM900, "atmodem", data->dlcs[GPRS_DLC]); if (gc) ofono_gprs_add_context(gprs, gc); } static void sim900_post_online(struct ofono_modem *modem) { struct sim900_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_netreg_create(modem, OFONO_VENDOR_SIMCOM, "atmodem", data->dlcs[NETREG_DLC]); ofono_ussd_create(modem, 0, "atmodem", data->dlcs[VOICE_DLC]); ofono_voicecall_create(modem, 0, "atmodem", data->dlcs[VOICE_DLC]); ofono_call_volume_create(modem, 0, "atmodem", data->dlcs[VOICE_DLC]); } static struct ofono_modem_driver sim900_driver = { .name = "sim900", .probe = sim900_probe, .remove = sim900_remove, .enable = sim900_enable, .disable = sim900_disable, .pre_sim = sim900_pre_sim, .post_sim = sim900_post_sim, .post_online = sim900_post_online, }; static int sim900_init(void) { return ofono_modem_driver_register(&sim900_driver); } static void sim900_exit(void) { ofono_modem_driver_unregister(&sim900_driver); } OFONO_PLUGIN_DEFINE(sim900, "SIM900 modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, sim900_init, sim900_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/wavecom.c0000644000015600001650000001163412671500024021672 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int wavecom_probe(struct ofono_modem *modem) { return 0; } static void wavecom_remove(struct ofono_modem *modem) { } static void wavecom_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static int wavecom_enable(struct ofono_modem *modem) { GAtChat *chat; GIOChannel *channel; GAtSyntax *syntax; const char *device; GHashTable *options; DBG("%p", modem); device = ofono_modem_get_string(modem, "Device"); if (device == NULL) return -EINVAL; options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) return -ENOMEM; g_hash_table_insert(options, "Baud", "115200"); g_hash_table_insert(options, "Parity", "none"); g_hash_table_insert(options, "StopBits", "1"); g_hash_table_insert(options, "DataBits", "8"); channel = g_at_tty_open(device, options); g_hash_table_destroy(options); if (channel == NULL) return -EIO; /* * Could not figure out whether it is fully compliant or not, use * permissive for now * */ syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return -ENOMEM; g_at_chat_add_terminator(chat, "+CPIN:", 6, TRUE); if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, wavecom_debug, ""); ofono_modem_set_data(modem, chat); return 0; } static int wavecom_disable(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); g_at_chat_unref(chat); return 0; } static void wavecom_pre_sim(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); const char *model; enum ofono_vendor vendor = 0; struct ofono_sim *sim; DBG("%p", modem); model = ofono_modem_get_string(modem, "Model"); if (model && strcmp(model, "Q2XXX") == 0) vendor = OFONO_VENDOR_WAVECOM_Q2XXX; ofono_devinfo_create(modem, 0, "atmodem", chat); sim = ofono_sim_create(modem, vendor, "atmodem", chat); ofono_voicecall_create(modem, 0, "atmodem", chat); if (vendor == OFONO_VENDOR_WAVECOM_Q2XXX) ofono_sim_inserted_notify(sim, TRUE); } static void wavecom_post_sim(struct ofono_modem *modem) { GAtChat *chat = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; const char *model; enum ofono_vendor vendor = 0; DBG("%p", modem); model = ofono_modem_get_string(modem, "Model"); if (model && strcmp(model, "Q2XXX") == 0) vendor = OFONO_VENDOR_WAVECOM_Q2XXX; ofono_ussd_create(modem, 0, "atmodem", chat); ofono_call_forwarding_create(modem, 0, "atmodem", chat); ofono_call_settings_create(modem, 0, "atmodem", chat); ofono_netreg_create(modem, 0, "atmodem", chat); ofono_call_meter_create(modem, 0, "atmodem", chat); ofono_call_barring_create(modem, 0, "atmodem", chat); ofono_sms_create(modem, vendor, "atmodem", chat); ofono_phonebook_create(modem, 0, "atmodem", chat); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } static struct ofono_modem_driver wavecom_driver = { .name = "wavecom", .probe = wavecom_probe, .remove = wavecom_remove, .enable = wavecom_enable, .disable = wavecom_disable, .pre_sim = wavecom_pre_sim, .post_sim = wavecom_post_sim, }; static int wavecom_init(void) { return ofono_modem_driver_register(&wavecom_driver); } static void wavecom_exit(void) { ofono_modem_driver_unregister(&wavecom_driver); } OFONO_PLUGIN_DEFINE(wavecom, "Wavecom driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, wavecom_init, wavecom_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/ublox.c0000644000015600001650000001505712671500024021365 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Philip Paeps. 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; struct ublox_data { GAtChat *modem; GAtChat *aux; }; static void ublox_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static int ublox_probe(struct ofono_modem *modem) { struct ublox_data *data; DBG("%p", modem); data = g_try_new0(struct ublox_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void ublox_remove(struct ofono_modem *modem) { struct ublox_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); g_at_chat_unref(data->aux); g_at_chat_unref(data->modem); g_free(data); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GAtSyntax *syntax; GIOChannel *channel; GAtChat *chat; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, ublox_debug, debug); return chat; } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct ublox_data * data = ofono_modem_get_data(modem); DBG("ok %d", ok); if (!ok) { g_at_chat_unref(data->aux); data->aux = NULL; g_at_chat_unref(data->modem); data->modem = NULL; ofono_modem_set_powered(modem, FALSE); return; } ofono_modem_set_powered(modem, TRUE); } static int ublox_enable(struct ofono_modem *modem) { struct ublox_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->aux = open_device(modem, "Aux", "Aux: "); if (data->aux == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_set_slave(data->modem, data->aux); g_at_chat_send(data->modem, "ATE0 +CMEE=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->aux, "ATE0 +CMEE=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct ublox_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->aux); data->aux = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int ublox_disable(struct ofono_modem *modem) { struct ublox_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->aux); g_at_chat_unregister_all(data->aux); g_at_chat_send(data->aux, "AT+CFUN=0", none_prefix, cfun_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void ublox_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct ublox_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4"; DBG("modem %p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(data->aux, command, none_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void ublox_pre_sim(struct ofono_modem *modem) { struct ublox_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->aux); sim = ofono_sim_create(modem, OFONO_VENDOR_UBLOX, "atmodem", data->aux); if (sim) ofono_sim_inserted_notify(sim, TRUE); } static void ublox_post_sim(struct ofono_modem *modem) { struct ublox_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); gprs = ofono_gprs_create(modem, OFONO_VENDOR_UBLOX, "atmodem", data->aux); gc = ofono_gprs_context_create(modem, OFONO_VENDOR_UBLOX, "atmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static void ublox_post_online(struct ofono_modem *modem) { struct ublox_data *data = ofono_modem_get_data(modem); ofono_netreg_create(modem, 0, "atmodem", data->aux); } static struct ofono_modem_driver ublox_driver = { .name = "ublox", .probe = ublox_probe, .remove = ublox_remove, .enable = ublox_enable, .disable = ublox_disable, .set_online = ublox_set_online, .pre_sim = ublox_pre_sim, .post_sim = ublox_post_sim, .post_online = ublox_post_online, }; static int ublox_init(void) { return ofono_modem_driver_register(&ublox_driver); } static void ublox_exit(void) { ofono_modem_driver_unregister(&ublox_driver); } OFONO_PLUGIN_DEFINE(ublox, "u-blox modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, ublox_init, ublox_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/mnclength.c0000644000015600001650000003134312671500024022207 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 Canonical 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. * * 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 #endif #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include struct mcc_mnclength { int mcc; int mnclength; }; /* * Database of MCC to MNC length correspondences based on "Mobile Network Codes * (MNC) for the international identification plan for public networks and * subscriptions (According to Recommendation ITU-T E.212 (05/2008))". Based on * position on 1st January 2013 (http://www.itu.int/pub/T-SP-E.212B-2013). * Latest version of that document can be found in * http://www.itu.int/pub/T-SP-E.212B. Countries wiht no operators have been * given a default length depending on their geographical area. */ static struct mcc_mnclength mnclen_db[] = { {202, 2}, /* Greece */ {204, 2}, /* Netherlands (Kingdom of the) */ {206, 2}, /* Belgium */ {208, 2}, /* France */ {212, 2}, /* Monaco (Principality of) */ {213, 2}, /* Andorra (Principality of) */ {214, 2}, /* Spain */ {216, 2}, /* Hungary */ {218, 2}, /* Bosnia and Herzegovina */ {219, 2}, /* Croatia (Republic of) */ {220, 2}, /* Serbia (Republic of) */ {222, 2}, /* Italy */ {225, 2}, /* Vatican City State */ {226, 2}, /* Romania */ {228, 2}, /* Switzerland (Confederation of) */ {230, 2}, /* Czech Republic */ {231, 2}, /* Slovak Republic */ {232, 2}, /* Austria */ {234, 2}, /* United Kingdom of G. Britain and Northern Ireland */ {235, 2}, /* United Kingdom of G. Britain and Northern Ireland */ {238, 2}, /* Denmark */ {240, 2}, /* Sweden */ {242, 2}, /* Norway */ {244, 2}, /* Finland */ {246, 2}, /* Lithuania (Republic of) */ {247, 2}, /* Latvia (Republic of) */ {248, 2}, /* Estonia (Republic of) */ {250, 2}, /* Russian Federation */ {255, 2}, /* Ukraine */ {257, 2}, /* Belarus (Republic of) */ {259, 2}, /* Moldova (Republic of) */ {260, 2}, /* Poland (Republic of) */ {262, 2}, /* Germany (Federal Republic of) */ {266, 2}, /* Gibraltar */ {268, 2}, /* Portugal */ {270, 2}, /* Luxembourg */ {272, 2}, /* Ireland */ {274, 2}, /* Iceland */ {276, 2}, /* Albania (Republic of) */ {278, 2}, /* Malta */ {280, 2}, /* Cyprus (Republic of) */ {282, 2}, /* Georgia */ {283, 2}, /* Armenia (Republic of) */ {284, 2}, /* Bulgaria (Republic of) */ {286, 2}, /* Turkey */ {288, 2}, /* Faroe Islands */ {290, 2}, /* Greenland (Denmark) */ {292, 2}, /* San Marino (Republic of) */ {293, 2}, /* Slovenia (Republic of) */ {294, 2}, /* The Former Yugoslav Republic of Macedonia */ {295, 2}, /* Liechtenstein (Principality of) */ {297, 2}, /* Montenegro (Republic of) */ {302, 3}, /* Canada */ {308, 2}, /* Saint Pierre and Miquelon (french Republic) */ {310, 3}, /* United States of America */ {311, 3}, /* United States of America */ {312, 3}, /* United States of America */ {313, 3}, /* United States of America */ {314, 3}, /* United States of America */ {315, 3}, /* United States of America */ {316, 3}, /* United States of America */ {330, 3}, /* Puerto Rico */ {332, 3}, /* United States Virgin Islands */ {334, 3}, /* Mexico */ {338, 3}, /* Jamaica */ {340, 2}, /* Guadeloupe and Martinique (French Departments) */ {342, 3}, /* Barbados */ {344, 3}, /* Antigua and Barbuda */ {346, 3}, /* Cayman Islands */ {348, 3}, /* British Virgin Islands */ {350, 3}, /* Bermuda */ {352, 3}, /* Grenada */ {354, 3}, /* Montserrat */ {356, 3}, /* Saint Kitts and Nevis */ {358, 3}, /* Saint Lucia */ {360, 3}, /* Saint Vincent and the Grenadines */ {362, 2}, /* Curazao, St Maarten, Bonaire, St Eustatius, Saba */ {363, 2}, /* Aruba */ {364, 3}, /* Bahamas (Commonwealth of the) */ {365, 3}, /* Anguilla */ {366, 3}, /* Dominica (Commonwealth of) */ {368, 2}, /* Cuba */ {370, 2}, /* Dominican Republic */ {372, 2}, /* Haiti (Republic of) */ {374, 2}, /* Trinidad and Tobago */ {376, 3}, /* Turks and Caicos Islands */ {400, 2}, /* Azerbaijani Republic */ {401, 2}, /* Kazakhstan (Republic of) */ {402, 2}, /* Bhutan (Kingdom of) */ {404, 2}, /* India (Republic of) */ {405, 2}, /* India (Republic of) */ {406, 2}, /* India (Republic of) */ {410, 2}, /* Pakistan (Islamic Republic of) */ {412, 2}, /* Afghanistan */ {413, 2}, /* Sri Lanka (Democratic Socialist Republic of) */ {414, 2}, /* Myanmar (the Republic of the Union of) */ {415, 2}, /* Lebanon */ {416, 2}, /* Jordan (Hashemite Kingdom of) */ {417, 2}, /* Syrian Arab Republic */ {418, 2}, /* Iraq (Republic of) */ {419, 2}, /* Kuwait (State of) */ {420, 2}, /* Saudi Arabia (Kingdom of) */ {421, 2}, /* Yemen (Republic of) */ {422, 2}, /* Oman (Sultanate of) */ {424, 2}, /* United Arab Emirates */ {425, 2}, /* Israel (State of) */ {426, 2}, /* Bahrain (Kingdom of) */ {427, 2}, /* Qatar (State of) */ {428, 2}, /* Mongolia */ {429, 2}, /* Nepal (Federal Democratic Republic of) */ {430, 2}, /* United Arab Emirates */ {431, 2}, /* United Arab Emirates */ {432, 2}, /* Iran (Islamic Republic of) */ {434, 2}, /* Uzbekistan (Republic of) */ {436, 2}, /* Tajikistan (Republic of) */ {437, 2}, /* Kyrgyz Republic */ {438, 2}, /* Turkmenistan */ {440, 2}, /* Japan */ {441, 2}, /* Japan */ {450, 2}, /* Korea (Republic of) */ {452, 2}, /* Viet Nam (Socialist Republic of) */ {454, 2}, /* Hong Kong, China */ {455, 2}, /* Macao, China */ {456, 2}, /* Cambodia (Kingdom of) */ {457, 2}, /* Lao People's Democratic Republic */ {460, 2}, /* China (People's Republic of) */ {461, 2}, /* China (People's Republic of) */ {466, 2}, /* Taiwan, China */ {467, 2}, /* Democratic People's Republic of Korea */ {470, 2}, /* Bangladesh (People's Republic of) */ {472, 2}, /* Maldives (Republic of) */ {502, 2}, /* Malaysia */ {505, 2}, /* Australia */ {510, 2}, /* Indonesia (Republic of) */ {514, 2}, /* Democratic Republic of Timor-Leste */ {515, 2}, /* Philippines (Republic of the) */ {520, 2}, /* Thailand */ {525, 2}, /* Singapore (Republic of) */ {528, 2}, /* Brunei Darussalam */ {530, 2}, /* New Zealand */ {536, 2}, /* Nauru (Republic of) */ {537, 2}, /* Papua New Guinea */ {539, 2}, /* Tonga (Kingdom of) */ {540, 2}, /* Solomon Islands */ {541, 2}, /* Vanuatu (Republic of) */ {542, 2}, /* Fiji (Republic of) */ {543, 2}, /* Wallis and Futuna (french territory) */ {544, 2}, /* American Samoa */ {545, 2}, /* Kiribati (Republic of) */ {546, 2}, /* New Caledonia (french territory) */ {547, 2}, /* French Polynesia (french territory) */ {548, 2}, /* Cook Islands */ {549, 2}, /* Samoa (Independent State of) */ {550, 2}, /* Micronesia (Federated States of) */ {551, 2}, /* Marshall Islands (Republic of the) */ {552, 2}, /* Palau (Republic of) */ {553, 2}, /* Tuvalu */ {555, 2}, /* Niue */ {602, 2}, /* Egypt (Arab Republic of) */ {603, 2}, /* Algeria (People's Democratic Republic of) */ {604, 2}, /* Morocco (Kingdom of) */ {605, 2}, /* Tunisia */ {606, 2}, /* Libya */ {607, 2}, /* Gambia (Republic of the) */ {608, 2}, /* Senegal (Republic of) */ {609, 2}, /* Mauritania (Islamic Republic of) */ {610, 2}, /* Mali (Republic of) */ {611, 2}, /* Guinea (Republic of) */ {612, 2}, /* Ivory Coast (Republic of) */ {613, 2}, /* Burkina Faso */ {614, 2}, /* Niger (Republic of the) */ {615, 2}, /* Togolese Republic */ {616, 2}, /* Benin (Republic of) */ {617, 2}, /* Mauritius (Republic of) */ {618, 2}, /* Liberia (Republic of) */ {619, 2}, /* Sierra Leone */ {620, 2}, /* Ghana */ {621, 2}, /* Nigeria (Federal Republic of) */ {622, 2}, /* Chad (Republic of) */ {623, 2}, /* Central African Republic */ {624, 2}, /* Cameroon (Republic of) */ {625, 2}, /* Cape Verde (Republic of) */ {626, 2}, /* Sao Tome and Principe (Democratic Republic of) */ {627, 2}, /* Equatorial Guinea (Republic of) */ {628, 2}, /* Gabonese Republic */ {629, 2}, /* Congo (Republic of the) */ {630, 2}, /* Democratic Republic of the Congo */ {631, 2}, /* Angola (Republic of) */ {632, 2}, /* Guinea-Bissau (Republic of) */ {633, 2}, /* Seychelles (Republic of) */ {634, 2}, /* Sudan (Republic of the) */ {635, 2}, /* Rwanda (Republic of) */ {636, 2}, /* Ethiopia (Federal Democratic Republic of) */ {637, 2}, /* Somali Democratic Republic */ {638, 2}, /* Djibouti (Republic of) */ {639, 2}, /* Kenya (Republic of) */ {640, 2}, /* Tanzania (United Republic of) */ {641, 2}, /* Uganda (Republic of) */ {642, 2}, /* Burundi (Republic of) */ {643, 2}, /* Mozambique (Republic of) */ {645, 2}, /* Zambia (Republic of) */ {646, 2}, /* Madagascar (Republic of) */ {647, 2}, /* French Departments in the Indian Ocean */ {648, 2}, /* Zimbabwe (Republic of) */ {649, 2}, /* Namibia (Republic of) */ {650, 2}, /* Malawi */ {651, 2}, /* Lesotho (Kingdom of) */ {652, 2}, /* Botswana (Republic of) */ {653, 2}, /* Swaziland (Kingdom of) */ {654, 2}, /* Comoros (Union of the) */ {655, 2}, /* South Africa (Republic of) */ {657, 2}, /* Eritrea */ {658, 2}, /* Saint Helena, Ascension and Tristan da Cunha */ {659, 2}, /* South Sudan (Republic of) */ {702, 2}, /* Belize */ {704, 2}, /* Guatemala (Republic of) */ {706, 2}, /* El Salvador (Republic of) */ {708, 3}, /* Honduras (Republic of) */ {710, 2}, /* Nicaragua */ {712, 2}, /* Costa Rica */ {714, 2}, /* Panama (Republic of) */ {716, 2}, /* Peru */ {722, 3}, /* Argentine Republic */ {724, 2}, /* Brazil (Federative Republic of) */ {730, 2}, /* Chile */ {732, 3}, /* Colombia (Republic of) */ {734, 2}, /* Venezuela (Bolivarian Republic of) */ {736, 2}, /* Bolivia (Plurinational State of) */ {738, 2}, /* Guyana */ {740, 2}, /* Ecuador */ {742, 2}, /* French Guiana (French Department of) */ {744, 2}, /* Paraguay (Republic of) */ {746, 2}, /* Suriname (Republic of) */ {748, 2}, /* Uruguay (Eastern Republic of) */ {750, 3}, /* Falkland Islands (Malvinas) */ {901, 2}, /* International Mobile, shared code */ }; /* * These MCC+MNC combinations have 3 digit MNC even though the default for * the corresponing MCC in mnclen_db is length 2. */ static int codes_mnclen3_db[] = { 374130, 374140, 405000, 405005, 405006, 405007, 405009, 405010, 405011, 405012, 405013, 405014, 405018, 405020, 405021, 405022, 405025, 405027, 405029, 405030, 405031, 405032, 405033, 405034, 405035, 405036, 405037, 405038, 405039, 405040, 405041, 405042, 405043, 405044, 405045, 405046, 405047, 405750, 405751, 405752, 405753, 405754, 405755, 405799, 405800, 405801, 405802, 405803, 405804, 405805, 405806, 405807, 405808, 405809, 714020 }; static int comp_int(const void *key, const void *value) { int mccmnckey = *(int *) key; int mccmnccurr = *(int *) value; return mccmnckey - mccmnccurr; } static int comp_mcc(const void *key, const void *value) { int mcc = *(int *) key; struct mcc_mnclength *mccmnc = (struct mcc_mnclength *) value; return mcc - mccmnc->mcc; } static int mnclength_get_mnclength(const char *imsi) { char mccmnc[OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1]; int mccmnc_num; int *mccmnc3_res; int mcc_num; struct mcc_mnclength *mccmnc_res; char *endp; if (imsi == NULL || *imsi == '\0') return -EINVAL; /* Special case for some operators */ strncpy(mccmnc, imsi, sizeof(mccmnc) - 1); mccmnc[sizeof(mccmnc) - 1] = '\0'; mccmnc_num = (int) strtoul(mccmnc, &endp, 10); if (*endp != '\0') return -EINVAL; mccmnc3_res = bsearch(&mccmnc_num, codes_mnclen3_db, G_N_ELEMENTS(codes_mnclen3_db), sizeof(codes_mnclen3_db[0]), comp_int); if (mccmnc3_res) return 3; /* General case */ mccmnc[OFONO_MAX_MCC_LENGTH] = '\0'; mcc_num = (int) strtoul(mccmnc, &endp, 10); if (*endp != '\0') return -EINVAL; mccmnc_res = bsearch(&mcc_num, mnclen_db, G_N_ELEMENTS(mnclen_db), sizeof(mnclen_db[0]), comp_mcc); if (mccmnc_res) return mccmnc_res->mnclength; return -ENOENT; } static struct ofono_sim_mnclength_driver mnclength_driver = { .name = "MNC length", .get_mnclength = mnclength_get_mnclength }; static int mnclength_init(void) { return ofono_sim_mnclength_driver_register(&mnclength_driver); } static void mnclength_exit(void) { ofono_sim_mnclength_driver_unregister(&mnclength_driver); } OFONO_PLUGIN_DEFINE(mnclength, "MNC length Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, mnclength_init, mnclength_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/nokiacdma.c0000644000015600001650000001011212671500024022145 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include "common.h" struct nokiacdma_data { GAtChat *chat; }; static void nokiacdma_debug(const char *str, void *data) { const char *prefix = data; ofono_info("%s%s", prefix, str); } static int nokiacdma_probe(struct ofono_modem *modem) { struct nokiacdma_data *data; DBG("%p", modem); data = g_try_new0(struct nokiacdma_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void nokiacdma_remove(struct ofono_modem *modem) { struct nokiacdma_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); g_at_chat_unref(data->chat); g_free(data); } static int nokiacdma_enable(struct ofono_modem *modem) { struct nokiacdma_data *data = ofono_modem_get_data(modem); GAtSyntax *syntax; GIOChannel *channel; const char *device; DBG("%p", modem); device = ofono_modem_get_string(modem, "Device"); if (device == NULL) return -EINVAL; channel = g_at_tty_open(device, NULL); if (channel == NULL) return -EIO; /* * TODO: Will need a CDMA AT syntax parser later. * Using GSM V1 for now. */ syntax = g_at_syntax_new_gsmv1(); data->chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (data->chat == NULL) return -ENOMEM; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(data->chat, nokiacdma_debug, "CDMA Device: "); return 0; } static int nokiacdma_disable(struct ofono_modem *modem) { struct nokiacdma_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_unref(data->chat); data->chat = NULL; return 0; } static void nokiacdma_pre_sim(struct ofono_modem *modem) { struct nokiacdma_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_cdma_voicecall_create(modem, 0, "cdmamodem", data->chat); ofono_devinfo_create(modem, 0, "cdmamodem", data->chat); } static void nokiacdma_post_sim(struct ofono_modem *modem) { DBG("%p", modem); } static void nokiacdma_post_online(struct ofono_modem *modem) { struct nokiacdma_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_cdma_connman_create(modem, 0, "cdmamodem", data->chat); } static struct ofono_modem_driver nokiacdma_driver = { .name = "nokiacdma", .probe = nokiacdma_probe, .remove = nokiacdma_remove, .enable = nokiacdma_enable, .disable = nokiacdma_disable, .pre_sim = nokiacdma_pre_sim, .post_sim = nokiacdma_post_sim, .post_online = nokiacdma_post_online, }; static int nokiacdma_init(void) { return ofono_modem_driver_register(&nokiacdma_driver); } static void nokiacdma_exit(void) { ofono_modem_driver_unregister(&nokiacdma_driver); } OFONO_PLUGIN_DEFINE(nokiacdma, "Nokia CDMA AT Modem", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, nokiacdma_init, nokiacdma_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/stktest.c0000644000015600001650000001255212671500024021732 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include "ofono.h" static struct ofono_modem *stktest; static const char *none_prefix[] = { NULL }; struct stktest_data { GAtChat *chat; }; static int stktest_probe(struct ofono_modem *modem) { struct stktest_data *data; DBG("%p", modem); data = g_try_new0(struct stktest_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void stktest_remove(struct ofono_modem *modem) { struct stktest_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_free(data); ofono_modem_set_data(modem, NULL); } static void stktest_debug(const char *str, void *prefix) { ofono_info("%s%s", (const char *) prefix, str); } static void stktest_disconnected(gpointer user_data) { struct ofono_modem *modem = user_data; struct stktest_data *data = ofono_modem_get_data(modem); DBG(""); ofono_modem_set_powered(modem, FALSE); g_at_chat_unref(data->chat); data->chat = NULL; } static int connect_socket(const char *address, int port) { struct sockaddr_in addr; int sk; int err; sk = socket(PF_INET, SOCK_STREAM, 0); if (sk < 0) return -EINVAL; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(address); addr.sin_port = htons(port); err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { close(sk); return -errno; } return sk; } static int stktest_enable(struct ofono_modem *modem) { struct stktest_data *data = ofono_modem_get_data(modem); GIOChannel *io; GAtSyntax *syntax; int sk; DBG("%p", modem); sk = connect_socket("127.0.0.1", 12765); if (sk < 0) return sk; io = g_io_channel_unix_new(sk); if (io == NULL) { close(sk); return -ENOMEM; } syntax = g_at_syntax_new_gsmv1(); data->chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); if (data->chat == NULL) return -ENOMEM; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(data->chat, stktest_debug, ""); g_at_chat_set_disconnect_function(data->chat, stktest_disconnected, modem); return 0; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t callback = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); callback(&error, cbd->data); } static void stktest_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct stktest_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char buf[64]; DBG("%p", modem); snprintf(buf, sizeof(buf), "AT+CFUN=%d", online ? 1 : 4); if (g_at_chat_send(data->chat, buf, none_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, user_data); } static int stktest_disable(struct ofono_modem *modem) { struct stktest_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_unref(data->chat); data->chat = NULL; return 0; } static void stktest_pre_sim(struct ofono_modem *modem) { DBG("%p", modem); } static void stktest_post_sim(struct ofono_modem *modem) { struct stktest_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_stk_create(modem, OFONO_VENDOR_PHONESIM, "atmodem", data->chat); } static void stktest_post_online(struct ofono_modem *modem) { } static struct ofono_modem_driver stktest_driver = { .modem_type = OFONO_MODEM_TYPE_TEST, .name = "stktest", .probe = stktest_probe, .remove = stktest_remove, .enable = stktest_enable, .disable = stktest_disable, .set_online = stktest_set_online, .pre_sim = stktest_pre_sim, .post_sim = stktest_post_sim, .post_online = stktest_post_online, }; static int stktest_init(void) { int err; err = ofono_modem_driver_register(&stktest_driver); if (err < 0) return err; stktest = ofono_modem_create("stktest", "stktest"); ofono_modem_register(stktest); return 0; } static void stktest_exit(void) { ofono_modem_remove(stktest); ofono_modem_driver_unregister(&stktest_driver); } OFONO_PLUGIN_DEFINE(stktest, "STK End-to-End tester driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, stktest_init, stktest_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/udev.c0000644000015600001650000003066512671500024021201 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include static GSList *modem_list = NULL; static GHashTable *devpath_list = NULL; static struct ofono_modem *find_modem(const char *devpath) { GSList *list; for (list = modem_list; list; list = list->next) { struct ofono_modem *modem = list->data; const char *path = ofono_modem_get_string(modem, "Path"); if (g_strcmp0(devpath, path) == 0) return modem; } return NULL; } static const char *get_property(struct udev_device *device, char const *property_name) { struct udev_list_entry *entry; entry = udev_device_get_properties_list_entry(device); while (entry) { const char *name = udev_list_entry_get_name(entry); if (g_strcmp0(name, property_name) == 0) return udev_list_entry_get_value(entry); entry = udev_list_entry_get_next(entry); } return NULL; } static const char *get_driver(struct udev_device *udev_device) { return get_property(udev_device, "OFONO_DRIVER"); } static const char *get_serial(struct udev_device *udev_device) { const char *serial; serial = get_property(udev_device, "ID_SERIAL_SHORT"); if (serial != NULL) { unsigned int i, len = strlen(serial); for (i = 0; i < len; i++) { if (!g_ascii_isalnum(serial[i])) return NULL; } } return serial; } static void add_ifx(struct ofono_modem *modem, struct udev_device *udev_device) { struct udev_list_entry *entry; const char *devnode; DBG("modem %p", modem); devnode = udev_device_get_devnode(udev_device); ofono_modem_set_string(modem, "Device", devnode); entry = udev_device_get_properties_list_entry(udev_device); while (entry) { const char *name = udev_list_entry_get_name(entry); const char *value = udev_list_entry_get_value(entry); if (g_str_equal(name, "OFONO_IFX_LDISC") == TRUE) ofono_modem_set_string(modem, "LineDiscipline", value); else if (g_str_equal(name, "OFONO_IFX_AUDIO") == TRUE) ofono_modem_set_string(modem, "AudioSetting", value); else if (g_str_equal(name, "OFONO_IFX_LOOPBACK") == TRUE) ofono_modem_set_string(modem, "AudioLoopback", value); entry = udev_list_entry_get_next(entry); } ofono_modem_register(modem); } static void add_isi(struct ofono_modem *modem, struct udev_device *udev_device) { const char *ifname, *type, *addr; DBG("modem %p", modem); if (ofono_modem_get_string(modem, "Interface")) return; addr = get_property(udev_device, "OFONO_ISI_ADDRESS"); if (addr != NULL) ofono_modem_set_integer(modem, "Address", atoi(addr)); if (g_strcmp0(udev_device_get_subsystem(udev_device), "net") != 0) return; type = udev_device_get_sysattr_value(udev_device, "type"); if (g_strcmp0(type, "820") != 0) return; ifname = udev_device_get_sysname(udev_device); ofono_modem_set_string(modem, "Interface", ifname); DBG("interface %s", ifname); ofono_modem_register(modem); } static void add_calypso(struct ofono_modem *modem, struct udev_device *udev_device) { const char *devnode; DBG("modem %p", modem); devnode = udev_device_get_devnode(udev_device); ofono_modem_set_string(modem, "Device", devnode); ofono_modem_register(modem); } static void add_wavecom(struct ofono_modem *modem, struct udev_device *udev_device) { const char *devnode; struct udev_list_entry *entry; DBG("modem %p", modem); devnode = udev_device_get_devnode(udev_device); ofono_modem_set_string(modem, "Device", devnode); entry = udev_device_get_properties_list_entry(udev_device); while (entry) { const char *name = udev_list_entry_get_name(entry); const char *value = udev_list_entry_get_value(entry); if (g_str_equal(name, "OFONO_WAVECOM_MODEL") == TRUE) ofono_modem_set_string(modem, "Model", value); entry = udev_list_entry_get_next(entry); } ofono_modem_register(modem); } static void add_cinterion(struct ofono_modem *modem, struct udev_device *udev_device) { const char *devnode; DBG("modem %p", modem); devnode = udev_device_get_devnode(udev_device); ofono_modem_set_string(modem, "Device", devnode); ofono_modem_register(modem); } static void add_nokiacdma(struct ofono_modem *modem, struct udev_device *udev_device) { const char *devnode; DBG("modem %p", modem); devnode = udev_device_get_devnode(udev_device); ofono_modem_set_string(modem, "Device", devnode); ofono_modem_register(modem); } static void add_sim900(struct ofono_modem *modem, struct udev_device *udev_device) { const char *devnode; DBG("modem %p", modem); devnode = udev_device_get_devnode(udev_device); ofono_modem_set_string(modem, "Device", devnode); ofono_modem_register(modem); } static void add_modem(struct udev_device *udev_device) { struct ofono_modem *modem; struct udev_device *parent; const char *devpath, *curpath, *driver; driver = get_driver(udev_device); if (driver != NULL) { devpath = udev_device_get_devpath(udev_device); if (devpath == NULL) return; if(g_strcmp0(driver, "tc65") == 0) driver = "cinterion"; if(g_strcmp0(driver, "ehs6") == 0) driver = "cinterion"; modem = ofono_modem_create(NULL, driver); if (modem == NULL) return; ofono_modem_set_string(modem, "Path", devpath); modem_list = g_slist_prepend(modem_list, modem); goto done; } parent = udev_device_get_parent(udev_device); if (parent == NULL) return; driver = get_driver(parent); if (driver == NULL) { parent = udev_device_get_parent(parent); driver = get_driver(parent); if (driver == NULL) { parent = udev_device_get_parent(parent); driver = get_driver(parent); if (driver == NULL) return; } } devpath = udev_device_get_devpath(parent); if (devpath == NULL) return; modem = find_modem(devpath); if (modem == NULL) { const char *serial = get_serial(parent); modem = ofono_modem_create(serial, driver); if (modem == NULL) return; ofono_modem_set_string(modem, "Path", devpath); ofono_modem_set_integer(modem, "Registered", 0); modem_list = g_slist_prepend(modem_list, modem); } done: curpath = udev_device_get_devpath(udev_device); if (curpath == NULL) return; DBG("%s (%s)", curpath, driver); g_hash_table_insert(devpath_list, g_strdup(curpath), g_strdup(devpath)); if (g_strcmp0(driver, "ifx") == 0) add_ifx(modem, udev_device); else if (g_strcmp0(driver, "u8500") == 0) add_isi(modem, udev_device); else if (g_strcmp0(driver, "n900") == 0) add_isi(modem, udev_device); else if (g_strcmp0(driver, "calypso") == 0) add_calypso(modem, udev_device); else if (g_strcmp0(driver, "cinterion") == 0) add_cinterion(modem, udev_device); else if (g_strcmp0(driver, "nokiacdma") == 0) add_nokiacdma(modem, udev_device); else if (g_strcmp0(driver, "sim900") == 0) add_sim900(modem, udev_device); else if (g_strcmp0(driver, "wavecom") == 0) add_wavecom(modem, udev_device); } static gboolean devpath_remove(gpointer key, gpointer value, gpointer user_data) { const char *path = value; const char *devpath = user_data; DBG("%s -> %s", path, devpath); return g_str_equal(path, devpath); } static void remove_modem(struct udev_device *udev_device) { struct ofono_modem *modem; const char *curpath = udev_device_get_devpath(udev_device); char *devpath, *remove; if (curpath == NULL) return; DBG("%s", curpath); devpath = g_hash_table_lookup(devpath_list, curpath); if (devpath == NULL) return; modem = find_modem(devpath); if (modem == NULL) return; modem_list = g_slist_remove(modem_list, modem); ofono_modem_remove(modem); DBG("%s", devpath); remove = g_strdup(devpath); g_hash_table_foreach_remove(devpath_list, devpath_remove, remove); g_free(remove); } static void enumerate_devices(struct udev *context) { struct udev_enumerate *enumerate; struct udev_list_entry *entry; enumerate = udev_enumerate_new(context); if (enumerate == NULL) return; udev_enumerate_add_match_subsystem(enumerate, "tty"); udev_enumerate_add_match_subsystem(enumerate, "net"); udev_enumerate_add_match_subsystem(enumerate, "hsi"); udev_enumerate_scan_devices(enumerate); entry = udev_enumerate_get_list_entry(enumerate); while (entry) { const char *syspath = udev_list_entry_get_name(entry); struct udev_device *device; device = udev_device_new_from_syspath(context, syspath); if (device != NULL) { const char *subsystem; subsystem = udev_device_get_subsystem(device); if (g_strcmp0(subsystem, "tty") == 0 || g_strcmp0(subsystem, "net") == 0 || g_strcmp0(subsystem, "hsi") == 0) add_modem(device); udev_device_unref(device); } entry = udev_list_entry_get_next(entry); } udev_enumerate_unref(enumerate); } static struct udev *udev_ctx; static struct udev_monitor *udev_mon; static guint udev_watch = 0; static gboolean udev_event(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct udev_device *device; const char *subsystem, *action; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { ofono_warn("Error with udev monitor channel"); udev_watch = 0; return FALSE; } device = udev_monitor_receive_device(udev_mon); if (device == NULL) return TRUE; subsystem = udev_device_get_subsystem(device); if (subsystem == NULL) goto done; action = udev_device_get_action(device); if (action == NULL) goto done; DBG("subsystem %s %s", subsystem, action); if (g_str_equal(action, "add") == TRUE) { if (g_strcmp0(subsystem, "tty") == 0 || g_strcmp0(subsystem, "net") == 0 || g_strcmp0(subsystem, "hsi") == 0) add_modem(device); } else if (g_str_equal(action, "remove") == TRUE) { if (g_strcmp0(subsystem, "tty") == 0 || g_strcmp0(subsystem, "net") == 0 || g_strcmp0(subsystem, "hsi") == 0) remove_modem(device); } DBG("subsystem %s finished", subsystem); done: udev_device_unref(device); return TRUE; } static void udev_start(void) { GIOChannel *channel; int fd; if (udev_monitor_enable_receiving(udev_mon) < 0) { ofono_error("Failed to enable udev monitor"); return; } enumerate_devices(udev_ctx); fd = udev_monitor_get_fd(udev_mon); channel = g_io_channel_unix_new(fd); if (channel == NULL) return; udev_watch = g_io_add_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, udev_event, NULL); g_io_channel_unref(channel); } static int udev_init(void) { devpath_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); if (devpath_list == NULL) { ofono_error("Failed to create udev path list"); return -ENOMEM; } udev_ctx = udev_new(); if (udev_ctx == NULL) { ofono_error("Failed to create udev context"); g_hash_table_destroy(devpath_list); return -EIO; } udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev"); if (udev_mon == NULL) { ofono_error("Failed to create udev monitor"); g_hash_table_destroy(devpath_list); udev_unref(udev_ctx); udev_ctx = NULL; return -EIO; } udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "tty", NULL); udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "net", NULL); udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "hsi", NULL); udev_monitor_filter_update(udev_mon); udev_start(); return 0; } static void udev_exit(void) { GSList *list; if (udev_watch > 0) g_source_remove(udev_watch); for (list = modem_list; list; list = list->next) { struct ofono_modem *modem = list->data; ofono_modem_remove(modem); } g_slist_free(modem_list); modem_list = NULL; g_hash_table_destroy(devpath_list); devpath_list = NULL; if (udev_ctx == NULL) return; udev_monitor_filter_remove(udev_mon); udev_monitor_unref(udev_mon); udev_unref(udev_ctx); } OFONO_PLUGIN_DEFINE(udev, "udev hardware detection", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, udev_init, udev_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/rildev.c0000644000015600001650000000661112671500073021521 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include static GSList *modem_list; static int create_rilmodem(const char *ril_type, int slot) { struct ofono_modem *modem; char dev_name[64]; char *socket; int retval; snprintf(dev_name, sizeof(dev_name), "ril_%d", slot); /* Currently there is only one ril implementation, create always */ modem = ofono_modem_create(dev_name, ril_type); if (modem == NULL) { DBG("ofono_modem_create failed for type %s", ril_type); return -ENODEV; } modem_list = g_slist_prepend(modem_list, modem); ofono_modem_set_integer(modem, "Slot", slot); /* AOSP has socket path "rild", "rild2"..., while others may differ */ if (slot != 0) socket = g_strdup_printf("/dev/socket/rild%d", slot + 1); else socket = g_strdup("/dev/socket/rild"); ofono_modem_set_string(modem, "Socket", socket); g_free(socket); /* This causes driver->probe() to be called... */ retval = ofono_modem_register(modem); if (retval != 0) { ofono_error("%s: ofono_modem_register returned: %d", __func__, retval); return retval; } /* * kickstart the modem: * causes core modem code to call * - set_powered(TRUE) - which in turn * calls driver->enable() * * - driver->pre_sim() * * Could also be done via: * * - a DBus call to SetProperties w/"Powered=TRUE" *1 * - sim_state_watch ( handles SIM removal? LOCKED states? **2 * - ofono_modem_set_powered() */ ofono_modem_reset(modem); return 0; } static int detect_init(void) { const char *ril_type; const char *multi_sim; int num_slots = 1; int i; ril_type = getenv("OFONO_RIL_DEVICE"); if (ril_type == NULL) return 0; /* Check for multi-SIM support */ multi_sim = getenv("OFONO_RIL_NUM_SIM_SLOTS"); if (multi_sim != NULL && *multi_sim != '\0') { int env_slots; char *endp; env_slots = (int) strtoul(multi_sim, &endp, 10); if (*endp == '\0') num_slots = env_slots; } ofono_info("RILDEV detected modem type %s, %d SIM slot(s)", ril_type, num_slots); for (i = 0; i < num_slots; ++i) create_rilmodem(ril_type, i); return 0; } static void detect_exit(void) { GSList *list; for (list = modem_list; list; list = list->next) { struct ofono_modem *modem = list->data; ofono_modem_remove(modem); } g_slist_free(modem_list); modem_list = NULL; } OFONO_PLUGIN_DEFINE(rildev, "ril type detection", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, detect_init, detect_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/accounts-settings.c0000644000015600001650000002547712671500073023724 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2016 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #define ACCOUNTS_SERVICE "org.freedesktop.Accounts" #define ACCOUNTS_PATH "/org/freedesktop/Accounts/User" #define ACCOUNTS_PHONE_INTERFACE "com.ubuntu.touch.AccountsService.Phone" #define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" struct setting { char* ofono_name; char* property_name; }; static const struct setting setting_names[] = { { PREFERRED_VOICE_MODEM, "DefaultSimForCalls" } }; #define NUM_SETTINGS G_N_ELEMENTS(setting_names) struct accounts_data { sd_login_monitor *login_monitor; guint login_watch; char *property_values[NUM_SETTINGS]; uid_t current_uid; guint prop_change_watch; char uid_path[sizeof(ACCOUNTS_PATH) + 32]; }; static struct accounts_data g_accounts; static int translate_name(const char *name) { size_t i; for (i = 0; i < NUM_SETTINGS; ++i) { if (g_strcmp0(setting_names[i].ofono_name, name) != 0) continue; return i; } return -1; } static char *accounts_settings_get_string_value(const char *name) { int id; id = translate_name(name); if (id < 0) return NULL; return g_strdup(g_accounts.property_values[id]); } static struct ofono_system_settings_driver accounts_settings_driver = { .name = "Accounts Service System Settings", .get_string_value = accounts_settings_get_string_value }; static int get_id_from_name(const char *name) { size_t i; for (i = 0; i < NUM_SETTINGS; ++i) { if (g_strcmp0(setting_names[i].property_name, name) != 0) continue; return i; } return -1; } static char *get_property_value(DBusMessage *reply) { DBusMessageIter iter, val; const char *ptr; char *property = NULL; if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { ofono_error("%s: ERROR reply to Get", __func__); goto done; } if (dbus_message_iter_init(reply, &iter) == FALSE) { ofono_error("%s: error initializing array iter", __func__); goto done; } if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { ofono_error("%s: type != VARIANT!", __func__); goto done; } dbus_message_iter_recurse(&iter, &val); if (dbus_message_iter_get_arg_type(&val) != DBUS_TYPE_STRING) { ofono_error("%s: type != STRING!", __func__); goto done; } dbus_message_iter_get_basic(&val, &ptr); property = g_strdup(ptr); done: return property; } struct get_property_data { struct accounts_data *accounts; int prop_id; }; static void get_property_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply; struct get_property_data *gpd = user_data; char **values = gpd->accounts->property_values; int id = gpd->prop_id; reply = dbus_pending_call_steal_reply(call); if (reply == NULL) { ofono_error("%s: failed to get reply", __func__); goto done; } g_free(values[id]); values[id] = get_property_value(reply); dbus_message_unref(reply); DBG("property %s has value %s", setting_names[id].property_name, PRINTABLE_STR(values[id])); done: dbus_pending_call_unref(call); g_free(gpd); } static void get_property(struct accounts_data *accounts, int id) { DBusMessageIter iter; DBusMessage *msg; const char *iface = ACCOUNTS_PHONE_INTERFACE; DBusConnection *conn = ofono_dbus_get_connection(); DBusPendingCall *call; struct get_property_data *gpd; msg = dbus_message_new_method_call(ACCOUNTS_SERVICE, accounts->uid_path, DBUS_PROPERTIES_INTERFACE, "Get"); if (msg == NULL) { ofono_error("%s: dbus_message_new_method failed", __func__); return; } dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &setting_names[id].property_name); if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) { ofono_error("%s: Sending Get failed", __func__); goto done; } gpd = g_malloc0(sizeof(*gpd)); gpd->accounts = accounts; gpd->prop_id = id; dbus_pending_call_set_notify(call, get_property_reply, gpd, NULL); done: dbus_message_unref(msg); } static gboolean property_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct accounts_data *accounts = user_data; const char *iface; DBusMessageIter iter, dict, inv_it; dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { ofono_error("%s: iface != TYPE_STRING!", __func__); goto done; } dbus_message_iter_get_basic(&iter, &iface); /* We can receive notifications from other AccountsService interfaces */ if (g_str_equal(iface, ACCOUNTS_PHONE_INTERFACE) != TRUE) goto done; if (!dbus_message_iter_next(&iter)) { ofono_error("%s: advance iter failed!", __func__); goto done; } if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { ofono_error("%s: type != ARRAY!", __func__); goto done; } dbus_message_iter_recurse(&iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, val; const char *key, *str_val; int id; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) { ofono_error("%s: key type != STRING!", __func__); goto done; } dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) { ofono_error("%s: val != VARIANT", __func__); goto done; } dbus_message_iter_recurse(&entry, &val); if (dbus_message_iter_get_arg_type(&val) != DBUS_TYPE_STRING) { ofono_error("%s: *val != STRING", __func__); goto done; } dbus_message_iter_get_basic(&val, &str_val); DBG("property %s changed to %s", key, PRINTABLE_STR(str_val)); id = get_id_from_name(key); if (id >= 0) accounts->property_values[id] = g_strdup(str_val); dbus_message_iter_next(&dict); } /* Check invalidated properties */ if (!dbus_message_iter_next(&iter)) { ofono_error("%s: advance iter failed!", __func__); goto done; } if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { ofono_error("%s: type != ARRAY!", __func__); goto done; } dbus_message_iter_recurse(&iter, &inv_it); while (dbus_message_iter_get_arg_type(&inv_it) == DBUS_TYPE_STRING) { const char *inv_name; int id; dbus_message_iter_get_basic(&inv_it, &inv_name); DBG("property %s invalidated", inv_name); id = get_id_from_name(inv_name); if (id >= 0) get_property(accounts, id); dbus_message_iter_next(&inv_it); } done: return TRUE; } static void get_user_settings(struct accounts_data *accounts) { DBusConnection *conn = ofono_dbus_get_connection(); size_t i; snprintf(accounts->uid_path, sizeof(accounts->uid_path), ACCOUNTS_PATH"%u", (unsigned) accounts->current_uid); /* * Register for property changes. Note that AccountsService is D-Bus, * initiated, so there is no risk for race conditions on start. */ accounts->prop_change_watch = g_dbus_add_signal_watch(conn, ACCOUNTS_SERVICE, accounts->uid_path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", property_changed, accounts, NULL); /* Retrieve AccountService properties */ for (i = 0; i < NUM_SETTINGS; ++i) get_property(accounts, i); } static void release_accounts_data(struct accounts_data *accounts) { DBusConnection *conn = ofono_dbus_get_connection(); size_t i; g_dbus_remove_watch(conn, accounts->prop_change_watch); for (i = 0; i < NUM_SETTINGS; ++i) { g_free(accounts->property_values[i]); accounts->property_values[i] = NULL; } } static uid_t get_active_seat_uid(void) { char **seats, **iter; int res; gboolean found = FALSE; uid_t uid; res = sd_get_seats(&seats); if (res < 0) { ofono_error("Error retrieving seats: %s (%d)", strerror(-res), -res); goto end; } else if (res == 0) { ofono_info("No seats found"); goto end; } for (iter = seats; *iter; ++iter) { if (!found && sd_seat_get_active(*iter, NULL, &uid) >= 0) { DBG("seat %s with uid %d", *iter, uid); found = TRUE; } free(*iter); } free(seats); end: if (!found) uid = geteuid(); return uid; } static gboolean sd_changed(GIOChannel *stream, GIOCondition condition, gpointer user_data) { struct accounts_data *accounts = user_data; uid_t new_uid; sd_login_monitor_flush(accounts->login_monitor); new_uid = get_active_seat_uid(); if (new_uid != accounts->current_uid) { /* User in seat changed, resetting data */ release_accounts_data(accounts); accounts->current_uid = new_uid; get_user_settings(accounts); } return TRUE; } static void sd_init(struct accounts_data *accounts) { int status; GIOChannel *stream; int fd; status = sd_login_monitor_new(NULL, &accounts->login_monitor); if (status < 0) { ofono_error("Error creating systemd login monitor: %d", status); accounts->login_monitor = NULL; return; } fd = sd_login_monitor_get_fd(accounts->login_monitor); stream = g_io_channel_unix_new(fd); accounts->login_watch = g_io_add_watch(stream, G_IO_IN, sd_changed, accounts); g_io_channel_unref(stream); } static void sd_finalize(struct accounts_data *accounts) { if (accounts->login_monitor == NULL) return; g_source_remove(accounts->login_watch); sd_login_monitor_unref(accounts->login_monitor); } static int accounts_settings_init(void) { g_accounts.current_uid = get_active_seat_uid(); /* Register for login changes */ sd_init(&g_accounts); /* Get property values for current user */ get_user_settings(&g_accounts); return ofono_system_settings_driver_register(&accounts_settings_driver); } static void accounts_settings_exit(void) { ofono_system_settings_driver_unregister(&accounts_settings_driver); sd_finalize(&g_accounts); release_accounts_data(&g_accounts); } OFONO_PLUGIN_DEFINE(accounts_settings, "Accounts Service System Settings Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, accounts_settings_init, accounts_settings_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/hfp_ag_bluez4.c0000644000015600001650000001256012671500024022741 0ustar pbuserpbgroup00000000000000/* * oFono - Open Source Telephony * * Copyright (C) 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "bluez4.h" #define HFP_AG_CHANNEL 13 static struct server *server; static guint modemwatch_id; static GList *modems; static GHashTable *sim_hash = NULL; static const gchar *hfp_ag_record = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" "\n"; static void hfp_ag_connect_cb(GIOChannel *io, GError *err, gpointer user_data) { struct ofono_modem *modem; struct ofono_emulator *em; int fd; GList *last; DBG(""); if (err) { DBG("%s", err->message); return; } /* * Pick the last one to avoid creating a new list with one modem just * for the call to ofono_emulator_create() (for this plugin we only * support registering one modem in the emulator) */ last = g_list_last(modems); modem = last->data; if (modem == NULL) return; DBG("Picked modem %p for emulator", modem); em = ofono_emulator_create(last, OFONO_EMULATOR_TYPE_HFP); if (em == NULL) return; fd = g_io_channel_unix_get_fd(io); g_io_channel_set_close_on_unref(io, FALSE); ofono_emulator_register(em, fd); } static void sim_state_watch(enum ofono_sim_state new_state, void *data) { struct ofono_modem *modem = data; if (new_state != OFONO_SIM_STATE_READY) { modems = g_list_remove(modems, modem); if (modems == NULL && server != NULL) { bluetooth_unregister_server(server); server = NULL; } return; } if (__ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_VOICECALL) == NULL) return; modems = g_list_append(modems, modem); if (modems->next != NULL) return; server = bluetooth_register_server(HFP_AG_CHANNEL, hfp_ag_record, hfp_ag_connect_cb, NULL); } static gboolean sim_watch_remove(gpointer key, gpointer value, gpointer user_data) { struct ofono_sim *sim = key; ofono_sim_remove_state_watch(sim, GPOINTER_TO_UINT(value)); return TRUE; } static void sim_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_sim *sim = __ofono_atom_get_data(atom); struct ofono_modem *modem = data; int watch; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { sim_state_watch(OFONO_SIM_STATE_NOT_PRESENT, modem); sim_watch_remove(sim, g_hash_table_lookup(sim_hash, sim), NULL); g_hash_table_remove(sim_hash, sim); return; } watch = ofono_sim_add_state_watch(sim, sim_state_watch, modem, NULL); g_hash_table_insert(sim_hash, sim, GUINT_TO_POINTER(watch)); sim_state_watch(ofono_sim_get_state(sim), modem); } static void modem_watch(struct ofono_modem *modem, gboolean added, void *user) { DBG("modem: %p, added: %d", modem, added); if (added == FALSE) return; __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SIM, sim_watch, modem, NULL); } static void call_modemwatch(struct ofono_modem *modem, void *user) { modem_watch(modem, TRUE, user); } static int hfp_ag_init(void) { sim_hash = g_hash_table_new(g_direct_hash, g_direct_equal); modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL); __ofono_modem_foreach(call_modemwatch, NULL); return 0; } static void hfp_ag_exit(void) { __ofono_modemwatch_remove(modemwatch_id); g_list_free(modems); g_hash_table_foreach_remove(sim_hash, sim_watch_remove, NULL); g_hash_table_destroy(sim_hash); if (server) { bluetooth_unregister_server(server); server = NULL; } } OFONO_PLUGIN_DEFINE(hfp_ag_bluez4, "Hands-Free Audio Gateway Profile Plugins", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, hfp_ag_init, hfp_ag_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/ril.h0000644000015600001650000000232212671500024021016 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL-based devices * * Copyright (C) 2014 Canonical 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. * * 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 * */ int ril_create(struct ofono_modem *modem, enum ofono_ril_vendor vendor); void ril_remove(struct ofono_modem *modem); int ril_enable(struct ofono_modem *modem); int ril_disable(struct ofono_modem *modem); void ril_pre_sim(struct ofono_modem *modem); void ril_post_sim(struct ofono_modem *modem); void ril_post_online(struct ofono_modem *modem); void ril_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t callback, void *data); ofono-1.17.bzr6912+16.04.20160314.3/plugins/hfp_hf_bluez4.c0000644000015600001650000003206012671500024022744 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ProFUSION embedded systems * Copyright (C) 2011 BMW Car IT GmbH. 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 #endif #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include "bluez4.h" #define BLUEZ_GATEWAY_INTERFACE BLUEZ_SERVICE ".HandsfreeGateway" #define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent" #define HFP_AGENT_ERROR_INTERFACE "org.bluez.Error" #ifndef DBUS_TYPE_UNIX_FD #define DBUS_TYPE_UNIX_FD -1 #endif static DBusConnection *connection; static GHashTable *modem_hash = NULL; struct hfp_data { struct hfp_slc_info info; char *handsfree_path; char *handsfree_address; DBusMessage *slc_msg; gboolean agent_registered; DBusPendingCall *call; }; static void hfp_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static void slc_established(gpointer userdata) { struct ofono_modem *modem = userdata; struct hfp_data *data = ofono_modem_get_data(modem); DBusMessage *msg; ofono_modem_set_powered(modem, TRUE); msg = dbus_message_new_method_return(data->slc_msg); g_dbus_send_message(connection, msg); dbus_message_unref(data->slc_msg); data->slc_msg = NULL; ofono_info("Service level connection established"); } static void slc_failed(gpointer userdata) { struct ofono_modem *modem = userdata; struct hfp_data *data = ofono_modem_get_data(modem); DBusMessage *msg; msg = g_dbus_create_error(data->slc_msg, HFP_AGENT_ERROR_INTERFACE ".Failed", "HFP Handshake failed"); g_dbus_send_message(connection, msg); dbus_message_unref(data->slc_msg); data->slc_msg = NULL; ofono_error("Service level connection failed"); ofono_modem_set_powered(modem, FALSE); g_at_chat_unref(data->info.chat); data->info.chat = NULL; } static void hfp_disconnected_cb(gpointer user_data) { struct ofono_modem *modem = user_data; struct hfp_data *data = ofono_modem_get_data(modem); ofono_modem_set_powered(modem, FALSE); g_at_chat_unref(data->info.chat); data->info.chat = NULL; } /* either oFono or Phone could request SLC connection */ static int service_level_connection(struct ofono_modem *modem, int fd) { struct hfp_data *data = ofono_modem_get_data(modem); GIOChannel *io; GAtSyntax *syntax; GAtChat *chat; io = g_io_channel_unix_new(fd); if (io == NULL) { ofono_error("Service level connection failed: %s (%d)", strerror(errno), errno); return -EIO; } syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); if (chat == NULL) return -ENOMEM; g_at_chat_set_disconnect_function(chat, hfp_disconnected_cb, modem); if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, hfp_debug, ""); data->info.chat = chat; hfp_slc_establish(&data->info, slc_established, slc_failed, modem); return -EINPROGRESS; } static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data) { int fd, err; struct ofono_modem *modem = data; struct hfp_data *hfp_data = ofono_modem_get_data(modem); guint16 version; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_UINT16, &version, DBUS_TYPE_INVALID)) return __ofono_error_invalid_args(msg); hfp_slc_info_init(&hfp_data->info, version); err = service_level_connection(modem, fd); if (err < 0 && err != -EINPROGRESS) return __ofono_error_failed(msg); hfp_data->slc_msg = msg; dbus_message_ref(msg); return NULL; } static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_modem *modem = data; struct hfp_data *hfp_data = ofono_modem_get_data(modem); const char *obj_path = ofono_modem_get_path(modem); g_dbus_unregister_interface(connection, obj_path, HFP_AGENT_INTERFACE); hfp_data->agent_registered = FALSE; g_hash_table_remove(modem_hash, hfp_data->handsfree_path); ofono_modem_remove(modem); return dbus_message_new_method_return(msg); } static const GDBusMethodTable agent_methods[] = { { GDBUS_ASYNC_METHOD("NewConnection", GDBUS_ARGS({ "fd", "h" }, { "version", "q" }), NULL, hfp_agent_new_connection) }, { GDBUS_METHOD("Release", NULL, NULL, hfp_agent_release) }, { } }; static int hfp_hf_probe(const char *device, const char *dev_addr, const char *adapter_addr, const char *alias) { struct ofono_modem *modem; struct hfp_data *data; char buf[256]; /* We already have this device in our hash, ignore */ if (g_hash_table_lookup(modem_hash, device) != NULL) return -EALREADY; ofono_info("Using device: %s, devaddr: %s, adapter: %s", device, dev_addr, adapter_addr); strcpy(buf, "hfp/"); bluetooth_create_path(dev_addr, adapter_addr, buf + 4, sizeof(buf) - 4); modem = ofono_modem_create(buf, "hfp"); if (modem == NULL) return -ENOMEM; data = g_try_new0(struct hfp_data, 1); if (data == NULL) goto free; data->handsfree_path = g_strdup(device); if (data->handsfree_path == NULL) goto free; data->handsfree_address = g_strdup(dev_addr); if (data->handsfree_address == NULL) goto free; ofono_modem_set_data(modem, data); ofono_modem_set_name(modem, alias); ofono_modem_register(modem); g_hash_table_insert(modem_hash, g_strdup(device), modem); return 0; free: if (data != NULL) g_free(data->handsfree_path); g_free(data); ofono_modem_remove(modem); return -ENOMEM; } static gboolean hfp_remove_modem(gpointer key, gpointer value, gpointer user_data) { struct ofono_modem *modem = value; const char *device = key; const char *prefix = user_data; if (prefix && g_str_has_prefix(device, prefix) == FALSE) return FALSE; ofono_modem_remove(modem); return TRUE; } static void hfp_hf_remove(const char *prefix) { DBG("%s", prefix); if (modem_hash == NULL) return; g_hash_table_foreach_remove(modem_hash, hfp_remove_modem, (gpointer) prefix); } static void hfp_hf_set_alias(const char *device, const char *alias) { struct ofono_modem *modem; if (device == NULL || alias == NULL) return; modem = g_hash_table_lookup(modem_hash, device); if (modem == NULL) return; ofono_modem_set_name(modem, alias); } static int hfp_register_ofono_handsfree(struct ofono_modem *modem) { const char *obj_path = ofono_modem_get_path(modem); struct hfp_data *data = ofono_modem_get_data(modem); DBusMessage *msg; DBG("Registering oFono Agent to bluetooth daemon"); msg = dbus_message_new_method_call(BLUEZ_SERVICE, data->handsfree_path, BLUEZ_GATEWAY_INTERFACE, "RegisterAgent"); if (msg == NULL) return -ENOMEM; dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &obj_path, DBUS_TYPE_INVALID); g_dbus_send_message(connection, msg); return 0; } static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem) { const char *obj_path = ofono_modem_get_path(modem); struct hfp_data *data = ofono_modem_get_data(modem); DBusMessage *msg; DBG("Unregistering oFono Agent from bluetooth daemon"); msg = dbus_message_new_method_call(BLUEZ_SERVICE, data->handsfree_path, BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent"); if (msg == NULL) return -ENOMEM; dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &obj_path, DBUS_TYPE_INVALID); g_dbus_send_message(connection, msg); return 0; } static int hfp_probe(struct ofono_modem *modem) { const char *obj_path = ofono_modem_get_path(modem); struct hfp_data *data = ofono_modem_get_data(modem); if (data == NULL) return -EINVAL; g_dbus_register_interface(connection, obj_path, HFP_AGENT_INTERFACE, agent_methods, NULL, NULL, modem, NULL); data->agent_registered = TRUE; if (hfp_register_ofono_handsfree(modem) != 0) return -EINVAL; return 0; } static void hfp_remove(struct ofono_modem *modem) { struct hfp_data *data = ofono_modem_get_data(modem); const char *obj_path = ofono_modem_get_path(modem); if (data->call != NULL) dbus_pending_call_cancel(data->call); if (g_dbus_unregister_interface(connection, obj_path, HFP_AGENT_INTERFACE)) hfp_unregister_ofono_handsfree(modem); g_free(data->handsfree_address); g_free(data->handsfree_path); g_free(data); ofono_modem_set_data(modem, NULL); } static void hfp_connect_reply(DBusPendingCall *call, gpointer user_data) { struct ofono_modem *modem = user_data; struct hfp_data *data = ofono_modem_get_data(modem); DBusError derr; DBusMessage *reply, *msg; reply = dbus_pending_call_steal_reply(call); if (ofono_modem_get_powered(modem)) goto done; dbus_error_init(&derr); if (!dbus_set_error_from_message(&derr, reply)) goto done; DBG("Connect reply: %s", derr.message); if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY)) { msg = dbus_message_new_method_call(BLUEZ_SERVICE, data->handsfree_path, BLUEZ_GATEWAY_INTERFACE, "Disconnect"); if (msg == NULL) ofono_error("Disconnect failed"); else g_dbus_send_message(connection, msg); } ofono_modem_set_powered(modem, FALSE); dbus_error_free(&derr); done: dbus_message_unref(reply); data->call = NULL; } /* power up hardware */ static int hfp_enable(struct ofono_modem *modem) { struct hfp_data *data = ofono_modem_get_data(modem); int status; DBG("%p", modem); status = bluetooth_send_with_reply(data->handsfree_path, BLUEZ_GATEWAY_INTERFACE, "Connect", &data->call, hfp_connect_reply, modem, NULL, DBUS_TIMEOUT, DBUS_TYPE_INVALID); if (status < 0) return -EINVAL; return -EINPROGRESS; } static void hfp_power_down(DBusPendingCall *call, gpointer user_data) { struct ofono_modem *modem = user_data; struct hfp_data *data = ofono_modem_get_data(modem); DBusMessage *reply; DBusError derr; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { DBG("Disconnect reply: %s", derr.message); dbus_error_free(&derr); goto done; } ofono_modem_set_powered(modem, FALSE); done: dbus_message_unref(reply); data->call = NULL; } static int hfp_disable(struct ofono_modem *modem) { struct hfp_data *data = ofono_modem_get_data(modem); int status; DBG("%p", modem); g_at_chat_unref(data->info.chat); data->info.chat = NULL; if (data->agent_registered) { status = bluetooth_send_with_reply(data->handsfree_path, BLUEZ_GATEWAY_INTERFACE, "Disconnect", &data->call, hfp_power_down, modem, NULL, DBUS_TIMEOUT, DBUS_TYPE_INVALID); if (status < 0) return -EINVAL; } return -EINPROGRESS; } static void hfp_pre_sim(struct ofono_modem *modem) { struct hfp_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_devinfo_create(modem, 0, "hfpmodem", data->handsfree_address); ofono_voicecall_create(modem, 0, "hfpmodem", &data->info); ofono_netreg_create(modem, 0, "hfpmodem", &data->info); ofono_call_volume_create(modem, 0, "hfpmodem", &data->info); ofono_handsfree_create(modem, 0, "hfpmodem", &data->info); ofono_siri_create(modem, 0, "hfpmodem", &data->info); } static void hfp_post_sim(struct ofono_modem *modem) { DBG("%p", modem); } static struct ofono_modem_driver hfp_driver = { .name = "hfp", .modem_type = OFONO_MODEM_TYPE_HFP, .probe = hfp_probe, .remove = hfp_remove, .enable = hfp_enable, .disable = hfp_disable, .pre_sim = hfp_pre_sim, .post_sim = hfp_post_sim, }; static struct bluetooth_profile hfp_hf = { .name = "hfp_hf", .probe = hfp_hf_probe, .remove = hfp_hf_remove, .set_alias = hfp_hf_set_alias, }; static int hfp_init(void) { int err; if (DBUS_TYPE_UNIX_FD < 0) return -EBADF; connection = ofono_dbus_get_connection(); err = ofono_modem_driver_register(&hfp_driver); if (err < 0) return err; err = bluetooth_register_uuid(HFP_AG_UUID, &hfp_hf); if (err < 0) { ofono_modem_driver_unregister(&hfp_driver); return err; } modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); return 0; } static void hfp_exit(void) { bluetooth_unregister_uuid(HFP_AG_UUID); ofono_modem_driver_unregister(&hfp_driver); g_hash_table_destroy(modem_hash); } OFONO_PLUGIN_DEFINE(hfp_bluez4, "Hands-Free Profile Plugins", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, hfp_init, hfp_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/smart-messaging.c0000644000015600001650000002245412671500024023334 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "smsagent.h" #include "smsutil.h" #include "common.h" #define SMART_MESSAGING_INTERFACE "org.ofono.SmartMessaging" #define AGENT_INTERFACE "org.ofono.SmartMessagingAgent" #define VCARD_SRC_PORT -1 #define VCARD_DST_PORT 9204 #define VCAL_SRC_PORT -1 #define VCAL_DST_PORT 9205 static unsigned int modemwatch_id; struct smart_messaging { struct ofono_modem *modem; struct ofono_sms *sms; struct sms_agent *agent; unsigned int vcard_watch; unsigned int vcal_watch; }; static void agent_exited(void *userdata) { struct smart_messaging *sm = userdata; if (sm->vcard_watch > 0) { __ofono_sms_datagram_watch_remove(sm->sms, sm->vcard_watch); sm->vcard_watch = 0; } if (sm->vcal_watch > 0) { __ofono_sms_datagram_watch_remove(sm->sms, sm->vcal_watch); sm->vcal_watch = 0; } sm->agent = NULL; } static void vcard_received(const char *from, const struct tm *remote, const struct tm *local, int dst, int src, const unsigned char *buffer, unsigned int len, void *data) { struct smart_messaging *sm = data; if (sm->agent == NULL) return; sms_agent_dispatch_datagram(sm->agent, "ReceiveBusinessCard", from, remote, local, buffer, len, NULL, NULL, NULL); } static void vcal_received(const char *from, const struct tm *remote, const struct tm *local, int dst, int src, const unsigned char *buffer, unsigned int len, void *data) { struct smart_messaging *sm = data; if (sm->agent == NULL) return; sms_agent_dispatch_datagram(sm->agent, "ReceiveAppointment", from, remote, local, buffer, len, NULL, NULL, NULL); } static DBusMessage *smart_messaging_register_agent(DBusConnection *conn, DBusMessage *msg, void *data) { struct smart_messaging *sm = data; const char *agent_path; if (sm->agent) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!__ofono_dbus_valid_object_path(agent_path)) return __ofono_error_invalid_format(msg); sm->agent = sms_agent_new(AGENT_INTERFACE, dbus_message_get_sender(msg), agent_path); if (sm->agent == NULL) return __ofono_error_failed(msg); sms_agent_set_removed_notify(sm->agent, agent_exited, sm); sm->vcard_watch = __ofono_sms_datagram_watch_add(sm->sms, vcard_received, VCARD_DST_PORT, VCARD_SRC_PORT, sm, NULL); sm->vcal_watch = __ofono_sms_datagram_watch_add(sm->sms, vcal_received, VCAL_DST_PORT, VCAL_SRC_PORT, sm, NULL); return dbus_message_new_method_return(msg); } static DBusMessage *smart_messaging_unregister_agent(DBusConnection *conn, DBusMessage *msg, void *data) { struct smart_messaging *sm = data; const char *agent_path; const char *agent_bus = dbus_message_get_sender(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (sm->agent == NULL) return __ofono_error_failed(msg); if (sms_agent_matches(sm->agent, agent_bus, agent_path) == FALSE) return __ofono_error_failed(msg); sms_agent_free(sm->agent); sm->agent = NULL; return dbus_message_new_method_return(msg); } static void message_queued(struct ofono_sms *sms, const struct ofono_uuid *uuid, void *data) { DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *msg = data; const char *path; path = __ofono_sms_message_path_from_uuid(sms, uuid); g_dbus_send_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); } static DBusMessage *smart_messaging_send_vcard(DBusConnection *conn, DBusMessage *msg, void *data) { struct smart_messaging *sm = data; const char *to; unsigned char *bytes; int len; GSList *msg_list; unsigned int flags; gboolean use_16bit_ref = FALSE; int err; struct ofono_uuid uuid; unsigned short ref; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &to, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &bytes, &len, DBUS_TYPE_INVALID)) return __ofono_error_invalid_args(msg); if (valid_phone_number_format(to) == FALSE) return __ofono_error_invalid_format(msg); ref = __ofono_sms_get_next_ref(sm->sms); msg_list = sms_datagram_prepare(to, bytes, len, ref, use_16bit_ref, 0, VCARD_DST_PORT, TRUE, FALSE); if (msg_list == NULL) return __ofono_error_invalid_format(msg); flags = OFONO_SMS_SUBMIT_FLAG_RETRY | OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS; err = __ofono_sms_txq_submit(sm->sms, msg_list, flags, &uuid, message_queued, msg); g_slist_foreach(msg_list, (GFunc)g_free, NULL); g_slist_free(msg_list); if (err < 0) return __ofono_error_failed(msg); return NULL; } static DBusMessage *smart_messaging_send_vcal(DBusConnection *conn, DBusMessage *msg, void *data) { struct smart_messaging *sm = data; const char *to; unsigned char *bytes; int len; GSList *msg_list; unsigned int flags; gboolean use_16bit_ref = FALSE; int err; struct ofono_uuid uuid; unsigned short ref; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &to, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &bytes, &len, DBUS_TYPE_INVALID)) return __ofono_error_invalid_args(msg); if (valid_phone_number_format(to) == FALSE) return __ofono_error_invalid_format(msg); ref = __ofono_sms_get_next_ref(sm->sms); msg_list = sms_datagram_prepare(to, bytes, len, ref, use_16bit_ref, 0, VCAL_DST_PORT, TRUE, FALSE); if (msg_list == NULL) return __ofono_error_invalid_format(msg); flags = OFONO_SMS_SUBMIT_FLAG_RETRY | OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS; err = __ofono_sms_txq_submit(sm->sms, msg_list, flags, &uuid, message_queued, msg); g_slist_foreach(msg_list, (GFunc)g_free, NULL); g_slist_free(msg_list); if (err < 0) return __ofono_error_failed(msg); return NULL; } static const GDBusMethodTable smart_messaging_methods[] = { { GDBUS_METHOD("RegisterAgent", GDBUS_ARGS({ "path", "o" }), NULL, smart_messaging_register_agent) }, { GDBUS_METHOD("UnregisterAgent", GDBUS_ARGS({ "path", "o" }), NULL, smart_messaging_unregister_agent) }, { GDBUS_ASYNC_METHOD("SendBusinessCard", GDBUS_ARGS({ "to", "s" }, { "card", "ay" }), GDBUS_ARGS({ "path", "o" }), smart_messaging_send_vcard) }, { GDBUS_ASYNC_METHOD("SendAppointment", GDBUS_ARGS({ "to", "s" }, { "appointment", "ay" }), GDBUS_ARGS({ "path", "o" }), smart_messaging_send_vcal) }, { } }; static void smart_messaging_cleanup(gpointer user) { struct smart_messaging *sm = user; DBG("%p", sm); sm->vcard_watch = 0; sm->vcal_watch = 0; sm->sms = NULL; sms_agent_free(sm->agent); ofono_modem_remove_interface(sm->modem, SMART_MESSAGING_INTERFACE); } static void sms_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct smart_messaging *sm = data; DBusConnection *conn = ofono_dbus_get_connection(); if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { g_dbus_unregister_interface(conn, ofono_modem_get_path(sm->modem), SMART_MESSAGING_INTERFACE); return; } DBG("registered"); sm->sms = __ofono_atom_get_data(atom); if (!g_dbus_register_interface(conn, ofono_modem_get_path(sm->modem), SMART_MESSAGING_INTERFACE, smart_messaging_methods, NULL, NULL, sm, smart_messaging_cleanup)) { ofono_error("Could not create %s interface", SMART_MESSAGING_INTERFACE); return; } ofono_modem_add_interface(sm->modem, SMART_MESSAGING_INTERFACE); } static void modem_watch(struct ofono_modem *modem, gboolean added, void *user) { struct smart_messaging *sm; DBG("modem: %p, added: %d", modem, added); if (added == FALSE) return; sm = g_try_new0(struct smart_messaging, 1); if (sm == NULL) return; sm->modem = modem; __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SMS, sms_watch, sm, g_free); } static void call_modemwatch(struct ofono_modem *modem, void *user) { modem_watch(modem, TRUE, user); } static int smart_messaging_init(void) { DBG(""); modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL); __ofono_modem_foreach(call_modemwatch, NULL); return 0; } static void smart_messaging_exit(void) { DBG(""); __ofono_modemwatch_remove(modemwatch_id); } OFONO_PLUGIN_DEFINE(smart_messaging, "Smart Messaging Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, smart_messaging_init, smart_messaging_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/linktop.c0000644000015600001650000001571512671500024021715 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; struct linktop_data { GAtChat *modem; GAtChat *aux; }; static int linktop_probe(struct ofono_modem *modem) { struct linktop_data *data; DBG("%p", modem); data = g_try_new0(struct linktop_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void linktop_remove(struct ofono_modem *modem) { struct linktop_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); g_free(data); } static void linktop_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GIOChannel *channel; GAtSyntax *syntax; GAtChat *chat; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, linktop_debug, debug); return chat; } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct linktop_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_unref(data->aux); data->aux = NULL; } ofono_modem_set_powered(modem, ok); } static int linktop_enable(struct ofono_modem *modem) { struct linktop_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->aux = open_device(modem, "Aux", "Aux: "); if (data->aux == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_send(data->modem, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CFUN=4", NULL, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct linktop_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->aux); data->aux = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int linktop_disable(struct ofono_modem *modem) { struct linktop_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->aux); g_at_chat_unregister_all(data->aux); g_at_chat_send(data->aux, "AT+CFUN=4", NULL, cfun_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void linktop_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct linktop_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4"; DBG("modem %p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(data->aux, command, none_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void linktop_pre_sim(struct ofono_modem *modem) { struct linktop_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->aux); sim = ofono_sim_create(modem, 0, "atmodem", data->aux); if (sim) ofono_sim_inserted_notify(sim, TRUE); } static void linktop_post_sim(struct ofono_modem *modem) { struct linktop_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_phonebook_create(modem, 0, "atmodem", data->aux); ofono_sms_create(modem, 0, "atmodem", data->aux); ofono_radio_settings_create(modem, 0, "stemodem", data->aux); gprs = ofono_gprs_create(modem, 0, "atmodem", data->aux); gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static void linktop_post_online(struct ofono_modem *modem) { struct linktop_data *data = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; DBG("%p", modem); ofono_netreg_create(modem, 0, "atmodem", data->aux); ofono_cbs_create(modem, 0, "atmodem", data->aux); ofono_ussd_create(modem, 0, "atmodem", data->aux); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); } static struct ofono_modem_driver linktop_driver = { .name = "linktop", .probe = linktop_probe, .remove = linktop_remove, .enable = linktop_enable, .disable = linktop_disable, .set_online = linktop_set_online, .pre_sim = linktop_pre_sim, .post_sim = linktop_post_sim, .post_online = linktop_post_online, }; static int linktop_init(void) { return ofono_modem_driver_register(&linktop_driver); } static void linktop_exit(void) { ofono_modem_driver_unregister(&linktop_driver); } OFONO_PLUGIN_DEFINE(linktop, "Linktop Datacard modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, linktop_init, linktop_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/android-spn-table.c0000644000015600001650000001233312671500024023531 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include /* TODO: consider reading path from an environment variable */ #define ANDROID_SPN_DATABASE "/system/etc/spn-conf.xml" static GHashTable *android_spn_table; static void android_spndb_g_set_error(GMarkupParseContext *context, GError **error, GQuark domain, gint code, const gchar *fmt, ...) { va_list ap; gint line_number, char_number; g_markup_parse_context_get_position(context, &line_number, &char_number); va_start(ap, fmt); *error = g_error_new_valist(domain, code, fmt, ap); va_end(ap); g_prefix_error(error, "%s:%d ", ANDROID_SPN_DATABASE, line_number); } static void toplevel_spndb_start(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer userdata, GError **error) { GHashTable *spn_table = userdata; int i; const gchar *numeric = NULL; const gchar *spn = NULL; char *numeric_dup; char *spn_dup; if (!g_str_equal(element_name, "spnOverride")) return; for (i = 0; attribute_names[i]; ++i) { if (g_str_equal(attribute_names[i], "numeric")) numeric = attribute_values[i]; else if (g_str_equal(attribute_names[i], "spn")) spn = attribute_values[i]; } if (numeric == NULL) { android_spndb_g_set_error(context, error, G_MARKUP_ERROR, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "Missing attribute: numeric"); return; } if (spn == NULL) { android_spndb_g_set_error(context, error, G_MARKUP_ERROR, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "Missing attribute: spn"); return; } numeric_dup = g_malloc(strlen(numeric) + 1); strcpy(numeric_dup, numeric); spn_dup = g_malloc(strlen(spn) + 1); strcpy(spn_dup, spn); g_hash_table_insert(spn_table, numeric_dup, spn_dup); } static void toplevel_spndb_end(GMarkupParseContext *context, const gchar *element_name, gpointer userdata, GError **error) { } static const GMarkupParser toplevel_spndb_parser = { toplevel_spndb_start, toplevel_spndb_end, NULL, NULL, NULL, }; static gboolean android_spndb_parse(const GMarkupParser *parser, gpointer userdata, GError **error) { struct stat st; char *db; int fd; GMarkupParseContext *context; gboolean ret; fd = open(ANDROID_SPN_DATABASE, O_RDONLY); if (fd < 0) { g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "open(%s) failed: %s", ANDROID_SPN_DATABASE, g_strerror(errno)); return FALSE; } if (fstat(fd, &st) < 0) { close(fd); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "fstat(%s) failed: %s", ANDROID_SPN_DATABASE, g_strerror(errno)); return FALSE; } db = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (db == MAP_FAILED) { close(fd); g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "mmap(%s) failed: %s", ANDROID_SPN_DATABASE, g_strerror(errno)); return FALSE; } context = g_markup_parse_context_new(parser, G_MARKUP_TREAT_CDATA_AS_TEXT, userdata, NULL); ret = g_markup_parse_context_parse(context, db, st.st_size, error); if (ret == TRUE) g_markup_parse_context_end_parse(context, error); munmap(db, st.st_size); close(fd); g_markup_parse_context_free(context); return ret; } static const char *android_get_spn(const char *numeric) { return g_hash_table_lookup(android_spn_table, numeric); } static struct ofono_spn_table_driver android_spn_table_driver = { .name = "Android SPN table", .get_spn = android_get_spn }; static int android_spn_table_init(void) { GError *error = NULL; android_spn_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); if (android_spndb_parse(&toplevel_spndb_parser, android_spn_table, &error) == FALSE) { g_hash_table_destroy(android_spn_table); android_spn_table = NULL; g_clear_error(&error); return -EINVAL; } return ofono_spn_table_driver_register(&android_spn_table_driver); } static void android_spn_table_exit(void) { ofono_spn_table_driver_unregister(&android_spn_table_driver); g_hash_table_destroy(android_spn_table); } OFONO_PLUGIN_DEFINE(androidspntable, "Android SPN table Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, android_spn_table_init, android_spn_table_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/dun_gw_bluez5.c0000644000015600001650000001373512671500024023006 0ustar pbuserpbgroup00000000000000/* * oFono - Open Source Telephony * * Copyright (C) 2013 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 #endif #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "bluez5.h" #ifndef DBUS_TYPE_UNIX_FD #define DBUS_TYPE_UNIX_FD -1 #endif #define DUN_GW_VERSION_1_0 0x0100 #define DUN_GW_CHANNEL 1 #define DUN_GW_EXT_PROFILE_PATH "/bluetooth/profile/dun_gw" static guint modemwatch_id; static GList *modems; static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessageIter entry; const char *device; int fd; struct ofono_emulator *em; struct ofono_modem *modem; GList *last; DBG("Profile handler NewConnection"); if (dbus_message_iter_init(msg, &entry) == FALSE) goto invalid; if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_OBJECT_PATH) goto invalid; dbus_message_iter_get_basic(&entry, &device); dbus_message_iter_next(&entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_UNIX_FD) goto invalid; dbus_message_iter_get_basic(&entry, &fd); dbus_message_iter_next(&entry); if (fd < 0) goto invalid; DBG("%s", device); if (modems == NULL) { close(fd); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "No GPRS capable modem"); } /* * Pick the last one to avoid creating a new list with one modem just * for the call to ofono_emulator_create() (for this plugin we only * support registering one modem in the emulator) */ last = g_list_last(modems); modem = last->data; DBG("Picked modem %p for emulator", modem); em = ofono_emulator_create(last, OFONO_EMULATOR_TYPE_DUN); if (em == NULL) { close(fd); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Not enough resources"); } ofono_emulator_register(em, fd); return dbus_message_new_method_return(msg); invalid: return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Invalid arguments in method call"); } static DBusMessage *profile_release(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBG("Profile handler Release"); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".NotImplemented", "Implementation not provided"); } static DBusMessage *profile_cancel(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBG("Profile handler Cancel"); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".NotImplemented", "Implementation not provided"); } static DBusMessage *profile_disconnection(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBG("Profile handler RequestDisconnection"); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".NotImplemented", "Implementation not provided"); } static const GDBusMethodTable profile_methods[] = { { GDBUS_ASYNC_METHOD("NewConnection", GDBUS_ARGS({ "device", "o"}, { "fd", "h"}, { "fd_properties", "a{sv}" }), NULL, profile_new_connection) }, { GDBUS_METHOD("Release", NULL, NULL, profile_release) }, { GDBUS_METHOD("Cancel", NULL, NULL, profile_cancel) }, { GDBUS_METHOD("RequestDisconnection", GDBUS_ARGS({"device", "o"}), NULL, profile_disconnection) }, { } }; static void gprs_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_modem *modem = data; DBusConnection *conn = ofono_dbus_get_connection(); if (cond != OFONO_ATOM_WATCH_CONDITION_REGISTERED) { modems = g_list_remove(modems, modem); if (modems != NULL) return; bt_unregister_profile(conn, DUN_GW_EXT_PROFILE_PATH); return; } modems = g_list_append(modems, modem); if (modems->next == NULL) bt_register_profile(conn, DUN_GW_UUID, DUN_GW_VERSION_1_0, "dun_gw", DUN_GW_EXT_PROFILE_PATH, NULL, 0); } static void modem_watch(struct ofono_modem *modem, gboolean added, void *user) { DBG("modem: %p, added: %d", modem, added); if (added == FALSE) return; __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_GPRS, gprs_watch, modem, NULL); } static void call_modemwatch(struct ofono_modem *modem, void *user) { modem_watch(modem, TRUE, user); } static int dun_gw_init(void) { DBusConnection *conn = ofono_dbus_get_connection(); DBG(""); if (DBUS_TYPE_UNIX_FD < 0) return -EBADF; /* Registers External Profile handler */ if (!g_dbus_register_interface(conn, DUN_GW_EXT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE, profile_methods, NULL, NULL, NULL, NULL)) { ofono_error("Register Profile interface failed: %s", DUN_GW_EXT_PROFILE_PATH); return -EIO; } modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL); __ofono_modem_foreach(call_modemwatch, NULL); return 0; } static void dun_gw_exit(void) { DBusConnection *conn = ofono_dbus_get_connection(); __ofono_modemwatch_remove(modemwatch_id); g_list_free(modems); bt_unregister_profile(conn, DUN_GW_EXT_PROFILE_PATH); g_dbus_unregister_interface(conn, DUN_GW_EXT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE); } OFONO_PLUGIN_DEFINE(dun_gw_bluez5, "Dial-up Networking Profile Plugins", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, dun_gw_init, dun_gw_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/quectel.c0000644000015600001650000002143412671500024021672 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Philip Paeps. 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include static const char *cfun_prefix[] = { "+CFUN:", NULL }; static const char *cpin_prefix[] = { "+CPIN:", NULL }; static const char *none_prefix[] = { NULL }; struct quectel_data { GAtChat *modem; GAtChat *aux; guint cpin_ready; gboolean have_sim; }; static void quectel_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static int quectel_probe(struct ofono_modem *modem) { struct quectel_data *data; DBG("%p", modem); data = g_try_new0(struct quectel_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void quectel_remove(struct ofono_modem *modem) { struct quectel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (data->cpin_ready != 0) g_at_chat_unregister(data->aux, data->cpin_ready); ofono_modem_set_data(modem, NULL); g_at_chat_unref(data->aux); g_at_chat_unref(data->modem); g_free(data); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GAtSyntax *syntax; GIOChannel *channel; GAtChat *chat; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, quectel_debug, debug); return chat; } static void cpin_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct quectel_data *data = ofono_modem_get_data(modem); const char *sim_inserted; GAtResultIter iter; DBG("%p", modem); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CPIN:")) return; g_at_result_iter_next_unquoted_string(&iter, &sim_inserted); if (g_strcmp0(sim_inserted, "NOT INSERTED") != 0) data->have_sim = TRUE; ofono_modem_set_powered(modem, TRUE); /* Turn off the radio. */ g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix, NULL, NULL, NULL); g_at_chat_unregister(data->aux, data->cpin_ready); data->cpin_ready = 0; } static void cpin_query(gboolean ok, GAtResult *result, gpointer user_data) { DBG("ok %d", ok); if (ok) cpin_notify(result, user_data); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct quectel_data *data = ofono_modem_get_data(modem); DBG("ok %d", ok); if (!ok) { g_at_chat_unref(data->aux); data->aux = NULL; g_at_chat_unref(data->modem); data->modem = NULL; ofono_modem_set_powered(modem, FALSE); return; } data->cpin_ready = g_at_chat_register(data->aux, "+CPIN", cpin_notify, FALSE, modem, NULL); g_at_chat_send(data->aux, "AT+CPIN?", cpin_prefix, cpin_query, modem, NULL); } static void cfun_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct quectel_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int status; DBG("ok %d", ok); if (!ok) return; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "+CFUN:") == FALSE) return; g_at_result_iter_next_number(&iter, &status); /* * The modem firmware powers up in CFUN=1 but will respond to AT+CFUN=4 * with ERROR until some amount of time (which varies with temperature) * passes. Empirical evidence suggests that the firmware will report an * unsolicited +CPIN: notification when it is ready to be useful. * * Work around this feature by only transitioning to CFUN=4 after we've * received an unsolicited +CPIN: notification. */ if (status != 1) { g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix, cfun_enable, modem, NULL); return; } cfun_enable(TRUE, NULL, modem); } static int quectel_enable(struct ofono_modem *modem) { struct quectel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->aux = open_device(modem, "Aux", "Aux: "); if (data->aux == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_set_slave(data->modem, data->aux); g_at_chat_send(data->modem, "ATE0 &C0 +CMEE=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->aux, "ATE0 &C0 +CMEE=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CFUN?", cfun_prefix, cfun_query, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct quectel_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->aux); data->aux = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int quectel_disable(struct ofono_modem *modem) { struct quectel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->aux); g_at_chat_unregister_all(data->aux); g_at_chat_send(data->aux, "AT+CFUN=0", cfun_prefix, cfun_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void quectel_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct quectel_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4"; DBG("modem %p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(data->aux, command, cfun_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void quectel_pre_sim(struct ofono_modem *modem) { struct quectel_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->aux); sim = ofono_sim_create(modem, OFONO_VENDOR_QUECTEL, "atmodem", data->aux); if (sim && data->have_sim == TRUE) ofono_sim_inserted_notify(sim, TRUE); } static void quectel_post_sim(struct ofono_modem *modem) { struct quectel_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); gprs = ofono_gprs_create(modem, 0, "atmodem", data->aux); gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static void quectel_post_online(struct ofono_modem *modem) { struct quectel_data *data = ofono_modem_get_data(modem); ofono_netreg_create(modem, 0, "atmodem", data->aux); } static struct ofono_modem_driver quectel_driver = { .name = "quectel", .probe = quectel_probe, .remove = quectel_remove, .enable = quectel_enable, .disable = quectel_disable, .set_online = quectel_set_online, .pre_sim = quectel_pre_sim, .post_sim = quectel_post_sim, .post_online = quectel_post_online, }; static int quectel_init(void) { return ofono_modem_driver_register(&quectel_driver); } static void quectel_exit(void) { ofono_modem_driver_unregister(&quectel_driver); } OFONO_PLUGIN_DEFINE(quectel, "Quectel driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, quectel_init, quectel_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/qcom-msim.c0000644000015600001650000000452312671500073022136 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL-based devices: Qualcomm multi-sim modems * * Copyright (C) 2015 Ratchanan Srirattanamet. * * 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 #endif #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include "ofono.h" #include "drivers/rilmodem/vendor.h" #include "ril.h" static int qcom_msim_probe(struct ofono_modem *modem) { int slot_id = ofono_modem_get_integer(modem, "Slot"); /* qcom_msim has socket path "rild", "rild1", ... */ if (slot_id != 0) { char *socket; socket = g_strdup_printf("/dev/socket/rild%d", slot_id); ofono_modem_set_string(modem, "Socket", socket); g_free(socket); } return ril_create(modem, OFONO_RIL_VENDOR_QCOM_MSIM); } static struct ofono_modem_driver qcom_msim_driver = { .name = "qcom_msim", .probe = qcom_msim_probe, .remove = ril_remove, .enable = ril_enable, .disable = ril_disable, .pre_sim = ril_pre_sim, .post_sim = ril_post_sim, .post_online = ril_post_online, .set_online = ril_set_online, }; /* * This plugin is a device plugin for Qualcomm's multi-sim device that use * RIL interface. The plugin 'rildev' is used to determine which RIL plugin * should be loaded based upon an environment variable. */ static int qcom_msim_init(void) { int retval = ofono_modem_driver_register(&qcom_msim_driver); if (retval) DBG("ofono_modem_driver_register returned: %d", retval); return retval; } static void qcom_msim_exit(void) { DBG(""); ofono_modem_driver_unregister(&qcom_msim_driver); } OFONO_PLUGIN_DEFINE(qcom_msim, "Modem driver for Qualcomm's multi-sim device", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, qcom_msim_init, qcom_msim_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/samsung.c0000644000015600001650000001526012671500024021705 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; struct samsung_data { GAtChat *chat; gboolean have_sim; struct at_util_sim_state_query *sim_state_query; }; static void samsung_debug(const char *str, void *data) { const char *prefix = data; ofono_info("%s%s", prefix, str); } static int samsung_probe(struct ofono_modem *modem) { struct samsung_data *data; DBG("%p", modem); data = g_try_new0(struct samsung_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void samsung_remove(struct ofono_modem *modem) { struct samsung_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup potential SIM state polling */ at_util_sim_state_query_free(data->sim_state_query); /* Cleanup after hot-unplug */ g_at_chat_unref(data->chat); g_free(data); } static void mode_select(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct samsung_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (!ok) { g_at_chat_unref(data->chat); data->chat = NULL; ofono_modem_set_powered(modem, FALSE); return; } g_at_chat_send(data->chat, "AT+VERSNAME=1,0", NULL, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT+VERSNAME=1,1", NULL, NULL, NULL, NULL); ofono_modem_set_powered(modem, TRUE); } static void sim_state_cb(gboolean present, gpointer user_data) { struct ofono_modem *modem = user_data; struct samsung_data *data = ofono_modem_get_data(modem); DBG("%p", modem); at_util_sim_state_query_free(data->sim_state_query); data->sim_state_query = NULL; data->have_sim = present; g_at_chat_send(data->chat, "AT+MODESELECT=3", none_prefix, mode_select, modem, NULL); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct samsung_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (!ok) { g_at_chat_unref(data->chat); data->chat = NULL; ofono_modem_set_powered(modem, FALSE); return; } data->sim_state_query = at_util_sim_state_query_new(data->chat, 1, 5, sim_state_cb, modem, NULL); } static int samsung_enable(struct ofono_modem *modem) { struct samsung_data *data = ofono_modem_get_data(modem); GAtSyntax *syntax; GIOChannel *channel; GHashTable *options; const char *device; device = ofono_modem_get_string(modem, "ControlPort"); if (device == NULL) return -EINVAL; options = g_hash_table_new(g_str_hash, g_str_equal); if (options == NULL) return -ENOMEM; g_hash_table_insert(options, "Baud", "115200"); g_hash_table_insert(options, "Parity", "none"); g_hash_table_insert(options, "StopBits", "1"); g_hash_table_insert(options, "DataBits", "8"); g_hash_table_insert(options, "XonXoff", "off"); g_hash_table_insert(options, "RtsCts", "on"); g_hash_table_insert(options, "Local", "on"); g_hash_table_insert(options, "Read", "on"); channel = g_at_tty_open(device, options); g_hash_table_destroy(options); if (channel == NULL) return -EIO; syntax = g_at_syntax_new_gsm_permissive(); data->chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (data->chat == NULL) return -ENOMEM; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(data->chat, samsung_debug, "Device: "); g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT+CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT+CFUN=?", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT+CFUN?", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT+CFUN=5", none_prefix, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct samsung_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_unref(data->chat); data->chat = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int samsung_disable(struct ofono_modem *modem) { struct samsung_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->chat); g_at_chat_unregister_all(data->chat); g_at_chat_send(data->chat, "AT+MODESELECT=2", none_prefix, cfun_disable, modem, NULL); return -EINPROGRESS; } static void samsung_pre_sim(struct ofono_modem *modem) { struct samsung_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->chat); sim = ofono_sim_create(modem, 0, "atmodem", data->chat); if (sim && data->have_sim == TRUE) ofono_sim_inserted_notify(sim, TRUE); } static void samsung_post_sim(struct ofono_modem *modem) { DBG("%p", modem); } static void samsung_post_online(struct ofono_modem *modem) { struct samsung_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_netreg_create(modem, OFONO_VENDOR_SAMSUNG, "atmodem", data->chat); } static struct ofono_modem_driver samsung_driver = { .name = "samsung", .probe = samsung_probe, .remove = samsung_remove, .enable = samsung_enable, .disable = samsung_disable, .pre_sim = samsung_pre_sim, .post_sim = samsung_post_sim, .post_online = samsung_post_online, }; static int samsung_init(void) { return ofono_modem_driver_register(&samsung_driver); } static void samsung_exit(void) { ofono_modem_driver_unregister(&samsung_driver); } OFONO_PLUGIN_DEFINE(samsung, "Samsung modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, samsung_init, samsung_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/sierra.c0000644000015600001650000001564112671500024021520 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include static const char *none_prefix[] = { NULL }; struct sierra_data { GAtChat *modem; gboolean have_sim; struct at_util_sim_state_query *sim_state_query; }; static void sierra_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static int sierra_probe(struct ofono_modem *modem) { struct sierra_data *data; DBG("%p", modem); data = g_try_new0(struct sierra_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void sierra_remove(struct ofono_modem *modem) { struct sierra_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup potential SIM state polling */ at_util_sim_state_query_free(data->sim_state_query); /* Cleanup after hot-unplug */ g_at_chat_unref(data->modem); g_free(data); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GAtSyntax *syntax; GIOChannel *channel; GAtChat *chat; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, sierra_debug, debug); return chat; } static void sim_state_cb(gboolean present, gpointer user_data) { struct ofono_modem *modem = user_data; struct sierra_data *data = ofono_modem_get_data(modem); DBG("%p", modem); at_util_sim_state_query_free(data->sim_state_query); data->sim_state_query = NULL; data->have_sim = present; ofono_modem_set_powered(modem, TRUE); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct sierra_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->modem); data->modem = NULL; } data->sim_state_query = at_util_sim_state_query_new(data->modem, 2, 20, sim_state_cb, modem, NULL); } static int sierra_enable(struct ofono_modem *modem) { struct sierra_data *data = ofono_modem_get_data(modem); DBG("%p", modem); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; g_at_chat_send(data->modem, "ATE0 &C0", NULL, NULL, NULL, NULL); /* This is separate because it is not supported by all modems. */ g_at_chat_send(data->modem, "AT+CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->modem, "AT+CFUN=4", none_prefix, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct sierra_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->modem); data->modem = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int sierra_disable(struct ofono_modem *modem) { struct sierra_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_send(data->modem, "AT+CFUN=0", none_prefix, cfun_disable, modem, NULL); return -EINPROGRESS; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void sierra_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct sierra_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4"; DBG("modem %p %s", modem, online ? "online" : "offline"); if (g_at_chat_send(data->modem, command, none_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void sierra_pre_sim(struct ofono_modem *modem) { struct sierra_data *data = ofono_modem_get_data(modem); struct ofono_sim *sim; DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->modem); sim = ofono_sim_create(modem, OFONO_VENDOR_SIERRA, "atmodem", data->modem); if (sim && data->have_sim == TRUE) ofono_sim_inserted_notify(sim, TRUE); } static void sierra_post_sim(struct ofono_modem *modem) { struct sierra_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); ofono_phonebook_create(modem, 0, "atmodem", data->modem); gprs = ofono_gprs_create(modem, 0, "atmodem", data->modem); gc = ofono_gprs_context_create(modem, 0, "swmodem", data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } static void sierra_post_online(struct ofono_modem *modem) { struct sierra_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_netreg_create(modem, 0, "atmodem", data->modem); } static struct ofono_modem_driver sierra_driver = { .name = "sierra", .probe = sierra_probe, .remove = sierra_remove, .enable = sierra_enable, .disable = sierra_disable, .set_online = sierra_set_online, .pre_sim = sierra_pre_sim, .post_sim = sierra_post_sim, .post_online = sierra_post_online, }; static int sierra_init(void) { return ofono_modem_driver_register(&sierra_driver); } static void sierra_exit(void) { ofono_modem_driver_unregister(&sierra_driver); } OFONO_PLUGIN_DEFINE(sierra, "Sierra Wireless modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, sierra_init, sierra_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/phonesim.conf0000644000015600001650000000055112671500024022552 0ustar pbuserpbgroup00000000000000# This is a sample file for the phonesim configuration # # It should be installed in your oFono system directory, # e.g. /etc/ofono/phonesim.conf # # Each group is parsed as a modem device # # Each group shall at least define the address and port # Address = # Port = #[phonesim] #Address=127.0.0.1 #Port=12345 ofono-1.17.bzr6912+16.04.20160314.3/plugins/gobi.c0000644000015600001650000002611412671500024021150 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 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 #endif #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GOBI_DMS (1 << 0) #define GOBI_NAS (1 << 1) #define GOBI_WMS (1 << 2) #define GOBI_WDS (1 << 3) #define GOBI_PDS (1 << 4) #define GOBI_PBM (1 << 5) #define GOBI_UIM (1 << 6) #define GOBI_CAT (1 << 7) #define GOBI_CAT_OLD (1 << 8) #define GOBI_VOICE (1 << 9) struct gobi_data { struct qmi_device *device; struct qmi_service *dms; unsigned long features; unsigned int discover_attempts; uint8_t oper_mode; }; static void gobi_debug(const char *str, void *user_data) { const char *prefix = user_data; ofono_info("%s%s", prefix, str); } static int gobi_probe(struct ofono_modem *modem) { struct gobi_data *data; DBG("%p", modem); data = g_try_new0(struct gobi_data, 1); if (!data) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void gobi_remove(struct ofono_modem *modem) { struct gobi_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); qmi_service_unref(data->dms); qmi_device_unref(data->device); g_free(data); } static void shutdown_cb(void *user_data) { struct ofono_modem *modem = user_data; struct gobi_data *data = ofono_modem_get_data(modem); DBG(""); data->discover_attempts = 0; qmi_device_unref(data->device); data->device = NULL; ofono_modem_set_powered(modem, FALSE); } static void shutdown_device(struct ofono_modem *modem) { struct gobi_data *data = ofono_modem_get_data(modem); DBG("%p", modem); qmi_service_unref(data->dms); data->dms = NULL; qmi_device_shutdown(data->device, shutdown_cb, modem, NULL); } static void power_reset_cb(struct qmi_result *result, void *user_data) { struct ofono_modem *modem = user_data; DBG(""); if (qmi_result_set_error(result, NULL)) { shutdown_device(modem); return; } ofono_modem_set_powered(modem, TRUE); } static void get_oper_mode_cb(struct qmi_result *result, void *user_data) { struct ofono_modem *modem = user_data; struct gobi_data *data = ofono_modem_get_data(modem); struct qmi_param *param; uint8_t mode; DBG(""); if (qmi_result_set_error(result, NULL)) { shutdown_device(modem); return; } if (!qmi_result_get_uint8(result, QMI_DMS_RESULT_OPER_MODE, &mode)) { shutdown_device(modem); return; } data->oper_mode = mode; switch (data->oper_mode) { case QMI_DMS_OPER_MODE_ONLINE: param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE, QMI_DMS_OPER_MODE_PERSIST_LOW_POWER); if (!param) { shutdown_device(modem); return; } if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param, power_reset_cb, modem, NULL) > 0) return; shutdown_device(modem); break; default: ofono_modem_set_powered(modem, TRUE); break; } } static void get_caps_cb(struct qmi_result *result, void *user_data) { struct ofono_modem *modem = user_data; struct gobi_data *data = ofono_modem_get_data(modem); const struct qmi_dms_device_caps *caps; 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; DBG("service capabilities %d", caps->data_capa); DBG("sim supported %d", caps->sim_supported); for (i = 0; i < caps->radio_if_count; i++) DBG("radio = %d", caps->radio_if[i]); if (qmi_service_send(data->dms, QMI_DMS_GET_OPER_MODE, NULL, get_oper_mode_cb, modem, NULL) > 0) return; error: shutdown_device(modem); } static void create_dms_cb(struct qmi_service *service, void *user_data) { struct ofono_modem *modem = user_data; struct gobi_data *data = ofono_modem_get_data(modem); DBG(""); if (!service) goto error; data->dms = qmi_service_ref(service); if (qmi_service_send(data->dms, QMI_DMS_GET_CAPS, NULL, get_caps_cb, modem, NULL) > 0) return; error: shutdown_device(modem); } static void discover_cb(uint8_t count, const struct qmi_version *list, void *user_data) { struct ofono_modem *modem = user_data; struct gobi_data *data = ofono_modem_get_data(modem); uint8_t i; DBG(""); for (i = 0; i < count; i++) { DBG("%s %d.%d", list[i].name, list[i].major, list[i].minor); switch (list[i].type) { case QMI_SERVICE_DMS: data->features |= GOBI_DMS; break; case QMI_SERVICE_NAS: data->features |= GOBI_NAS; break; case QMI_SERVICE_WMS: data->features |= GOBI_WMS; break; case QMI_SERVICE_WDS: data->features |= GOBI_WDS; break; case QMI_SERVICE_PDS: data->features |= GOBI_PDS; break; case QMI_SERVICE_PBM: data->features |= GOBI_PBM; break; case QMI_SERVICE_UIM: data->features |= GOBI_UIM; break; case QMI_SERVICE_CAT: data->features |= GOBI_CAT; break; case QMI_SERVICE_CAT_OLD: if (list[i].major > 0) data->features |= GOBI_CAT_OLD; break; case QMI_SERVICE_VOICE: data->features |= GOBI_VOICE; break; } } if (!(data->features & GOBI_DMS)) { if (++data->discover_attempts < 3) { qmi_device_discover(data->device, discover_cb, modem, NULL); return; } shutdown_device(modem); return; } qmi_service_create_shared(data->device, QMI_SERVICE_DMS, create_dms_cb, modem, NULL); } static int gobi_enable(struct ofono_modem *modem) { struct gobi_data *data = ofono_modem_get_data(modem); const char *device; int fd; DBG("%p", modem); device = ofono_modem_get_string(modem, "Device"); if (!device) return -EINVAL; fd = open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd < 0) return -EIO; data->device = qmi_device_new(fd); if (!data->device) { close(fd); return -ENOMEM; } if (getenv("OFONO_QMI_DEBUG")) qmi_device_set_debug(data->device, gobi_debug, "QMI: "); qmi_device_set_close_on_unref(data->device, true); qmi_device_discover(data->device, discover_cb, modem, NULL); return -EINPROGRESS; } static void power_disable_cb(struct qmi_result *result, void *user_data) { struct ofono_modem *modem = user_data; DBG(""); shutdown_device(modem); } static int gobi_disable(struct ofono_modem *modem) { struct gobi_data *data = ofono_modem_get_data(modem); struct qmi_param *param; DBG("%p", modem); qmi_service_cancel_all(data->dms); qmi_service_unregister_all(data->dms); param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE, QMI_DMS_OPER_MODE_PERSIST_LOW_POWER); if (!param) return -ENOMEM; if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param, power_disable_cb, modem, NULL) > 0) return -EINPROGRESS; shutdown_device(modem); return -EINPROGRESS; } static void set_online_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t cb = cbd->cb; DBG(""); if (qmi_result_set_error(result, NULL)) CALLBACK_WITH_FAILURE(cb, cbd->data); else CALLBACK_WITH_SUCCESS(cb, cbd->data); } static void gobi_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct gobi_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); struct qmi_param *param; uint8_t mode; DBG("%p %s", modem, online ? "online" : "offline"); if (online) mode = QMI_DMS_OPER_MODE_ONLINE; else mode = QMI_DMS_OPER_MODE_LOW_POWER; param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE, mode); if (!param) goto error; if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param, set_online_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void gobi_pre_sim(struct ofono_modem *modem) { struct gobi_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_devinfo_create(modem, 0, "qmimodem", data->device); if (data->features & GOBI_UIM) ofono_sim_create(modem, 0, "qmimodem", data->device); else if (data->features & GOBI_DMS) ofono_sim_create(modem, 0, "qmimodem-legacy", data->device); if (data->features & GOBI_VOICE) ofono_voicecall_create(modem, 0, "qmimodem", data->device); if (data->features & GOBI_PDS) ofono_location_reporting_create(modem, 0, "qmimodem", data->device); } static void gobi_post_sim(struct ofono_modem *modem) { struct gobi_data *data = ofono_modem_get_data(modem); DBG("%p", modem); if (data->features & GOBI_CAT) ofono_stk_create(modem, 0, "qmimodem", data->device); else if (data->features & GOBI_CAT_OLD) ofono_stk_create(modem, 1, "qmimodem", data->device); if (data->features & GOBI_PBM) ofono_phonebook_create(modem, 0, "qmimodem", data->device); if (data->features & GOBI_NAS) ofono_radio_settings_create(modem, 0, "qmimodem", data->device); if (data->features & GOBI_WMS) ofono_sms_create(modem, 0, "qmimodem", data->device); } static void gobi_post_online(struct ofono_modem *modem) { struct gobi_data *data = ofono_modem_get_data(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; DBG("%p", modem); if (data->features & GOBI_NAS) ofono_netreg_create(modem, 0, "qmimodem", data->device); if (data->features & GOBI_VOICE) ofono_ussd_create(modem, 0, "qmimodem", data->device); if (data->features & GOBI_WDS) { gprs = ofono_gprs_create(modem, 0, "qmimodem", data->device); gc = ofono_gprs_context_create(modem, 0, "qmimodem", data->device); if (gprs && gc) ofono_gprs_add_context(gprs, gc); } } static struct ofono_modem_driver gobi_driver = { .name = "gobi", .probe = gobi_probe, .remove = gobi_remove, .enable = gobi_enable, .disable = gobi_disable, .set_online = gobi_set_online, .pre_sim = gobi_pre_sim, .post_sim = gobi_post_sim, .post_online = gobi_post_online, }; static int gobi_init(void) { return ofono_modem_driver_register(&gobi_driver); } static void gobi_exit(void) { ofono_modem_driver_unregister(&gobi_driver); } OFONO_PLUGIN_DEFINE(gobi, "Qualcomm Gobi modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, gobi_init, gobi_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/udevng.c0000644000015600001650000010525312671500024021522 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include struct modem_info { char *syspath; char *devname; char *driver; char *vendor; char *model; GSList *devices; struct ofono_modem *modem; const char *sysattr; }; struct device_info { char *devpath; char *devnode; char *devtype; char *interface; char *number; char *label; char *sysattr; }; static gboolean setup_isi(struct modem_info *modem) { const char *node = NULL; int addr = 0; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s %s", info->devnode, info->interface, info->number, info->label, info->sysattr); if (g_strcmp0(info->sysattr, "820") == 0) { if (g_strcmp0(info->interface, "2/254/0") == 0) addr = 16; node = info->devnode; } } if (node == NULL) return FALSE; DBG("interface=%s address=%d", node, addr); ofono_modem_set_string(modem->modem, "Interface", node); ofono_modem_set_integer(modem->modem, "Address", addr); return TRUE; } static gboolean setup_mbm(struct modem_info *modem) { const char *mdm = NULL, *app = NULL, *network = NULL, *gps = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s %s", info->devnode, info->interface, info->number, info->label, info->sysattr); if (g_str_has_suffix(info->sysattr, "Modem") == TRUE || g_str_has_suffix(info->sysattr, "Modem 2") == TRUE) { if (mdm == NULL) mdm = info->devnode; else app = info->devnode; } else if (g_str_has_suffix(info->sysattr, "GPS Port") == TRUE || g_str_has_suffix(info->sysattr, "Module NMEA") == TRUE) { gps = info->devnode; } else if (g_str_has_suffix(info->sysattr, "Network Adapter") == TRUE || g_str_has_suffix(info->sysattr, "gw") == TRUE || g_str_has_suffix(info->sysattr, "NetworkAdapter") == TRUE || g_strcmp0(info->devtype, "wwan") == 0) { network = info->devnode; } } if (mdm == NULL || app == NULL) return FALSE; DBG("modem=%s data=%s network=%s gps=%s", mdm, app, network, gps); ofono_modem_set_string(modem->modem, "ModemDevice", mdm); ofono_modem_set_string(modem->modem, "DataDevice", app); ofono_modem_set_string(modem->modem, "GPSDevice", gps); ofono_modem_set_string(modem->modem, "NetworkInterface", network); return TRUE; } static gboolean setup_hso(struct modem_info *modem) { const char *ctl = NULL, *app = NULL, *mdm = NULL, *net = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s %s", info->devnode, info->interface, info->number, info->label, info->sysattr); if (g_strcmp0(info->sysattr, "Control") == 0) ctl = info->devnode; else if (g_strcmp0(info->sysattr, "Application") == 0) app = info->devnode; else if (g_strcmp0(info->sysattr, "Modem") == 0) mdm = info->devnode; else if (info->sysattr == NULL && g_str_has_prefix(info->devnode, "hso") == TRUE) net = info->devnode; } if (ctl == NULL || app == NULL) return FALSE; DBG("control=%s application=%s modem=%s network=%s", ctl, app, mdm, net); ofono_modem_set_string(modem->modem, "Control", ctl); ofono_modem_set_string(modem->modem, "Application", app); ofono_modem_set_string(modem->modem, "Modem", mdm); ofono_modem_set_string(modem->modem, "NetworkInterface", net); return TRUE; } static gboolean setup_gobi(struct modem_info *modem) { const char *qmi = NULL, *mdm = NULL, *net = NULL; const char *gps = NULL, *diag = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->interface, "255/255/255") == 0) { if (info->number == NULL) qmi = info->devnode; else if (g_strcmp0(info->number, "00") == 0) net = info->devnode; else if (g_strcmp0(info->number, "01") == 0) diag = info->devnode; else if (g_strcmp0(info->number, "02") == 0) mdm = info->devnode; else if (g_strcmp0(info->number, "03") == 0) gps = info->devnode; } } if (qmi == NULL || mdm == NULL || net == NULL) return FALSE; DBG("qmi=%s net=%s mdm=%s gps=%s diag=%s", qmi, net, mdm, gps, diag); ofono_modem_set_string(modem->modem, "Device", qmi); ofono_modem_set_string(modem->modem, "Modem", mdm); ofono_modem_set_string(modem->modem, "Diag", diag); ofono_modem_set_string(modem->modem, "NetworkInterface", net); return TRUE; } static gboolean setup_sierra(struct modem_info *modem) { const char *mdm = NULL, *app = NULL, *net = NULL, *diag = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->number, "01") == 0) diag = info->devnode; if (g_strcmp0(info->number, "03") == 0) mdm = info->devnode; else if (g_strcmp0(info->number, "04") == 0) app = info->devnode; else if (g_strcmp0(info->number, "07") == 0) net = info->devnode; } } if (mdm == NULL || net == NULL) return FALSE; DBG("modem=%s app=%s net=%s diag=%s", mdm, app, net, diag); ofono_modem_set_string(modem->modem, "Modem", mdm); ofono_modem_set_string(modem->modem, "App", app); ofono_modem_set_string(modem->modem, "Diag", diag); ofono_modem_set_string(modem->modem, "NetworkInterface", net); return TRUE; } static gboolean setup_option(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL, *diag = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->number, "00") == 0) mdm = info->devnode; else if (g_strcmp0(info->number, "01") == 0) diag = info->devnode; else if (g_strcmp0(info->number, "02") == 0) aux = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("aux=%s modem=%s diag=%s", aux, mdm, diag); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "Modem", mdm); ofono_modem_set_string(modem->modem, "Diag", diag); return TRUE; } static gboolean setup_huawei(struct modem_info *modem) { const char *qmi = NULL, *mdm = NULL, *net = NULL; const char *pcui = NULL, *diag = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->label, "modem") == 0 || g_strcmp0(info->interface, "255/1/1") == 0 || g_strcmp0(info->interface, "255/2/1") == 0 || g_strcmp0(info->interface, "255/1/49") == 0) { mdm = info->devnode; } else if (g_strcmp0(info->label, "pcui") == 0 || g_strcmp0(info->interface, "255/1/2") == 0 || g_strcmp0(info->interface, "255/2/2") == 0 || g_strcmp0(info->interface, "255/1/50") == 0) { pcui = info->devnode; } else if (g_strcmp0(info->label, "diag") == 0 || g_strcmp0(info->interface, "255/1/3") == 0 || g_strcmp0(info->interface, "255/2/3") == 0 || g_strcmp0(info->interface, "255/1/51") == 0) { diag = info->devnode; } else if (g_strcmp0(info->interface, "255/1/8") == 0 || g_strcmp0(info->interface, "255/1/56") == 0) { net = info->devnode; } else if (g_strcmp0(info->interface, "255/1/9") == 0 || g_strcmp0(info->interface, "255/1/57") == 0) { qmi = info->devnode; } else if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->number, "00") == 0) mdm = info->devnode; else if (g_strcmp0(info->number, "01") == 0) pcui = info->devnode; else if (g_strcmp0(info->number, "02") == 0) pcui = info->devnode; else if (g_strcmp0(info->number, "03") == 0) pcui = info->devnode; else if (g_strcmp0(info->number, "04") == 0) pcui = info->devnode; } } if (qmi != NULL && net != NULL) { ofono_modem_set_driver(modem->modem, "gobi"); goto done; } if (mdm == NULL || pcui == NULL) return FALSE; done: DBG("mdm=%s pcui=%s diag=%s qmi=%s net=%s", mdm, pcui, diag, qmi, net); ofono_modem_set_string(modem->modem, "Device", qmi); ofono_modem_set_string(modem->modem, "Modem", mdm); ofono_modem_set_string(modem->modem, "Pcui", pcui); ofono_modem_set_string(modem->modem, "Diag", diag); ofono_modem_set_string(modem->modem, "NetworkInterface", net); return TRUE; } static gboolean setup_speedup(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->label, "aux") == 0) { aux = info->devnode; if (mdm != NULL) break; } else if (g_strcmp0(info->label, "modem") == 0) { mdm = info->devnode; if (aux != NULL) break; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("aux=%s modem=%s", aux, mdm); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "Modem", mdm); return TRUE; } static gboolean setup_linktop(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->interface, "2/2/1") == 0) { if (g_strcmp0(info->number, "01") == 0) aux = info->devnode; else if (g_strcmp0(info->number, "03") == 0) mdm = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("aux=%s modem=%s", aux, mdm); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "Modem", mdm); return TRUE; } static gboolean setup_icera(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL, *net = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->interface, "2/2/1") == 0) { if (g_strcmp0(info->number, "00") == 0) aux = info->devnode; else if (g_strcmp0(info->number, "01") == 0) aux = info->devnode; else if (g_strcmp0(info->number, "02") == 0) mdm = info->devnode; else if (g_strcmp0(info->number, "03") == 0) mdm = info->devnode; } else if (g_strcmp0(info->interface, "2/6/0") == 0) { if (g_strcmp0(info->number, "05") == 0) net = info->devnode; else if (g_strcmp0(info->number, "06") == 0) net = info->devnode; else if (g_strcmp0(info->number, "07") == 0) net = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("aux=%s modem=%s net=%s", aux, mdm, net); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "Modem", mdm); ofono_modem_set_string(modem->modem, "NetworkInterface", net); return TRUE; } static gboolean setup_alcatel(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->label, "aux") == 0) { aux = info->devnode; if (mdm != NULL) break; } else if (g_strcmp0(info->label, "modem") == 0) { mdm = info->devnode; if (aux != NULL) break; } else if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->number, "03") == 0) aux = info->devnode; else if (g_strcmp0(info->number, "05") == 0) mdm = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("aux=%s modem=%s", aux, mdm); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "Modem", mdm); return TRUE; } static gboolean setup_novatel(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->label, "aux") == 0) { aux = info->devnode; if (mdm != NULL) break; } else if (g_strcmp0(info->label, "modem") == 0) { mdm = info->devnode; if (aux != NULL) break; } else if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->number, "00") == 0) aux = info->devnode; else if (g_strcmp0(info->number, "01") == 0) mdm = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("aux=%s modem=%s", aux, mdm); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "Modem", mdm); return TRUE; } static gboolean setup_nokia(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->label, "aux") == 0) { aux = info->devnode; if (mdm != NULL) break; } else if (g_strcmp0(info->label, "modem") == 0) { mdm = info->devnode; if (aux != NULL) break; } else if (g_strcmp0(info->interface, "10/0/0") == 0) { if (g_strcmp0(info->number, "02") == 0) mdm = info->devnode; else if (g_strcmp0(info->number, "04") == 0) aux = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("aux=%s modem=%s", aux, mdm); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "Modem", mdm); return TRUE; } static gboolean setup_telit(struct modem_info *modem) { const char *mdm = NULL, *aux = NULL, *gps = NULL, *diag = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->label, "aux") == 0) { aux = info->devnode; if (mdm != NULL) break; } else if (g_strcmp0(info->label, "modem") == 0) { mdm = info->devnode; if (aux != NULL) break; } else if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->number, "00") == 0) mdm = info->devnode; else if (g_strcmp0(info->number, "01") == 0) diag = info->devnode; else if (g_strcmp0(info->number, "02") == 0) gps = info->devnode; else if (g_strcmp0(info->number, "03") == 0) aux = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("modem=%s aux=%s gps=%s diag=%s", mdm, aux, gps, diag); ofono_modem_set_string(modem->modem, "Modem", mdm); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "GPS", gps); return TRUE; } static gboolean setup_he910(struct modem_info *modem) { const char *mdm = NULL, *aux = NULL, *gps = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->interface, "2/2/1") == 0) { if (g_strcmp0(info->number, "00") == 0) mdm = info->devnode; else if (g_strcmp0(info->number, "06") == 0) aux = info->devnode; else if (g_strcmp0(info->number, "0a") == 0) gps = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("modem=%s aux=%s gps=%s", mdm, aux, gps); ofono_modem_set_string(modem->modem, "Modem", mdm); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "GPS", gps); return TRUE; } static gboolean setup_simcom(struct modem_info *modem) { const char *mdm = NULL, *aux = NULL, *gps = NULL, *diag = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->label, "aux") == 0) { aux = info->devnode; if (mdm != NULL) break; } else if (g_strcmp0(info->label, "modem") == 0) { mdm = info->devnode; if (aux != NULL) break; } else if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->number, "00") == 0) diag = info->devnode; else if (g_strcmp0(info->number, "01") == 0) gps = info->devnode; else if (g_strcmp0(info->number, "02") == 0) aux = info->devnode; else if (g_strcmp0(info->number, "03") == 0) mdm = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("modem=%s aux=%s gps=%s diag=%s", mdm, aux, gps, diag); ofono_modem_set_string(modem->modem, "Modem", mdm); ofono_modem_set_string(modem->modem, "Data", aux); ofono_modem_set_string(modem->modem, "GPS", gps); return TRUE; } static gboolean setup_zte(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL, *qcdm = NULL; const char *modem_intf; GSList *list; DBG("%s", modem->syspath); if (g_strcmp0(modem->model, "0016") == 0 || g_strcmp0(modem->model, "0017") == 0 || g_strcmp0(modem->model, "0117") == 0) modem_intf = "02"; else modem_intf = "03"; for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->label, "aux") == 0) { aux = info->devnode; if (mdm != NULL) break; } else if (g_strcmp0(info->label, "modem") == 0) { mdm = info->devnode; if (aux != NULL) break; } else if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->number, "00") == 0) qcdm = info->devnode; else if (g_strcmp0(info->number, "01") == 0) aux = info->devnode; else if (g_strcmp0(info->number, modem_intf) == 0) mdm = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("aux=%s modem=%s qcdm=%s", aux, mdm, qcdm); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "Modem", mdm); return TRUE; } static gboolean setup_samsung(struct modem_info *modem) { const char *control = NULL, *network = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->interface, "10/0/0") == 0) control = info->devnode; else if (g_strcmp0(info->interface, "255/0/0") == 0) network = info->devnode; } if (control == NULL && network == NULL) return FALSE; DBG("control=%s network=%s", control, network); ofono_modem_set_string(modem->modem, "ControlPort", control); ofono_modem_set_string(modem->modem, "NetworkInterface", network); return TRUE; } static gboolean setup_quectel(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->label, "aux") == 0) { aux = info->devnode; if (mdm != NULL) break; } else if (g_strcmp0(info->label, "modem") == 0) { mdm = info->devnode; if (aux != NULL) break; } else if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->number, "02") == 0) aux = info->devnode; else if (g_strcmp0(info->number, "03") == 0) mdm = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("aux=%s modem=%s", aux, mdm); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "Modem", mdm); return TRUE; } static gboolean setup_ublox(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL; GSList *list; DBG("%s", modem->syspath); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s %s %s %s", info->devnode, info->interface, info->number, info->label); if (g_strcmp0(info->label, "aux") == 0) { aux = info->devnode; if (mdm != NULL) break; } else if (g_strcmp0(info->label, "modem") == 0) { mdm = info->devnode; if (aux != NULL) break; } else if (g_strcmp0(info->interface, "2/2/1") == 0) { if (g_strcmp0(info->number, "02") == 0) aux = info->devnode; else if (g_strcmp0(info->number, "00") == 0) mdm = info->devnode; } } if (aux == NULL || mdm == NULL) return FALSE; DBG("aux=%s modem=%s", aux, mdm); ofono_modem_set_string(modem->modem, "Aux", aux); ofono_modem_set_string(modem->modem, "Modem", mdm); return TRUE; } static struct { const char *name; gboolean (*setup)(struct modem_info *modem); const char *sysattr; } driver_list[] = { { "isiusb", setup_isi, "type" }, { "mbm", setup_mbm, "device/interface" }, { "hso", setup_hso, "hsotype" }, { "gobi", setup_gobi }, { "sierra", setup_sierra }, { "option", setup_option }, { "huawei", setup_huawei }, { "speedupcdma",setup_speedup }, { "speedup", setup_speedup }, { "linktop", setup_linktop }, { "alcatel", setup_alcatel }, { "novatel", setup_novatel }, { "nokia", setup_nokia }, { "telit", setup_telit }, { "he910", setup_he910 }, { "simcom", setup_simcom }, { "zte", setup_zte }, { "icera", setup_icera }, { "samsung", setup_samsung }, { "quectel", setup_quectel }, { "ublox", setup_ublox }, { } }; static GHashTable *modem_list; static const char *get_sysattr(const char *driver) { unsigned int i; for (i = 0; driver_list[i].name; i++) { if (g_str_equal(driver_list[i].name, driver) == TRUE) return driver_list[i].sysattr; } return NULL; } static void destroy_modem(gpointer data) { struct modem_info *modem = data; GSList *list; DBG("%s", modem->syspath); ofono_modem_remove(modem->modem); for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; DBG("%s", info->devnode); g_free(info->devpath); g_free(info->devnode); g_free(info->interface); g_free(info->number); g_free(info->label); g_free(info->sysattr); g_free(info); list->data = NULL; } g_slist_free(modem->devices); g_free(modem->syspath); g_free(modem->devname); g_free(modem->driver); g_free(modem->vendor); g_free(modem->model); g_free(modem); } static gboolean check_remove(gpointer key, gpointer value, gpointer user_data) { struct modem_info *modem = value; const char *devpath = user_data; GSList *list; for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; if (g_strcmp0(info->devpath, devpath) == 0) return TRUE; } return FALSE; } static void remove_device(struct udev_device *device) { const char *syspath; syspath = udev_device_get_syspath(device); if (syspath == NULL) return; DBG("%s", syspath); g_hash_table_foreach_remove(modem_list, check_remove, (char *) syspath); } static gint compare_device(gconstpointer a, gconstpointer b) { const struct device_info *info1 = a; const struct device_info *info2 = b; return g_strcmp0(info1->number, info2->number); } static void add_device(const char *syspath, const char *devname, const char *driver, const char *vendor, const char *model, struct udev_device *device) { struct udev_device *intf; const char *devpath, *devnode, *devtype, *interface, *number, *label, *sysattr; struct modem_info *modem; struct device_info *info; devpath = udev_device_get_syspath(device); if (devpath == NULL) return; devnode = udev_device_get_devnode(device); if (devnode == NULL) { devnode = udev_device_get_property_value(device, "INTERFACE"); if (devnode == NULL) return; } intf = udev_device_get_parent_with_subsystem_devtype(device, "usb", "usb_interface"); if (intf == NULL) return; modem = g_hash_table_lookup(modem_list, syspath); if (modem == NULL) { modem = g_try_new0(struct modem_info, 1); if (modem == NULL) return; modem->syspath = g_strdup(syspath); modem->devname = g_strdup(devname); modem->driver = g_strdup(driver); modem->vendor = g_strdup(vendor); modem->model = g_strdup(model); modem->sysattr = get_sysattr(driver); g_hash_table_replace(modem_list, modem->syspath, modem); } interface = udev_device_get_property_value(intf, "INTERFACE"); number = udev_device_get_property_value(device, "ID_USB_INTERFACE_NUM"); label = udev_device_get_property_value(device, "OFONO_LABEL"); if (modem->sysattr != NULL) sysattr = udev_device_get_sysattr_value(device, modem->sysattr); else sysattr = NULL; devtype = udev_device_get_devtype(device); DBG("%s", syspath); DBG("%s", devpath); DBG("%s: %s (%s) %s [%s] ==> %s %s", devtype, devnode, driver, interface, number, label, sysattr); info = g_try_new0(struct device_info, 1); if (info == NULL) return; info->devpath = g_strdup(devpath); info->devnode = g_strdup(devnode); info->devtype = g_strdup(devtype); info->interface = g_strdup(interface); info->number = g_strdup(number); info->label = g_strdup(label); info->sysattr = g_strdup(sysattr); modem->devices = g_slist_insert_sorted(modem->devices, info, compare_device); } static struct { const char *driver; const char *drv; const char *vid; const char *pid; } vendor_list[] = { { "isiusb", "cdc_phonet" }, { "linktop", "cdc_acm", "230d" }, { "icera", "cdc_acm", "19d2" }, { "icera", "cdc_ether", "19d2" }, { "icera", "cdc_acm", "04e8", "6872" }, { "icera", "cdc_ether", "04e8", "6872" }, { "icera", "cdc_acm", "0421", "0633" }, { "icera", "cdc_ether", "0421", "0633" }, { "mbm", "cdc_acm", "0bdb" }, { "mbm", "cdc_ether", "0bdb" }, { "mbm", "cdc_ncm", "0bdb" }, { "mbm", "cdc_acm", "0fce" }, { "mbm", "cdc_ether", "0fce" }, { "mbm", "cdc_ncm", "0fce" }, { "mbm", "cdc_acm", "413c" }, { "mbm", "cdc_ether", "413c" }, { "mbm", "cdc_ncm", "413c" }, { "mbm", "cdc_acm", "03f0" }, { "mbm", "cdc_ether", "03f0" }, { "mbm", "cdc_ncm", "03f0" }, { "mbm", "cdc_acm", "0930" }, { "mbm", "cdc_ether", "0930" }, { "mbm", "cdc_ncm", "0930" }, { "hso", "hso" }, { "gobi", "qmi_wwan" }, { "gobi", "qcserial" }, { "sierra", "sierra" }, { "sierra", "sierra_net" }, { "option", "option", "0af0" }, { "huawei", "option", "201e" }, { "huawei", "cdc_wdm", "12d1" }, { "huawei", "cdc_ether", "12d1" }, { "huawei", "qmi_wwan", "12d1" }, { "huawei", "option", "12d1" }, { "speedupcdma","option", "1c9e", "9e00" }, { "speedup", "option", "1c9e" }, { "speedup", "option", "2020" }, { "alcatel", "option", "1bbb", "0017" }, { "novatel", "option", "1410" }, { "zte", "option", "19d2" }, { "simcom", "option", "05c6", "9000" }, { "telit", "usbserial", "1bc7" }, { "telit", "option", "1bc7" }, { "he910", "cdc_acm", "1bc7", "0021" }, { "nokia", "option", "0421", "060e" }, { "nokia", "option", "0421", "0623" }, { "samsung", "option", "04e8", "6889" }, { "samsung", "kalmia" }, { "quectel", "option", "05c6", "9090" }, { "ublox", "cdc_acm", "1546", "1102" }, { } }; static void check_usb_device(struct udev_device *device) { struct udev_device *usb_device; const char *syspath, *devname, *driver; const char *vendor = NULL, *model = NULL; usb_device = udev_device_get_parent_with_subsystem_devtype(device, "usb", "usb_device"); if (usb_device == NULL) return; syspath = udev_device_get_syspath(usb_device); if (syspath == NULL) return; devname = udev_device_get_devnode(usb_device); if (devname == NULL) return; driver = udev_device_get_property_value(usb_device, "OFONO_DRIVER"); if (driver == NULL) { const char *drv, *vid, *pid; unsigned int i; drv = udev_device_get_property_value(device, "ID_USB_DRIVER"); if (drv == NULL) { drv = udev_device_get_driver(device); if (drv == NULL) { struct udev_device *parent; parent = udev_device_get_parent(device); if (parent == NULL) return; drv = udev_device_get_driver(parent); if (drv == NULL) return; } } vid = udev_device_get_property_value(device, "ID_VENDOR_ID"); pid = udev_device_get_property_value(device, "ID_MODEL_ID"); DBG("%s [%s:%s]", drv, vid, pid); for (i = 0; vendor_list[i].driver; i++) { if (g_str_equal(vendor_list[i].drv, drv) == FALSE) continue; if (vendor_list[i].vid == NULL) { driver = vendor_list[i].driver; vendor = vid; model = pid; continue; } if (vid == NULL || pid == NULL) continue; if (g_str_equal(vendor_list[i].vid, vid) == TRUE) { if (vendor_list[i].pid == NULL) { driver = vendor_list[i].driver; vendor = vid; model = pid; continue; } if (g_strcmp0(vendor_list[i].pid, pid) == 0) { driver = vendor_list[i].driver; vendor = vid; model = pid; break; } } } if (driver == NULL) return; } add_device(syspath, devname, driver, vendor, model, device); } static void check_device(struct udev_device *device) { const char *bus; bus = udev_device_get_property_value(device, "ID_BUS"); if (bus == NULL) { bus = udev_device_get_subsystem(device); if (bus == NULL) return; } if (g_str_equal(bus, "usb") == TRUE) check_usb_device(device); } static gboolean create_modem(gpointer key, gpointer value, gpointer user_data) { struct modem_info *modem = value; const char *syspath = key; unsigned int i; if (modem->modem != NULL) return FALSE; DBG("%s", syspath); if (modem->devices == NULL) return TRUE; DBG("driver=%s", modem->driver); modem->modem = ofono_modem_create(NULL, modem->driver); if (modem->modem == NULL) return TRUE; for (i = 0; driver_list[i].name; i++) { if (g_str_equal(driver_list[i].name, modem->driver) == FALSE) continue; if (driver_list[i].setup(modem) == TRUE) { ofono_modem_register(modem->modem); return FALSE; } } return TRUE; } static void enumerate_devices(struct udev *context) { struct udev_enumerate *enumerate; struct udev_list_entry *entry; DBG(""); enumerate = udev_enumerate_new(context); if (enumerate == NULL) return; udev_enumerate_add_match_subsystem(enumerate, "tty"); udev_enumerate_add_match_subsystem(enumerate, "usb"); udev_enumerate_add_match_subsystem(enumerate, "net"); udev_enumerate_scan_devices(enumerate); entry = udev_enumerate_get_list_entry(enumerate); while (entry) { const char *syspath = udev_list_entry_get_name(entry); struct udev_device *device; device = udev_device_new_from_syspath(context, syspath); if (device != NULL) { check_device(device); udev_device_unref(device); } entry = udev_list_entry_get_next(entry); } udev_enumerate_unref(enumerate); g_hash_table_foreach_remove(modem_list, create_modem, NULL); } static struct udev *udev_ctx; static struct udev_monitor *udev_mon; static guint udev_watch = 0; static guint udev_delay = 0; static gboolean check_modem_list(gpointer user_data) { udev_delay = 0; DBG(""); g_hash_table_foreach_remove(modem_list, create_modem, NULL); return FALSE; } static gboolean udev_event(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct udev_device *device; const char *action; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { ofono_warn("Error with udev monitor channel"); udev_watch = 0; return FALSE; } device = udev_monitor_receive_device(udev_mon); if (device == NULL) return TRUE; action = udev_device_get_action(device); if (action == NULL) return TRUE; if (g_str_equal(action, "add") == TRUE) { if (udev_delay > 0) g_source_remove(udev_delay); check_device(device); udev_delay = g_timeout_add_seconds(1, check_modem_list, NULL); } else if (g_str_equal(action, "remove") == TRUE) remove_device(device); udev_device_unref(device); return TRUE; } static void udev_start(void) { GIOChannel *channel; int fd; DBG(""); if (udev_monitor_enable_receiving(udev_mon) < 0) { ofono_error("Failed to enable udev monitor"); return; } enumerate_devices(udev_ctx); fd = udev_monitor_get_fd(udev_mon); channel = g_io_channel_unix_new(fd); if (channel == NULL) return; udev_watch = g_io_add_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, udev_event, NULL); g_io_channel_unref(channel); } static int detect_init(void) { udev_ctx = udev_new(); if (udev_ctx == NULL) { ofono_error("Failed to create udev context"); return -EIO; } udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev"); if (udev_mon == NULL) { ofono_error("Failed to create udev monitor"); udev_unref(udev_ctx); udev_ctx = NULL; return -EIO; } modem_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, destroy_modem); udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "tty", NULL); udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "usb", NULL); udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "net", NULL); udev_monitor_filter_update(udev_mon); udev_start(); return 0; } static void detect_exit(void) { if (udev_delay > 0) g_source_remove(udev_delay); if (udev_watch > 0) g_source_remove(udev_watch); if (udev_ctx == NULL) return; udev_monitor_filter_remove(udev_mon); g_hash_table_destroy(modem_list); udev_monitor_unref(udev_mon); udev_unref(udev_ctx); } OFONO_PLUGIN_DEFINE(udevng, "udev hardware detection", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, detect_init, detect_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/speedupcdma.c0000644000015600001650000001264412671500024022525 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include "drivers/atmodem/vendor.h" struct speedupcdma_data { GAtChat *modem; GAtChat *aux; }; static void speedupcdma_debug(const char *str, void *data) { const char *prefix = data; ofono_info("%s%s", prefix, str); } static int speedupcdma_probe(struct ofono_modem *modem) { struct speedupcdma_data *data; DBG("%p", modem); data = g_try_new0(struct speedupcdma_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void speedupcdma_remove(struct ofono_modem *modem) { struct speedupcdma_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_modem_set_data(modem, NULL); /* Cleanup after hot-unplug */ g_at_chat_unref(data->aux); g_free(data); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct speedupcdma_data *data = ofono_modem_get_data(modem); DBG(""); if (!ok) { g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_unref(data->aux); data->aux = NULL; } ofono_modem_set_powered(modem, ok); } static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { const char *device; GIOChannel *channel; GAtSyntax *syntax; GAtChat *chat; device = ofono_modem_get_string(modem, key); if (device == NULL) return NULL; DBG("%s %s", key, device); channel = g_at_tty_open(device, NULL); if (channel == NULL) return NULL; syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(channel, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(channel); if (chat == NULL) return NULL; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, speedupcdma_debug, debug); return chat; } static int speedupcdma_enable(struct ofono_modem *modem) { struct speedupcdma_data *data = ofono_modem_get_data(modem); DBG(""); data->modem = open_device(modem, "Modem", "Modem: "); if (data->modem == NULL) return -EINVAL; data->aux = open_device(modem, "Aux", "Aux: "); if (data->aux == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } g_at_chat_set_slave(data->modem, data->aux); g_at_chat_send(data->modem, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "ATE0 &C0 +CMEE=1", NULL, NULL, NULL, NULL); g_at_chat_send(data->aux, "AT+CFUN=1", NULL, cfun_enable, modem, NULL); return -EINPROGRESS; } static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct speedupcdma_data *data = ofono_modem_get_data(modem); DBG(""); g_at_chat_unref(data->aux); data->aux = NULL; if (ok) ofono_modem_set_powered(modem, FALSE); } static int speedupcdma_disable(struct ofono_modem *modem) { struct speedupcdma_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); g_at_chat_unref(data->modem); data->modem = NULL; g_at_chat_cancel_all(data->aux); g_at_chat_unregister_all(data->aux); g_at_chat_send(data->aux, "AT+CFUN=0", NULL, cfun_disable, modem, NULL); return -EINPROGRESS; } static void speedupcdma_pre_sim(struct ofono_modem *modem) { struct speedupcdma_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_devinfo_create(modem, 0, "cdmamodem", data->aux); } static void speedupcdma_post_sim(struct ofono_modem *modem) { DBG("%p", modem); } static void speedupcdma_post_online(struct ofono_modem *modem) { struct speedupcdma_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_cdma_netreg_create(modem, 0, "huaweicdmamodem", data->aux); ofono_cdma_connman_create(modem, OFONO_VENDOR_HUAWEI, "cdmamodem", data->modem); } static struct ofono_modem_driver speedupcdma_driver = { .name = "speedupcdma", .probe = speedupcdma_probe, .remove = speedupcdma_remove, .enable = speedupcdma_enable, .disable = speedupcdma_disable, .pre_sim = speedupcdma_pre_sim, .post_sim = speedupcdma_post_sim, .post_online = speedupcdma_post_online, }; static int speedupcdma_init(void) { return ofono_modem_driver_register(&speedupcdma_driver); } static void speedupcdma_exit(void) { ofono_modem_driver_unregister(&speedupcdma_driver); } OFONO_PLUGIN_DEFINE(speedupcdma, "Speed Up CDMA modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, speedupcdma_init, speedupcdma_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/ubuntu-apndb.h0000644000015600001650000000220412671500024022633 0ustar pbuserpbgroup00000000000000/* * * ofono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * 2013 Simon Busch * Copyright (C) 2014 Canonical 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. * * 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 apndb_provision_data { struct ofono_gprs_provision_data gprs_data; gboolean mvno; }; void ubuntu_apndb_ap_free(gpointer data); GSList *ubuntu_apndb_lookup_apn(const char *mcc, const char *mnc, const char *spn, const char *imsi, const char *gid1, GError **error); ofono-1.17.bzr6912+16.04.20160314.3/plugins/ubuntu-provision.c0000644000015600001650000000745512671500024023607 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * 2013 Simon Busch * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include "ubuntu-apndb.h" #include "mbpi.h" static int provision_get_settings(const char *mcc, const char *mnc, const char *spn, const char *imsi, const char *gid1, struct ofono_gprs_provision_data **settings, int *count) { GSList *apns = NULL; GSList *l = NULL; GError *error = NULL; unsigned int i; char *tmp; int retval = 0; if ((tmp = getenv("OFONO_CUSTOM_MCC")) != NULL) mcc = tmp; if ((tmp = getenv("OFONO_CUSTOM_MNC")) != NULL) mnc = tmp; if ((tmp = getenv("OFONO_CUSTOM_SPN")) != NULL) spn = tmp; if ((tmp = getenv("OFONO_CUSTOM_IMSI")) != NULL) imsi = tmp; if ((tmp = getenv("OFONO_CUSTOM_GID1")) != NULL) gid1 = tmp; ofono_info("Provisioning for MCC %s, MNC %s, SPN '%s', IMSI '%s', " "GID1 '%s'", mcc, mnc, spn, imsi, gid1); apns = ubuntu_apndb_lookup_apn(mcc, mnc, spn, imsi, gid1, &error); if (apns == NULL) { if (error != NULL) { ofono_error("%s: apndb_lookup error -%s for mcc %s" " mnc %s spn %s imsi %s", __func__, error->message, mcc, mnc, spn, imsi); g_error_free(error); error = NULL; } } *count = g_slist_length(apns); DBG("ap_count: '%d'", *count); if (*count == 0) { ofono_error("%s: provisioning failed - no APNs found.", __func__); retval = -1; goto done; } *settings = g_try_new0(struct ofono_gprs_provision_data, *count); if (*settings == NULL) { ofono_error("%s: provisioning failed: out-of-memory", __func__); g_slist_free_full(apns, ubuntu_apndb_ap_free); *count = 0; return -ENOMEM; } for (l = apns, i = 0; l; l = l->next, i++) { struct apndb_provision_data *ap = l->data; DBG("Name: '%s'", ap->gprs_data.name); DBG("APN: '%s'", ap->gprs_data.apn); DBG("Type: %s", mbpi_ap_type(ap->gprs_data.type)); DBG("Username: '%s'", ap->gprs_data.username); DBG("Password: '%s'", ap->gprs_data.password); DBG("Message Proxy: '%s'", ap->gprs_data.message_proxy); DBG("Message Center: '%s'", ap->gprs_data.message_center); DBG("MVNO: %u", ap->mvno); memcpy(*settings + i, &ap->gprs_data, sizeof(ap->gprs_data)); g_free(ap); } done: if (apns != NULL) g_slist_free(apns); return retval; } static struct ofono_gprs_provision_driver ubuntu_provision_driver = { .name = "Ubuntu APN database Provisioning", .get_settings = provision_get_settings }; static int ubuntu_provision_init(void) { return ofono_gprs_provision_driver_register(&ubuntu_provision_driver); } static void ubuntu_provision_exit(void) { ofono_gprs_provision_driver_unregister(&ubuntu_provision_driver); } OFONO_PLUGIN_DEFINE(ubuntu_provision, "Ubuntu APN database Provisioning Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, ubuntu_provision_init, ubuntu_provision_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/emulator_fuzz.c0000644000015600001650000000632412671500024023137 0ustar pbuserpbgroup00000000000000/* * oFono - Open Source Telephony * * Copyright (C) 2014 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 #endif #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "hfp.h" #define EMULATOR_FUZZ_INTERFACE "org.ofono.test.EmulatorFuzz" #define EMULATOR_FUZZ_PATH "/test" static void emulator_set_indicator(struct ofono_atom *atom, void *data) { struct ofono_emulator *em = __ofono_atom_get_data(atom); ofono_bool_t active = GPOINTER_TO_INT(data); ofono_emulator_set_hf_indicator_active(em, HFP_HF_INDICATOR_ENHANCED_SAFETY, active); } static void modem_set_indicators(struct ofono_modem *modem, void *user) { __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_set_indicator, user); } static DBusMessage *set_indicator_active(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *indicator; dbus_bool_t active; DBG(""); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &indicator, DBUS_TYPE_BOOLEAN, &active, DBUS_TYPE_INVALID) == FALSE) goto invalid; DBG("%s,%d", indicator, active); if (strcmp(indicator, "DistractedDrivingReduction")) goto invalid; __ofono_modem_foreach(modem_set_indicators, GINT_TO_POINTER(active)); return dbus_message_new_method_return(msg); invalid: return g_dbus_create_error(msg, "org.ofono.test.Error", "Invalid arguments in method call"); } static const GDBusMethodTable emulator_fuzz_methods[] = { { GDBUS_ASYNC_METHOD("SetIndicatorActive", GDBUS_ARGS({ "indicator", "s" }, { "active", "b" }), NULL, set_indicator_active) }, { }, }; static int emulator_fuzz_init(void) { DBusConnection *conn = ofono_dbus_get_connection(); DBG(""); if (!g_dbus_register_interface(conn, EMULATOR_FUZZ_PATH, EMULATOR_FUZZ_INTERFACE, emulator_fuzz_methods, NULL, NULL, NULL, NULL)) { ofono_error("Register Profile interface failed: %s", EMULATOR_FUZZ_PATH); return -EIO; } return 0; } static void emulator_fuzz_exit(void) { DBusConnection *conn = ofono_dbus_get_connection(); DBG(""); g_dbus_unregister_interface(conn, EMULATOR_FUZZ_PATH, EMULATOR_FUZZ_INTERFACE); } OFONO_PLUGIN_DEFINE(emulator_fuzz, "Emulator Fuzz", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, emulator_fuzz_init, emulator_fuzz_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/dun_gw_bluez4.c0000644000015600001650000001026312671500024022776 0ustar pbuserpbgroup00000000000000/* * oFono - Open Source Telephony * * Copyright (C) 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "bluez4.h" #define DUN_GW_CHANNEL 1 static struct server *server; static guint modemwatch_id; static GList *modems; static const gchar *dun_record = "\n" "\n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" "\n"; static void dun_gw_connect_cb(GIOChannel *io, GError *err, gpointer user_data) { struct ofono_emulator *em = user_data; struct ofono_modem *modem; int fd; GList *last; DBG(""); if (err) { DBG("%s", err->message); g_io_channel_shutdown(io, TRUE, NULL); return; } /* * Pick the last one to avoid creating a new list with one modem just * for the call to ofono_emulator_create() (for this plugin we only * support registering one modem in the emulator) */ last = g_list_last(modems); modem = last->data; DBG("Picked modem %p for emulator", modem); em = ofono_emulator_create(last, OFONO_EMULATOR_TYPE_DUN); if (em == NULL) { g_io_channel_shutdown(io, TRUE, NULL); return; } fd = g_io_channel_unix_get_fd(io); g_io_channel_set_close_on_unref(io, FALSE); ofono_emulator_register(em, fd); } static void gprs_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_modem *modem = data; if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) { modems = g_list_append(modems, modem); if (modems->next == NULL) server = bluetooth_register_server(DUN_GW_CHANNEL, dun_record, dun_gw_connect_cb, NULL); } else { modems = g_list_remove(modems, modem); if (modems == NULL && server != NULL) { bluetooth_unregister_server(server); server = NULL; } } } static void modem_watch(struct ofono_modem *modem, gboolean added, void *user) { DBG("modem: %p, added: %d", modem, added); if (added == FALSE) return; __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_GPRS, gprs_watch, modem, NULL); } static void call_modemwatch(struct ofono_modem *modem, void *user) { modem_watch(modem, TRUE, user); } static int dun_gw_init(void) { DBG(""); modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL); __ofono_modem_foreach(call_modemwatch, NULL); return 0; } static void dun_gw_exit(void) { __ofono_modemwatch_remove(modemwatch_id); g_list_free(modems); if (server) { bluetooth_unregister_server(server); server = NULL; } } OFONO_PLUGIN_DEFINE(dun_gw_bluez4, "Dial-up Networking Profile Plugins", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, dun_gw_init, dun_gw_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/push-notification.c0000644000015600001650000001354012671500024023672 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "smsagent.h" #define PUSH_NOTIFICATION_INTERFACE "org.ofono.PushNotification" #define AGENT_INTERFACE "org.ofono.PushNotificationAgent" #define WAP_PUSH_SRC_PORT -1 #define WAP_PUSH_DST_PORT 2948 static unsigned int modemwatch_id; struct push_notification { struct ofono_modem *modem; struct ofono_sms *sms; struct sms_agent *agent; unsigned int push_watch; }; static void agent_exited(void *userdata) { struct push_notification *pn = userdata; if (pn->push_watch > 0) { __ofono_sms_datagram_watch_remove(pn->sms, pn->push_watch); pn->push_watch = 0; } pn->agent = NULL; } static void push_received(const char *from, const struct tm *remote, const struct tm *local, int dst, int src, const unsigned char *buffer, unsigned int len, void *data) { struct push_notification *pn = data; DBG("Received push of size: %u", len); if (pn->agent == NULL) return; sms_agent_dispatch_datagram(pn->agent, "ReceiveNotification", from, remote, local, buffer, len, NULL, NULL, NULL); } static DBusMessage *push_notification_register_agent(DBusConnection *conn, DBusMessage *msg, void *data) { struct push_notification *pn = data; const char *agent_path; if (pn->agent) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!__ofono_dbus_valid_object_path(agent_path)) return __ofono_error_invalid_format(msg); pn->agent = sms_agent_new(AGENT_INTERFACE, dbus_message_get_sender(msg), agent_path); if (pn->agent == NULL) return __ofono_error_failed(msg); sms_agent_set_removed_notify(pn->agent, agent_exited, pn); pn->push_watch = __ofono_sms_datagram_watch_add(pn->sms, push_received, WAP_PUSH_DST_PORT, WAP_PUSH_SRC_PORT, pn, NULL); return dbus_message_new_method_return(msg); } static DBusMessage *push_notification_unregister_agent(DBusConnection *conn, DBusMessage *msg, void *data) { struct push_notification *pn = data; const char *agent_path; const char *agent_bus = dbus_message_get_sender(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (pn->agent == NULL) return __ofono_error_failed(msg); if (sms_agent_matches(pn->agent, agent_bus, agent_path) == FALSE) return __ofono_error_failed(msg); sms_agent_free(pn->agent); pn->agent = NULL; return dbus_message_new_method_return(msg); } static const GDBusMethodTable push_notification_methods[] = { { GDBUS_METHOD("RegisterAgent", GDBUS_ARGS({ "path", "o" }), NULL, push_notification_register_agent) }, { GDBUS_METHOD("UnregisterAgent", GDBUS_ARGS({ "path", "o" }), NULL, push_notification_unregister_agent) }, { } }; static void push_notification_cleanup(gpointer user) { struct push_notification *pn = user; DBG("%p", pn); /* The push watch was already cleaned up */ pn->push_watch = 0; pn->sms = NULL; sms_agent_free(pn->agent); ofono_modem_remove_interface(pn->modem, PUSH_NOTIFICATION_INTERFACE); } static void sms_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct push_notification *pn = data; DBusConnection *conn = ofono_dbus_get_connection(); if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { g_dbus_unregister_interface(conn, ofono_modem_get_path(pn->modem), PUSH_NOTIFICATION_INTERFACE); return; } DBG("registered"); pn->sms = __ofono_atom_get_data(atom); if (!g_dbus_register_interface(conn, ofono_modem_get_path(pn->modem), PUSH_NOTIFICATION_INTERFACE, push_notification_methods, NULL, NULL, pn, push_notification_cleanup)) { ofono_error("Could not create %s interface", PUSH_NOTIFICATION_INTERFACE); return; } ofono_modem_add_interface(pn->modem, PUSH_NOTIFICATION_INTERFACE); } static void modem_watch(struct ofono_modem *modem, gboolean added, void *user) { struct push_notification *pn; DBG("modem: %p, added: %d", modem, added); if (added == FALSE) return; pn = g_try_new0(struct push_notification, 1); if (pn == NULL) return; pn->modem = modem; __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SMS, sms_watch, pn, g_free); } static void call_modemwatch(struct ofono_modem *modem, void *user) { modem_watch(modem, TRUE, user); } static int push_notification_init(void) { DBG(""); modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL); __ofono_modem_foreach(call_modemwatch, NULL); return 0; } static void push_notification_exit(void) { DBG(""); __ofono_modemwatch_remove(modemwatch_id); } OFONO_PLUGIN_DEFINE(push_notification, "Push Notification Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, push_notification_init, push_notification_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/phonesim.c0000644000015600001650000007140312671500024022053 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hfp.h" #include "ofono.h" static const char *none_prefix[] = { NULL }; static const char *ptty_prefix[] = { "+PTTY:", NULL }; static const char *simstate_prefix[] = { "+SIMSTATE:", NULL }; static int next_iface = 0; static const char CONTROL_PATH[] = "/"; static const char CONTROL_INTERFACE[] = "org.ofono.phonesim.Manager"; struct phonesim_data { GAtMux *mux; GAtChat *chat; gboolean calypso; gboolean use_mux; gboolean hfp; struct hfp_slc_info hfp_info; unsigned int hfp_watch; int batt_level; struct ofono_sim *sim; }; struct gprs_context_data { GAtChat *chat; char *interface; enum ofono_gprs_proto proto; }; static void at_cgact_up_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (ok == FALSE) goto done; ofono_gprs_context_set_interface(gc, gcd->interface); if (gcd->proto == OFONO_GPRS_PROTO_IP || gcd->proto == OFONO_GPRS_PROTO_IPV4V6) ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE); if (gcd->proto == OFONO_GPRS_PROTO_IPV6 || gcd->proto == OFONO_GPRS_PROTO_IPV4V6) { ofono_gprs_context_set_ipv6_address(gc, "fe80::1"); ofono_gprs_context_set_ipv6_prefix_length(gc, 10); } done: cb(&error, cbd->data); } static void at_cgact_down_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void phonesim_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 cb_data *cbd = cb_data_new(cb, data); char buf[OFONO_GPRS_MAX_APN_LENGTH + 128]; int len = 0; cbd->user = gc; gcd->proto = ctx->proto; switch (ctx->proto) { case OFONO_GPRS_PROTO_IP: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); break; case OFONO_GPRS_PROTO_IPV6: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"", ctx->cid); break; case OFONO_GPRS_PROTO_IPV4V6: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"", ctx->cid); break; } if (ctx->apn) snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); /* Assume always succeeds */ if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0) goto error; sprintf(buf, "AT+CGACT=1,%u", ctx->cid); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_cgact_up_cb, cbd, g_free) > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void phonesim_deactivate_primary(struct ofono_gprs_context *gc, unsigned int id, ofono_gprs_context_cb_t cb, void *data) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *cbd = cb_data_new(cb, data); char buf[128]; cbd->user = gc; snprintf(buf, sizeof(buf), "AT+CGACT=0,%u", id); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_cgact_down_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static int phonesim_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *data) { GAtChat *chat = data; struct gprs_context_data *gcd; gcd = g_try_new0(struct gprs_context_data, 1); if (gcd == NULL) return -ENOMEM; gcd->chat = g_at_chat_clone(chat); gcd->interface = g_strdup_printf("dummy%d", next_iface++); ofono_gprs_context_set_data(gc, gcd); return 0; } static void phonesim_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); g_at_chat_unref(gcd->chat); g_free(gcd->interface); g_free(gcd); } static void phonesim_ctm_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_ctm *ctm = user_data; if (!ok) { ofono_ctm_remove(ctm); return; } ofono_ctm_register(ctm); } static int phonesim_ctm_probe(struct ofono_ctm *ctm, unsigned int vendor, void *data) { GAtChat *chat; DBG(""); chat = g_at_chat_clone(data); ofono_ctm_set_data(ctm, chat); g_at_chat_send(chat, "AT+PTTY=?", ptty_prefix, phonesim_ctm_support_cb, ctm, NULL); return 0; } static void phonesim_ctm_remove(struct ofono_ctm *ctm) { GAtChat *chat = ofono_ctm_get_data(ctm); DBG(""); ofono_ctm_set_data(ctm, NULL); g_at_chat_unref(chat); } static void ctm_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_error error; GAtResultIter iter; ofono_ctm_query_cb_t cb = cbd->cb; int value; 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, "+PTTY:") == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &value) == FALSE) goto error; cb(&error, value, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void phonesim_ctm_query(struct ofono_ctm *ctm, ofono_ctm_query_cb_t cb, void *data) { GAtChat *chat = ofono_ctm_get_data(ctm); struct cb_data *cbd = cb_data_new(cb, data); DBG(""); if (g_at_chat_send(chat, "AT+PTTY?", ptty_prefix, ctm_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, 0, data); } static void ctm_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_ctm_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void phonesim_ctm_set(struct ofono_ctm *ctm, ofono_bool_t enable, ofono_ctm_set_cb_t cb, void *data) { GAtChat *chat = ofono_ctm_get_data(ctm); struct cb_data *cbd = cb_data_new(cb, data); char buf[20]; DBG(""); snprintf(buf, sizeof(buf), "AT+PTTY=%d", enable); if (g_at_chat_send(chat, buf, none_prefix, ctm_set_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static gboolean phonesim_radio_settings_register(gpointer user) { struct ofono_radio_settings *rs = user; ofono_radio_settings_register(rs); return FALSE; } static int phonesim_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *data) { GAtChat *chat; DBG(""); chat = g_at_chat_clone(data); ofono_radio_settings_set_data(rs, chat); g_idle_add(phonesim_radio_settings_register, rs); return 0; } static void phonesim_radio_settings_remove(struct ofono_radio_settings *rs) { GAtChat *chat = ofono_radio_settings_get_data(rs); DBG(""); ofono_radio_settings_set_data(rs, NULL); g_at_chat_unref(chat); } static void phonesim_query_rat_mode(struct ofono_radio_settings *rs, ofono_radio_settings_rat_mode_query_cb_t cb, void *data) { DBG(""); CALLBACK_WITH_SUCCESS(cb, OFONO_RADIO_ACCESS_MODE_ANY, data); } static void phonesim_query_available_rats(struct ofono_radio_settings *rs, ofono_radio_settings_available_rats_query_cb_t cb, void *data) { uint32_t techs = 0; DBG(""); techs |= OFONO_RADIO_ACCESS_MODE_GSM; techs |= OFONO_RADIO_ACCESS_MODE_UMTS; techs |= OFONO_RADIO_ACCESS_MODE_LTE; CALLBACK_WITH_SUCCESS(cb, techs, data); } static struct ofono_gprs_context_driver context_driver = { .name = "phonesim", .probe = phonesim_context_probe, .remove = phonesim_context_remove, .activate_primary = phonesim_activate_primary, .deactivate_primary = phonesim_deactivate_primary, }; static struct ofono_radio_settings_driver radio_settings_driver = { .name = "phonesim", .probe = phonesim_radio_settings_probe, .remove = phonesim_radio_settings_remove, .query_rat_mode = phonesim_query_rat_mode, .query_available_rats = phonesim_query_available_rats, }; static struct ofono_ctm_driver ctm_driver = { .name = "phonesim", .probe = phonesim_ctm_probe, .remove = phonesim_ctm_remove, .query_tty = phonesim_ctm_query, .set_tty = phonesim_ctm_set, }; static int phonesim_probe(struct ofono_modem *modem) { struct phonesim_data *data; DBG("%p", modem); data = g_try_new0(struct phonesim_data, 1); if (data == NULL) return -ENOMEM; ofono_modem_set_data(modem, data); return 0; } static void phonesim_remove(struct ofono_modem *modem) { struct phonesim_data *data = ofono_modem_get_data(modem); DBG("%p", modem); g_free(data); ofono_modem_set_data(modem, NULL); } static void phonesim_debug(const char *str, void *prefix) { ofono_info("%s%s", (const char *) prefix, str); } static void simstate_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct phonesim_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int inserted; /* Assume that is this fails we are dealing with an older phonesim */ if (ok == FALSE) goto done; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+SIMSTATE:")) return; if (!g_at_result_iter_next_number(&iter, &inserted)) return; if (inserted != 1) return; done: ofono_sim_inserted_notify(data->sim, TRUE); } static void usimstate_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct phonesim_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int inserted; if (data->sim == NULL) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+USIMSTATE:")) return; if (!g_at_result_iter_next_number(&iter, &inserted)) return; ofono_sim_inserted_notify(data->sim, inserted); } static void cfun_set_on_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; DBG(""); ofono_modem_set_powered(modem, ok); } static gboolean phonesim_reset(void *user_data) { struct ofono_modem *modem = user_data; struct phonesim_data *data = ofono_modem_get_data(modem); g_at_chat_unref(data->chat); data->chat = NULL; if (data->mux) { g_at_mux_shutdown(data->mux); g_at_mux_unref(data->mux); data->mux = NULL; } ofono_modem_reset(modem); return FALSE; } static void crst_notify(GAtResult *result, gpointer user_data) { g_idle_add(phonesim_reset, user_data); } static void emulator_battery_cb(struct ofono_atom *atom, void *data) { int val = 0; if (GPOINTER_TO_INT(data) > 0) val = (GPOINTER_TO_INT(data) - 1) / 20 + 1; ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_BATTERY, val); } static void cbc_notify(GAtResult *result, gpointer user_data) { struct ofono_modem *modem = user_data; struct phonesim_data *data = ofono_modem_get_data(modem); GAtResultIter iter; int status; int level; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CBC:")) return; if (!g_at_result_iter_next_number(&iter, &status)) return; if (!g_at_result_iter_next_number(&iter, &level)) return; data->batt_level = level; __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_battery_cb, GUINT_TO_POINTER(level)); } static void phonesim_disconnected(gpointer user_data) { struct ofono_modem *modem = user_data; struct phonesim_data *data = ofono_modem_get_data(modem); DBG(""); ofono_modem_set_powered(modem, FALSE); g_at_chat_unref(data->chat); data->chat = NULL; if (data->mux) { g_at_mux_shutdown(data->mux); g_at_mux_unref(data->mux); data->mux = NULL; } } static void mux_setup(GAtMux *mux, gpointer user_data) { struct ofono_modem *modem = user_data; struct phonesim_data *data = ofono_modem_get_data(modem); GIOChannel *io; GAtSyntax *syntax; DBG("%p", mux); if (mux == NULL) { ofono_modem_set_powered(modem, FALSE); return; } data->mux = mux; if (getenv("OFONO_AT_DEBUG")) g_at_mux_set_debug(data->mux, phonesim_debug, ""); g_at_mux_start(mux); io = g_at_mux_create_channel(mux); if (data->calypso) syntax = g_at_syntax_new_gsm_permissive(); else syntax = g_at_syntax_new_gsmv1(); data->chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(data->chat, phonesim_debug, ""); if (data->calypso) g_at_chat_set_wakeup_command(data->chat, "AT\r", 500, 5000); g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix, cfun_set_on_cb, modem, NULL); } static void emulator_hfp_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *user_data) { struct phonesim_data *data = user_data; if (cond != OFONO_ATOM_WATCH_CONDITION_REGISTERED) return; emulator_battery_cb(atom, GUINT_TO_POINTER(data->batt_level)); } static int connect_socket(const char *address, int port) { struct sockaddr_in addr; int sk; int err; sk = socket(PF_INET, SOCK_STREAM, 0); if (sk < 0) return -EINVAL; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(address); addr.sin_port = htons(port); err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { close(sk); return -errno; } return sk; } static int phonesim_enable(struct ofono_modem *modem) { struct phonesim_data *data = ofono_modem_get_data(modem); GIOChannel *io; GAtSyntax *syntax; const char *address, *value; int sk, port; DBG("%p", modem); address = ofono_modem_get_string(modem, "Address"); if (address == NULL) return -EINVAL; port = ofono_modem_get_integer(modem, "Port"); if (port < 0) return -EINVAL; value = ofono_modem_get_string(modem, "Modem"); if (!g_strcmp0(value, "calypso")) data->calypso = TRUE; value = ofono_modem_get_string(modem, "Multiplexer"); if (!g_strcmp0(value, "internal")) data->use_mux = TRUE; sk = connect_socket(address, port); if (sk < 0) return sk; io = g_io_channel_unix_new(sk); if (io == NULL) { close(sk); return -ENOMEM; } if (data->calypso) syntax = g_at_syntax_new_gsm_permissive(); else syntax = g_at_syntax_new_gsmv1(); data->chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); if (data->chat == NULL) return -ENOMEM; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(data->chat, phonesim_debug, ""); g_at_chat_set_disconnect_function(data->chat, phonesim_disconnected, modem); if (data->calypso) { g_at_chat_set_wakeup_command(data->chat, "AT\r", 500, 5000); g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT%CUNS=0", NULL, NULL, NULL, NULL); } if (data->use_mux) { g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL); g_at_mux_setup_gsm0710(data->chat, mux_setup, modem, NULL); g_at_chat_unref(data->chat); data->chat = NULL; return -EINPROGRESS; } g_at_chat_send(data->chat, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL); g_at_chat_register(data->chat, "+CRST:", crst_notify, FALSE, modem, NULL); g_at_chat_register(data->chat, "+CBC:", cbc_notify, FALSE, modem, NULL); g_at_chat_send(data->chat, "AT+CBC", none_prefix, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT+SIMSTATE?", simstate_prefix, simstate_query, modem, NULL); g_at_chat_register(data->chat, "+USIMSTATE:", usimstate_notify, FALSE, modem, NULL); data->hfp_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_hfp_watch, data, NULL); return 0; } static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_modem_online_cb_t callback = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); callback(&error, cbd->data); } static void phonesim_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *user_data) { struct phonesim_data *data = ofono_modem_get_data(modem); struct cb_data *cbd = cb_data_new(cb, user_data); char buf[64]; DBG("%p", modem); snprintf(buf, sizeof(buf), "AT+CFUN=%d", online ? 1 : 4); if (g_at_chat_send(data->chat, buf, none_prefix, set_online_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, user_data); } static int phonesim_disable(struct ofono_modem *modem) { struct phonesim_data *data = ofono_modem_get_data(modem); DBG("%p", modem); __ofono_modem_remove_atom_watch(modem, data->hfp_watch); g_at_chat_unref(data->chat); data->chat = NULL; if (data->mux) { g_at_mux_shutdown(data->mux); g_at_mux_unref(data->mux); data->mux = NULL; } return 0; } static void phonesim_pre_sim(struct ofono_modem *modem) { struct phonesim_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->chat); data->sim = ofono_sim_create(modem, 0, "atmodem", data->chat); if (data->calypso) ofono_voicecall_create(modem, 0, "calypsomodem", data->chat); else ofono_voicecall_create(modem, 0, "atmodem", data->chat); } static void phonesim_post_sim(struct ofono_modem *modem) { struct phonesim_data *data = ofono_modem_get_data(modem); DBG("%p", modem); ofono_ctm_create(modem, 0, "phonesim", data->chat); ofono_phonebook_create(modem, 0, "atmodem", data->chat); if (!data->calypso) ofono_stk_create(modem, OFONO_VENDOR_PHONESIM, "atmodem", data->chat); ofono_call_forwarding_create(modem, 0, "atmodem", data->chat); if (!data->calypso) ofono_sms_create(modem, 0, "atmodem", data->chat); ofono_radio_settings_create(modem, 0, "phonesim", data->chat); } static void phonesim_post_online(struct ofono_modem *modem) { struct phonesim_data *data = ofono_modem_get_data(modem); struct ofono_message_waiting *mw; struct ofono_gprs *gprs; struct ofono_gprs_context *gc1, *gc2; DBG("%p", modem); ofono_ussd_create(modem, 0, "atmodem", data->chat); ofono_call_settings_create(modem, 0, "atmodem", data->chat); if (data->calypso) ofono_netreg_create(modem, OFONO_VENDOR_CALYPSO, "atmodem", data->chat); else ofono_netreg_create(modem, OFONO_VENDOR_PHONESIM, "atmodem", data->chat); ofono_call_meter_create(modem, 0, "atmodem", data->chat); ofono_call_barring_create(modem, 0, "atmodem", data->chat); ofono_call_volume_create(modem, 0, "atmodem", data->chat); if (!data->calypso) ofono_cbs_create(modem, 0, "atmodem", data->chat); gc1 = ofono_gprs_context_create(modem, 0, "phonesim", data->chat); gprs = ofono_gprs_create(modem, 0, "atmodem", data->chat); gc2 = ofono_gprs_context_create(modem, 0, "phonesim", data->chat); if (gprs && gc1) ofono_gprs_add_context(gprs, gc1); if (gprs && gc2) ofono_gprs_add_context(gprs, gc2); mw = ofono_message_waiting_create(modem); if (mw) ofono_message_waiting_register(mw); ofono_gnss_create(modem, 0, "atmodem", data->chat); } static struct ofono_modem_driver phonesim_driver = { .name = "phonesim", .probe = phonesim_probe, .remove = phonesim_remove, .enable = phonesim_enable, .disable = phonesim_disable, .set_online = phonesim_set_online, .pre_sim = phonesim_pre_sim, .post_sim = phonesim_post_sim, .post_online = phonesim_post_online, }; static int localhfp_probe(struct ofono_modem *modem) { struct hfp_slc_info *info; DBG("%p", modem); info = g_try_new(struct hfp_slc_info, 1); if (info == NULL) return -ENOMEM; ofono_modem_set_data(modem, info); return 0; } static void localhfp_remove(struct ofono_modem *modem) { struct hfp_slc_info *info = ofono_modem_get_data(modem); DBG("%p", modem); g_free(info); ofono_modem_set_data(modem, NULL); } static void slc_established(gpointer userdata) { struct ofono_modem *modem = userdata; ofono_modem_set_powered(modem, TRUE); } static void slc_failed(gpointer userdata) { struct ofono_modem *modem = userdata; struct hfp_slc_info *info = ofono_modem_get_data(modem); ofono_modem_set_powered(modem, FALSE); g_at_chat_unref(info->chat); info->chat = NULL; } static int localhfp_enable(struct ofono_modem *modem) { struct hfp_slc_info *info = ofono_modem_get_data(modem); GIOChannel *io; GAtSyntax *syntax; GAtChat *chat; const char *address; int sk, port; address = ofono_modem_get_string(modem, "Address"); if (address == NULL) return -EINVAL; port = ofono_modem_get_integer(modem, "Port"); if (port < 0) return -EINVAL; sk = connect_socket(address, port); if (sk < 0) return sk; io = g_io_channel_unix_new(sk); if (io == NULL) { close(sk); return -ENOMEM; } syntax = g_at_syntax_new_gsmv1(); chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); if (chat == NULL) return -ENOMEM; if (getenv("OFONO_AT_DEBUG")) g_at_chat_set_debug(chat, phonesim_debug, "LocalHfp: "); g_at_chat_set_disconnect_function(chat, slc_failed, modem); hfp_slc_info_init(info, HFP_VERSION_LATEST); info->chat = chat; hfp_slc_establish(info, slc_established, slc_failed, modem); return -EINPROGRESS; } static int localhfp_disable(struct ofono_modem *modem) { struct hfp_slc_info *info = ofono_modem_get_data(modem); g_at_chat_unref(info->chat); info->chat = NULL; return 0; } static void localhfp_pre_sim(struct ofono_modem *modem) { struct hfp_slc_info *info = ofono_modem_get_data(modem); DBG("%p", modem); ofono_voicecall_create(modem, 0, "hfpmodem", info); ofono_netreg_create(modem, 0, "hfpmodem", info); ofono_call_volume_create(modem, 0, "hfpmodem", info); ofono_handsfree_create(modem, 0, "hfpmodem", info); ofono_siri_create(modem, 0, "hfpmodem", info); } static struct ofono_modem_driver localhfp_driver = { .name = "localhfp", .probe = localhfp_probe, .remove = localhfp_remove, .enable = localhfp_enable, .disable = localhfp_disable, .pre_sim = localhfp_pre_sim, }; static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group) { const char *driver = "phonesim"; struct ofono_modem *modem; char *value; DBG("group %s", group); value = g_key_file_get_string(keyfile, group, "Modem", NULL); if (value && g_str_equal(value, "hfp")) driver = "localhfp"; g_free(value); modem = ofono_modem_create(group, driver); if (modem == NULL) return NULL; value = g_key_file_get_string(keyfile, group, "Address", NULL); if (value == NULL) goto error; ofono_modem_set_string(modem, "Address", value); g_free(value); value = g_key_file_get_string(keyfile, group, "Port", NULL); if (value == NULL) goto error; ofono_modem_set_integer(modem, "Port", atoi(value)); g_free(value); value = g_key_file_get_string(keyfile, group, "Modem", NULL); if (value) { ofono_modem_set_string(modem, "Modem", value); g_free(value); } value = g_key_file_get_string(keyfile, group, "Multiplexer", NULL); if (value) { ofono_modem_set_string(modem, "Multiplexer", value); g_free(value); } DBG("%p", modem); return modem; error: ofono_error("Missing address or port setting for %s", group); ofono_modem_remove(modem); return NULL; } static GSList *modem_list = NULL; static void parse_config(void) { GKeyFile *keyfile; GError *err = NULL; char **modems; int i; const char *filename; char *conf_override = getenv("OFONO_PHONESIM_CONFIG"); if (conf_override) filename = conf_override; else filename = CONFIGDIR "/phonesim.conf"; DBG("filename %s", filename); keyfile = g_key_file_new(); g_key_file_set_list_separator(keyfile, ','); if (!g_key_file_load_from_file(keyfile, filename, 0, &err)) { ofono_warn("Reading of %s failed: %s", filename, err->message); g_error_free(err); goto done; } modems = g_key_file_get_groups(keyfile, NULL); for (i = 0; modems[i]; i++) { struct ofono_modem *modem; modem = create_modem(keyfile, modems[i]); if (modem == NULL) continue; modem_list = g_slist_prepend(modem_list, modem); ofono_modem_register(modem); } g_strfreev(modems); done: g_key_file_free(keyfile); } static void release_modems(void) { GSList *list; for (list = modem_list; list; list = list->next) { struct ofono_modem *modem = list->data; ofono_modem_remove(modem); } g_slist_free(modem_list); modem_list = NULL; } static DBusMessage *control_add(DBusConnection *conn, DBusMessage *msg, void *data) { const char *driver = "phonesim"; struct ofono_modem *modem; DBusMessageIter iter; char *name; char *address; char *port; if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &address); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &port); modem = ofono_modem_create(name, driver); if (modem == NULL) return NULL; ofono_modem_set_string(modem, "Address", address); ofono_modem_set_integer(modem, "Port", atoi(port)); if (ofono_modem_register(modem) != 0) { ofono_modem_remove(modem); return __ofono_error_invalid_args(msg); } modem_list = g_slist_prepend(modem_list, modem); return dbus_message_new_method_return(msg); } static DBusMessage *control_remove_all(DBusConnection *conn, DBusMessage *msg, void *data) { release_modems(); return dbus_message_new_method_return(msg); } static DBusMessage *control_reset(DBusConnection *conn, DBusMessage *msg, void *data) { release_modems(); parse_config(); return dbus_message_new_method_return(msg); } static const GDBusMethodTable control_methods[] = { { GDBUS_METHOD("Add", GDBUS_ARGS({"name", "s"}, {"address", "s"}, {"port", "s"}), NULL, control_add) }, { GDBUS_METHOD("RemoveAll", GDBUS_ARGS({ }), NULL, control_remove_all) }, { GDBUS_METHOD("Reset", GDBUS_ARGS({ }), NULL, control_reset) }, {} }; static int setup_control_channel(void) { int err = 0; DBusConnection *conn = ofono_dbus_get_connection(); void *user_data = NULL; g_dbus_register_interface(conn, CONTROL_PATH, CONTROL_INTERFACE, control_methods, NULL, NULL, user_data, NULL); return err; } static void shutdown_control_channel(void) { g_dbus_unregister_interface(ofono_dbus_get_connection(), CONTROL_PATH, CONTROL_INTERFACE); } static int phonesim_init(void) { int err; err = ofono_modem_driver_register(&phonesim_driver); if (err < 0) return err; ofono_modem_driver_register(&localhfp_driver); ofono_gprs_context_driver_register(&context_driver); ofono_ctm_driver_register(&ctm_driver); ofono_radio_settings_driver_register(&radio_settings_driver); parse_config(); err = setup_control_channel(); if (err < 0) return err; return 0; } static void phonesim_exit(void) { shutdown_control_channel(); release_modems(); modem_list = NULL; ofono_radio_settings_driver_unregister(&radio_settings_driver); ofono_ctm_driver_unregister(&ctm_driver); ofono_gprs_context_driver_unregister(&context_driver); ofono_modem_driver_unregister(&phonesim_driver); } OFONO_PLUGIN_DEFINE(phonesim, "Phone Simulator driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, phonesim_init, phonesim_exit) ofono-1.17.bzr6912+16.04.20160314.3/plugins/nokia-gpio.c0000644000015600001650000004103312671500024022262 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "nokia-gpio.h" #define GPIO_SWITCH "/sys/devices/platform/gpio-switch" #define DEV_CMT "/dev/cmt" enum rapu_type { RAPU_TYPE_1, RAPU_TYPE_2, }; enum retry_count { RETRY_COUNT_RESET = 5, RETRY_COUNT_POWER_ON = 10, }; enum phonet_link { PHONET_LINK_NONE = 0, PHONET_LINK_DOWN, PHONET_LINK_UP, }; enum power_event { POWER_EVENT_PHONET_LINK_UP = 1, POWER_EVENT_PHONET_LINK_DOWN, POWER_EVENT_ON, POWER_EVENT_ON_TIMEOUT, POWER_EVENT_REBOOT_TIMEOUT, POWER_EVENT_OFF, POWER_EVENT_OFF_IMMEDIATELY, POWER_EVENT_OFF_TIMEOUT, POWER_EVENT_OFF_COMPLETE, }; struct gpio_data { GIsiPhonetNetlink *link; gpio_finished_cb_t callback; void *data; enum power_state state; enum phonet_link current; enum phonet_link target; enum power_event timer_event; enum rapu_type rapu; guint timeout_source; unsigned retry_count; unsigned have_gpio_switch:1; unsigned have_cmt_en:1; unsigned have_cmt_rst_rq:1; unsigned have_cmt_rst:1; unsigned have_cmt_bsi:1; unsigned have_cmt_apeslpx:1; unsigned reset_in_progress:1; unsigned startup_in_progress:1; }; static struct gpio_data self; #define _(X) case X: return #X static inline char const *gpio_power_event_name(enum power_event value) { switch (value) { _(POWER_EVENT_PHONET_LINK_UP); _(POWER_EVENT_PHONET_LINK_DOWN); _(POWER_EVENT_ON); _(POWER_EVENT_ON_TIMEOUT); _(POWER_EVENT_REBOOT_TIMEOUT); _(POWER_EVENT_OFF); _(POWER_EVENT_OFF_IMMEDIATELY); _(POWER_EVENT_OFF_TIMEOUT); _(POWER_EVENT_OFF_COMPLETE); } return ""; } char const *gpio_power_state_name(enum power_state value) { switch (value) { _(POWER_STATE_NONE); _(POWER_STATE_ON_STARTED); _(POWER_STATE_ON); _(POWER_STATE_ON_RESET); _(POWER_STATE_ON_FAILED); _(POWER_STATE_OFF_STARTED); _(POWER_STATE_OFF_WAITING); _(POWER_STATE_OFF); } return ""; } #undef _ static void gpio_power_state_machine(enum power_event event); static void gpio_power_set_state(enum power_state new_state); static int file_exists(char const *filename) { struct stat st; return stat(filename, &st) == 0; } static int dir_exists(char const *filename) { struct stat st; return stat(filename, &st) == 0 && S_ISDIR(st.st_mode); } static int file_write(char const *filename, char const *output) { FILE *f; f = fopen(filename, "r+"); if (f == NULL) { DBG("%s: %s (%d)", filename, strerror(errno), errno); return -1; } fputs(output, f); return fclose(f); } static int gpio_write(char *line, int value) { char filename[256]; DBG("(\"%s\", \"%s\")", line, value ? "active" : "inactive"); if (self.have_gpio_switch) { snprintf(filename, sizeof filename, "%s/%s/%s", GPIO_SWITCH, line, "state"); return file_write(filename, value ? "active" : "inactive"); } else { snprintf(filename, sizeof filename, "%s/%s/%s", DEV_CMT, line, "value"); return file_write(filename, value ? "1" : "0"); } } #define GPIO_WRITE(line, value) \ (self.have_ ## line ? gpio_write(#line, value) : 0) static int gpio_line_probe(char const *line) { char filename[256]; int result; if (self.have_gpio_switch) snprintf(filename, sizeof filename, "%s/%s/state", GPIO_SWITCH, line); else snprintf(filename, sizeof filename, "%s/%s/value", DEV_CMT, line); result = file_exists(filename); DBG("%s: %s", line, result ? "found" : "not found"); return result; } /* * Modem start up function * * Sets all lines down and leaves "power key" pressed (power key must * be released after some time) */ static void gpio_start_modem_power_on(void) { DBG(""); if (self.startup_in_progress) return; self.startup_in_progress = 1; GPIO_WRITE(cmt_apeslpx, 0); /* skip flash mode */ GPIO_WRITE(cmt_rst_rq, 0); /* prevent current drain */ switch (self.rapu) { case RAPU_TYPE_2: GPIO_WRITE(cmt_en, 0); /* 15 ms needed for ASIC poweroff */ usleep(20000); GPIO_WRITE(cmt_en, 1); break; case RAPU_TYPE_1: GPIO_WRITE(cmt_en, 0); GPIO_WRITE(cmt_bsi, 0); /* toggle BSI visible to modem */ GPIO_WRITE(cmt_rst, 0); /* Assert PURX */ GPIO_WRITE(cmt_en, 1); /* Press "power key" */ GPIO_WRITE(cmt_rst, 1); /* Release CMT to boot */ break; } GPIO_WRITE(cmt_rst_rq, 1); } static void gpio_finish_modem_power_on(void) { DBG(""); if (!self.startup_in_progress) return; self.startup_in_progress = 0; switch (self.rapu) { case RAPU_TYPE_2: break; case RAPU_TYPE_1: GPIO_WRITE(cmt_en, 0); /* release "power key" */ break; } } static void gpio_start_modem_reset(void) { DBG(""); if (self.reset_in_progress) return; self.reset_in_progress = 1; if (self.have_cmt_rst_rq) { GPIO_WRITE(cmt_rst_rq, 0); /* Just in case */ GPIO_WRITE(cmt_rst_rq, 1); } else { gpio_start_modem_power_on(); } } static void gpio_finish_modem_reset(void) { DBG(""); if (!self.reset_in_progress) return; self.reset_in_progress = 0; gpio_finish_modem_power_on(); } static void gpio_finish_modem_power_off(void) { DBG(""); if (self.reset_in_progress) gpio_finish_modem_reset(); if (self.startup_in_progress) gpio_finish_modem_power_on(); GPIO_WRITE(cmt_apeslpx, 0); /* skip flash mode */ GPIO_WRITE(cmt_rst_rq, 0); /* prevent current drain */ switch (self.rapu) { case RAPU_TYPE_2: GPIO_WRITE(cmt_en, 0); /* Power off */ break; case RAPU_TYPE_1: GPIO_WRITE(cmt_en, 0); /* release "power key" */ GPIO_WRITE(cmt_rst, 0); /* force modem to reset state */ GPIO_WRITE(cmt_rst, 1); /* release modem to be powered off by bootloader */ break; } } static gboolean gpio_power_timer_cb(gpointer user) { self.timeout_source = 0; if (self.timer_event) gpio_power_state_machine(self.timer_event); return FALSE; } static void gpio_power_state_machine(enum power_event event) { enum power_state new_state; DBG("(%s) @ state %s", gpio_power_event_name(event), gpio_power_state_name(self.state)); switch (event) { case POWER_EVENT_ON: self.target = PHONET_LINK_UP; if (self.current == PHONET_LINK_NONE) return; switch (self.state) { case POWER_STATE_ON_STARTED: case POWER_STATE_ON_RESET: case POWER_STATE_ON: /* Do nothing */ break; case POWER_STATE_OFF_STARTED: /* Do nothing */ break; case POWER_STATE_NONE: case POWER_STATE_OFF_WAITING: case POWER_STATE_OFF: case POWER_STATE_ON_FAILED: gpio_power_set_state(POWER_STATE_ON_STARTED); break; } return; case POWER_EVENT_PHONET_LINK_DOWN: switch (self.target) { case PHONET_LINK_UP: break; case PHONET_LINK_DOWN: case PHONET_LINK_NONE: default: if (self.state == POWER_STATE_OFF || self.state == POWER_STATE_NONE) new_state = POWER_STATE_OFF; else new_state = POWER_STATE_OFF_WAITING; gpio_power_set_state(new_state); return; } switch (self.state) { case POWER_STATE_NONE: /* first connection down event => start modem */ gpio_power_set_state(POWER_STATE_ON_STARTED); break; case POWER_STATE_ON_STARTED: case POWER_STATE_ON_RESET: break; default: self.retry_count = 0; gpio_power_set_state(POWER_STATE_ON_RESET); break; } return; case POWER_EVENT_ON_TIMEOUT: if (self.target == PHONET_LINK_DOWN) new_state = POWER_STATE_OFF_STARTED; else if (self.retry_count <= RETRY_COUNT_POWER_ON) new_state = POWER_STATE_ON_STARTED; else new_state = POWER_STATE_ON_FAILED; gpio_power_set_state(new_state); return; case POWER_EVENT_REBOOT_TIMEOUT: /* Modem not rebooting - try to powercycle */ if (self.target == PHONET_LINK_DOWN) new_state = POWER_STATE_OFF_STARTED; else if (self.retry_count <= RETRY_COUNT_RESET) new_state = POWER_STATE_ON_RESET; else new_state = POWER_STATE_ON_STARTED; gpio_power_set_state(new_state); return; case POWER_EVENT_PHONET_LINK_UP: switch (self.state) { case POWER_STATE_NONE: return; case POWER_STATE_ON_STARTED: case POWER_STATE_ON_RESET: break; case POWER_STATE_ON: return; case POWER_STATE_OFF_STARTED: case POWER_STATE_OFF_WAITING: case POWER_STATE_OFF: case POWER_STATE_ON_FAILED: DBG("LINK_UP event while modem should be powered off"); /* should never come here */ break; } if (self.target == PHONET_LINK_DOWN) gpio_power_set_state(POWER_STATE_OFF_STARTED); else gpio_power_set_state(POWER_STATE_ON); return; case POWER_EVENT_OFF: self.target = PHONET_LINK_DOWN; switch (self.state) { case POWER_STATE_ON_STARTED: case POWER_STATE_ON_RESET: /* Do nothing until a timer expires */ break; case POWER_STATE_ON: gpio_power_set_state(POWER_STATE_OFF_STARTED); break; case POWER_STATE_OFF_STARTED: case POWER_STATE_OFF_WAITING: case POWER_STATE_OFF: /* Do nothing */ break; case POWER_STATE_NONE: case POWER_STATE_ON_FAILED: gpio_power_set_state(POWER_STATE_OFF); break; } return; case POWER_EVENT_OFF_IMMEDIATELY: gpio_power_set_state(POWER_STATE_OFF); return; case POWER_EVENT_OFF_TIMEOUT: DBG("Modem power off timed out"); gpio_power_set_state(POWER_STATE_OFF); return; case POWER_EVENT_OFF_COMPLETE: if (self.state == POWER_STATE_OFF_WAITING) { DBG("Modem shutdown complete"); gpio_power_set_state(POWER_STATE_OFF); } return; } DBG("Event %s (%d) not handled", gpio_power_event_name(event), event); } static void gpio_power_set_state(enum power_state new_state) { enum power_state old_state = self.state; unsigned timeout = 0; enum power_event timer_event; DBG("(%s) at (%s)%s", gpio_power_state_name(new_state), gpio_power_state_name(old_state), new_state == old_state ? " - already" : ""); switch (old_state) { case POWER_STATE_ON_STARTED: gpio_finish_modem_power_on(); break; case POWER_STATE_ON_RESET: gpio_finish_modem_reset(); break; default: break; } if (self.timeout_source) { g_source_remove(self.timeout_source); self.timeout_source = 0; self.timer_event = 0; } if (old_state == new_state && new_state != POWER_STATE_ON_STARTED && new_state != POWER_STATE_ON_RESET) return; self.state = new_state; switch (self.state) { case POWER_STATE_NONE: break; case POWER_STATE_ON_STARTED: self.retry_count++; /* Maximum time modem power on procedure on can take */ timeout = 5000; timer_event = POWER_EVENT_ON_TIMEOUT; gpio_start_modem_power_on(); break; case POWER_STATE_ON_RESET: DBG("Starting modem restart timeout"); /* Time allowed for modem to restart after crash */ timeout = 5000; timer_event = POWER_EVENT_REBOOT_TIMEOUT; if (self.retry_count++ > 0) gpio_start_modem_reset(); break; case POWER_STATE_ON: DBG("Power on"); self.retry_count = 0; break; case POWER_STATE_OFF_STARTED: DBG("Starting power off"); /* Maximum time modem power_off can take */ timeout = 6150; timer_event = POWER_EVENT_OFF_TIMEOUT; break; case POWER_STATE_OFF_WAITING: gpio_finish_modem_power_off(); DBG("Waiting for modem to settle down"); /* Cooling time after power off */ timeout = 1000; timer_event = POWER_EVENT_OFF_COMPLETE; break; case POWER_STATE_OFF: if (old_state != POWER_STATE_OFF_WAITING && old_state != POWER_STATE_ON_FAILED) gpio_finish_modem_power_off(); break; case POWER_STATE_ON_FAILED: DBG("Link to modem cannot be established, giving up"); gpio_finish_modem_power_off(); break; } if (timeout) { self.timer_event = timer_event; self.timeout_source = g_timeout_add(timeout, gpio_power_timer_cb, NULL); } self.callback(new_state, self.data); } static void phonet_status_cb(GIsiModem *idx, enum GIsiPhonetLinkState state, char const *ifname, void *dummy) { DBG("Link %s (%u) is %s", ifname, g_isi_modem_index(idx), state == PN_LINK_REMOVED ? "removed" : state == PN_LINK_DOWN ? "down" : "up"); if (state == PN_LINK_UP) { if (self.current == PHONET_LINK_UP) return; self.current = PHONET_LINK_UP; /* link is up - we can lower cmt_rst_rq */ GPIO_WRITE(cmt_rst_rq, 0); gpio_power_state_machine(POWER_EVENT_PHONET_LINK_UP); } else { if (self.current == PHONET_LINK_DOWN) return; self.current = PHONET_LINK_DOWN; gpio_power_state_machine(POWER_EVENT_PHONET_LINK_DOWN); } } static int gpio_probe_links(void) { char const *gpiodir = "/sys/class/gpio"; char const *cmtdir = "/dev/cmt"; DIR *gpio; struct dirent *d, entry[1]; if (file_exists(cmtdir)) { DBG("Using %s", cmtdir); return 0; } DBG("Using %s: trying to make links to %s", gpiodir, cmtdir); if (!dir_exists(cmtdir)) { if (mkdir(cmtdir, 0755) == -1) { DBG("%s: %s", cmtdir, strerror(errno)); return -(errno = ENODEV); } } gpio = opendir(gpiodir); if (gpio == NULL) { DBG("%s: %s", "gpiodir", strerror(errno)); return -(errno = ENODEV); } while (readdir_r(gpio, entry, &d) == 0) { char nn[PATH_MAX], name[PATH_MAX], from[PATH_MAX], to[PATH_MAX]; FILE *nf; size_t len; if (d == NULL) { (void) closedir(gpio); return 0; } snprintf(nn, sizeof nn, "%s/%s/name", gpiodir, d->d_name); nf = fopen(nn, "rb"); if (nf == NULL) { DBG("%s: %s", nn, strerror(errno)); continue; } len = fread(name, sizeof name, 1, nf); if (ferror(nf)) { DBG("read from %s: %s", nn, strerror(errno)); fclose(nf); continue; } fclose(nf); if (len < 4) continue; name[--len] = '\0'; if (strncmp(name, "cmt_", 4)) continue; snprintf(from, sizeof from, "%s/%s", gpiodir, d->d_name); snprintf(to, sizeof to, "%s/%s", cmtdir, name); if (symlink(from, to) == -1) DBG("%s: %s", to, strerror(errno)); } DBG("%s: %s", "/sys/class/gpio", strerror(errno)); (void) closedir(gpio); return -(errno = ENODEV); } int gpio_probe(GIsiModem *idx, unsigned addr, gpio_finished_cb_t cb, void *data) { int error; if (cb == NULL) { DBG("gpio: No callback given"); return -(errno = EFAULT); } if (self.callback) { DBG("gpio: %s", strerror(EBUSY)); return -(errno = EBUSY); } if (g_isi_pn_netlink_by_modem(idx)) { DBG("Phonet link %p: %s", idx, strerror(EBUSY)); return -(errno = EBUSY); } self.target = PHONET_LINK_NONE; self.have_gpio_switch = file_exists(GPIO_SWITCH); if (self.have_gpio_switch) { DBG("Using GPIO switch"); } else { error = gpio_probe_links(); if (error) return error; } /* GPIO lines availability depends on HW and SW versions */ self.have_cmt_en = gpio_line_probe("cmt_en"); self.have_cmt_rst_rq = gpio_line_probe("cmt_rst_rq"); self.have_cmt_rst = gpio_line_probe("cmt_rst"); self.have_cmt_bsi = gpio_line_probe("cmt_bsi"); self.have_cmt_apeslpx = gpio_line_probe("cmt_apeslpx"); if (!self.have_cmt_en) { DBG("Modem control GPIO lines are not available"); memset(&self, 0, sizeof self); return -(errno = ENODEV); } if (self.have_cmt_bsi) self.rapu = RAPU_TYPE_1; else self.rapu = RAPU_TYPE_2; self.link = g_isi_pn_netlink_start(idx, phonet_status_cb, NULL); if (self.link == NULL) { memset(&self, 0, sizeof self); return -errno; } self.callback = cb; self.data = data; if (addr) { error = g_isi_pn_netlink_set_address(idx, addr); if (error && error != -EEXIST) DBG("g_isi_netlink_set_address: %s", strerror(-error)); } return 0; } int gpio_remove(void *data) { if (self.data != data) return -EINVAL; if (self.link) g_isi_pn_netlink_stop(self.link); if (self.timeout_source) { g_source_remove(self.timeout_source); self.timeout_source = 0; } memset(&self, 0, sizeof self); return 0; } int gpio_enable(void *data) { if (self.data != data) return -EINVAL; if (self.state == POWER_STATE_ON) return 0; gpio_power_state_machine(POWER_EVENT_ON); return -EINPROGRESS; } int gpio_disable(void *data) { if (self.data != data) return -EINVAL; if (self.state == POWER_STATE_OFF || self.state == POWER_STATE_ON_FAILED) return 0; gpio_power_state_machine(POWER_EVENT_OFF); return -EINPROGRESS; } ofono-1.17.bzr6912+16.04.20160314.3/include/0000755000015600001650000000000012671500304020023 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/include/location-reporting.h0000644000015600001650000000515612671500024024021 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2010 ProFUSION embedded systems. * * 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 * */ #ifndef __OFONO_LOCATION_REPORTING_H #define __OFONO_LOCATION_REPORTING_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_location_reporting; enum ofono_location_reporting_type { OFONO_LOCATION_REPORTING_TYPE_NMEA = 0, }; typedef void (*ofono_location_reporting_enable_cb_t)( const struct ofono_error *error, int fd, void *data); typedef void (*ofono_location_reporting_disable_cb_t)( const struct ofono_error *error, void *data); struct ofono_location_reporting_driver { const char *name; enum ofono_location_reporting_type type; int (*probe)(struct ofono_location_reporting *lr, unsigned int vendor, void *data); void (*remove)(struct ofono_location_reporting *lr); void (*enable)(struct ofono_location_reporting *lr, ofono_location_reporting_enable_cb_t cb, void *data); void (*disable)(struct ofono_location_reporting *lr, ofono_location_reporting_disable_cb_t cb, void *data); }; int ofono_location_reporting_driver_register( const struct ofono_location_reporting_driver *d); void ofono_location_reporting_driver_unregister( const struct ofono_location_reporting_driver *d); struct ofono_location_reporting *ofono_location_reporting_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_location_reporting_register(struct ofono_location_reporting *lr); void ofono_location_reporting_remove(struct ofono_location_reporting *lr); void ofono_location_reporting_set_data(struct ofono_location_reporting *lr, void *data); void *ofono_location_reporting_get_data(struct ofono_location_reporting *lr); struct ofono_modem *ofono_location_reporting_get_modem( struct ofono_location_reporting *lr); #ifdef __cplusplus } #endif #endif /* __OFONO_LOCATION_REPORTING_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/audio-settings.h0000644000015600001650000000412412671500024023133 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_AUDIO_SETTINGS_H #define __OFONO_AUDIO_SETTINGS_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_audio_settings; struct ofono_audio_settings_driver { const char *name; int (*probe)(struct ofono_audio_settings *as, unsigned int vendor, void *data); void (*remove)(struct ofono_audio_settings *as); }; void ofono_audio_settings_active_notify(struct ofono_audio_settings *as, ofono_bool_t active); void ofono_audio_settings_mode_notify(struct ofono_audio_settings *as, const char *mode); int ofono_audio_settings_driver_register( const struct ofono_audio_settings_driver *d); void ofono_audio_settings_driver_unregister( const struct ofono_audio_settings_driver *d); struct ofono_audio_settings *ofono_audio_settings_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_audio_settings_register(struct ofono_audio_settings *as); void ofono_audio_settings_remove(struct ofono_audio_settings *as); void ofono_audio_settings_set_data(struct ofono_audio_settings *as, void *data); void *ofono_audio_settings_get_data(struct ofono_audio_settings *as); struct ofono_modem *ofono_audio_settings_get_modem( struct ofono_audio_settings *as); #ifdef __cplusplus } #endif #endif /* __OFONO_AUDIO_SETTINGS_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/sim.h0000644000015600001650000002317112671500024020767 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_SIM_H #define __OFONO_SIM_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_sim; struct ofono_sim_context; /* 51.011 Section 9.3 */ enum ofono_sim_file_structure { OFONO_SIM_FILE_STRUCTURE_TRANSPARENT = 0, OFONO_SIM_FILE_STRUCTURE_FIXED = 1, OFONO_SIM_FILE_STRUCTURE_CYCLIC = 3 }; enum ofono_sim_password_type { OFONO_SIM_PASSWORD_NONE = 0, OFONO_SIM_PASSWORD_SIM_PIN, OFONO_SIM_PASSWORD_PHSIM_PIN, OFONO_SIM_PASSWORD_PHFSIM_PIN, OFONO_SIM_PASSWORD_SIM_PIN2, OFONO_SIM_PASSWORD_PHNET_PIN, OFONO_SIM_PASSWORD_PHNETSUB_PIN, OFONO_SIM_PASSWORD_PHSP_PIN, OFONO_SIM_PASSWORD_PHCORP_PIN, OFONO_SIM_PASSWORD_SIM_PUK, OFONO_SIM_PASSWORD_PHFSIM_PUK, OFONO_SIM_PASSWORD_SIM_PUK2, OFONO_SIM_PASSWORD_PHNET_PUK, OFONO_SIM_PASSWORD_PHNETSUB_PUK, OFONO_SIM_PASSWORD_PHSP_PUK, OFONO_SIM_PASSWORD_PHCORP_PUK, OFONO_SIM_PASSWORD_INVALID, }; enum ofono_sim_phase { OFONO_SIM_PHASE_1G, OFONO_SIM_PHASE_2G, OFONO_SIM_PHASE_2G_PLUS, OFONO_SIM_PHASE_3G, OFONO_SIM_PHASE_UNKNOWN, }; enum ofono_sim_cphs_phase { OFONO_SIM_CPHS_PHASE_NONE, OFONO_SIM_CPHS_PHASE_1G, OFONO_SIM_CPHS_PHASE_2G, }; enum ofono_sim_state { OFONO_SIM_STATE_NOT_PRESENT, OFONO_SIM_STATE_INSERTED, OFONO_SIM_STATE_LOCKED_OUT, OFONO_SIM_STATE_READY, OFONO_SIM_STATE_RESETTING, }; typedef void (*ofono_sim_file_info_cb_t)(const struct ofono_error *error, int filelength, enum ofono_sim_file_structure structure, int recordlength, const unsigned char access[3], unsigned char file_status, void *data); typedef void (*ofono_sim_read_cb_t)(const struct ofono_error *error, const unsigned char *sdata, int length, void *data); typedef void (*ofono_sim_write_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_sim_imsi_cb_t)(const struct ofono_error *error, const char *imsi, void *data); typedef void (*ofono_sim_state_event_cb_t)(enum ofono_sim_state new_state, void *data); typedef void (*ofono_sim_file_read_cb_t)(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata); typedef void (*ofono_sim_read_info_cb_t)(int ok, unsigned char file_status, int total_length, int record_length, void *userdata); typedef void (*ofono_sim_file_changed_cb_t)(int id, void *userdata); typedef void (*ofono_sim_file_write_cb_t)(int ok, void *userdata); typedef void (*ofono_sim_passwd_cb_t)(const struct ofono_error *error, enum ofono_sim_password_type type, void *data); typedef void (*ofono_sim_pin_retries_cb_t)(const struct ofono_error *error, int retries[OFONO_SIM_PASSWORD_INVALID], void *data); typedef void (*ofono_sim_lock_unlock_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_sim_locked_cb_t)(const struct ofono_error *error, int locked, void *data); struct ofono_sim_driver { const char *name; int (*probe)(struct ofono_sim *sim, unsigned int vendor, void *data); void (*remove)(struct ofono_sim *sim); void (*read_file_info)(struct ofono_sim *sim, int fileid, const unsigned char *path, unsigned int path_len, ofono_sim_file_info_cb_t cb, void *data); void (*read_file_transparent)(struct ofono_sim *sim, int fileid, int start, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_cb_t cb, void *data); void (*read_file_linear)(struct ofono_sim *sim, int fileid, int record, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_cb_t cb, void *data); void (*read_file_cyclic)(struct ofono_sim *sim, int fileid, int record, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_cb_t cb, void *data); void (*write_file_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 *data); void (*write_file_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 *data); void (*write_file_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 *data); void (*read_imsi)(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb, void *data); void (*query_passwd_state)(struct ofono_sim *sim, ofono_sim_passwd_cb_t cb, void *data); void (*send_passwd)(struct ofono_sim *sim, const char *passwd, ofono_sim_lock_unlock_cb_t cb, void *data); void (*query_pin_retries)(struct ofono_sim *sim, ofono_sim_pin_retries_cb_t cb, void *data); void (*reset_passwd)(struct ofono_sim *sim, const char *puk, const char *passwd, ofono_sim_lock_unlock_cb_t cb, void *data); void (*change_passwd)(struct ofono_sim *sim, enum ofono_sim_password_type type, const char *old_passwd, const char *new_passwd, ofono_sim_lock_unlock_cb_t cb, void *data); void (*lock)(struct ofono_sim *sim, enum ofono_sim_password_type type, int enable, const char *passwd, ofono_sim_lock_unlock_cb_t cb, void *data); void (*query_locked)(struct ofono_sim *sim, enum ofono_sim_password_type type, ofono_sim_locked_cb_t cb, void *data); }; int ofono_sim_driver_register(const struct ofono_sim_driver *d); void ofono_sim_driver_unregister(const struct ofono_sim_driver *d); struct ofono_sim *ofono_sim_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_sim_register(struct ofono_sim *sim); void ofono_sim_remove(struct ofono_sim *sim); void ofono_sim_set_data(struct ofono_sim *sim, void *data); void *ofono_sim_get_data(struct ofono_sim *sim); const char *ofono_sim_get_imsi(struct ofono_sim *sim); const char *ofono_sim_get_mcc(struct ofono_sim *sim); const char *ofono_sim_get_mnc(struct ofono_sim *sim); const char *ofono_sim_get_spn(struct ofono_sim *sim); enum ofono_sim_phase ofono_sim_get_phase(struct ofono_sim *sim); enum ofono_sim_cphs_phase ofono_sim_get_cphs_phase(struct ofono_sim *sim); const unsigned char *ofono_sim_get_cphs_service_table(struct ofono_sim *sim); enum ofono_sim_password_type ofono_sim_get_password_type(struct ofono_sim *sim); unsigned int ofono_sim_add_state_watch(struct ofono_sim *sim, ofono_sim_state_event_cb_t cb, void *data, ofono_destroy_func destroy); void ofono_sim_remove_state_watch(struct ofono_sim *sim, unsigned int id); enum ofono_sim_state ofono_sim_get_state(struct ofono_sim *sim); typedef void (*ofono_sim_spn_cb_t)(const char *spn, const char *dc, void *data); ofono_bool_t ofono_sim_add_spn_watch(struct ofono_sim *sim, unsigned int *id, ofono_sim_spn_cb_t cb, void *data, ofono_destroy_func destroy); ofono_bool_t ofono_sim_remove_spn_watch(struct ofono_sim *sim, unsigned int *id); void ofono_sim_inserted_notify(struct ofono_sim *sim, ofono_bool_t inserted); struct ofono_sim_context *ofono_sim_context_create(struct ofono_sim *sim); void ofono_sim_context_free(struct ofono_sim_context *context); /* * This will queue an operation to read all available records with id from the * SIM. Callback cb will be called every time a record has been read, or once * if an error has occurred. For transparent files, the callback will only * be called once. * * Returns 0 if the request could be queued, -1 otherwise. */ int ofono_sim_read(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected, ofono_sim_file_read_cb_t cb, void *data); int ofono_sim_read_path(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, const unsigned char *path, unsigned int path_len, ofono_sim_file_read_cb_t cb, void *data); int ofono_sim_read_info(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, const unsigned char *path, unsigned int pth_len, ofono_sim_read_info_cb_t cb, void *data); int ofono_sim_read_record(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, int record, int record_length, const unsigned char *path, unsigned int pth_len, ofono_sim_file_read_cb_t cb, void *data); int ofono_sim_write(struct ofono_sim_context *context, int id, ofono_sim_file_write_cb_t cb, enum ofono_sim_file_structure structure, int record, const unsigned char *data, int length, void *userdata); int ofono_sim_read_bytes(struct ofono_sim_context *context, int id, unsigned short offset, unsigned short num_bytes, const unsigned char *path, unsigned int path_len, ofono_sim_file_read_cb_t cb, void *data); unsigned int ofono_sim_add_file_watch(struct ofono_sim_context *context, int id, ofono_sim_file_changed_cb_t cb, void *userdata, ofono_destroy_func destroy); void ofono_sim_remove_file_watch(struct ofono_sim_context *context, unsigned int id); #ifdef __cplusplus } #endif #endif /* __OFONO_SIM_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/cbs.h0000644000015600001650000000363712671500024020753 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_CBS_H #define __OFONO_CBS_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_cbs; typedef void (*ofono_cbs_set_cb_t)(const struct ofono_error *error, void *data); struct ofono_cbs_driver { const char *name; int (*probe)(struct ofono_cbs *cbs, unsigned int vendor, void *data); void (*remove)(struct ofono_cbs *cbs); void (*set_topics)(struct ofono_cbs *cbs, const char *topics, ofono_cbs_set_cb_t cb, void *data); void (*clear_topics)(struct ofono_cbs *cbs, ofono_cbs_set_cb_t cb, void *data); }; void ofono_cbs_notify(struct ofono_cbs *cbs, const unsigned char *pdu, int len); int ofono_cbs_driver_register(const struct ofono_cbs_driver *d); void ofono_cbs_driver_unregister(const struct ofono_cbs_driver *d); struct ofono_cbs *ofono_cbs_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_cbs_register(struct ofono_cbs *cbs); void ofono_cbs_remove(struct ofono_cbs *cbs); void ofono_cbs_set_data(struct ofono_cbs *cbs, void *data); void *ofono_cbs_get_data(struct ofono_cbs *cbs); #ifdef __cplusplus } #endif #endif /* __OFONO_CBS_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/siri.h0000644000015600001650000000354312671500024021146 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2013 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 * */ #ifndef __OFONO_SIRI_H #define __OFONO_SIRI_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_siri; typedef void (*ofono_siri_cb_t)(const struct ofono_error *error, struct ofono_siri *siri); struct ofono_siri_driver { const char *name; int (*probe)(struct ofono_siri *siri, unsigned int vendor, void *data); void (*remove)(struct ofono_siri *siri); void (*set_eyes_free_mode) (struct ofono_siri *siri, ofono_siri_cb_t cb, unsigned int val); }; void ofono_siri_set_status(struct ofono_siri *siri, int value); int ofono_siri_driver_register(const struct ofono_siri_driver *driver); void ofono_siri_driver_unregister(const struct ofono_siri_driver *driver); struct ofono_siri *ofono_siri_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_siri_register(struct ofono_siri *siri); void ofono_siri_remove(struct ofono_siri *siri); void ofono_siri_set_data(struct ofono_siri *siri, void *data); void *ofono_siri_get_data(struct ofono_siri *siri); #ifdef __cplusplus } #endif #endif /* __OFONO_SIRI_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/message-waiting.h0000644000015600001650000000234312671500024023261 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_MESSAGE_WAITING_H #define __OFONO_MESSAGE_WAITING_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_message_waiting; struct ofono_message_waiting *ofono_message_waiting_create( struct ofono_modem *modem); void ofono_message_waiting_register(struct ofono_message_waiting *mw); void ofono_message_waiting_remove(struct ofono_message_waiting *mw); #ifdef __cplusplus } #endif #endif /* __OFONO_MESSAGE_WAITING_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/gprs-provision.h0000644000015600001650000000323612671500024023200 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __OFONO_GPRS_PROVISION_H #define __OFONO_GPRS_PROVISION_H #ifdef __cplusplus extern "C" { #endif #include "gprs-context.h" struct ofono_gprs_provision_data { enum ofono_gprs_context_type type; enum ofono_gprs_proto proto; char *name; char *apn; char *username; char *password; enum ofono_gprs_auth_method auth_method; char *message_proxy; char *message_center; }; struct ofono_gprs_provision_driver { const char *name; int priority; int (*get_settings)(const char *mcc, const char *mnc, const char *spn, const char *imsi, const char *gid1, struct ofono_gprs_provision_data **settings, int *count); }; int ofono_gprs_provision_driver_register( const struct ofono_gprs_provision_driver *driver); void ofono_gprs_provision_driver_unregister( const struct ofono_gprs_provision_driver *driver); #ifdef __cplusplus } #endif #endif /* __OFONO_GPRS_PROVISION_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/phonebook.h0000644000015600001650000000447612671500024022172 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * 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 * */ #ifndef __OFONO_PHONEBOOK_H #define __OFONO_PHONEBOOK_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_phonebook; typedef void (*ofono_phonebook_cb_t)(const struct ofono_error *error, void *data); /* Export entries reports results through ofono_phonebook_entry, if an error * occurs, ofono_phonebook_entry should not be called */ struct ofono_phonebook_driver { const char *name; int (*probe)(struct ofono_phonebook *pb, unsigned int vendor, void *data); void (*remove)(struct ofono_phonebook *pb); void (*export_entries)(struct ofono_phonebook *pb, const char *storage, ofono_phonebook_cb_t cb, void *data); }; void ofono_phonebook_entry(struct ofono_phonebook *pb, int index, const char *number, int type, const char *text, int hidden, const char *group, const char *adnumber, int adtype, const char *secondtext, const char *email, const char *sip_uri, const char *tel_uri); int ofono_phonebook_driver_register(const struct ofono_phonebook_driver *d); void ofono_phonebook_driver_unregister(const struct ofono_phonebook_driver *d); struct ofono_phonebook *ofono_phonebook_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_phonebook_register(struct ofono_phonebook *pb); void ofono_phonebook_remove(struct ofono_phonebook *pb); void ofono_phonebook_set_data(struct ofono_phonebook *pb, void *data); void *ofono_phonebook_get_data(struct ofono_phonebook *pb); #ifdef __cplusplus } #endif #endif /* __OFONO_PHONEBOOK_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/private-network.h0000644000015600001650000000300312671500024023330 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_PRIVATE_NETWORK_H #define __OFONO_PRIVATE_NETWORK_H #ifdef __cplusplus extern "C" { #endif struct ofono_private_network_settings { int fd; char *server_ip; char *peer_ip; char *primary_dns; char *secondary_dns; }; typedef void (*ofono_private_network_cb_t)( const struct ofono_private_network_settings *settings, void *data); struct ofono_private_network_driver { char *name; int (*request)(ofono_private_network_cb_t cb, void *data); void (*release)(int uid); }; int ofono_private_network_driver_register( const struct ofono_private_network_driver *d); void ofono_private_network_driver_unregister( const struct ofono_private_network_driver *d); #ifdef __cplusplus } #endif #endif /* __OFONO_PRIVATE_NETWORK_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/plugin.h0000644000015600001650000000512012671500024021467 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_PLUGIN_H #define __OFONO_PLUGIN_H #include #include #ifdef __cplusplus extern "C" { #endif #ifndef OFONO_API_SUBJECT_TO_CHANGE #error "Please define OFONO_API_SUBJECT_TO_CHANGE to acknowledge your \ understanding that oFono hasn't reached a stable API." #endif #define OFONO_PLUGIN_PRIORITY_LOW -100 #define OFONO_PLUGIN_PRIORITY_DEFAULT 0 #define OFONO_PLUGIN_PRIORITY_HIGH 100 /** * SECTION:plugin * @title: Plugin premitives * @short_description: Functions for declaring plugins */ struct ofono_plugin_desc { const char *name; const char *description; const char *version; int priority; int (*init) (void); void (*exit) (void); void *debug_start; void *debug_stop; }; /** * OFONO_PLUGIN_DEFINE: * @name: plugin name * @description: plugin description * @version: plugin version string * @init: init function called on plugin loading * @exit: exit function called on plugin removal * * Macro for defining a plugin descriptor */ #ifdef OFONO_PLUGIN_BUILTIN #define OFONO_PLUGIN_DEFINE(name, description, version, priority, init, exit) \ struct ofono_plugin_desc __ofono_builtin_ ## name = { \ #name, description, version, priority, init, exit \ }; #else #define OFONO_PLUGIN_DEFINE(name, description, version, priority, init, exit) \ extern struct ofono_debug_desc __start___debug[] \ __attribute__ ((weak, visibility("hidden"))); \ extern struct ofono_debug_desc __stop___debug[] \ __attribute__ ((weak, visibility("hidden"))); \ extern struct ofono_plugin_desc ofono_plugin_desc \ __attribute__ ((visibility("default"))); \ struct ofono_plugin_desc ofono_plugin_desc = { \ #name, description, version, priority, init, exit, \ __start___debug, __stop___debug \ }; #endif #ifdef __cplusplus } #endif #endif /* __OFONO_PLUGIN_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/call-barring.h0000644000015600001650000000464312671500024022537 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * 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 * */ #ifndef __OFONO_CALL_BARRING_H #define __OFONO_CALL_BARRING_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_call_barring; typedef void (*ofono_call_barring_set_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_call_barring_query_cb_t)(const struct ofono_error *error, int status, void *data); struct ofono_call_barring_driver { const char *name; int (*probe)(struct ofono_call_barring *cb, unsigned int vendor, void *data); void (*remove)(struct ofono_call_barring *cb); void (*set)(struct ofono_call_barring *barr, const char *lock, int enable, const char *passwd, int cls, ofono_call_barring_set_cb_t cb, void *data); void (*query)(struct ofono_call_barring *barr, const char *lock, int cls, ofono_call_barring_query_cb_t cb, void *data); void (*set_passwd)(struct ofono_call_barring *barr, const char *lock, const char *old_passwd, const char *new_passwd, ofono_call_barring_set_cb_t cb, void *data); }; int ofono_call_barring_driver_register( const struct ofono_call_barring_driver *d); void ofono_call_barring_driver_unregister( const struct ofono_call_barring_driver *d); struct ofono_call_barring *ofono_call_barring_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_call_barring_register(struct ofono_call_barring *cb); void ofono_call_barring_remove(struct ofono_call_barring *cb); void ofono_call_barring_set_data(struct ofono_call_barring *cb, void *data); void *ofono_call_barring_get_data(struct ofono_call_barring *cb); #ifdef __cplusplus } #endif #endif /* __OFONO_CALL_BARRING_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/wakelock.h0000644000015600001650000000606412671500073022005 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2015 Jolla Ltd. All rights reserved. * Contact: Hannu Mallat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef __OFONO_WAKELOCK_H_ #define __OFONO_WAKELOCK_H_ #include /* Wakelock interface * * Wakelocks ensure system does not suspend/enter power save mode * before an ongoing operation completes. This header declares a * wakelock API that can be implemented by a OS-specific plugin. */ struct wakelock; /* Opaque */ struct wakelock_table { int (*create)(const char *name, struct wakelock **); int (*free)(struct wakelock *); int (*acquire)(struct wakelock *); int (*release)(struct wakelock *); ofono_bool_t (*is_locked)(struct wakelock *); }; /*** Functions for wakelock users ***/ /* * This will create a wakelock which will be held for a short duration * and then automatically freed. If this is called multiple times the * timeout will be restarted on every call. */ void wakelock_system_lock(void); /* * Create a wakelock. Multiple wakelocks can be created; if any one of * them is activated, system will be prevented from going to suspend. * This makes it possible to overlap locks to hand over from subsystem * to subsystem, each with their own wakelock-guarded sections, * without falling to suspend in between. */ int wakelock_create(const char *name, struct wakelock **wakelock); /* * Free a wakelock, releasing all acquisitions at the same time * and deallocating the lock. */ int wakelock_free(struct wakelock *wakelock); /* * Acquire a wakelock. Multiple acquisitions are possible, meaning * that the wakelock needs to be released the same number of times * until it is actually deactivated. */ int wakelock_acquire(struct wakelock *wakelock); /* * Release a wakelock, deactivating it if all acquisitions are * released, letting system suspend. */ int wakelock_release(struct wakelock *wakelock); /* * Check if a wakelock is currently locked or not. */ ofono_bool_t wakelock_is_locked(struct wakelock *wakelock); /*** Functions for wakelock implementors ***/ /* * Register a wakelock implementation. Only one implementation may be * registered at a time. In the absence of an implementation, all * wakelock functions are no-ops. */ int wakelock_plugin_register(const char *name, struct wakelock_table *fns); int wakelock_plugin_unregister(void); #endif ofono-1.17.bzr6912+16.04.20160314.3/include/spn-table.h0000644000015600001650000000233312671500024022061 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * Copyright (C) 2014 Canonical 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. * * 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 * */ #ifndef OFONO_SPN_TABLE_H #define OFONO_SPN_TABLE_H #ifdef __cplusplus extern "C" { #endif struct ofono_spn_table_driver { const char *name; const char *(*get_spn)(const char *numeric); }; const char *__ofono_spn_table_get_spn(const char *numeric); int ofono_spn_table_driver_register(struct ofono_spn_table_driver *driver); void ofono_spn_table_driver_unregister( const struct ofono_spn_table_driver *driver); #ifdef __cplusplus } #endif #endif /* OFONO_SPN_TABLE_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/radio-settings.h0000644000015600001650000001070112671500024023126 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __OFONO_RADIO_SETTINGS_H #define __OFONO_RADIO_SETTINGS_H #ifdef __cplusplus extern "C" { #endif #include enum ofono_radio_access_mode { OFONO_RADIO_ACCESS_MODE_ANY = 0x0, OFONO_RADIO_ACCESS_MODE_GSM = 0x1, OFONO_RADIO_ACCESS_MODE_UMTS = 0x2, OFONO_RADIO_ACCESS_MODE_LTE = 0x4, }; enum ofono_radio_band_gsm { OFONO_RADIO_BAND_GSM_ANY, OFONO_RADIO_BAND_GSM_850, OFONO_RADIO_BAND_GSM_900P, OFONO_RADIO_BAND_GSM_900E, OFONO_RADIO_BAND_GSM_1800, OFONO_RADIO_BAND_GSM_1900, }; enum ofono_radio_band_umts { OFONO_RADIO_BAND_UMTS_ANY, OFONO_RADIO_BAND_UMTS_850, OFONO_RADIO_BAND_UMTS_900, OFONO_RADIO_BAND_UMTS_1700AWS, OFONO_RADIO_BAND_UMTS_1900, OFONO_RADIO_BAND_UMTS_2100, }; struct ofono_radio_settings; typedef void (*ofono_radio_settings_rat_mode_set_cb_t)( const struct ofono_error *error, void *data); typedef void (*ofono_radio_settings_rat_mode_query_cb_t)( const struct ofono_error *error, enum ofono_radio_access_mode mode, void *data); typedef void (*ofono_radio_settings_band_set_cb_t)( const struct ofono_error *error, void *data); typedef void (*ofono_radio_settings_band_query_cb_t)( const struct ofono_error *error, enum ofono_radio_band_gsm band_gsm, enum ofono_radio_band_umts band_umts, void *data); typedef void (*ofono_radio_settings_fast_dormancy_set_cb_t)( const struct ofono_error *error, void *data); typedef void (*ofono_radio_settings_fast_dormancy_query_cb_t)( const struct ofono_error *error, ofono_bool_t enable, void *data); typedef void (*ofono_radio_settings_available_rats_query_cb_t)( const struct ofono_error *error, unsigned int available_rats, void *data); struct ofono_radio_settings_driver { const char *name; int (*probe)(struct ofono_radio_settings *rs, unsigned int vendor, void *data); void (*remove)(struct ofono_radio_settings *rs); void (*query_rat_mode)(struct ofono_radio_settings *rs, ofono_radio_settings_rat_mode_query_cb_t cb, void *data); void (*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); void (*query_band)(struct ofono_radio_settings *rs, ofono_radio_settings_band_query_cb_t cb, void *data); void (*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); void (*query_fast_dormancy)(struct ofono_radio_settings *rs, ofono_radio_settings_fast_dormancy_query_cb_t cb, void *data); void (*set_fast_dormancy)(struct ofono_radio_settings *rs, ofono_bool_t enable, ofono_radio_settings_fast_dormancy_set_cb_t, void *data); void (*query_available_rats)(struct ofono_radio_settings *rs, ofono_radio_settings_available_rats_query_cb_t cb, void *data); }; int ofono_radio_settings_driver_register( const struct ofono_radio_settings_driver *d); void ofono_radio_settings_driver_unregister( const struct ofono_radio_settings_driver *d); struct ofono_radio_settings *ofono_radio_settings_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_radio_settings_register(struct ofono_radio_settings *rs); void ofono_radio_settings_remove(struct ofono_radio_settings *rs); void ofono_radio_settings_set_data(struct ofono_radio_settings *rs, void *data); void *ofono_radio_settings_get_data(struct ofono_radio_settings *rs); void ofono_radio_settings_set_rat_mode(struct ofono_radio_settings *rs, enum ofono_radio_access_mode mode); #ifdef __cplusplus } #endif #endif /* __OFONO_RADIO_SETTINGS_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/stk.h0000644000015600001650000000464212671500024021002 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_STK_H #define __OFONO_STK_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_stk; typedef void (*ofono_stk_envelope_cb_t)(const struct ofono_error *error, const unsigned char *rdata, int length, void *data); typedef void (*ofono_stk_generic_cb_t)(const struct ofono_error *error, void *data); struct ofono_stk_driver { const char *name; int (*probe)(struct ofono_stk *stk, unsigned int vendor, void *data); void (*remove)(struct ofono_stk *stk); void (*envelope)(struct ofono_stk *stk, int length, const unsigned char *command, ofono_stk_envelope_cb_t cb, void *data); void (*terminal_response)(struct ofono_stk *stk, int length, const unsigned char *resp, ofono_stk_generic_cb_t cb, void *data); void (*user_confirmation)(struct ofono_stk *stk, ofono_bool_t confirm); }; int ofono_stk_driver_register(const struct ofono_stk_driver *d); void ofono_stk_driver_unregister(const struct ofono_stk_driver *d); struct ofono_stk *ofono_stk_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_stk_register(struct ofono_stk *stk); void ofono_stk_remove(struct ofono_stk *stk); void ofono_stk_set_data(struct ofono_stk *stk, void *data); void *ofono_stk_get_data(struct ofono_stk *stk); void ofono_stk_proactive_command_notify(struct ofono_stk *stk, int length, const unsigned char *pdu); void ofono_stk_proactive_session_end_notify(struct ofono_stk *stk); void ofono_stk_proactive_command_handled_notify(struct ofono_stk *stk, int length, const unsigned char *pdu); #ifdef __cplusplus } #endif #endif /* __OFONO_STK_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/history.h0000644000015600001650000000465212671500024021703 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * 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 * */ #ifndef __OFONO_HISTORY_H #define __OFONO_HISTORY_H #ifdef __cplusplus extern "C" { #endif #include enum ofono_disconnect_reason; struct ofono_call; enum ofono_history_sms_status { OFONO_HISTORY_SMS_STATUS_PENDING, OFONO_HISTORY_SMS_STATUS_SUBMITTED, OFONO_HISTORY_SMS_STATUS_SUBMIT_FAILED, OFONO_HISTORY_SMS_STATUS_SUBMIT_CANCELLED, OFONO_HISTORY_SMS_STATUS_DELIVERED, OFONO_HISTORY_SMS_STATUS_DELIVER_FAILED, }; struct ofono_history_context { struct ofono_history_driver *driver; struct ofono_modem *modem; void *data; }; struct ofono_history_driver { const char *name; int (*probe)(struct ofono_history_context *context); void (*remove)(struct ofono_history_context *context); void (*call_ended)(struct ofono_history_context *context, const struct ofono_call *call, time_t start, time_t end); void (*call_missed)(struct ofono_history_context *context, const struct ofono_call *call, time_t when); void (*sms_received)(struct ofono_history_context *context, const struct ofono_uuid *uuid, const char *from, const struct tm *remote, const struct tm *local, const char *text); void (*sms_send_pending)(struct ofono_history_context *context, const struct ofono_uuid *uuid, const char *to, time_t when, const char *text); void (*sms_send_status)(struct ofono_history_context *context, const struct ofono_uuid *uuid, time_t when, enum ofono_history_sms_status status); }; int ofono_history_driver_register(const struct ofono_history_driver *driver); void ofono_history_driver_unregister(const struct ofono_history_driver *driver); #ifdef __cplusplus } #endif #endif /* __OFONO_HISTORY_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/voicecall.h0000644000015600001650000001446012671500024022141 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_VOICECALL_H #define __OFONO_VOICECALL_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_voicecall; typedef void (*ofono_voicecall_cb_t)(const struct ofono_error *error, void *data); /* Voice call related functionality, including ATD, ATA, +CHLD, CTFR, CLCC * and VTS. * * It is up to the plugin to implement polling of CLCC if the modem does * not support vendor extensions for call progress indication. */ struct ofono_voicecall_driver { const char *name; int (*probe)(struct ofono_voicecall *vc, unsigned int vendor, void *data); void (*remove)(struct ofono_voicecall *vc); /* According to 22.030 the dial is expected to do the following: * - If an there is an existing active call(s), and the dial is * successful, the active calls are automatically put on hold. * Driver must take special care to put the call on hold before * returning from atd call. * * - The dial has no affect on the state of the waiting call, * if the hardware does not support this, then it is better * to return an error here. No special handling of the * waiting call is performed by the core */ void (*dial)(struct ofono_voicecall *vc, const struct ofono_phone_number *number, enum ofono_clir_option clir, ofono_voicecall_cb_t cb, void *data); /* Answers an incoming call, this usually corresponds to ATA */ void (*answer)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); /* Hangs up active, dialing, alerting or incoming calls */ void (*hangup_active)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); /* Hangs up all calls except waiting calls */ void (*hangup_all)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); /* * Holds all active calls and answers waiting call. If there is * no waiting calls, retrieves held call. This usually * corresponds to +CHLD=2 */ void (*hold_all_active)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); /* Releases all held calls, this usually corresponds to +CHLD=0*/ void (*release_all_held)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); /* * Sets the UDUB condition on a waiting call. This usually * corresponds to +CHLD=0 */ void (*set_udub)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); /* * Releases all active calls and accepts a possible waiting call. * This usually corresponds to +CHLD=1 */ void (*release_all_active)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); /* * Releases a specific call given by id. This usually corresponds to * +CHLD=1X. In 3GPP this command is only guaranteed to affect active * calls. Plugins are encouraged to implement this using vendor * commands that can also affect held calls */ void (*release_specific)(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data); /* * Breaks out a party given by id from a multiparty call. This * usually corresponds to +CHLD=2X */ void (*private_chat)(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data); /* * Joins held and active calls together into a multiparty call. This * usually corresponds to +CHLD=3 */ void (*create_multiparty)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); /* * Connects two calls together and disconnects from both calls. This * usually corresponds to +CHLD=4 */ void (*transfer)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); /* * Deflects an incoming or waiting call to a given number. This * usually corresponds to +CTFR */ void (*deflect)(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, ofono_voicecall_cb_t cb, void *data); /* * This is equivalent to +CHLD=2 but does not affect a possible * waiting call. */ void (*swap_without_accept)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); void (*send_tones)(struct ofono_voicecall *vc, const char *tones, ofono_voicecall_cb_t cb, void *data); }; void ofono_voicecall_en_list_notify(struct ofono_voicecall *vc, char **nw_en_list); void ofono_voicecall_notify(struct ofono_voicecall *vc, const struct ofono_call *call); void ofono_voicecall_disconnected(struct ofono_voicecall *vc, int id, enum ofono_disconnect_reason reason, const struct ofono_error *error); /* * For those protocols where MPTY creation happens outside of oFono's control, * e.g. Bluetooth Handsfree, set the hint of the MPTY call list by passing * in a bitmask of ids participating in the MPTY call */ void ofono_voicecall_mpty_hint(struct ofono_voicecall *vc, unsigned int ids); int ofono_voicecall_driver_register(const struct ofono_voicecall_driver *d); void ofono_voicecall_driver_unregister(const struct ofono_voicecall_driver *d); struct ofono_voicecall *ofono_voicecall_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_voicecall_register(struct ofono_voicecall *vc); void ofono_voicecall_remove(struct ofono_voicecall *vc); void ofono_voicecall_set_data(struct ofono_voicecall *vc, void *data); void *ofono_voicecall_get_data(struct ofono_voicecall *vc); int ofono_voicecall_get_next_callid(struct ofono_voicecall *vc); void ofono_voicecall_ssn_mo_notify(struct ofono_voicecall *vc, unsigned int id, int code, int index); void ofono_voicecall_ssn_mt_notify(struct ofono_voicecall *vc, unsigned int id, int code, int index, const struct ofono_phone_number *ph); #ifdef __cplusplus } #endif #endif /* __OFONO_VOICECALL_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/netreg.h0000644000015600001650000001020212671500024021452 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_NETREG_H #define __OFONO_NETREG_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_netreg; /* Theoretical limit is 16, but each GSM char can be encoded into * * 3 UTF8 characters resulting in 16*3=48 chars * */ #define OFONO_MAX_OPERATOR_NAME_LENGTH 63 struct ofono_network_operator { char name[OFONO_MAX_OPERATOR_NAME_LENGTH + 1]; char mcc[OFONO_MAX_MCC_LENGTH + 1]; char mnc[OFONO_MAX_MNC_LENGTH + 1]; int status; int tech; }; typedef void (*ofono_netreg_operator_cb_t)(const struct ofono_error *error, const struct ofono_network_operator *op, void *data); typedef void (*ofono_netreg_register_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_netreg_operator_list_cb_t)(const struct ofono_error *error, int total, const struct ofono_network_operator *list, void *data); typedef void (*ofono_netreg_status_cb_t)(const struct ofono_error *error, int status, int lac, int ci, int tech, void *data); typedef void (*ofono_netreg_strength_cb_t)(const struct ofono_error *error, int strength, void *data); /* Network related functions, including registration status, operator selection * and signal strength indicators. * * It is up to the plugin to implement CSQ polling if the modem does not support * vendor extensions for signal strength notification. */ struct ofono_netreg_driver { const char *name; int (*probe)(struct ofono_netreg *netreg, unsigned int vendor, void *data); void (*remove)(struct ofono_netreg *netreg); void (*registration_status)(struct ofono_netreg *netreg, ofono_netreg_status_cb_t cb, void *data); void (*current_operator)(struct ofono_netreg *netreg, ofono_netreg_operator_cb_t cb, void *data); void (*list_operators)(struct ofono_netreg *netreg, ofono_netreg_operator_list_cb_t cb, void *data); void (*register_auto)(struct ofono_netreg *netreg, ofono_netreg_register_cb_t cb, void *data); void (*register_manual)(struct ofono_netreg *netreg, const char *mcc, const char *mnc, ofono_netreg_register_cb_t cb, void *data); void (*strength)(struct ofono_netreg *netreg, ofono_netreg_strength_cb_t, void *data); }; void ofono_netreg_strength_notify(struct ofono_netreg *netreg, int strength); void ofono_netreg_status_notify(struct ofono_netreg *netreg, int status, int lac, int ci, int tech); void ofono_netreg_time_notify(struct ofono_netreg *netreg, struct ofono_network_time *info); int ofono_netreg_driver_register(const struct ofono_netreg_driver *d); void ofono_netreg_driver_unregister(const struct ofono_netreg_driver *d); struct ofono_netreg *ofono_netreg_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_netreg_register(struct ofono_netreg *netreg); void ofono_netreg_remove(struct ofono_netreg *netreg); void ofono_netreg_set_data(struct ofono_netreg *netreg, void *data); void *ofono_netreg_get_data(struct ofono_netreg *netreg); int ofono_netreg_get_location(struct ofono_netreg *netreg); int ofono_netreg_get_cellid(struct ofono_netreg *netreg); int ofono_netreg_get_status(struct ofono_netreg *netreg); int ofono_netreg_get_technology(struct ofono_netreg *netreg); const char *ofono_netreg_get_mcc(struct ofono_netreg *netreg); const char *ofono_netreg_get_mnc(struct ofono_netreg *netreg); #ifdef __cplusplus } #endif #endif /* __OFONO_NETREG_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/ctm.h0000644000015600001650000000377112671500024020766 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 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 * */ #ifndef __OFONO_CTM_H #define __OFONO_CTM_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_ctm; typedef void (*ofono_ctm_set_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_ctm_query_cb_t)(const struct ofono_error *error, ofono_bool_t enable, void *data); struct ofono_ctm_driver { const char *name; int (*probe)(struct ofono_ctm *ctm, unsigned int vendor, void *data); void (*remove)(struct ofono_ctm *ctm); void (*query_tty)(struct ofono_ctm *ctm, ofono_ctm_query_cb_t cb, void *data); void (*set_tty)(struct ofono_ctm *ctm, ofono_bool_t enable, ofono_ctm_set_cb_t cb, void *data); }; int ofono_ctm_driver_register(const struct ofono_ctm_driver *d); void ofono_ctm_driver_unregister(const struct ofono_ctm_driver *d); struct ofono_ctm *ofono_ctm_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_ctm_register(struct ofono_ctm *ctm); void ofono_ctm_remove(struct ofono_ctm *ctm); void ofono_ctm_set_data(struct ofono_ctm *ctm, void *data); void *ofono_ctm_get_data(struct ofono_ctm *ctm); #ifdef __cplusplus } #endif #endif /* __OFONO_CTM_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/handsfree-audio.h0000644000015600001650000000564412671500024023242 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 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 * */ #ifndef __OFONO_HANDSFREE_AUDIO_H #define __OFONO_HANDSFREE_AUDIO_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_handsfree_card; enum ofono_handsfree_card_type { OFONO_HANDSFREE_CARD_TYPE_HANDSFREE, OFONO_HANDSFREE_CARD_TYPE_GATEWAY, }; typedef void (*ofono_handsfree_card_connect_cb_t)( const struct ofono_error *error, void *data); struct ofono_handsfree_card_driver { const char *name; int (*probe)(struct ofono_handsfree_card *card, unsigned int vendor, void *data); void (*remove)(struct ofono_handsfree_card *card); void (*connect)(struct ofono_handsfree_card *card, ofono_handsfree_card_connect_cb_t cb, void *data); void (*sco_connected_hint)(struct ofono_handsfree_card *card); }; struct ofono_handsfree_card *ofono_handsfree_card_create(unsigned int vendor, enum ofono_handsfree_card_type type, const char *driver, void *data); int ofono_handsfree_card_register(struct ofono_handsfree_card *card); void ofono_handsfree_card_remove(struct ofono_handsfree_card *card); ofono_bool_t ofono_handsfree_card_set_codec(struct ofono_handsfree_card *card, unsigned char codec); ofono_bool_t ofono_handsfree_audio_has_wideband(void); ofono_bool_t ofono_handsfree_audio_has_transparent_sco(void); void ofono_handsfree_card_set_data(struct ofono_handsfree_card *card, void *data); void *ofono_handsfree_card_get_data(struct ofono_handsfree_card *card); void ofono_handsfree_card_set_remote(struct ofono_handsfree_card *card, const char *remote); const char *ofono_handsfree_card_get_remote(struct ofono_handsfree_card *card); void ofono_handsfree_card_set_local(struct ofono_handsfree_card *card, const char *local); const char *ofono_handsfree_card_get_local(struct ofono_handsfree_card *card); int ofono_handsfree_card_connect_sco(struct ofono_handsfree_card *card); void ofono_handsfree_audio_ref(void); void ofono_handsfree_audio_unref(void); int ofono_handsfree_card_driver_register( const struct ofono_handsfree_card_driver *d); void ofono_handsfree_card_driver_unregister( const struct ofono_handsfree_card_driver *d); #ifdef __cplusplus } #endif #endif /* __OFONO_HANDSFREE_AUDIO_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/gprs-context.h0000644000015600001650000001021312671500024022625 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_GPRS_CONTEXT_H #define __OFONO_GPRS_CONTEXT_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_gprs_context; #define OFONO_GPRS_MAX_APN_LENGTH 127 #define OFONO_GPRS_MAX_USERNAME_LENGTH 63 #define OFONO_GPRS_MAX_PASSWORD_LENGTH 255 enum ofono_gprs_proto { OFONO_GPRS_PROTO_IP = 0, OFONO_GPRS_PROTO_IPV6, OFONO_GPRS_PROTO_IPV4V6, }; enum ofono_gprs_context_type { OFONO_GPRS_CONTEXT_TYPE_ANY = 0, OFONO_GPRS_CONTEXT_TYPE_INTERNET, OFONO_GPRS_CONTEXT_TYPE_MMS, OFONO_GPRS_CONTEXT_TYPE_WAP, OFONO_GPRS_CONTEXT_TYPE_IMS, OFONO_GPRS_CONTEXT_TYPE_IA, }; enum ofono_gprs_auth_method { OFONO_GPRS_AUTH_METHOD_CHAP = 0, OFONO_GPRS_AUTH_METHOD_PAP, }; struct ofono_gprs_primary_context { unsigned int cid; int direction; char apn[OFONO_GPRS_MAX_APN_LENGTH + 1]; char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1]; char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1]; enum ofono_gprs_proto proto; enum ofono_gprs_auth_method auth_method; }; typedef void (*ofono_gprs_context_cb_t)(const struct ofono_error *error, void *data); struct ofono_gprs_context_driver { const char *name; int (*probe)(struct ofono_gprs_context *gc, unsigned int vendor, void *data); void (*remove)(struct ofono_gprs_context *gc); void (*activate_primary)(struct ofono_gprs_context *gc, const struct ofono_gprs_primary_context *ctx, ofono_gprs_context_cb_t cb, void *data); void (*deactivate_primary)(struct ofono_gprs_context *gc, unsigned int id, ofono_gprs_context_cb_t cb, void *data); void (*detach_shutdown)(struct ofono_gprs_context *gc, unsigned int id); }; void ofono_gprs_context_deactivated(struct ofono_gprs_context *gc, unsigned int id); int ofono_gprs_context_driver_register( const struct ofono_gprs_context_driver *d); void ofono_gprs_context_driver_unregister( const struct ofono_gprs_context_driver *d); struct ofono_gprs_context *ofono_gprs_context_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_gprs_context_remove(struct ofono_gprs_context *gc); void ofono_gprs_context_set_data(struct ofono_gprs_context *gc, void *data); void *ofono_gprs_context_get_data(struct ofono_gprs_context *gc); struct ofono_modem *ofono_gprs_context_get_modem(struct ofono_gprs_context *gc); void ofono_gprs_context_set_type(struct ofono_gprs_context *gc, enum ofono_gprs_context_type type); void ofono_gprs_context_set_interface(struct ofono_gprs_context *gc, const char *interface); void ofono_gprs_context_set_ipv4_address(struct ofono_gprs_context *gc, const char *address, ofono_bool_t static_ip); void ofono_gprs_context_set_ipv4_netmask(struct ofono_gprs_context *gc, const char *netmask); void ofono_gprs_context_set_ipv4_gateway(struct ofono_gprs_context *gc, const char *gateway); void ofono_gprs_context_set_ipv4_dns_servers(struct ofono_gprs_context *gc, const char **dns); void ofono_gprs_context_set_ipv6_address(struct ofono_gprs_context *gc, const char *address); void ofono_gprs_context_set_ipv6_prefix_length(struct ofono_gprs_context *gc, unsigned char length); void ofono_gprs_context_set_ipv6_gateway(struct ofono_gprs_context *gc, const char *gateway); void ofono_gprs_context_set_ipv6_dns_servers(struct ofono_gprs_context *gc, const char **dns); #ifdef __cplusplus } #endif #endif /* __OFONO_GPRS_CONTEXT_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/ussd.h0000644000015600001650000000441712671500024021157 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_USSD_H #define __OFONO_USSD_H #ifdef __cplusplus extern "C" { #endif #include /* 3GPP TS 27.007 section 7.15, values for */ enum ofono_ussd_status { OFONO_USSD_STATUS_NOTIFY = 0, OFONO_USSD_STATUS_ACTION_REQUIRED = 1, OFONO_USSD_STATUS_TERMINATED = 2, OFONO_USSD_STATUS_LOCAL_CLIENT_RESPONDED = 3, OFONO_USSD_STATUS_NOT_SUPPORTED = 4, OFONO_USSD_STATUS_TIMED_OUT = 5, }; struct ofono_ussd; typedef void (*ofono_ussd_cb_t)(const struct ofono_error *error, void *data); struct ofono_ussd_driver { const char *name; int (*probe)(struct ofono_ussd *ussd, unsigned int vendor, void *data); void (*remove)(struct ofono_ussd *ussd); void (*request)(struct ofono_ussd *ussd, int dcs, const unsigned char *pdu, int len, ofono_ussd_cb_t, void *data); void (*cancel)(struct ofono_ussd *ussd, ofono_ussd_cb_t cb, void *data); }; void ofono_ussd_notify(struct ofono_ussd *ussd, int status, int dcs, const unsigned char *data, int data_len); int ofono_ussd_driver_register(const struct ofono_ussd_driver *d); void ofono_ussd_driver_unregister(const struct ofono_ussd_driver *d); struct ofono_ussd *ofono_ussd_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_ussd_register(struct ofono_ussd *ussd); void ofono_ussd_remove(struct ofono_ussd *ussd); void ofono_ussd_set_data(struct ofono_ussd *ussd, void *data); void *ofono_ussd_get_data(struct ofono_ussd *ussd); #ifdef __cplusplus } #endif #endif /* __OFONO_USSD_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/log.h0000644000015600001650000000421612671500073020763 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * 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 * */ #ifndef __OFONO_LOG_H #define __OFONO_LOG_H #ifdef __cplusplus extern "C" { #endif /** * SECTION:log * @title: Logging premitives * @short_description: Functions for logging error and debug information */ extern void ofono_info(const char *format, ...) __attribute__((format(printf, 1, 2))); extern void ofono_warn(const char *format, ...) __attribute__((format(printf, 1, 2))); extern void ofono_error(const char *format, ...) __attribute__((format(printf, 1, 2))); extern void ofono_debug(const char *format, ...) __attribute__((format(printf, 1, 2))); struct ofono_debug_desc { const char *name; const char *file; #define OFONO_DEBUG_FLAG_DEFAULT (0) #define OFONO_DEBUG_FLAG_PRINT (1 << 0) unsigned int flags; } __attribute__((aligned(8))); /** * DBG: * @fmt: format string * @arg...: list of arguments * * Simple macro around ofono_debug() which also include the function * name it is called in. */ #define DBG(fmt, arg...) do { \ static struct ofono_debug_desc __ofono_debug_desc \ __attribute__((used, section("__debug"), aligned(8))) = { \ .file = __FILE__, .flags = OFONO_DEBUG_FLAG_DEFAULT, \ }; \ if (__ofono_debug_desc.flags & OFONO_DEBUG_FLAG_PRINT) \ ofono_debug("%s:%s() " fmt, \ __FILE__, __FUNCTION__ , ## arg); \ } while (0) #define PRINTABLE_STR(s) ((s) ? (s) : "(null)") #ifdef __cplusplus } #endif #endif /* __OFONO_LOG_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/system-settings.h0000644000015600001650000000267212671500073023370 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * Copyright (C) 2016 Canonical 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. * * 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 * */ #ifndef OFONO_SYSTEM_SETTINGS_H #define OFONO_SYSTEM_SETTINGS_H #ifdef __cplusplus extern "C" { #endif /* Settings names */ #define PREFERRED_VOICE_MODEM "PreferredVoiceModem" struct ofono_system_settings_driver { const char *name; /* The user must free the returned string */ char *(*get_string_value)(const char *name); }; /* The user must free the returned string */ char *__ofono_system_settings_get_string_value(const char *name); int ofono_system_settings_driver_register( struct ofono_system_settings_driver *driver); void ofono_system_settings_driver_unregister( const struct ofono_system_settings_driver *driver); #ifdef __cplusplus } #endif #endif /* OFONO_SYSTEM_SETTINGS_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/modem.h0000644000015600001650000001050512671500024021275 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_MODEM_H #define __OFONO_MODEM_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_modem; enum ofono_modem_type { OFONO_MODEM_TYPE_HARDWARE = 0, OFONO_MODEM_TYPE_HFP, OFONO_MODEM_TYPE_SAP, OFONO_MODEM_TYPE_TEST, }; typedef void (*ofono_modem_online_cb_t)(const struct ofono_error *error, void *data); typedef ofono_bool_t (*ofono_modem_compare_cb_t)(struct ofono_modem *modem, void *user_data); struct ofono_modem_driver { const char *name; enum ofono_modem_type modem_type; /* Detect existence of device and initialize any device-specific data * structures */ int (*probe)(struct ofono_modem *modem); /* Destroy data structures allocated during probe and cleanup */ void (*remove)(struct ofono_modem *modem); /* Power up device */ int (*enable)(struct ofono_modem *modem); /* Power down device */ int (*disable)(struct ofono_modem *modem); /* Enable or disable cellular radio */ void (*set_online)(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t callback, void *data); /* Populate the atoms available without SIM / Locked SIM */ void (*pre_sim)(struct ofono_modem *modem); /* Populate the atoms that are available with SIM / Unlocked SIM*/ void (*post_sim)(struct ofono_modem *modem); /* Populate the atoms available online */ void (*post_online)(struct ofono_modem *modem); /* Is it a multi-SIM standby modem? */ ofono_bool_t (*is_standby)(struct ofono_modem *modem); }; void ofono_modem_add_interface(struct ofono_modem *modem, const char *interface); void ofono_modem_remove_interface(struct ofono_modem *modem, const char *interface); const char *ofono_modem_get_path(struct ofono_modem *modem); void ofono_modem_set_data(struct ofono_modem *modem, void *data); void *ofono_modem_get_data(struct ofono_modem *modem); struct ofono_modem *ofono_modem_create(const char *name, const char *type); int ofono_modem_register(struct ofono_modem *modem); ofono_bool_t ofono_modem_is_registered(struct ofono_modem *modem); void ofono_modem_remove(struct ofono_modem *modem); void ofono_modem_reset(struct ofono_modem *modem); void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered); ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem); ofono_bool_t ofono_modem_get_online(struct ofono_modem *modem); ofono_bool_t ofono_modem_get_emergency_mode(struct ofono_modem *modem); void ofono_modem_set_name(struct ofono_modem *modem, const char *name); void ofono_modem_set_driver(struct ofono_modem *modem, const char *type); int ofono_modem_set_string(struct ofono_modem *modem, const char *key, const char *value); const char *ofono_modem_get_string(struct ofono_modem *modem, const char *key); int ofono_modem_set_integer(struct ofono_modem *modem, const char *key, int value); int ofono_modem_get_integer(struct ofono_modem *modem, const char *key); int ofono_modem_set_boolean(struct ofono_modem *modem, const char *key, ofono_bool_t value); ofono_bool_t ofono_modem_get_boolean(struct ofono_modem *modem, const char *key); void ofono_modem_set_driver_watches_sim(struct ofono_modem *modem, ofono_bool_t value); ofono_bool_t ofono_modem_get_driver_watches_sim(struct ofono_modem *modem); ofono_bool_t ofono_modem_is_standby(struct ofono_modem *modem); int ofono_modem_driver_register(const struct ofono_modem_driver *); void ofono_modem_driver_unregister(const struct ofono_modem_driver *); struct ofono_modem *ofono_modem_find(ofono_modem_compare_cb_t func, void *user_data); #ifdef __cplusplus } #endif #endif /* __OFONO_MODEM_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/nettime.h0000644000015600001650000000274712671500024021652 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __OFONO_NETTIME_H #define __OFONO_NETTIME_H #ifdef __cplusplus extern "C" { #endif struct ofono_network_time; struct ofono_nettime_context { struct ofono_nettime_driver *driver; struct ofono_modem *modem; void *data; }; struct ofono_nettime_driver { const char *name; int (*probe)(struct ofono_nettime_context *context); void (*remove)(struct ofono_nettime_context *context); void (*info_received)(struct ofono_nettime_context *context, struct ofono_network_time *info); }; int ofono_nettime_driver_register(const struct ofono_nettime_driver *driver); void ofono_nettime_driver_unregister(const struct ofono_nettime_driver *driver); #ifdef __cplusplus } #endif #endif /* __OFONO_NETTIME_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/cdma-provision.h0000644000015600001650000000240212671500024023123 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_CDMA_PROVISION_H #define __OFONO_CDMA_PROVISION_H #ifdef __cplusplus extern "C" { #endif struct ofono_cdma_provision_driver { const char *name; int priority; int (*get_provider_name)(const char *sid, char **name); }; int ofono_cdma_provision_driver_register( const struct ofono_cdma_provision_driver *driver); void ofono_cdma_provision_driver_unregister( const struct ofono_cdma_provision_driver *driver); #ifdef __cplusplus } #endif #endif /* __OFONO_CDMA_PROVISION_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/sms.h0000644000015600001650000000550012671500024020775 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_SMS_H #define __OFONO_SMS_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_sms; typedef void (*ofono_sms_sca_query_cb_t)(const struct ofono_error *error, const struct ofono_phone_number *ph, void *data); typedef void (*ofono_sms_submit_cb_t)(const struct ofono_error *error, int mr, void *data); typedef void (*ofono_sms_sca_set_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_sms_bearer_set_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_sms_bearer_query_cb_t)(const struct ofono_error *error, int bearer, void *data); struct ofono_sms_driver { const char *name; int (*probe)(struct ofono_sms *sms, unsigned int vendor, void *data); void (*remove)(struct ofono_sms *sms); void (*sca_query)(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb, void *data); void (*sca_set)(struct ofono_sms *sms, const struct ofono_phone_number *sca, ofono_sms_sca_set_cb_t cb, void *data); void (*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); void (*bearer_query)(struct ofono_sms *sms, ofono_sms_bearer_query_cb_t, void *data); void (*bearer_set)(struct ofono_sms *sms, int bearer, ofono_sms_bearer_set_cb_t, void *data); }; void ofono_sms_deliver_notify(struct ofono_sms *sms, const unsigned char *pdu, int len, int tpdu_len); void ofono_sms_status_notify(struct ofono_sms *sms, const unsigned char *pdu, int len, int tpdu_len); int ofono_sms_driver_register(const struct ofono_sms_driver *d); void ofono_sms_driver_unregister(const struct ofono_sms_driver *d); struct ofono_sms *ofono_sms_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_sms_register(struct ofono_sms *sms); void ofono_sms_remove(struct ofono_sms *sms); void ofono_sms_set_data(struct ofono_sms *sms, void *data); void *ofono_sms_get_data(struct ofono_sms *sms); #ifdef __cplusplus } #endif #endif /* __OFONO_SMS_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/dns-client.h0000644000015600001650000001010512671500024022230 0ustar pbuserpbgroup00000000000000/* * * DNS Client - provides an asynchronous DNS resolution client. * * This file originally created by Google, Inc. * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. * Copyright (c) 2015 Canonical 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. * * 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 * */ #ifndef OFONO_DNS_CLIENT_H #define OFONO_DNS_CLIENT_H struct sockaddr; /** * SECTION:dns_client * @title: DNS client interface * @short_description: Functions for interacting with asynchronous DNS client */ /* * Enumeration of possible DNS request completion status codes. * These are intentionally consistent with shill error codes. */ typedef enum { OFONO_DNS_CLIENT_SUCCESS = 0, OFONO_DNS_CLIENT_ERROR_NO_DATA, OFONO_DNS_CLIENT_ERROR_FORM_ERR, OFONO_DNS_CLIENT_ERROR_SERVER_FAIL, OFONO_DNS_CLIENT_ERROR_NOT_FOUND, OFONO_DNS_CLIENT_ERROR_NOT_IMP, OFONO_DNS_CLIENT_ERROR_REFUSED, OFONO_DNS_CLIENT_ERROR_BAD_QUERY, OFONO_DNS_CLIENT_ERROR_NET_REFUSED, OFONO_DNS_CLIENT_ERROR_TIMED_OUT, OFONO_DNS_CLIENT_ERROR_UNKNOWN, } ofono_dns_client_status_t; /* * Callbacks of this type are provided by a client when initiating a DNS request * and invoked by the dns_client module when a request has completed. * |data| is user data supplied by the client. * |status| is the completion status of the request. * |ip_addr| is a struct sockaddr containing an IPv4 or IPv6 address if the * request succeeded and NULL otherwise. The callee should copy |ip_addr| if it * wishes to retain it beyond the lifetime of this callback. */ typedef void (*ofono_dns_client_callback_t)(void *data, ofono_dns_client_status_t status, struct sockaddr *ip_addr); /* * Opaque handle uniquely indentifying an in-progress request. */ typedef void *ofono_dns_client_request_t; struct ofono_dns_client_driver { const char *name; ofono_dns_client_request_t (*submit_request)(const char *hostname, const char *device, const char **servers, int timeout_ms, ofono_dns_client_callback_t cb, void *data); gboolean (*cancel_request)(ofono_dns_client_request_t request); }; /* * Initiate an asynchronous name resolution request for |hostname|. * |device| specifies the interface out which the request should be sent, or * NULL to use the default interface. * |servers| string array with name servers to query or NULL to use system * defaults * |timeout_ms| specifies a max timeout in milliseconds, or 0 to use the default * timeout (which depends on the plugin). * Returns an opaque handle identifying the request on success and NULL on * failure. Success indicates only that the request was initiated, not that the * request itself was successful. * When the request is complete, the callback |cb| will be invoked with |data| * passed as an arg. */ ofono_dns_client_request_t __ofono_dns_client_submit_request( const char *hostname, const char *device, const char **servers, int timeout_ms, ofono_dns_client_callback_t cb, void *data); /* * Cancel the request identified by |request|. Does not invoke the callback for * the cancelled request. */ void __ofono_dns_client_cancel_request(ofono_dns_client_request_t request); /* * Returns a human-friendly error string corresponding to |status|. */ const char *__ofono_dns_client_strerror(ofono_dns_client_status_t status); int ofono_dns_client_driver_register(struct ofono_dns_client_driver *driver); void ofono_dns_client_driver_unregister( const struct ofono_dns_client_driver *driver); #endif /* OFONO_DNS_CLIENT_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/call-settings.h0000644000015600001650000000607212671500024022751 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * 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 * */ #ifndef __OFONO_CALL_SETTINGS_H #define __OFONO_CALL_SETTINGS_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_call_settings; typedef void (*ofono_call_settings_status_cb_t)(const struct ofono_error *error, int status, void *data); typedef void (*ofono_call_settings_set_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_call_settings_clir_cb_t)(const struct ofono_error *error, int override, int network, void *data); struct ofono_call_settings_driver { const char *name; int (*probe)(struct ofono_call_settings *cs, unsigned int vendor, void *data); void (*remove)(struct ofono_call_settings *cs); void (*clip_query)(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data); void (*cnap_query)(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data); void (*cdip_query)(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data); void (*colp_query)(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data); void (*clir_query)(struct ofono_call_settings *cs, ofono_call_settings_clir_cb_t cb, void *data); void (*colr_query)(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data); void (*clir_set)(struct ofono_call_settings *cs, int mode, ofono_call_settings_set_cb_t cb, void *data); void (*cw_query)(struct ofono_call_settings *cs, int cls, ofono_call_settings_status_cb_t cb, void *data); void (*cw_set)(struct ofono_call_settings *cs, int mode, int cls, ofono_call_settings_set_cb_t cb, void *data); }; int ofono_call_settings_driver_register( const struct ofono_call_settings_driver *d); void ofono_call_settings_driver_unregister( const struct ofono_call_settings_driver *d); struct ofono_call_settings *ofono_call_settings_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_call_settings_register(struct ofono_call_settings *cs); void ofono_call_settings_remove(struct ofono_call_settings *cs); void ofono_call_settings_set_data(struct ofono_call_settings *cs, void *data); void *ofono_call_settings_get_data(struct ofono_call_settings *cs); #ifdef __cplusplus } #endif #endif /* __OFONO_CALL_SETTINGS_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/devinfo.h0000644000015600001650000000424412671500024021631 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * 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 * */ #ifndef __OFONO_DEVINFO_H #define __OFONO_DEVINFO_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_devinfo; typedef void (*ofono_devinfo_query_cb_t)(const struct ofono_error *error, const char *attribute, void *data); struct ofono_devinfo_driver { const char *name; int (*probe)(struct ofono_devinfo *info, unsigned int vendor, void *data); void (*remove)(struct ofono_devinfo *info); void (*query_manufacturer)(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data); void (*query_serial)(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data); void (*query_model)(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data); void (*query_revision)(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data); }; int ofono_devinfo_driver_register(const struct ofono_devinfo_driver *d); void ofono_devinfo_driver_unregister(const struct ofono_devinfo_driver *d); struct ofono_devinfo *ofono_devinfo_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_devinfo_register(struct ofono_devinfo *info); void ofono_devinfo_remove(struct ofono_devinfo *info); void ofono_devinfo_set_data(struct ofono_devinfo *info, void *data); void *ofono_devinfo_get_data(struct ofono_devinfo *info); #ifdef __cplusplus } #endif #endif /* __OFONO_MODEM_INFO_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/version.h.in0000644000015600001650000000172312671500024022270 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * 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 * */ #ifndef __OFONO_VERSION_H #define __OFONO_VERSION_H #ifdef __cplusplus extern "C" { #endif #define OFONO_VERSION "@VERSION@" #ifdef __cplusplus } #endif #endif /* __OFONO_VERSION_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/call-volume.h0000644000015600001650000000471312671500024022420 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_CALL_VOLUME_H #define __OFONO_CALL_VOLUME_H #ifdef __cplusplus extern "C" { #endif #include #include struct ofono_call_volume; typedef void (*ofono_call_volume_cb_t)(const struct ofono_error *error, void *data); struct ofono_call_volume_driver { const char *name; int (*probe)(struct ofono_call_volume *cv, unsigned int vendor, void *data); void (*remove)(struct ofono_call_volume *cv); void (*speaker_volume)(struct ofono_call_volume *cv, unsigned char percent, ofono_call_volume_cb_t cb, void *data); void (*microphone_volume)(struct ofono_call_volume *cv, unsigned char percent, ofono_call_volume_cb_t cb, void *data); void (*mute)(struct ofono_call_volume *cv, int muted, ofono_call_volume_cb_t cb, void *data); }; void ofono_call_volume_set_speaker_volume(struct ofono_call_volume *cv, unsigned char percent); void ofono_call_volume_set_microphone_volume(struct ofono_call_volume *cv, unsigned char percent); void ofono_call_volume_set_muted(struct ofono_call_volume *cv, int muted); int ofono_call_volume_driver_register(const struct ofono_call_volume_driver *d); void ofono_call_volume_driver_unregister( const struct ofono_call_volume_driver *d); struct ofono_call_volume *ofono_call_volume_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_call_volume_register(struct ofono_call_volume *cv); void ofono_call_volume_remove(struct ofono_call_volume *cv); void ofono_call_volume_set_data(struct ofono_call_volume *cv, void *data); void *ofono_call_volume_get_data(struct ofono_call_volume *cv); #ifdef __cplusplus } #endif #endif /* __OFONO_CALL_VOLUME_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/sim-auth.h0000644000015600001650000000361012671500024021722 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_SIM_AUTH_H #define __OFONO_SIM_AUTH_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_sim_auth; typedef void (*ofono_sim_list_apps_cb_t)(const struct ofono_error *error, const unsigned char *dataobj, int len, void *data); struct ofono_sim_auth_driver { const char *name; int (*probe)(struct ofono_sim_auth *sa, unsigned int vendor, void *data); void (*remove)(struct ofono_sim_auth *sa); void (*list_apps)(struct ofono_sim_auth *sa, ofono_sim_list_apps_cb_t cb, void *data); }; int ofono_sim_auth_driver_register(const struct ofono_sim_auth_driver *d); void ofono_sim_auth_driver_unregister(const struct ofono_sim_auth_driver *d); struct ofono_sim_auth *ofono_sim_auth_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_sim_auth_register(struct ofono_sim_auth *sa); void ofono_sim_auth_remove(struct ofono_sim_auth *sa); void ofono_sim_auth_set_data(struct ofono_sim_auth *sa, void *data); void *ofono_sim_auth_get_data(struct ofono_sim_auth *sa); #ifdef __cplusplus } #endif #endif /* __OFONO_SIM_AUTH_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/sim-mnclength.h0000644000015600001650000000240512671500024022741 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2013 Canonical 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. * * 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 * */ #ifndef OFONO_SIM_MNCLENGTH_H #define OFONO_SIM_MNCLENGTH_H #ifdef __cplusplus extern "C" { #endif struct ofono_sim_mnclength_driver { const char *name; int (*get_mnclength)(const char *imsi); }; int ofono_sim_mnclength_driver_register( struct ofono_sim_mnclength_driver *driver); void ofono_sim_mnclength_driver_unregister( const struct ofono_sim_mnclength_driver *driver); #ifdef __cplusplus } #endif #endif /* OFONO_SIM_MNCLENGTH_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/dbus.h0000644000015600001650000001064412671500024021135 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2013 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. * * 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 * */ #ifndef __OFONO_DBUS_H #define __OFONO_DBUS_H #ifdef __cplusplus extern "C" { #endif #include #define OFONO_SERVICE "org.ofono" #define OFONO_MANAGER_INTERFACE "org.ofono.Manager" #define OFONO_MANAGER_PATH "/" #define OFONO_MODEM_INTERFACE "org.ofono.Modem" #define OFONO_CALL_BARRING_INTERFACE "org.ofono.CallBarring" #define OFONO_CALL_FORWARDING_INTERFACE "org.ofono.CallForwarding" #define OFONO_CALL_METER_INTERFACE "org.ofono.CallMeter" #define OFONO_CALL_SETTINGS_INTERFACE "org.ofono.CallSettings" #define OFONO_CALL_VOLUME_INTERFACE OFONO_SERVICE ".CallVolume" #define OFONO_CELL_BROADCAST_INTERFACE "org.ofono.CellBroadcast" #define OFONO_CONNECTION_CONTEXT_INTERFACE "org.ofono.ConnectionContext" #define OFONO_CONNECTION_MANAGER_INTERFACE "org.ofono.ConnectionManager" #define OFONO_MESSAGE_MANAGER_INTERFACE "org.ofono.MessageManager" #define OFONO_MESSAGE_INTERFACE "org.ofono.Message" #define OFONO_MESSAGE_WAITING_INTERFACE "org.ofono.MessageWaiting" #define OFONO_SUPPLEMENTARY_SERVICES_INTERFACE "org.ofono.SupplementaryServices" #define OFONO_NETWORK_REGISTRATION_INTERFACE "org.ofono.NetworkRegistration" #define OFONO_NETWORK_OPERATOR_INTERFACE "org.ofono.NetworkOperator" #define OFONO_PHONEBOOK_INTERFACE "org.ofono.Phonebook" #define OFONO_RADIO_SETTINGS_INTERFACE "org.ofono.RadioSettings" #define OFONO_AUDIO_SETTINGS_INTERFACE "org.ofono.AudioSettings" #define OFONO_TEXT_TELEPHONY_INTERFACE "org.ofono.TextTelephony" #define OFONO_SIM_MANAGER_INTERFACE "org.ofono.SimManager" #define OFONO_VOICECALL_INTERFACE "org.ofono.VoiceCall" #define OFONO_VOICECALL_MANAGER_INTERFACE "org.ofono.VoiceCallManager" #define OFONO_STK_INTERFACE OFONO_SERVICE ".SimToolkit" #define OFONO_SIM_APP_INTERFACE OFONO_SERVICE ".SimToolkitAgent" #define OFONO_LOCATION_REPORTING_INTERFACE OFONO_SERVICE ".LocationReporting" #define OFONO_GNSS_INTERFACE "org.ofono.AssistedSatelliteNavigation" #define OFONO_GNSS_POSR_AGENT_INTERFACE "org.ofono.PositioningRequestAgent" #define OFONO_HANDSFREE_INTERFACE OFONO_SERVICE ".Handsfree" #define OFONO_SIRI_INTERFACE OFONO_SERVICE ".Siri" #define OFONO_NETWORK_TIME_INTERFACE OFONO_SERVICE ".NetworkTime" /* CDMA Interfaces */ #define OFONO_CDMA_VOICECALL_MANAGER_INTERFACE "org.ofono.cdma.VoiceCallManager" #define OFONO_CDMA_MESSAGE_MANAGER_INTERFACE "org.ofono.cdma.MessageManager" #define OFONO_CDMA_CONNECTION_MANAGER_INTERFACE "org.ofono.cdma.ConnectionManager" #define OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE \ "org.ofono.cdma.NetworkRegistration" /* Essentially a{sv} */ #define OFONO_PROPERTIES_ARRAY_SIGNATURE DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \ DBUS_TYPE_STRING_AS_STRING \ DBUS_TYPE_VARIANT_AS_STRING \ DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBusConnection *ofono_dbus_get_connection(void); void ofono_dbus_dict_append(DBusMessageIter *dict, const char *key, int type, const void *value); void ofono_dbus_dict_append_array(DBusMessageIter *dict, const char *key, int type, const void *val); void ofono_dbus_dict_append_dict(DBusMessageIter *dict, const char *key, int type, const void *val); int ofono_dbus_signal_property_changed(DBusConnection *conn, const char *path, const char *interface, const char *name, int type, const void *value); int ofono_dbus_signal_array_property_changed(DBusConnection *conn, const char *path, const char *interface, const char *name, int type, const void *value); int ofono_dbus_signal_dict_property_changed(DBusConnection *conn, const char *path, const char *interface, const char *name, int type, const void *value); #ifdef __cplusplus } #endif #endif /* __OFONO_DBUS_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/types.h0000644000015600001650000000705612671500024021347 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_TYPES_H #define __OFONO_TYPES_H #ifdef __cplusplus extern "C" { #endif #ifndef FALSE #define FALSE (0) #endif #ifndef TRUE #define TRUE (!FALSE) #endif typedef int ofono_bool_t; /* MCC is always three digits. MNC is either two or three digits */ #define OFONO_MAX_MCC_LENGTH 3 #define OFONO_MAX_MNC_LENGTH 3 typedef void (*ofono_destroy_func)(void *data); /* 27.007 Section 6.2 */ enum ofono_clir_option { OFONO_CLIR_OPTION_DEFAULT = 0, OFONO_CLIR_OPTION_INVOCATION, OFONO_CLIR_OPTION_SUPPRESSION, }; enum ofono_error_type { OFONO_ERROR_TYPE_NO_ERROR = 0, OFONO_ERROR_TYPE_CME, OFONO_ERROR_TYPE_CMS, OFONO_ERROR_TYPE_CEER, OFONO_ERROR_TYPE_SIM, OFONO_ERROR_TYPE_FAILURE, }; enum ofono_disconnect_reason { OFONO_DISCONNECT_REASON_UNKNOWN = 0, OFONO_DISCONNECT_REASON_LOCAL_HANGUP, OFONO_DISCONNECT_REASON_REMOTE_HANGUP, OFONO_DISCONNECT_REASON_ERROR, }; struct ofono_error { enum ofono_error_type type; int error; }; #define OFONO_EINVAL(error) do { \ error->type = OFONO_ERROR_TYPE_FAILURE; \ error->error = -EINVAL; \ } while (0) #define OFONO_NO_ERROR(error) do { \ error->type = OFONO_ERROR_TYPE_NO_ERROR; \ error->error = 0; \ } while (0) #define OFONO_MAX_PHONE_NUMBER_LENGTH 80 #define OFONO_MAX_CALLER_NAME_LENGTH 80 /* Number types, 3GPP TS 24.008 subclause 10.5.4.7, octect 3 */ /* Unknown, ISDN numbering plan */ #define OFONO_NUMBER_TYPE_UNKNOWN 129 /* International, ISDN numbering plan */ #define OFONO_NUMBER_TYPE_INTERNATIONAL 145 struct ofono_phone_number { char number[OFONO_MAX_PHONE_NUMBER_LENGTH + 1]; int type; }; /* Length of NUM_FIELDS in 3GPP2 C.S0005-E v2.0 */ #define OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH 256 struct ofono_cdma_phone_number { /* char maps to max size of CHARi (8 bit) in 3GPP2 C.S0005-E v2.0 */ char number[OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH]; }; struct ofono_call { unsigned int id; int type; int direction; int status; struct ofono_phone_number phone_number; struct ofono_phone_number called_number; char name[OFONO_MAX_CALLER_NAME_LENGTH + 1]; int clip_validity; int cnap_validity; }; struct ofono_network_time { int sec; /* Seconds [0..59], -1 if unavailable */ int min; /* Minutes [0..59], -1 if unavailable */ int hour; /* Hours [0..23], -1 if unavailable */ int mday; /* Day of month [1..31], -1 if unavailable */ int mon; /* Month [1..12], -1 if unavailable */ int year; /* Current year, -1 if unavailable */ int dst; /* Current adjustment, in hours */ int utcoff; /* Offset from UTC in seconds */ }; #define OFONO_SHA1_UUID_LEN 20 struct ofono_uuid { unsigned char uuid[OFONO_SHA1_UUID_LEN]; }; const char *ofono_uuid_to_str(const struct ofono_uuid *uuid); void ofono_call_init(struct ofono_call *call); #ifdef __cplusplus } #endif #endif /* __OFONO_TYPES_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/call-forwarding.h0000644000015600001650000000567412671500024023262 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * 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 * */ #ifndef __OFONO_CALL_FORWARDING_H #define __OFONO_CALL_FORWARDING_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_call_forwarding; /* 27.007 Section 7.11 Call Forwarding */ struct ofono_call_forwarding_condition { int status; int cls; struct ofono_phone_number phone_number; int time; }; typedef void (*ofono_call_forwarding_set_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_call_forwarding_query_cb_t)( const struct ofono_error *error, int total, const struct ofono_call_forwarding_condition *list, void *data); struct ofono_call_forwarding_driver { const char *name; int (*probe)(struct ofono_call_forwarding *cf, unsigned int vendor, void *data); void (*remove)(struct ofono_call_forwarding *cf); void (*activation)(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data); void (*registration)(struct ofono_call_forwarding *cf, int type, int cls, const struct ofono_phone_number *number, int time, ofono_call_forwarding_set_cb_t cb, void *data); void (*deactivation)(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data); void (*erasure)(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data); void (*query)(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_query_cb_t cb, void *data); }; int ofono_call_forwarding_driver_register( const struct ofono_call_forwarding_driver *d); void ofono_call_forwarding_driver_unregister( const struct ofono_call_forwarding_driver *d); struct ofono_call_forwarding *ofono_call_forwarding_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_call_forwarding_register(struct ofono_call_forwarding *cf); void ofono_call_forwarding_remove(struct ofono_call_forwarding *cf); void ofono_call_forwarding_set_data(struct ofono_call_forwarding *cf, void *data); void *ofono_call_forwarding_get_data(struct ofono_call_forwarding *cf); #ifdef __cplusplus } #endif #endif /* __OFONO_CALL_FORWARDING_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/handsfree.h0000644000015600001650000000665412671500024022145 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 BMW Car IT GmbH. 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 * */ #ifndef __OFONO_HANDSFREE_H #define __OFONO_HANDSFREE_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_handsfree; typedef void (*ofono_handsfree_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_handsfree_phone_cb_t)(const struct ofono_error *error, const struct ofono_phone_number *number, void *data); typedef void (*ofono_handsfree_cnum_query_cb_t)(const struct ofono_error *error, int total, const struct ofono_phone_number *numbers, void *data); struct ofono_handsfree_driver { const char *name; int (*probe)(struct ofono_handsfree *hf, unsigned int vendor, void *data); void (*remove)(struct ofono_handsfree *hf); void (*cnum_query)(struct ofono_handsfree *hf, ofono_handsfree_cnum_query_cb_t cb, void *data); void (*request_phone_number) (struct ofono_handsfree *hf, ofono_handsfree_phone_cb_t cb, void *data); void (*voice_recognition)(struct ofono_handsfree *hf, ofono_bool_t enabled, ofono_handsfree_cb_t cb, void *data); void (*disable_nrec)(struct ofono_handsfree *hf, ofono_handsfree_cb_t cb, void *data); void (*hf_indicator)(struct ofono_handsfree *hf, unsigned short indicator, unsigned int value, ofono_handsfree_cb_t cb, void *data); }; void ofono_handsfree_set_ag_features(struct ofono_handsfree *hf, unsigned int ag_features); void ofono_handsfree_set_ag_chld_features(struct ofono_handsfree *hf, unsigned int ag_chld_features); void ofono_handsfree_set_inband_ringing(struct ofono_handsfree *hf, ofono_bool_t enabled); void ofono_handsfree_voice_recognition_notify(struct ofono_handsfree *hf, ofono_bool_t enabled); void ofono_handsfree_set_hf_indicators(struct ofono_handsfree *hf, const unsigned short *indicators, unsigned int num); void ofono_handsfree_hf_indicator_active_notify(struct ofono_handsfree *hf, unsigned int indicator, ofono_bool_t active); void ofono_handsfree_battchg_notify(struct ofono_handsfree *hf, unsigned char level); int ofono_handsfree_driver_register(const struct ofono_handsfree_driver *d); void ofono_handsfree_driver_unregister( const struct ofono_handsfree_driver *d); struct ofono_handsfree *ofono_handsfree_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_handsfree_register(struct ofono_handsfree *hf); void ofono_handsfree_remove(struct ofono_handsfree *hf); void ofono_handsfree_set_data(struct ofono_handsfree *hf, void *data); void *ofono_handsfree_get_data(struct ofono_handsfree *hf); #ifdef __cplusplus } #endif #endif /* __OFONO_HANDSFREE_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/emulator.h0000644000015600001650000000757212671500024022036 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 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 * */ #ifndef __OFONO_EMULATOR_H #define __OFONO_EMULATOR_H #ifdef __cplusplus extern "C" { #endif #include #define OFONO_EMULATOR_IND_BATTERY "battchg" #define OFONO_EMULATOR_IND_CALL "call" #define OFONO_EMULATOR_IND_CALLHELD "callheld" #define OFONO_EMULATOR_IND_CALLSETUP "callsetup" #define OFONO_EMULATOR_IND_ROAMING "roam" #define OFONO_EMULATOR_IND_SERVICE "service" #define OFONO_EMULATOR_IND_SIGNAL "signal" #define OFONO_EMULATOR_CALL_INACTIVE 0 #define OFONO_EMULATOR_CALL_ACTIVE 1 #define OFONO_EMULATOR_CALLSETUP_INACTIVE 0 #define OFONO_EMULATOR_CALLSETUP_INCOMING 1 #define OFONO_EMULATOR_CALLSETUP_OUTGOING 2 #define OFONO_EMULATOR_CALLSETUP_ALERTING 3 #define OFONO_EMULATOR_CALLHELD_NONE 0 #define OFONO_EMULATOR_CALLHELD_MULTIPLE 1 #define OFONO_EMULATOR_CALLHELD_ON_HOLD 2 struct ofono_emulator; struct ofono_emulator_request; struct ofono_handsfree_card; struct ofono_atom; enum ofono_emulator_type { OFONO_EMULATOR_TYPE_DUN, OFONO_EMULATOR_TYPE_HFP, }; enum ofono_emulator_request_type { OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY, OFONO_EMULATOR_REQUEST_TYPE_QUERY, OFONO_EMULATOR_REQUEST_TYPE_SUPPORT, OFONO_EMULATOR_REQUEST_TYPE_SET, }; typedef void (*ofono_emulator_request_cb_t)(struct ofono_emulator *em, struct ofono_emulator_request *req, void *data); struct ofono_emulator *ofono_emulator_create(GList *modems, enum ofono_emulator_type type); void ofono_emulator_register(struct ofono_emulator *em, int fd); void ofono_emulator_remove(struct ofono_emulator *em); void ofono_emulator_send_final(struct ofono_emulator *em, const struct ofono_error *final); void ofono_emulator_send_unsolicited(struct ofono_emulator *em, const char *result); void ofono_emulator_send_intermediate(struct ofono_emulator *em, const char *result); void ofono_emulator_send_info(struct ofono_emulator *em, const char *line, ofono_bool_t last); ofono_bool_t ofono_emulator_add_handler(struct ofono_atom *atom, const char *prefix, ofono_emulator_request_cb_t cb, void *data, ofono_destroy_func destroy); ofono_bool_t ofono_emulator_remove_handler(struct ofono_atom *atom, const char *prefix); ofono_bool_t ofono_emulator_request_next_string( struct ofono_emulator_request *req, const char **str); ofono_bool_t ofono_emulator_request_next_number( struct ofono_emulator_request *req, int *number); const char *ofono_emulator_request_get_raw(struct ofono_emulator_request *req); enum ofono_emulator_request_type ofono_emulator_request_get_type( struct ofono_emulator_request *req); void ofono_emulator_set_indicator(struct ofono_atom *atom, const char *name, int value); void ofono_emulator_set_hf_indicator_active(struct ofono_emulator *em, int indicator, ofono_bool_t active); void ofono_emulator_set_handsfree_card(struct ofono_emulator *em, struct ofono_handsfree_card *card); typedef void (*ofono_emulator_codec_negotiation_cb)(int err, void *data); int ofono_emulator_start_codec_negotiation(struct ofono_emulator *em, ofono_emulator_codec_negotiation_cb cb, void *data); #ifdef __cplusplus } #endif #endif /* __OFONO_EMULATOR_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/gprs.h0000644000015600001650000000555312671500024021156 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_GPRS_H #define __OFONO_GPRS_H #ifdef __cplusplus extern "C" { #endif #include #include struct ofono_gprs; struct ofono_gprs_context; typedef void (*ofono_gprs_status_cb_t)(const struct ofono_error *error, int status, void *data); typedef void (*ofono_gprs_cb_t)(const struct ofono_error *error, void *data); struct ofono_gprs_driver { const char *name; int (*probe)(struct ofono_gprs *gprs, unsigned int vendor, void *data); void (*remove)(struct ofono_gprs *gprs); void (*set_attached)(struct ofono_gprs *gprs, int attached, ofono_gprs_cb_t cb, void *data); void (*attached_status)(struct ofono_gprs *gprs, ofono_gprs_status_cb_t cb, void *data); void (*set_ia_apn)(struct ofono_gprs *gprs, const char *apn, enum ofono_gprs_proto proto, const char *user, const char *passwd, const char *mccmnc, ofono_gprs_cb_t cb, void *data); }; enum gprs_suspend_cause { GPRS_SUSPENDED_DETACHED, GPRS_SUSPENDED_SIGNALLING, GPRS_SUSPENDED_CALL, GPRS_SUSPENDED_NO_COVERAGE, GPRS_SUSPENDED_UNKNOWN_CAUSE, }; void ofono_gprs_status_notify(struct ofono_gprs *gprs, int status); void ofono_gprs_detached_notify(struct ofono_gprs *gprs); void ofono_gprs_suspend_notify(struct ofono_gprs *gprs, int cause); void ofono_gprs_resume_notify(struct ofono_gprs *gprs); void ofono_gprs_bearer_notify(struct ofono_gprs *gprs, int bearer); int ofono_gprs_driver_register(const struct ofono_gprs_driver *d); void ofono_gprs_driver_unregister(const struct ofono_gprs_driver *d); struct ofono_gprs *ofono_gprs_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_gprs_register(struct ofono_gprs *gprs); void ofono_gprs_remove(struct ofono_gprs *gprs); void ofono_gprs_set_data(struct ofono_gprs *gprs, void *data); void *ofono_gprs_get_data(struct ofono_gprs *gprs); void ofono_gprs_set_cid_range(struct ofono_gprs *gprs, unsigned int min, unsigned int max); void ofono_gprs_add_context(struct ofono_gprs *gprs, struct ofono_gprs_context *gc); #ifdef __cplusplus } #endif #endif /* __OFONO_GPRS_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/cdma-voicecall.h0000644000015600001650000000561712671500024023047 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __OFONO_CDMA_VOICECALL_H #define __OFONO_CDMA_VOICECALL_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_cdma_voicecall; enum cdma_call_status { CDMA_CALL_STATUS_ACTIVE, CDMA_CALL_STATUS_DIALING, CDMA_CALL_STATUS_ALERTING, CDMA_CALL_STATUS_INCOMING, CDMA_CALL_STATUS_DISCONNECTED }; typedef void (*ofono_cdma_voicecall_cb_t)(const struct ofono_error *error, void *data); /* Voice call related functionality, including AT+CDV, AT+CHV */ struct ofono_cdma_voicecall_driver { const char *name; int (*probe)(struct ofono_cdma_voicecall *vc, unsigned int vendor, void *data); void (*remove)(struct ofono_cdma_voicecall *vc); void (*dial)(struct ofono_cdma_voicecall *vc, const struct ofono_cdma_phone_number *number, ofono_cdma_voicecall_cb_t cb, void *data); /* Hangs up active, dialing, alerting or incoming calls */ void (*hangup)(struct ofono_cdma_voicecall *vc, ofono_cdma_voicecall_cb_t cb, void *data); void (*answer)(struct ofono_cdma_voicecall *vc, ofono_cdma_voicecall_cb_t cb, void *data); void (*send_flash)(struct ofono_cdma_voicecall *vc, const char *string, ofono_cdma_voicecall_cb_t cb, void *data); void (*send_tones)(struct ofono_cdma_voicecall *vc, const char *tones, ofono_cdma_voicecall_cb_t cb, void *data); }; void ofono_cdma_voicecall_disconnected(struct ofono_cdma_voicecall *vc, enum ofono_disconnect_reason reason, const struct ofono_error *error); int ofono_cdma_voicecall_driver_register( const struct ofono_cdma_voicecall_driver *d); void ofono_cdma_voicecall_driver_unregister( const struct ofono_cdma_voicecall_driver *d); struct ofono_cdma_voicecall *ofono_cdma_voicecall_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_cdma_voicecall_register(struct ofono_cdma_voicecall *vc); void ofono_cdma_voicecall_remove(struct ofono_cdma_voicecall *vc); void ofono_cdma_voicecall_set_data(struct ofono_cdma_voicecall *vc, void *data); void *ofono_cdma_voicecall_get_data(struct ofono_cdma_voicecall *vc); #ifdef __cplusplus } #endif #endif /* __OFONO_CDMA_VOICECALL_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/cdma-sms.h0000644000015600001650000000406512671500024021704 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __OFONO_CDMA_SMS_H #define __OFONO_CDMA_SMS_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_cdma_sms; typedef void (*ofono_cdma_sms_submit_cb_t)(const struct ofono_error *error, void *data); struct ofono_cdma_sms_driver { const char *name; int (*probe)(struct ofono_cdma_sms *cdma_sms, unsigned int vendor, void *data); void (*remove)(struct ofono_cdma_sms *cdma_sms); void (*submit)(struct ofono_cdma_sms *cdma_sms, const unsigned char *tpdu, int tpdu_len, ofono_cdma_sms_submit_cb_t cb, void *data); }; void ofono_cdma_sms_deliver_notify(struct ofono_cdma_sms *cdma_sms, const unsigned char *pdu, int tpdu_len); int ofono_cdma_sms_driver_register(const struct ofono_cdma_sms_driver *d); void ofono_cdma_sms_driver_unregister(const struct ofono_cdma_sms_driver *d); struct ofono_cdma_sms *ofono_cdma_sms_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_cdma_sms_register(struct ofono_cdma_sms *cdma_sms); void ofono_cdma_sms_remove(struct ofono_cdma_sms *cdma_sms); void ofono_cdma_sms_set_data(struct ofono_cdma_sms *cdma_sms, void *data); void *ofono_cdma_sms_get_data(struct ofono_cdma_sms *cdma_sms); #ifdef __cplusplus } #endif #endif /* __OFONO_CDMA_SMS_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/call-meter.h0000644000015600001650000000600712671500024022223 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Telephony stack for Linux * * 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 * */ #ifndef __OFONO_CALL_METER_H #define __OFONO_CALL_METER_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_call_meter; typedef void (*ofono_call_meter_query_cb_t)(const struct ofono_error *error, int value, void *data); typedef void (*ofono_call_meter_puct_query_cb_t)( const struct ofono_error *error, const char *currency, double ppu, void *data); typedef void(*ofono_call_meter_set_cb_t)(const struct ofono_error *error, void *data); struct ofono_call_meter_driver { const char *name; int (*probe)(struct ofono_call_meter *cm, unsigned int vendor, void *data); void (*remove)(struct ofono_call_meter *cm); void (*call_meter_query)(struct ofono_call_meter *cm, ofono_call_meter_query_cb_t cb, void *data); void (*acm_query)(struct ofono_call_meter *cm, ofono_call_meter_query_cb_t cb, void *data); void (*acm_reset)(struct ofono_call_meter *cm, const char *sim_pin2, ofono_call_meter_set_cb_t cb, void *data); void (*acm_max_query)(struct ofono_call_meter *cm, ofono_call_meter_query_cb_t cb, void *data); void (*acm_max_set)(struct ofono_call_meter *cm, int new_value, const char *sim_pin2, ofono_call_meter_set_cb_t cb, void *data); void (*puct_query)(struct ofono_call_meter *cm, ofono_call_meter_puct_query_cb_t cb, void *data); void (*puct_set)(struct ofono_call_meter *cm, const char *currency, double ppu, const char *sim_pin2, ofono_call_meter_set_cb_t cb, void *data); }; int ofono_call_meter_driver_register(const struct ofono_call_meter_driver *d); void ofono_call_meter_driver_unregister( const struct ofono_call_meter_driver *d); struct ofono_call_meter *ofono_call_meter_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_call_meter_register(struct ofono_call_meter *cm); void ofono_call_meter_remove(struct ofono_call_meter *cm); void ofono_call_meter_maximum_notify(struct ofono_call_meter *cm); void ofono_call_meter_changed_notify(struct ofono_call_meter *cm, int new_value); void ofono_call_meter_set_data(struct ofono_call_meter *cm, void *data); void *ofono_call_meter_get_data(struct ofono_call_meter *cm); #ifdef __cplusplus } #endif #endif /* __OFONO_CALL_METER_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/gnss.h0000644000015600001650000000407712671500024021155 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 ST-Ericsson AB. * * 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 * */ #ifndef __OFONO_GNSS_H #define __OFONO_GNSS_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_gnss; typedef void (*ofono_gnss_cb_t)(const struct ofono_error *error, void *data); struct ofono_gnss_driver { const char *name; int (*probe)(struct ofono_gnss *gnss, unsigned int vendor, void *data); void (*remove)(struct ofono_gnss *gnss); void (*send_element)(struct ofono_gnss *gnss, const char *xml, ofono_gnss_cb_t cb, void *data); void (*set_position_reporting)(struct ofono_gnss *gnss, ofono_bool_t enable, ofono_gnss_cb_t cb, void *data); }; void ofono_gnss_notify_posr_request(struct ofono_gnss *gnss, const char *xml); void ofono_gnss_notify_posr_reset(struct ofono_gnss *gnss); int ofono_gnss_driver_register(const struct ofono_gnss_driver *d); void ofono_gnss_driver_unregister(const struct ofono_gnss_driver *d); struct ofono_gnss *ofono_gnss_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_gnss_register(struct ofono_gnss *gnss); void ofono_gnss_remove(struct ofono_gnss *gnss); void ofono_gnss_set_data(struct ofono_gnss *gnss, void *data); void *ofono_gnss_get_data(struct ofono_gnss *gnss); #ifdef __cplusplus } #endif #endif /* __OFONO_GNSS_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/cdma-netreg.h0000644000015600001650000000513512671500024022365 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #ifndef __OFONO_CDMA_NETREG_H #define __OFONO_CDMA_NETREG_H #ifdef __cplusplus extern "C" { #endif #include enum cdma_netreg_status { CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED = 0, CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED = 1, CDMA_NETWORK_REGISTRATION_STATUS_ROAMING = 2, }; struct ofono_cdma_netreg; typedef void (*ofono_cdma_netreg_serving_system_cb_t)( const struct ofono_error *error, const char *sid, void *data); struct ofono_cdma_netreg_driver { const char *name; int (*probe)(struct ofono_cdma_netreg *cdma_netreg, unsigned int vendor, void *data); void (*remove)(struct ofono_cdma_netreg *cdma_netreg); void (*serving_system)(struct ofono_cdma_netreg *cdma_netreg, ofono_cdma_netreg_serving_system_cb_t cb, void *data); }; void ofono_cdma_netreg_status_notify(struct ofono_cdma_netreg *netreg, enum cdma_netreg_status status); void ofono_cdma_netreg_strength_notify(struct ofono_cdma_netreg *netreg, int strength); void ofono_cdma_netreg_data_strength_notify(struct ofono_cdma_netreg *netreg, int data_strength); int ofono_cdma_netreg_get_status(struct ofono_cdma_netreg *netreg); int ofono_cdma_netreg_driver_register( const struct ofono_cdma_netreg_driver *d); void ofono_cdma_netreg_driver_unregister( const struct ofono_cdma_netreg_driver *d); struct ofono_cdma_netreg *ofono_cdma_netreg_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_cdma_netreg_register(struct ofono_cdma_netreg *cdma_netreg); void ofono_cdma_netreg_remove(struct ofono_cdma_netreg *cdma_netreg); void ofono_cdma_netreg_set_data(struct ofono_cdma_netreg *cdma_netreg, void *data); void *ofono_cdma_netreg_get_data(struct ofono_cdma_netreg *cdma_netreg); #ifdef __cplusplus } #endif #endif /* __OFONO_CDMA_NETREG_H */ ofono-1.17.bzr6912+16.04.20160314.3/include/cdma-connman.h0000644000015600001650000000521712671500024022533 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __OFONO_CDMA_CONNMAN_H #define __OFONO_CDMA_CONNMAN_H #ifdef __cplusplus extern "C" { #endif #include struct ofono_cdma_connman; #define OFONO_CDMA_CONNMAN_MAX_USERNAME_LENGTH 63 #define OFONO_CDMA_CONNMAN_MAX_PASSWORD_LENGTH 255 typedef void (*ofono_cdma_connman_cb_t)(const struct ofono_error *error, void *data); typedef void (*ofono_cdma_connman_up_cb_t)(const struct ofono_error *error, const char *interface, ofono_bool_t static_ip, const char *address, const char *netmask, const char *gw, const char **dns, void *data); struct ofono_cdma_connman_driver { const char *name; int (*probe)(struct ofono_cdma_connman *cm, unsigned int vendor, void *data); void (*remove)(struct ofono_cdma_connman *cm); void (*activate)(struct ofono_cdma_connman *cm, const char *username, const char *password, ofono_cdma_connman_up_cb_t cb, void *data); void (*deactivate)(struct ofono_cdma_connman *cm, ofono_cdma_connman_cb_t cb, void *data); }; int ofono_cdma_connman_driver_register( const struct ofono_cdma_connman_driver *d); void ofono_cdma_connman_driver_unregister( const struct ofono_cdma_connman_driver *d); void ofono_cdma_connman_deactivated(struct ofono_cdma_connman *cm); void ofono_cdma_connman_dormant_notify(struct ofono_cdma_connman *cm, ofono_bool_t dormant); struct ofono_cdma_connman *ofono_cdma_connman_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data); void ofono_cdma_connman_register(struct ofono_cdma_connman *cm); void ofono_cdma_connman_remove(struct ofono_cdma_connman *cm); void ofono_cdma_connman_set_data(struct ofono_cdma_connman *cm, void *data); void *ofono_cdma_connman_get_data(struct ofono_cdma_connman *cm); #ifdef __cplusplus } #endif #endif /* __OFONO_CDMA_CONNMAN_H */ ofono-1.17.bzr6912+16.04.20160314.3/src/0000755000015600001650000000000012671500304017167 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/src/simfs.h0000644000015600001650000000525612671500024020470 0ustar pbuserpbgroup00000000000000/* * * 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 * */ struct sim_fs; struct sim_fs *sim_fs_new(struct ofono_sim *sim, const struct ofono_sim_driver *driver); struct ofono_sim_context *sim_fs_context_new(struct sim_fs *fs); unsigned int sim_fs_file_watch_add(struct ofono_sim_context *context, int id, ofono_sim_file_changed_cb_t cb, void *userdata, ofono_destroy_func destroy); void sim_fs_file_watch_remove(struct ofono_sim_context *context, unsigned int id); /* Id of -1 notifies all watches, serving as a wildcard */ void sim_fs_notify_file_watches(struct sim_fs *fs, int id); int sim_fs_read(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, unsigned short offset, unsigned short num_bytes, const unsigned char *path, unsigned int len, ofono_sim_file_read_cb_t cb, void *data); int sim_fs_read_record(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, int record, int record_length, const unsigned char *path, unsigned int len, ofono_sim_file_read_cb_t cb, void *data); int sim_fs_read_info(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, const unsigned char *path, unsigned int pth_len, ofono_sim_read_info_cb_t cb, void *data); void sim_fs_check_version(struct sim_fs *fs); int sim_fs_write(struct ofono_sim_context *context, int id, ofono_sim_file_write_cb_t cb, enum ofono_sim_file_structure structure, int record, const unsigned char *data, int length, void *userdata); char *sim_fs_get_cached_image(struct sim_fs *fs, int id); void sim_fs_cache_image(struct sim_fs *fs, const char *image, int id); void sim_fs_cache_flush(struct sim_fs *fs); void sim_fs_cache_flush_file(struct sim_fs *fs, int id); void sim_fs_image_cache_flush(struct sim_fs *fs); void sim_fs_image_cache_flush_file(struct sim_fs *fs, int id); void sim_fs_free(struct sim_fs *fs); void sim_fs_context_free(struct ofono_sim_context *context); ofono-1.17.bzr6912+16.04.20160314.3/src/siri.c0000644000015600001650000002007712671500024020306 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2013 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" static GSList *g_drivers = NULL; struct ofono_siri { ofono_bool_t siri_status; unsigned int eyes_free_mode; unsigned int pending_eyes_free_mode; const struct ofono_siri_driver *driver; void *driver_data; struct ofono_atom *atom; DBusMessage *pending; }; void ofono_siri_set_status(struct ofono_siri *siri, int value) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(siri->atom); dbus_bool_t siri_status; if (siri == NULL) return; if (value == 1) siri->siri_status = TRUE; else siri->siri_status = FALSE; siri_status = siri->siri_status; if (__ofono_atom_get_registered(siri->atom) == FALSE) return; ofono_dbus_signal_property_changed(conn, path, OFONO_SIRI_INTERFACE, "Enabled", DBUS_TYPE_BOOLEAN, &siri_status); } static DBusMessage *siri_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_siri *siri = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; dbus_bool_t status; const char *eyes_free_str; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); status = siri->siri_status; ofono_dbus_dict_append(&dict, "Enabled", DBUS_TYPE_BOOLEAN, &status); if (siri->eyes_free_mode == 0) eyes_free_str = "disabled"; else eyes_free_str = "enabled"; ofono_dbus_dict_append(&dict, "EyesFreeMode", DBUS_TYPE_STRING, &eyes_free_str); dbus_message_iter_close_container(&iter, &dict); return reply; } static void set_eyes_free_mode_callback(const struct ofono_error *error, struct ofono_siri *siri) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(siri->atom); DBusMessage *reply; const char *eyes_free_str; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Set eyes free mode callback returned error %s", telephony_error_to_str(error)); reply = __ofono_error_failed(siri->pending); __ofono_dbus_pending_reply(&siri->pending, reply); return; } siri->eyes_free_mode = siri->pending_eyes_free_mode; if (siri->eyes_free_mode == 0) eyes_free_str = "disabled"; else eyes_free_str = "enabled"; reply = dbus_message_new_method_return(siri->pending); __ofono_dbus_pending_reply(&siri->pending, reply); ofono_dbus_signal_property_changed(conn, path, OFONO_SIRI_INTERFACE, "EyesFreeMode", DBUS_TYPE_STRING, &eyes_free_str); } static DBusMessage *siri_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_siri *siri = data; DBusMessageIter iter, var; char *val; const char *name; if (siri->pending) return __ofono_error_busy(msg); if (dbus_message_iter_init(msg, &iter) == FALSE) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &val); if (g_str_equal(name, "EyesFreeMode") == TRUE) { if (!siri->driver->set_eyes_free_mode) return __ofono_error_not_implemented(msg); if (g_str_equal(val, "disabled") == TRUE) siri->pending_eyes_free_mode = 0; else if (g_str_equal(val, "enabled") == TRUE) siri->pending_eyes_free_mode = 1; else return __ofono_error_invalid_args(msg); siri->pending = dbus_message_ref(msg); siri->driver->set_eyes_free_mode(siri, set_eyes_free_mode_callback, siri->pending_eyes_free_mode); } else return __ofono_error_invalid_args(msg); return NULL; } static const GDBusMethodTable siri_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}"}), siri_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, siri_set_property) }, { } }; static const GDBusSignalTable siri_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s"}, { "value", "v"})) }, { } }; static void siri_remove(struct ofono_atom *atom) { struct ofono_siri *siri = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (siri == NULL) return; if (siri->driver != NULL && siri->driver->remove != NULL) siri->driver->remove(siri); g_free(siri); } struct ofono_siri *ofono_siri_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_siri *siri; GSList *l; if (driver == NULL) return NULL; siri = g_try_new0(struct ofono_siri, 1); if (siri == NULL) return NULL; siri->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_SIRI, siri_remove, siri); siri->eyes_free_mode = 0; siri->pending_eyes_free_mode = 0; for (l = g_drivers; l; l = l->next) { const struct ofono_siri_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(siri, vendor, data) < 0) continue; siri->driver = drv; break; } return siri; } static void ofono_siri_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); struct ofono_siri *siri = __ofono_atom_get_data(atom); if (siri->pending) { DBusMessage *reply = __ofono_error_failed(siri->pending); __ofono_dbus_pending_reply(&siri->pending, reply); } ofono_modem_remove_interface(modem, OFONO_SIRI_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_SIRI_INTERFACE); } void ofono_siri_register(struct ofono_siri *siri) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(siri->atom); const char *path = __ofono_atom_get_path(siri->atom); if (!g_dbus_register_interface(conn, path, OFONO_SIRI_INTERFACE, siri_methods, siri_signals, NULL, siri, NULL)) { ofono_error("Could not create %s interface", OFONO_SIRI_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_SIRI_INTERFACE); __ofono_atom_register(siri->atom, ofono_siri_unregister); } int ofono_siri_driver_register(const struct ofono_siri_driver *driver) { DBG("driver: %p, name: %s", driver, driver->name); if (driver->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) driver); return 0; } void ofono_siri_driver_unregister(const struct ofono_siri_driver *driver) { DBG("driver: %p, name: %s", driver, driver->name); g_drivers = g_slist_remove(g_drivers, (void *) driver); } void ofono_siri_remove(struct ofono_siri *siri) { __ofono_atom_free(siri->atom); } void ofono_siri_set_data(struct ofono_siri *siri, void *data) { siri->driver_data = data; } void *ofono_siri_get_data(struct ofono_siri *siri) { return siri->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/ofono.conf0000644000015600001650000000165112671500024021160 0ustar pbuserpbgroup00000000000000 ofono-1.17.bzr6912+16.04.20160314.3/src/dns-client.c0000644000015600001650000000706312671500024021400 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. * Copyright (C) 2015 Canonical 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. * * 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 #endif #include #include #include #include "ofono.h" #include "dns-client.h" static GSList *g_drivers; ofono_dns_client_request_t __ofono_dns_client_submit_request( const char *hostname, const char *device, const char **servers, int timeout_ms, ofono_dns_client_callback_t cb, void *data) { GSList *d; ofono_dns_client_request_t token = NULL; for (d = g_drivers; d != NULL; d = d->next) { const struct ofono_dns_client_driver *driver = d->data; if (driver->submit_request == NULL) continue; DBG("Calling dns client plugin '%s'", driver->name); token = driver->submit_request(hostname, device, servers, timeout_ms, cb, data); if (token == NULL) continue; return token; } return token; } void __ofono_dns_client_cancel_request(ofono_dns_client_request_t request) { GSList *d; for (d = g_drivers; d != NULL; d = d->next) { const struct ofono_dns_client_driver *driver = d->data; if (driver->cancel_request == NULL) continue; /* Returns TRUE if this driver is the request owner */ if (driver->cancel_request(request)) break; } } /* * Returns a human-friendly error string corresponding to |status|. * The strings that we return are intentionally consistent with shill error * messages. */ const char *__ofono_dns_client_strerror(ofono_dns_client_status_t status) { switch (status) { case OFONO_DNS_CLIENT_SUCCESS: return "The query was successful."; case OFONO_DNS_CLIENT_ERROR_NO_DATA: return "The query response contains no answers."; case OFONO_DNS_CLIENT_ERROR_FORM_ERR: return "The server says the query is bad."; case OFONO_DNS_CLIENT_ERROR_SERVER_FAIL: return "The server says it had a failure."; case OFONO_DNS_CLIENT_ERROR_NOT_FOUND: return "The queried-for domain was not found."; case OFONO_DNS_CLIENT_ERROR_NOT_IMP: return "The server doesn't implement operation."; case OFONO_DNS_CLIENT_ERROR_REFUSED: return "The server replied, refused the query."; case OFONO_DNS_CLIENT_ERROR_BAD_QUERY: return "Locally we could not format a query."; case OFONO_DNS_CLIENT_ERROR_NET_REFUSED: return "The network connection was refused."; case OFONO_DNS_CLIENT_ERROR_TIMED_OUT: return "The network connection was timed out."; case OFONO_DNS_CLIENT_ERROR_UNKNOWN: default: return "DNS Resolver unknown internal error."; } } int ofono_dns_client_driver_register(struct ofono_dns_client_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_prepend(g_drivers, driver); return 0; } void ofono_dns_client_driver_unregister( const struct ofono_dns_client_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_remove(g_drivers, driver); } ofono-1.17.bzr6912+16.04.20160314.3/src/genbuiltin0000755000015600001650000000035412671500024021256 0ustar pbuserpbgroup00000000000000#!/bin/sh for i in $* do echo "extern struct ofono_plugin_desc __ofono_builtin_$i;" done echo echo "static struct ofono_plugin_desc *__ofono_builtin[] = {" for i in $* do echo " &__ofono_builtin_$i," done echo " NULL" echo "};" ofono-1.17.bzr6912+16.04.20160314.3/src/common.c0000644000015600001650000004543312671500024020633 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include "common.h" #include "util.h" struct error_entry { int error; const char *str; }; /* * 0-127 from 24.011 Annex E2 * 127-255 23.040 Section 9.2.3.22 * Rest are from 27.005 Section 3.2.5 */ struct error_entry cms_errors[] = { { 1, "Unassigned number" }, { 8, "Operator determined barring" }, { 10, "Call barred" }, { 21, "Short message transfer rejected" }, { 27, "Destination out of service" }, { 28, "Unidentified subscriber" }, { 29, "Facility rejected" }, { 30, "Unknown subscriber" }, { 38, "Network out of order" }, { 41, "Temporary failure" }, { 42, "Congestion" }, { 47, "Resources unavailable" }, { 50, "Requested facility not subscribed" }, { 69, "Requested facility not implemented" }, { 81, "Invalid short message transfer reference value" }, { 95, "Invalid message, unspecified" }, { 96, "Invalid mandatory information" }, { 97, "Message type non existent or not implemented" }, { 98, "Message not compatible with short message protocol state" }, { 99, "Information element non-existent or not implemented" }, { 111, "Protocol error, unspecified" }, { 127, "Interworking error, unspecified" }, { 128, "Telematic interworking not supported" }, { 129, "Short message type 0 not supported" }, { 130, "Cannot replace short message" }, { 143, "Unspecified TP-PID error" }, { 144, "Data code scheme not supported" }, { 145, "Message class not supported" }, { 159, "Unspecified TP-DCS error" }, { 160, "Command cannot be actioned" }, { 161, "Command unsupported" }, { 175, "Unspecified TP-Command error" }, { 176, "TPDU not supported" }, { 192, "SC busy" }, { 193, "No SC subscription" }, { 194, "SC System failure" }, { 195, "Invalid SME address" }, { 196, "Destination SME barred" }, { 197, "SM Rejected-Duplicate SM" }, { 198, "TP-VPF not supported" }, { 199, "TP-VP not supported" }, { 208, "(U)SIM SMS Storage full" }, { 209, "No SMS Storage capability in SIM" }, { 210, "Error in MS" }, { 211, "Memory capacity exceeded" }, { 212, "SIM application toolkit busy" }, { 213, "SIM data download error" }, { 255, "Unspecified error cause" }, { 300, "ME Failure" }, { 301, "SMS service of ME reserved" }, { 302, "Operation not allowed" }, { 303, "Operation not supported" }, { 304, "Invalid PDU mode parameter" }, { 305, "Invalid Text mode parameter" }, { 310, "(U)SIM not inserted" }, { 311, "(U)SIM PIN required" }, { 312, "PH-(U)SIM PIN required" }, { 313, "(U)SIM failure" }, { 314, "(U)SIM busy" }, { 315, "(U)SIM wrong" }, { 316, "(U)SIM PUK required" }, { 317, "(U)SIM PIN2 required" }, { 318, "(U)SIM PUK2 required" }, { 320, "Memory failure" }, { 321, "Invalid memory index" }, { 322, "Memory full" }, { 330, "SMSC address unknown" }, { 331, "No network service" }, { 332, "Network timeout" }, { 340, "No +CNMA expected" }, { 500, "Unknown error" }, }; /* 27.007, Section 9 */ struct error_entry cme_errors[] = { { 0, "Phone failure" }, { 1, "No connection to phone" }, { 2, "Phone adaptor link reserved" }, { 3, "Operation not allowed" }, { 4, "Operation not supported" }, { 5, "PH_SIM PIN required" }, { 6, "PH_FSIM PIN required" }, { 7, "PH_FSIM PUK required" }, { 10, "SIM not inserted" }, { 11, "SIM PIN required" }, { 12, "SIM PUK required" }, { 13, "SIM failure" }, { 14, "SIM busy" }, { 15, "SIM wrong" }, { 16, "Incorrect password" }, { 17, "SIM PIN2 required" }, { 18, "SIM PUK2 required" }, { 20, "Memory full" }, { 21, "Invalid index" }, { 22, "Not found" }, { 23, "Memory failure" }, { 24, "Text string too long" }, { 25, "Invalid characters in text string" }, { 26, "Dial string too long" }, { 27, "Invalid characters in dial string" }, { 30, "No network service" }, { 31, "Network timeout" }, { 32, "Network not allowed, emergency calls only" }, { 40, "Network personalization PIN required" }, { 41, "Network personalization PUK required" }, { 42, "Network subset personalization PIN required" }, { 43, "Network subset personalization PUK required" }, { 44, "Service provider personalization PIN required" }, { 45, "Service provider personalization PUK required" }, { 46, "Corporate personalization PIN required" }, { 47, "Corporate personalization PUK required" }, { 48, "PH-SIM PUK required" }, { 50, "Incorrect parameters" }, { 100, "Unknown error" }, { 103, "Illegal MS" }, { 106, "Illegal ME" }, { 107, "GPRS services not allowed" }, { 111, "PLMN not allowed" }, { 112, "Location area not allowed" }, { 113, "Roaming not allowed in this location area" }, { 126, "Operation temporary not allowed" }, { 132, "Service operation not supported" }, { 133, "Requested service option not subscribed" }, { 134, "Service option temporary out of order" }, { 148, "Unspecified GPRS error" }, { 149, "PDP authentication failure" }, { 150, "Invalid mobile class" }, { 256, "Operation temporarily not allowed" }, { 257, "Call barred" }, { 258, "Phone is busy" }, { 259, "User abort" }, { 260, "Invalid dial string" }, { 261, "SS not executed" }, { 262, "SIM Blocked" }, { 263, "Invalid block" }, { 772, "SIM powered down" }, }; /* 24.008 Annex H */ struct error_entry ceer_errors[] = { { 1, "Unassigned number" }, { 3, "No route to destination" }, { 6, "Channel unacceptable" }, { 8, "Operator determined barring" }, { 16, "Normal call clearing" }, { 17, "User busy" }, { 18, "No user responding" }, { 19, "User alerting, no answer" }, { 21, "Call rejected" }, { 22, "Number changed" }, { 25, "Pre-emption" }, { 26, "Non-selected user clearing" }, { 27, "Destination out of order" }, { 28, "Invalid number format (incomplete number)" }, { 29, "Facility rejected" }, { 30, "Response to STATUS ENQUIRY" }, { 31, "Normal, unspecified" }, { 34, "No circuit/channel available" }, { 38, "Network out of order" }, { 41, "Temporary failure" }, { 42, "Switching equipment congestion" }, { 43, "Access information discarded" }, { 44, "Requested circuit/channel not available" }, { 47, "Resource unavailable (unspecified)" }, { 49, "Quality of service unavailable" }, { 50, "Requested facility not subscribed" }, { 55, "Incoming calls barred within the CUG" }, { 57, "Bearer capability not authorized" }, { 58, "Bearer capability not presently available" }, { 63, "Service or option not available, unspecified" }, { 65, "Bearer service not implemented" }, { 68, "ACM equal to or greater than ACMmax" }, { 69, "Requested facility not implemented" }, { 70, "Only restricted digital information bearer capability is available" }, { 79, "Service or option not implemented, unspecified" }, { 81, "Invalid transaction identifier value" }, { 87, "User not member of CUG" }, { 88, "Incompatible destination" }, { 91, "Invalid transit network selection" }, { 95, "Semantically incorrect message" }, { 96, "Invalid mandatory information"}, { 97, "Message type non-existent or not implemented" }, { 98, "Message type not compatible with protocol state" }, { 99, "Information element non-existent or not implemented" }, { 100, "Conditional IE error" }, { 101, "Message not compatible with protocol state" }, { 102, "Recovery on timer expiry" }, { 111, "Protocol error, unspecified" }, { 127, "Interworking, unspecified" }, }; gboolean valid_number_format(const char *number, int length) { int len = strlen(number); int begin = 0; int i; if (!len) return FALSE; if (number[0] == '+') begin = 1; if (begin == len) return FALSE; if ((len - begin) > length) return FALSE; for (i = begin; i < len; i++) { if (number[i] >= '0' && number[i] <= '9') continue; if (number[i] == '*' || number[i] == '#') continue; return FALSE; } return TRUE; } /* * According to 3GPP TS 24.011 or 3GPP TS 31.102, some * addresses (or numbers), like Service Centre address, * Destination address, or EFADN (Abbreviated dialling numbers), * are up 20 digits. */ gboolean valid_phone_number_format(const char *number) { return valid_number_format(number, 20); } gboolean valid_long_phone_number_format(const char *number) { return valid_number_format(number, OFONO_MAX_PHONE_NUMBER_LENGTH); } gboolean valid_cdma_phone_number_format(const char *number) { int len = strlen(number); int i; if (!len) return FALSE; if (len > OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH) return FALSE; for (i = 0; i < len; i++) { if (number[i] >= '0' && number[i] <= '9') continue; if (number[i] == '*' || number[i] == '#') continue; return FALSE; } return TRUE; } const char *telephony_error_to_str(const struct ofono_error *error) { struct error_entry *e; int maxentries; int i; switch (error->type) { case OFONO_ERROR_TYPE_CME: e = cme_errors; maxentries = sizeof(cme_errors) / sizeof(struct error_entry); break; case OFONO_ERROR_TYPE_CMS: e = cms_errors; maxentries = sizeof(cms_errors) / sizeof(struct error_entry); break; case OFONO_ERROR_TYPE_CEER: e = ceer_errors; maxentries = sizeof(ceer_errors) / sizeof(struct error_entry); break; default: return "Unknown error type"; } for (i = 0; i < maxentries; i++) if (e[i].error == error->error) return e[i].str; return "Unknown error"; } int mmi_service_code_to_bearer_class(int code) { int cls = 0; /* * Teleservices according to 22.004 * 1 - Voice * 2 - SMS * 3,4,5 - Unallocated * 6 - Fax * 7 - All Data Async * 8 - All Data Sync * 12 - Voice Group */ switch (code) { /* 22.030: 1 to 6, 12 */ case 10: cls = BEARER_CLASS_VOICE | BEARER_CLASS_FAX | BEARER_CLASS_SMS; break; /* 22.030: 1 */ case 11: cls = BEARER_CLASS_VOICE; break; /* 22.030: 2-6 */ case 12: cls = BEARER_CLASS_SMS | BEARER_CLASS_FAX; break; /* 22.030: 6 */ case 13: cls = BEARER_CLASS_FAX; break; /* 22.030: 2 */ case 16: cls = BEARER_CLASS_SMS; break; /* TODO: Voice Group Call & Broadcast VGCS & VBS */ case 17: case 18: break; /* 22.030: 1, 3 to 6, 12 */ case 19: cls = BEARER_CLASS_VOICE | BEARER_CLASS_FAX; break; /* * 22.030: 7-11 * 22.004 only defines BS 7 (Data Sync) & BS 8 (Data Async) * and PAD and Packet bearer services are deprecated. Still, * AT modems rely on these to differentiate between sending * a 'All Sync' or 'All Data Sync' message types. In theory * both message types cover the same bearer services, but we * must still send these for conformance reasons. */ case 20: cls = BEARER_CLASS_DATA_ASYNC | BEARER_CLASS_DATA_SYNC | BEARER_CLASS_PAD | BEARER_CLASS_PACKET; break; /* According to 22.030: All Async (7) */ case 21: cls = BEARER_CLASS_DATA_ASYNC | BEARER_CLASS_PAD; break; /* According to 22.030: All Data Async (7)*/ case 25: cls = BEARER_CLASS_DATA_ASYNC; break; /* According to 22.030: All Sync (8) */ case 22: cls = BEARER_CLASS_DATA_SYNC | BEARER_CLASS_PACKET; break; /* According to 22.030: All Data Sync (8) */ case 24: cls = BEARER_CLASS_DATA_SYNC; break; /* According to 22.030: Telephony & All Sync services (1, 8) */ case 26: cls = BEARER_CLASS_VOICE | BEARER_CLASS_DATA_SYNC | BEARER_CLASS_PACKET; break; default: break; } return cls; } const char *phone_number_to_string(const struct ofono_phone_number *ph) { static char buffer[OFONO_MAX_PHONE_NUMBER_LENGTH + 2]; if (ph->type == 145 && (strlen(ph->number) > 0) && ph->number[0] != '+') { buffer[0] = '+'; strncpy(buffer + 1, ph->number, OFONO_MAX_PHONE_NUMBER_LENGTH); buffer[OFONO_MAX_PHONE_NUMBER_LENGTH + 1] = '\0'; } else { strncpy(buffer, ph->number, OFONO_MAX_PHONE_NUMBER_LENGTH + 1); buffer[OFONO_MAX_PHONE_NUMBER_LENGTH + 1] = '\0'; } return buffer; } void string_to_phone_number(const char *str, struct ofono_phone_number *ph) { if (str[0] == '+') { strcpy(ph->number, str+1); ph->type = 145; /* International */ } else { strcpy(ph->number, str); ph->type = 129; /* Local */ } } const char *cdma_phone_number_to_string( const struct ofono_cdma_phone_number *ph) { static char buffer[OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH + 1]; strncpy(buffer, ph->number, OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH); buffer[OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH] = '\0'; return buffer; } void string_to_cdma_phone_number(const char *str, struct ofono_cdma_phone_number *ph) { strcpy(ph->number, str); } gboolean valid_ussd_string(const char *str, gboolean call_in_progress) { int len = strlen(str); if (!len) return FALSE; /* * Return true if an MMI input string is to be sent as USSD. * * According to 3GPP TS 22.030, after checking the well-known * supplementary service control, SIM control and manufacturer * defined control codes, the terminal should check if the input * should be sent as USSD according to the following rules: * * 1) Terminated by '#' * 2) A short string of 1 or 2 digits * * As an exception, if a 2 digit string starts with a '1' and * there are no calls in progress then this string is treated as * a call setup request instead. */ if (str[len-1] == '#') return TRUE; if (!call_in_progress && len == 2 && str[0] == '1') return FALSE; if (len <= 2) return TRUE; return FALSE; } const char *ss_control_type_to_string(enum ss_control_type type) { switch (type) { case SS_CONTROL_TYPE_ACTIVATION: return "activation"; case SS_CONTROL_TYPE_REGISTRATION: return "registration"; case SS_CONTROL_TYPE_QUERY: return "interrogation"; case SS_CONTROL_TYPE_DEACTIVATION: return "deactivation"; case SS_CONTROL_TYPE_ERASURE: return "erasure"; } return NULL; } #define NEXT_FIELD(str, dest) \ do { \ dest = str; \ \ str = strchrnul(str, '*'); \ if (*str) { \ *str = '\0'; \ str += 1; \ } \ } while (0) \ /* * Note: The str will be modified, so in case of error you should * throw it away and start over */ gboolean parse_ss_control_string(char *str, int *ss_type, char **sc, char **sia, char **sib, char **sic, char **sid, char **dn) { int len = strlen(str); int cur = 0; char *c; unsigned int i; gboolean ret = FALSE; /* Minimum is {*,#}SC# */ if (len < 4) goto out; if (str[0] != '*' && str[0] != '#') goto out; cur = 1; if (str[1] != '*' && str[1] != '#' && (str[1] > '9' || str[1] < '0')) goto out; if (str[0] == '#' && str[1] == '*') goto out; if (str[1] == '#' || str[1] == '*') cur = 2; if (str[0] == '*' && str[1] == '*') *ss_type = SS_CONTROL_TYPE_REGISTRATION; else if (str[0] == '#' && str[1] == '#') *ss_type = SS_CONTROL_TYPE_ERASURE; else if (str[0] == '*' && str[1] == '#') *ss_type = SS_CONTROL_TYPE_QUERY; else if (str[0] == '*') *ss_type = SS_CONTROL_TYPE_ACTIVATION; else *ss_type = SS_CONTROL_TYPE_DEACTIVATION; /* Must have at least one other '#' */ c = strrchr(str+cur, '#'); if (c == NULL) goto out; *dn = c+1; *c = '\0'; if (strlen(*dn) > 0 && !valid_phone_number_format(*dn)) goto out; c = str+cur; NEXT_FIELD(c, *sc); /* * According to 22.030 SC is 2 or 3 digits, there can be * an optional digit 'n' if this is a call setup string, * however 22.030 does not define any SC of length 3 * with an 'n' present */ if (strlen(*sc) < 2 || strlen(*sc) > 3) goto out; for (i = 0; i < strlen(*sc); i++) if (!g_ascii_isdigit((*sc)[i])) goto out; NEXT_FIELD(c, *sia); NEXT_FIELD(c, *sib); NEXT_FIELD(c, *sic); NEXT_FIELD(c, *sid); if (*c == '\0') ret = TRUE; out: return ret; } static const char *bearer_class_lut[] = { "Voice", "Data", "Fax", "Sms", "DataSync", "DataAsync", "DataPad", "DataPacket" }; const char *bearer_class_to_string(enum bearer_class cls) { switch (cls) { case BEARER_CLASS_VOICE: return bearer_class_lut[0]; case BEARER_CLASS_DATA: return bearer_class_lut[1]; case BEARER_CLASS_FAX: return bearer_class_lut[2]; case BEARER_CLASS_SMS: return bearer_class_lut[3]; case BEARER_CLASS_DATA_SYNC: return bearer_class_lut[4]; case BEARER_CLASS_DATA_ASYNC: return bearer_class_lut[5]; case BEARER_CLASS_PACKET: return bearer_class_lut[6]; case BEARER_CLASS_PAD: return bearer_class_lut[7]; case BEARER_CLASS_DEFAULT: case BEARER_CLASS_SS_DEFAULT: break; }; return NULL; } const char *packet_bearer_to_string(int bearer) { switch (bearer) { case PACKET_BEARER_NONE: return "none"; case PACKET_BEARER_GPRS: return "gprs"; case PACKET_BEARER_EGPRS: return "edge"; case PACKET_BEARER_UMTS: return "umts"; case PACKET_BEARER_HSUPA: return "hsupa"; case PACKET_BEARER_HSDPA: return "hsdpa"; case PACKET_BEARER_HSUPA_HSDPA: return "hspa"; case PACKET_BEARER_EPS: return "lte"; } return ""; } const char *registration_status_to_string(int status) { switch (status) { case NETWORK_REGISTRATION_STATUS_NOT_REGISTERED: return "unregistered"; case NETWORK_REGISTRATION_STATUS_REGISTERED: return "registered"; case NETWORK_REGISTRATION_STATUS_SEARCHING: return "searching"; case NETWORK_REGISTRATION_STATUS_DENIED: return "denied"; case NETWORK_REGISTRATION_STATUS_UNKNOWN: return "unknown"; case NETWORK_REGISTRATION_STATUS_ROAMING: return "roaming"; } return ""; } const char *registration_tech_to_string(int tech) { switch (tech) { case ACCESS_TECHNOLOGY_GSM: return "gsm"; case ACCESS_TECHNOLOGY_GSM_COMPACT: return "gsm"; case ACCESS_TECHNOLOGY_UTRAN: return "umts"; case ACCESS_TECHNOLOGY_GSM_EGPRS: return "edge"; case ACCESS_TECHNOLOGY_UTRAN_HSDPA: return "hspa"; case ACCESS_TECHNOLOGY_UTRAN_HSUPA: return "hspa"; case ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA: return "hspa"; case ACCESS_TECHNOLOGY_EUTRAN: return "lte"; default: return ""; } } gboolean is_valid_apn(const char *apn) { int i; int last_period = 0; if (apn[0] == '.') return FALSE; for (i = 0; apn[i] != '\0'; i++) { if (g_ascii_isalnum(apn[i])) continue; if (apn[i] == '-') continue; if (apn[i] == '.' && (i - last_period) > 1) { last_period = i; continue; } return FALSE; } return TRUE; } const char *ofono_uuid_to_str(const struct ofono_uuid *uuid) { static char buf[OFONO_SHA1_UUID_LEN * 2 + 1]; return encode_hex_own_buf(uuid->uuid, OFONO_SHA1_UUID_LEN, 0, buf); } void ofono_call_init(struct ofono_call *call) { memset(call, 0, sizeof(struct ofono_call)); call->cnap_validity = CNAP_VALIDITY_NOT_AVAILABLE; call->clip_validity = CLIP_VALIDITY_NOT_AVAILABLE; } ofono-1.17.bzr6912+16.04.20160314.3/src/ctm.c0000644000015600001650000001740712671500024020126 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 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 #endif #include #include #include #include #include #include "ofono.h" #include "common.h" #define CTM_FLAG_CACHED 0x1 static GSList *g_drivers = NULL; struct ofono_ctm { DBusMessage *pending; int flags; ofono_bool_t enabled; const struct ofono_ctm_driver *driver; void *driver_data; struct ofono_atom *atom; }; static DBusMessage *ctm_get_properties_reply(DBusMessage *msg, struct ofono_ctm *ctm) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; dbus_bool_t value; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); value = ctm->enabled; ofono_dbus_dict_append(&dict, "Enabled", DBUS_TYPE_BOOLEAN, &value); dbus_message_iter_close_container(&iter, &dict); return reply; } static void ctm_signal_enabled(struct ofono_ctm *ctm) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(ctm->atom); ofono_bool_t value = ctm->enabled; ofono_dbus_signal_property_changed(conn, path, OFONO_TEXT_TELEPHONY_INTERFACE, "Enabled", DBUS_TYPE_BOOLEAN, &value); } static void ctm_set_enabled_callback(const struct ofono_error *error, void *data) { struct ofono_ctm *ctm = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error setting ctm enabled property"); reply = __ofono_error_failed(ctm->pending); __ofono_dbus_pending_reply(&ctm->pending, reply); return; } ctm->enabled = !ctm->enabled; reply = dbus_message_new_method_return(ctm->pending); __ofono_dbus_pending_reply(&ctm->pending, reply); ctm_signal_enabled(ctm); } static void ctm_query_enabled_callback(const struct ofono_error *error, ofono_bool_t enable, void *data) { struct ofono_ctm *ctm = data; DBusMessage *reply; ofono_bool_t enabled_old; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error during ctm enabled query"); reply = __ofono_error_failed(ctm->pending); __ofono_dbus_pending_reply(&ctm->pending, reply); return; } ctm->flags |= CTM_FLAG_CACHED; enabled_old = ctm->enabled; ctm->enabled = enable; reply = ctm_get_properties_reply(ctm->pending, ctm); __ofono_dbus_pending_reply(&ctm->pending, reply); if (ctm->enabled != enabled_old) ctm_signal_enabled(ctm); } static DBusMessage *ctm_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_ctm *ctm = data; if (ctm->flags & CTM_FLAG_CACHED) return ctm_get_properties_reply(msg, ctm); if (ctm->pending) return __ofono_error_busy(msg); ctm->pending = dbus_message_ref(msg); ctm->driver->query_tty(ctm, ctm_query_enabled_callback, ctm); return NULL; } static DBusMessage *ctm_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_ctm *ctm = data; DBusMessageIter iter; DBusMessageIter var; const char *property; if (ctm->pending) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (g_strcmp0(property, "Enabled") == 0) { dbus_bool_t value; int target; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); target = value; if (ctm->enabled == target) return dbus_message_new_method_return(msg); ctm->pending = dbus_message_ref(msg); ctm->driver->set_tty(ctm, target, ctm_set_enabled_callback, ctm); return NULL; } return __ofono_error_invalid_args(msg); } static const GDBusMethodTable ctm_methods[] = { { GDBUS_ASYNC_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), ctm_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, ctm_set_property) }, { } }; static const GDBusSignalTable ctm_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; int ofono_ctm_driver_register(const struct ofono_ctm_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d == NULL || d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *)d); return 0; } void ofono_ctm_driver_unregister(const struct ofono_ctm_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d == NULL) return; g_drivers = g_slist_remove(g_drivers, (void *)d); } static void text_telephony_unregister(struct ofono_atom *atom) { struct ofono_ctm *ctm = __ofono_atom_get_data(atom); const char *path = __ofono_atom_get_path(ctm->atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(ctm->atom); ofono_modem_remove_interface(modem, OFONO_TEXT_TELEPHONY_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_TEXT_TELEPHONY_INTERFACE); } static void text_telephony_remove(struct ofono_atom *atom) { struct ofono_ctm *ctm = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (ctm == NULL) return; if (ctm->driver && ctm->driver->remove) ctm->driver->remove(ctm); g_free(ctm); } struct ofono_ctm *ofono_ctm_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_ctm *ctm; GSList *l; if (driver == NULL) return NULL; ctm = g_try_new0(struct ofono_ctm, 1); if (ctm == NULL) return NULL; ctm->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CTM, text_telephony_remove, ctm); for (l = g_drivers; l; l = l->next) { const struct ofono_ctm_driver *drv = l->data; if (g_strcmp0(drv->name, driver) != 0) continue; if (drv->probe(ctm, vendor, data) < 0) continue; ctm->driver = drv; break; } return ctm; } void ofono_ctm_register(struct ofono_ctm *ctm) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(ctm->atom); const char *path = __ofono_atom_get_path(ctm->atom); if (!g_dbus_register_interface(conn, path, OFONO_TEXT_TELEPHONY_INTERFACE, ctm_methods, ctm_signals, NULL, ctm, NULL)) { ofono_error("Could not create %s interface", OFONO_TEXT_TELEPHONY_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_TEXT_TELEPHONY_INTERFACE); __ofono_atom_register(ctm->atom, text_telephony_unregister); } void ofono_ctm_remove(struct ofono_ctm *ctm) { __ofono_atom_free(ctm->atom); } void ofono_ctm_set_data(struct ofono_ctm *ctm, void *data) { ctm->driver_data = data; } void *ofono_ctm_get_data(struct ofono_ctm *ctm) { return ctm->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/modem.c0000644000015600001650000014341412671500024020442 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include "ofono.h" #include "common.h" static GSList *g_devinfo_drivers = NULL; static GSList *g_driver_list = NULL; static GSList *g_modem_list = NULL; static int next_modem_id = 0; static gboolean powering_down = FALSE; static int modems_remaining = 0; static struct ofono_watchlist *g_modemwatches = NULL; enum property_type { PROPERTY_TYPE_INVALID = 0, PROPERTY_TYPE_STRING, PROPERTY_TYPE_INTEGER, PROPERTY_TYPE_BOOLEAN, }; enum modem_state { MODEM_STATE_POWER_OFF, MODEM_STATE_PRE_SIM, MODEM_STATE_OFFLINE, MODEM_STATE_ONLINE, }; struct ofono_modem { char *path; enum modem_state modem_state; GSList *atoms; struct ofono_watchlist *atom_watches; GSList *interface_list; GSList *feature_list; unsigned int call_ids; DBusMessage *pending; guint interface_update; ofono_bool_t powered; ofono_bool_t powered_pending; ofono_bool_t get_online; ofono_bool_t lockdown; char *lock_owner; guint lock_watch; guint timeout; ofono_bool_t online; struct ofono_watchlist *online_watches; struct ofono_watchlist *powered_watches; guint emergency; GHashTable *properties; struct ofono_sim *sim; unsigned int sim_watch; unsigned int sim_ready_watch; const struct ofono_modem_driver *driver; void *driver_data; char *driver_type; char *name; ofono_bool_t driver_watches_sim; }; struct ofono_devinfo { char *manufacturer; char *model; char *revision; char *serial; unsigned int dun_watch; const struct ofono_devinfo_driver *driver; void *driver_data; struct ofono_atom *atom; }; struct ofono_atom { enum ofono_atom_type type; enum modem_state modem_state; void (*destruct)(struct ofono_atom *atom); void (*unregister)(struct ofono_atom *atom); void *data; struct ofono_modem *modem; }; struct atom_watch { struct ofono_watchlist_item item; enum ofono_atom_type type; }; struct modem_property { enum property_type type; void *value; }; static const char *modem_type_to_string(enum ofono_modem_type type) { switch (type) { case OFONO_MODEM_TYPE_HARDWARE: return "hardware"; case OFONO_MODEM_TYPE_HFP: return "hfp"; case OFONO_MODEM_TYPE_SAP: return "sap"; case OFONO_MODEM_TYPE_TEST: return "test"; } return "unknown"; } unsigned int __ofono_modem_callid_next(struct ofono_modem *modem) { unsigned int i; for (i = 1; i < sizeof(modem->call_ids) * 8; i++) { if (modem->call_ids & (1 << i)) continue; return i; } return 0; } void __ofono_modem_callid_hold(struct ofono_modem *modem, int id) { modem->call_ids |= (1 << id); } void __ofono_modem_callid_release(struct ofono_modem *modem, int id) { modem->call_ids &= ~(1 << id); } void ofono_modem_set_data(struct ofono_modem *modem, void *data) { if (modem == NULL) return; modem->driver_data = data; } void *ofono_modem_get_data(struct ofono_modem *modem) { if (modem == NULL) return NULL; return modem->driver_data; } const char *ofono_modem_get_path(struct ofono_modem *modem) { if (modem) return modem->path; return NULL; } struct ofono_atom *__ofono_modem_add_atom(struct ofono_modem *modem, enum ofono_atom_type type, void (*destruct)(struct ofono_atom *), void *data) { struct ofono_atom *atom; if (modem == NULL) return NULL; atom = g_new0(struct ofono_atom, 1); atom->type = type; atom->modem_state = modem->modem_state; atom->destruct = destruct; atom->data = data; atom->modem = modem; modem->atoms = g_slist_prepend(modem->atoms, atom); return atom; } struct ofono_atom *__ofono_modem_add_atom_offline(struct ofono_modem *modem, enum ofono_atom_type type, void (*destruct)(struct ofono_atom *), void *data) { struct ofono_atom *atom; atom = __ofono_modem_add_atom(modem, type, destruct, data); atom->modem_state = MODEM_STATE_OFFLINE; return atom; } void *__ofono_atom_get_data(struct ofono_atom *atom) { return atom->data; } const char *__ofono_atom_get_path(struct ofono_atom *atom) { return atom->modem->path; } struct ofono_modem *__ofono_atom_get_modem(struct ofono_atom *atom) { return atom->modem; } static void call_watches(struct ofono_atom *atom, enum ofono_atom_watch_condition cond) { struct ofono_modem *modem = atom->modem; GSList *atom_watches = modem->atom_watches->items; GSList *l; struct atom_watch *watch; ofono_atom_watch_func notify; for (l = atom_watches; l; l = l->next) { watch = l->data; if (watch->type != atom->type) continue; notify = watch->item.notify; notify(atom, cond, watch->item.notify_data); } } void __ofono_atom_register(struct ofono_atom *atom, void (*unregister)(struct ofono_atom *)) { if (unregister == NULL) return; atom->unregister = unregister; call_watches(atom, OFONO_ATOM_WATCH_CONDITION_REGISTERED); } void __ofono_atom_unregister(struct ofono_atom *atom) { if (atom->unregister == NULL) return; call_watches(atom, OFONO_ATOM_WATCH_CONDITION_UNREGISTERED); atom->unregister(atom); atom->unregister = NULL; } gboolean __ofono_atom_get_registered(struct ofono_atom *atom) { return atom->unregister ? TRUE : FALSE; } unsigned int __ofono_modem_add_atom_watch(struct ofono_modem *modem, enum ofono_atom_type type, ofono_atom_watch_func notify, void *data, ofono_destroy_func destroy) { struct atom_watch *watch; unsigned int id; GSList *l; struct ofono_atom *atom; if (notify == NULL) return 0; watch = g_new0(struct atom_watch, 1); watch->type = type; watch->item.notify = notify; watch->item.destroy = destroy; watch->item.notify_data = data; id = __ofono_watchlist_add_item(modem->atom_watches, (struct ofono_watchlist_item *)watch); for (l = modem->atoms; l; l = l->next) { atom = l->data; if (atom->type != type || atom->unregister == NULL) continue; notify(atom, OFONO_ATOM_WATCH_CONDITION_REGISTERED, data); } return id; } gboolean __ofono_modem_remove_atom_watch(struct ofono_modem *modem, unsigned int id) { return __ofono_watchlist_remove_item(modem->atom_watches, id); } struct ofono_atom *__ofono_modem_find_atom(struct ofono_modem *modem, enum ofono_atom_type type) { GSList *l; struct ofono_atom *atom; if (modem == NULL) return NULL; for (l = modem->atoms; l; l = l->next) { atom = l->data; if (atom->type == type && atom->unregister != NULL) return atom; } return NULL; } void __ofono_modem_foreach_atom(struct ofono_modem *modem, enum ofono_atom_type type, ofono_atom_func callback, void *data) { GSList *l; struct ofono_atom *atom; if (modem == NULL) return; for (l = modem->atoms; l; l = l->next) { atom = l->data; if (atom->type != type) continue; callback(atom, data); } } void __ofono_modem_foreach_registered_atom(struct ofono_modem *modem, enum ofono_atom_type type, ofono_atom_func callback, void *data) { GSList *l; struct ofono_atom *atom; if (modem == NULL) return; for (l = modem->atoms; l; l = l->next) { atom = l->data; if (atom->type != type) continue; if (atom->unregister == NULL) continue; callback(atom, data); } } void __ofono_atom_free(struct ofono_atom *atom) { struct ofono_modem *modem = atom->modem; modem->atoms = g_slist_remove(modem->atoms, atom); __ofono_atom_unregister(atom); if (atom->destruct) atom->destruct(atom); g_free(atom); } static void flush_atoms(struct ofono_modem *modem, enum modem_state new_state) { GSList *cur; GSList *prev; GSList *tmp; DBG(""); prev = NULL; cur = modem->atoms; while (cur) { struct ofono_atom *atom = cur->data; if (atom->modem_state <= new_state) { prev = cur; cur = cur->next; continue; } __ofono_atom_unregister(atom); if (atom->destruct) atom->destruct(atom); g_free(atom); if (prev) prev->next = cur->next; else modem->atoms = cur->next; tmp = cur; cur = cur->next; g_slist_free_1(tmp); } } static void notify_online_watches(struct ofono_modem *modem) { struct ofono_watchlist_item *item; GSList *l; ofono_modem_online_notify_func notify; if (modem->online_watches == NULL) return; for (l = modem->online_watches->items; l; l = l->next) { item = l->data; notify = item->notify; notify(modem, modem->online, item->notify_data); } } static void notify_powered_watches(struct ofono_modem *modem) { struct ofono_watchlist_item *item; GSList *l; ofono_modem_powered_notify_func notify; if (modem->powered_watches == NULL) return; for (l = modem->powered_watches->items; l; l = l->next) { item = l->data; notify = item->notify; notify(modem, modem->powered, item->notify_data); } } static void set_online(struct ofono_modem *modem, ofono_bool_t new_online) { DBusConnection *conn = ofono_dbus_get_connection(); if (new_online == modem->online) return; modem->online = new_online; ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Online", DBUS_TYPE_BOOLEAN, &modem->online); notify_online_watches(modem); } static void modem_change_state(struct ofono_modem *modem, enum modem_state new_state) { struct ofono_modem_driver const *driver = modem->driver; enum modem_state old_state = modem->modem_state; DBG("old state: %d, new state: %d", old_state, new_state); if (old_state == new_state) return; modem->modem_state = new_state; if (old_state > new_state) flush_atoms(modem, new_state); switch (new_state) { case MODEM_STATE_POWER_OFF: modem->call_ids = 0; break; case MODEM_STATE_PRE_SIM: if (old_state < MODEM_STATE_PRE_SIM && driver->pre_sim) driver->pre_sim(modem); break; case MODEM_STATE_OFFLINE: if (old_state < MODEM_STATE_OFFLINE) { if (driver->post_sim) driver->post_sim(modem); __ofono_history_probe_drivers(modem); __ofono_nettime_probe_drivers(modem); } break; case MODEM_STATE_ONLINE: if (driver->post_online) driver->post_online(modem); break; } } unsigned int __ofono_modem_add_online_watch(struct ofono_modem *modem, ofono_modem_online_notify_func notify, void *data, ofono_destroy_func destroy) { struct ofono_watchlist_item *item; if (modem == NULL || notify == NULL) return 0; item = g_new0(struct ofono_watchlist_item, 1); item->notify = notify; item->destroy = destroy; item->notify_data = data; return __ofono_watchlist_add_item(modem->online_watches, item); } void __ofono_modem_remove_online_watch(struct ofono_modem *modem, unsigned int id) { __ofono_watchlist_remove_item(modem->online_watches, id); } unsigned int __ofono_modem_add_powered_watch(struct ofono_modem *modem, ofono_modem_powered_notify_func notify, void *data, ofono_destroy_func destroy) { struct ofono_watchlist_item *item; if (modem == NULL || notify == NULL) return 0; item = g_new0(struct ofono_watchlist_item, 1); item->notify = notify; item->destroy = destroy; item->notify_data = data; return __ofono_watchlist_add_item(modem->powered_watches, item); } void __ofono_modem_remove_powered_watch(struct ofono_modem *modem, unsigned int id) { __ofono_watchlist_remove_item(modem->powered_watches, id); } static gboolean modem_has_sim(struct ofono_modem *modem) { GSList *l; struct ofono_atom *atom; for (l = modem->atoms; l; l = l->next) { atom = l->data; if (atom->type == OFONO_ATOM_TYPE_SIM) return TRUE; } return FALSE; } static gboolean modem_is_always_online(struct ofono_modem *modem) { if (modem->driver->set_online == NULL) return TRUE; if (ofono_modem_get_boolean(modem, "AlwaysOnline") == TRUE) return TRUE; return FALSE; } static void common_online_cb(const struct ofono_error *error, void *data) { struct ofono_modem *modem = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) return; /* * If we need to get online after a silent reset this callback * is called. The callback should not consider the pending dbus * message. * * Additionally, this process can be interrupted by the following * events: * - Sim being removed or reset * - SetProperty(Powered, False) being called * - SetProperty(Lockdown, True) being called * * We should not set the modem to the online state in these cases. */ switch (modem->modem_state) { case MODEM_STATE_OFFLINE: set_online(modem, TRUE); /* Will this increase emergency call setup time??? */ modem_change_state(modem, MODEM_STATE_ONLINE); break; case MODEM_STATE_POWER_OFF: /* The powered operation is pending */ break; case MODEM_STATE_PRE_SIM: /* * Its valid to be in online even without a SIM/SIM being * PIN locked. e.g.: Emergency mode */ DBG("Online in PRE SIM state"); set_online(modem, TRUE); break; case MODEM_STATE_ONLINE: ofono_error("Online called when the modem is already online!"); break; }; } static void online_cb(const struct ofono_error *error, void *data) { struct ofono_modem *modem = data; DBusMessage *reply; if (!modem->pending) goto out; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) reply = dbus_message_new_method_return(modem->pending); else reply = __ofono_error_failed(modem->pending); __ofono_dbus_pending_reply(&modem->pending, reply); out: common_online_cb(error, data); } static void offline_cb(const struct ofono_error *error, void *data) { struct ofono_modem *modem = data; DBusMessage *reply; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) reply = dbus_message_new_method_return(modem->pending); else reply = __ofono_error_failed(modem->pending); __ofono_dbus_pending_reply(&modem->pending, reply); if (error->type == OFONO_ERROR_TYPE_NO_ERROR) { switch (modem->modem_state) { case MODEM_STATE_PRE_SIM: set_online(modem, FALSE); break; case MODEM_STATE_ONLINE: set_online(modem, FALSE); modem_change_state(modem, MODEM_STATE_OFFLINE); break; default: break; } } } static void sim_state_watch(enum ofono_sim_state new_state, void *user) { struct ofono_modem *modem = user; switch (new_state) { case OFONO_SIM_STATE_NOT_PRESENT: modem_change_state(modem, MODEM_STATE_PRE_SIM); case OFONO_SIM_STATE_INSERTED: case OFONO_SIM_STATE_RESETTING: break; case OFONO_SIM_STATE_LOCKED_OUT: if (modem->driver_watches_sim == FALSE) modem_change_state(modem, MODEM_STATE_PRE_SIM); break; case OFONO_SIM_STATE_READY: /* Avoid state regressions */ if (modem->modem_state != MODEM_STATE_ONLINE) { modem_change_state(modem, MODEM_STATE_OFFLINE); /* Modem is always online, proceed to online state. */ if (modem_is_always_online(modem) == TRUE) set_online(modem, TRUE); if (modem->online == TRUE) modem_change_state(modem, MODEM_STATE_ONLINE); else if (modem->get_online) modem->driver->set_online(modem, 1, common_online_cb, modem); modem->get_online = FALSE; } break; } } static DBusMessage *set_property_online(struct ofono_modem *modem, DBusMessage *msg, DBusMessageIter *var) { ofono_bool_t online; const struct ofono_modem_driver *driver = modem->driver; if (modem->powered == FALSE) return __ofono_error_not_available(msg); if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(var, &online); if (modem->pending != NULL) return __ofono_error_busy(msg); if (modem->online == online) return dbus_message_new_method_return(msg); if (ofono_modem_get_emergency_mode(modem) == TRUE) return __ofono_error_emergency_active(msg); if (modem_is_always_online(modem) == TRUE) return __ofono_error_not_implemented(msg); modem->pending = dbus_message_ref(msg); driver->set_online(modem, online, online ? online_cb : offline_cb, modem); return NULL; } ofono_bool_t ofono_modem_get_online(struct ofono_modem *modem) { if (modem == NULL) return FALSE; return modem->online; } void __ofono_modem_append_properties(struct ofono_modem *modem, DBusMessageIter *dict) { char **interfaces; char **features; int i; GSList *l; struct ofono_devinfo *info; dbus_bool_t emergency = ofono_modem_get_emergency_mode(modem); const char *strtype; ofono_dbus_dict_append(dict, "Online", DBUS_TYPE_BOOLEAN, &modem->online); ofono_dbus_dict_append(dict, "Powered", DBUS_TYPE_BOOLEAN, &modem->powered); ofono_dbus_dict_append(dict, "Lockdown", DBUS_TYPE_BOOLEAN, &modem->lockdown); ofono_dbus_dict_append(dict, "Emergency", DBUS_TYPE_BOOLEAN, &emergency); info = __ofono_atom_find(OFONO_ATOM_TYPE_DEVINFO, modem); if (info) { if (info->manufacturer) ofono_dbus_dict_append(dict, "Manufacturer", DBUS_TYPE_STRING, &info->manufacturer); if (info->model) ofono_dbus_dict_append(dict, "Model", DBUS_TYPE_STRING, &info->model); if (info->revision) ofono_dbus_dict_append(dict, "Revision", DBUS_TYPE_STRING, &info->revision); if (info->serial) ofono_dbus_dict_append(dict, "Serial", DBUS_TYPE_STRING, &info->serial); } interfaces = g_new0(char *, g_slist_length(modem->interface_list) + 1); for (i = 0, l = modem->interface_list; l; l = l->next, i++) interfaces[i] = l->data; ofono_dbus_dict_append_array(dict, "Interfaces", DBUS_TYPE_STRING, &interfaces); g_free(interfaces); features = g_new0(char *, g_slist_length(modem->feature_list) + 1); for (i = 0, l = modem->feature_list; l; l = l->next, i++) features[i] = l->data; ofono_dbus_dict_append_array(dict, "Features", DBUS_TYPE_STRING, &features); g_free(features); if (modem->name) ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING, &modem->name); strtype = modem_type_to_string(modem->driver->modem_type); ofono_dbus_dict_append(dict, "Type", DBUS_TYPE_STRING, &strtype); } static DBusMessage *modem_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_modem *modem = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); __ofono_modem_append_properties(modem, &dict); dbus_message_iter_close_container(&iter, &dict); return reply; } static int set_powered(struct ofono_modem *modem, ofono_bool_t powered) { const struct ofono_modem_driver *driver = modem->driver; int err = -EINVAL; if (modem->powered_pending == powered) return -EALREADY; /* Remove the atoms even if the driver is no longer available */ if (powered == FALSE) modem_change_state(modem, MODEM_STATE_POWER_OFF); modem->powered_pending = powered; if (driver == NULL) return -EINVAL; if (powered == TRUE) { if (driver->enable) err = driver->enable(modem); } else { if (driver->disable) err = driver->disable(modem); } if (err == 0) { modem->powered = powered; notify_powered_watches(modem); } else if (err != -EINPROGRESS) modem->powered_pending = modem->powered; return err; } static void lockdown_remove(struct ofono_modem *modem) { DBusConnection *conn = ofono_dbus_get_connection(); if (modem->lock_watch) { g_dbus_remove_watch(conn, modem->lock_watch); modem->lock_watch = 0; } g_free(modem->lock_owner); modem->lock_owner = NULL; modem->lockdown = FALSE; } static gboolean set_powered_timeout(gpointer user) { struct ofono_modem *modem = user; DBG("modem: %p", modem); modem->timeout = 0; if (modem->powered_pending == FALSE) { DBusConnection *conn = ofono_dbus_get_connection(); dbus_bool_t powered = FALSE; set_online(modem, FALSE); modem->powered = FALSE; notify_powered_watches(modem); ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &powered); } else { modem->powered_pending = modem->powered; } if (modem->pending != NULL) { DBusMessage *reply; reply = __ofono_error_timed_out(modem->pending); __ofono_dbus_pending_reply(&modem->pending, reply); if (modem->lockdown) lockdown_remove(modem); } return FALSE; } static void lockdown_disconnect(DBusConnection *conn, void *user_data) { struct ofono_modem *modem = user_data; DBG(""); ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Lockdown", DBUS_TYPE_BOOLEAN, &modem->lockdown); modem->lock_watch = 0; lockdown_remove(modem); } static DBusMessage *set_property_lockdown(struct ofono_modem *modem, DBusMessage *msg, DBusMessageIter *var) { DBusConnection *conn = ofono_dbus_get_connection(); ofono_bool_t lockdown; dbus_bool_t powered; const char *caller; int err; if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(var, &lockdown); if (modem->pending != NULL) return __ofono_error_busy(msg); caller = dbus_message_get_sender(msg); if (modem->lockdown && g_strcmp0(caller, modem->lock_owner)) return __ofono_error_access_denied(msg); if (modem->lockdown == lockdown) return dbus_message_new_method_return(msg); if (lockdown == FALSE) { lockdown_remove(modem); goto done; } if (ofono_modem_get_emergency_mode(modem) == TRUE) return __ofono_error_emergency_active(msg); modem->lock_owner = g_strdup(caller); modem->lock_watch = g_dbus_add_disconnect_watch(conn, modem->lock_owner, lockdown_disconnect, modem, NULL); if (modem->lock_watch == 0) { g_free(modem->lock_owner); modem->lock_owner = NULL; return __ofono_error_failed(msg); } modem->lockdown = lockdown; if (modem->powered == FALSE) goto done; err = set_powered(modem, FALSE); if (err < 0) { if (err != -EINPROGRESS) { lockdown_remove(modem); return __ofono_error_failed(msg); } modem->pending = dbus_message_ref(msg); modem->timeout = g_timeout_add_seconds(20, set_powered_timeout, modem); return NULL; } set_online(modem, FALSE); powered = FALSE; ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &powered); done: g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Lockdown", DBUS_TYPE_BOOLEAN, &lockdown); return NULL; } static DBusMessage *modem_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_modem *modem = data; DBusMessageIter iter, var; const char *name; if (dbus_message_iter_init(msg, &iter) == FALSE) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); if (powering_down == TRUE) return __ofono_error_failed(msg); dbus_message_iter_recurse(&iter, &var); if (g_str_equal(name, "Online")) return set_property_online(modem, msg, &var); if (g_str_equal(name, "Powered") == TRUE) { ofono_bool_t powered; int err; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &powered); if (modem->pending != NULL) return __ofono_error_busy(msg); if (modem->powered == powered) return dbus_message_new_method_return(msg); if (ofono_modem_get_emergency_mode(modem) == TRUE) return __ofono_error_emergency_active(msg); if (modem->lockdown) return __ofono_error_access_denied(msg); err = set_powered(modem, powered); if (err < 0) { if (err != -EINPROGRESS) return __ofono_error_failed(msg); modem->pending = dbus_message_ref(msg); modem->timeout = g_timeout_add_seconds(20, set_powered_timeout, modem); return NULL; } g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &powered); if (powered) { modem_change_state(modem, MODEM_STATE_PRE_SIM); /* Force SIM Ready for devies with no sim atom */ if (modem_has_sim(modem) == FALSE) sim_state_watch(OFONO_SIM_STATE_READY, modem); } else { set_online(modem, FALSE); modem_change_state(modem, MODEM_STATE_POWER_OFF); } return NULL; } if (g_str_equal(name, "Lockdown")) return set_property_lockdown(modem, msg, &var); return __ofono_error_invalid_args(msg); } static const GDBusMethodTable modem_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), modem_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, modem_set_property) }, { } }; static const GDBusSignalTable modem_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered) { DBusConnection *conn = ofono_dbus_get_connection(); dbus_bool_t dbus_powered = powered; if (modem->timeout > 0) { g_source_remove(modem->timeout); modem->timeout = 0; } if (modem->powered_pending != modem->powered && modem->pending != NULL) { DBusMessage *reply; if (powered == modem->powered_pending) reply = dbus_message_new_method_return(modem->pending); else reply = __ofono_error_failed(modem->pending); __ofono_dbus_pending_reply(&modem->pending, reply); } modem->powered_pending = powered; if (modem->powered == powered) goto out; modem->powered = powered; notify_powered_watches(modem); if (modem->lockdown) ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Lockdown", DBUS_TYPE_BOOLEAN, &modem->lockdown); if (modem->driver == NULL) { ofono_error("Calling ofono_modem_set_powered on a" "modem with no driver is not valid, " "please fix the modem driver."); return; } ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &dbus_powered); if (powered) { modem_change_state(modem, MODEM_STATE_PRE_SIM); /* Force SIM Ready for devices with no sim atom */ if (modem_has_sim(modem) == FALSE) sim_state_watch(OFONO_SIM_STATE_READY, modem); } else { set_online(modem, FALSE); modem_change_state(modem, MODEM_STATE_POWER_OFF); } out: if (powering_down && powered == FALSE) { modems_remaining -= 1; if (modems_remaining == 0) __ofono_exit(); } } ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem) { if (modem == NULL) return FALSE; return modem->powered; } static gboolean trigger_interface_update(void *data) { struct ofono_modem *modem = data; DBusConnection *conn = ofono_dbus_get_connection(); char **interfaces; char **features; GSList *l; int i; interfaces = g_new0(char *, g_slist_length(modem->interface_list) + 1); for (i = 0, l = modem->interface_list; l; l = l->next, i++) interfaces[i] = l->data; ofono_dbus_signal_array_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Interfaces", DBUS_TYPE_STRING, &interfaces); g_free(interfaces); features = g_new0(char *, g_slist_length(modem->feature_list) + 1); for (i = 0, l = modem->feature_list; l; l = l->next, i++) features[i] = l->data; ofono_dbus_signal_array_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Features", DBUS_TYPE_STRING, &features); g_free(features); modem->interface_update = 0; return FALSE; } static const struct { const char *interface; const char *feature; } feature_map[] = { { OFONO_NETWORK_REGISTRATION_INTERFACE, "net" }, { OFONO_RADIO_SETTINGS_INTERFACE, "rat" }, { OFONO_CELL_BROADCAST_INTERFACE, "cbs" }, { OFONO_MESSAGE_MANAGER_INTERFACE, "sms" }, { OFONO_SIM_MANAGER_INTERFACE, "sim" }, { OFONO_STK_INTERFACE, "stk" }, { OFONO_SUPPLEMENTARY_SERVICES_INTERFACE, "ussd" }, { OFONO_CONNECTION_MANAGER_INTERFACE, "gprs" }, { OFONO_TEXT_TELEPHONY_INTERFACE, "tty" }, { OFONO_LOCATION_REPORTING_INTERFACE, "gps" }, { }, }; static const char *get_feature(const char *interface) { int i; for (i = 0; feature_map[i].interface; i++) { if (strcmp(feature_map[i].interface, interface) == 0) return feature_map[i].feature; } return NULL; } void ofono_modem_add_interface(struct ofono_modem *modem, const char *interface) { const char *feature; modem->interface_list = g_slist_prepend(modem->interface_list, g_strdup(interface)); feature = get_feature(interface); if (feature) modem->feature_list = g_slist_prepend(modem->feature_list, g_strdup(feature)); if (modem->interface_update != 0) return; modem->interface_update = g_idle_add(trigger_interface_update, modem); } void ofono_modem_remove_interface(struct ofono_modem *modem, const char *interface) { GSList *found; const char *feature; found = g_slist_find_custom(modem->interface_list, interface, (GCompareFunc) strcmp); if (found == NULL) { ofono_error("Interface %s not found on the interface_list", interface); return; } g_free(found->data); modem->interface_list = g_slist_remove(modem->interface_list, found->data); feature = get_feature(interface); if (feature) { found = g_slist_find_custom(modem->feature_list, feature, (GCompareFunc) strcmp); if (found) { g_free(found->data); modem->feature_list = g_slist_remove(modem->feature_list, found->data); } } if (modem->interface_update != 0) return; modem->interface_update = g_idle_add(trigger_interface_update, modem); } static void query_serial_cb(const struct ofono_error *error, const char *serial, void *user) { struct ofono_devinfo *info = user; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(info->atom); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) return; info->serial = g_strdup(serial); ofono_dbus_signal_property_changed(conn, path, OFONO_MODEM_INTERFACE, "Serial", DBUS_TYPE_STRING, &info->serial); } static void query_serial(struct ofono_devinfo *info) { if (info->driver->query_serial == NULL) return; info->driver->query_serial(info, query_serial_cb, info); } static void query_revision_cb(const struct ofono_error *error, const char *revision, void *user) { struct ofono_devinfo *info = user; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(info->atom); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) goto out; info->revision = g_strdup(revision); ofono_dbus_signal_property_changed(conn, path, OFONO_MODEM_INTERFACE, "Revision", DBUS_TYPE_STRING, &info->revision); out: query_serial(info); } static void query_revision(struct ofono_devinfo *info) { if (info->driver->query_revision == NULL) { query_serial(info); return; } info->driver->query_revision(info, query_revision_cb, info); } static void query_model_cb(const struct ofono_error *error, const char *model, void *user) { struct ofono_devinfo *info = user; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(info->atom); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) goto out; info->model = g_strdup(model); ofono_dbus_signal_property_changed(conn, path, OFONO_MODEM_INTERFACE, "Model", DBUS_TYPE_STRING, &info->model); out: query_revision(info); } static void query_model(struct ofono_devinfo *info) { if (info->driver->query_model == NULL) { /* If model is not supported, don't bother querying revision */ query_serial(info); return; } info->driver->query_model(info, query_model_cb, info); } static void query_manufacturer_cb(const struct ofono_error *error, const char *manufacturer, void *user) { struct ofono_devinfo *info = user; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(info->atom); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) goto out; info->manufacturer = g_strdup(manufacturer); ofono_dbus_signal_property_changed(conn, path, OFONO_MODEM_INTERFACE, "Manufacturer", DBUS_TYPE_STRING, &info->manufacturer); out: query_model(info); } static gboolean query_manufacturer(gpointer user) { struct ofono_devinfo *info = user; if (info->driver->query_manufacturer == NULL) { query_model(info); return FALSE; } info->driver->query_manufacturer(info, query_manufacturer_cb, info); return FALSE; } static void attr_template(struct ofono_emulator *em, struct ofono_emulator_request *req, const char *attr) { struct ofono_error result; if (attr == NULL) attr = "Unknown"; result.error = 0; switch (ofono_emulator_request_get_type(req)) { case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY: ofono_emulator_send_info(em, attr, TRUE); result.type = OFONO_ERROR_TYPE_NO_ERROR; ofono_emulator_send_final(em, &result); break; case OFONO_EMULATOR_REQUEST_TYPE_SUPPORT: result.type = OFONO_ERROR_TYPE_NO_ERROR; ofono_emulator_send_final(em, &result); break; default: result.type = OFONO_ERROR_TYPE_FAILURE; ofono_emulator_send_final(em, &result); }; } static void gmi_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_devinfo *info = userdata; attr_template(em, req, info->manufacturer); } static void gmm_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_devinfo *info = userdata; attr_template(em, req, info->model); } static void gmr_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_devinfo *info = userdata; attr_template(em, req, info->revision); } static void gcap_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { attr_template(em, req, "+GCAP: +CGSM"); } static void dun_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) return; ofono_emulator_add_handler(atom, "+GMI", gmi_cb, data, NULL); ofono_emulator_add_handler(atom, "+GMM", gmm_cb, data, NULL); ofono_emulator_add_handler(atom, "+GMR", gmr_cb, data, NULL); ofono_emulator_add_handler(atom, "+GCAP", gcap_cb, data, NULL); } int ofono_devinfo_driver_register(const struct ofono_devinfo_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_devinfo_drivers = g_slist_prepend(g_devinfo_drivers, (void *) d); return 0; } void ofono_devinfo_driver_unregister(const struct ofono_devinfo_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_devinfo_drivers = g_slist_remove(g_devinfo_drivers, (void *) d); } static void devinfo_remove(struct ofono_atom *atom) { struct ofono_devinfo *info = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (info == NULL) return; if (info->driver == NULL) return; if (info->driver->remove) info->driver->remove(info); g_free(info); } struct ofono_devinfo *ofono_devinfo_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_devinfo *info; GSList *l; info = g_new0(struct ofono_devinfo, 1); info->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_DEVINFO, devinfo_remove, info); for (l = g_devinfo_drivers; l; l = l->next) { const struct ofono_devinfo_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(info, vendor, data) < 0) continue; info->driver = drv; break; } return info; } static void devinfo_unregister(struct ofono_atom *atom) { struct ofono_devinfo *info = __ofono_atom_get_data(atom); g_free(info->manufacturer); info->manufacturer = NULL; g_free(info->model); info->model = NULL; g_free(info->revision); info->revision = NULL; g_free(info->serial); info->serial = NULL; } void ofono_devinfo_register(struct ofono_devinfo *info) { struct ofono_modem *modem = __ofono_atom_get_modem(info->atom); __ofono_atom_register(info->atom, devinfo_unregister); info->dun_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_EMULATOR_DUN, dun_watch, info, NULL); query_manufacturer(info); } void ofono_devinfo_remove(struct ofono_devinfo *info) { __ofono_atom_free(info->atom); } void ofono_devinfo_set_data(struct ofono_devinfo *info, void *data) { info->driver_data = data; } void *ofono_devinfo_get_data(struct ofono_devinfo *info) { return info->driver_data; } static void unregister_property(gpointer data) { struct modem_property *property = data; DBG("property %p", property); g_free(property->value); g_free(property); } static int set_modem_property(struct ofono_modem *modem, const char *name, enum property_type type, const void *value) { struct modem_property *property; DBG("modem %p property %s", modem, name); if (type != PROPERTY_TYPE_STRING && type != PROPERTY_TYPE_INTEGER && type != PROPERTY_TYPE_BOOLEAN) return -EINVAL; property = g_try_new0(struct modem_property, 1); if (property == NULL) return -ENOMEM; property->type = type; switch (type) { case PROPERTY_TYPE_STRING: property->value = g_strdup((const char *) value); break; case PROPERTY_TYPE_INTEGER: property->value = g_memdup(value, sizeof(int)); break; case PROPERTY_TYPE_BOOLEAN: property->value = g_memdup(value, sizeof(ofono_bool_t)); break; default: break; } g_hash_table_replace(modem->properties, g_strdup(name), property); return 0; } static gboolean get_modem_property(struct ofono_modem *modem, const char *name, enum property_type type, void *value) { struct modem_property *property; DBG("modem %p property %s", modem, name); property = g_hash_table_lookup(modem->properties, name); if (property == NULL) return FALSE; if (property->type != type) return FALSE; switch (property->type) { case PROPERTY_TYPE_STRING: *((const char **) value) = property->value; return TRUE; case PROPERTY_TYPE_INTEGER: memcpy(value, property->value, sizeof(int)); return TRUE; case PROPERTY_TYPE_BOOLEAN: memcpy(value, property->value, sizeof(ofono_bool_t)); return TRUE; default: return FALSE; } } int ofono_modem_set_string(struct ofono_modem *modem, const char *key, const char *value) { return set_modem_property(modem, key, PROPERTY_TYPE_STRING, value); } int ofono_modem_set_integer(struct ofono_modem *modem, const char *key, int value) { return set_modem_property(modem, key, PROPERTY_TYPE_INTEGER, &value); } int ofono_modem_set_boolean(struct ofono_modem *modem, const char *key, ofono_bool_t value) { return set_modem_property(modem, key, PROPERTY_TYPE_BOOLEAN, &value); } const char *ofono_modem_get_string(struct ofono_modem *modem, const char *key) { const char *value; if (get_modem_property(modem, key, PROPERTY_TYPE_STRING, &value) == FALSE) return NULL; return value; } int ofono_modem_get_integer(struct ofono_modem *modem, const char *key) { int value; if (get_modem_property(modem, key, PROPERTY_TYPE_INTEGER, &value) == FALSE) return 0; return value; } ofono_bool_t ofono_modem_get_boolean(struct ofono_modem *modem, const char *key) { ofono_bool_t value; if (get_modem_property(modem, key, PROPERTY_TYPE_BOOLEAN, &value) == FALSE) return FALSE; return value; } void ofono_modem_set_name(struct ofono_modem *modem, const char *name) { if (modem->name) g_free(modem->name); modem->name = g_strdup(name); if (modem->driver) { DBusConnection *conn = ofono_dbus_get_connection(); ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Name", DBUS_TYPE_STRING, &modem->name); } } void ofono_modem_set_driver(struct ofono_modem *modem, const char *type) { DBG("type: %s", type); if (modem->driver) return; if (strlen(type) > 16) return; g_free(modem->driver_type); modem->driver_type = g_strdup(type); } void ofono_modem_set_driver_watches_sim(struct ofono_modem *modem, ofono_bool_t value) { modem->driver_watches_sim = value; } ofono_bool_t ofono_modem_get_driver_watches_sim(struct ofono_modem *modem) { return modem->driver_watches_sim; } struct ofono_modem *ofono_modem_create(const char *name, const char *type) { struct ofono_modem *modem; char path[128]; DBG("name: %s, type: %s", name, type); if (strlen(type) > 16) return NULL; if (name && strlen(name) > 64) return NULL; if (name == NULL) snprintf(path, sizeof(path), "/%s_%d", type, next_modem_id); else snprintf(path, sizeof(path), "/%s", name); if (__ofono_dbus_valid_object_path(path) == FALSE) return NULL; modem = g_try_new0(struct ofono_modem, 1); if (modem == NULL) return modem; modem->path = g_strdup(path); modem->driver_type = g_strdup(type); modem->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, unregister_property); g_modem_list = g_slist_prepend(g_modem_list, modem); if (name == NULL) next_modem_id += 1; return modem; } static void sim_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_modem *modem = data; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { modem->sim_ready_watch = 0; return; } modem->sim = __ofono_atom_get_data(atom); modem->sim_ready_watch = ofono_sim_add_state_watch(modem->sim, sim_state_watch, modem, NULL); } void __ofono_modemwatch_init(void) { g_modemwatches = __ofono_watchlist_new(g_free); } void __ofono_modemwatch_cleanup(void) { __ofono_watchlist_free(g_modemwatches); } unsigned int __ofono_modemwatch_add(ofono_modemwatch_cb_t cb, void *user, ofono_destroy_func destroy) { struct ofono_watchlist_item *watch; if (cb == NULL) return 0; watch = g_new0(struct ofono_watchlist_item, 1); watch->notify = cb; watch->destroy = destroy; watch->notify_data = user; return __ofono_watchlist_add_item(g_modemwatches, watch); } gboolean __ofono_modemwatch_remove(unsigned int id) { return __ofono_watchlist_remove_item(g_modemwatches, id); } static void call_modemwatches(struct ofono_modem *modem, gboolean added) { GSList *l; struct ofono_watchlist_item *watch; ofono_modemwatch_cb_t notify; DBG("%p added:%d", modem, added); for (l = g_modemwatches->items; l; l = l->next) { watch = l->data; notify = watch->notify; notify(modem, added, watch->notify_data); } } static void emit_modem_added(struct ofono_modem *modem) { DBusMessage *signal; DBusMessageIter iter; DBusMessageIter dict; const char *path; DBG("%p", modem); signal = dbus_message_new_signal(OFONO_MANAGER_PATH, OFONO_MANAGER_INTERFACE, "ModemAdded"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); path = modem->path; dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); __ofono_modem_append_properties(modem, &dict); dbus_message_iter_close_container(&iter, &dict); g_dbus_send_message(ofono_dbus_get_connection(), signal); } ofono_bool_t ofono_modem_is_registered(struct ofono_modem *modem) { if (modem == NULL) return FALSE; if (modem->driver == NULL) return FALSE; return TRUE; } int ofono_modem_register(struct ofono_modem *modem) { DBusConnection *conn = ofono_dbus_get_connection(); GSList *l; DBG("%p", modem); if (modem == NULL) return -EINVAL; if (powering_down == TRUE) return -EBUSY; if (modem->driver != NULL) return -EALREADY; for (l = g_driver_list; l; l = l->next) { const struct ofono_modem_driver *drv = l->data; if (g_strcmp0(drv->name, modem->driver_type)) continue; if (drv->probe(modem) < 0) continue; modem->driver = drv; break; } if (modem->driver == NULL) return -ENODEV; if (!g_dbus_register_interface(conn, modem->path, OFONO_MODEM_INTERFACE, modem_methods, modem_signals, NULL, modem, NULL)) { ofono_error("Modem register failed on path %s", modem->path); if (modem->driver->remove) modem->driver->remove(modem); modem->driver = NULL; return -EIO; } g_free(modem->driver_type); modem->driver_type = NULL; modem->atom_watches = __ofono_watchlist_new(g_free); modem->online_watches = __ofono_watchlist_new(g_free); modem->powered_watches = __ofono_watchlist_new(g_free); emit_modem_added(modem); call_modemwatches(modem, TRUE); modem->sim_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SIM, sim_watch, modem, NULL); return 0; } static void emit_modem_removed(struct ofono_modem *modem) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = modem->path; DBG("%p", modem); g_dbus_emit_signal(conn, OFONO_MANAGER_PATH, OFONO_MANAGER_INTERFACE, "ModemRemoved", DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); } static void modem_unregister(struct ofono_modem *modem) { DBusConnection *conn = ofono_dbus_get_connection(); DBG("%p", modem); if (modem->powered == TRUE) set_powered(modem, FALSE); __ofono_watchlist_free(modem->atom_watches); modem->atom_watches = NULL; __ofono_watchlist_free(modem->online_watches); modem->online_watches = NULL; __ofono_watchlist_free(modem->powered_watches); modem->powered_watches = NULL; modem->sim_watch = 0; modem->sim_ready_watch = 0; g_slist_foreach(modem->interface_list, (GFunc) g_free, NULL); g_slist_free(modem->interface_list); modem->interface_list = NULL; g_slist_foreach(modem->feature_list, (GFunc) g_free, NULL); g_slist_free(modem->feature_list); modem->feature_list = NULL; if (modem->timeout) { g_source_remove(modem->timeout); modem->timeout = 0; } if (modem->pending) { dbus_message_unref(modem->pending); modem->pending = NULL; } if (modem->interface_update) { g_source_remove(modem->interface_update); modem->interface_update = 0; } if (modem->lock_watch) { lockdown_remove(modem); ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Lockdown", DBUS_TYPE_BOOLEAN, &modem->lockdown); } g_dbus_unregister_interface(conn, modem->path, OFONO_MODEM_INTERFACE); if (modem->driver && modem->driver->remove) modem->driver->remove(modem); g_hash_table_destroy(modem->properties); modem->properties = NULL; modem->driver = NULL; emit_modem_removed(modem); call_modemwatches(modem, FALSE); } void ofono_modem_remove(struct ofono_modem *modem) { DBG("%p", modem); if (modem == NULL) return; if (modem->driver) modem_unregister(modem); g_modem_list = g_slist_remove(g_modem_list, modem); g_free(modem->driver_type); g_free(modem->name); g_free(modem->path); g_free(modem); } void ofono_modem_reset(struct ofono_modem *modem) { int err; DBG("%p", modem); if (modem->pending) { DBusMessage *reply = __ofono_error_failed(modem->pending); __ofono_dbus_pending_reply(&modem->pending, reply); } if (modem->modem_state == MODEM_STATE_ONLINE) modem->get_online = TRUE; ofono_modem_set_powered(modem, FALSE); err = set_powered(modem, TRUE); if (err == -EINPROGRESS) return; modem_change_state(modem, MODEM_STATE_PRE_SIM); } void __ofono_modem_sim_reset(struct ofono_modem *modem) { DBG("%p", modem); modem_change_state(modem, MODEM_STATE_PRE_SIM); } int ofono_modem_driver_register(const struct ofono_modem_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_driver_list = g_slist_prepend(g_driver_list, (void *) d); return 0; } void ofono_modem_driver_unregister(const struct ofono_modem_driver *d) { GSList *l; struct ofono_modem *modem; DBG("driver: %p, name: %s", d, d->name); g_driver_list = g_slist_remove(g_driver_list, (void *) d); for (l = g_modem_list; l; l = l->next) { modem = l->data; if (modem->driver != d) continue; modem_unregister(modem); } } void __ofono_modem_shutdown(void) { struct ofono_modem *modem; GSList *l; powering_down = TRUE; for (l = g_modem_list; l; l = l->next) { modem = l->data; if (modem->driver == NULL) continue; if (modem->powered == FALSE && modem->powered_pending == FALSE) continue; if (set_powered(modem, FALSE) == -EINPROGRESS) modems_remaining += 1; } if (modems_remaining == 0) __ofono_exit(); } void __ofono_modem_foreach(ofono_modem_foreach_func func, void *userdata) { struct ofono_modem *modem; GSList *l; for (l = g_modem_list; l; l = l->next) { modem = l->data; func(modem, userdata); } } struct ofono_modem *ofono_modem_find(ofono_modem_compare_cb_t func, void *user_data) { struct ofono_modem *modem; GSList *l; for (l = g_modem_list; l; l = l->next) { modem = l->data; if (func(modem, user_data) == TRUE) return modem; } return NULL; } ofono_bool_t ofono_modem_get_emergency_mode(struct ofono_modem *modem) { return modem->emergency != 0; } void __ofono_modem_inc_emergency_mode(struct ofono_modem *modem) { DBusConnection *conn = ofono_dbus_get_connection(); dbus_bool_t emergency = TRUE; if (++modem->emergency > 1) return; ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Emergency", DBUS_TYPE_BOOLEAN, &emergency); } void __ofono_modem_dec_emergency_mode(struct ofono_modem *modem) { DBusConnection *conn = ofono_dbus_get_connection(); dbus_bool_t emergency = FALSE; if (modem->emergency == 0) { ofono_error("emergency mode is already deactivated!!!"); return; } if (modem->emergency > 1) goto out; ofono_dbus_signal_property_changed(conn, modem->path, OFONO_MODEM_INTERFACE, "Emergency", DBUS_TYPE_BOOLEAN, &emergency); out: modem->emergency--; } ofono_bool_t ofono_modem_is_standby(struct ofono_modem *modem) { if (modem->driver->is_standby == NULL) return FALSE; return modem->driver->is_standby(modem); } ofono-1.17.bzr6912+16.04.20160314.3/src/storage.h0000644000015600001650000000271512671500024021010 0ustar pbuserpbgroup00000000000000/* * * 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 TEMP_FAILURE_RETRY #define TFR TEMP_FAILURE_RETRY #else #define TFR #endif #include #include #include int create_dirs(const char *filename, const mode_t mode); ssize_t read_file(unsigned char *buffer, size_t len, const char *path_fmt, ...) __attribute__((format(printf, 3, 4))); ssize_t write_file(const unsigned char *buffer, size_t len, mode_t mode, const char *path_fmt, ...) __attribute__((format(printf, 4, 5))); GKeyFile *storage_open(const char *imsi, const char *store); void storage_sync(const char *imsi, const char *store, GKeyFile *keyfile); void storage_close(const char *imsi, const char *store, GKeyFile *keyfile, gboolean save); ofono-1.17.bzr6912+16.04.20160314.3/src/sms.c0000644000015600001650000014341412671500024020143 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include "ofono.h" #include "common.h" #include "util.h" #include "smsutil.h" #include "storage.h" #include "simutil.h" #include "message.h" #define uninitialized_var(x) x = x #define MESSAGE_MANAGER_FLAG_CACHED 0x1 #define MESSAGE_MANAGER_FLAG_TXQ_ACTIVE 0x2 #define SETTINGS_STORE "sms" #define SETTINGS_GROUP "Settings" #define TXQ_MAX_RETRIES 4 #define NETWORK_TIMEOUT 332 static gboolean tx_next(gpointer user_data); static GSList *g_drivers = NULL; struct sms_handler { struct ofono_watchlist_item item; int dst; int src; }; struct ofono_sms { int flags; DBusMessage *pending; struct ofono_phone_number sca; struct sms_assembly *assembly; guint ref; GQueue *txq; unsigned long tx_counter; guint tx_source; struct ofono_message_waiting *mw; unsigned int mw_watch; ofono_bool_t registered; struct ofono_netreg *netreg; unsigned int netreg_watch; unsigned int status_watch; GKeyFile *settings; char *imsi; int bearer; enum sms_alphabet alphabet; const struct ofono_sms_driver *driver; void *driver_data; struct ofono_atom *atom; ofono_bool_t use_delivery_reports; struct status_report_assembly *sr_assembly; GHashTable *messages; struct ofono_watchlist *text_handlers; struct ofono_watchlist *datagram_handlers; }; struct pending_pdu { unsigned char pdu[176]; int tpdu_len; int pdu_len; }; struct tx_queue_entry { struct pending_pdu *pdus; unsigned char num_pdus; unsigned char cur_pdu; struct sms_address receiver; struct ofono_uuid uuid; unsigned int retry; unsigned int flags; ofono_sms_txq_submit_cb_t cb; void *data; ofono_destroy_func destroy; unsigned long id; }; static gboolean uuid_equal(gconstpointer v1, gconstpointer v2) { return memcmp(v1, v2, OFONO_SHA1_UUID_LEN) == 0; } static gboolean port_equal(int received, int expected) { return expected == -1 || received == expected; } static guint uuid_hash(gconstpointer v) { const struct ofono_uuid *uuid = v; guint h; memcpy(&h, uuid->uuid, sizeof(h)); return h; } static const char *sms_bearer_to_string(int bearer) { switch (bearer) { case 0: return "ps-only"; case 1: return "cs-only"; case 2: return "ps-preferred"; case 3: return "cs-preferred"; }; return NULL; } static gboolean sms_bearer_from_string(const char *str, int *bearer) { if (g_str_equal(str, "ps-only")) *bearer = 0; else if (g_str_equal(str, "cs-only")) *bearer = 1; else if (g_str_equal(str, "ps-preferred")) *bearer = 2; else if (g_str_equal(str, "cs-preferred")) *bearer = 3; else return FALSE; return TRUE; } static const char *sms_alphabet_to_string(enum sms_alphabet alphabet) { switch (alphabet) { case SMS_ALPHABET_TURKISH: return "turkish"; case SMS_ALPHABET_SPANISH: return "spanish"; case SMS_ALPHABET_PORTUGUESE: return "portuguese"; case SMS_ALPHABET_DEFAULT: return "default"; } return NULL; } static gboolean sms_alphabet_from_string(const char *str, enum sms_alphabet *alphabet) { if (g_str_equal(str, "default")) *alphabet = SMS_ALPHABET_DEFAULT; else if (g_str_equal(str, "turkish")) *alphabet = SMS_ALPHABET_TURKISH; else if (g_str_equal(str, "spanish")) *alphabet = SMS_ALPHABET_SPANISH; else if (g_str_equal(str, "portuguese")) *alphabet = SMS_ALPHABET_PORTUGUESE; else return FALSE; return TRUE; } static unsigned int add_sms_handler(struct ofono_watchlist *watchlist, int dst, int src, void *notify, void *data, ofono_destroy_func destroy) { struct sms_handler *handler; if (notify == NULL) return 0; handler = g_try_new0(struct sms_handler, 1); if (handler == NULL) return 0; handler->dst = dst; handler->src = src; handler->item.notify = notify; handler->item.notify_data = data; handler->item.destroy = destroy; return __ofono_watchlist_add_item(watchlist, (struct ofono_watchlist_item *) handler); } unsigned int __ofono_sms_text_watch_add(struct ofono_sms *sms, ofono_sms_text_notify_cb_t cb, void *data, ofono_destroy_func destroy) { if (sms == NULL) return 0; DBG("%p", sms); return add_sms_handler(sms->text_handlers, -1, -1, cb, data, destroy); } gboolean __ofono_sms_text_watch_remove(struct ofono_sms *sms, unsigned int id) { if (sms == NULL) return FALSE; DBG("%p", sms); return __ofono_watchlist_remove_item(sms->text_handlers, id); } unsigned int __ofono_sms_datagram_watch_add(struct ofono_sms *sms, ofono_sms_datagram_notify_cb_t cb, int dst, int src, void *data, ofono_destroy_func destroy) { if (sms == NULL) return 0; DBG("%p: dst %d, src %d", sms, dst, src); return add_sms_handler(sms->datagram_handlers, dst, src, cb, data, destroy); } gboolean __ofono_sms_datagram_watch_remove(struct ofono_sms *sms, unsigned int id) { if (sms == NULL) return FALSE; DBG("%p", sms); return __ofono_watchlist_remove_item(sms->datagram_handlers, id); } const char *__ofono_sms_message_path_from_uuid(struct ofono_sms *sms, const struct ofono_uuid *uuid) { return message_path_from_uuid(sms->atom, uuid); } static void set_bearer(struct ofono_sms *sms, int bearer) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sms->atom); const char *value; if (sms->bearer == bearer) return; sms->bearer = bearer; value = sms_bearer_to_string(sms->bearer); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_MANAGER_INTERFACE, "Bearer", DBUS_TYPE_STRING, &value); } static void set_alphabet(struct ofono_sms *sms, enum sms_alphabet alphabet) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sms->atom); const char *value; if (sms->alphabet == alphabet) return; sms->alphabet = alphabet; value = sms_alphabet_to_string(sms->alphabet); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_MANAGER_INTERFACE, "Alphabet", DBUS_TYPE_STRING, &value); } static void set_sca(struct ofono_sms *sms, const struct ofono_phone_number *sca) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sms->atom); const char *value; if (sms->sca.type == sca->type && !strcmp(sms->sca.number, sca->number)) return; sms->sca.type = sca->type; strncpy(sms->sca.number, sca->number, OFONO_MAX_PHONE_NUMBER_LENGTH); sms->sca.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0'; value = phone_number_to_string(&sms->sca); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_MANAGER_INTERFACE, "ServiceCenterAddress", DBUS_TYPE_STRING, &value); } static DBusMessage *generate_get_properties_reply(struct ofono_sms *sms, DBusMessage *msg) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; const char *sca; const char *bearer; const char *alphabet; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); sca = phone_number_to_string(&sms->sca); ofono_dbus_dict_append(&dict, "ServiceCenterAddress", DBUS_TYPE_STRING, &sca); ofono_dbus_dict_append(&dict, "UseDeliveryReports", DBUS_TYPE_BOOLEAN, &sms->use_delivery_reports); bearer = sms_bearer_to_string(sms->bearer); ofono_dbus_dict_append(&dict, "Bearer", DBUS_TYPE_STRING, &bearer); alphabet = sms_alphabet_to_string(sms->alphabet); ofono_dbus_dict_append(&dict, "Alphabet", DBUS_TYPE_STRING, &alphabet); dbus_message_iter_close_container(&iter, &dict); return reply; } static void sms_sca_query_cb(const struct ofono_error *error, const struct ofono_phone_number *sca, void *data) { struct ofono_sms *sms = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) goto out; set_sca(sms, sca); sms->flags |= MESSAGE_MANAGER_FLAG_CACHED; out: if (sms->pending) { DBusMessage *reply = generate_get_properties_reply(sms, sms->pending); __ofono_dbus_pending_reply(&sms->pending, reply); } } static DBusMessage *sms_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sms *sms = data; if (sms->flags & MESSAGE_MANAGER_FLAG_CACHED) return generate_get_properties_reply(sms, msg); if (sms->pending) return __ofono_error_busy(msg); if (sms->driver->sca_query == NULL) return __ofono_error_not_implemented(msg); sms->pending = dbus_message_ref(msg); sms->driver->sca_query(sms, sms_sca_query_cb, sms); return NULL; } static void bearer_set_query_callback(const struct ofono_error *error, int bearer, void *data) { struct ofono_sms *sms = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Set Bearer succeeded, but query failed"); reply = __ofono_error_failed(sms->pending); __ofono_dbus_pending_reply(&sms->pending, reply); return; } reply = dbus_message_new_method_return(sms->pending); __ofono_dbus_pending_reply(&sms->pending, reply); set_bearer(sms, bearer); } static void bearer_set_callback(const struct ofono_error *error, void *data) { struct ofono_sms *sms = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Setting Bearer failed"); __ofono_dbus_pending_reply(&sms->pending, __ofono_error_failed(sms->pending)); return; } sms->driver->bearer_query(sms, bearer_set_query_callback, sms); } static void sca_set_query_callback(const struct ofono_error *error, const struct ofono_phone_number *sca, void *data) { struct ofono_sms *sms = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Set SCA succeeded, but query failed"); sms->flags &= ~MESSAGE_MANAGER_FLAG_CACHED; reply = __ofono_error_failed(sms->pending); __ofono_dbus_pending_reply(&sms->pending, reply); return; } set_sca(sms, sca); reply = dbus_message_new_method_return(sms->pending); __ofono_dbus_pending_reply(&sms->pending, reply); } static void sca_set_callback(const struct ofono_error *error, void *data) { struct ofono_sms *sms = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Setting SCA failed"); __ofono_dbus_pending_reply(&sms->pending, __ofono_error_failed(sms->pending)); return; } sms->driver->sca_query(sms, sca_set_query_callback, sms); } static DBusMessage *sms_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sms *sms = data; DBusMessageIter iter; DBusMessageIter var; const char *property; if (sms->pending) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (!strcmp(property, "ServiceCenterAddress")) { const char *value; struct ofono_phone_number sca; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (strlen(value) == 0 || !valid_phone_number_format(value)) return __ofono_error_invalid_format(msg); if (sms->driver->sca_set == NULL || sms->driver->sca_query == NULL) return __ofono_error_not_implemented(msg); string_to_phone_number(value, &sca); sms->pending = dbus_message_ref(msg); sms->driver->sca_set(sms, &sca, sca_set_callback, sms); return NULL; } if (!strcmp(property, "Bearer")) { const char *value; int bearer; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (sms_bearer_from_string(value, &bearer) != TRUE) return __ofono_error_invalid_format(msg); if (sms->driver->bearer_set == NULL || sms->driver->bearer_query == NULL) return __ofono_error_not_implemented(msg); sms->pending = dbus_message_ref(msg); sms->driver->bearer_set(sms, bearer, bearer_set_callback, sms); return NULL; } if (!strcmp(property, "UseDeliveryReports")) { const char *path = __ofono_atom_get_path(sms->atom); dbus_bool_t value; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); if (sms->use_delivery_reports != (ofono_bool_t) value) { sms->use_delivery_reports = value; ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_MANAGER_INTERFACE, "UseDeliveryReports", DBUS_TYPE_BOOLEAN, &value); } return NULL; } if (!strcmp(property, "Alphabet")) { const char *value; enum sms_alphabet alphabet; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (!sms_alphabet_from_string(value, &alphabet)) return __ofono_error_invalid_format(msg); set_alphabet(sms, alphabet); g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); return NULL; } return __ofono_error_invalid_args(msg); } /* * Destroy/release the contents of a 'struct tx_queue_entry' * * This releases resources allocated *inside* @entry and @entry * itself. */ static void tx_queue_entry_destroy(struct tx_queue_entry *entry) { if (entry->destroy) entry->destroy(entry->data); g_free(entry->pdus); g_free(entry); } static void tx_queue_entry_destroy_foreach(gpointer _entry, gpointer unused) { tx_queue_entry_destroy(_entry); } static void sms_tx_queue_remove_entry(struct ofono_sms *sms, GList *entry_list, enum message_state tx_state) { struct tx_queue_entry *entry = entry_list->data; struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom); g_queue_delete_link(sms->txq, entry_list); DBG("%p", entry); if (entry->cb) entry->cb(tx_state == MESSAGE_STATE_SENT, entry->data); if (entry->flags & OFONO_SMS_SUBMIT_FLAG_RECORD_HISTORY) { enum ofono_history_sms_status hs; switch(tx_state) { case MESSAGE_STATE_SENT: hs = OFONO_HISTORY_SMS_STATUS_SUBMITTED; break; case MESSAGE_STATE_FAILED: hs = OFONO_HISTORY_SMS_STATUS_SUBMIT_FAILED; break; case MESSAGE_STATE_CANCELLED: hs = OFONO_HISTORY_SMS_STATUS_SUBMIT_CANCELLED; break; default: ofono_error("Unexpected sms state %d", tx_state); goto done; } __ofono_history_sms_send_status(modem, &entry->uuid, time(NULL), hs); } if (entry->flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS) { struct message *m; sms_tx_backup_free(sms->imsi, entry->id, entry->flags, ofono_uuid_to_str(&entry->uuid)); m = g_hash_table_lookup(sms->messages, &entry->uuid); if (m != NULL) { message_set_state(m, tx_state); g_hash_table_remove(sms->messages, &entry->uuid); message_emit_removed(m, OFONO_MESSAGE_MANAGER_INTERFACE); message_dbus_unregister(m); } } done: tx_queue_entry_destroy(entry); } static void tx_finished(const struct ofono_error *error, int mr, void *data) { struct ofono_sms *sms = data; struct tx_queue_entry *entry = g_queue_peek_head(sms->txq); gboolean ok = error->type == OFONO_ERROR_TYPE_NO_ERROR; enum message_state tx_state; DBG("tx_finished %p", entry); sms->flags &= ~MESSAGE_MANAGER_FLAG_TXQ_ACTIVE; if (ok == FALSE) { /* Retry again when back in online mode */ /* Note this does not increment retry count */ if (sms->registered == FALSE) return; tx_state = MESSAGE_STATE_FAILED; /* Retry done only for Network Timeout failure */ if (error->type == OFONO_ERROR_TYPE_CMS && error->error != NETWORK_TIMEOUT) goto next_q; if (!(entry->flags & OFONO_SMS_SUBMIT_FLAG_RETRY)) goto next_q; entry->retry += 1; if (entry->retry < TXQ_MAX_RETRIES) { DBG("Sending failed, retry in %d secs", entry->retry * 5); sms->tx_source = g_timeout_add_seconds(entry->retry * 5, tx_next, sms); return; } DBG("Max retries reached, giving up"); goto next_q; } if (entry->flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS) sms_tx_backup_remove(sms->imsi, entry->id, entry->flags, ofono_uuid_to_str(&entry->uuid), entry->cur_pdu); entry->cur_pdu += 1; entry->retry = 0; if (entry->flags & OFONO_SMS_SUBMIT_FLAG_REQUEST_SR) status_report_assembly_add_fragment(sms->sr_assembly, entry->uuid.uuid, &entry->receiver, mr, time(NULL), entry->num_pdus); if (entry->cur_pdu < entry->num_pdus) { sms->tx_source = g_timeout_add(0, tx_next, sms); return; } tx_state = MESSAGE_STATE_SENT; next_q: sms_tx_queue_remove_entry(sms, g_queue_peek_head_link(sms->txq), tx_state); if (sms->registered == FALSE) return; if (g_queue_peek_head(sms->txq)) { DBG("Scheduling next"); sms->tx_source = g_timeout_add(0, tx_next, sms); } } static gboolean tx_next(gpointer user_data) { struct ofono_sms *sms = user_data; int send_mms = 0; struct tx_queue_entry *entry = g_queue_peek_head(sms->txq); struct pending_pdu *pdu = &entry->pdus[entry->cur_pdu]; DBG("tx_next: %p", entry); sms->tx_source = 0; if (sms->registered == FALSE) return FALSE; if (g_queue_get_length(sms->txq) > 1 || (entry->num_pdus - entry->cur_pdu) > 1) send_mms = 1; sms->flags |= MESSAGE_MANAGER_FLAG_TXQ_ACTIVE; sms->driver->submit(sms, pdu->pdu, pdu->pdu_len, pdu->tpdu_len, send_mms, tx_finished, sms); return FALSE; } static void netreg_status_watch(int status, int lac, int ci, int tech, const char *mcc, const char *mnc, void *data) { struct ofono_sms *sms = data; switch (status) { case NETWORK_REGISTRATION_STATUS_REGISTERED: case NETWORK_REGISTRATION_STATUS_ROAMING: sms->registered = TRUE; break; default: sms->registered = FALSE; break; } if (sms->registered == FALSE) return; if (sms->tx_source > 0) return; if (sms->flags & MESSAGE_MANAGER_FLAG_TXQ_ACTIVE) return; if (g_queue_get_length(sms->txq)) sms->tx_source = g_timeout_add(0, tx_next, sms); } static void netreg_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_sms *sms = data; int status; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { sms->registered = FALSE; sms->status_watch = 0; sms->netreg = NULL; return; } sms->netreg = __ofono_atom_get_data(atom); sms->status_watch = __ofono_netreg_add_status_watch(sms->netreg, netreg_status_watch, sms, NULL); status = ofono_netreg_get_status(sms->netreg); netreg_status_watch(status, 0, 0, 0, NULL, NULL, sms); } /** * Generate a UUID from an SMS PDU List * * @param pdu Pointer to array of PDUs data to generate the ID from * @param pdus Number of entries in the \e pdu array * @return 0 in error (no memory or serious code inconsistency in the * input data structures), otherwise the SMS UUID. * * @internal * * The current time is added to avoid the UUID being the same when the * same message is sent to the same destination repeatedly. Note we * need a high resolution time (not just seconds), otherwise resending * in the same second (not that rare) could yield the same UUID. */ static gboolean sms_uuid_from_pdus(const struct pending_pdu *pdu, unsigned char pdus, struct ofono_uuid *uuid) { GChecksum *checksum; gsize uuid_size = sizeof(uuid->uuid); unsigned int cnt; struct timeval now; checksum = g_checksum_new(G_CHECKSUM_SHA1); if (checksum == NULL) return FALSE; for (cnt = 0; cnt < pdus; cnt++) g_checksum_update(checksum, pdu[cnt].pdu, pdu[cnt].pdu_len); gettimeofday(&now, NULL); g_checksum_update(checksum, (void *) &now, sizeof(now)); g_checksum_get_digest(checksum, uuid->uuid, &uuid_size); g_checksum_free(checksum); return TRUE; } static struct tx_queue_entry *tx_queue_entry_new(GSList *msg_list, unsigned int flags) { struct tx_queue_entry *entry; int i = 0; GSList *l; entry = g_try_new0(struct tx_queue_entry, 1); if (entry == NULL) return NULL; entry->num_pdus = g_slist_length(msg_list); entry->pdus = g_try_new0(struct pending_pdu, entry->num_pdus); if (entry->pdus == NULL) goto error; if (flags & OFONO_SMS_SUBMIT_FLAG_REQUEST_SR) { struct sms *head = msg_list->data; memcpy(&entry->receiver, &head->submit.daddr, sizeof(entry->receiver)); } entry->flags = flags; for (l = msg_list; l; l = l->next) { struct pending_pdu *pdu = &entry->pdus[i++]; struct sms *s = l->data; sms_encode(s, &pdu->pdu_len, &pdu->tpdu_len, pdu->pdu); DBG("pdu_len: %d, tpdu_len: %d", pdu->pdu_len, pdu->tpdu_len); } if (flags & OFONO_SMS_SUBMIT_FLAG_REUSE_UUID) return entry; if (sms_uuid_from_pdus(entry->pdus, entry->num_pdus, &entry->uuid)) return entry; error: g_free(entry->pdus); g_free(entry); return NULL; } static void tx_queue_entry_set_submit_notify(struct tx_queue_entry *entry, ofono_sms_txq_submit_cb_t cb, void *data, ofono_destroy_func destroy) { entry->cb = cb; entry->data = data; entry->destroy = destroy; } static void message_queued(struct ofono_sms *sms, const struct ofono_uuid *uuid, void *data) { DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *msg = data; const char *path; path = __ofono_sms_message_path_from_uuid(sms, uuid); g_dbus_send_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); } /* * Pre-process a SMS text message and deliver it [D-Bus SendMessage()] * * @conn: D-Bus connection * @msg: message data (telephone number and text) * @data: SMS object to use for transmision * * An alphabet is chosen for the text and it (might be) segmented in * fragments by sms_text_prepare() into @msg_list. A queue list @entry * is created by tx_queue_entry_new() and g_queue_push_tail() * appends that entry to the SMS transmit queue. Then the tx_next() * function is scheduled to run to process the queue. */ static DBusMessage *sms_send_message(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sms *sms = data; const char *to; const char *text; GSList *msg_list; struct ofono_modem *modem; unsigned int flags; gboolean use_16bit_ref = FALSE; int err; struct ofono_uuid uuid; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &to, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID)) return __ofono_error_invalid_args(msg); if (valid_phone_number_format(to) == FALSE) return __ofono_error_invalid_format(msg); msg_list = sms_text_prepare_with_alphabet(to, text, sms->ref, use_16bit_ref, sms->use_delivery_reports, sms->alphabet); if (msg_list == NULL) return __ofono_error_invalid_format(msg); flags = OFONO_SMS_SUBMIT_FLAG_RECORD_HISTORY; flags |= OFONO_SMS_SUBMIT_FLAG_RETRY; flags |= OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS; if (sms->use_delivery_reports) flags |= OFONO_SMS_SUBMIT_FLAG_REQUEST_SR; err = __ofono_sms_txq_submit(sms, msg_list, flags, &uuid, message_queued, msg); g_slist_foreach(msg_list, (GFunc) g_free, NULL); g_slist_free(msg_list); if (err < 0) return __ofono_error_failed(msg); modem = __ofono_atom_get_modem(sms->atom); __ofono_history_sms_send_pending(modem, &uuid, to, time(NULL), text); return NULL; } static DBusMessage *sms_get_messages(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sms *sms = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; DBusMessageIter entry, dict; const char *path; GHashTableIter hashiter; gpointer key, value; struct message *m; const struct ofono_uuid *uuid; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array); g_hash_table_iter_init(&hashiter, sms->messages); while (g_hash_table_iter_next(&hashiter, &key, &value)) { m = value; uuid = message_get_uuid(m); path = __ofono_sms_message_path_from_uuid(sms, uuid); dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); message_append_properties(m, &dict); dbus_message_iter_close_container(&entry, &dict); dbus_message_iter_close_container(&array, &entry); } dbus_message_iter_close_container(&iter, &array); return reply; } static gint entry_compare_by_uuid(gconstpointer a, gconstpointer b) { const struct tx_queue_entry *entry = a; const struct ofono_uuid *uuid = b; return memcmp(&entry->uuid, uuid, sizeof(entry->uuid)); } int __ofono_sms_txq_cancel(struct ofono_sms *sms, const struct ofono_uuid *uuid) { GList *l; struct tx_queue_entry *entry; l = g_queue_find_custom(sms->txq, uuid, entry_compare_by_uuid); if (l == NULL) return -ENOENT; entry = l->data; if (entry == g_queue_peek_head(sms->txq)) { /* * Fail if any pdu was already transmitted or if we are * waiting the answer from driver. */ if (entry->cur_pdu > 0) return -EPERM; if (sms->flags & MESSAGE_MANAGER_FLAG_TXQ_ACTIVE) return -EPERM; /* * Make sure we don't call tx_next() if there are no entries * and that next entry doesn't have to wait a 'retry time' * from this one. */ if (sms->tx_source) { g_source_remove(sms->tx_source); sms->tx_source = 0; if (g_queue_get_length(sms->txq) > 1) sms->tx_source = g_timeout_add(0, tx_next, sms); } } sms_tx_queue_remove_entry(sms, l, MESSAGE_STATE_CANCELLED); return 0; } static const GDBusMethodTable sms_manager_methods[] = { { GDBUS_ASYNC_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), sms_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, sms_set_property) }, { GDBUS_ASYNC_METHOD("SendMessage", GDBUS_ARGS({ "to", "s" }, { "text", "s" }), GDBUS_ARGS({ "path", "o" }), sms_send_message) }, { GDBUS_METHOD("GetMessages", NULL, GDBUS_ARGS({ "messages", "a(oa{sv})" }), sms_get_messages) }, { } }; static const GDBusSignalTable sms_manager_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { GDBUS_SIGNAL("IncomingMessage", GDBUS_ARGS({ "message", "s" }, { "info", "a{sv}" })) }, { GDBUS_SIGNAL("ImmediateMessage", GDBUS_ARGS({ "message", "s" }, { "info", "a{sv}" })) }, { GDBUS_SIGNAL("MessageAdded", GDBUS_ARGS({ "path", "o" }, { "properties", "a{sv}" })) }, { GDBUS_SIGNAL("MessageRemoved", GDBUS_ARGS({ "path", "o" })) }, { } }; static gboolean compute_incoming_msgid(GSList *sms_list, struct ofono_uuid *uuid) { GChecksum *checksum; GSList *l; const struct sms *s; unsigned char buf[176]; gsize uuid_size = sizeof(uuid->uuid); int len; checksum = g_checksum_new(G_CHECKSUM_SHA1); if (checksum == NULL) return FALSE; for (l = sms_list; l; l = l->next) { s = l->data; if (sms_encode(s, &len, NULL, buf) == FALSE) { g_checksum_free(checksum); return FALSE; } g_checksum_update(checksum, buf, len); } g_checksum_get_digest(checksum, uuid->uuid, &uuid_size); g_checksum_free(checksum); return TRUE; } static void dispatch_app_datagram(struct ofono_sms *sms, const struct ofono_uuid *uuid, int dst, int src, unsigned char *buf, unsigned len, const struct sms_address *addr, const struct sms_scts *scts) { const char *sender = sms_address_to_string(addr); time_t ts; struct tm remote; struct tm local; ofono_sms_datagram_notify_cb_t notify; struct sms_handler *h; GSList *l; gboolean dispatched = FALSE; ts = sms_scts_to_time(scts, &remote); localtime_r(&ts, &local); for (l = sms->datagram_handlers->items; l; l = l->next) { h = l->data; notify = h->item.notify; if (!port_equal(dst, h->dst) || !port_equal(src, h->src)) continue; dispatched = TRUE; notify(sender, &remote, &local, dst, src, buf, len, h->item.notify_data); } if (!dispatched) ofono_info("Datagram with ports [%d,%d] not delivered", dst, src); } static void dispatch_text_message(struct ofono_sms *sms, const struct ofono_uuid *uuid, const char *message, enum sms_class cls, const struct sms_address *addr, const struct sms_scts *scts) { struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom); DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sms->atom); DBusMessage *signal; DBusMessageIter iter; DBusMessageIter dict; char buf[128]; const char *signal_name; time_t ts; struct tm remote; struct tm local; const char *str = buf; ofono_sms_text_notify_cb_t notify; struct sms_handler *h; GSList *l; if (message == NULL) return; if (cls == SMS_CLASS_0) signal_name = "ImmediateMessage"; else signal_name = "IncomingMessage"; signal = dbus_message_new_signal(path, OFONO_MESSAGE_MANAGER_INTERFACE, signal_name); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &message); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ts = sms_scts_to_time(scts, &remote); localtime_r(&ts, &local); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", &local); buf[127] = '\0'; ofono_dbus_dict_append(&dict, "LocalSentTime", DBUS_TYPE_STRING, &str); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", &remote); buf[127] = '\0'; ofono_dbus_dict_append(&dict, "SentTime", DBUS_TYPE_STRING, &str); str = sms_address_to_string(addr); ofono_dbus_dict_append(&dict, "Sender", DBUS_TYPE_STRING, &str); dbus_message_iter_close_container(&iter, &dict); g_dbus_send_message(conn, signal); if (cls == SMS_CLASS_0) return; for (l = sms->text_handlers->items; l; l = l->next) { h = l->data; notify = h->item.notify; notify(str, &remote, &local, message, h->item.notify_data); } __ofono_history_sms_received(modem, uuid, str, &remote, &local, message); } static void sms_dispatch(struct ofono_sms *sms, GSList *sms_list) { GSList *l; const struct sms *s; struct ofono_uuid uuid; enum sms_charset uninitialized_var(old_charset); enum sms_class cls; int srcport = -1; int dstport = -1; DBG(""); if (sms_list == NULL) return; /* * Qutoting 23.040: The TP elements in the SMS‑SUBMIT PDU, apart from * TP‑MR, TP-SRR, TP‑UDL and TP‑UD, should remain unchanged for each * SM which forms part of a concatenated SM, otherwise this may lead * to irrational behaviour * * This means that we assume that at least the charset is the same * across all parts of the SMS in the case of 8-bit data. Other * cases can be handled by converting to UTF8. * * We also check that if 8-bit or 16-bit application addressing is * used, the addresses are the same across all segments. */ for (l = sms_list; l; l = l->next) { guint8 dcs; gboolean comp = FALSE; enum sms_charset charset; int cdst = -1; int csrc = -1; gboolean is_8bit; s = l->data; dcs = s->deliver.dcs; if (sms_mwi_dcs_decode(dcs, NULL, &charset, NULL, NULL)) cls = SMS_CLASS_UNSPECIFIED; else if (!sms_dcs_decode(dcs, &cls, &charset, &comp, NULL)) { ofono_error("The deliver DCS is not recognized"); return; } if (comp) { ofono_error("Compressed data not supported"); return; } if (l == sms_list) old_charset = charset; if (charset == SMS_CHARSET_8BIT && charset != old_charset) { ofono_error("Can't concatenate disparate charsets"); return; } if (sms_extract_app_port(s, &cdst, &csrc, &is_8bit)) { csrc = is_8bit ? (csrc << 16) : csrc; cdst = is_8bit ? (cdst << 16) : cdst; if (l == sms_list) { srcport = csrc; dstport = cdst; } } DBG("dst %d src %d", cdst, csrc); if (srcport != csrc || dstport != cdst) { ofono_error("Source / Destination ports across " "concatenated message are not the " "same, ignoring"); return; } } if (!compute_incoming_msgid(sms_list, &uuid)) return; s = sms_list->data; /* Handle datagram */ if (old_charset == SMS_CHARSET_8BIT) { unsigned char *buf; long len; if (srcport == -1 || dstport == -1) { ofono_error("Got an 8-bit encoded message, however " "no valid src/address port, ignore"); return; } buf = sms_decode_datagram(sms_list, &len); if (buf == NULL) return; dispatch_app_datagram(sms, &uuid, dstport, srcport, buf, len, &s->deliver.oaddr, &s->deliver.scts); g_free(buf); } else { char *message = sms_decode_text(sms_list); if (message == NULL) return; dispatch_text_message(sms, &uuid, message, cls, &s->deliver.oaddr, &s->deliver.scts); g_free(message); } } static void handle_deliver(struct ofono_sms *sms, const struct sms *incoming) { GSList *l; guint16 ref; guint8 max; guint8 seq; DBG(""); if (sms_extract_concatenation(incoming, &ref, &max, &seq)) { GSList *sms_list; if (sms->assembly == NULL) return; sms_list = sms_assembly_add_fragment(sms->assembly, incoming, time(NULL), &incoming->deliver.oaddr, ref, max, seq); if (sms_list == NULL) return; sms_dispatch(sms, sms_list); g_slist_foreach(sms_list, (GFunc) g_free, NULL); g_slist_free(sms_list); return; } l = g_slist_append(NULL, (void *) incoming); sms_dispatch(sms, l); g_slist_free(l); } static void handle_sms_status_report(struct ofono_sms *sms, const struct sms *incoming) { struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom); gboolean delivered; struct ofono_uuid uuid; DBG(""); if (status_report_assembly_report(sms->sr_assembly, incoming, uuid.uuid, &delivered) == FALSE) return; __ofono_history_sms_send_status(modem, &uuid, time(NULL), delivered ? OFONO_HISTORY_SMS_STATUS_DELIVERED : OFONO_HISTORY_SMS_STATUS_DELIVER_FAILED); } static inline gboolean handle_mwi(struct ofono_sms *sms, struct sms *s) { gboolean discard; DBG(""); if (sms->mw == NULL) return FALSE; __ofono_message_waiting_mwi(sms->mw, s, &discard); return discard; } void ofono_sms_deliver_notify(struct ofono_sms *sms, const unsigned char *pdu, int len, int tpdu_len) { struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom); struct ofono_sim *sim; struct ofono_stk *stk; struct sms s; enum sms_class cls; DBG("len %d tpdu len %d", len, tpdu_len); if (!sms_decode(pdu, len, FALSE, tpdu_len, &s)) { ofono_error("Unable to decode PDU"); return; } if (s.type != SMS_TYPE_DELIVER) { ofono_error("Expecting a DELIVER pdu"); return; } if (s.deliver.pid == SMS_PID_TYPE_SM_TYPE_0) { DBG("Explicitly ignoring type 0 SMS"); return; } /* * This is an older style MWI notification, process MWI * headers and handle it like any other message */ if (s.deliver.pid == SMS_PID_TYPE_RETURN_CALL) { if (handle_mwi(sms, &s)) return; goto out; } /* * The DCS indicates this is an MWI notification, process it * and then handle the User-Data as any other message */ if (sms_mwi_dcs_decode(s.deliver.dcs, NULL, NULL, NULL, NULL)) { if (handle_mwi(sms, &s)) return; goto out; } if (!sms_dcs_decode(s.deliver.dcs, &cls, NULL, NULL, NULL)) { ofono_error("Unknown / Reserved DCS. Ignoring"); return; } switch (s.deliver.pid) { case SMS_PID_TYPE_ME_DOWNLOAD: if (cls == SMS_CLASS_1) { ofono_error("ME Download message ignored"); return; } break; case SMS_PID_TYPE_ME_DEPERSONALIZATION: if (s.deliver.dcs == 0x11) { ofono_error("ME Depersonalization message ignored"); return; } break; case SMS_PID_TYPE_USIM_DOWNLOAD: case SMS_PID_TYPE_ANSI136: /* If not Class 2, handle in a "normal" way */ if (cls != SMS_CLASS_2) break; sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem); if (sim == NULL) return; if (!__ofono_sim_service_available(sim, SIM_UST_SERVICE_DATA_DOWNLOAD_SMS_PP, SIM_SST_SERVICE_DATA_DOWNLOAD_SMS_PP)) return; stk = __ofono_atom_find(OFONO_ATOM_TYPE_STK, modem); if (stk == NULL) return; __ofono_sms_sim_download(stk, &s, NULL, sms); /* * Passing the USIM response back to network is not * currently supported * * TODO: store in EFsms if not handled */ return; default: break; } /* * Check to see if the SMS has any other MWI related headers, * as sometimes they are "tacked on" by the SMSC. * While we're doing this we also check for messages containing * WCMP headers or headers that can't possibly be in a normal * message. If we find messages like that, we ignore them. */ if (s.deliver.udhi) { struct sms_udh_iter iter; enum sms_iei iei; if (!sms_udh_iter_init(&s, &iter)) goto out; while ((iei = sms_udh_iter_get_ie_type(&iter)) != SMS_IEI_INVALID) { if (iei > 0x25) { ofono_error("Reserved / Unknown / USAT" "header in use, ignore"); return; } switch (iei) { case SMS_IEI_SPECIAL_MESSAGE_INDICATION: case SMS_IEI_ENHANCED_VOICE_MAIL_INFORMATION: /* * TODO: ignore if not in the very first * segment of a concatenated SM so as not * to repeat the indication. */ if (handle_mwi(sms, &s)) return; goto out; case SMS_IEI_WCMP: ofono_error("No support for WCMP, ignoring"); return; default: sms_udh_iter_next(&iter); } } } out: handle_deliver(sms, &s); } void ofono_sms_status_notify(struct ofono_sms *sms, const unsigned char *pdu, int len, int tpdu_len) { struct sms s; enum sms_class cls; DBG("len %d tpdu len %d", len, tpdu_len); if (!sms_decode(pdu, len, FALSE, tpdu_len, &s)) { ofono_error("Unable to decode PDU"); return; } if (s.type != SMS_TYPE_STATUS_REPORT) { ofono_error("Expecting a STATUS REPORT pdu"); return; } if (s.status_report.srq) { ofono_error("Waiting an answer to SMS-SUBMIT, not SMS-COMMAND"); return; } if (!sms_dcs_decode(s.status_report.dcs, &cls, NULL, NULL, NULL)) { ofono_error("Unknown / Reserved DCS. Ignoring"); return; } handle_sms_status_report(sms, &s); } int ofono_sms_driver_register(const struct ofono_sms_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_sms_driver_unregister(const struct ofono_sms_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void sms_unregister(struct ofono_atom *atom) { struct ofono_sms *sms = __ofono_atom_get_data(atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); g_dbus_unregister_interface(conn, path, OFONO_MESSAGE_MANAGER_INTERFACE); ofono_modem_remove_interface(modem, OFONO_MESSAGE_MANAGER_INTERFACE); if (sms->mw_watch) { __ofono_modem_remove_atom_watch(modem, sms->mw_watch); sms->mw_watch = 0; sms->mw = NULL; } if (sms->status_watch) { __ofono_netreg_remove_status_watch(sms->netreg, sms->status_watch); sms->status_watch = 0; } if (sms->netreg_watch) { __ofono_modem_remove_atom_watch(modem, sms->netreg_watch); sms->netreg_watch = 0; } sms->netreg = NULL; if (sms->messages) { GHashTableIter iter; struct message *m; gpointer key, value; g_hash_table_iter_init(&iter, sms->messages); while (g_hash_table_iter_next(&iter, &key, &value)) { m = value; message_dbus_unregister(m); } g_hash_table_destroy(sms->messages); sms->messages = NULL; } __ofono_watchlist_free(sms->text_handlers); sms->text_handlers = NULL; __ofono_watchlist_free(sms->datagram_handlers); sms->datagram_handlers = NULL; } static void sms_remove(struct ofono_atom *atom) { struct ofono_sms *sms = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (sms == NULL) return; if (sms->driver && sms->driver->remove) sms->driver->remove(sms); if (sms->tx_source) { g_source_remove(sms->tx_source); sms->tx_source = 0; } if (sms->assembly) { sms_assembly_free(sms->assembly); sms->assembly = NULL; } if (sms->txq) { g_queue_foreach(sms->txq, tx_queue_entry_destroy_foreach, NULL); g_queue_free(sms->txq); sms->txq = NULL; } if (sms->settings) { g_key_file_set_integer(sms->settings, SETTINGS_GROUP, "NextReference", sms->ref); g_key_file_set_boolean(sms->settings, SETTINGS_GROUP, "UseDeliveryReports", sms->use_delivery_reports); g_key_file_set_integer(sms->settings, SETTINGS_GROUP, "Bearer", sms->bearer); g_key_file_set_integer(sms->settings, SETTINGS_GROUP, "Alphabet", sms->alphabet); storage_close(sms->imsi, SETTINGS_STORE, sms->settings, TRUE); g_free(sms->imsi); sms->imsi = NULL; sms->settings = NULL; } if (sms->sr_assembly) { status_report_assembly_free(sms->sr_assembly); sms->sr_assembly = NULL; } g_free(sms); } /* * Create a SMS driver * * This creates a SMS driver that is hung off a @modem * object. However, for the driver to be used by the system, it has to * be registered with the oFono core using ofono_sms_register(). * * This is done once the modem driver determines that SMS is properly * supported by the hardware. */ struct ofono_sms *ofono_sms_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_sms *sms; GSList *l; if (driver == NULL) return NULL; sms = g_try_new0(struct ofono_sms, 1); if (sms == NULL) return NULL; sms->sca.type = 129; sms->ref = 1; sms->txq = g_queue_new(); sms->messages = g_hash_table_new(uuid_hash, uuid_equal); sms->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_SMS, sms_remove, sms); for (l = g_drivers; l; l = l->next) { const struct ofono_sms_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(sms, vendor, data) < 0) continue; sms->driver = drv; break; } return sms; } static void mw_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_sms *sms = data; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { sms->mw = NULL; return; } sms->mw = __ofono_atom_get_data(atom); } static void sms_load_settings(struct ofono_sms *sms, const char *imsi) { GError *error; sms->settings = storage_open(imsi, SETTINGS_STORE); if (sms->settings == NULL) return; sms->imsi = g_strdup(imsi); error = NULL; sms->ref = g_key_file_get_integer(sms->settings, SETTINGS_GROUP, "NextReference", &error); if (error || sms->ref > 65536) { g_error_free(error); sms->ref = 1; g_key_file_set_integer(sms->settings, SETTINGS_GROUP, "NextReference", sms->ref); } error = NULL; sms->use_delivery_reports = g_key_file_get_boolean(sms->settings, SETTINGS_GROUP, "UseDeliveryReports", &error); if (error) { g_error_free(error); g_key_file_set_boolean(sms->settings, SETTINGS_GROUP, "UseDeliveryReports", sms->use_delivery_reports); } error = NULL; sms->bearer = g_key_file_get_integer(sms->settings, SETTINGS_GROUP, "Bearer", &error); if (error || sms_bearer_to_string(sms->bearer) == NULL) { g_error_free(error); sms->bearer = 3; /* Default to CS then PS */ g_key_file_set_integer(sms->settings, SETTINGS_GROUP, "Bearer", sms->bearer); } error = NULL; sms->alphabet = g_key_file_get_integer(sms->settings, SETTINGS_GROUP, "Alphabet", &error); if (error || sms_alphabet_to_string(sms->alphabet) == NULL) { g_error_free(error); sms->alphabet = SMS_ALPHABET_DEFAULT; g_key_file_set_integer(sms->settings, SETTINGS_GROUP, "Alphabet", sms->alphabet); } } static void bearer_init_callback(const struct ofono_error *error, void *data) { if (error->type != OFONO_ERROR_TYPE_NO_ERROR) ofono_error("Error bootstrapping SMS Bearer Preference"); } static void sms_restore_tx_queue(struct ofono_sms *sms) { GQueue *backupq; struct txq_backup_entry *backup_entry; DBG(""); backupq = sms_tx_queue_load(sms->imsi); if (backupq == NULL) return; while ((backup_entry = g_queue_pop_head(backupq))) { struct message *m; struct tx_queue_entry *txq_entry; backup_entry->flags |= OFONO_SMS_SUBMIT_FLAG_REUSE_UUID; txq_entry = tx_queue_entry_new(backup_entry->msg_list, backup_entry->flags); if (txq_entry == NULL) goto loop_out; txq_entry->flags &= ~OFONO_SMS_SUBMIT_FLAG_REUSE_UUID; memcpy(&txq_entry->uuid.uuid, &backup_entry->uuid, SMS_MSGID_LEN); m = message_create(&txq_entry->uuid, sms->atom); if (m == NULL) { tx_queue_entry_destroy(txq_entry); goto loop_out; } if (message_dbus_register(m) == FALSE) { tx_queue_entry_destroy(txq_entry); goto loop_out; } message_set_data(m, txq_entry); g_hash_table_insert(sms->messages, &txq_entry->uuid, m); txq_entry->id = sms->tx_counter++; g_queue_push_tail(sms->txq, txq_entry); loop_out: g_slist_foreach(backup_entry->msg_list, (GFunc)g_free, NULL); g_slist_free(backup_entry->msg_list); g_free(backup_entry); } if (g_queue_get_length(sms->txq) > 0) sms->tx_source = g_timeout_add(0, tx_next, sms); g_queue_free(backupq); } /* * Indicate oFono that a SMS driver is ready for operation * * This is called after ofono_sms_create() was done and the modem * driver determined that a modem supports SMS correctly. Once this * call succeeds, the D-BUS interface for SMS goes live. */ void ofono_sms_register(struct ofono_sms *sms) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(sms->atom); const char *path = __ofono_atom_get_path(sms->atom); struct ofono_sim *sim; if (!g_dbus_register_interface(conn, path, OFONO_MESSAGE_MANAGER_INTERFACE, sms_manager_methods, sms_manager_signals, NULL, sms, NULL)) { ofono_error("Could not create %s interface", OFONO_MESSAGE_MANAGER_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_MESSAGE_MANAGER_INTERFACE); sms->mw_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_MESSAGE_WAITING, mw_watch, sms, NULL); sms->netreg_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_NETREG, netreg_watch, sms, NULL); sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem); /* * If we have a sim atom, we can uniquely identify the SIM, * otherwise create an sms assembly which doesn't backup the fragment * store. */ if (sim) { const char *imsi; imsi = ofono_sim_get_imsi(sim); sms->assembly = sms_assembly_new(imsi); sms->sr_assembly = status_report_assembly_new(imsi); sms_load_settings(sms, imsi); } else { sms->assembly = sms_assembly_new(NULL); sms->sr_assembly = status_report_assembly_new(NULL); sms->bearer = 3; /* Default to CS then PS */ } if (sms->driver->bearer_set) sms->driver->bearer_set(sms, sms->bearer, bearer_init_callback, sms); sms_restore_tx_queue(sms); sms->text_handlers = __ofono_watchlist_new(g_free); sms->datagram_handlers = __ofono_watchlist_new(g_free); __ofono_atom_register(sms->atom, sms_unregister); } void ofono_sms_remove(struct ofono_sms *sms) { __ofono_atom_free(sms->atom); } void ofono_sms_set_data(struct ofono_sms *sms, void *data) { sms->driver_data = data; } void *ofono_sms_get_data(struct ofono_sms *sms) { return sms->driver_data; } unsigned short __ofono_sms_get_next_ref(struct ofono_sms *sms) { return sms->ref; } int __ofono_sms_txq_submit(struct ofono_sms *sms, GSList *list, unsigned int flags, struct ofono_uuid *uuid, ofono_sms_txq_queued_cb_t cb, void *data) { struct message *m = NULL; struct tx_queue_entry *entry; entry = tx_queue_entry_new(list, flags); if (entry == NULL) return -ENOMEM; if (flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS) { m = message_create(&entry->uuid, sms->atom); if (m == NULL) goto err; if (message_dbus_register(m) == FALSE) goto err; message_set_data(m, entry); g_hash_table_insert(sms->messages, &entry->uuid, m); } if (list->next != NULL) { if (sms->ref == 65536) sms->ref = 1; else sms->ref = sms->ref + 1; } entry->id = sms->tx_counter++; g_queue_push_tail(sms->txq, entry); if (sms->registered && g_queue_get_length(sms->txq) == 1) sms->tx_source = g_timeout_add(0, tx_next, sms); if (uuid) memcpy(uuid, &entry->uuid, sizeof(*uuid)); if (flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS) { const char *uuid_str; unsigned char i; uuid_str = ofono_uuid_to_str(&entry->uuid); for (i = 0; i < entry->num_pdus; i++) { struct pending_pdu *pdu; pdu = &entry->pdus[i]; sms_tx_backup_store(sms->imsi, entry->id, entry->flags, uuid_str, i, pdu->pdu, pdu->pdu_len, pdu->tpdu_len); } } if (cb) cb(sms, &entry->uuid, data); if (m && (flags & OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS)) message_emit_added(m, OFONO_MESSAGE_MANAGER_INTERFACE); return 0; err: tx_queue_entry_destroy(entry); return -EINVAL; } int __ofono_sms_txq_set_submit_notify(struct ofono_sms *sms, struct ofono_uuid *uuid, ofono_sms_txq_submit_cb_t cb, void *data, ofono_destroy_func destroy) { GList *l; struct tx_queue_entry *entry = g_queue_peek_tail(sms->txq); if (memcmp(&entry->uuid, uuid, sizeof(entry->uuid))) { l = g_queue_find_custom(sms->txq, uuid, entry_compare_by_uuid); if (l == NULL) return -ENOENT; entry = l->data; } tx_queue_entry_set_submit_notify(entry, cb, data, destroy); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/src/message-waiting.c0000644000015600001650000006524512671500024022432 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" #include "util.h" #include "simutil.h" #include "smsutil.h" struct mailbox_state { gboolean indication; unsigned char message_count; }; struct ofono_message_waiting { struct mailbox_state messages[5]; unsigned char efmwis_length; unsigned char efmbdn_length; unsigned char efmbdn_record_id[5]; unsigned int efmbdn_watch; unsigned char ef_cphs_mwis_length; unsigned char ef_cphs_mbdn_length; unsigned int ef_cphs_mbdn_watch; gboolean mbdn_not_provided; gboolean cphs_mbdn_not_provided; struct ofono_phone_number mailbox_number[5]; struct ofono_sim *sim; struct ofono_sim_context *sim_context; struct ofono_atom *atom; }; struct mbdn_set_request { struct ofono_message_waiting *mw; int mailbox; struct ofono_phone_number number; DBusMessage *msg; gboolean cphs; }; static const char *mw_message_waiting_property_name[5] = { "VoicemailWaiting", #if 0 "FaxWaiting", "EmailWaiting", "OtherWaiting", "VideomailWaiting", #endif }; static const char *mw_message_count_property_name[5] = { "VoicemailMessageCount", #if 0 "FaxMessageCount", "EmailMessageCount", "OtherMessageCount", "VideomailMessageCount", #endif }; static const char *mw_mailbox_property_name[5] = { "VoicemailMailboxNumber", #if 0 "FaxMailboxNumber", "EmailMailboxNumber", "OtherMailboxNumber", "VideomailMailboxNumber", #endif }; static const int mw_mailbox_to_cphs_record[5] = { 1, /* Line 1 mailbox */ 4, /* Fax mailbox */ 0, 3, /* Data mailbox */ 0, }; static void mbdn_set_cb(int ok, void *data); static DBusMessage *mw_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_message_waiting *mw = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; int i; dbus_bool_t indication; unsigned char count; const char *number; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); for (i = 0; i < 5; i++) { if (mw_message_waiting_property_name[i]) { indication = mw->messages[i].indication; ofono_dbus_dict_append(&dict, mw_message_waiting_property_name[i], DBUS_TYPE_BOOLEAN, &indication); } if (mw_message_count_property_name[i]) { count = mw->messages[i].message_count; ofono_dbus_dict_append(&dict, mw_message_count_property_name[i], DBUS_TYPE_BYTE, &count); } if (mw_mailbox_property_name[i]) { number = phone_number_to_string(&mw->mailbox_number[i]); ofono_dbus_dict_append(&dict, mw_mailbox_property_name[i], DBUS_TYPE_STRING, &number); } } dbus_message_iter_close_container(&iter, &dict); return reply; } static void cphs_mbdn_sync_cb(int ok, void *data) { struct mbdn_set_request *req = data; if (!ok) ofono_info("Failed to synchronize CPHS MBDN record"); g_free(req); } static DBusMessage *set_cphs_mbdn(struct ofono_message_waiting *mw, gboolean sync, int mailbox, const char *number, DBusMessage *msg) { struct mbdn_set_request *req; unsigned char efmbdn[255]; if ((mw->ef_cphs_mbdn_length && !mw_mailbox_to_cphs_record[mailbox]) || mw->cphs_mbdn_not_provided == TRUE) { if (msg) return __ofono_error_not_supported(msg); return NULL; } if (mw->ef_cphs_mbdn_length == 0) { if (msg) return __ofono_error_sim_not_ready(msg); return NULL; } req = g_new0(struct mbdn_set_request, 1); req->mw = mw; req->mailbox = mailbox; string_to_phone_number(number, &req->number); req->cphs = TRUE; sim_adn_build(efmbdn, req->mw->ef_cphs_mbdn_length, &req->number, NULL); if (ofono_sim_write(mw->sim_context, SIM_EF_CPHS_MBDN_FILEID, sync ? cphs_mbdn_sync_cb : mbdn_set_cb, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mailbox_to_cphs_record[mailbox], efmbdn, mw->ef_cphs_mbdn_length, req) == -1) { g_free(req); if (msg) return __ofono_error_failed(msg); } else req->msg = msg ? dbus_message_ref(msg) : NULL; return NULL; } static void mbdn_set_cb(int ok, void *data) { struct mbdn_set_request *req = data; struct ofono_phone_number *old = &req->mw->mailbox_number[req->mailbox]; const char *property; DBusMessage *reply = NULL; if (!ok) { if (req->msg) reply = __ofono_error_failed(req->msg); goto out; } if (req->msg) reply = dbus_message_new_method_return(req->msg); if (g_str_equal(req->number.number, old->number) && req->number.type == old->type) goto out; memcpy(old, &req->number, sizeof(struct ofono_phone_number)); property = mw_mailbox_property_name[req->mailbox]; if (property) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(req->mw->atom); const char *number; number = phone_number_to_string(old); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, property, DBUS_TYPE_STRING, &number); } /* * Make a single attempt at keeping the CPHS version of the file * in sync. */ if (req->cphs == FALSE) set_cphs_mbdn(req->mw, TRUE, req->mailbox, phone_number_to_string(&req->number), NULL); out: if (req->msg && reply) __ofono_dbus_pending_reply(&req->msg, reply); g_free(req); } static DBusMessage *set_mbdn(struct ofono_message_waiting *mw, int mailbox, const char *number, DBusMessage *msg) { struct mbdn_set_request *req; unsigned char efmbdn[255]; /* * If we have no 3GPP EFmbdn on the card, maybe the * CPHS version is available */ if ((mw->efmbdn_length > 0 && mw->efmbdn_record_id[mailbox] == 0) || mw->mbdn_not_provided == TRUE) return set_cphs_mbdn(mw, FALSE, mailbox, number, msg); if (mw->efmbdn_length == 0) { if (msg) return __ofono_error_sim_not_ready(msg); return NULL; } req = g_new0(struct mbdn_set_request, 1); req->mw = mw; req->mailbox = mailbox; string_to_phone_number(number, &req->number); req->cphs = FALSE; sim_adn_build(efmbdn, req->mw->efmbdn_length, &req->number, NULL); if (ofono_sim_write(req->mw->sim_context, SIM_EFMBDN_FILEID, mbdn_set_cb, OFONO_SIM_FILE_STRUCTURE_FIXED, req->mw->efmbdn_record_id[mailbox], efmbdn, req->mw->efmbdn_length, req) == -1) { g_free(req); if (msg) return __ofono_error_failed(msg); } else req->msg = msg ? dbus_message_ref(msg) : NULL; return NULL; } static DBusMessage *mw_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_message_waiting *mw = data; DBusMessageIter iter; DBusMessageIter var; const char *name, *value; int i; if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &name); for (i = 0; i < 5; i++) if (mw_mailbox_property_name[i] && !strcmp(name, mw_mailbox_property_name[i])) break; if (i < 5) { const char *cur_number; dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (!valid_phone_number_format(value)) return __ofono_error_invalid_format(msg); cur_number = phone_number_to_string(&mw->mailbox_number[i]); if (g_str_equal(cur_number, value)) return dbus_message_new_method_return(msg); return set_mbdn(mw, i, value, msg); } return __ofono_error_invalid_args(msg); } static const GDBusMethodTable message_waiting_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), mw_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, mw_set_property) }, { } }; static const GDBusSignalTable message_waiting_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static void update_indicator_and_emit(struct ofono_message_waiting *mw, int mailbox, struct mailbox_state *info) { dbus_bool_t indication; unsigned char count; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(mw->atom); if (mw->messages[mailbox].message_count == info->message_count && mw->messages[mailbox].indication == info->indication) return; memcpy(&mw->messages[mailbox], info, sizeof(struct mailbox_state)); indication = info->indication; count = info->message_count; if (mw_message_waiting_property_name[mailbox] == NULL) return; ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_message_waiting_property_name[mailbox], DBUS_TYPE_BOOLEAN, &indication); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_message_count_property_name[mailbox], DBUS_TYPE_BYTE, &count); } static void mw_cphs_mwis_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_message_waiting *mw = userdata; struct mailbox_state info; unsigned char indication; if (!ok || total_length < 1) { DBG("No CPHS MWIS on SIM"); mw->ef_cphs_mwis_length = 0; return; } mw->ef_cphs_mwis_length = total_length; if (mw->efmwis_length != 0) return; /* Read Line 1 indication */ indication = data[0] & 0xf; info.indication = (indication == 0xa); info.message_count = 0; update_indicator_and_emit(mw, 0, &info); if (total_length == 1) return; /* Read Fax indication */ indication = data[1] & 0xf; info.indication = (indication == 0xa); info.message_count = 0; update_indicator_and_emit(mw, 1, &info); /* Read Data indication, map to 'Other' */ indication = (data[1] >> 4) & 0xf; info.indication = (indication == 0xa); info.message_count = 0; update_indicator_and_emit(mw, 3, &info); } static void mw_mwis_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_message_waiting *mw = userdata; int i, status; struct mailbox_state info; if (!ok || record_length < 5) { ofono_error("Unable to read waiting messages numbers " "from SIM"); mw->efmwis_length = 0; return; } /* Handle only current identity (TODO: currently assumes first) */ if (record != 1) return; status = data[0]; data++; for (i = 0; i < 5 && i < record_length - 1; i++, data++) { info.indication = (status >> i) & 1; info.message_count = info.indication ? data[0] : 0; update_indicator_and_emit(mw, i, &info); } mw->efmwis_length = record_length; } static void mw_cphs_mbdn_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_message_waiting *mw = userdata; int i; DBusConnection *conn = ofono_dbus_get_connection(); const char *value; if (!ok || record_length < 14 || total_length < record_length) { ofono_error("Unable to read CPHS mailbox dialling numbers " "from SIM"); mw->ef_cphs_mbdn_length = 0; mw->cphs_mbdn_not_provided = TRUE; return; } for (i = 0; i < 5; i++) if (record == mw_mailbox_to_cphs_record[i]) break; if (i == 5) return; mw->ef_cphs_mbdn_length = record_length; if (mw->mbdn_not_provided != TRUE) return; ofono_info("3GPP MBDN not provided, parsing CPHS.."); if (sim_adn_parse(data, record_length, &mw->mailbox_number[i], NULL) == FALSE) mw->mailbox_number[i].number[0] = '\0'; if (mw_mailbox_property_name[i]) { const char *path = __ofono_atom_get_path(mw->atom); value = phone_number_to_string(&mw->mailbox_number[i]); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_mailbox_property_name[i], DBUS_TYPE_STRING, &value); } } static void mw_mbdn_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_message_waiting *mw = userdata; int i; DBusConnection *conn = ofono_dbus_get_connection(); const char *value; if (!ok || record_length < 14 || total_length < record_length) { ofono_error("Unable to read mailbox dialling numbers " "from SIM"); mw->efmbdn_length = 0; mw->mbdn_not_provided = TRUE; return; } for (i = 0; i < 5; i++) if (record == mw->efmbdn_record_id[i]) break; if (i == 5) return; if (sim_adn_parse(data, record_length, &mw->mailbox_number[i], NULL) == FALSE) mw->mailbox_number[i].number[0] = '\0'; if (mw_mailbox_property_name[i]) { const char *path = __ofono_atom_get_path(mw->atom); value = phone_number_to_string(&mw->mailbox_number[i]); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_mailbox_property_name[i], DBUS_TYPE_STRING, &value); } mw->efmbdn_length = record_length; } static void mw_mbdn_changed(int id, void *userdata) { struct ofono_message_waiting *mw = userdata; int err; mw->efmbdn_length = 0; mw->mbdn_not_provided = FALSE; err = ofono_sim_read(mw->sim_context, SIM_EFMBDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mbdn_read_cb, mw); if (err != 0) ofono_error("Unable to read EF-MBDN from SIM"); } static void mw_cphs_mbdn_changed(int id, void *userdata) { struct ofono_message_waiting *mw = userdata; mw->ef_cphs_mbdn_length = 0; mw->cphs_mbdn_not_provided = FALSE; ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MBDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_cphs_mbdn_read_cb, mw); } const struct ofono_phone_number *__ofono_message_waiting_get_mbdn( struct ofono_message_waiting *mw, unsigned int index) { return &mw->mailbox_number[index]; } static void mw_mbi_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_message_waiting *mw = userdata; int i, err; if (!ok || record_length < 4) { ofono_error("Unable to read mailbox identifies " "from SIM"); mw->efmbdn_length = 0; mw->mbdn_not_provided = TRUE; goto out; } /* Handle only current identity (TODO: currently assumes first) */ if (record != 1) return; for (i = 0; i < 5 && i < record_length; i++) mw->efmbdn_record_id[i] = data[i]; err = ofono_sim_read(mw->sim_context, SIM_EFMBDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mbdn_read_cb, mw); mw->efmbdn_watch = ofono_sim_add_file_watch(mw->sim_context, SIM_EFMBDN_FILEID, mw_mbdn_changed, mw, NULL); if (err != 0) ofono_error("Unable to read EF-MBDN from SIM"); out: /* * Mailbox numbers located in Byte 1, bits 6 & 5, * Check for Activated & Allocated */ if (__ofono_sim_cphs_service_available(mw->sim, SIM_CPHS_SERVICE_MAILBOX_NUMBERS)) { ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MBDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_cphs_mbdn_read_cb, mw); mw->ef_cphs_mbdn_watch = ofono_sim_add_file_watch( mw->sim_context, SIM_EF_CPHS_MBDN_FILEID, mw_cphs_mbdn_changed, mw, NULL); } } static void mw_mwis_write_cb(int ok, void *userdata) { if (!ok) ofono_error("Writing new EF-MWIS failed"); } static void mw_set_indicator(struct ofono_message_waiting *mw, int profile, enum sms_mwi_type type, gboolean present, unsigned char messages) { DBusConnection *conn = ofono_dbus_get_connection(); unsigned char efmwis[255]; /* Max record size */ int i; if (mw == NULL) return; /* Handle only current identity (TODO: currently assumes first) */ if (profile != 1) return; if (mw->messages[type].indication == present && mw->messages[type].message_count == messages) return; if (mw->messages[type].indication != present) { dbus_bool_t indication; const char *path = __ofono_atom_get_path(mw->atom); indication = present; mw->messages[type].indication = present; if (mw_message_waiting_property_name[type]) ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_message_waiting_property_name[type], DBUS_TYPE_BOOLEAN, &indication); } if (mw->messages[type].message_count != messages) { const char *path = __ofono_atom_get_path(mw->atom); mw->messages[type].message_count = messages; if (mw_message_waiting_property_name[type]) ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, mw_message_count_property_name[type], DBUS_TYPE_BYTE, &messages); } /* Writes MWI states and/or MBDN back to SIM */ if (mw->efmwis_length < 5) { if (mw->ef_cphs_mwis_length >= 1) goto try_cphs; ofono_error("Unable to update MWIS indicator"); return; } /* Fill in numbers of messages in bytes 1 to X of EF-MWIS */ for (i = 0; i < 5 && i < mw->efmwis_length - 1; i++) efmwis[i + 1] = mw->messages[i].message_count; /* Fill in indicator state bits in byte 0 */ efmwis[0] = 0; for (i = 0; i < 5 && i < mw->efmwis_length - 1; i++) if (mw->messages[i].indication) efmwis[0] |= 1 << i; if (ofono_sim_write(mw->sim_context, SIM_EFMWIS_FILEID, mw_mwis_write_cb, OFONO_SIM_FILE_STRUCTURE_FIXED, 1, efmwis, mw->efmwis_length, mw) != 0) { ofono_error("Queuing a EF-MWI write to SIM failed"); } if (mw->ef_cphs_mwis_length == 0) return; try_cphs: memset(efmwis, 0x55, 255); efmwis[0] = mw->messages[0].indication ? 0xa : 0x5; if (mw->ef_cphs_mwis_length > 1) efmwis[1] = mw->messages[1].indication ? 0xa : 0x5 | mw->messages[3].indication ? 0xa0 : 0x50; if (ofono_sim_write(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID, mw_mwis_write_cb, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, 0, efmwis, mw->ef_cphs_mwis_length, mw) != 0) ofono_error("Queuing a EF-MWIS write to SIM failed (CPHS)"); } static void handle_special_sms_iei(struct ofono_message_waiting *mw, const guint8 *iei, gboolean *discard) { enum sms_mwi_type type; int profile; gboolean set; /* Parse type & storage byte */ if (discard) *discard = (iei[0] & (1 << 7)) ? FALSE : TRUE; type = iei[0] & 0x1f; if (type > SMS_MWI_TYPE_OTHER) { if (type == (SMS_MWI_TYPE_OTHER | 4)) type = SMS_MWI_TYPE_VIDEO; else /* * 23.040 9.2.3.24.2: "Terminals should be capable of * receiving any values in octet 1, even including * those marked as Reserved." Treat Reserved as * "Other". */ type = SMS_MWI_TYPE_OTHER; } set = iei[1] > 0 ? TRUE : FALSE; profile = ((iei[0] >> 5) & 3) + 1; mw_set_indicator(mw, profile, type, set, iei[1]); } static void handle_enhanced_voicemail_iei(struct ofono_message_waiting *mw, const guint8 *iei, gboolean *discard, int length) { int profile, n; gboolean set; struct sms_address mailbox_address; if (length < 3) return; /* ENHANCED_VOICE_MAIL_PDU_TYPE */ if (!(iei[0] & 1)) { /* 9.2.3.24.13.1 Enhanced Voice Mail Notification */ /* MULTIPLE_SUBSCRIBER_PROFILE */ profile = ((iei[0] >> 2) & 3) + 1; /* SM_STORAGE */ if (discard) *discard = (iei[0] & (1 << 4)) ? FALSE : TRUE; /* VM_MAILBOX_ACCESS_ADDRESS */ n = 0; if (!sms_decode_address_field(iei + 1, length - 1, &n, FALSE, &mailbox_address)) return; /* TODO: VM_MESSAGE_PRIORITY_INDICATION */ /* Other parameters currently not supported */ if (length < n + 3) return; set = iei[n + 1] > 0 ? TRUE : FALSE; mw_set_indicator(mw, profile, SMS_MWI_TYPE_VOICE, set, iei[n + 1]); } else { /* 9.2.3.24.13.2 Enhanced Voice Delete Confirmation */ /* MULTIPLE_SUBSCRIBER_PROFILE */ profile = ((iei[0] >> 2) & 3) + 1; /* SM_STORAGE */ if (discard) *discard = (iei[0] & (1 << 4)) ? FALSE : TRUE; /* VM_MAILBOX_ACCESS_ADDRESS */ n = 0; if (!sms_decode_address_field(iei + 1, length - 1, &n, FALSE, &mailbox_address)) return; /* Other parameters currently not supported */ if (length < n + 3) return; set = iei[n + 1] > 0 ? TRUE : FALSE; mw_set_indicator(mw, profile, SMS_MWI_TYPE_VOICE, set, iei[n + 1]); } if (mailbox_address.address[0] != '\0') set_mbdn(mw, SMS_MWI_TYPE_VOICE, sms_address_to_string(&mailbox_address), NULL); } void __ofono_message_waiting_mwi(struct ofono_message_waiting *mw, struct sms *sms, gboolean *out_discard) { gboolean active, discard; enum sms_mwi_type type; int profile = 1, iei_found = 0; if (out_discard) *out_discard = FALSE; /* * Check MWI types in the order from highest priority to lowest * because they must override one another. */ if (sms->deliver.udhi) { guint8 evm_iei[140]; struct sms_udh_iter iter; enum sms_iei iei; if (!sms_udh_iter_init(sms, &iter)) return; while ((iei = sms_udh_iter_get_ie_type(&iter)) != SMS_IEI_INVALID) { switch (iei) { case SMS_IEI_ENHANCED_VOICE_MAIL_INFORMATION: sms_udh_iter_get_ie_data(&iter, evm_iei); handle_enhanced_voicemail_iei(mw, evm_iei, out_discard, sms_udh_iter_get_ie_length( &iter)); return; default: break; } sms_udh_iter_next(&iter); } } if (sms->deliver.udhi) { guint8 special_iei[4]; struct sms_udh_iter iter; enum sms_iei iei; if (!sms_udh_iter_init(sms, &iter)) return; while ((iei = sms_udh_iter_get_ie_type(&iter)) != SMS_IEI_INVALID) { switch (iei) { case SMS_IEI_SPECIAL_MESSAGE_INDICATION: if (sms_udh_iter_get_ie_length(&iter) != 2) break; sms_udh_iter_get_ie_data(&iter, special_iei); handle_special_sms_iei(mw, special_iei, &discard); if (out_discard) *out_discard = *out_discard || discard; iei_found = 1; break; default: break; } sms_udh_iter_next(&iter); } if (iei_found) { /* * 23.040 9.2.3.24.2 says "In the event of a * conflict between this setting and the setting * of the Data Coding Scheme (see 3GPP TS 23.038 [9]) * then the message shall be stored if either the DCS * indicates this, or Octet 1 above indicates this." */ if (sms_mwi_dcs_decode(sms->deliver.dcs, NULL, NULL, NULL, &discard)) { if (out_discard) *out_discard = *out_discard || discard; } return; } } if (sms_mwi_dcs_decode(sms->deliver.dcs, &type, NULL, &active, out_discard)) { mw_set_indicator(mw, profile, type, active, 0); return; } if (sms->deliver.pid == SMS_PID_TYPE_RETURN_CALL) return; } static void message_waiting_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); struct ofono_message_waiting *mw = __ofono_atom_get_data(atom); if (mw->sim_context) { ofono_sim_context_free(mw->sim_context); mw->sim_context = NULL; } mw->sim = NULL; g_dbus_unregister_interface(conn, path, OFONO_MESSAGE_WAITING_INTERFACE); ofono_modem_remove_interface(modem, OFONO_MESSAGE_WAITING_INTERFACE); } static void mw_mwis_changed(int id, void *userdata) { struct ofono_message_waiting *mw = userdata; mw->efmwis_length = 0; ofono_sim_read(mw->sim_context, SIM_EFMWIS_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mwis_read_cb, mw); } static void mw_cphs_mwis_changed(int id, void *userdata) { struct ofono_message_waiting *mw = userdata; mw->ef_cphs_mwis_length = 0; ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, mw_cphs_mwis_read_cb, mw); } static void mw_mbi_changed(int id, void *userdata) { struct ofono_message_waiting *mw = userdata; mw->efmbdn_length = 0; mw->mbdn_not_provided = FALSE; mw->ef_cphs_mbdn_length = 0; mw->cphs_mbdn_not_provided = FALSE; ofono_sim_remove_file_watch(mw->sim_context, mw->efmbdn_watch); ofono_sim_remove_file_watch(mw->sim_context, mw->ef_cphs_mbdn_watch); ofono_sim_read(mw->sim_context, SIM_EFMBI_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mbi_read_cb, mw); } void ofono_message_waiting_register(struct ofono_message_waiting *mw) { DBusConnection *conn; const char *path; struct ofono_modem *modem; if (mw == NULL) return; conn = ofono_dbus_get_connection(); modem = __ofono_atom_get_modem(mw->atom); path = __ofono_atom_get_path(mw->atom); if (!g_dbus_register_interface(conn, path, OFONO_MESSAGE_WAITING_INTERFACE, message_waiting_methods, message_waiting_signals, NULL, mw, NULL)) { ofono_error("Could not create %s interface", OFONO_MESSAGE_WAITING_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_MESSAGE_WAITING_INTERFACE); mw->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem); if (mw->sim) { /* Assume that if sim atom exists, it is ready */ mw->sim_context = ofono_sim_context_create(mw->sim); /* Loads MWI states and MBDN from SIM */ ofono_sim_read(mw->sim_context, SIM_EFMWIS_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mwis_read_cb, mw); ofono_sim_read(mw->sim_context, SIM_EFMBI_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, mw_mbi_read_cb, mw); /* Also read CPHS MWIS field */ ofono_sim_read(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, mw_cphs_mwis_read_cb, mw); /* * The operator could send us SMS mwi updates, but let's be * extra careful and track the file contents too. */ ofono_sim_add_file_watch(mw->sim_context, SIM_EFMWIS_FILEID, mw_mwis_changed, mw, NULL); ofono_sim_add_file_watch(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID, mw_cphs_mwis_changed, mw, NULL); ofono_sim_add_file_watch(mw->sim_context, SIM_EFMBI_FILEID, mw_mbi_changed, mw, NULL); } __ofono_atom_register(mw->atom, message_waiting_unregister); } static void mw_remove(struct ofono_atom *atom) { struct ofono_message_waiting *mw = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (mw == NULL) return; g_free(mw); } struct ofono_message_waiting *ofono_message_waiting_create(struct ofono_modem *modem) { struct ofono_message_waiting *mw; mw = g_try_new0(struct ofono_message_waiting, 1); if (mw == NULL) return NULL; mw->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_MESSAGE_WAITING, mw_remove, mw); return mw; } void ofono_message_waiting_remove(struct ofono_message_waiting *mw) { __ofono_atom_free(mw->atom); } ofono-1.17.bzr6912+16.04.20160314.3/src/gnss.c0000644000015600001650000002132412671500024020306 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 ST-Ericsson AB. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" #include "gnssagent.h" static GSList *g_drivers = NULL; struct ofono_gnss { const struct ofono_gnss_driver *driver; void *driver_data; struct ofono_atom *atom; DBusMessage *pending; struct gnss_agent *posr_agent; ofono_bool_t enabled; }; static void gnss_unregister_agent_cb(const struct ofono_error *error, void *data) { DBusMessage *reply; struct ofono_gnss *gnss = data; DBG(""); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) ofono_error("Disabling Location Reporting Failed"); gnss->enabled = FALSE; if (gnss->posr_agent) gnss_agent_free(gnss->posr_agent); reply = dbus_message_new_method_return(gnss->pending); __ofono_dbus_pending_reply(&gnss->pending, reply); } static void gnss_disable_posr_cb(const struct ofono_error *error, void *data) { struct ofono_gnss *gnss = data; gnss->enabled = FALSE; } static void gnss_register_agent_cb(const struct ofono_error *error, void *data) { DBusMessage *reply; struct ofono_gnss *gnss = data; DBG(""); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Enabling Location Reporting Failed"); reply = __ofono_error_failed(gnss->pending); if (gnss->posr_agent) gnss_agent_free(gnss->posr_agent); __ofono_dbus_pending_reply(&gnss->pending, reply); return; } reply = dbus_message_new_method_return(gnss->pending); __ofono_dbus_pending_reply(&gnss->pending, reply); gnss->enabled = TRUE; if (gnss->posr_agent == NULL) gnss->driver->set_position_reporting(gnss, FALSE, gnss_disable_posr_cb, gnss); } static void gnss_agent_notify(gpointer user_data) { struct ofono_gnss *gnss = user_data; gnss->posr_agent = NULL; if (gnss->enabled == TRUE) gnss->driver->set_position_reporting(gnss, FALSE, gnss_disable_posr_cb, gnss); } static DBusMessage *gnss_register_agent(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_gnss *gnss = data; const char *agent_path; if (gnss->pending) return __ofono_error_busy(msg); if (gnss->posr_agent) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!__ofono_dbus_valid_object_path(agent_path)) return __ofono_error_invalid_format(msg); gnss->posr_agent = gnss_agent_new(agent_path, dbus_message_get_sender(msg)); if (gnss->posr_agent == NULL) return __ofono_error_failed(msg); gnss_agent_set_removed_notify(gnss->posr_agent, gnss_agent_notify, gnss); gnss->driver->set_position_reporting(gnss, TRUE, gnss_register_agent_cb, gnss); gnss->pending = dbus_message_ref(msg); return NULL; } static DBusMessage *gnss_unregister_agent(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_gnss *gnss = data; const char *agent_path; const char *agent_bus = dbus_message_get_sender(msg); if (gnss->pending) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (gnss->posr_agent == NULL) return __ofono_error_failed(msg); if (!gnss_agent_matches(gnss->posr_agent, agent_path, agent_bus)) return __ofono_error_access_denied(msg); gnss->pending = dbus_message_ref(msg); gnss->enabled = FALSE; gnss->driver->set_position_reporting(gnss, FALSE, gnss_unregister_agent_cb, gnss); return NULL; } static void gnss_send_element_cb(const struct ofono_error *error, void *data) { DBusMessage *reply; struct ofono_gnss *gnss = data; DBG(""); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Sending Positioning Element failed"); reply = __ofono_error_failed(gnss->pending); } else reply = dbus_message_new_method_return(gnss->pending); __ofono_dbus_pending_reply(&gnss->pending, reply); } static DBusMessage *gnss_send_element(DBusConnection *conn, DBusMessage *msg, void *data) { const char *caller = dbus_message_get_sender(msg); struct ofono_gnss *gnss = data; const char *xml; DBG(""); if (gnss->pending) return __ofono_error_busy(msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)) return __ofono_error_invalid_args(msg); if (gnss->posr_agent == NULL) return __ofono_error_not_available(msg); if (!gnss_agent_sender_matches(gnss->posr_agent, caller)) return __ofono_error_access_denied(msg); gnss->pending = dbus_message_ref(msg); gnss->driver->send_element(gnss, xml, gnss_send_element_cb, gnss); return NULL; } static const GDBusMethodTable gnss_methods[] = { { GDBUS_ASYNC_METHOD("SendPositioningElement", GDBUS_ARGS({ "xml_element" "s" }), NULL, gnss_send_element) }, { GDBUS_ASYNC_METHOD("RegisterPositioningRequestAgent", GDBUS_ARGS({ "agent", "o" }), NULL, gnss_register_agent) }, { GDBUS_ASYNC_METHOD("UnregisterPositioningRequestAgent", GDBUS_ARGS({ "agent", "o" }), NULL, gnss_unregister_agent) }, { } }; static void gnss_unregister(struct ofono_atom *atom) { struct ofono_gnss *gnss = __ofono_atom_get_data(atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); if (gnss->posr_agent) gnss_agent_free(gnss->posr_agent); ofono_modem_remove_interface(modem, OFONO_GNSS_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_GNSS_INTERFACE); } static void gnss_remove(struct ofono_atom *atom) { struct ofono_gnss *gnss = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (gnss == NULL) return; if (gnss->driver && gnss->driver->remove) gnss->driver->remove(gnss); g_free(gnss); } void ofono_gnss_register(struct ofono_gnss *gnss) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(gnss->atom); const char *path = __ofono_atom_get_path(gnss->atom); if (!g_dbus_register_interface(conn, path, OFONO_GNSS_INTERFACE, gnss_methods, NULL, NULL, gnss, NULL)) { ofono_error("Could not create %s interface", OFONO_GNSS_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_GNSS_INTERFACE); __ofono_atom_register(gnss->atom, gnss_unregister); } int ofono_gnss_driver_register(const struct ofono_gnss_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_gnss_driver_unregister(const struct ofono_gnss_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } struct ofono_gnss *ofono_gnss_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_gnss *gnss; GSList *l; if (driver == NULL) return NULL; gnss = g_try_new0(struct ofono_gnss, 1); if (gnss == NULL) return NULL; gnss->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_GNSS, gnss_remove, gnss); for (l = g_drivers; l; l = l->next) { const struct ofono_gnss_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(gnss, vendor, data) < 0) continue; gnss->driver = drv; break; } return gnss; } void ofono_gnss_notify_posr_request(struct ofono_gnss *gnss, const char *xml) { if (gnss->posr_agent) gnss_agent_receive_request(gnss->posr_agent, xml); } void ofono_gnss_notify_posr_reset(struct ofono_gnss *gnss) { if (gnss->posr_agent) gnss_agent_receive_reset(gnss->posr_agent); } void ofono_gnss_remove(struct ofono_gnss *gnss) { __ofono_atom_free(gnss->atom); } void ofono_gnss_set_data(struct ofono_gnss *gnss, void *data) { gnss->driver_data = data; } void *ofono_gnss_get_data(struct ofono_gnss *gnss) { return gnss->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/call-meter.c0000644000015600001650000004534712671500024021374 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include "ofono.h" #include "common.h" #define CALL_METER_FLAG_CACHED 0x1 #define CALL_METER_FLAG_HAVE_PUCT 0x2 static GSList *g_drivers = NULL; struct ofono_call_meter { int flags; DBusMessage *pending; int call_meter; int acm; int acm_max; double ppu; char currency[4]; const struct ofono_call_meter_driver *driver; void *driver_data; struct ofono_atom *atom; }; static void set_call_meter(struct ofono_call_meter *cm, int value) { DBusConnection *conn; const char *path; if (cm->call_meter == value) return; cm->call_meter = value; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cm->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_METER_INTERFACE, "CallMeter", DBUS_TYPE_UINT32, &cm->call_meter); } static void set_acm(struct ofono_call_meter *cm, int value) { DBusConnection *conn; const char *path; if (cm->acm == value) return; cm->acm = value; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cm->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_METER_INTERFACE, "AccumulatedCallMeter", DBUS_TYPE_UINT32, &cm->acm); } static void set_acm_max(struct ofono_call_meter *cm, int value) { DBusConnection *conn; const char *path; if (cm->acm_max == value) return; cm->acm_max = value; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cm->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_METER_INTERFACE, "AccumulatedCallMeterMaximum", DBUS_TYPE_UINT32, &cm->acm_max); } static void set_ppu(struct ofono_call_meter *cm, double value) { DBusConnection *conn; const char *path; if (cm->ppu == value) return; cm->ppu = value; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cm->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_METER_INTERFACE, "PricePerUnit", DBUS_TYPE_DOUBLE, &cm->ppu); } static void set_currency(struct ofono_call_meter *cm, const char *value) { DBusConnection *conn; const char *path; const char *dbusval; if (strlen(value) > 3) { ofono_error("Currency reported with size > 3: %s", value); return; } if (!strcmp(cm->currency, value)) return; strncpy(cm->currency, value, 3); cm->currency[3] = '\0'; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cm->atom); dbusval = cm->currency; ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_METER_INTERFACE, "Currency", DBUS_TYPE_STRING, &dbusval); } static void cm_get_properties_reply(struct ofono_call_meter *cm) { DBusMessage *reply; DBusMessageIter iter, dict; const char *currency = cm->currency; reply = dbus_message_new_method_return(cm->pending); if (reply == NULL) return; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "CallMeter", DBUS_TYPE_UINT32, &cm->call_meter); ofono_dbus_dict_append(&dict, "AccumulatedCallMeter", DBUS_TYPE_UINT32, &cm->acm); ofono_dbus_dict_append(&dict, "AccumulatedCallMeterMaximum", DBUS_TYPE_UINT32, &cm->acm_max); ofono_dbus_dict_append(&dict, "PricePerUnit", DBUS_TYPE_DOUBLE, &cm->ppu); ofono_dbus_dict_append(&dict, "Currency", DBUS_TYPE_STRING, ¤cy); dbus_message_iter_close_container(&iter, &dict); __ofono_dbus_pending_reply(&cm->pending, reply); } static void query_call_meter_callback(const struct ofono_error *error, int value, void *data) { struct ofono_call_meter *cm = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) set_call_meter(cm, value); if (cm->pending) cm_get_properties_reply(cm); } static void query_call_meter(struct ofono_call_meter *cm) { if (cm->driver->call_meter_query == NULL) { if (cm->pending) cm_get_properties_reply(cm); return; } cm->driver->call_meter_query(cm, query_call_meter_callback, cm); } static void query_acm_callback(const struct ofono_error *error, int value, void *data) { struct ofono_call_meter *cm = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) set_acm(cm, value); query_call_meter(cm); } static void query_acm(struct ofono_call_meter *cm) { if (cm->driver->acm_query == NULL) { query_call_meter(cm); return; } cm->driver->acm_query(cm, query_acm_callback, cm); } static void query_acm_max_callback(const struct ofono_error *error, int value, void *data) { struct ofono_call_meter *cm = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) set_acm_max(cm, value); cm->flags |= CALL_METER_FLAG_CACHED; query_acm(cm); } static void query_acm_max(struct ofono_call_meter *cm) { if (cm->driver->acm_max_query == NULL) { cm->flags |= CALL_METER_FLAG_CACHED; query_acm(cm); return; } cm->driver->acm_max_query(cm, query_acm_max_callback, cm); } static void query_puct_callback(const struct ofono_error *error, const char *currency, double ppu, void *data) { struct ofono_call_meter *cm = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) { cm->flags |= CALL_METER_FLAG_HAVE_PUCT; set_currency(cm, currency); set_ppu(cm, ppu); } query_acm_max(cm); } static void query_puct(struct ofono_call_meter *cm) { if (cm->driver->puct_query == NULL) query_acm_max(cm); else cm->driver->puct_query(cm, query_puct_callback, cm); } static DBusMessage *cm_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_meter *cm = data; if (cm->pending) return __ofono_error_busy(msg); cm->pending = dbus_message_ref(msg); /* * We don't need to query ppu, currency & acm_max every time * Not sure if we have to query acm & call_meter every time * so lets play on the safe side and query them. They should be * fast to query anyway */ if (cm->flags & CALL_METER_FLAG_CACHED) query_acm(cm); else query_puct(cm); return NULL; } static void set_acm_max_query_callback(const struct ofono_error *error, int value, void *data) { struct ofono_call_meter *cm = data; DBusMessage *reply; if (cm->pending == NULL) return; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Setting acm_max successful, but query was not"); cm->flags &= ~CALL_METER_FLAG_CACHED; __ofono_dbus_pending_reply(&cm->pending, __ofono_error_failed(cm->pending)); return; } reply = dbus_message_new_method_return(cm->pending); __ofono_dbus_pending_reply(&cm->pending, reply); set_acm_max(cm, value); } static void check_pin2_state(struct ofono_call_meter *cm) { struct ofono_atom *sim_atom; sim_atom = __ofono_modem_find_atom(__ofono_atom_get_modem(cm->atom), OFONO_ATOM_TYPE_SIM); if (sim_atom == NULL) return; __ofono_sim_recheck_pin(__ofono_atom_get_data(sim_atom)); } static void set_acm_max_callback(const struct ofono_error *error, void *data) { struct ofono_call_meter *cm = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Setting acm_max failed"); __ofono_dbus_pending_reply(&cm->pending, __ofono_error_failed(cm->pending)); check_pin2_state(cm); return; } /* Assume if we have acm_reset, we have acm_query */ cm->driver->acm_max_query(cm, set_acm_max_query_callback, cm); } static DBusMessage *prop_set_acm_max(DBusMessage *msg, struct ofono_call_meter *cm, DBusMessageIter *dbus_value, const char *pin2) { dbus_uint32_t value; if (cm->driver->acm_max_set == NULL) return __ofono_error_not_implemented(msg); dbus_message_iter_get_basic(dbus_value, &value); cm->pending = dbus_message_ref(msg); cm->driver->acm_max_set(cm, value, pin2, set_acm_max_callback, cm); return NULL; } static void set_puct_query_callback(const struct ofono_error *error, const char *currency, double ppu, void *data) { struct ofono_call_meter *cm = data; DBusMessage *reply; if (cm->pending == NULL) return; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Setting PUCT successful, but query was not"); cm->flags &= ~CALL_METER_FLAG_CACHED; __ofono_dbus_pending_reply(&cm->pending, __ofono_error_failed(cm->pending)); return; } reply = dbus_message_new_method_return(cm->pending); __ofono_dbus_pending_reply(&cm->pending, reply); set_currency(cm, currency); set_ppu(cm, ppu); } static void set_puct_callback(const struct ofono_error *error, void *data) { struct ofono_call_meter *cm = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("setting puct failed"); __ofono_dbus_pending_reply(&cm->pending, __ofono_error_failed(cm->pending)); check_pin2_state(cm); return; } /* Assume if we have puct_set, we have puct_query */ cm->driver->puct_query(cm, set_puct_query_callback, cm); } /* * This function is for the really bizarre case of someone trying to call * SetProperty before GetProperties. But we must handle it... */ static void set_puct_initial_query_callback(const struct ofono_error *error, const char *currency, double ppu, void *data) { struct ofono_call_meter *cm = data; DBusMessageIter iter; DBusMessageIter var; const char *name; const char *pin2; if (cm->pending == NULL) return; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { __ofono_dbus_pending_reply(&cm->pending, __ofono_error_failed(cm->pending)); return; } set_currency(cm, currency); set_ppu(cm, ppu); cm->flags |= CALL_METER_FLAG_HAVE_PUCT; dbus_message_iter_init(cm->pending, &iter); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &var); dbus_message_iter_next(&iter); dbus_message_iter_get_basic(&iter, &pin2); if (!strcmp(name, "PricePerUnit")) dbus_message_iter_get_basic(&var, &ppu); else dbus_message_iter_get_basic(&var, ¤cy); cm->driver->puct_set(cm, currency, ppu, pin2, set_puct_callback, cm); } static DBusMessage *prop_set_ppu(DBusMessage *msg, struct ofono_call_meter *cm, DBusMessageIter *var, const char *pin2) { double ppu; if (cm->driver->puct_set == NULL || cm->driver->puct_query == NULL) return __ofono_error_not_implemented(msg); dbus_message_iter_get_basic(var, &ppu); if (ppu < 0.0) return __ofono_error_invalid_format(msg); cm->pending = dbus_message_ref(msg); if (cm->flags & CALL_METER_FLAG_HAVE_PUCT) cm->driver->puct_set(cm, cm->currency, ppu, pin2, set_puct_callback, cm); else cm->driver->puct_query(cm, set_puct_initial_query_callback, cm); return NULL; } static DBusMessage *prop_set_cur(DBusMessage *msg, struct ofono_call_meter *cm, DBusMessageIter *var, const char *pin2) { const char *value; if (cm->driver->puct_set == NULL || cm->driver->puct_query == NULL) return __ofono_error_not_implemented(msg); dbus_message_iter_get_basic(var, &value); if (strlen(value) > 3) return __ofono_error_invalid_format(msg); cm->pending = dbus_message_ref(msg); if (cm->flags & CALL_METER_FLAG_HAVE_PUCT) cm->driver->puct_set(cm, value, cm->ppu, pin2, set_puct_callback, cm); else cm->driver->puct_query(cm, set_puct_initial_query_callback, cm); return NULL; } struct call_meter_property { const char *name; int type; DBusMessage* (*set)(DBusMessage *msg, struct ofono_call_meter *cm, DBusMessageIter *var, const char *pin2); }; static struct call_meter_property cm_properties[] = { { "AccumulatedCallMeterMaximum",DBUS_TYPE_UINT32, prop_set_acm_max }, { "PricePerUnit", DBUS_TYPE_DOUBLE, prop_set_ppu }, { "Currency", DBUS_TYPE_STRING, prop_set_cur }, { NULL, 0, 0 }, }; static DBusMessage *cm_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_meter *cm = data; DBusMessageIter iter; DBusMessageIter var; const char *name, *passwd = ""; struct call_meter_property *property; if (cm->pending) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (!dbus_message_iter_next(&iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &passwd); if (!__ofono_is_valid_sim_pin(passwd, OFONO_SIM_PASSWORD_SIM_PIN2)) return __ofono_error_invalid_format(msg); for (property = cm_properties; property->name; property++) { if (strcmp(name, property->name)) continue; if (dbus_message_iter_get_arg_type(&var) != property->type) return __ofono_error_invalid_args(msg); return property->set(msg, cm, &var, passwd); } return __ofono_error_invalid_args(msg); } static void reset_acm_query_callback(const struct ofono_error *error, int value, void *data) { struct ofono_call_meter *cm = data; DBusMessage *reply; if (cm->pending == NULL) return; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Reseting ACM successful, but query was not"); cm->flags &= ~CALL_METER_FLAG_CACHED; __ofono_dbus_pending_reply(&cm->pending, __ofono_error_failed(cm->pending)); return; } reply = dbus_message_new_method_return(cm->pending); __ofono_dbus_pending_reply(&cm->pending, reply); set_acm(cm, value); } static void acm_reset_callback(const struct ofono_error *error, void *data) { struct ofono_call_meter *cm = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("reseting acm failed"); __ofono_dbus_pending_reply(&cm->pending, __ofono_error_failed(cm->pending)); check_pin2_state(cm); return; } /* Assume if we have acm_reset, we have acm_query */ cm->driver->acm_query(cm, reset_acm_query_callback, cm); } static DBusMessage *cm_acm_reset(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_meter *cm = data; const char *pin2; if (cm->driver->acm_reset == NULL) return __ofono_error_not_implemented(msg); if (cm->pending) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pin2, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!__ofono_is_valid_sim_pin(pin2, OFONO_SIM_PASSWORD_SIM_PIN2)) return __ofono_error_invalid_format(msg); cm->pending = dbus_message_ref(msg); cm->driver->acm_reset(cm, pin2, acm_reset_callback, cm); return NULL; } static const GDBusMethodTable cm_methods[] = { { GDBUS_ASYNC_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), cm_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }, { "password", "s" }), NULL, cm_set_property) }, { GDBUS_ASYNC_METHOD("Reset", GDBUS_ARGS({ "passoword", "s" }), NULL, cm_acm_reset) }, { } }; static const GDBusSignalTable cm_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "property", "s" }, { "value", "v" })) }, { GDBUS_SIGNAL("NearMaximumWarning", NULL) }, { } }; void ofono_call_meter_changed_notify(struct ofono_call_meter *cm, int new_value) { set_call_meter(cm, new_value); } void ofono_call_meter_maximum_notify(struct ofono_call_meter *cm) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cm->atom); g_dbus_emit_signal(conn, path, OFONO_CALL_METER_INTERFACE, "NearMaximumWarning", DBUS_TYPE_INVALID); } int ofono_call_meter_driver_register(const struct ofono_call_meter_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_call_meter_driver_unregister(const struct ofono_call_meter_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void call_meter_unregister(struct ofono_atom *atom) { struct ofono_call_meter *cm = __ofono_atom_get_data(atom); const char *path = __ofono_atom_get_path(cm->atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom); ofono_modem_remove_interface(modem, OFONO_CALL_METER_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_CALL_METER_INTERFACE); } static void call_meter_remove(struct ofono_atom *atom) { struct ofono_call_meter *cm = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (cm == NULL) return; if (cm->driver && cm->driver->remove) cm->driver->remove(cm); g_free(cm); } struct ofono_call_meter *ofono_call_meter_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_call_meter *cm; GSList *l; if (driver == NULL) return NULL; cm = g_try_new0(struct ofono_call_meter, 1); if (cm == NULL) return NULL; cm->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CALL_METER, call_meter_remove, cm); for (l = g_drivers; l; l = l->next) { const struct ofono_call_meter_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(cm, vendor, data) < 0) continue; cm->driver = drv; break; } return cm; } void ofono_call_meter_register(struct ofono_call_meter *cm) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cm->atom); struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom); if (!g_dbus_register_interface(conn, path, OFONO_CALL_METER_INTERFACE, cm_methods, cm_signals, NULL, cm, NULL)) { ofono_error("Could not create %s interface", OFONO_CALL_METER_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_CALL_METER_INTERFACE); __ofono_atom_register(cm->atom, call_meter_unregister); } void ofono_call_meter_remove(struct ofono_call_meter *cm) { __ofono_atom_free(cm->atom); } void ofono_call_meter_set_data(struct ofono_call_meter *cm, void *data) { cm->driver_data = data; } void *ofono_call_meter_get_data(struct ofono_call_meter *cm) { return cm->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/stkagent.c0000644000015600001650000007677412671500024021177 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include "ofono.h" #include "common.h" #include "smsutil.h" #include "stkutil.h" #include "stkagent.h" #ifndef DBUS_TIMEOUT_INFINITE #define DBUS_TIMEOUT_INFINITE ((int) 0x7fffffff) #endif enum allowed_error { ALLOWED_ERROR_GO_BACK = 0x1, ALLOWED_ERROR_TERMINATE = 0x2, ALLOWED_ERROR_BUSY = 0x4, }; struct stk_agent { char *path; /* Agent Path */ char *bus; /* Agent bus */ guint disconnect_watch; /* DBus disconnect watch */ ofono_bool_t remove_on_terminate; ofono_destroy_func removed_cb; void *removed_data; DBusMessage *msg; DBusPendingCall *call; void *user_cb; void *user_data; int min_length; int max_length; ofono_bool_t hidden_entry; ofono_destroy_func user_destroy; const struct stk_menu *request_selection_menu; }; #define ERROR_PREFIX OFONO_SERVICE ".Error" #define GOBACK_ERROR ERROR_PREFIX ".GoBack" #define TERMINATE_ERROR ERROR_PREFIX ".EndSession" #define BUSY_ERROR ERROR_PREFIX ".Busy" static void stk_agent_send_noreply(struct stk_agent *agent, const char *method) { DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *message; message = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, method); if (message == NULL) return; dbus_message_set_no_reply(message, TRUE); g_dbus_send_message(conn, message); } static inline void stk_agent_send_release(struct stk_agent *agent) { stk_agent_send_noreply(agent, "Release"); } static inline void stk_agent_send_cancel(struct stk_agent *agent) { stk_agent_send_noreply(agent, "Cancel"); } static void stk_agent_request_end(struct stk_agent *agent) { if (agent->msg) { dbus_message_unref(agent->msg); agent->msg = NULL; } if (agent->call) { dbus_pending_call_unref(agent->call); agent->call = NULL; } if (agent->user_destroy) agent->user_destroy(agent->user_data); agent->user_destroy = NULL; agent->user_data = NULL; agent->user_cb = NULL; } ofono_bool_t stk_agent_matches(struct stk_agent *agent, const char *path, const char *sender) { return !strcmp(agent->path, path) && !strcmp(agent->bus, sender); } void stk_agent_set_removed_notify(struct stk_agent *agent, ofono_destroy_func destroy, void *user_data) { agent->removed_cb = destroy; agent->removed_data = user_data; } void stk_agent_request_cancel(struct stk_agent *agent) { if (agent->call == NULL) return; dbus_pending_call_cancel(agent->call); if (agent->disconnect_watch) stk_agent_send_cancel(agent); stk_agent_request_end(agent); } void stk_agent_free(struct stk_agent *agent) { DBusConnection *conn = ofono_dbus_get_connection(); stk_agent_request_cancel(agent); if (agent->disconnect_watch) { stk_agent_send_release(agent); g_dbus_remove_watch(conn, agent->disconnect_watch); agent->disconnect_watch = 0; } if (agent->removed_cb) agent->removed_cb(agent->removed_data); g_free(agent->path); g_free(agent->bus); g_free(agent); } static int check_error(struct stk_agent *agent, DBusMessage *reply, int allowed_errors, enum stk_agent_result *out_result) { DBusError err; int result = 0; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == FALSE) { *out_result = STK_AGENT_RESULT_OK; return 0; } ofono_debug("SimToolkitAgent %s replied with error %s, %s", agent->path, err.name, err.message); /* Timeout is always valid */ if (g_str_equal(err.name, DBUS_ERROR_NO_REPLY)) { /* Send a Cancel() to the agent since its taking too long */ stk_agent_send_cancel(agent); *out_result = STK_AGENT_RESULT_TIMEOUT; goto out; } if ((allowed_errors & ALLOWED_ERROR_GO_BACK) && g_str_equal(err.name, GOBACK_ERROR)) { *out_result = STK_AGENT_RESULT_BACK; goto out; } if ((allowed_errors & ALLOWED_ERROR_TERMINATE) && g_str_equal(err.name, TERMINATE_ERROR)) { *out_result = STK_AGENT_RESULT_TERMINATE; goto out; } if ((allowed_errors & ALLOWED_ERROR_BUSY) && g_str_equal(err.name, BUSY_ERROR)) { *out_result = STK_AGENT_RESULT_BUSY; goto out; } result = -EINVAL; out: dbus_error_free(&err); return result; } static void stk_agent_disconnect_cb(DBusConnection *conn, void *user_data) { struct stk_agent *agent = user_data; ofono_debug("Agent exited without calling Unregister"); agent->disconnect_watch = 0; stk_agent_free(agent); } struct stk_agent *stk_agent_new(const char *path, const char *sender, ofono_bool_t remove_on_terminate) { struct stk_agent *agent = g_try_new0(struct stk_agent, 1); DBusConnection *conn = ofono_dbus_get_connection(); if (agent == NULL) return NULL; agent->path = g_strdup(path); agent->bus = g_strdup(sender); agent->remove_on_terminate = remove_on_terminate; agent->disconnect_watch = g_dbus_add_disconnect_watch(conn, sender, stk_agent_disconnect_cb, agent, NULL); return agent; } static void append_menu_items(DBusMessageIter *iter, const struct stk_menu_item *item) { DBusMessageIter array, entry; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(sy)", &array); while (item && item->text) { dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &item->text); dbus_message_iter_append_basic(&entry, DBUS_TYPE_BYTE, &item->icon_id); dbus_message_iter_close_container(&array, &entry); item++; } dbus_message_iter_close_container(iter, &array); } void append_menu_items_variant(DBusMessageIter *iter, const struct stk_menu_item *items) { DBusMessageIter variant; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a(sy)", &variant); append_menu_items(&variant, items); dbus_message_iter_close_container(iter, &variant); } #define CALLBACK_END() \ done: \ if (result == STK_AGENT_RESULT_TERMINATE && \ agent->remove_on_terminate) \ remove_agent = TRUE; \ else \ remove_agent = FALSE; \ \ error: \ stk_agent_request_end(agent); \ dbus_message_unref(reply); \ \ if (remove_agent) \ stk_agent_free(agent) \ static void request_selection_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; const struct stk_menu *menu = agent->request_selection_menu; stk_agent_selection_cb cb = (stk_agent_selection_cb) agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); unsigned char selection, i; enum stk_agent_result result; gboolean remove_agent; if (check_error(agent, reply, ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (result != STK_AGENT_RESULT_OK) { cb(result, 0, agent->user_data); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_BYTE, &selection, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to RequestSelection()"); remove_agent = TRUE; goto error; } for (i = 0; i < selection && menu->items[i].text; i++); if (i != selection) { ofono_error("Invalid item selected"); remove_agent = TRUE; goto error; } cb(result, menu->items[selection].item_id, agent->user_data); CALLBACK_END(); } int stk_agent_request_selection(struct stk_agent *agent, const struct stk_menu *menu, stk_agent_selection_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); dbus_int16_t default_item = menu->default_item; DBusMessageIter iter; agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "RequestSelection"); if (agent->msg == NULL) return -ENOMEM; dbus_message_iter_init_append(agent->msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &menu->title); dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE, &menu->icon.id); append_menu_items(&iter, menu->items); dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT16, &default_item); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; agent->request_selection_menu = menu; dbus_pending_call_set_notify(agent->call, request_selection_cb, agent, NULL); return 0; } static void display_text_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_display_text_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; if (check_error(agent, reply, ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE | ALLOWED_ERROR_BUSY, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (result != STK_AGENT_RESULT_OK) { cb(result, agent->user_data); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to DisplayText()"); remove_agent = TRUE; goto error; } cb(result, agent->user_data); CALLBACK_END(); } int stk_agent_display_text(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, ofono_bool_t urgent, stk_agent_display_text_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); dbus_bool_t priority = urgent; agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "DisplayText"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_BOOLEAN, &priority, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; dbus_pending_call_set_notify(agent->call, display_text_cb, agent, NULL); return 0; } static void get_confirmation_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_confirmation_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; dbus_bool_t confirm; if (check_error(agent, reply, ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (result != STK_AGENT_RESULT_OK) { cb(result, FALSE, agent->user_data); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_BOOLEAN, &confirm, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to GetConfirmation()"); remove_agent = TRUE; goto error; } cb(result, confirm, agent->user_data); CALLBACK_END(); } int stk_agent_request_confirmation(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_confirmation_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "RequestConfirmation"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; dbus_pending_call_set_notify(agent->call, get_confirmation_cb, agent, NULL); return 0; } static void get_digit_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_string_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; char *digit; if (check_error(agent, reply, ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (result != STK_AGENT_RESULT_OK) { cb(result, NULL, agent->user_data); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &digit, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to GetDigit()"); remove_agent = TRUE; goto error; } if (strlen(digit) != 1 || !strspn(digit, "0123456789*#+")) { ofono_error("Invalid character"); remove_agent = TRUE; goto error; } if (agent->hidden_entry && digit[0] == '+') { ofono_error("The character + is not allowed in this mode"); remove_agent = TRUE; goto error; } cb(result, digit, agent->user_data); CALLBACK_END(); } int stk_agent_request_digit(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_string_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "RequestDigit"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; agent->hidden_entry = FALSE; dbus_pending_call_set_notify(agent->call, get_digit_cb, agent, NULL); return 0; } int stk_agent_request_quick_digit(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_string_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "RequestQuickDigit"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; agent->hidden_entry = TRUE; dbus_pending_call_set_notify(agent->call, get_digit_cb, agent, NULL); return 0; } static void get_key_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_string_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; char *key; if (check_error(agent, reply, ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (result != STK_AGENT_RESULT_OK) { cb(result, NULL, agent->user_data); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID) == FALSE || g_utf8_strlen(key, 10) != 1) { ofono_error("Can't parse the reply to GetKey()"); remove_agent = TRUE; goto error; } cb(result, key, agent->user_data); CALLBACK_END(); } int stk_agent_request_key(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, ofono_bool_t unicode_charset, stk_agent_string_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "RequestKey"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; dbus_pending_call_set_notify(agent->call, get_key_cb, agent, NULL); return 0; } static void get_digits_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_string_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; char *string; int len, span; if (check_error(agent, reply, ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (result != STK_AGENT_RESULT_OK) { cb(result, NULL, agent->user_data); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &string, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to GetDigits()"); remove_agent = TRUE; goto error; } len = strlen(string); if (len < agent->min_length || len > agent->max_length) { ofono_error("Length not acceptable"); remove_agent = TRUE; goto error; } if (agent->hidden_entry) span = strspn(string, "0123456789*#"); else span = strspn(string, "0123456789*#+"); if (span != len) { ofono_error("Invalid character found"); remove_agent = TRUE; goto error; } cb(result, string, agent->user_data); CALLBACK_END(); } int stk_agent_request_digits(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, const char *default_text, int min, int max, ofono_bool_t hidden, stk_agent_string_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); uint8_t min_val = min; uint8_t max_val = max; dbus_bool_t hidden_val = hidden; agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "RequestDigits"); if (agent->msg == NULL) return -ENOMEM; if (default_text == NULL) default_text = ""; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_STRING, &default_text, DBUS_TYPE_BYTE, &min_val, DBUS_TYPE_BYTE, &max_val, DBUS_TYPE_BOOLEAN, &hidden_val, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; agent->min_length = min_val; agent->max_length = max_val; agent->hidden_entry = hidden_val; dbus_pending_call_set_notify(agent->call, get_digits_cb, agent, NULL); return 0; } static void get_input_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_string_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; char *string; int len; if (check_error(agent, reply, ALLOWED_ERROR_GO_BACK | ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (result != STK_AGENT_RESULT_OK) { cb(result, NULL, agent->user_data); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &string, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to GetInput()"); remove_agent = TRUE; goto error; } len = g_utf8_strlen(string, -1); if (len < agent->min_length || len > agent->max_length) { ofono_error("Length not acceptable"); remove_agent = TRUE; goto error; } cb(result, string, agent->user_data); CALLBACK_END(); } int stk_agent_request_input(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, const char *default_text, ofono_bool_t unicode_charset, int min, int max, ofono_bool_t hidden, stk_agent_string_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); uint8_t min_val = min; uint8_t max_val = max; dbus_bool_t hidden_val = hidden; agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "RequestInput"); if (agent->msg == NULL) return -ENOMEM; if (default_text == NULL) default_text = ""; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_STRING, &default_text, DBUS_TYPE_BYTE, &min_val, DBUS_TYPE_BYTE, &max_val, DBUS_TYPE_BOOLEAN, &hidden_val, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; agent->min_length = min_val; agent->max_length = max_val; agent->hidden_entry = hidden_val; dbus_pending_call_set_notify(agent->call, get_input_cb, agent, NULL); return 0; } static void confirm_call_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_confirmation_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; dbus_bool_t confirm; if (check_error(agent, reply, ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (result != STK_AGENT_RESULT_OK) { cb(result, FALSE, agent->user_data); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_BOOLEAN, &confirm, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to ConfirmCallSetup()"); remove_agent = TRUE; goto error; } cb(result, confirm, agent->user_data); CALLBACK_END(); } int stk_agent_confirm_call(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_confirmation_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "ConfirmCallSetup"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; dbus_pending_call_set_notify(agent->call, confirm_call_cb, agent, NULL); return 0; } static void play_tone_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_tone_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; if (check_error(agent, reply, ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to PlayTone()"); remove_agent = TRUE; goto error; } cb(result, agent->user_data); goto done; CALLBACK_END(); } int stk_agent_play_tone(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, ofono_bool_t vibrate, const char *tone, stk_agent_tone_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "PlayTone"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &tone, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; dbus_pending_call_set_notify(agent->call, play_tone_cb, agent, NULL); return 0; } int stk_agent_loop_tone(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, ofono_bool_t vibrate, const char *tone, stk_agent_tone_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "LoopTone"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &tone, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; dbus_pending_call_set_notify(agent->call, play_tone_cb, agent, NULL); return 0; } static void action_info_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; if (check_error(agent, reply, 0, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to DisplayActionInfo()"); remove_agent = TRUE; goto error; } goto done; CALLBACK_END(); } int stk_agent_display_action_info(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "DisplayActionInformation"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, DBUS_TIMEOUT_INFINITE) == FALSE || agent->call == NULL) return -EIO; dbus_pending_call_set_notify(agent->call, action_info_cb, agent, NULL); return 0; } static void confirm_launch_browser_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_confirmation_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; dbus_bool_t confirm; if (check_error(agent, reply, 0, &result) == -EINVAL) { remove_agent = TRUE; cb(STK_AGENT_RESULT_TERMINATE, FALSE, agent->user_data); goto error; } if (result != STK_AGENT_RESULT_OK) { cb(result, FALSE, agent->user_data); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_BOOLEAN, &confirm, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to ConfirmLaunchBrowser()"); remove_agent = TRUE; goto error; } cb(result, confirm, agent->user_data); CALLBACK_END(); } int stk_agent_confirm_launch_browser(struct stk_agent *agent, const char *text, unsigned char icon_id, const char *url, stk_agent_confirmation_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "ConfirmLaunchBrowser"); if (agent->msg == NULL) return -ENOMEM; if (url == NULL) url = ""; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon_id, DBUS_TYPE_STRING, &url, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; dbus_pending_call_set_notify(agent->call, confirm_launch_browser_cb, agent, NULL); return 0; } static void display_action_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_display_action_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; if (check_error(agent, reply, ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to DisplayAction()"); remove_agent = TRUE; goto error; } cb(result, agent->user_data); goto done; CALLBACK_END(); } int stk_agent_display_action(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_display_action_cb cb, void *user_data, ofono_destroy_func destroy) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "DisplayAction"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, DBUS_TIMEOUT_INFINITE) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; dbus_pending_call_set_notify(agent->call, display_action_cb, agent, NULL); return 0; } static void confirm_open_channel_cb(DBusPendingCall *call, void *data) { struct stk_agent *agent = data; stk_agent_confirmation_cb cb = agent->user_cb; DBusMessage *reply = dbus_pending_call_steal_reply(call); enum stk_agent_result result; gboolean remove_agent; dbus_bool_t confirm; if (check_error(agent, reply, ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) { remove_agent = TRUE; goto error; } if (result != STK_AGENT_RESULT_OK) { cb(result, FALSE, agent->user_data); goto done; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_BOOLEAN, &confirm, DBUS_TYPE_INVALID) == FALSE) { ofono_error("Can't parse the reply to ConfirmOpenChannel()"); remove_agent = TRUE; goto error; } cb(result, confirm, agent->user_data); CALLBACK_END(); } int stk_agent_confirm_open_channel(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_confirmation_cb cb, void *user_data, ofono_destroy_func destroy, int timeout) { DBusConnection *conn = ofono_dbus_get_connection(); agent->msg = dbus_message_new_method_call(agent->bus, agent->path, OFONO_SIM_APP_INTERFACE, "ConfirmOpenChannel"); if (agent->msg == NULL) return -ENOMEM; dbus_message_append_args(agent->msg, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon->id, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call, timeout) == FALSE || agent->call == NULL) return -EIO; agent->user_cb = cb; agent->user_data = user_data; agent->user_destroy = destroy; dbus_pending_call_set_notify(agent->call, confirm_open_channel_cb, agent, NULL); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/src/voicecall.c0000644000015600001650000030130612671500073021302 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" #include "simutil.h" #include "smsutil.h" #include "storage.h" #include "wakelock.h" #define MAX_VOICE_CALLS 16 #define VOICECALL_FLAG_SIM_ECC_READY 0x1 #define VOICECALL_FLAG_STK_MODEM_CALLSETUP 0x2 #define SETTINGS_STORE "voicecall" #define SETTINGS_GROUP "Settings" GSList *g_drivers = NULL; struct ofono_voicecall { GSList *call_list; GSList *release_list; GSList *multiparty_list; GHashTable *en_list; /* emergency number list */ GSList *sim_en_list; /* Emergency numbers already read from SIM */ GSList *new_sim_en_list; /* Emergency numbers being read from SIM */ char **nw_en_list; /* Emergency numbers from modem/network */ DBusMessage *pending; uint32_t flags; struct ofono_sim *sim; struct ofono_sim_context *sim_context; unsigned int sim_watch; unsigned int sim_state_watch; const struct ofono_voicecall_driver *driver; void *driver_data; struct ofono_atom *atom; struct dial_request *dial_req; GQueue *toneq; guint tone_source; unsigned int hfp_watch; GKeyFile *settings; char *imsi; ofono_voicecall_cb_t release_queue_done_cb; struct ofono_emulator *pending_em; unsigned int pending_id; }; struct voicecall { struct ofono_call *call; struct ofono_voicecall *vc; time_t start_time; time_t detect_time; char *message; uint8_t icon_id; gboolean untracked; gboolean dial_result_handled; ofono_bool_t remote_held; ofono_bool_t remote_multiparty; }; struct dial_request { struct ofono_voicecall *vc; char *message; uint8_t icon_id; enum ofono_voicecall_interaction interaction; ofono_voicecall_dial_cb_t cb; void *user_data; struct voicecall *call; struct ofono_phone_number ph; }; struct tone_queue_entry { char *tone_str; char *left; ofono_voicecall_tone_cb_t cb; void *user_data; ofono_destroy_func destroy; int id; }; struct emulator_status { struct ofono_voicecall *vc; int status; }; static const char *default_en_list[] = { "911", "112", NULL }; static const char *default_en_list_no_sim[] = { "119", "118", "999", "110", "08", "000", NULL }; static void send_ciev_after_swap_callback(const struct ofono_error *error, void *data); static void generic_callback(const struct ofono_error *error, void *data); static void hangup_all_active(const struct ofono_error *error, void *data); static void multirelease_callback(const struct ofono_error *err, void *data); static gboolean tone_request_run(gpointer user_data); static gint call_compare_by_id(gconstpointer a, gconstpointer b) { const struct ofono_call *call = ((struct voicecall *)a)->call; unsigned int id = GPOINTER_TO_UINT(b); if (id < call->id) return -1; if (id > call->id) return 1; return 0; } static gint call_compare(gconstpointer a, gconstpointer b) { const struct voicecall *ca = a; const struct voicecall *cb = b; if (ca->call->id < cb->call->id) return -1; if (ca->call->id > cb->call->id) return 1; return 0; } static void add_to_en_list(struct ofono_voicecall *vc, char **list) { int i = 0; while (list[i]) g_hash_table_insert(vc->en_list, g_strdup(list[i++]), NULL); } static const char *disconnect_reason_to_string(enum ofono_disconnect_reason r) { switch (r) { case OFONO_DISCONNECT_REASON_LOCAL_HANGUP: return "local"; case OFONO_DISCONNECT_REASON_REMOTE_HANGUP: return "remote"; default: return "network"; } } static const char *call_status_to_string(int status) { switch (status) { case CALL_STATUS_ACTIVE: return "active"; case CALL_STATUS_HELD: return "held"; case CALL_STATUS_DIALING: return "dialing"; case CALL_STATUS_ALERTING: return "alerting"; case CALL_STATUS_INCOMING: return "incoming"; case CALL_STATUS_WAITING: return "waiting"; default: return "disconnected"; } } static const char *phone_and_clip_to_string(const struct ofono_phone_number *n, int clip_validity) { if (clip_validity == CLIP_VALIDITY_WITHHELD && !strlen(n->number)) return "withheld"; if (clip_validity == CLIP_VALIDITY_NOT_AVAILABLE) return ""; return phone_number_to_string(n); } static const char *cnap_to_string(const char *name, int cnap_validity) { if (cnap_validity == CNAP_VALIDITY_WITHHELD && !strlen(name)) return "withheld"; if (cnap_validity == CNAP_VALIDITY_NOT_AVAILABLE) return ""; return name; } static const char *time_to_str(const time_t *t) { static char buf[128]; struct tm tm; strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime_r(t, &tm)); buf[127] = '\0'; return buf; } static unsigned int voicecalls_num_with_status(struct ofono_voicecall *vc, int status) { GSList *l; struct voicecall *v; int num = 0; for (l = vc->call_list; l; l = l->next) { v = l->data; if (v->call->status == status) num += 1; } return num; } static unsigned int voicecalls_num_active(struct ofono_voicecall *vc) { return voicecalls_num_with_status(vc, CALL_STATUS_ACTIVE); } static unsigned int voicecalls_num_held(struct ofono_voicecall *vc) { return voicecalls_num_with_status(vc, CALL_STATUS_HELD); } static unsigned int voicecalls_num_connecting(struct ofono_voicecall *vc) { unsigned int r = 0; r += voicecalls_num_with_status(vc, CALL_STATUS_DIALING); r += voicecalls_num_with_status(vc, CALL_STATUS_ALERTING); return r; } static gboolean voicecalls_have_active(struct ofono_voicecall *vc) { GSList *l; struct voicecall *v; for (l = vc->call_list; l; l = l->next) { v = l->data; if (v->call->status == CALL_STATUS_ACTIVE || v->call->status == CALL_STATUS_DIALING || v->call->status == CALL_STATUS_ALERTING) return TRUE; } return FALSE; } static gboolean voicecalls_have_with_status(struct ofono_voicecall *vc, int status) { GSList *l; struct voicecall *v; for (l = vc->call_list; l; l = l->next) { v = l->data; if (v->call->status == status) return TRUE; } return FALSE; } static gboolean voicecalls_have_held(struct ofono_voicecall *vc) { return voicecalls_have_with_status(vc, CALL_STATUS_HELD); } static gboolean voicecalls_have_waiting(struct ofono_voicecall *vc) { return voicecalls_have_with_status(vc, CALL_STATUS_WAITING); } static gboolean voicecalls_have_incoming(struct ofono_voicecall *vc) { return voicecalls_have_with_status(vc, CALL_STATUS_INCOMING); } static void dial_request_finish(struct ofono_voicecall *vc) { struct dial_request *dial_req = vc->dial_req; if (dial_req->cb) dial_req->cb(dial_req->call ? dial_req->call->call : NULL, dial_req->user_data); g_free(dial_req->message); g_free(dial_req); vc->dial_req = NULL; } static gboolean voicecalls_can_dtmf(struct ofono_voicecall *vc) { GSList *l; struct voicecall *v; for (l = vc->call_list; l; l = l->next) { v = l->data; if (v->call->status == CALL_STATUS_ACTIVE) return TRUE; /* Connected for 2nd stage dialing */ if (v->call->status == CALL_STATUS_ALERTING) return TRUE; } return FALSE; } static int tone_queue(struct ofono_voicecall *vc, const char *tone_str, ofono_voicecall_tone_cb_t cb, void *data, ofono_destroy_func destroy) { struct tone_queue_entry *entry; int id = 1; int n = 0; int i; /* * Tones can be 0-9, *, #, A-D according to 27.007 C.2.11, * and p for Pause. */ for (i = 0; tone_str[i]; i++) if (!g_ascii_isdigit(tone_str[i]) && tone_str[i] != 'p' && tone_str[i] != 'P' && tone_str[i] != '*' && tone_str[i] != '#' && (tone_str[i] < 'A' || tone_str[i] > 'D')) return -EINVAL; while ((entry = g_queue_peek_nth(vc->toneq, n++)) != NULL) if (entry->id >= id) id = entry->id + 1; entry = g_try_new0(struct tone_queue_entry, 1); if (entry == NULL) return -ENOMEM; entry->tone_str = g_strdup(tone_str); entry->left = entry->tone_str; entry->cb = cb; entry->user_data = data; entry->destroy = destroy; entry->id = id; g_queue_push_tail(vc->toneq, entry); if (g_queue_get_length(vc->toneq) == 1) g_timeout_add(0, tone_request_run, vc); return id; } static void tone_request_finish(struct ofono_voicecall *vc, struct tone_queue_entry *entry, int error, gboolean callback) { g_queue_remove(vc->toneq, entry); if (callback) entry->cb(error, entry->user_data); if (entry->destroy) entry->destroy(entry->user_data); g_free(entry->tone_str); g_free(entry); } static gboolean is_emergency_number(struct ofono_voicecall *vc, const char *number) { return g_hash_table_lookup_extended(vc->en_list, number, NULL, NULL); } static void append_voicecall_properties(struct voicecall *v, DBusMessageIter *dict) { struct ofono_call *call = v->call; const char *status; const char *callerid; const char *timestr; const char *name; ofono_bool_t mpty; dbus_bool_t emergency_call; status = call_status_to_string(call->status); ofono_dbus_dict_append(dict, "State", DBUS_TYPE_STRING, &status); if (call->direction == CALL_DIRECTION_MOBILE_TERMINATED) callerid = phone_and_clip_to_string(&call->phone_number, call->clip_validity); else callerid = phone_number_to_string(&call->phone_number); ofono_dbus_dict_append(dict, "LineIdentification", DBUS_TYPE_STRING, &callerid); if (call->called_number.number[0] != '\0') { const char *calledid; calledid = phone_number_to_string(&call->called_number); ofono_dbus_dict_append(dict, "IncomingLine", DBUS_TYPE_STRING, &calledid); } name = cnap_to_string(call->name, call->cnap_validity); ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING, &name); if (call->status == CALL_STATUS_ACTIVE || call->status == CALL_STATUS_HELD || (call->status == CALL_STATUS_DISCONNECTED && v->start_time != 0)) { timestr = time_to_str(&v->start_time); ofono_dbus_dict_append(dict, "StartTime", DBUS_TYPE_STRING, ×tr); } if (g_slist_find_custom(v->vc->multiparty_list, GINT_TO_POINTER(call->id), call_compare_by_id)) mpty = TRUE; else mpty = FALSE; ofono_dbus_dict_append(dict, "Multiparty", DBUS_TYPE_BOOLEAN, &mpty); ofono_dbus_dict_append(dict, "RemoteHeld", DBUS_TYPE_BOOLEAN, &v->remote_held); ofono_dbus_dict_append(dict, "RemoteMultiparty", DBUS_TYPE_BOOLEAN, &v->remote_multiparty); if (v->message) ofono_dbus_dict_append(dict, "Information", DBUS_TYPE_STRING, &v->message); if (v->icon_id) ofono_dbus_dict_append(dict, "Icon", DBUS_TYPE_BYTE, &v->icon_id); if (is_emergency_number(v->vc, callerid) == TRUE) emergency_call = TRUE; else emergency_call = FALSE; ofono_dbus_dict_append(dict, "Emergency", DBUS_TYPE_BOOLEAN, &emergency_call); } static DBusMessage *voicecall_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct voicecall *v = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); append_voicecall_properties(v, &dict); dbus_message_iter_close_container(&iter, &dict); return reply; } static DBusMessage *voicecall_deflect(DBusConnection *conn, DBusMessage *msg, void *data) { struct voicecall *v = data; struct ofono_voicecall *vc = v->vc; struct ofono_call *call = v->call; struct ofono_phone_number ph; const char *number; if (call->status != CALL_STATUS_INCOMING && call->status != CALL_STATUS_WAITING) return __ofono_error_failed(msg); if (vc->driver->deflect == NULL) return __ofono_error_not_implemented(msg); if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!valid_phone_number_format(number)) return __ofono_error_invalid_format(msg); vc->pending = dbus_message_ref(msg); string_to_phone_number(number, &ph); vc->driver->deflect(vc, &ph, generic_callback, vc); return NULL; } static DBusMessage *voicecall_hangup(DBusConnection *conn, DBusMessage *msg, void *data) { struct voicecall *v = data; struct ofono_voicecall *vc = v->vc; struct ofono_call *call = v->call; gboolean single_call = vc->call_list->next == 0; if (vc->pending || vc->pending_em) return __ofono_error_busy(msg); if (vc->dial_req && vc->dial_req->call != v) return __ofono_error_busy(msg); switch (call->status) { case CALL_STATUS_DISCONNECTED: return __ofono_error_failed(msg); case CALL_STATUS_INCOMING: if (vc->driver->hangup_all == NULL && vc->driver->hangup_active == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); if (vc->driver->hangup_all) vc->driver->hangup_all(vc, generic_callback, vc); else vc->driver->hangup_active(vc, generic_callback, vc); return NULL; case CALL_STATUS_WAITING: if (vc->driver->set_udub == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); vc->driver->set_udub(vc, generic_callback, vc); return NULL; case CALL_STATUS_HELD: if (vc->driver->release_all_held && voicecalls_num_held(vc) == 1 && voicecalls_have_waiting(vc) == FALSE) { vc->pending = dbus_message_ref(msg); vc->driver->release_all_held(vc, generic_callback, vc); return NULL; } break; case CALL_STATUS_DIALING: case CALL_STATUS_ALERTING: if (vc->driver->hangup_active != NULL) { vc->pending = dbus_message_ref(msg); vc->driver->hangup_active(vc, generic_callback, vc); return NULL; } /* * Fall through, we check if we have a single alerting, * dialing or active call and try to hang it up with * hangup_all or hangup_active */ case CALL_STATUS_ACTIVE: if (single_call == TRUE && vc->driver->hangup_all != NULL) { vc->pending = dbus_message_ref(msg); vc->driver->hangup_all(vc, generic_callback, vc); return NULL; } if (voicecalls_num_active(vc) == 1 && vc->driver->hangup_active != NULL) { vc->pending = dbus_message_ref(msg); vc->driver->hangup_active(vc, generic_callback, vc); return NULL; } break; } if (vc->driver->release_specific == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); vc->driver->release_specific(vc, call->id, generic_callback, vc); return NULL; } static DBusMessage *voicecall_answer(DBusConnection *conn, DBusMessage *msg, void *data) { struct voicecall *v = data; struct ofono_voicecall *vc = v->vc; struct ofono_call *call = v->call; if (call->status != CALL_STATUS_INCOMING) return __ofono_error_failed(msg); if (vc->driver->answer == NULL) return __ofono_error_not_implemented(msg); if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); vc->pending = dbus_message_ref(msg); vc->driver->answer(vc, generic_callback, vc); return NULL; } static const GDBusMethodTable voicecall_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), voicecall_get_properties) }, { GDBUS_ASYNC_METHOD("Deflect", GDBUS_ARGS({ "number", "s" }), NULL, voicecall_deflect) }, { GDBUS_ASYNC_METHOD("Hangup", NULL, NULL, voicecall_hangup) }, { GDBUS_ASYNC_METHOD("Answer", NULL, NULL, voicecall_answer) }, { } }; static const GDBusSignalTable voicecall_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { GDBUS_SIGNAL("DisconnectReason", GDBUS_ARGS({ "reason", "s" })) }, { } }; static struct voicecall *voicecall_create(struct ofono_voicecall *vc, struct ofono_call *call) { struct voicecall *v; v = g_try_new0(struct voicecall, 1); if (v == NULL) return NULL; v->call = call; v->vc = vc; return v; } static void voicecall_destroy(gpointer userdata) { struct voicecall *voicecall = (struct voicecall *)userdata; g_free(voicecall->call); g_free(voicecall->message); g_free(voicecall); } static const char *voicecall_build_path(struct ofono_voicecall *vc, const struct ofono_call *call) { static char path[256]; snprintf(path, sizeof(path), "%s/voicecall%02d", __ofono_atom_get_path(vc->atom), call->id); return path; } static void voicecall_emit_disconnect_reason(struct voicecall *call, enum ofono_disconnect_reason reason) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; const char *reason_str; reason_str = disconnect_reason_to_string(reason); path = voicecall_build_path(call->vc, call->call); g_dbus_emit_signal(conn, path, OFONO_VOICECALL_INTERFACE, "DisconnectReason", DBUS_TYPE_STRING, &reason_str, DBUS_TYPE_INVALID); } static void voicecall_emit_multiparty(struct voicecall *call, gboolean mpty) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = voicecall_build_path(call->vc, call->call); dbus_bool_t val = mpty; ofono_dbus_signal_property_changed(conn, path, OFONO_VOICECALL_INTERFACE, "Multiparty", DBUS_TYPE_BOOLEAN, &val); } static void emulator_set_indicator_forced(struct ofono_voicecall *vc, const char *name, int value) { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); struct ofono_atom *atom; atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP); if (atom) __ofono_emulator_set_indicator_forced(atom, name, value); } static void emulator_call_status_cb(struct ofono_atom *atom, void *data) { struct emulator_status *s = data; ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_CALL, s->status); } static void emulator_callsetup_status_cb(struct ofono_atom *atom, void *data) { struct emulator_status *s = data; ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_CALLSETUP, s->status); } static void emulator_callheld_status_cb(struct ofono_atom *atom, void *data) { struct emulator_status *s = data; ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_CALLHELD, s->status); } static void notify_emulator_call_status(struct ofono_voicecall *vc) { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); gboolean call = FALSE; unsigned int non_mpty = 0; gboolean multiparty = FALSE; gboolean held = FALSE; unsigned int non_mpty_held = 0; gboolean multiparty_held = FALSE; gboolean incoming = FALSE; gboolean dialing = FALSE; gboolean alerting = FALSE; gboolean waiting = FALSE; GSList *l; struct voicecall *v; struct emulator_status data; data.vc = vc; for (l = vc->call_list; l; l = l->next) { v = l->data; switch (v->call->status) { case CALL_STATUS_ACTIVE: call = TRUE; if (g_slist_find_custom(vc->multiparty_list, GINT_TO_POINTER(v->call->id), call_compare_by_id)) multiparty = TRUE; else non_mpty++; break; case CALL_STATUS_HELD: held = TRUE; if (g_slist_find_custom(vc->multiparty_list, GINT_TO_POINTER(v->call->id), call_compare_by_id)) multiparty_held = TRUE; else non_mpty_held++; break; case CALL_STATUS_DIALING: dialing = TRUE; break; case CALL_STATUS_ALERTING: alerting = TRUE; break; case CALL_STATUS_INCOMING: incoming = TRUE; break; case CALL_STATUS_WAITING: waiting = TRUE; break; } } /* * Perform some basic sanity checks for transitionary states; * if a transitionary state is detected, then ignore it. The call * indicators will be updated properly in the follow-on calls to * this function once the final state has been reached */ if (incoming && (held || call)) return; if (waiting && (held == FALSE && call == FALSE)) return; if (non_mpty > 1 || (non_mpty && multiparty)) return; if (non_mpty_held > 1 || (non_mpty_held && multiparty_held)) return; if (multiparty && multiparty_held) return; data.status = call || held ? OFONO_EMULATOR_CALL_ACTIVE : OFONO_EMULATOR_CALL_INACTIVE; __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_call_status_cb, &data); if (incoming) data.status = OFONO_EMULATOR_CALLSETUP_INCOMING; else if (dialing) data.status = OFONO_EMULATOR_CALLSETUP_OUTGOING; else if (alerting) data.status = OFONO_EMULATOR_CALLSETUP_ALERTING; else if (waiting) data.status = OFONO_EMULATOR_CALLSETUP_INCOMING; else data.status = OFONO_EMULATOR_CALLSETUP_INACTIVE; __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_callsetup_status_cb, &data); if (held) data.status = call ? OFONO_EMULATOR_CALLHELD_MULTIPLE : OFONO_EMULATOR_CALLHELD_ON_HOLD; else data.status = OFONO_EMULATOR_CALLHELD_NONE; __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_callheld_status_cb, &data); } static void voicecall_set_call_status(struct voicecall *call, int status) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; const char *status_str; int old_status; if (call->call->status == status) return; old_status = call->call->status; call->call->status = status; status_str = call_status_to_string(status); path = voicecall_build_path(call->vc, call->call); ofono_dbus_signal_property_changed(conn, path, OFONO_VOICECALL_INTERFACE, "State", DBUS_TYPE_STRING, &status_str); notify_emulator_call_status(call->vc); if (status == CALL_STATUS_ACTIVE && (old_status == CALL_STATUS_INCOMING || old_status == CALL_STATUS_DIALING || old_status == CALL_STATUS_ALERTING || old_status == CALL_STATUS_WAITING)) { const char *timestr; call->start_time = time(NULL); timestr = time_to_str(&call->start_time); ofono_dbus_signal_property_changed(conn, path, OFONO_VOICECALL_INTERFACE, "StartTime", DBUS_TYPE_STRING, ×tr); if (call->vc->dial_req && call == call->vc->dial_req->call) dial_request_finish(call->vc); } if (status == CALL_STATUS_DISCONNECTED && call->vc->dial_req && call == call->vc->dial_req->call) dial_request_finish(call->vc); if (!voicecalls_can_dtmf(call->vc)) { struct tone_queue_entry *entry; while ((entry = g_queue_peek_head(call->vc->toneq))) tone_request_finish(call->vc, entry, ENOENT, TRUE); } } static void voicecall_set_call_lineid(struct voicecall *v, const struct ofono_phone_number *ph, int clip_validity) { struct ofono_call *call = v->call; DBusConnection *conn = ofono_dbus_get_connection(); const char *path; const char *lineid_str; if (!strcmp(call->phone_number.number, ph->number) && call->phone_number.type == ph->type && call->clip_validity == clip_validity) return; /* * Two cases: We get an incoming call with CLIP factored in, or * CLIP comes in later as a separate event * For COLP only the phone number should be checked, it can come * in with the initial call event or later as a separate event */ /* For plugins that don't keep state, ignore */ if (call->clip_validity == CLIP_VALIDITY_VALID && clip_validity == CLIP_VALIDITY_NOT_AVAILABLE) return; strcpy(call->phone_number.number, ph->number); call->clip_validity = clip_validity; call->phone_number.type = ph->type; path = voicecall_build_path(v->vc, call); if (call->direction == CALL_DIRECTION_MOBILE_TERMINATED) lineid_str = phone_and_clip_to_string(ph, clip_validity); else lineid_str = phone_number_to_string(ph); ofono_dbus_signal_property_changed(conn, path, OFONO_VOICECALL_INTERFACE, "LineIdentification", DBUS_TYPE_STRING, &lineid_str); if (is_emergency_number(v->vc, lineid_str)) { dbus_bool_t emergency_call = TRUE; ofono_dbus_signal_property_changed(conn, path, OFONO_VOICECALL_INTERFACE, "Emergency", DBUS_TYPE_BOOLEAN, &emergency_call); } } static void voicecall_set_call_calledid(struct voicecall *v, const struct ofono_phone_number *ph) { struct ofono_call *call = v->call; DBusConnection *conn = ofono_dbus_get_connection(); const char *path; const char *calledid_str; if (!strcmp(call->called_number.number, ph->number) && call->called_number.type == ph->type) return; strcpy(call->called_number.number, ph->number); call->called_number.type = ph->type; path = voicecall_build_path(v->vc, call); calledid_str = phone_number_to_string(ph); ofono_dbus_signal_property_changed(conn, path, OFONO_VOICECALL_INTERFACE, "IncomingLine", DBUS_TYPE_STRING, &calledid_str); } static void voicecall_set_call_name(struct voicecall *v, const char *name, int cnap_validity) { struct ofono_call *call = v->call; DBusConnection *conn = ofono_dbus_get_connection(); const char *path; const char *name_str; if (!strcmp(call->name, name) && call->cnap_validity == cnap_validity) return; /* For plugins that don't keep state, ignore */ if (call->cnap_validity == CNAP_VALIDITY_VALID && cnap_validity == CNAP_VALIDITY_NOT_AVAILABLE) return; strncpy(call->name, name, OFONO_MAX_CALLER_NAME_LENGTH); call->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0'; call->cnap_validity = cnap_validity; path = voicecall_build_path(v->vc, call); name_str = cnap_to_string(name, cnap_validity); ofono_dbus_signal_property_changed(conn, path, OFONO_VOICECALL_INTERFACE, "Name", DBUS_TYPE_STRING, &name_str); } static gboolean voicecall_dbus_register(struct voicecall *v) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; if (v == NULL) return FALSE; path = voicecall_build_path(v->vc, v->call); if (!g_dbus_register_interface(conn, path, OFONO_VOICECALL_INTERFACE, voicecall_methods, voicecall_signals, NULL, v, voicecall_destroy)) { ofono_error("Could not register VoiceCall %s", path); voicecall_destroy(v); return FALSE; } return TRUE; } static gboolean voicecall_dbus_unregister(struct ofono_voicecall *vc, struct voicecall *v) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = voicecall_build_path(vc, v->call); return g_dbus_unregister_interface(conn, path, OFONO_VOICECALL_INTERFACE); } static int voicecalls_path_list(struct ofono_voicecall *vc, GSList *call_list, char ***objlist) { GSList *l; int i; struct voicecall *v; *objlist = g_new0(char *, g_slist_length(call_list) + 1); if (*objlist == NULL) return -1; for (i = 0, l = call_list; l; l = l->next, i++) { v = l->data; (*objlist)[i] = g_strdup(voicecall_build_path(vc, v->call)); } return 0; } static GSList *voicecalls_held_list(struct ofono_voicecall *vc) { GSList *l; GSList *r = NULL; struct voicecall *v; for (l = vc->call_list; l; l = l->next) { v = l->data; if (v->call->status == CALL_STATUS_HELD) r = g_slist_prepend(r, v); } if (r) r = g_slist_reverse(r); return r; } /* * Intended to be used for multiparty, which cannot be incoming, * alerting or dialing */ static GSList *voicecalls_active_list(struct ofono_voicecall *vc) { GSList *l; GSList *r = NULL; struct voicecall *v; for (l = vc->call_list; l; l = l->next) { v = l->data; if (v->call->status == CALL_STATUS_ACTIVE) r = g_slist_prepend(r, v); } if (r) r = g_slist_reverse(r); return r; } struct ofono_call *__ofono_voicecall_find_call_with_status( struct ofono_voicecall *vc, int status) { GSList *l; struct voicecall *v; for (l = vc->call_list; l; l = l->next) { v = l->data; if (v->call->status == status) return v->call; } return NULL; } static void voicecalls_multiparty_changed(GSList *old, GSList *new) { GSList *o, *n; struct voicecall *nc, *oc; n = new; o = old; while (n || o) { nc = n ? n->data : NULL; oc = o ? o->data : NULL; if (oc && (nc == NULL || (nc->call->id > oc->call->id))) { voicecall_emit_multiparty(oc, FALSE); o = o->next; } else if (nc && (oc == NULL || (nc->call->id < oc->call->id))) { voicecall_emit_multiparty(nc, TRUE); n = n->next; } else { n = n->next; o = o->next; } } } static void voicecalls_emit_call_removed(struct ofono_voicecall *vc, struct voicecall *v) { DBusConnection *conn = ofono_dbus_get_connection(); const char *atompath = __ofono_atom_get_path(vc->atom); const char *path = voicecall_build_path(vc, v->call); g_dbus_emit_signal(conn, atompath, OFONO_VOICECALL_MANAGER_INTERFACE, "CallRemoved", DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); } static void voicecalls_emit_call_added(struct ofono_voicecall *vc, struct voicecall *v) { DBusMessage *signal; DBusMessageIter iter; DBusMessageIter dict; const char *path; notify_emulator_call_status(vc); path = __ofono_atom_get_path(vc->atom); signal = dbus_message_new_signal(path, OFONO_VOICECALL_MANAGER_INTERFACE, "CallAdded"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); path = voicecall_build_path(vc, v->call); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); append_voicecall_properties(v, &dict); dbus_message_iter_close_container(&iter, &dict); g_dbus_send_message(ofono_dbus_get_connection(), signal); } static void voicecalls_release_queue(struct ofono_voicecall *vc, GSList *calls, ofono_voicecall_cb_t cb, ofono_bool_t skip_held) { GSList *l; struct voicecall *call; g_slist_free(vc->release_list); vc->release_list = NULL; for (l = calls; l; l = l->next) { call = l->data; if (call->call->status == CALL_STATUS_WAITING) continue; if (skip_held && call->call->status == CALL_STATUS_HELD) continue; vc->release_list = g_slist_prepend(vc->release_list, l->data); } vc->release_queue_done_cb = cb; } static void voicecalls_release_next(struct ofono_voicecall *vc) { struct voicecall *call; if (vc->release_list == NULL) return; call = vc->release_list->data; vc->release_list = g_slist_remove(vc->release_list, call); if (vc->driver->hangup_active == NULL) goto fallback; if (call->call->status == CALL_STATUS_ACTIVE && voicecalls_num_active(vc) == 1) { vc->driver->hangup_active(vc, multirelease_callback, vc); return; } if (call->call->status == CALL_STATUS_ALERTING || call->call->status == CALL_STATUS_DIALING || call->call->status == CALL_STATUS_INCOMING) { vc->driver->hangup_active(vc, multirelease_callback, vc); return; } fallback: vc->driver->release_specific(vc, call->call->id, multirelease_callback, vc); } static void voicecalls_release_done(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; DBusMessage *reply; reply = dbus_message_new_method_return(vc->pending); __ofono_dbus_pending_reply(&vc->pending, reply); } static DBusMessage *manager_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; int i; char **list; GHashTableIter ht_iter; gpointer key, value; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); /* property EmergencyNumbers */ list = g_new0(char *, g_hash_table_size(vc->en_list) + 1); g_hash_table_iter_init(&ht_iter, vc->en_list); for (i = 0; g_hash_table_iter_next(&ht_iter, &key, &value); i++) list[i] = key; ofono_dbus_dict_append_array(&dict, "EmergencyNumbers", DBUS_TYPE_STRING, &list); g_free(list); dbus_message_iter_close_container(&iter, &dict); return reply; } static ofono_bool_t clir_string_to_clir(const char *clirstr, enum ofono_clir_option *clir) { if (strlen(clirstr) == 0 || !strcmp(clirstr, "default")) { *clir = OFONO_CLIR_OPTION_DEFAULT; return TRUE; } else if (!strcmp(clirstr, "disabled")) { *clir = OFONO_CLIR_OPTION_SUPPRESSION; return TRUE; } else if (!strcmp(clirstr, "enabled")) { *clir = OFONO_CLIR_OPTION_INVOCATION; return TRUE; } else { return FALSE; } } static struct ofono_call *synthesize_outgoing_call(struct ofono_voicecall *vc, const char *number) { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); struct ofono_call *call; call = g_try_new0(struct ofono_call, 1); if (call == NULL) return call; call->id = __ofono_modem_callid_next(modem); if (call->id == 0) { ofono_error("Failed to alloc callid, too many calls"); g_free(call); return NULL; } __ofono_modem_callid_hold(modem, call->id); if (number) string_to_phone_number(number, &call->phone_number); call->direction = CALL_DIRECTION_MOBILE_ORIGINATED; call->status = CALL_STATUS_DIALING; call->clip_validity = CLIP_VALIDITY_VALID; return call; } static struct voicecall *dial_handle_result(struct ofono_voicecall *vc, const struct ofono_error *error, const char *number, gboolean *need_to_emit) { GSList *l; struct voicecall *v; struct ofono_call *call; *need_to_emit = FALSE; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Dial callback returned error: %s", telephony_error_to_str(error)); return NULL; } /* * Two things can happen, the call notification arrived before dial * callback or dial callback was first. Handle here */ for (l = vc->call_list; l; l = l->next) { v = l->data; if (v->call->status == CALL_STATUS_DIALING || v->call->status == CALL_STATUS_ALERTING) goto handled; /* * Dial request may return before existing active call * is put on hold or after dialed call has got active */ if (v->call->status == CALL_STATUS_ACTIVE && v->call->direction == CALL_DIRECTION_MOBILE_ORIGINATED && !v->dial_result_handled) goto handled; } call = synthesize_outgoing_call(vc, number); if (call == NULL) return NULL; v = voicecall_create(vc, call); if (v == NULL) return NULL; v->detect_time = time(NULL); DBG("Registering new call: %d", call->id); voicecall_dbus_register(v); vc->call_list = g_slist_insert_sorted(vc->call_list, v, call_compare); *need_to_emit = TRUE; handled: v->dial_result_handled = TRUE; return v; } static void manager_dial_callback(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; DBusMessage *reply; const char *number; gboolean need_to_emit; struct voicecall *v; if (dbus_message_get_args(vc->pending, NULL, DBUS_TYPE_STRING, &number, DBUS_TYPE_INVALID) == FALSE) number = NULL; v = dial_handle_result(vc, error, number, &need_to_emit); if (v) { const char *path = voicecall_build_path(vc, v->call); reply = dbus_message_new_method_return(vc->pending); dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); } else { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); if (is_emergency_number(vc, number) == TRUE) __ofono_modem_dec_emergency_mode(modem); reply = __ofono_error_failed(vc->pending); } __ofono_dbus_pending_reply(&vc->pending, reply); if (need_to_emit) voicecalls_emit_call_added(vc, v); } static int voicecall_dial(struct ofono_voicecall *vc, const char *number, enum ofono_clir_option clir, ofono_voicecall_cb_t cb, void *data) { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); struct ofono_phone_number ph; if (g_slist_length(vc->call_list) >= MAX_VOICE_CALLS) return -EPERM; if (valid_ussd_string(number, vc->call_list != NULL)) return -EINVAL; if (!valid_long_phone_number_format(number)) return -EINVAL; if (ofono_modem_get_online(modem) == FALSE) return -ENETDOWN; if (vc->driver->dial == NULL) return -ENOTSUP; if (voicecalls_have_incoming(vc)) return -EBUSY; /* We can't have two dialing/alerting calls, reject outright */ if (voicecalls_num_connecting(vc) > 0) return -EBUSY; if (voicecalls_have_active(vc) && voicecalls_have_held(vc)) return -EBUSY; if (is_emergency_number(vc, number) == TRUE) __ofono_modem_inc_emergency_mode(modem); string_to_phone_number(number, &ph); if (vc->settings) { g_key_file_set_string(vc->settings, SETTINGS_GROUP, "Number", number); storage_sync(vc->imsi, SETTINGS_STORE, vc->settings); } vc->driver->dial(vc, &ph, clir, cb, vc); return 0; } static DBusMessage *manager_dial(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; const char *number; const char *clirstr; enum ofono_clir_option clir; int err; if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, DBUS_TYPE_STRING, &clirstr, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (clir_string_to_clir(clirstr, &clir) == FALSE) return __ofono_error_invalid_format(msg); vc->pending = dbus_message_ref(msg); err = voicecall_dial(vc, number, clir, manager_dial_callback, vc); if (err >= 0) return NULL; vc->pending = NULL; dbus_message_unref(msg); switch (err) { case -EINVAL: return __ofono_error_invalid_format(msg); case -ENETDOWN: return __ofono_error_not_available(msg); case -ENOTSUP: return __ofono_error_not_implemented(msg); } return __ofono_error_failed(msg); } static DBusMessage *manager_transfer(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; int numactive; int numheld; if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); numactive = voicecalls_num_active(vc); /* * According to 22.091 section 5.8, the network has the option of * implementing the call transfer operation for a call that is * still dialing/alerting. */ numactive += voicecalls_num_connecting(vc); numheld = voicecalls_num_held(vc); if (numactive != 1 || numheld != 1) return __ofono_error_failed(msg); if (vc->driver->transfer == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); vc->driver->transfer(vc, generic_callback, vc); return NULL; } static DBusMessage *manager_swap_without_accept(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; ofono_voicecall_cb_t cb; if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); vc->pending = dbus_message_ref(msg); if (voicecalls_have_active(vc) && voicecalls_have_held(vc)) cb = send_ciev_after_swap_callback; else cb = generic_callback; vc->driver->swap_without_accept(vc, cb, vc); return NULL; } static DBusMessage *manager_swap_calls(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; ofono_voicecall_cb_t cb; if (vc->driver->swap_without_accept) return manager_swap_without_accept(conn, msg, data); if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); if (voicecalls_have_waiting(vc)) return __ofono_error_failed(msg); if (vc->driver->hold_all_active == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); if (voicecalls_have_active(vc) && voicecalls_have_held(vc)) cb = send_ciev_after_swap_callback; else cb = generic_callback; vc->driver->hold_all_active(vc, cb, vc); return NULL; } static DBusMessage *manager_release_and_answer(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); if (!voicecalls_have_waiting(vc)) return __ofono_error_failed(msg); if (vc->driver->release_all_active == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); vc->driver->release_all_active(vc, generic_callback, vc); return NULL; } static DBusMessage *manager_release_and_swap(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); if (voicecalls_have_waiting(vc)) return __ofono_error_failed(msg); if (vc->driver->release_all_active == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); vc->driver->release_all_active(vc, generic_callback, vc); return NULL; } static DBusMessage *manager_hold_and_answer(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); if (voicecalls_have_waiting(vc) == FALSE) return __ofono_error_failed(msg); /* * We have waiting call and both an active and held call. According * to 22.030 we cannot use CHLD=2 in this situation. */ if (voicecalls_have_active(vc) && voicecalls_have_held(vc)) return __ofono_error_failed(msg); if (vc->driver->hold_all_active == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); vc->driver->hold_all_active(vc, generic_callback, vc); return NULL; } static DBusMessage *manager_hangup_all(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; if (vc->pending || vc->pending_em) return __ofono_error_busy(msg); if (vc->dial_req && vc->dial_req->call == NULL) return __ofono_error_busy(msg); if (vc->driver->hangup_all == NULL && (vc->driver->release_specific == NULL || vc->driver->hangup_active == NULL)) return __ofono_error_not_implemented(msg); if (vc->call_list == NULL) { DBusMessage *reply = dbus_message_new_method_return(msg); return reply; } vc->pending = dbus_message_ref(msg); if (vc->driver->hangup_all) { vc->driver->hangup_all(vc, generic_callback, vc); return NULL; } if (voicecalls_num_held(vc) > 0) vc->driver->hangup_active(vc, hangup_all_active, vc); else vc->driver->hangup_active(vc, generic_callback, vc); return NULL; } static void multiparty_callback_common(struct ofono_voicecall *vc, DBusMessage *reply) { DBusMessageIter iter; DBusMessageIter array_iter; char **objpath_list; int i; voicecalls_path_list(vc, vc->multiparty_list, &objpath_list); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter); for (i = 0; objpath_list[i]; i++) dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_OBJECT_PATH, &objpath_list[i]); dbus_message_iter_close_container(&iter, &array_iter); g_strfreev(objpath_list); } static void private_chat_callback(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; DBusMessage *reply; const char *callpath; const char *c; int id; GSList *l; GSList *old; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("command failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&vc->pending, __ofono_error_failed(vc->pending)); return; } dbus_message_get_args(vc->pending, NULL, DBUS_TYPE_OBJECT_PATH, &callpath, DBUS_TYPE_INVALID); c = strrchr(callpath, '/'); sscanf(c, "/voicecall%2u", &id); old = g_slist_copy(vc->multiparty_list); l = g_slist_find_custom(vc->multiparty_list, GINT_TO_POINTER(id), call_compare_by_id); if (l) { vc->multiparty_list = g_slist_remove(vc->multiparty_list, l->data); if (vc->multiparty_list->next == NULL) { g_slist_free(vc->multiparty_list); vc->multiparty_list = NULL; } } reply = dbus_message_new_method_return(vc->pending); multiparty_callback_common(vc, reply); __ofono_dbus_pending_reply(&vc->pending, reply); voicecalls_multiparty_changed(old, vc->multiparty_list); g_slist_free(old); } static DBusMessage *multiparty_private_chat(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; const char *path = __ofono_atom_get_path(vc->atom); const char *callpath; const char *c; unsigned int id; GSList *l; if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &callpath, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (strlen(callpath) == 0) return __ofono_error_invalid_format(msg); c = strrchr(callpath, '/'); if (c == NULL || strncmp(path, callpath, c-callpath)) return __ofono_error_not_found(msg); if (!sscanf(c, "/voicecall%2u", &id)) return __ofono_error_not_found(msg); for (l = vc->multiparty_list; l; l = l->next) { struct voicecall *v = l->data; if (v->call->id == id) break; } if (l == NULL) return __ofono_error_not_found(msg); /* * If we found id on the list of multiparty calls, then by definition * the multiparty call exists. Only thing to check is whether we have * held calls */ if (voicecalls_have_held(vc)) return __ofono_error_failed(msg); if (vc->driver->private_chat == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); vc->driver->private_chat(vc, id, private_chat_callback, vc); return NULL; } static void multiparty_create_callback(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; DBusMessage *reply; GSList *old; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("command failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&vc->pending, __ofono_error_failed(vc->pending)); return; } /* * We just created a multiparty call, gather all held * active calls and add them to the multiparty list */ old = vc->multiparty_list; vc->multiparty_list = NULL; vc->multiparty_list = g_slist_concat(vc->multiparty_list, voicecalls_held_list(vc)); vc->multiparty_list = g_slist_concat(vc->multiparty_list, voicecalls_active_list(vc)); vc->multiparty_list = g_slist_sort(vc->multiparty_list, call_compare); if (g_slist_length(vc->multiparty_list) < 2) { ofono_error("Created multiparty call, but size is less than 2" " panic!"); __ofono_dbus_pending_reply(&vc->pending, __ofono_error_failed(vc->pending)); return; } reply = dbus_message_new_method_return(vc->pending); multiparty_callback_common(vc, reply); __ofono_dbus_pending_reply(&vc->pending, reply); voicecalls_multiparty_changed(old, vc->multiparty_list); g_slist_free(old); } static DBusMessage *multiparty_create(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); if (!voicecalls_have_held(vc) || !voicecalls_have_active(vc)) return __ofono_error_failed(msg); if (vc->driver->create_multiparty == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); vc->driver->create_multiparty(vc, multiparty_create_callback, vc); return NULL; } static DBusMessage *multiparty_hangup(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; if (vc->pending || vc->dial_req || vc->pending_em) return __ofono_error_busy(msg); if (vc->driver->release_specific == NULL) return __ofono_error_not_implemented(msg); if (vc->driver->release_all_held == NULL) return __ofono_error_not_implemented(msg); if (vc->driver->release_all_active == NULL) return __ofono_error_not_implemented(msg); if (vc->multiparty_list == NULL) { DBusMessage *reply = dbus_message_new_method_return(msg); return reply; } vc->pending = dbus_message_ref(msg); /* We don't have waiting calls, as we can't use +CHLD to release */ if (!voicecalls_have_waiting(vc)) { struct voicecall *v = vc->multiparty_list->data; if (v->call->status == CALL_STATUS_HELD) { vc->driver->release_all_held(vc, generic_callback, vc); goto out; } /* * Multiparty is currently active, if we have held calls * we shouldn't use release_all_active here since this also * has the side-effect of activating held calls */ if (!voicecalls_have_held(vc)) { vc->driver->release_all_active(vc, generic_callback, vc); goto out; } } /* Fall back to the old-fashioned way */ voicecalls_release_queue(vc, vc->multiparty_list, voicecalls_release_done, FALSE); voicecalls_release_next(vc); out: return NULL; } static void tone_callback(int error, void *data) { struct ofono_voicecall *vc = data; DBusMessage *reply; if (error) reply = __ofono_error_failed(vc->pending); else reply = dbus_message_new_method_return(vc->pending); __ofono_dbus_pending_reply(&vc->pending, reply); } static DBusMessage *manager_tone(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; const char *in_tones; char *tones; int err, len; if (vc->pending) return __ofono_error_busy(msg); if (vc->driver->send_tones == NULL) return __ofono_error_not_implemented(msg); /* Send DTMFs only if we have at least one connected call */ if (!voicecalls_can_dtmf(vc)) return __ofono_error_failed(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &in_tones, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); len = strlen(in_tones); if (len == 0) return __ofono_error_invalid_format(msg); tones = g_ascii_strup(in_tones, len); err = tone_queue(vc, tones, tone_callback, vc, NULL); g_free(tones); if (err < 0) return __ofono_error_invalid_format(msg); vc->pending = dbus_message_ref(msg); return NULL; } static DBusMessage *manager_get_calls(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_voicecall *vc = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; DBusMessageIter entry, dict; const char *path; GSList *l; struct voicecall *v; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array); for (l = vc->call_list; l; l = l->next) { v = l->data; path = voicecall_build_path(vc, v->call); dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); append_voicecall_properties(v, &dict); dbus_message_iter_close_container(&entry, &dict); dbus_message_iter_close_container(&array, &entry); } dbus_message_iter_close_container(&iter, &array); return reply; } static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), manager_get_properties) }, { GDBUS_ASYNC_METHOD("Dial", GDBUS_ARGS({ "number", "s" }, { "hide_callerid", "s" }), GDBUS_ARGS({ "path", "o" }), manager_dial) }, { GDBUS_ASYNC_METHOD("Transfer", NULL, NULL, manager_transfer) }, { GDBUS_ASYNC_METHOD("SwapCalls", NULL, NULL, manager_swap_calls) }, { GDBUS_ASYNC_METHOD("ReleaseAndAnswer", NULL, NULL, manager_release_and_answer) }, { GDBUS_ASYNC_METHOD("ReleaseAndSwap", NULL, NULL, manager_release_and_swap) }, { GDBUS_ASYNC_METHOD("HoldAndAnswer", NULL, NULL, manager_hold_and_answer) }, { GDBUS_ASYNC_METHOD("HangupAll", NULL, NULL, manager_hangup_all) }, { GDBUS_ASYNC_METHOD("PrivateChat", GDBUS_ARGS({ "call", "o" }), GDBUS_ARGS({ "calls", "ao" }), multiparty_private_chat) }, { GDBUS_ASYNC_METHOD("CreateMultiparty", NULL, GDBUS_ARGS({ "calls", "o" }), multiparty_create) }, { GDBUS_ASYNC_METHOD("HangupMultiparty", NULL, NULL, multiparty_hangup) }, { GDBUS_ASYNC_METHOD("SendTones", GDBUS_ARGS({ "SendTones", "s" }), NULL, manager_tone) }, { GDBUS_METHOD("GetCalls", NULL, GDBUS_ARGS({ "calls_with_properties", "a(oa{sv})" }), manager_get_calls) }, { } }; static const GDBusSignalTable manager_signals[] = { { GDBUS_SIGNAL("Forwarded", GDBUS_ARGS({ "type", "s" })) }, { GDBUS_SIGNAL("BarringActive", GDBUS_ARGS({ "type", "s" })) }, { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { GDBUS_SIGNAL("CallAdded", GDBUS_ARGS({ "path", "o" }, { "properties", "a{sv}" })) }, { GDBUS_SIGNAL("CallRemoved", GDBUS_ARGS({ "path", "o"})) }, { } }; void ofono_voicecall_disconnected(struct ofono_voicecall *vc, int id, enum ofono_disconnect_reason reason, const struct ofono_error *error) { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); GSList *l; struct voicecall *call; time_t ts; enum call_status prev_status; const char *number; DBG("Got disconnection event for id: %d, reason: %d", id, reason); __ofono_modem_callid_release(modem, id); l = g_slist_find_custom(vc->call_list, GUINT_TO_POINTER(id), call_compare_by_id); if (l == NULL) { ofono_error("Plugin notified us of call disconnect for" " unknown call"); return; } call = l->data; ts = time(NULL); prev_status = call->call->status; l = g_slist_find_custom(vc->multiparty_list, GUINT_TO_POINTER(id), call_compare_by_id); if (l) { vc->multiparty_list = g_slist_remove(vc->multiparty_list, call); if (vc->multiparty_list->next == NULL) { /* Size == 1 */ struct voicecall *v = vc->multiparty_list->data; voicecall_emit_multiparty(v, FALSE); g_slist_free(vc->multiparty_list); vc->multiparty_list = NULL; } } vc->release_list = g_slist_remove(vc->release_list, call); if (reason != OFONO_DISCONNECT_REASON_UNKNOWN) voicecall_emit_disconnect_reason(call, reason); number = phone_number_to_string(&call->call->phone_number); if (is_emergency_number(vc, number) == TRUE) __ofono_modem_dec_emergency_mode(modem); voicecall_set_call_status(call, CALL_STATUS_DISCONNECTED); if (!call->untracked) { if (prev_status == CALL_STATUS_INCOMING || prev_status == CALL_STATUS_WAITING) __ofono_history_call_missed(modem, call->call, ts); else __ofono_history_call_ended(modem, call->call, call->detect_time, ts); } voicecalls_emit_call_removed(vc, call); voicecall_dbus_unregister(vc, call); vc->call_list = g_slist_remove(vc->call_list, call); } void ofono_voicecall_notify(struct ofono_voicecall *vc, const struct ofono_call *call) { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); GSList *l; struct voicecall *v = NULL; struct ofono_call *newcall; DBG("Got a voicecall event, status: %d, id: %u, number: %s" " called_number: %s, called_name %s", call->status, call->id, call->phone_number.number, call->called_number.number, call->name); l = g_slist_find_custom(vc->call_list, GUINT_TO_POINTER(call->id), call_compare_by_id); if (l) { DBG("Found call with id: %d", call->id); voicecall_set_call_status(l->data, call->status); voicecall_set_call_lineid(l->data, &call->phone_number, call->clip_validity); voicecall_set_call_calledid(l->data, &call->called_number); voicecall_set_call_name(l->data, call->name, call->cnap_validity); return; } DBG("Did not find a call with id: %d", call->id); __ofono_modem_callid_hold(modem, call->id); newcall = g_memdup(call, sizeof(struct ofono_call)); if (newcall == NULL) { ofono_error("Unable to allocate call"); goto error; } v = voicecall_create(vc, newcall); if (v == NULL) { ofono_error("Unable to allocate voicecall_data"); goto error; } if (vc->flags & VOICECALL_FLAG_STK_MODEM_CALLSETUP) { struct dial_request *req = vc->dial_req; const char *phone_number = phone_number_to_string(&req->ph); if (!strcmp(phone_number, "112")) __ofono_modem_inc_emergency_mode(modem); if (v->call->clip_validity == CLIP_VALIDITY_NOT_AVAILABLE) { char *number = v->call->phone_number.number; v->call->phone_number.type = req->ph.type; strncpy(number, req->ph.number, OFONO_MAX_PHONE_NUMBER_LENGTH); v->call->clip_validity = CLIP_VALIDITY_VALID; number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0'; } v->message = req->message; v->icon_id = req->icon_id; req->message = NULL; req->call = v; /* * TS 102 223 Section 6.4.13: The terminal shall not store * in the UICC the call set-up details (called party number * and associated parameters) */ v->untracked = TRUE; vc->flags &= ~VOICECALL_FLAG_STK_MODEM_CALLSETUP; } v->detect_time = time(NULL); if (!voicecall_dbus_register(v)) { ofono_error("Unable to register voice call"); goto error; } vc->call_list = g_slist_insert_sorted(vc->call_list, v, call_compare); voicecalls_emit_call_added(vc, v); return; error: if (newcall) g_free(newcall); if (v) g_free(v); } void ofono_voicecall_mpty_hint(struct ofono_voicecall *vc, unsigned int ids) { GSList *old; GSList *l; DBG("ids: %u", ids); /* id of 0 is never valid for a call */ if (ids & 0x1) return; /* Ignore the hint if there's nothing to do */ if (__builtin_popcount(ids) < 2 && vc->multiparty_list == NULL) return; old = vc->multiparty_list; vc->multiparty_list = NULL; for (l = vc->call_list; l; l = l->next) { struct voicecall *v = l->data; if (ids & (1 << v->call->id)) vc->multiparty_list = g_slist_prepend(vc->multiparty_list, v); } if (vc->multiparty_list) vc->multiparty_list = g_slist_reverse(vc->multiparty_list); if (g_slist_length(vc->multiparty_list) == 1) { ofono_error("Created multiparty list length is 1" ", which would indicate a bug in the driver" " or the remote device"); vc->multiparty_list = NULL; } voicecalls_multiparty_changed(old, vc->multiparty_list); g_slist_free(old); } static void send_ciev_after_swap_callback(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) DBG("command failed with error: %s", telephony_error_to_str(error)); if (error->type == OFONO_ERROR_TYPE_NO_ERROR) { reply = dbus_message_new_method_return(vc->pending); emulator_set_indicator_forced(vc, OFONO_EMULATOR_IND_CALLHELD, OFONO_EMULATOR_CALLHELD_MULTIPLE); } else reply = __ofono_error_failed(vc->pending); __ofono_dbus_pending_reply(&vc->pending, reply); } static void generic_callback(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) DBG("command failed with error: %s", telephony_error_to_str(error)); if (error->type == OFONO_ERROR_TYPE_NO_ERROR) reply = dbus_message_new_method_return(vc->pending); else reply = __ofono_error_failed(vc->pending); __ofono_dbus_pending_reply(&vc->pending, reply); } static void hangup_all_active(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { __ofono_dbus_pending_reply(&vc->pending, __ofono_error_failed(vc->pending)); return; } /* * If we have waiting call, we cannot use CHLD=0 due to side effects * to that call. Instead we try to hangup all calls one by one, * which might fail if the modem / AG does not support release_specific * for held calls. In that case the waiting call and held calls will * remain. */ if (vc->driver->release_all_held == NULL || voicecalls_have_waiting(vc)) { GSList *held = voicecalls_held_list(vc); voicecalls_release_queue(vc, held, voicecalls_release_done, FALSE); voicecalls_release_next(vc); g_slist_free(held); } else vc->driver->release_all_held(vc, generic_callback, vc); } static void multirelease_callback(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; if (vc->release_list != NULL) { voicecalls_release_next(vc); return; } vc->release_queue_done_cb(error, vc); } static void emit_en_list_changed(struct ofono_voicecall *vc) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(vc->atom); char **list; int i; GHashTableIter iter; gpointer key, value; list = g_new0(char *, g_hash_table_size(vc->en_list) + 1); g_hash_table_iter_init(&iter, vc->en_list); for (i = 0; g_hash_table_iter_next(&iter, &key, &value); i++) list[i] = key; ofono_dbus_signal_array_property_changed(conn, path, OFONO_VOICECALL_MANAGER_INTERFACE, "EmergencyNumbers", DBUS_TYPE_STRING, &list); g_free(list); } static void set_new_ecc(struct ofono_voicecall *vc) { g_hash_table_destroy(vc->en_list); vc->en_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); /* Emergency numbers from modem/network */ if (vc->nw_en_list) add_to_en_list(vc, vc->nw_en_list); /* Emergency numbers read from SIM */ if (vc->flags & VOICECALL_FLAG_SIM_ECC_READY) { GSList *l; for (l = vc->sim_en_list; l; l = l->next) g_hash_table_insert(vc->en_list, g_strdup(l->data), NULL); } else add_to_en_list(vc, (char **) default_en_list_no_sim); /* Default emergency numbers */ add_to_en_list(vc, (char **) default_en_list); emit_en_list_changed(vc); } static void free_sim_ecc_numbers(struct ofono_voicecall *vc, gboolean old_only) { /* * Free the currently being read EN list, just in case the * we're still reading them */ if (old_only == FALSE) { if (vc->new_sim_en_list) { g_slist_foreach(vc->new_sim_en_list, (GFunc) g_free, NULL); g_slist_free(vc->new_sim_en_list); vc->new_sim_en_list = NULL; } vc->flags &= ~VOICECALL_FLAG_SIM_ECC_READY; } if (vc->sim_en_list) { g_slist_foreach(vc->sim_en_list, (GFunc) g_free, NULL); g_slist_free(vc->sim_en_list); vc->sim_en_list = NULL; } } static void ecc_g2_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_voicecall *vc = userdata; char en[7]; DBG("%d", ok); if (!ok) return; if (total_length < 3) { ofono_error("Unable to read emergency numbers from SIM"); return; } free_sim_ecc_numbers(vc, TRUE); total_length /= 3; while (total_length--) { extract_bcd_number(data, 3, en); data += 3; if (en[0] != '\0') vc->sim_en_list = g_slist_prepend(vc->sim_en_list, g_strdup(en)); } vc->flags |= VOICECALL_FLAG_SIM_ECC_READY; set_new_ecc(vc); } static void ecc_g3_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_voicecall *vc = userdata; int total; char en[7]; DBG("%d", ok); if (!ok) goto check; if (record_length < 4 || total_length < record_length) { ofono_error("Unable to read emergency numbers from SIM"); return; } total = total_length / record_length; extract_bcd_number(data, 3, en); if (en[0] != '\0') vc->new_sim_en_list = g_slist_prepend(vc->new_sim_en_list, g_strdup(en)); if (record != total) return; check: if (!ok && vc->new_sim_en_list == NULL) return; free_sim_ecc_numbers(vc, TRUE); vc->sim_en_list = vc->new_sim_en_list; vc->new_sim_en_list = NULL; vc->flags |= VOICECALL_FLAG_SIM_ECC_READY; set_new_ecc(vc); } void ofono_voicecall_en_list_notify(struct ofono_voicecall *vc, char **nw_en_list) { g_strfreev(vc->nw_en_list); vc->nw_en_list = g_strdupv(nw_en_list); set_new_ecc(vc); } int ofono_voicecall_driver_register(const struct ofono_voicecall_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_voicecall_driver_unregister(const struct ofono_voicecall_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void emulator_remove_handler(struct ofono_atom *atom, void *data) { ofono_emulator_remove_handler(atom, data); } static void emulator_hfp_unregister(struct ofono_atom *atom) { struct ofono_voicecall *vc = __ofono_atom_get_data(atom); struct ofono_modem *modem = __ofono_atom_get_modem(atom); struct emulator_status data; data.vc = vc; data.status = OFONO_EMULATOR_CALL_INACTIVE; __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_call_status_cb, &data); data.status = OFONO_EMULATOR_CALLSETUP_INACTIVE; __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_callsetup_status_cb, &data); data.status = OFONO_EMULATOR_CALLHELD_NONE; __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_callheld_status_cb, &data); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_remove_handler, "A"); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_remove_handler, "+CHUP"); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_remove_handler, "+CLCC"); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_remove_handler, "+CHLD"); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_remove_handler, "+VTS"); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_remove_handler, "D"); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_remove_handler, "+BLDN"); __ofono_modem_remove_atom_watch(modem, vc->hfp_watch); } static void voicecall_load_settings(struct ofono_voicecall *vc) { const char *imsi; imsi = ofono_sim_get_imsi(vc->sim); if (imsi == NULL) return; vc->settings = storage_open(imsi, SETTINGS_STORE); if (vc->settings == NULL) return; vc->imsi = g_strdup(imsi); } static void voicecall_close_settings(struct ofono_voicecall *vc) { if (vc->settings) { storage_close(vc->imsi, SETTINGS_STORE, vc->settings, TRUE); g_free(vc->imsi); vc->imsi = NULL; vc->settings = NULL; } } static void voicecall_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_voicecall *vc = __ofono_atom_get_data(atom); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); GSList *l; emulator_hfp_unregister(atom); voicecall_close_settings(vc); if (vc->sim_state_watch) { ofono_sim_remove_state_watch(vc->sim, vc->sim_state_watch); vc->sim_state_watch = 0; } if (vc->sim_watch) { __ofono_modem_remove_atom_watch(modem, vc->sim_watch); vc->sim_watch = 0; } vc->sim = NULL; free_sim_ecc_numbers(vc, FALSE); if (vc->nw_en_list) { g_strfreev(vc->nw_en_list); vc->nw_en_list = NULL; } g_hash_table_destroy(vc->en_list); vc->en_list = NULL; if (vc->dial_req) dial_request_finish(vc); for (l = vc->call_list; l; l = l->next) voicecall_dbus_unregister(vc, l->data); g_slist_free(vc->call_list); vc->call_list = NULL; ofono_modem_remove_interface(modem, OFONO_VOICECALL_MANAGER_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_VOICECALL_MANAGER_INTERFACE); } static void voicecall_remove(struct ofono_atom *atom) { struct ofono_voicecall *vc = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (vc == NULL) return; if (vc->driver && vc->driver->remove) vc->driver->remove(vc); if (vc->tone_source) { g_source_remove(vc->tone_source); vc->tone_source = 0; } if (vc->toneq) { struct tone_queue_entry *entry; while ((entry = g_queue_peek_head(vc->toneq))) tone_request_finish(vc, entry, ESHUTDOWN, TRUE); g_queue_free(vc->toneq); } g_free(vc); } struct ofono_voicecall *ofono_voicecall_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_voicecall *vc; GSList *l; if (driver == NULL) return NULL; vc = g_try_new0(struct ofono_voicecall, 1); if (vc == NULL) return NULL; vc->toneq = g_queue_new(); vc->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_VOICECALL, voicecall_remove, vc); for (l = g_drivers; l; l = l->next) { const struct ofono_voicecall_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(vc, vendor, data) < 0) continue; vc->driver = drv; break; } return vc; } static void read_sim_ecc_numbers(int id, void *userdata) { struct ofono_voicecall *vc = userdata; /* Try both formats, only one or none will work */ ofono_sim_read(vc->sim_context, SIM_EFECC_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, ecc_g2_read_cb, vc); ofono_sim_read(vc->sim_context, SIM_EFECC_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, ecc_g3_read_cb, vc); } /* * When this function is called context is being destroyed from SIM. So we mark * it as such in voicecall data. This happens when the SIM atom is destroyed, * and we need to know because it can be re-created later and we will need to * create a new sim context. */ static void ecc_watch_destroy(void *userdata) { struct ofono_voicecall *vc = userdata; vc->sim_context = NULL; } static void sim_state_watch(enum ofono_sim_state new_state, void *user) { struct ofono_voicecall *vc = user; switch (new_state) { case OFONO_SIM_STATE_INSERTED: if (vc->sim_context == NULL) vc->sim_context = ofono_sim_context_create(vc->sim); read_sim_ecc_numbers(SIM_EFECC_FILEID, vc); ofono_sim_add_file_watch(vc->sim_context, SIM_EFECC_FILEID, read_sim_ecc_numbers, vc, ecc_watch_destroy); break; case OFONO_SIM_STATE_NOT_PRESENT: case OFONO_SIM_STATE_RESETTING: /* TODO: Must release all non-emergency calls */ if (vc->sim_context) { ofono_sim_context_free(vc->sim_context); vc->sim_context = NULL; } free_sim_ecc_numbers(vc, FALSE); set_new_ecc(vc); voicecall_close_settings(vc); break; case OFONO_SIM_STATE_READY: voicecall_load_settings(vc); break; case OFONO_SIM_STATE_LOCKED_OUT: voicecall_close_settings(vc); break; } } static void sim_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_voicecall *vc = data; struct ofono_sim *sim = __ofono_atom_get_data(atom); if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { voicecall_close_settings(vc); vc->sim_state_watch = 0; vc->sim = NULL; return; } vc->sim = sim; vc->sim_state_watch = ofono_sim_add_state_watch(sim, sim_state_watch, vc, NULL); sim_state_watch(ofono_sim_get_state(sim), vc); } static void emulator_send_ciev_after_swap_cb(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; if (vc->pending_em == NULL) return; ofono_emulator_send_final(vc->pending_em, error); if (error->type == OFONO_ERROR_TYPE_NO_ERROR) emulator_set_indicator_forced(vc, OFONO_EMULATOR_IND_CALLHELD, OFONO_EMULATOR_CALLHELD_MULTIPLE); vc->pending_em = NULL; } static void emulator_generic_cb(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; if (vc->pending_em == NULL) return; ofono_emulator_send_final(vc->pending_em, error); vc->pending_em = NULL; } static void emulator_mpty_join_cb(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; GSList *old; if (vc->pending_em != NULL) ofono_emulator_send_final(vc->pending_em, error); vc->pending_em = NULL; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) return; /* * We just created a multiparty call, gather all held * active calls and add them to the multiparty list */ old = vc->multiparty_list; vc->multiparty_list = NULL; vc->multiparty_list = g_slist_concat(vc->multiparty_list, voicecalls_held_list(vc)); vc->multiparty_list = g_slist_concat(vc->multiparty_list, voicecalls_active_list(vc)); vc->multiparty_list = g_slist_sort(vc->multiparty_list, call_compare); if (g_slist_length(vc->multiparty_list) < 2) { ofono_error("Created multiparty call, but size is less than 2" " panic!"); g_slist_free(old); return; } voicecalls_multiparty_changed(old, vc->multiparty_list); g_slist_free(old); } static void emulator_mpty_private_chat_cb(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; GSList *old; GSList *l; if (vc->pending_em != NULL) ofono_emulator_send_final(vc->pending_em, error); vc->pending_em = NULL; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) return; old = g_slist_copy(vc->multiparty_list); l = g_slist_find_custom(vc->multiparty_list, GINT_TO_POINTER(vc->pending_id), call_compare_by_id); if (l) { vc->multiparty_list = g_slist_remove(vc->multiparty_list, l->data); if (vc->multiparty_list->next == NULL) { g_slist_free(vc->multiparty_list); vc->multiparty_list = NULL; } } voicecalls_multiparty_changed(old, vc->multiparty_list); g_slist_free(old); } #define CHECK_BUSY(vc, em, result) \ if (vc->pending || vc->dial_req || vc->pending_em) { \ result.error = 126; \ result.type = OFONO_ERROR_TYPE_CME; \ ofono_emulator_send_final(em, &result); \ } \ static void emulator_ata_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_voicecall *vc = userdata; struct ofono_error result; result.error = 0; switch (ofono_emulator_request_get_type(req)) { case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY: CHECK_BUSY(vc, em, result) if (!voicecalls_have_incoming(vc)) goto fail; if (vc->driver->answer == NULL) goto fail; vc->pending_em = em; vc->driver->answer(vc, emulator_generic_cb, vc); break; default: fail: result.type = OFONO_ERROR_TYPE_FAILURE; ofono_emulator_send_final(em, &result); }; } static void emulator_chup_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_voicecall *vc = userdata; struct ofono_error result; result.error = 0; switch (ofono_emulator_request_get_type(req)) { case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY: if (vc->pending || vc->pending_em) goto fail; if (vc->dial_req && vc->dial_req->call == NULL) goto fail; if (vc->driver->release_specific == NULL && vc->driver->hangup_active == NULL) goto fail; if (vc->driver->hangup_active) { vc->pending_em = em; vc->driver->hangup_active(vc, emulator_generic_cb, vc); goto done; } if (voicecalls_have_active(vc) == FALSE && voicecalls_have_incoming(vc) == FALSE) goto fail; vc->pending_em = em; voicecalls_release_queue(vc, vc->call_list, emulator_generic_cb, TRUE); voicecalls_release_next(vc); done: break; default: fail: result.type = OFONO_ERROR_TYPE_FAILURE; ofono_emulator_send_final(em, &result); }; } static void emulator_clcc_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_voicecall *vc = userdata; struct ofono_error result; GSList *l; /* * idx dir stat mode mpty * '+CLCC: <0-7>,<0-1>,<0-5>,<0-9>,<0-1>,"",' + * phone number + phone type on 3 digits + terminating null */ char buf[20 + OFONO_MAX_PHONE_NUMBER_LENGTH + 3 + 1]; result.error = 0; switch (ofono_emulator_request_get_type(req)) { case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY: for (l = vc->call_list; l; l = l->next) { struct voicecall *v = l->data; const char *number = ""; int type = 128; gboolean mpty; if (g_slist_find_custom(vc->multiparty_list, GINT_TO_POINTER(v->call->id), call_compare_by_id)) mpty = TRUE; else mpty = FALSE; if (v->call->clip_validity == CLIP_VALIDITY_VALID) { number = v->call->phone_number.number; type = v->call->phone_number.type; } sprintf(buf, "+CLCC: %d,%d,%d,0,%d,\"%s\",%d", v->call->id, v->call->direction, v->call->status, mpty, number, type); ofono_emulator_send_info(em, buf, l->next == NULL ? TRUE : FALSE); } result.type = OFONO_ERROR_TYPE_NO_ERROR; break; default: result.type = OFONO_ERROR_TYPE_FAILURE; } ofono_emulator_send_final(em, &result); } #define ADD_CHLD_SUPPORT(cond, x) \ if (cond) { \ if (info[-1] != '(') \ *info++ = ','; \ \ *info++ = x[0]; \ \ if (x[1]) \ *info++ = x[1]; \ } \ static void emulator_chld_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_voicecall *vc = userdata; struct ofono_error result; char buf[64]; char *info; int chld; ofono_voicecall_cb_t cb; result.error = 0; switch (ofono_emulator_request_get_type(req)) { case OFONO_EMULATOR_REQUEST_TYPE_SET: if (!ofono_emulator_request_next_number(req, &chld)) goto fail; CHECK_BUSY(vc, em, result) switch (chld) { case 0: if (vc->driver->set_udub == NULL) goto fail; if (vc->driver->release_all_held == NULL) goto fail; vc->pending_em = em; if (voicecalls_have_waiting(vc)) { vc->driver->set_udub(vc, emulator_generic_cb, vc); return; } vc->driver->release_all_held(vc, emulator_generic_cb, vc); return; case 1: if (vc->driver->release_all_active == NULL) goto fail; vc->pending_em = em; vc->driver->release_all_active(vc, emulator_generic_cb, vc); return; case 2: if (vc->driver->hold_all_active == NULL) goto fail; if (voicecalls_have_active(vc) && voicecalls_have_held(vc)) cb = emulator_send_ciev_after_swap_cb; else cb = emulator_generic_cb; vc->pending_em = em; vc->driver->hold_all_active(vc, cb, vc); return; case 3: if (vc->driver->create_multiparty == NULL) goto fail; if (!voicecalls_have_held(vc) || !voicecalls_have_active(vc)) goto fail; vc->pending_em = em; vc->driver->create_multiparty(vc, emulator_mpty_join_cb, vc); return; case 4: if (vc->driver->transfer == NULL) goto fail; vc->pending_em = em; vc->driver->transfer(vc, emulator_generic_cb, vc); return; default: break; } if (chld >= 11 && chld <= 17) { if (vc->driver->release_specific == NULL) goto fail; vc->pending_em = em; vc->driver->release_specific(vc, chld - 10, emulator_generic_cb, vc); return; } if (chld >= 21 && chld <= 27) { GSList *l; unsigned int id = chld - 20; if (vc->driver->private_chat == NULL) goto fail; for (l = vc->multiparty_list; l; l = l->next) { struct voicecall *v = l->data; if (v->call->id == id) break; } if (l == NULL) goto fail; if (voicecalls_have_held(vc)) goto fail; vc->pending_em = em; vc->pending_id = id; vc->driver->private_chat(vc, id, emulator_mpty_private_chat_cb, vc); return; } goto fail; case OFONO_EMULATOR_REQUEST_TYPE_SUPPORT: memcpy(buf, "+CHLD: (", 8); info = buf + 8; ADD_CHLD_SUPPORT(vc->driver->release_all_held && vc->driver->set_udub, "0") ADD_CHLD_SUPPORT(vc->driver->release_all_active, "1") ADD_CHLD_SUPPORT(vc->driver->release_specific, "1x") ADD_CHLD_SUPPORT(vc->driver->hold_all_active, "2") ADD_CHLD_SUPPORT(vc->driver->private_chat, "2x") ADD_CHLD_SUPPORT(vc->driver->create_multiparty, "3") ADD_CHLD_SUPPORT(vc->driver->transfer, "4") *info++ = ')'; *info++ = '\0'; ofono_emulator_send_info(em, buf, TRUE); result.type = OFONO_ERROR_TYPE_NO_ERROR; __ofono_emulator_slc_condition(em, OFONO_EMULATOR_SLC_CONDITION_CHLD); break; case OFONO_EMULATOR_REQUEST_TYPE_QUERY: case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY: fail: result.type = OFONO_ERROR_TYPE_FAILURE; } ofono_emulator_send_final(em, &result); } static void vts_tone_cb(int error, void *data) { struct ofono_emulator *em = data; struct ofono_error result; result.error = 0; result.type = error ? OFONO_ERROR_TYPE_FAILURE : OFONO_ERROR_TYPE_NO_ERROR; ofono_emulator_send_final(em, &result); } static void emulator_vts_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_voicecall *vc = userdata; struct ofono_error result; const char *str; switch (ofono_emulator_request_get_type(req)) { case OFONO_EMULATOR_REQUEST_TYPE_SET: str = ofono_emulator_request_get_raw(req); if (str == NULL) break; if (!g_ascii_isdigit(str[0]) && str[0] != '*' && str[0] != '#' && (str[0] < 'A' || str[0] > 'D')) break; if (str[1] != '\0') break; if (__ofono_voicecall_tone_send(vc, str, vts_tone_cb, em) >= 0) return; break; default: break; } result.error = 0; result.type = OFONO_ERROR_TYPE_FAILURE; ofono_emulator_send_final(em, &result); } static void emulator_dial_callback(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; gboolean need_to_emit; struct voicecall *v; const char *number; GError *err = NULL; number = g_key_file_get_string(vc->settings, SETTINGS_GROUP, "Number", &err); v = dial_handle_result(vc, error, number, &need_to_emit); if (v == NULL) { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); if (is_emergency_number(vc, number) == TRUE) __ofono_modem_dec_emergency_mode(modem); } if (vc->pending_em) ofono_emulator_send_final(vc->pending_em, error); vc->pending_em = NULL; if (need_to_emit) voicecalls_emit_call_added(vc, v); } static void emulator_dial(struct ofono_emulator *em, struct ofono_voicecall *vc, const char *number) { struct ofono_error result; int err; result.error = 0; if (vc->pending || vc->dial_req || vc->pending_em) { result.type = OFONO_ERROR_TYPE_FAILURE; goto send; } vc->pending_em = em; err = voicecall_dial(vc, number, OFONO_CLIR_OPTION_DEFAULT, emulator_dial_callback, vc); if (err >= 0) return; vc->pending_em = NULL; switch (err) { case -ENETDOWN: result.error = 30; result.type = OFONO_ERROR_TYPE_CME; break; default: result.type = OFONO_ERROR_TYPE_FAILURE; } send: ofono_emulator_send_final(em, &result); } static void emulator_atd_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_voicecall *vc = userdata; struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); const char *str; size_t len; char number[OFONO_MAX_PHONE_NUMBER_LENGTH + 1]; struct ofono_error result; /* * If the device supports wakelocks, acquire one to * ensure the device is kept awake after being woken * by an incoming request from the HFP HF. */ wakelock_system_lock(); switch (ofono_emulator_request_get_type(req)) { case OFONO_EMULATOR_REQUEST_TYPE_SET: str = ofono_emulator_request_get_raw(req); if (str == NULL || str[0] == '\0') goto fail; len = strlen(str); if (len > OFONO_MAX_PHONE_NUMBER_LENGTH + 1 || str[len - 1] != ';') goto fail; if (len == 3 && str[0] == '>' && str[1] == '1') { struct ofono_message_waiting *mw; const struct ofono_phone_number *ph; const char *num; mw = __ofono_atom_find(OFONO_ATOM_TYPE_MESSAGE_WAITING, modem); if (mw == NULL) goto fail; ph = __ofono_message_waiting_get_mbdn(mw, 0); if (ph == NULL) goto fail; num = phone_number_to_string(ph); emulator_dial(em, vc, num); } else { strncpy(number, str, len - 1); number[len - 1] = '\0'; emulator_dial(em, vc, number); } break; default: fail: result.error = 0; result.type = OFONO_ERROR_TYPE_FAILURE; ofono_emulator_send_final(em, &result); }; } static void emulator_bldn_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_voicecall *vc = userdata; const char *number; struct ofono_error result; GError *error = NULL; switch (ofono_emulator_request_get_type(req)) { case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY: if (vc->settings == NULL) goto fail; number = g_key_file_get_string(vc->settings, SETTINGS_GROUP, "Number", &error); if (number == NULL || number[0] == '\0') goto fail; emulator_dial(em, vc, number); break; default: fail: result.error = 0; result.type = OFONO_ERROR_TYPE_FAILURE; ofono_emulator_send_final(em, &result); }; } static void emulator_hfp_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_emulator *em = __ofono_atom_get_data(atom); struct ofono_voicecall *vc = data; switch (cond) { case OFONO_ATOM_WATCH_CONDITION_UNREGISTERED: if (vc->pending_em == em) vc->pending_em = NULL; return; case OFONO_ATOM_WATCH_CONDITION_REGISTERED: break; } notify_emulator_call_status(vc); ofono_emulator_add_handler(atom, "A", emulator_ata_cb, vc, NULL); ofono_emulator_add_handler(atom, "+CHUP", emulator_chup_cb, vc, NULL); ofono_emulator_add_handler(atom, "+CLCC", emulator_clcc_cb, vc, NULL); ofono_emulator_add_handler(atom, "+CHLD", emulator_chld_cb, vc, NULL); ofono_emulator_add_handler(atom, "+VTS", emulator_vts_cb, vc, NULL); ofono_emulator_add_handler(atom, "D", emulator_atd_cb, vc, NULL); ofono_emulator_add_handler(atom, "+BLDN", emulator_bldn_cb, vc, NULL); } void ofono_voicecall_register(struct ofono_voicecall *vc) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); const char *path = __ofono_atom_get_path(vc->atom); if (!g_dbus_register_interface(conn, path, OFONO_VOICECALL_MANAGER_INTERFACE, manager_methods, manager_signals, NULL, vc, NULL)) { ofono_error("Could not create %s interface", OFONO_VOICECALL_MANAGER_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_VOICECALL_MANAGER_INTERFACE); vc->en_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); /* * Start out with the 22.101 mandated numbers, if we have a SIM and * the SIM contains EFecc, then we update the list once we've read them */ add_to_en_list(vc, (char **) default_en_list_no_sim); add_to_en_list(vc, (char **) default_en_list); vc->sim_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SIM, sim_watch, vc, NULL); __ofono_atom_register(vc->atom, voicecall_unregister); vc->hfp_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_hfp_watch, vc, NULL); } void ofono_voicecall_remove(struct ofono_voicecall *vc) { __ofono_atom_free(vc->atom); } void ofono_voicecall_set_data(struct ofono_voicecall *vc, void *data) { vc->driver_data = data; } void *ofono_voicecall_get_data(struct ofono_voicecall *vc) { return vc->driver_data; } int ofono_voicecall_get_next_callid(struct ofono_voicecall *vc) { struct ofono_modem *modem; if (vc == NULL || vc->atom == NULL) return 0; modem = __ofono_atom_get_modem(vc->atom); return __ofono_modem_callid_next(modem); } ofono_bool_t __ofono_voicecall_is_busy(struct ofono_voicecall *vc, enum ofono_voicecall_interaction type) { if (vc->pending || vc->dial_req || vc->pending_em) return TRUE; switch (type) { case OFONO_VOICECALL_INTERACTION_NONE: return vc->call_list != NULL; case OFONO_VOICECALL_INTERACTION_DISCONNECT: /* Only support releasing active calls */ if (voicecalls_num_active(vc) == g_slist_length(vc->call_list)) return FALSE; return TRUE; case OFONO_VOICECALL_INTERACTION_PUT_ON_HOLD: if (voicecalls_num_active(vc) == g_slist_length(vc->call_list)) return FALSE; if (voicecalls_num_held(vc) == g_slist_length(vc->call_list)) return FALSE; return TRUE; } return TRUE; } static void dial_request_cb(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; const char *number = phone_number_to_string(&vc->dial_req->ph); gboolean need_to_emit; struct voicecall *v; v = dial_handle_result(vc, error, number, &need_to_emit); if (v == NULL) { if (is_emergency_number(vc, number) == TRUE) { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); __ofono_modem_dec_emergency_mode(modem); } dial_request_finish(vc); return; } v->message = vc->dial_req->message; v->icon_id = vc->dial_req->icon_id; vc->dial_req->message = NULL; vc->dial_req->call = v; /* * TS 102 223 Section 6.4.13: The terminal shall not store * in the UICC the call set-up details (called party number * and associated parameters) */ v->untracked = TRUE; if (v->call->status == CALL_STATUS_ACTIVE) dial_request_finish(vc); if (need_to_emit) voicecalls_emit_call_added(vc, v); } static void dial_request(struct ofono_voicecall *vc) { const char *number = phone_number_to_string(&vc->dial_req->ph); if (is_emergency_number(vc, number) == TRUE) { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); __ofono_modem_inc_emergency_mode(modem); } vc->driver->dial(vc, &vc->dial_req->ph, OFONO_CLIR_OPTION_DEFAULT, dial_request_cb, vc); } static void dial_req_disconnect_cb(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { dial_request_finish(vc); return; } /* * Note that the callback might come back fore we receive call * disconnection notifications. So it makes no sense to recheck * whether we can dial here. We simply dial and hope for the best. */ dial_request(vc); } int __ofono_voicecall_dial(struct ofono_voicecall *vc, const char *addr, int addr_type, const char *message, unsigned char icon_id, enum ofono_voicecall_interaction interaction, ofono_voicecall_dial_cb_t cb, void *user_data) { struct dial_request *req; if (!valid_long_phone_number_format(addr)) return -EINVAL; if (vc->driver->dial == NULL) return -ENOSYS; if (interaction == OFONO_VOICECALL_INTERACTION_DISCONNECT && vc->driver->release_all_active == NULL) return -ENOSYS; if (__ofono_voicecall_is_busy(vc, interaction) == TRUE) return -EBUSY; /* * TODO: if addr starts with "112", possibly translate into the * technology-specific emergency number. */ req = g_try_new0(struct dial_request, 1); if (req == NULL) return -ENOMEM; req->message = g_strdup(message); req->icon_id = icon_id; req->interaction = interaction; req->cb = cb; req->user_data = user_data; /* TODO: parse the tones to dial after call connected */ req->ph.type = addr_type; strncpy(req->ph.number, addr, OFONO_MAX_PHONE_NUMBER_LENGTH); vc->dial_req = req; switch (interaction) { case OFONO_VOICECALL_INTERACTION_NONE: dial_request(vc); break; case OFONO_VOICECALL_INTERACTION_PUT_ON_HOLD: /* Note: dialling automatically puts active calls on hold */ dial_request(vc); break; case OFONO_VOICECALL_INTERACTION_DISCONNECT: if (voicecalls_have_active(vc)) vc->driver->release_all_active(vc, dial_req_disconnect_cb, vc); else dial_request(vc); break; } return 0; } void __ofono_voicecall_dial_cancel(struct ofono_voicecall *vc) { if (vc->dial_req == NULL || vc->dial_req->cb == NULL) return; vc->dial_req->cb = NULL; } static void tone_request_cb(const struct ofono_error *error, void *data) { struct ofono_voicecall *vc = data; struct tone_queue_entry *entry = g_queue_peek_head(vc->toneq); int len = 0; if (entry == NULL) return; /* * Call back with error only if the error is related to the * current entry. If the error corresponds to a cancelled * request, do nothing. */ if (error && error->type != OFONO_ERROR_TYPE_NO_ERROR && entry->left > entry->tone_str) { DBG("command failed with error: %s", telephony_error_to_str(error)); tone_request_finish(vc, entry, EIO, TRUE); goto done; } if (*entry->left == '\0') { tone_request_finish(vc, entry, 0, TRUE); goto done; } len = strspn(entry->left, "pP"); entry->left += len; done: /* * Wait 3 seconds per PAUSE, same as for DTMF separator characters * passed in a telephone number according to TS 22.101 A.21, * although 27.007 claims this delay can be set using S8 and * defaults to 2 seconds. */ vc->tone_source = g_timeout_add_seconds(len * 3, tone_request_run, vc); } static gboolean tone_request_run(gpointer user_data) { struct ofono_voicecall *vc = user_data; struct tone_queue_entry *entry = g_queue_peek_head(vc->toneq); char final; unsigned len; vc->tone_source = 0; if (entry == NULL) return FALSE; len = strcspn(entry->left, "pP"); if (len) { if (len > 8) /* Arbitrary length limit per request */ len = 8; /* Temporarily move the end of the string */ final = entry->left[len]; entry->left[len] = '\0'; vc->driver->send_tones(vc, entry->left, tone_request_cb, vc); entry->left += len; entry->left[0] = final; } else tone_request_cb(NULL, vc); return FALSE; } int __ofono_voicecall_tone_send(struct ofono_voicecall *vc, const char *tone_str, ofono_voicecall_tone_cb_t cb, void *user_data) { if (vc->driver->send_tones == NULL) return -ENOSYS; /* Send DTMFs only if we have at least one connected call */ if (!voicecalls_can_dtmf(vc)) return -ENOENT; return tone_queue(vc, tone_str, cb, user_data, NULL); } void __ofono_voicecall_tone_cancel(struct ofono_voicecall *vc, int id) { struct tone_queue_entry *entry; int n = 0; while ((entry = g_queue_peek_nth(vc->toneq, n++)) != NULL) if (entry->id == id) break; tone_request_finish(vc, entry, 0, FALSE); /* * If we were in the middle of a PAUSE, wake queue up * now, else wake up when current tone finishes. */ if (n == 1 && vc->tone_source) { g_source_remove(vc->tone_source); tone_request_run(vc); } } void __ofono_voicecall_set_alpha_and_icon_id(struct ofono_voicecall *vc, const char *addr, int addr_type, const char *message, unsigned char icon_id) { struct dial_request *req; req = g_new0(struct dial_request, 1); req->message = g_strdup(message); req->icon_id = icon_id; req->ph.type = addr_type; strncpy(req->ph.number, addr, OFONO_MAX_PHONE_NUMBER_LENGTH); vc->dial_req = req; vc->flags |= VOICECALL_FLAG_STK_MODEM_CALLSETUP; DBG("%p, %p", vc, vc->dial_req); } void __ofono_voicecall_clear_alpha_and_icon_id(struct ofono_voicecall *vc) { DBG("%p, %p", vc, vc->dial_req); if (vc->dial_req) { g_free(vc->dial_req->message); vc->dial_req->message = NULL; g_free(vc->dial_req); vc->dial_req = NULL; } vc->flags &= ~VOICECALL_FLAG_STK_MODEM_CALLSETUP; } static void ssn_mt_forwarded_notify(struct ofono_voicecall *vc, unsigned int id, int code, const struct ofono_phone_number *ph) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(vc->atom); char *info = "incoming"; g_dbus_emit_signal(conn, path, OFONO_VOICECALL_MANAGER_INTERFACE, "Forwarded", DBUS_TYPE_STRING, &info, DBUS_TYPE_INVALID); } static struct voicecall *voicecall_select(struct ofono_voicecall *vc, unsigned int id) { if (id != 0) { GSList *l = g_slist_find_custom(vc->call_list, GUINT_TO_POINTER(id), call_compare_by_id); if (l == NULL) return NULL; return l->data; } if (g_slist_length(vc->call_list) == 1) return vc->call_list->data; return NULL; } static void ssn_mt_remote_held_notify(struct ofono_voicecall *vc, unsigned int id, gboolean held, const struct ofono_phone_number *ph) { struct voicecall *v = voicecall_select(vc, id); DBusConnection *conn = ofono_dbus_get_connection(); const char *path; if (v == NULL) return; if (v->remote_held == held) return; v->remote_held = held; path = voicecall_build_path(vc, v->call); ofono_dbus_signal_property_changed(conn, path, OFONO_VOICECALL_INTERFACE, "RemoteHeld", DBUS_TYPE_BOOLEAN, &v->remote_held); } static void ssn_mt_remote_multiparty_notify(struct ofono_voicecall *vc, unsigned int id, const struct ofono_phone_number *ph) { struct voicecall *v = voicecall_select(vc, id); DBusConnection *conn = ofono_dbus_get_connection(); const char *path; if (v == NULL) return; if (v->remote_multiparty == TRUE) return; v->remote_multiparty = TRUE; path = voicecall_build_path(vc, v->call); ofono_dbus_signal_property_changed(conn, path, OFONO_VOICECALL_INTERFACE, "RemoteMultiparty", DBUS_TYPE_BOOLEAN, &v->remote_multiparty); } void ofono_voicecall_ssn_mt_notify(struct ofono_voicecall *vc, unsigned int id, int code, int index, const struct ofono_phone_number *ph) { switch (code) { case SS_MT_CALL_FORWARDED: ssn_mt_forwarded_notify(vc, id, code, ph); break; case SS_MT_VOICECALL_ON_HOLD: ssn_mt_remote_held_notify(vc, id, TRUE, ph); break; case SS_MT_VOICECALL_RETRIEVED: ssn_mt_remote_held_notify(vc, id, FALSE, ph); break; case SS_MT_MULTIPARTY_VOICECALL: ssn_mt_remote_multiparty_notify(vc, id, ph); break; } } static void ssn_mo_call_barred_notify(struct ofono_voicecall *vc, unsigned int id, int code) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(vc->atom); const char *info; if (code == SS_MO_INCOMING_BARRING) info = "remote"; else info = "local"; g_dbus_emit_signal(conn, path, OFONO_VOICECALL_MANAGER_INTERFACE, "BarringActive", DBUS_TYPE_STRING, &info, DBUS_TYPE_INVALID); } static void ssn_mo_forwarded_notify(struct ofono_voicecall *vc, unsigned int id, int code) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(vc->atom); char *info = "outgoing"; g_dbus_emit_signal(conn, path, OFONO_VOICECALL_MANAGER_INTERFACE, "Forwarded", DBUS_TYPE_STRING, &info, DBUS_TYPE_INVALID); } void ofono_voicecall_ssn_mo_notify(struct ofono_voicecall *vc, unsigned int id, int code, int index) { switch (code) { case SS_MO_OUTGOING_BARRING: case SS_MO_INCOMING_BARRING: ssn_mo_call_barred_notify(vc, id, code); break; case SS_MO_CALL_FORWARDED: ssn_mo_forwarded_notify(vc, id, code); break; } } ofono-1.17.bzr6912+16.04.20160314.3/src/sim-auth.c0000644000015600001650000000544012671500024021064 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include "ofono.h" #include "simutil.h" static GSList *g_drivers = NULL; struct ofono_sim_auth { const struct ofono_sim_auth_driver *driver; void *driver_data; struct ofono_atom *atom; }; int ofono_sim_auth_driver_register(const struct ofono_sim_auth_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_sim_auth_driver_unregister(const struct ofono_sim_auth_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void sim_auth_unregister(struct ofono_atom *atom) { } static void sim_auth_remove(struct ofono_atom *atom) { struct ofono_sim_auth *sa = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (sa == NULL) return; if (sa->driver && sa->driver->remove) sa->driver->remove(sa); g_free(sa); } struct ofono_sim_auth *ofono_sim_auth_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_sim_auth *sa; GSList *l; if (driver == NULL) return NULL; sa = g_try_new0(struct ofono_sim_auth, 1); if (sa == NULL) return NULL; sa->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_SIM_AUTH, sim_auth_remove, sa); for (l = g_drivers; l; l = l->next) { const struct ofono_sim_auth_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(sa, vendor, data) < 0) continue; sa->driver = drv; break; } return sa; } void ofono_sim_auth_register(struct ofono_sim_auth *sa) { __ofono_atom_register(sa->atom, sim_auth_unregister); } void ofono_sim_auth_remove(struct ofono_sim_auth *sa) { __ofono_atom_free(sa->atom); } void ofono_sim_auth_set_data(struct ofono_sim_auth *sa, void *data) { sa->driver_data = data; } void *ofono_sim_auth_get_data(struct ofono_sim_auth *sa) { return sa->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/audio-settings.c0000644000015600001650000001371512671500024022300 0ustar pbuserpbgroup00000000000000/* * * 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 veasion 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 #endif #include #include #include #include #include #include "ofono.h" #include "common.h" static GSList *g_drivers = NULL; struct ofono_audio_settings { ofono_bool_t active; char *mode; const struct ofono_audio_settings_driver *driver; void *driver_data; struct ofono_atom *atom; }; void ofono_audio_settings_active_notify(struct ofono_audio_settings *as, ofono_bool_t active) { const char *path = __ofono_atom_get_path(as->atom); DBusConnection *conn = ofono_dbus_get_connection(); if (as->active == active) return; DBG("active %d", active); as->active = active; ofono_dbus_signal_property_changed(conn, path, OFONO_AUDIO_SETTINGS_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &as->active); } void ofono_audio_settings_mode_notify(struct ofono_audio_settings *as, const char *mode) { const char *path = __ofono_atom_get_path(as->atom); DBusConnection *conn = ofono_dbus_get_connection(); DBG("mode %s", mode); g_free(as->mode); as->mode = g_strdup(mode); if (as->mode == NULL) return; ofono_dbus_signal_property_changed(conn, path, OFONO_AUDIO_SETTINGS_INTERFACE, "Mode", DBUS_TYPE_STRING, &as->mode); } static DBusMessage *audio_get_properties_reply(DBusMessage *msg, struct ofono_audio_settings *as) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "Active", DBUS_TYPE_BOOLEAN, &as->active); if (as->mode) ofono_dbus_dict_append(&dict, "Mode", DBUS_TYPE_STRING, &as->mode); dbus_message_iter_close_container(&iter, &dict); return reply; } static DBusMessage *audio_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_audio_settings *as = data; return audio_get_properties_reply(msg, as); } static const GDBusMethodTable audio_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), audio_get_properties) }, { } }; static const GDBusSignalTable audio_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; int ofono_audio_settings_driver_register(const struct ofono_audio_settings_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_audio_settings_driver_unregister(const struct ofono_audio_settings_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void audio_settings_unregister(struct ofono_atom *atom) { struct ofono_audio_settings *as = __ofono_atom_get_data(atom); const char *path = __ofono_atom_get_path(as->atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(as->atom); ofono_modem_remove_interface(modem, OFONO_AUDIO_SETTINGS_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_AUDIO_SETTINGS_INTERFACE); } static void audio_settings_remove(struct ofono_atom *atom) { struct ofono_audio_settings *as = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (as == NULL) return; if (as->driver && as->driver->remove) as->driver->remove(as); g_free(as->mode); g_free(as); } struct ofono_audio_settings *ofono_audio_settings_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_audio_settings *as; GSList *l; if (driver == NULL) return NULL; as = g_try_new0(struct ofono_audio_settings, 1); if (as == NULL) return NULL; as->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_AUDIO_SETTINGS, audio_settings_remove, as); for (l = g_drivers; l; l = l->next) { const struct ofono_audio_settings_driver *drv = l->data; if (g_strcmp0(drv->name, driver) != 0) continue; if (drv->probe(as, vendor, data) < 0) continue; as->driver = drv; break; } return as; } void ofono_audio_settings_register(struct ofono_audio_settings *as) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(as->atom); const char *path = __ofono_atom_get_path(as->atom); if (!g_dbus_register_interface(conn, path, OFONO_AUDIO_SETTINGS_INTERFACE, audio_methods, audio_signals, NULL, as, NULL)) { ofono_error("Could not create %s interface", OFONO_AUDIO_SETTINGS_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_AUDIO_SETTINGS_INTERFACE); __ofono_atom_register(as->atom, audio_settings_unregister); } void ofono_audio_settings_remove(struct ofono_audio_settings *as) { __ofono_atom_free(as->atom); } void ofono_audio_settings_set_data(struct ofono_audio_settings *as, void *data) { as->driver_data = data; } void *ofono_audio_settings_get_data(struct ofono_audio_settings *as) { return as->driver_data; } struct ofono_modem *ofono_audio_settings_get_modem(struct ofono_audio_settings *as) { return __ofono_atom_get_modem(as->atom); } ofono-1.17.bzr6912+16.04.20160314.3/src/common.h0000644000015600001650000001205212671500024020627 0ustar pbuserpbgroup00000000000000/* * * 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 * */ /* 27.007 Section 7.3 */ enum access_technology { ACCESS_TECHNOLOGY_GSM = 0, ACCESS_TECHNOLOGY_GSM_COMPACT = 1, ACCESS_TECHNOLOGY_UTRAN = 2, ACCESS_TECHNOLOGY_GSM_EGPRS = 3, ACCESS_TECHNOLOGY_UTRAN_HSDPA = 4, ACCESS_TECHNOLOGY_UTRAN_HSUPA = 5, ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA = 6, ACCESS_TECHNOLOGY_EUTRAN = 7, }; /* 27.007 Section 7.2 */ enum network_registration_status { NETWORK_REGISTRATION_STATUS_NOT_REGISTERED = 0, NETWORK_REGISTRATION_STATUS_REGISTERED = 1, NETWORK_REGISTRATION_STATUS_SEARCHING = 2, NETWORK_REGISTRATION_STATUS_DENIED = 3, NETWORK_REGISTRATION_STATUS_UNKNOWN = 4, NETWORK_REGISTRATION_STATUS_ROAMING = 5, }; /* 27.007 Section 7.3 */ enum operator_status { OPERATOR_STATUS_UNKNOWN = 0, OPERATOR_STATUS_AVAILABLE = 1, OPERATOR_STATUS_CURRENT = 2, OPERATOR_STATUS_FORBIDDEN = 3, }; /* 27.007 Section 7.6 */ enum clip_validity { CLIP_VALIDITY_VALID = 0, CLIP_VALIDITY_WITHHELD = 1, CLIP_VALIDITY_NOT_AVAILABLE = 2, }; /* 27.007 Section 7.29 */ enum packet_bearer { PACKET_BEARER_NONE = 0, PACKET_BEARER_GPRS = 1, PACKET_BEARER_EGPRS = 2, PACKET_BEARER_UMTS = 3, PACKET_BEARER_HSUPA = 4, PACKET_BEARER_HSDPA = 5, PACKET_BEARER_HSUPA_HSDPA = 6, PACKET_BEARER_EPS = 7, }; /* 27.007 Section 7.30 */ enum cnap_validity { CNAP_VALIDITY_VALID = 0, CNAP_VALIDITY_WITHHELD = 1, CNAP_VALIDITY_NOT_AVAILABLE = 2, }; /* 27.007 Section 7.18 */ enum call_status { CALL_STATUS_ACTIVE = 0, CALL_STATUS_HELD = 1, CALL_STATUS_DIALING = 2, CALL_STATUS_ALERTING = 3, CALL_STATUS_INCOMING = 4, CALL_STATUS_WAITING = 5, CALL_STATUS_DISCONNECTED }; /* 27.007 Section 7.18 */ enum call_direction { CALL_DIRECTION_MOBILE_ORIGINATED = 0, CALL_DIRECTION_MOBILE_TERMINATED = 1, }; /* 27.007 Section 7.11 */ enum bearer_class { BEARER_CLASS_VOICE = 1, BEARER_CLASS_DATA = 2, BEARER_CLASS_FAX = 4, BEARER_CLASS_DEFAULT = 7, BEARER_CLASS_SMS = 8, BEARER_CLASS_DATA_SYNC = 16, BEARER_CLASS_DATA_ASYNC = 32, /* According to 22.030, types 1-12 */ BEARER_CLASS_SS_DEFAULT = 61, BEARER_CLASS_PACKET = 64, BEARER_CLASS_PAD = 128, }; /* 22.030 Section 6.5.2 */ enum ss_control_type { SS_CONTROL_TYPE_ACTIVATION, SS_CONTROL_TYPE_DEACTIVATION, SS_CONTROL_TYPE_QUERY, SS_CONTROL_TYPE_REGISTRATION, SS_CONTROL_TYPE_ERASURE, }; /* TS 27.007 Supplementary service notifications +CSSN */ enum ss_cssi { SS_MO_UNCONDITIONAL_FORWARDING = 0, SS_MO_CONDITIONAL_FORWARDING = 1, SS_MO_CALL_FORWARDED = 2, SS_MO_CALL_WAITING = 3, SS_MO_CUG_CALL = 4, SS_MO_OUTGOING_BARRING = 5, SS_MO_INCOMING_BARRING = 6, SS_MO_CLIR_SUPPRESSION_REJECTED = 7, SS_MO_CALL_DEFLECTED = 8, }; enum ss_cssu { SS_MT_CALL_FORWARDED = 0, SS_MT_CUG_CALL = 1, SS_MT_VOICECALL_ON_HOLD = 2, SS_MT_VOICECALL_RETRIEVED = 3, SS_MT_MULTIPARTY_VOICECALL = 4, SS_MT_VOICECALL_HOLD_RELEASED = 5, SS_MT_FORWARD_CHECK_SS_MESSAGE = 6, SS_MT_VOICECALL_IN_TRANSFER = 7, SS_MT_VOICECALL_TRANSFERRED = 8, SS_MT_CALL_DEFLECTED = 9, }; /* 27.007 Section 10.1.10 */ enum context_status { CONTEXT_STATUS_DEACTIVATED = 0, CONTEXT_STATUS_ACTIVATED = 1, }; const char *telephony_error_to_str(const struct ofono_error *error); gboolean valid_number_format(const char *number, int length); gboolean valid_phone_number_format(const char *number); gboolean valid_long_phone_number_format(const char *number); const char *phone_number_to_string(const struct ofono_phone_number *ph); void string_to_phone_number(const char *str, struct ofono_phone_number *ph); gboolean valid_cdma_phone_number_format(const char *number); const char *cdma_phone_number_to_string( const struct ofono_cdma_phone_number *ph); void string_to_cdma_phone_number(const char *str, struct ofono_cdma_phone_number *ph); int mmi_service_code_to_bearer_class(int code); gboolean valid_ussd_string(const char *str, gboolean call_in_progress); gboolean parse_ss_control_string(char *str, int *ss_type, char **sc, char **sia, char **sib, char **sic, char **sid, char **dn); const char *ss_control_type_to_string(enum ss_control_type type); const char *bearer_class_to_string(enum bearer_class cls); const char *registration_status_to_string(int status); const char *registration_tech_to_string(int tech); const char *packet_bearer_to_string(int bearer); gboolean is_valid_apn(const char *apn); ofono-1.17.bzr6912+16.04.20160314.3/src/cdma-smsutil.h0000644000015600001650000002267712671500024021757 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 CDMA_SMS_MAX_ADDR_FIELDS 256 #define CDMA_SMS_UD_LEN 512 /* 3GPP2 C.S0015-B v2.0, Table 3.4-1 */ enum cdma_sms_tp_msg_type { CDMA_SMS_TP_MSG_TYPE_P2P = 0, CDMA_SMS_TP_MSG_TYPE_BCAST = 1, CDMA_SMS_TP_MSG_TYPE_ACK = 2 }; /* * 3GPP2 X.S0004-550-E, Section 2.256 * Only supported by 3GPP2 C.S0015-B v2.0 Section 3.4.3.1 listed. */ enum cdma_sms_teleservice_id { CDMA_SMS_TELESERVICE_ID_CMT91 = 4096, CDMA_SMS_TELESERVICE_ID_WPT = 4097, CDMA_SMS_TELESERVICE_ID_WMT = 4098, CDMA_SMS_TELESERVICE_ID_VMN = 4099, CDMA_SMS_TELESERVICE_ID_WAP = 4100, CDMA_SMS_TELESERVICE_ID_WEMT = 4101, CDMA_SMS_TELESERVICE_ID_SCPT = 4102, CDMA_SMS_TELESERVICE_ID_CATPT = 4103 }; /* 3GPP2 C.S0015-B v2.0 Section 3.4.3.3 */ enum cdma_sms_num_mode { CDMA_SMS_NUM_MODE_DIGIT = 0, CDMA_SMS_NUM_MODE_DATA_NW = 1 }; /* 3GPP2 C.S0005-E v2.0 Table 2.7.1.3.2.4-2 */ enum cdma_sms_digi_num_type { CDMA_SMS_DIGI_NUM_TYPE_UNKNOWN = 0, CDMA_SMS_DIGI_NUM_TYPE_INTERNATIONAL = 1, CDMA_SMS_DIGI_NUM_TYPE_NATIONAL = 2, CDMA_SMS_DIGI_NUM_TYPE_NETWORK = 3, CDMA_SMS_DIGI_NUM_TYPE_SUBSCRIBER = 4, CDMA_SMS_DIGI_NUM_TYPE_RESERVED1 = 5, CDMA_SMS_DIGI_NUM_TYPE_ABBREVIATED = 6, CDMA_SMS_DIGI_NUM_TYPE_RESERVED2 = 7 }; /* 3GPP2 C.S0015-B v2.0 Table 3.4.3.3-1 */ enum cdma_sms_data_nw_num_type { CDMA_SMS_DATA_NW_NUM_TYPE_UNKNOWN = 0, CDMA_SMS_DATA_NW_NUM_TYPE_INTERNET_PROTOCOL = 1, CDMA_SMS_DATA_NW_NUM_TYPE_INTERNET_EMAIL_ADDRESS = 2, /* All Other Values Reserved */ }; /* 3GPP2 C.S0005-E v2.0 Table 2.7.1.3.2.4-3 */ enum cdma_sms_numbering_plan { CDMA_SMS_NUMBERING_PLAN_UNKNOWN = 0, CDMA_SMS_NUMBERING_PLAN_ISDN = 1, CDMA_SMS_NUMBERING_PLAN_DATA = 3, CDMA_SMS_NUMBERING_PLAN_TELEX = 4, CDMA_SMS_NUMBERING_PLAN_PRIVATE = 9, CDMA_SMS_NUMBERING_PLAN_RESERVED = 15 }; /* 3GPP2 C.S0015-B v2.0 Table 4.5.1-1 */ enum cdma_sms_msg_type { CDMA_SMS_MSG_TYPE_RESERVED = 0, CDMA_SMS_MSG_TYPE_DELIVER = 1, CDMA_SMS_MSG_TYPE_SUBMIT = 2, CDMA_SMS_MSG_TYPE_CANCEL = 3, CDMA_SMS_MSG_TYPE_DELIVER_ACK = 4, CDMA_SMS_MSG_TYPE_USER_ACK = 5, CDMA_SMS_MSG_TYPE_READ_ACK = 6, CDMA_SMS_MSG_TYPE_DELIVER_REPORT = 7, CDMA_SMS_MSG_TYPE_SUBMIT_REPORT = 8, }; /* C.R1001-G_v1.0 Table 9.1-1 */ enum cdma_sms_msg_encoding { CDMA_SMS_MSG_ENCODING_OCTET = 0, CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG = 1, CDMA_SMS_MSG_ENCODING_7BIT_ASCII = 2, CDMA_SMS_MSG_ENCODING_IA5 = 3, CDMA_SMS_MSG_ENCODING_UNICODE = 4, CDMA_SMS_MSG_ENCODING_SHIFT_JIS = 5, CDMA_SMS_MSG_ENCODING_KOREAN = 6, CDMA_SMS_MSG_ENCODING_LATIN_HEBREW = 7, CDMA_SMS_MSG_ENCODING_LATIN = 8, CDMA_SMS_MSG_ENCODING_GSM_7BIT = 9, CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING = 10 }; /* 3GPP2 C.S0015-B v2.0 Table 3.4.3-1 */ enum cdma_sms_param_id { CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER = 0x00, CDMA_SMS_PARAM_ID_SERVICE_CATEGORY = 0x01, CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS = 0x02, CDMA_SMS_PARAM_ID_ORIGINATING_SUBADDRESS = 0x03, CDMA_SMS_PARAM_ID_DESTINATION_ADDRESS = 0x04, CDMA_SMS_PARAM_ID_DESTINATION_SUBADDRESS = 0x05, CDMA_SMS_PARAM_ID_BEARER_REPLY_OPTION = 0x06, CDMA_SMS_PARAM_ID_CAUSE_CODE = 0x07, CDMA_SMS_PARAM_ID_BEARER_DATA = 0x08 }; /* 3GPP2 C.S0015-B v2.0 Table 4.5-1 */ enum cdma_sms_subparam_id { CDMA_SMS_SUBPARAM_ID_MESSAGE_ID = 0x00, CDMA_SMS_SUBPARAM_ID_USER_DATA = 0x01, CDMA_SMS_SUBPARAM_ID_USER_RESPONSE_CODE = 0x02, CDMA_SMS_SUBPARAM_ID_MC_TIME_STAMP = 0x03, CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_ABSOLUTE = 0x04, CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_RELATIVE = 0x05, CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE = 0x06, CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_RELATIVE = 0x07, CDMA_SMS_SUBPARAM_ID_PRIORITY_INDICATOR = 0x08, CDMA_SMS_SUBPARAM_ID_PRIVACY_INDICATOR = 0x09, CDMA_SMS_SUBPARAM_ID_REPLY_OPTION = 0x0A, CDMA_SMS_SUBPARAM_ID_NUMBER_OF_MESSAGES = 0x0B, CDMA_SMS_SUBPARAM_ID_ALERT_ON_MESSAGE_DELIVERY = 0x0C, CDMA_SMS_SUBPARAM_ID_LANGUAGE_INDICATOR = 0x0D, CDMA_SMS_SUBPARAM_ID_CALL_BACK_NUMBER = 0x0E, CDMA_SMS_SUBPARAM_ID_MESSAGE_DISPLAY_MODE = 0x0F, CDMA_SMS_SUBPARAM_ID_MULTIPLE_ENCODING_USER_DATA = 0x10, CDMA_SMS_SUBPARAM_ID_MESSAGE_DEPOSIT_INDEX = 0x11, CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_DATA = 0x12, CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_RESULT = 0x13, CDMA_SMS_SUBPARAM_ID_MESSAGE_STATUS = 0x14, CDMA_SMS_SUBPARAM_ID_TP_FAILURE_CAUSE = 0x15, CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN = 0x16, CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN_ACK = 0x17 }; /* 3GPP2 C.R1001-G Table 9.3.1-1 and 9.3.3-1 */ enum cdma_sms_service_cat { CDMA_SMS_SERVICE_CAT_EMERGENCY_BROADCAST = 0x0001, CDMA_SMS_SERVICE_CAT_ADMINISTRATIVE = 0x0002, CDMA_SMS_SERVICE_CAT_MAINTENANCE = 0x0003, CDMA_SMS_SERVICE_CAT_GEN_NEWS_LOCAL = 0x0004, CDMA_SMS_SERVICE_CAT_GEN_NEWS_REGIONAL = 0x0005, CDMA_SMS_SERVICE_CAT_GEN_NEWS_NATIONAL = 0x0006, CDMA_SMS_SERVICE_CAT_GEN_NEWS_INT = 0x0007, CDMA_SMS_SERVICE_CAT_FIN_NEWS_LOCAL = 0x0008, CDMA_SMS_SERVICE_CAT_FIN_NEWS_REGIONAL = 0x0009, CDMA_SMS_SERVICE_CAT_FIN_NEWS_NATIONAL = 0x000A, CDMA_SMS_SERVICE_CAT_FIN_NEWS_INT = 0x000B, CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_LOCAL = 0x000C, CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_REGIONAL = 0x000D, CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_NATIONAL = 0x000E, CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_INT = 0x000F, CDMA_SMS_SERVICE_CAT_ENT_NEWS_LOCAL = 0x0010, CDMA_SMS_SERVICE_CAT_ENT_NEWS_REGIONAL = 0x0011, CDMA_SMS_SERVICE_CAT_ENT_NEWS_NATIONAL = 0x0012, CDMA_SMS_SERVICE_CAT_ENT_NEWS_INT = 0x0013, CDMA_SMS_SERVICE_CAT_LOCAL_WEATHER = 0x0014, CDMA_SMS_SERVICE_CAT_TRAFFIC_REPORT = 0x0015, CDMA_SMS_SERVICE_CAT_FLIGHT_SCHED = 0x0016, CDMA_SMS_SERVICE_CAT_RESTAURANT = 0x0017, CDMA_SMS_SERVICE_CAT_LODGINGS = 0x0018, CDMA_SMS_SERVICE_CAT_RETAIL_DIR = 0x0019, CDMA_SMS_SERVICE_CAT_ADVERTISEMENTS = 0x001A, CDMA_SMS_SERVICE_CAT_STOCK_QUOTES = 0x001B, CDMA_SMS_SERVICE_CAT_EMPLOYMENT = 0x001C, CDMA_SMS_SERVICE_CAT_HOSPITAL = 0x001D, CDMA_SMS_SERVICE_CAT_TECH_NEWS = 0x001E, CDMA_SMS_SERVICE_CAT_MULTICATEGORY = 0x001F, CDMA_SMS_SERVICE_CAT_CAPT = 0x0020, CDMA_SMS_SERVICE_CAT_PRESIDENTIAL_ALERT = 0x1000, CDMA_SMS_SERVICE_CAT_EXTREME_THREAT = 0x1001, CDMA_SMS_SERVICE_CAT_SEVERE_THREAT = 0x1002, CDMA_SMS_SERVICE_CAT_AMBER = 0x1003, CDMA_SMS_SERVICE_CAT_CMAS_TEST = 0x1004 }; /* 3GPP2 C.S0015-B v2.0 Section 3.4.3.3 */ enum cdma_sms_digit_mode { CDMA_SMS_DIGIT_MODE_4BIT_DTMF = 0, CDMA_SMS_DIGIT_MODE_8BIT_ASCII = 1 }; /* 3GPP2 C.S0015-B v2.0 Section 3.4.3.3 */ struct cdma_sms_address { enum cdma_sms_digit_mode digit_mode; enum cdma_sms_num_mode number_mode; union { enum cdma_sms_digi_num_type digi_num_type; enum cdma_sms_data_nw_num_type data_nw_num_type; }; enum cdma_sms_numbering_plan number_plan; guint8 num_fields; guint8 address[CDMA_SMS_MAX_ADDR_FIELDS]; }; /* 3GPP2 C.S0015-B v2.0 Section 3.4.3.6 */ struct cdma_sms_cause_code { guint8 reply_seq; guint8 error_class; guint8 cause_code; }; /* 3GPP2 C.S0015-B v2.0 Section 4.5.1 */ struct cdma_sms_identifier { enum cdma_sms_msg_type msg_type; guint16 msg_id; gboolean header_ind; }; /* 3GPP2 C.S0015-B v2.0 Section 4.5.2 */ struct cdma_sms_ud { enum cdma_sms_msg_encoding msg_encoding; guint8 num_fields; guint8 chari[CDMA_SMS_UD_LEN]; }; /* * 3GPP2 C.S0015-B v2.0 Table 4.3.4-1. * TODO: Not all subparameter records defined * and supported yet. */ struct cdma_sms_wmt_deliver { struct cdma_sms_ud ud; }; /* 3GPP2 C.S0015-B v2.0 Section 4.5 */ struct cdma_sms_bearer_data { guint32 subparam_bitmap; struct cdma_sms_identifier id; union { struct cdma_sms_wmt_deliver wmt_deliver; }; }; /* * 3GPP2 C.S0015-B v2.0 Table 3.4.2.1-1. * TODO: Not all parameter records defined * and supported yet. */ struct cdma_sms_p2p_msg { guint32 param_bitmap; enum cdma_sms_teleservice_id teleservice_id; struct cdma_sms_address oaddr; struct cdma_sms_bearer_data bd; }; /* 3GPP2 C.S0015-B v2.0 Table 3.4.2.2-1 */ struct cdma_sms_broadcast_msg { enum cdma_sms_service_cat service_category; struct cdma_sms_bearer_data bd; }; /* * 3GPP2 C.S0015-B v2.0 Table 3.4.2.3-1 * TODO: Not all parameter records defined * and supported yet. */ struct cdma_sms_ack_msg { struct cdma_sms_address daddr; struct cdma_sms_cause_code cause_code; }; /* 3GPP2 C.S0015-B v2.0 Section 3.4.1 */ struct cdma_sms { enum cdma_sms_tp_msg_type type; union { struct cdma_sms_p2p_msg p2p_msg; struct cdma_sms_broadcast_msg broadcast_msg; struct cdma_sms_ack_msg ack_msg; }; }; static inline gboolean check_bitmap(guint32 bitmap, guint32 pos) { guint32 mask = 0x1 << pos; return bitmap & mask ? TRUE : FALSE; } gboolean cdma_sms_decode(const guint8 *pdu, guint8 len, struct cdma_sms *out); char *cdma_sms_decode_text(const struct cdma_sms_ud *ud); const char *cdma_sms_address_to_string(const struct cdma_sms_address *addr); ofono-1.17.bzr6912+16.04.20160314.3/src/call-forwarding.c0000644000015600001650000011623512671500024022415 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include "ofono.h" #include "common.h" #include "simutil.h" #define CALL_FORWARDING_FLAG_CACHED 0x1 #define CALL_FORWARDING_FLAG_CPHS_CFF 0x2 /* According to 27.007 Spec */ #define DEFAULT_NO_REPLY_TIMEOUT 20 #define is_cfu_enabled(_cf) \ ({ \ cf_find_unconditional(_cf) ? TRUE : FALSE; \ }) enum call_forwarding_type { CALL_FORWARDING_TYPE_UNCONDITIONAL = 0, CALL_FORWARDING_TYPE_BUSY = 1, CALL_FORWARDING_TYPE_NO_REPLY = 2, CALL_FORWARDING_TYPE_NOT_REACHABLE = 3, CALL_FORWARDING_TYPE_ALL = 4, CALL_FORWARDING_TYPE_ALL_CONDITIONAL = 5 }; struct ofono_call_forwarding { GSList *cf_conditions[4]; int flags; DBusMessage *pending; int query_next; int query_end; struct cf_ss_request *ss_req; struct ofono_sim *sim; struct ofono_sim_context *sim_context; unsigned char cfis_record_id; struct ofono_ussd *ussd; unsigned int ussd_watch; const struct ofono_call_forwarding_driver *driver; void *driver_data; struct ofono_atom *atom; }; struct cf_ss_request { int ss_type; int cf_type; int cls; GSList *cf_list[4]; }; static GSList *g_drivers = NULL; static void get_query_next_cf_cond(struct ofono_call_forwarding *cf); static void set_query_next_cf_cond(struct ofono_call_forwarding *cf); static void ss_set_query_next_cf_cond(struct ofono_call_forwarding *cf); static gint cf_cond_compare(gconstpointer a, gconstpointer b) { const struct ofono_call_forwarding_condition *ca = a; const struct ofono_call_forwarding_condition *cb = b; return ca->cls - cb->cls; } static struct ofono_call_forwarding_condition *cf_cond_find(GSList *l, int cls) { struct ofono_call_forwarding_condition *c; for (; l; l = l->next) { c = l->data; if (c->cls == cls) return c; } return NULL; } static int cf_cond_find_timeout(GSList *l, int cls) { struct ofono_call_forwarding_condition *cond = cf_cond_find(l, cls); return cond ? cond->time : DEFAULT_NO_REPLY_TIMEOUT; } static void cf_cond_list_print(GSList *l) { struct ofono_call_forwarding_condition *cond; for (; l ; l = l->next) { cond = l->data; DBG("CF Condition status: %d, class: %d, number: %s," " number_type: %d, time: %d", cond->status, cond->cls, cond->phone_number.number, cond->phone_number.type, cond->time); } } static GSList *cf_cond_list_create(int total, const struct ofono_call_forwarding_condition *list) { GSList *l = NULL; int i; int j; struct ofono_call_forwarding_condition *cond; /* * Specification is not really clear how the results are reported, * so assume both multiple list items & compound values of class * are possible */ for (i = 0; i < total; i++) { for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) { if (!(list[i].cls & j)) continue; if (list[i].status == 0) continue; cond = g_try_new0( struct ofono_call_forwarding_condition, 1); if (cond == NULL) continue; memcpy(cond, &list[i], sizeof(struct ofono_call_forwarding_condition)); cond->cls = j; l = g_slist_insert_sorted(l, cond, cf_cond_compare); } } return l; } static inline void cf_clear_all(struct ofono_call_forwarding *cf) { int i; for (i = 0; i < 4; i++) { g_slist_free_full(cf->cf_conditions[i], g_free); cf->cf_conditions[i] = NULL; } } static const char *cf_type_lut[] = { "Unconditional", "Busy", "NoReply", "NotReachable", "All", "AllConditional" }; static void sim_cfis_update_cb(int ok, void *data) { if (!ok) ofono_info("Failed to update EFcfis"); } static void sim_cphs_cff_update_cb(int ok, void *data) { if (!ok) ofono_info("Failed to update EFcphs-cff"); } static inline struct ofono_call_forwarding_condition *cf_find_unconditional( struct ofono_call_forwarding *cf) { return cf_cond_find( cf->cf_conditions[CALL_FORWARDING_TYPE_UNCONDITIONAL], BEARER_CLASS_VOICE); } static void sim_set_cf_indicator(struct ofono_call_forwarding *cf) { struct ofono_call_forwarding_condition *cfu_voice = cf_find_unconditional(cf); if (cf->cfis_record_id) { unsigned char data[16]; int number_len; memset(data, 0xff, sizeof(data)); /* Profile Identifier */ data[0] = 0x01; if (cfu_voice) { number_len = strlen(cfu_voice->phone_number.number); /* CFU indicator Status - Voice */ data[1] = 0x01; number_len = (number_len + 1) / 2; data[2] = number_len + 1; data[3] = cfu_voice->phone_number.type; sim_encode_bcd_number(cfu_voice->phone_number.number, data + 4); } else { data[1] = 0x00; data[2] = 1; data[3] = 128; } ofono_sim_write(cf->sim_context, SIM_EFCFIS_FILEID, sim_cfis_update_cb, OFONO_SIM_FILE_STRUCTURE_FIXED, cf->cfis_record_id, data, sizeof(data), cf); return; } if (cf->flags & CALL_FORWARDING_FLAG_CPHS_CFF) { unsigned char cff_voice = cfu_voice ? 0x0A : 0x05; ofono_sim_write(cf->sim_context, SIM_EF_CPHS_CFF_FILEID, sim_cphs_cff_update_cb, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, 0, &cff_voice, sizeof(cff_voice), cf); } } static void set_new_cond_list(struct ofono_call_forwarding *cf, int type, GSList *list) { GSList *old = cf->cf_conditions[type]; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cf->atom); GSList *l; GSList *o; struct ofono_call_forwarding_condition *lc; struct ofono_call_forwarding_condition *oc; const char *number; dbus_uint16_t timeout; char attr[64]; char tattr[64]; gboolean update_sim = FALSE; gboolean old_cfu; gboolean new_cfu; if ((cf->flags & CALL_FORWARDING_FLAG_CPHS_CFF) || cf->cfis_record_id > 0) old_cfu = is_cfu_enabled(cf); else old_cfu = FALSE; for (l = list; l; l = l->next) { lc = l->data; /* * New condition lists might have attributes we don't care about * triggered by e.g. ss control magic strings just skip them * here. For now we only support Voice, although Fax & all Data * basic services are applicable as well. */ if (lc->cls > BEARER_CLASS_VOICE) continue; timeout = lc->time; number = phone_number_to_string(&lc->phone_number); snprintf(attr, sizeof(attr), "%s%s", bearer_class_to_string(lc->cls), cf_type_lut[type]); if (type == CALL_FORWARDING_TYPE_NO_REPLY) snprintf(tattr, sizeof(tattr), "%sTimeout", attr); oc = cf_cond_find(old, lc->cls); if (oc) { /* On the old list, must be active */ if (oc->phone_number.type != lc->phone_number.type || strcmp(oc->phone_number.number, lc->phone_number.number)) { ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, attr, DBUS_TYPE_STRING, &number); if (type == CALL_FORWARDING_TYPE_UNCONDITIONAL) update_sim = TRUE; } if (type == CALL_FORWARDING_TYPE_NO_REPLY && oc->time != lc->time) ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, tattr, DBUS_TYPE_UINT16, &timeout); /* Remove from the old list */ old = g_slist_remove(old, oc); g_free(oc); } else { number = phone_number_to_string(&lc->phone_number); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, attr, DBUS_TYPE_STRING, &number); if (type == CALL_FORWARDING_TYPE_UNCONDITIONAL) update_sim = TRUE; if (type == CALL_FORWARDING_TYPE_NO_REPLY && lc->time != DEFAULT_NO_REPLY_TIMEOUT) ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, tattr, DBUS_TYPE_UINT16, &timeout); } } timeout = DEFAULT_NO_REPLY_TIMEOUT; number = ""; for (o = old; o; o = o->next) { oc = o->data; /* * For now we only support Voice, although Fax & all Data * basic services are applicable as well. */ if (oc->cls > BEARER_CLASS_VOICE) continue; snprintf(attr, sizeof(attr), "%s%s", bearer_class_to_string(oc->cls), cf_type_lut[type]); if (type == CALL_FORWARDING_TYPE_NO_REPLY) snprintf(tattr, sizeof(tattr), "%sTimeout", attr); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, attr, DBUS_TYPE_STRING, &number); if (type == CALL_FORWARDING_TYPE_UNCONDITIONAL) update_sim = TRUE; if (type == CALL_FORWARDING_TYPE_NO_REPLY && oc->time != DEFAULT_NO_REPLY_TIMEOUT) ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, tattr, DBUS_TYPE_UINT16, &timeout); } g_slist_free_full(old, g_free); cf->cf_conditions[type] = list; if (update_sim == TRUE) sim_set_cf_indicator(cf); if ((cf->flags & CALL_FORWARDING_FLAG_CPHS_CFF) || cf->cfis_record_id > 0) new_cfu = is_cfu_enabled(cf); else new_cfu = FALSE; if (new_cfu != old_cfu) { ofono_bool_t status = new_cfu; int i; /* * Emit signals to mask/unmask conditional cfs on cfu change */ for (i = 0; i < 4; i++) { if (i == CALL_FORWARDING_TYPE_UNCONDITIONAL) continue; lc = cf_cond_find(cf->cf_conditions[i], BEARER_CLASS_VOICE); if (lc == NULL) continue; if (new_cfu) number = ""; else number = phone_number_to_string( &lc->phone_number); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, cf_type_lut[i], DBUS_TYPE_STRING, &number); } ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, "ForwardingFlagOnSim", DBUS_TYPE_BOOLEAN, &status); } } static inline void property_append_cf_condition(DBusMessageIter *dict, int cls, const char *postfix, const char *value, dbus_uint16_t timeout) { char attr[64]; char tattr[64]; int addt = !strcmp(postfix, "NoReply"); snprintf(attr, sizeof(attr), "%s%s", bearer_class_to_string(cls), postfix); if (addt) snprintf(tattr, sizeof(tattr), "%s%sTimeout", bearer_class_to_string(cls), postfix); ofono_dbus_dict_append(dict, attr, DBUS_TYPE_STRING, &value); if (addt) ofono_dbus_dict_append(dict, tattr, DBUS_TYPE_UINT16, &timeout); } static void property_append_cf_conditions(DBusMessageIter *dict, GSList *cf_list, int mask, const char *postfix) { GSList *l; int i; struct ofono_call_forwarding_condition *cf; const char *number; for (i = 1, l = cf_list; i <= BEARER_CLASS_PAD; i = i << 1) { if (!(mask & i)) continue; while (l && (cf = l->data) && (cf->cls < i)) l = l->next; if (l == NULL || cf->cls != i) { property_append_cf_condition(dict, i, postfix, "", DEFAULT_NO_REPLY_TIMEOUT); continue; } number = phone_number_to_string(&cf->phone_number); property_append_cf_condition(dict, i, postfix, number, cf->time); } } static DBusMessage *cf_get_properties_reply(DBusMessage *msg, struct ofono_call_forwarding *cf) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; int i; dbus_bool_t status; gboolean cfu_enabled; GSList *cf_list; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); cfu_enabled = is_cfu_enabled(cf); for (i = 0; i < 4; i++) { /* * Report conditional cfs as empty when CFU is active */ if (cfu_enabled && (i != CALL_FORWARDING_TYPE_UNCONDITIONAL)) cf_list = NULL; else cf_list = cf->cf_conditions[i]; property_append_cf_conditions(&dict, cf_list, BEARER_CLASS_VOICE, cf_type_lut[i]); } if ((cf->flags & CALL_FORWARDING_FLAG_CPHS_CFF) || cf->cfis_record_id > 0) status = cfu_enabled; else status = FALSE; ofono_dbus_dict_append(&dict, "ForwardingFlagOnSim", DBUS_TYPE_BOOLEAN, &status); dbus_message_iter_close_container(&iter, &dict); return reply; } static void get_query_cf_callback(const struct ofono_error *error, int total, const struct ofono_call_forwarding_condition *list, void *data) { struct ofono_call_forwarding *cf = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) { GSList *l = cf_cond_list_create(total, list); set_new_cond_list(cf, cf->query_next, l); DBG("%s conditions:", cf_type_lut[cf->query_next]); cf_cond_list_print(l); if (cf->query_next == CALL_FORWARDING_TYPE_NOT_REACHABLE) cf->flags |= CALL_FORWARDING_FLAG_CACHED; } if (cf->query_next == CALL_FORWARDING_TYPE_NOT_REACHABLE) { __ofono_dbus_pending_reply(&cf->pending, cf_get_properties_reply(cf->pending, cf)); return; } cf->query_next++; get_query_next_cf_cond(cf); } static inline void get_query_next_cf_cond(struct ofono_call_forwarding *cf) { cf->driver->query(cf, cf->query_next, BEARER_CLASS_DEFAULT, get_query_cf_callback, cf); } static DBusMessage *cf_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_forwarding *cf = data; struct ofono_modem *modem = __ofono_atom_get_modem(cf->atom); if ((cf->flags & CALL_FORWARDING_FLAG_CACHED) || ofono_modem_get_online(modem) == FALSE) return cf_get_properties_reply(msg, cf); if (cf->driver->query == NULL) return __ofono_error_not_implemented(msg); if (__ofono_call_forwarding_is_busy(cf) || __ofono_ussd_is_busy(cf->ussd)) return __ofono_error_busy(msg); cf->pending = dbus_message_ref(msg); cf->query_next = 0; get_query_next_cf_cond(cf); return NULL; } static gboolean cf_condition_enabled_property(struct ofono_call_forwarding *cf, const char *property, int *out_type, int *out_cls) { int i; int j; int len; const char *prefix; for (i = 1; i <= BEARER_CLASS_VOICE; i = i << 1) { prefix = bearer_class_to_string(i); len = strlen(prefix); if (strncmp(property, prefix, len)) continue; /* * We check the 4 call forwarding types, e.g. * unconditional, busy, no reply, not reachable */ for (j = 0; j < 4; j++) if (!strcmp(property+len, cf_type_lut[j])) { *out_type = j; *out_cls = i; return TRUE; } } return FALSE; } static gboolean cf_condition_timeout_property(const char *property, int *out_cls) { int i; int len; const char *prefix; for (i = 1; i <= BEARER_CLASS_VOICE; i = i << 1) { prefix = bearer_class_to_string(i); len = strlen(prefix); if (strncmp(property, prefix, len)) continue; if (!strcmp(property+len, "NoReplyTimeout")) { *out_cls = i; return TRUE; } } return FALSE; } static void set_query_cf_callback(const struct ofono_error *error, int total, const struct ofono_call_forwarding_condition *list, void *data) { struct ofono_call_forwarding *cf = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Setting succeeded, but query failed"); cf->flags &= ~CALL_FORWARDING_FLAG_CACHED; __ofono_dbus_pending_reply(&cf->pending, __ofono_error_failed(cf->pending)); return; } if (cf->query_next == cf->query_end) __ofono_dbus_pending_reply(&cf->pending, dbus_message_new_method_return(cf->pending)); set_new_cond_list(cf, cf->query_next, cf_cond_list_create(total, list)); DBG("%s conditions:", cf_type_lut[cf->query_next]); cf_cond_list_print(cf->cf_conditions[cf->query_next]); if (cf->query_next == cf->query_end) return; cf->query_next++; set_query_next_cf_cond(cf); } static void set_query_next_cf_cond(struct ofono_call_forwarding *cf) { cf->driver->query(cf, cf->query_next, BEARER_CLASS_DEFAULT, set_query_cf_callback, cf); } static void set_property_callback(const struct ofono_error *error, void *data) { struct ofono_call_forwarding *cf = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error occurred during set/erasure"); __ofono_dbus_pending_reply(&cf->pending, __ofono_error_failed(cf->pending)); return; } /* Successfully set, query the entire set just in case */ set_query_next_cf_cond(cf); } static DBusMessage *set_property_request(struct ofono_call_forwarding *cf, DBusMessage *msg, int type, int cls, struct ofono_phone_number *ph, int timeout) { if (ph->number[0] != '\0' && cf->driver->registration == NULL) return __ofono_error_not_implemented(msg); if (ph->number[0] == '\0' && cf->driver->erasure == NULL) return __ofono_error_not_implemented(msg); cf->pending = dbus_message_ref(msg); cf->query_next = type; cf->query_end = type; DBG("Farming off request, will be erasure: %d", ph->number[0] == '\0'); if (ph->number[0] != '\0') cf->driver->registration(cf, type, cls, ph, timeout, set_property_callback, cf); else cf->driver->erasure(cf, type, cls, set_property_callback, cf); return NULL; } static DBusMessage *cf_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_forwarding *cf = data; struct ofono_modem *modem = __ofono_atom_get_modem(cf->atom); DBusMessageIter iter; DBusMessageIter var; const char *property; int cls; int type; if (ofono_modem_get_online(modem) == FALSE) return __ofono_error_not_available(msg); if (__ofono_call_forwarding_is_busy(cf) || __ofono_ussd_is_busy(cf->ussd)) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (cf_condition_timeout_property(property, &cls)) { dbus_uint16_t timeout; struct ofono_call_forwarding_condition *c; type = CALL_FORWARDING_TYPE_NO_REPLY; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_UINT16) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &timeout); if (timeout < 1 || timeout > 30) return __ofono_error_invalid_format(msg); c = cf_cond_find(cf->cf_conditions[type], cls); if (c == NULL) return __ofono_error_failed(msg); return set_property_request(cf, msg, type, cls, &c->phone_number, timeout); } else if (cf_condition_enabled_property(cf, property, &type, &cls)) { struct ofono_phone_number ph; const char *number; int timeout; ph.number[0] = '\0'; ph.type = 129; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &number); if (strlen(number) > 0 && !valid_phone_number_format(number)) return __ofono_error_invalid_format(msg); /* * Don't set conditional cfs when cfu is active */ if (type != CALL_FORWARDING_TYPE_UNCONDITIONAL && number[0] != '\0' && is_cfu_enabled(cf)) return __ofono_error_not_available(msg); if (number[0] != '\0') string_to_phone_number(number, &ph); timeout = cf_cond_find_timeout(cf->cf_conditions[type], cls); return set_property_request(cf, msg, type, cls, &ph, timeout); } return __ofono_error_invalid_args(msg); } static void disable_conditional_callback(const struct ofono_error *error, void *data) { struct ofono_call_forwarding *cf = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error occurred during conditional erasure"); __ofono_dbus_pending_reply(&cf->pending, __ofono_error_failed(cf->pending)); return; } /* Query the three conditional cf types */ cf->query_next = CALL_FORWARDING_TYPE_BUSY; cf->query_end = CALL_FORWARDING_TYPE_NOT_REACHABLE; set_query_next_cf_cond(cf); } static void disable_all_callback(const struct ofono_error *error, void *data) { struct ofono_call_forwarding *cf = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error occurred during erasure of all"); __ofono_dbus_pending_reply(&cf->pending, __ofono_error_failed(cf->pending)); return; } /* Query all cf types */ cf->query_next = CALL_FORWARDING_TYPE_UNCONDITIONAL; cf->query_end = CALL_FORWARDING_TYPE_NOT_REACHABLE; set_query_next_cf_cond(cf); } static DBusMessage *cf_disable_all(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_forwarding *cf = data; const char *strtype; int type; if (cf->driver->erasure == NULL) return __ofono_error_not_implemented(msg); if (__ofono_call_forwarding_is_busy(cf) || __ofono_ussd_is_busy(cf->ussd)) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &strtype, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!strcmp(strtype, "all") || !strcmp(strtype, "")) type = CALL_FORWARDING_TYPE_ALL; else if (!strcmp(strtype, "conditional")) type = CALL_FORWARDING_TYPE_ALL_CONDITIONAL; else return __ofono_error_invalid_format(msg); cf->pending = dbus_message_ref(msg); if (type == CALL_FORWARDING_TYPE_ALL) cf->driver->erasure(cf, type, BEARER_CLASS_DEFAULT, disable_all_callback, cf); else cf->driver->erasure(cf, type, BEARER_CLASS_DEFAULT, disable_conditional_callback, cf); return NULL; } static const GDBusMethodTable cf_methods[] = { { GDBUS_ASYNC_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), cf_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, cf_set_property) }, { GDBUS_ASYNC_METHOD("DisableAll", GDBUS_ARGS({ "type", "s" }), NULL, cf_disable_all) }, { } }; static const GDBusSignalTable cf_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static DBusMessage *cf_ss_control_reply(struct ofono_call_forwarding *cf, struct cf_ss_request *req) { const char *context = "CallForwarding"; const char *sig = "(ssa{sv})"; const char *ss_type = ss_control_type_to_string(req->ss_type); const char *cf_type = cf_type_lut[req->cf_type]; DBusMessageIter iter; DBusMessageIter variant; DBusMessageIter vstruct; DBusMessageIter dict; DBusMessage *reply; reply = dbus_message_new_method_return(cf->pending); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_STRUCT, NULL, &vstruct); dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &ss_type); dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &cf_type); dbus_message_iter_open_container(&vstruct, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); if (req->cf_type == CALL_FORWARDING_TYPE_UNCONDITIONAL || req->cf_type == CALL_FORWARDING_TYPE_ALL) property_append_cf_conditions(&dict, req->cf_list[CALL_FORWARDING_TYPE_UNCONDITIONAL], req->cls, cf_type_lut[CALL_FORWARDING_TYPE_UNCONDITIONAL]); if (req->cf_type == CALL_FORWARDING_TYPE_NO_REPLY || req->cf_type == CALL_FORWARDING_TYPE_ALL || req->cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL) property_append_cf_conditions(&dict, req->cf_list[CALL_FORWARDING_TYPE_NO_REPLY], req->cls, cf_type_lut[CALL_FORWARDING_TYPE_NO_REPLY]); if (req->cf_type == CALL_FORWARDING_TYPE_NOT_REACHABLE || req->cf_type == CALL_FORWARDING_TYPE_ALL || req->cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL) property_append_cf_conditions(&dict, req->cf_list[CALL_FORWARDING_TYPE_NOT_REACHABLE], req->cls, cf_type_lut[CALL_FORWARDING_TYPE_NOT_REACHABLE]); if (req->cf_type == CALL_FORWARDING_TYPE_BUSY || req->cf_type == CALL_FORWARDING_TYPE_ALL || req->cf_type == CALL_FORWARDING_TYPE_ALL_CONDITIONAL) property_append_cf_conditions(&dict, req->cf_list[CALL_FORWARDING_TYPE_BUSY], req->cls, cf_type_lut[CALL_FORWARDING_TYPE_BUSY]); dbus_message_iter_close_container(&vstruct, &dict); dbus_message_iter_close_container(&variant, &vstruct); dbus_message_iter_close_container(&iter, &variant); return reply; } static void ss_set_query_cf_callback(const struct ofono_error *error, int total, const struct ofono_call_forwarding_condition *list, void *data) { struct ofono_call_forwarding *cf = data; GSList *l; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Query failed with error: %s", telephony_error_to_str(error)); cf->flags &= ~CALL_FORWARDING_FLAG_CACHED; reply = __ofono_error_from_error(error, cf->pending); __ofono_dbus_pending_reply(&cf->pending, reply); return; } l = cf_cond_list_create(total, list); DBG("%s conditions:", cf_type_lut[cf->query_next]); cf_cond_list_print(l); cf->ss_req->cf_list[cf->query_next] = l; if (cf->query_next == cf->query_end) { reply = cf_ss_control_reply(cf, cf->ss_req); __ofono_dbus_pending_reply(&cf->pending, reply); g_free(cf->ss_req); cf->ss_req = NULL; } set_new_cond_list(cf, cf->query_next, l); if (cf->query_next != cf->query_end) { cf->query_next++; ss_set_query_next_cf_cond(cf); } } static void ss_set_query_next_cf_cond(struct ofono_call_forwarding *cf) { int cls; cls = (cf->ss_req->ss_type == SS_CONTROL_TYPE_QUERY) ? cf->ss_req->cls : BEARER_CLASS_DEFAULT; if (cls == BEARER_CLASS_SS_DEFAULT) cls = BEARER_CLASS_DEFAULT; cf->driver->query(cf, cf->query_next, cls, ss_set_query_cf_callback, cf); } static void cf_ss_control_callback(const struct ofono_error *error, void *data) { struct ofono_call_forwarding *cf = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("CF ss control set/erasure failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&cf->pending, __ofono_error_from_error(error, cf->pending)); g_free(cf->ss_req); cf->ss_req = NULL; return; } ss_set_query_next_cf_cond(cf); } static gboolean cf_ss_control(int type, const char *sc, const char *sia, const char *sib, const char *sic, const char *dn, DBusMessage *msg, void *data) { struct ofono_call_forwarding *cf = data; DBusConnection *conn = ofono_dbus_get_connection(); int cls = BEARER_CLASS_SS_DEFAULT; int timeout = DEFAULT_NO_REPLY_TIMEOUT; int cf_type; DBusMessage *reply; struct ofono_phone_number ph; void *operation = NULL; /* Before we do anything, make sure we're actually initialized */ if (cf == NULL) return FALSE; if (__ofono_call_forwarding_is_busy(cf)) { reply = __ofono_error_busy(msg); g_dbus_send_message(conn, reply); return TRUE; } DBG("Received call forwarding ss control request"); DBG("type: %d, sc: %s, sia: %s, sib: %s, sic: %s, dn: %s", type, sc, sia, sib, sic, dn); if (!strcmp(sc, "21")) cf_type = CALL_FORWARDING_TYPE_UNCONDITIONAL; else if (!strcmp(sc, "67")) cf_type = CALL_FORWARDING_TYPE_BUSY; else if (!strcmp(sc, "61")) cf_type = CALL_FORWARDING_TYPE_NO_REPLY; else if (!strcmp(sc, "62")) cf_type = CALL_FORWARDING_TYPE_NOT_REACHABLE; else if (!strcmp(sc, "002")) cf_type = CALL_FORWARDING_TYPE_ALL; else if (!strcmp(sc, "004")) cf_type = CALL_FORWARDING_TYPE_ALL_CONDITIONAL; else return FALSE; if (strlen(sia) && (type == SS_CONTROL_TYPE_QUERY || type == SS_CONTROL_TYPE_ERASURE || type == SS_CONTROL_TYPE_DEACTIVATION)) goto error; /* * Activation / Registration is figured context specific according to * 22.030 Section 6.5.2 "The UE shall determine from the context * whether, an entry of a single *, activation or registration * was intended." */ if (type == SS_CONTROL_TYPE_ACTIVATION && strlen(sia) > 0) type = SS_CONTROL_TYPE_REGISTRATION; if (type == SS_CONTROL_TYPE_REGISTRATION && !valid_phone_number_format(sia)) goto error; if (strlen(sib) > 0) { long service_code; char *end; service_code = strtoul(sib, &end, 10); if (end == sib || *end != '\0') goto error; cls = mmi_service_code_to_bearer_class(service_code); if (cls == 0) goto error; } if (strlen(sic) > 0) { char *end; if (type != SS_CONTROL_TYPE_REGISTRATION) goto error; if (cf_type != CALL_FORWARDING_TYPE_ALL && cf_type != CALL_FORWARDING_TYPE_ALL_CONDITIONAL && cf_type != CALL_FORWARDING_TYPE_NO_REPLY) goto error; timeout = strtoul(sic, &end, 10); if (end == sic || *end != '\0') goto error; if (timeout < 1 || timeout > 30) goto error; } switch (type) { case SS_CONTROL_TYPE_REGISTRATION: operation = cf->driver->registration; break; case SS_CONTROL_TYPE_ACTIVATION: operation = cf->driver->activation; break; case SS_CONTROL_TYPE_DEACTIVATION: operation = cf->driver->deactivation; break; case SS_CONTROL_TYPE_ERASURE: operation = cf->driver->erasure; break; case SS_CONTROL_TYPE_QUERY: operation = cf->driver->query; break; } if (operation == NULL) { reply = __ofono_error_not_implemented(msg); g_dbus_send_message(conn, reply); return TRUE; } cf->ss_req = g_try_new0(struct cf_ss_request, 1); if (cf->ss_req == NULL) { reply = __ofono_error_failed(msg); g_dbus_send_message(conn, reply); return TRUE; } cf->ss_req->ss_type = type; cf->ss_req->cf_type = cf_type; cf->ss_req->cls = cls; cf->pending = dbus_message_ref(msg); switch (cf->ss_req->cf_type) { case CALL_FORWARDING_TYPE_ALL: cf->query_next = CALL_FORWARDING_TYPE_UNCONDITIONAL; cf->query_end = CALL_FORWARDING_TYPE_NOT_REACHABLE; break; case CALL_FORWARDING_TYPE_ALL_CONDITIONAL: cf->query_next = CALL_FORWARDING_TYPE_BUSY; cf->query_end = CALL_FORWARDING_TYPE_NOT_REACHABLE; break; default: cf->query_next = cf->ss_req->cf_type; cf->query_end = cf->ss_req->cf_type; break; } /* * Some modems don't understand all classes very well, particularly * the older models. So if the bearer class is the default, we * just use the more commonly understood value of 7 since BEARER_SMS * is not applicable to CallForwarding conditions according to 22.004 * Annex A */ if (cls == BEARER_CLASS_SS_DEFAULT) cls = BEARER_CLASS_DEFAULT; switch (cf->ss_req->ss_type) { case SS_CONTROL_TYPE_REGISTRATION: string_to_phone_number(sia, &ph); cf->driver->registration(cf, cf_type, cls, &ph, timeout, cf_ss_control_callback, cf); break; case SS_CONTROL_TYPE_ACTIVATION: cf->driver->activation(cf, cf_type, cls, cf_ss_control_callback, cf); break; case SS_CONTROL_TYPE_DEACTIVATION: cf->driver->deactivation(cf, cf_type, cls, cf_ss_control_callback, cf); break; case SS_CONTROL_TYPE_ERASURE: cf->driver->erasure(cf, cf_type, cls, cf_ss_control_callback, cf); break; case SS_CONTROL_TYPE_QUERY: ss_set_query_next_cf_cond(cf); break; } return TRUE; error: reply = __ofono_error_invalid_format(msg); g_dbus_send_message(conn, reply); return TRUE; } static void cf_register_ss_controls(struct ofono_call_forwarding *cf) { __ofono_ussd_ssc_register(cf->ussd, "21", cf_ss_control, cf, NULL); __ofono_ussd_ssc_register(cf->ussd, "67", cf_ss_control, cf, NULL); __ofono_ussd_ssc_register(cf->ussd, "61", cf_ss_control, cf, NULL); __ofono_ussd_ssc_register(cf->ussd, "62", cf_ss_control, cf, NULL); __ofono_ussd_ssc_register(cf->ussd, "002", cf_ss_control, cf, NULL); __ofono_ussd_ssc_register(cf->ussd, "004", cf_ss_control, cf, NULL); } static void cf_unregister_ss_controls(struct ofono_call_forwarding *cf) { __ofono_ussd_ssc_unregister(cf->ussd, "21"); __ofono_ussd_ssc_unregister(cf->ussd, "67"); __ofono_ussd_ssc_unregister(cf->ussd, "61"); __ofono_ussd_ssc_unregister(cf->ussd, "62"); __ofono_ussd_ssc_unregister(cf->ussd, "002"); __ofono_ussd_ssc_unregister(cf->ussd, "004"); } gboolean __ofono_call_forwarding_is_busy(struct ofono_call_forwarding *cf) { return cf->pending ? TRUE : FALSE; } static void sim_cfis_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_call_forwarding *cf = userdata; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cf->atom); if (!ok || record_length < 16 || total_length < record_length) { cf->cfis_record_id = 0; return; } /* * Multiple Subscriber Profile number which can have values 1-4. * Profile id 1 is assumed as the current profile. */ if (data[0] != 1) return; cf->cfis_record_id = record; if (cf->flags & CALL_FORWARDING_FLAG_CACHED) return; /* * For now we only support Voice, although Fax & all Data * basic services are applicable as well. */ if (data[1] & 0x01) { int ton_npi; int number_len; const char *number; char attr[64]; struct ofono_call_forwarding_condition *cond; dbus_bool_t status; number_len = data[2]; ton_npi = data[3]; if (number_len > 11 || ton_npi == 0xff) return; cond = g_try_new0(struct ofono_call_forwarding_condition, 1); if (cond == NULL) return; status = TRUE; cond->status = TRUE; cond->cls = BEARER_CLASS_VOICE; cond->time = 0; cond->phone_number.type = ton_npi; sim_extract_bcd_number(data + 4, number_len - 1, cond->phone_number.number); number = phone_number_to_string(&cond->phone_number); snprintf(attr, sizeof(attr), "%s%s", bearer_class_to_string(BEARER_CLASS_VOICE), cf_type_lut[CALL_FORWARDING_TYPE_UNCONDITIONAL]); cf->cf_conditions[CALL_FORWARDING_TYPE_UNCONDITIONAL] = g_slist_append(NULL, cond); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, attr, DBUS_TYPE_STRING, &number); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, "ForwardingFlagOnSim", DBUS_TYPE_BOOLEAN, &status); } } static void sim_cphs_cff_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_call_forwarding *cf = userdata; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cf->atom); dbus_bool_t cfu_voice; if (!ok || total_length < 1) return; cf->flags |= CALL_FORWARDING_FLAG_CPHS_CFF; if (cf->flags & CALL_FORWARDING_FLAG_CACHED) return; /* * For now we only support Voice, although Fax & all Data * basic services are applicable as well. */ if ((data[0] & 0xf) != 0xA) return; cfu_voice = TRUE; ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_FORWARDING_INTERFACE, "ForwardingFlagOnSim", DBUS_TYPE_BOOLEAN, &cfu_voice); } static void call_forwarding_unregister(struct ofono_atom *atom) { struct ofono_call_forwarding *cf = __ofono_atom_get_data(atom); const char *path = __ofono_atom_get_path(cf->atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(cf->atom); ofono_modem_remove_interface(modem, OFONO_CALL_FORWARDING_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_CALL_FORWARDING_INTERFACE); if (cf->sim_context) { ofono_sim_context_free(cf->sim_context); cf->sim_context = NULL; } if (cf->ussd) cf_unregister_ss_controls(cf); if (cf->ussd_watch) __ofono_modem_remove_atom_watch(modem, cf->ussd_watch); cf->flags = 0; } static void sim_cfis_changed(int id, void *userdata) { struct ofono_call_forwarding *cf = userdata; if (!(cf->flags & CALL_FORWARDING_FLAG_CACHED)) return; /* * If the values are cached it's because at least one client * requested them and we need to notify them about this * change. However the authoritative source of current * Call-Forwarding settings is the network operator and the * query can take a noticeable amount of time. Instead of * sending PropertyChanged, we reregister the Call Forwarding * atom. The client will invoke GetProperties only if it * is still interested. */ call_forwarding_unregister(cf->atom); ofono_call_forwarding_register(cf); } static void sim_read_cf_indicator(struct ofono_call_forwarding *cf) { if (__ofono_sim_service_available(cf->sim, SIM_UST_SERVICE_CFIS, SIM_SST_SERVICE_CFIS) == TRUE) { ofono_sim_read(cf->sim_context, SIM_EFCFIS_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, sim_cfis_read_cb, cf); ofono_sim_add_file_watch(cf->sim_context, SIM_EFCFIS_FILEID, sim_cfis_changed, cf, NULL); } else { ofono_sim_read(cf->sim_context, SIM_EF_CPHS_CFF_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_cphs_cff_read_cb, cf); ofono_sim_add_file_watch(cf->sim_context, SIM_EF_CPHS_CFF_FILEID, sim_cfis_changed, cf, NULL); } } int ofono_call_forwarding_driver_register( const struct ofono_call_forwarding_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_call_forwarding_driver_unregister( const struct ofono_call_forwarding_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void call_forwarding_remove(struct ofono_atom *atom) { struct ofono_call_forwarding *cf = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (cf == NULL) return; if (cf->driver && cf->driver->remove) cf->driver->remove(cf); cf_clear_all(cf); g_free(cf); } struct ofono_call_forwarding *ofono_call_forwarding_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_call_forwarding *cf; GSList *l; if (driver == NULL) return NULL; cf = g_try_new0(struct ofono_call_forwarding, 1); if (cf == NULL) return NULL; cf->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CALL_FORWARDING, call_forwarding_remove, cf); for (l = g_drivers; l; l = l->next) { const struct ofono_call_forwarding_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(cf, vendor, data) < 0) continue; cf->driver = drv; break; } return cf; } static void ussd_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_call_forwarding *cf = data; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { cf->ussd = NULL; return; } cf->ussd = __ofono_atom_get_data(atom); cf_register_ss_controls(cf); } void ofono_call_forwarding_register(struct ofono_call_forwarding *cf) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cf->atom); struct ofono_modem *modem = __ofono_atom_get_modem(cf->atom); if (!g_dbus_register_interface(conn, path, OFONO_CALL_FORWARDING_INTERFACE, cf_methods, cf_signals, NULL, cf, NULL)) { ofono_error("Could not create %s interface", OFONO_CALL_FORWARDING_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_CALL_FORWARDING_INTERFACE); cf->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem); if (cf->sim) { cf->sim_context = ofono_sim_context_create(cf->sim); sim_read_cf_indicator(cf); } cf->ussd_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_USSD, ussd_watch, cf, NULL); __ofono_atom_register(cf->atom, call_forwarding_unregister); } void ofono_call_forwarding_remove(struct ofono_call_forwarding *cf) { __ofono_atom_free(cf->atom); } void ofono_call_forwarding_set_data(struct ofono_call_forwarding *cf, void *data) { cf->driver_data = data; } void *ofono_call_forwarding_get_data(struct ofono_call_forwarding *cf) { return cf->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/main.c0000644000015600001650000001317212671500024020262 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include "ofono.h" #define SHUTDOWN_GRACE_SECONDS 10 static GMainLoop *event_loop; void __ofono_exit(void) { g_main_loop_quit(event_loop); } static gboolean quit_eventloop(gpointer user_data) { __ofono_exit(); return FALSE; } static unsigned int __terminated = 0; static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct signalfd_siginfo si; ssize_t result; int fd; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; fd = g_io_channel_unix_get_fd(channel); result = read(fd, &si, sizeof(si)); if (result != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGINT: case SIGTERM: if (__terminated == 0) { ofono_info("Terminating"); g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, quit_eventloop, NULL); __ofono_modem_shutdown(); } __terminated = 1; break; } return TRUE; } static guint setup_signalfd(void) { GIOChannel *channel; guint source; sigset_t mask; int fd; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { perror("Failed to set signal mask"); return 0; } fd = signalfd(-1, &mask, 0); if (fd < 0) { perror("Failed to create signal descriptor"); return 0; } channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_handler, NULL); g_io_channel_unref(channel); return source; } static void system_bus_disconnected(DBusConnection *conn, void *user_data) { ofono_error("System bus has disconnected!"); g_main_loop_quit(event_loop); } static gchar *option_debug = NULL; static gchar *option_plugin = NULL; static gchar *option_noplugin = NULL; static gboolean option_detach = TRUE; static gboolean option_version = FALSE; static gboolean parse_debug(const char *key, const char *value, gpointer user_data, GError **error) { if (value) option_debug = g_strdup(value); else option_debug = g_strdup("*"); return TRUE; } static GOptionEntry options[] = { { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_debug, "Specify debug options to enable", "DEBUG" }, { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin, "Specify plugins to load", "NAME,..," }, { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin, "Specify plugins not to load", "NAME,..." }, { "nodetach", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &option_detach, "Don't run as daemon in background" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { NULL }, }; int main(int argc, char **argv) { GOptionContext *context; GError *err = NULL; DBusConnection *conn; DBusError error; guint signal; #ifdef NEED_THREADS if (g_thread_supported() == FALSE) g_thread_init(NULL); #endif context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) { if (err != NULL) { g_printerr("%s\n", err->message); g_error_free(err); return 1; } g_printerr("An unknown error occurred\n"); return 1; } g_option_context_free(context); if (option_version == TRUE) { printf("%s\n", VERSION); exit(0); } if (option_detach == TRUE) { if (daemon(0, 0)) { perror("Can't start daemon"); return 1; } } event_loop = g_main_loop_new(NULL, FALSE); #ifdef NEED_THREADS if (dbus_threads_init_default() == FALSE) { fprintf(stderr, "Can't init usage of threads\n"); exit(1); } #endif signal = setup_signalfd(); __ofono_log_init(argv[0], option_debug, option_detach); dbus_error_init(&error); conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, OFONO_SERVICE, &error); if (conn == NULL) { if (dbus_error_is_set(&error) == TRUE) { ofono_error("Unable to hop onto D-Bus: %s", error.message); dbus_error_free(&error); } else { ofono_error("Unable to hop onto D-Bus"); } goto cleanup; } g_dbus_set_disconnect_function(conn, system_bus_disconnected, NULL, NULL); __ofono_dbus_init(conn); __ofono_modemwatch_init(); __ofono_manager_init(); __ofono_plugin_init(option_plugin, option_noplugin); g_free(option_plugin); g_free(option_noplugin); __ofono_wakelock_init(); g_main_loop_run(event_loop); __ofono_wakelock_cleanup(); __ofono_plugin_cleanup(); __ofono_manager_cleanup(); __ofono_modemwatch_cleanup(); __ofono_dbus_cleanup(); dbus_connection_unref(conn); cleanup: g_source_remove(signal); g_main_loop_unref(event_loop); __ofono_log_cleanup(); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/src/cbs.c0000644000015600001650000006357212671500024020116 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include "ofono.h" #include "common.h" #include "util.h" #include "smsutil.h" #include "simutil.h" #include "storage.h" #define SETTINGS_STORE "cbs" #define SETTINGS_GROUP "Settings" static GSList *g_drivers = NULL; enum etws_topic_type { ETWS_TOPIC_TYPE_EARTHQUAKE = 4352, ETWS_TOPIC_TYPE_TSUNAMI = 4353, ETWS_TOPIC_TYPE_EARTHQUAKE_TSUNAMI = 4354, ETWS_TOPIC_TYPE_TEST = 4355, ETWS_TOPIC_TYPE_EMERGENCY = 4356, }; struct ofono_cbs { DBusMessage *pending; struct cbs_assembly *assembly; GSList *topics; GSList *new_topics; struct ofono_sim *sim; struct ofono_sim_context *sim_context; struct ofono_stk *stk; struct ofono_netreg *netreg; unsigned int netreg_watch; unsigned int location_watch; unsigned short efcbmi_length; GSList *efcbmi_contents; unsigned short efcbmir_length; GSList *efcbmir_contents; unsigned short efcbmid_length; GSList *efcbmid_contents; gboolean efcbmid_update; guint reset_source; int lac; int ci; char mnc[OFONO_MAX_MNC_LENGTH + 1]; char mcc[OFONO_MAX_MCC_LENGTH + 1]; ofono_bool_t powered; GKeyFile *settings; char *imsi; const struct ofono_cbs_driver *driver; void *driver_data; struct ofono_atom *atom; }; static void cbs_dispatch_base_station_id(struct ofono_cbs *cbs, const char *id) { DBG("Base station id: %s", id); if (cbs->netreg == NULL) return; if (cbs->reset_source) { g_source_remove(cbs->reset_source); cbs->reset_source = 0; } __ofono_netreg_set_base_station_name(cbs->netreg, id); } static void cbs_dispatch_emergency(struct ofono_cbs *cbs, const char *message, enum etws_topic_type topic, gboolean alert, gboolean popup) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cbs->atom); DBusMessage *signal; DBusMessageIter iter; DBusMessageIter dict; dbus_bool_t boolean; const char *emergency_str; if (topic == ETWS_TOPIC_TYPE_TEST) { ofono_error("Explicitly ignoring ETWS Test messages"); return; } switch (topic) { case ETWS_TOPIC_TYPE_EARTHQUAKE: emergency_str = "Earthquake"; break; case ETWS_TOPIC_TYPE_TSUNAMI: emergency_str = "Tsunami"; break; case ETWS_TOPIC_TYPE_EARTHQUAKE_TSUNAMI: emergency_str = "Earthquake+Tsunami"; break; case ETWS_TOPIC_TYPE_EMERGENCY: emergency_str = "Other"; break; default: return; }; signal = dbus_message_new_signal(path, OFONO_CELL_BROADCAST_INTERFACE, "EmergencyBroadcast"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &message); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "EmergencyType", DBUS_TYPE_STRING, &emergency_str); boolean = alert; ofono_dbus_dict_append(&dict, "EmergencyAlert", DBUS_TYPE_BOOLEAN, &boolean); boolean = popup; ofono_dbus_dict_append(&dict, "Popup", DBUS_TYPE_BOOLEAN, &boolean); dbus_message_iter_close_container(&iter, &dict); g_dbus_send_message(conn, signal); } static void cbs_dispatch_text(struct ofono_cbs *cbs, enum sms_class cls, unsigned short channel, const char *message) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cbs->atom); g_dbus_emit_signal(conn, path, OFONO_CELL_BROADCAST_INTERFACE, "IncomingBroadcast", DBUS_TYPE_STRING, &message, DBUS_TYPE_UINT16, &channel, DBUS_TYPE_INVALID); } void ofono_cbs_notify(struct ofono_cbs *cbs, const unsigned char *pdu, int pdu_len) { struct cbs c; enum sms_class cls; gboolean udhi; gboolean comp; GSList *cbs_list; enum sms_charset charset; char *message; char iso639_lang[3]; if (cbs->assembly == NULL) return; if (!cbs_decode(pdu, pdu_len, &c)) { ofono_error("Unable to decode CBS PDU"); return; } if (cbs_topic_in_range(c.message_identifier, cbs->efcbmid_contents)) { if (cbs->sim == NULL) return; if (!__ofono_sim_service_available(cbs->sim, SIM_UST_SERVICE_DATA_DOWNLOAD_SMS_CB, SIM_SST_SERVICE_DATA_DOWNLOAD_SMS_CB)) return; if (cbs->stk) __ofono_cbs_sim_download(cbs->stk, &c); return; } if (!cbs->powered) { ofono_error("Ignoring CBS because powered is off"); return; } if (!cbs_dcs_decode(c.dcs, &udhi, &cls, &charset, &comp, NULL, NULL)) { ofono_error("Unknown / Reserved DCS. Ignoring"); return; } if (udhi) { ofono_error("CBS messages with UDH not supported"); return; } if (charset == SMS_CHARSET_8BIT) { ofono_error("Datagram CBS not supported"); return; } if (comp) { ofono_error("CBS messages with compression not supported"); return; } cbs_list = cbs_assembly_add_page(cbs->assembly, &c); if (cbs_list == NULL) return; message = cbs_decode_text(cbs_list, iso639_lang); if (message == NULL) goto out; if (c.message_identifier >= ETWS_TOPIC_TYPE_EARTHQUAKE && c.message_identifier <= ETWS_TOPIC_TYPE_EMERGENCY) { gboolean alert = FALSE; gboolean popup = FALSE; /* 3GPP 23.041 9.4.1.2.1: Alert is encoded in bit 9 */ if (c.message_code & (1 << 9)) alert = TRUE; /* 3GPP 23.041 9.4.1.2.1: Popup is encoded in bit 8 */ if (c.message_code & (1 << 8)) popup = TRUE; cbs_dispatch_emergency(cbs, message, c.message_identifier, alert, popup); goto out; } /* * 3GPP 23.041: NOTE 5: Code 00 is intended for use by the * network operators for base station IDs. */ if (c.gs == CBS_GEO_SCOPE_CELL_IMMEDIATE) { cbs_dispatch_base_station_id(cbs, message); goto out; } cbs_dispatch_text(cbs, cls, c.message_identifier, message); out: g_free(message); g_slist_foreach(cbs_list, (GFunc)g_free, NULL); g_slist_free(cbs_list); } static DBusMessage *cbs_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cbs *cbs = data; DBusMessage *reply; DBusMessageIter iter, dict; char *topics; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "Powered", DBUS_TYPE_BOOLEAN, &cbs->powered); topics = cbs_topic_ranges_to_string(cbs->topics); ofono_dbus_dict_append(&dict, "Topics", DBUS_TYPE_STRING, &topics); g_free(topics); dbus_message_iter_close_container(&iter, &dict); return reply; } static char *cbs_topics_to_str(struct ofono_cbs *cbs, GSList *user_topics) { GSList *topics = NULL; char *topic_str; struct cbs_topic_range etws_range = { 4352, 4356 }; if (user_topics != NULL) topics = g_slist_concat(topics, g_slist_copy(user_topics)); if (cbs->efcbmid_contents != NULL) topics = g_slist_concat(topics, g_slist_copy(cbs->efcbmid_contents)); topics = g_slist_append(topics, &etws_range); topic_str = cbs_topic_ranges_to_string(topics); g_slist_free(topics); return topic_str; } static void cbs_set_topics_cb(const struct ofono_error *error, void *data) { struct ofono_cbs *cbs = data; const char *path = __ofono_atom_get_path(cbs->atom); DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *reply; char *topics; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { g_slist_foreach(cbs->new_topics, (GFunc)g_free, NULL); g_slist_free(cbs->new_topics); cbs->new_topics = NULL; DBG("Setting Cell Broadcast topics failed"); __ofono_dbus_pending_reply(&cbs->pending, __ofono_error_failed(cbs->pending)); return; } g_slist_foreach(cbs->topics, (GFunc)g_free, NULL); g_slist_free(cbs->topics); cbs->topics = cbs->new_topics; cbs->new_topics = NULL; reply = dbus_message_new_method_return(cbs->pending); __ofono_dbus_pending_reply(&cbs->pending, reply); topics = cbs_topic_ranges_to_string(cbs->topics); ofono_dbus_signal_property_changed(conn, path, OFONO_CELL_BROADCAST_INTERFACE, "Topics", DBUS_TYPE_STRING, &topics); if (cbs->settings) { g_key_file_set_string(cbs->settings, SETTINGS_GROUP, "Topics", topics); storage_sync(cbs->imsi, SETTINGS_STORE, cbs->settings); } g_free(topics); } static DBusMessage *cbs_set_topics(struct ofono_cbs *cbs, const char *value, DBusMessage *msg) { GSList *topics; char *topic_str; struct ofono_error error; topics = cbs_extract_topic_ranges(value); if (topics == NULL && value[0] != '\0') return __ofono_error_invalid_format(msg); if (cbs->driver->set_topics == NULL) return __ofono_error_not_implemented(msg); cbs->new_topics = topics; cbs->pending = dbus_message_ref(msg); if (!cbs->powered) { error.type = OFONO_ERROR_TYPE_NO_ERROR; cbs_set_topics_cb(&error, cbs); return NULL; } topic_str = cbs_topics_to_str(cbs, topics); cbs->driver->set_topics(cbs, topic_str, cbs_set_topics_cb, cbs); g_free(topic_str); return NULL; } static void cbs_set_powered_cb(const struct ofono_error *error, void *data) { struct ofono_cbs *cbs = data; const char *path = __ofono_atom_get_path(cbs->atom); DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Setting Cell Broadcast topics failed"); if (cbs->pending == NULL) return; __ofono_dbus_pending_reply(&cbs->pending, __ofono_error_failed(cbs->pending)); return; } cbs->powered = !cbs->powered; if (cbs->settings) { g_key_file_set_boolean(cbs->settings, SETTINGS_GROUP, "Powered", cbs->powered); storage_sync(cbs->imsi, SETTINGS_STORE, cbs->settings); } ofono_dbus_signal_property_changed(conn, path, OFONO_CELL_BROADCAST_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &cbs->powered); if (cbs->pending == NULL) return; reply = dbus_message_new_method_return(cbs->pending); __ofono_dbus_pending_reply(&cbs->pending, reply); } static DBusMessage *cbs_set_powered(struct ofono_cbs *cbs, gboolean value, DBusMessage *msg) { const char *path = __ofono_atom_get_path(cbs->atom); DBusConnection *conn = ofono_dbus_get_connection(); char *topic_str; if (cbs->powered == value) goto reply; if (cbs->driver->set_topics == NULL || cbs->driver->clear_topics == NULL) goto done; if (msg) cbs->pending = dbus_message_ref(msg); if (value) { topic_str = cbs_topics_to_str(cbs, cbs->topics); cbs->driver->set_topics(cbs, topic_str, cbs_set_powered_cb, cbs); g_free(topic_str); } else { cbs->driver->clear_topics(cbs, cbs_set_powered_cb, cbs); } return NULL; done: cbs->powered = value; if (cbs->settings) { g_key_file_set_boolean(cbs->settings, SETTINGS_GROUP, "Powered", cbs->powered); storage_sync(cbs->imsi, SETTINGS_STORE, cbs->settings); } ofono_dbus_signal_property_changed(conn, path, OFONO_CELL_BROADCAST_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &cbs->powered); reply: if (msg) return dbus_message_new_method_return(msg); return NULL; } static DBusMessage *cbs_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cbs *cbs = data; DBusMessageIter iter; DBusMessageIter var; const char *property; if (cbs->pending) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (!strcmp(property, "Powered")) { dbus_bool_t value; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); return cbs_set_powered(cbs, value, msg); } if (!strcmp(property, "Topics")) { const char *value; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); return cbs_set_topics(cbs, value, msg); } return __ofono_error_invalid_args(msg); } static const GDBusMethodTable cbs_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), cbs_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, cbs_set_property) }, { } }; static const GDBusSignalTable cbs_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "property", "s" }, { "value", "v" })) }, { GDBUS_SIGNAL("IncomingBroadcast", GDBUS_ARGS({ "message", "s" }, { "channel", "q" })) }, { GDBUS_SIGNAL("EmergencyBroadcast", GDBUS_ARGS({ "message", "s" }, { "dict", "a{sv}" })) }, { } }; int ofono_cbs_driver_register(const struct ofono_cbs_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_cbs_driver_unregister(const struct ofono_cbs_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void cbs_unregister(struct ofono_atom *atom) { struct ofono_cbs *cbs = __ofono_atom_get_data(atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); g_dbus_unregister_interface(conn, path, OFONO_CELL_BROADCAST_INTERFACE); ofono_modem_remove_interface(modem, OFONO_CELL_BROADCAST_INTERFACE); if (cbs->topics) { g_slist_foreach(cbs->topics, (GFunc) g_free, NULL); g_slist_free(cbs->topics); cbs->topics = NULL; } if (cbs->new_topics) { g_slist_foreach(cbs->new_topics, (GFunc) g_free, NULL); g_slist_free(cbs->new_topics); cbs->new_topics = NULL; } if (cbs->efcbmid_length) { cbs->efcbmid_length = 0; g_slist_foreach(cbs->efcbmid_contents, (GFunc) g_free, NULL); g_slist_free(cbs->efcbmid_contents); cbs->efcbmid_contents = NULL; } if (cbs->sim_context) { ofono_sim_context_free(cbs->sim_context); cbs->sim_context = NULL; } cbs->sim = NULL; cbs->stk = NULL; if (cbs->reset_source) { g_source_remove(cbs->reset_source); cbs->reset_source = 0; if (cbs->netreg) __ofono_netreg_set_base_station_name(cbs->netreg, NULL); } cbs->powered = FALSE; if (cbs->settings) { storage_close(cbs->imsi, SETTINGS_STORE, cbs->settings, TRUE); g_free(cbs->imsi); cbs->imsi = NULL; cbs->settings = NULL; } if (cbs->netreg_watch) { if (cbs->location_watch) { __ofono_netreg_remove_status_watch(cbs->netreg, cbs->location_watch); cbs->location_watch = 0; } __ofono_modem_remove_atom_watch(modem, cbs->netreg_watch); cbs->netreg_watch = 0; cbs->netreg = NULL; } } static void cbs_remove(struct ofono_atom *atom) { struct ofono_cbs *cbs = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (cbs == NULL) return; if (cbs->driver != NULL && cbs->driver->remove != NULL) cbs->driver->remove(cbs); cbs_assembly_free(cbs->assembly); cbs->assembly = NULL; g_free(cbs); } struct ofono_cbs *ofono_cbs_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_cbs *cbs; GSList *l; if (driver == NULL) return NULL; cbs = g_try_new0(struct ofono_cbs, 1); if (cbs == NULL) return NULL; cbs->assembly = cbs_assembly_new(); cbs->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CBS, cbs_remove, cbs); for (l = g_drivers; l; l = l->next) { const struct ofono_cbs_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(cbs, vendor, data) < 0) continue; cbs->driver = drv; break; } return cbs; } static void cbs_got_file_contents(struct ofono_cbs *cbs) { gboolean powered; GSList *initial_topics = NULL; char *topics_str; GError *error = NULL; if (cbs->topics == NULL) { if (cbs->efcbmi_contents != NULL) initial_topics = g_slist_concat(initial_topics, g_slist_copy(cbs->efcbmi_contents)); if (cbs->efcbmir_contents != NULL) initial_topics = g_slist_concat(initial_topics, g_slist_copy(cbs->efcbmir_contents)); cbs->topics = cbs_optimize_ranges(initial_topics); g_slist_free(initial_topics); topics_str = cbs_topic_ranges_to_string(cbs->topics); g_key_file_set_string(cbs->settings, SETTINGS_GROUP, "Topics", topics_str); g_free(topics_str); storage_sync(cbs->imsi, SETTINGS_STORE, cbs->settings); } if (cbs->efcbmi_length) { cbs->efcbmi_length = 0; g_slist_foreach(cbs->efcbmi_contents, (GFunc) g_free, NULL); g_slist_free(cbs->efcbmi_contents); cbs->efcbmi_contents = NULL; } if (cbs->efcbmir_length) { cbs->efcbmir_length = 0; g_slist_foreach(cbs->efcbmir_contents, (GFunc) g_free, NULL); g_slist_free(cbs->efcbmir_contents); cbs->efcbmir_contents = NULL; } powered = g_key_file_get_boolean(cbs->settings, SETTINGS_GROUP, "Powered", &error); if (error) { g_error_free(error); powered = TRUE; g_key_file_set_boolean(cbs->settings, SETTINGS_GROUP, "Powered", powered); } cbs_set_powered(cbs, powered, NULL); } static void sim_cbmi_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_cbs *cbs = userdata; unsigned short mi; int i; char *str; if (!ok) return; if ((length % 2) == 1 || length < 2) return; cbs->efcbmi_length = length; for (i = 0; i < length; i += 2) { struct cbs_topic_range *range; if (data[i] == 0xff && data[i+1] == 0xff) continue; mi = (data[i] << 8) + data[i+1]; if (mi > 999) continue; range = g_new0(struct cbs_topic_range, 1); range->min = mi; range->max = mi; cbs->efcbmi_contents = g_slist_prepend(cbs->efcbmi_contents, range); } if (cbs->efcbmi_contents == NULL) return; str = cbs_topic_ranges_to_string(cbs->efcbmi_contents); DBG("Got cbmi: %s", str); g_free(str); } static void sim_cbmir_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_cbs *cbs = userdata; int i; unsigned short min; unsigned short max; char *str; if (!ok) return; if ((length % 4) != 0) return; cbs->efcbmir_length = length; for (i = 0; i < length; i += 4) { struct cbs_topic_range *range; if (data[i] == 0xff && data[i+1] == 0xff && data[i+2] == 0xff && data[i+3] == 0xff) continue; min = (data[i] << 8) + data[i+1]; max = (data[i+2] << 8) + data[i+3]; if (min > 999 || max > 999 || min > max) continue; range = g_new0(struct cbs_topic_range, 1); range->min = min; range->max = max; cbs->efcbmir_contents = g_slist_prepend(cbs->efcbmir_contents, range); } if (cbs->efcbmir_contents == NULL) return; str = cbs_topic_ranges_to_string(cbs->efcbmir_contents); DBG("Got cbmir: %s", str); g_free(str); } static void sim_cbmid_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_cbs *cbs = userdata; unsigned short mi; int i; char *str; GSList *contents = NULL; if (!ok) goto done; if ((length % 2) == 1 || length < 2) goto done; cbs->efcbmid_length = length; for (i = 0; i < length; i += 2) { struct cbs_topic_range *range; if (data[i] == 0xff && data[i+1] == 0xff) continue; mi = (data[i] << 8) + data[i+1]; range = g_new0(struct cbs_topic_range, 1); range->min = mi; range->max = mi; contents = g_slist_prepend(contents, range); } if (contents == NULL) goto done; cbs->efcbmid_contents = g_slist_reverse(contents); str = cbs_topic_ranges_to_string(cbs->efcbmid_contents); DBG("Got cbmid: %s", str); g_free(str); done: if (cbs->efcbmid_update) { if (cbs->powered == TRUE) { char *topic_str = cbs_topics_to_str(cbs, cbs->topics); cbs->driver->set_topics(cbs, topic_str, cbs_set_powered_cb, cbs); g_free(topic_str); } cbs->efcbmid_update = FALSE; } else cbs_got_file_contents(cbs); } static void cbs_efcbmid_changed(int id, void *userdata) { struct ofono_cbs *cbs = userdata; if (cbs->efcbmid_length) { cbs->efcbmid_length = 0; g_slist_foreach(cbs->efcbmid_contents, (GFunc) g_free, NULL); g_slist_free(cbs->efcbmid_contents); cbs->efcbmid_contents = NULL; } cbs->efcbmid_update = TRUE; ofono_sim_read(cbs->sim_context, SIM_EFCBMID_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_cbmid_read_cb, cbs); } static void cbs_got_imsi(struct ofono_cbs *cbs) { const char *imsi = ofono_sim_get_imsi(cbs->sim); char *topics_str; DBG("Got IMSI: %s", imsi); cbs->settings = storage_open(imsi, SETTINGS_STORE); if (cbs->settings == NULL) return; cbs->imsi = g_strdup(imsi); cbs->topics = NULL; topics_str = g_key_file_get_string(cbs->settings, SETTINGS_GROUP, "Topics", NULL); if (topics_str) cbs->topics = cbs_extract_topic_ranges(topics_str); /* * If stored value is invalid or no stored value, bootstrap * topics list from SIM contents */ if (topics_str == NULL || (cbs->topics == NULL && topics_str[0] != '\0')) { ofono_sim_read(cbs->sim_context, SIM_EFCBMI_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_cbmi_read_cb, cbs); ofono_sim_read(cbs->sim_context, SIM_EFCBMIR_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_cbmir_read_cb, cbs); } if (topics_str) g_free(topics_str); ofono_sim_read(cbs->sim_context, SIM_EFCBMID_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_cbmid_read_cb, cbs); ofono_sim_add_file_watch(cbs->sim_context, SIM_EFCBMID_FILEID, cbs_efcbmid_changed, cbs, NULL); } static gboolean reset_base_station_name(gpointer user) { struct ofono_cbs *cbs = user; cbs->reset_source = 0; if (cbs->netreg == NULL) goto out; __ofono_netreg_set_base_station_name(cbs->netreg, NULL); out: return FALSE; } static void cbs_location_changed(int status, int lac, int ci, int tech, const char *mcc, const char *mnc, void *data) { struct ofono_cbs *cbs = data; gboolean plmn_changed = FALSE; gboolean lac_changed = FALSE; gboolean ci_changed = FALSE; DBG("%d, %d, %d, %d, %s%s", status, lac, ci, tech, mcc, mnc); if (mcc == NULL || mnc == NULL) { if (cbs->mcc[0] == '\0' && cbs->mnc[0] == '\0') return; memset(cbs->mcc, 0, sizeof(cbs->mcc)); memset(cbs->mnc, 0, sizeof(cbs->mnc)); plmn_changed = TRUE; goto out; } if (strcmp(cbs->mcc, mcc) || strcmp(cbs->mnc, mnc)) { memcpy(cbs->mcc, mcc, sizeof(cbs->mcc)); memcpy(cbs->mnc, mnc, sizeof(cbs->mnc)); plmn_changed = TRUE; goto out; } if (cbs->lac != lac) { cbs->lac = lac; lac_changed = TRUE; goto out; } if (cbs->ci != ci) { cbs->ci = ci; ci_changed = TRUE; goto out; } return; out: DBG("%d, %d, %d", plmn_changed, lac_changed, ci_changed); /* * In order to minimize signal transmissions we wait about X seconds * before reseting the base station id. The hope is that we receive * another cell broadcast with the new base station name within * that time */ if (lac_changed || ci_changed) { cbs->reset_source = g_timeout_add_seconds(3, reset_base_station_name, cbs); } cbs_assembly_location_changed(cbs->assembly, plmn_changed, lac_changed, ci_changed); } static void netreg_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_cbs *cbs = data; const char *mcc; const char *mnc; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { cbs->location_watch = 0; cbs->netreg = 0; return; } cbs->netreg = __ofono_atom_get_data(atom); cbs->location_watch = __ofono_netreg_add_status_watch(cbs->netreg, cbs_location_changed, cbs, NULL); mcc = ofono_netreg_get_mcc(cbs->netreg); mnc = ofono_netreg_get_mnc(cbs->netreg); if (mcc && mnc) { memcpy(cbs->mcc, mcc, sizeof(cbs->mcc)); memcpy(cbs->mnc, mnc, sizeof(cbs->mnc)); } else { memset(cbs->mcc, 0, sizeof(cbs->mcc)); memset(cbs->mnc, 0, sizeof(cbs->mnc)); } cbs->lac = ofono_netreg_get_location(cbs->netreg); cbs->ci = ofono_netreg_get_cellid(cbs->netreg); /* * Clear out the cbs assembly just in case, worst case * we will receive the cell broadcasts again */ cbs_assembly_location_changed(cbs->assembly, TRUE, TRUE, TRUE); } void ofono_cbs_register(struct ofono_cbs *cbs) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(cbs->atom); const char *path = __ofono_atom_get_path(cbs->atom); if (!g_dbus_register_interface(conn, path, OFONO_CELL_BROADCAST_INTERFACE, cbs_methods, cbs_signals, NULL, cbs, NULL)) { ofono_error("Could not create %s interface", OFONO_CELL_BROADCAST_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_CELL_BROADCAST_INTERFACE); cbs->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem); if (cbs->sim) { cbs->sim_context = ofono_sim_context_create(cbs->sim); if (ofono_sim_get_state(cbs->sim) == OFONO_SIM_STATE_READY) cbs_got_imsi(cbs); } cbs->stk = __ofono_atom_find(OFONO_ATOM_TYPE_STK, modem); cbs->netreg_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_NETREG, netreg_watch, cbs, NULL); __ofono_atom_register(cbs->atom, cbs_unregister); } void ofono_cbs_remove(struct ofono_cbs *cbs) { __ofono_atom_free(cbs->atom); } void ofono_cbs_set_data(struct ofono_cbs *cbs, void *data) { cbs->driver_data = data; } void *ofono_cbs_get_data(struct ofono_cbs *cbs) { return cbs->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/wakelock.c0000644000015600001650000000623312671500073021142 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2015 Jolla Ltd. All rights reserved. * Contact: Hannu Mallat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "log.h" #include "wakelock.h" #include "ofono.h" #define SYSTEM_WAKELOCK_NAME "ofono-system" #define SYSTEM_WAKELOCK_DURATION 30 static char *impl = NULL; static struct wakelock_table table; static struct wakelock *system_wakelock = NULL; static guint system_wakelock_source = 0; static gboolean system_wakelock_put(gpointer user_data) { DBG("Releasing system wakelock"); wakelock_release(system_wakelock); system_wakelock_source = 0; return FALSE; } void wakelock_system_lock(void) { guint old_source; if (impl == NULL) return; DBG("Acquiring system wakelock"); old_source = system_wakelock_source; system_wakelock_source = g_timeout_add_seconds(SYSTEM_WAKELOCK_DURATION, system_wakelock_put, NULL); if (system_wakelock_source) wakelock_acquire(system_wakelock); if (old_source) { g_source_remove(old_source); wakelock_release(system_wakelock); } } int wakelock_create(const char *name, struct wakelock **wakelock) { if (impl == NULL) { *wakelock = NULL; return -EINVAL; } return (table.create)(name, wakelock); } int wakelock_free(struct wakelock *wakelock) { if (impl == NULL) return -EINVAL; return table.free(wakelock); } int wakelock_acquire(struct wakelock *wakelock) { if (impl == NULL) return -EINVAL; return table.acquire(wakelock); } int wakelock_release(struct wakelock *wakelock) { if (impl == NULL) return -EINVAL; return table.release(wakelock); } ofono_bool_t wakelock_is_locked(struct wakelock *wakelock) { if (impl == NULL) return -EINVAL; return table.is_locked(wakelock); } int wakelock_plugin_register(const char *name, struct wakelock_table *fns) { if (impl) return -EALREADY; impl = g_strdup(name); memcpy(&table, fns, sizeof(struct wakelock_table)); return 0; } int wakelock_plugin_unregister(void) { if (impl == NULL) return -ENOENT; memset(&table, 0, sizeof(struct wakelock_table)); g_free(impl); impl = NULL; return 0; } int __ofono_wakelock_init(void) { if (wakelock_create(SYSTEM_WAKELOCK_NAME, &system_wakelock) < 0) ofono_warn("Failed to create system keep alive wakelock"); return 0; } void __ofono_wakelock_cleanup(void) { if (system_wakelock) wakelock_free(system_wakelock); } ofono-1.17.bzr6912+16.04.20160314.3/src/cdma-smsutil.c0000644000015600001650000004254612671500024021747 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "cdma-smsutil.h" #define uninitialized_var(x) x = x enum cdma_sms_rec_flag { CDMA_SMS_REC_FLAG_MANDATORY = 1, }; typedef gboolean (*rec_handler)(const guint8 *, guint8, void *); struct simple_iter { guint8 max; const guint8 *pdu; guint8 pos; guint8 id; guint8 len; const guint8 *data; }; static void simple_iter_init(struct simple_iter *iter, const guint8 *pdu, guint8 len) { iter->pdu = pdu; iter->max = len; iter->pos = 0; iter->id = 0; iter->len = 0; iter->data = NULL; } static gboolean simple_iter_next(struct simple_iter *iter) { const guint8 *pdu = iter->pdu + iter->pos; const guint8 *end = iter->pdu + iter->max; guint8 id; guint8 len; if (pdu == end) return FALSE; id = *pdu; pdu++; if (pdu == end) return FALSE; len = *pdu++; if (pdu + len > end) return FALSE; iter->id = id; iter->len = len; iter->data = pdu; iter->pos = pdu + len - iter->pdu; return TRUE; } static guint8 simple_iter_get_id(struct simple_iter *iter) { return iter->id; } static guint8 simple_iter_get_length(struct simple_iter *iter) { return iter->len; } static const guint8 *simple_iter_get_data(struct simple_iter *iter) { return iter->data; } static inline void set_bitmap(guint32 *bitmap, guint8 pos) { *bitmap = *bitmap | (1 << pos); } /* Unpacks the byte stream. The field has to be <= 8 bits. */ static guint8 bit_field_unpack(const guint8 *buf, guint16 offset, guint8 nbit) { guint8 bit_pos; guint8 val = 0; const guint8 *pdu; pdu = buf + (offset >> 3); bit_pos = 8 - (offset & 0x7); /* Field to be extracted is within current byte */ if (nbit <= bit_pos) return (*pdu >> (bit_pos - nbit)) & ((1 << nbit) - 1); /* Field to be extracted crossing two bytes */ val = *pdu & ((1 << bit_pos) - 1); nbit -= bit_pos; pdu++; return (val << nbit) | (*pdu >> (8 - nbit)); } /* Convert CDMA DTMF digits into a string */ static gboolean dtmf_to_ascii(char *buf, const guint8 *addr, guint8 num_fields) { /* * Mapping from binary DTMF code to the digit it represents. * As defined in Table 2.7.1.3.2.4-4 of 3GPP2 C.S0005-E v2.0. * Note, 0 is NOT a valid value and not mapped to * any valid DTMF digit. */ static const char dtmf_digits[13] = {0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '*', '#'}; guint8 index; guint8 value; for (index = 0; index < num_fields; index++) { if (addr[index] == 0 || addr[index] > 12) return FALSE; /* Invalid digit in address field */ value = addr[index]; buf[index] = dtmf_digits[value]; } buf[index] = 0; /* Make it NULL terminated string */ return TRUE; } const char *cdma_sms_address_to_string(const struct cdma_sms_address *addr) { static char buf[CDMA_SMS_MAX_ADDR_FIELDS + 1]; /* TODO: Only support CDMA_SMS_DIGIT_MODE_4BIT_DTMF currently */ switch (addr->digit_mode) { case CDMA_SMS_DIGIT_MODE_4BIT_DTMF: if (dtmf_to_ascii(buf, addr->address, addr->num_fields) == TRUE) return buf; else return NULL; case CDMA_SMS_DIGIT_MODE_8BIT_ASCII: return NULL; } return NULL; } /* Decode Teleservice ID */ static gboolean cdma_sms_decode_teleservice(const guint8 *buf, guint8 len, void *data) { enum cdma_sms_teleservice_id *id = data; *id = bit_field_unpack(buf, 0, 8) << 8 | bit_field_unpack(buf, 8, 8); switch (*id) { case CDMA_SMS_TELESERVICE_ID_CMT91: case CDMA_SMS_TELESERVICE_ID_WPT: case CDMA_SMS_TELESERVICE_ID_WMT: case CDMA_SMS_TELESERVICE_ID_VMN: case CDMA_SMS_TELESERVICE_ID_WAP: case CDMA_SMS_TELESERVICE_ID_WEMT: case CDMA_SMS_TELESERVICE_ID_SCPT: case CDMA_SMS_TELESERVICE_ID_CATPT: return TRUE; } return FALSE; /* Invalid teleservice type */ } /* Decode Address parameter record */ static gboolean cdma_sms_decode_addr(const guint8 *buf, guint8 len, void *data) { struct cdma_sms_address *addr = data; guint16 bit_offset = 0; guint8 chari_len; guint16 total_num_bits = len * 8; guint8 index; addr->digit_mode = bit_field_unpack(buf, bit_offset, 1); bit_offset += 1; addr->number_mode = bit_field_unpack(buf, bit_offset, 1); bit_offset += 1; if (addr->digit_mode == CDMA_SMS_DIGIT_MODE_8BIT_ASCII) { if (addr->number_mode == CDMA_SMS_NUM_MODE_DIGIT) addr->digi_num_type = bit_field_unpack(buf, bit_offset, 3); else addr->data_nw_num_type = bit_field_unpack(buf, bit_offset, 3); bit_offset += 3; if (addr->number_mode == CDMA_SMS_NUM_MODE_DIGIT) { if (bit_offset + 4 > total_num_bits) return FALSE; addr->number_plan = bit_field_unpack(buf, bit_offset, 4); bit_offset += 4; } } if (bit_offset + 8 > total_num_bits) return FALSE; addr->num_fields = bit_field_unpack(buf, bit_offset, 8); bit_offset += 8; if (addr->digit_mode == CDMA_SMS_DIGIT_MODE_4BIT_DTMF) chari_len = 4; else chari_len = 8; if ((bit_offset + chari_len * addr->num_fields) > total_num_bits) return FALSE; for (index = 0; index < addr->num_fields; index++) { addr->address[index] = bit_field_unpack(buf, bit_offset, chari_len); bit_offset += chari_len; } return TRUE; } static char *decode_text_7bit_ascii(const struct cdma_sms_ud *ud) { char *buf; buf = g_new(char, ud->num_fields + 1); if (buf == NULL) return NULL; memcpy(buf, ud->chari, ud->num_fields); buf[ud->num_fields] = 0; /* Make it NULL terminated string */ return buf; } char *cdma_sms_decode_text(const struct cdma_sms_ud *ud) { switch (ud->msg_encoding) { case CDMA_SMS_MSG_ENCODING_OCTET: case CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG: return NULL; /* TODO */ case CDMA_SMS_MSG_ENCODING_7BIT_ASCII: return decode_text_7bit_ascii(ud); case CDMA_SMS_MSG_ENCODING_IA5: case CDMA_SMS_MSG_ENCODING_UNICODE: case CDMA_SMS_MSG_ENCODING_SHIFT_JIS: case CDMA_SMS_MSG_ENCODING_KOREAN: case CDMA_SMS_MSG_ENCODING_LATIN_HEBREW: case CDMA_SMS_MSG_ENCODING_LATIN: case CDMA_SMS_MSG_ENCODING_GSM_7BIT: case CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING: return NULL; /* TODO */ } return NULL; } /* Decode User Data */ static gboolean cdma_sms_decode_ud(const guint8 *buf, guint8 len, void *data) { guint16 bit_offset = 0; guint8 chari_len = 0; guint16 total_num_bits = len * 8; guint8 index; enum cdma_sms_msg_encoding msg_encoding; struct cdma_sms_ud *ud = data; if (total_num_bits < 13) return FALSE; msg_encoding = bit_field_unpack(buf, bit_offset, 5); ud->msg_encoding = msg_encoding; bit_offset += 5; if (msg_encoding == CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG || msg_encoding == CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING) { /* * Skip message type field for now. * TODO: Add support for message type field. */ bit_offset += 8; } if (bit_offset + 8 > total_num_bits) return FALSE; ud->num_fields = bit_field_unpack(buf, bit_offset, 8); bit_offset += 8; switch (msg_encoding) { case CDMA_SMS_MSG_ENCODING_OCTET: chari_len = 8; break; case CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG: return FALSE; /* TODO */ case CDMA_SMS_MSG_ENCODING_7BIT_ASCII: case CDMA_SMS_MSG_ENCODING_IA5: chari_len = 7; break; case CDMA_SMS_MSG_ENCODING_UNICODE: case CDMA_SMS_MSG_ENCODING_SHIFT_JIS: case CDMA_SMS_MSG_ENCODING_KOREAN: return FALSE; /* TODO */ case CDMA_SMS_MSG_ENCODING_LATIN_HEBREW: case CDMA_SMS_MSG_ENCODING_LATIN: chari_len = 8; break; case CDMA_SMS_MSG_ENCODING_GSM_7BIT: chari_len = 7; break; case CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING: return FALSE; /* TODO */ } /* TODO: Add support for all other encoding types */ if (chari_len == 0) return FALSE; if (bit_offset + chari_len * ud->num_fields > total_num_bits) return FALSE; for (index = 0; index < ud->num_fields; index++) { ud->chari[index] = bit_field_unpack(buf, bit_offset, chari_len); bit_offset += chari_len; } return TRUE; } /* Decode Message Identifier */ static gboolean cdma_sms_decode_message_id(const guint8 *buf, guint8 len, void *data) { struct cdma_sms_identifier *id = data; if (len != 3) return FALSE; id->msg_type = bit_field_unpack(buf, 0, 4); if (id->msg_type <= 0 || id->msg_type > CDMA_SMS_MSG_TYPE_SUBMIT_REPORT) return FALSE; /* Invalid message type */ id->msg_id = (bit_field_unpack(buf, 4, 8) << 8) | bit_field_unpack(buf, 12, 8); id->header_ind = bit_field_unpack(buf, 20, 1); return TRUE; } static gboolean find_and_decode(struct simple_iter *iter, guint8 rec_id, rec_handler handler, void *data) { guint8 id; guint8 len; const guint8 *buf; while (simple_iter_next(iter) == TRUE) { id = simple_iter_get_id(iter); if (id != rec_id) continue; len = simple_iter_get_length(iter); buf = simple_iter_get_data(iter); return handler(buf, len, data); } return FALSE; } static rec_handler subparam_handler_for_id(enum cdma_sms_subparam_id id) { switch (id) { case CDMA_SMS_SUBPARAM_ID_MESSAGE_ID: return cdma_sms_decode_message_id; case CDMA_SMS_SUBPARAM_ID_USER_DATA: return cdma_sms_decode_ud; case CDMA_SMS_SUBPARAM_ID_USER_RESPONSE_CODE: case CDMA_SMS_SUBPARAM_ID_MC_TIME_STAMP: case CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_ABSOLUTE: case CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_RELATIVE: case CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE: case CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_RELATIVE: case CDMA_SMS_SUBPARAM_ID_PRIORITY_INDICATOR: case CDMA_SMS_SUBPARAM_ID_PRIVACY_INDICATOR: case CDMA_SMS_SUBPARAM_ID_REPLY_OPTION: case CDMA_SMS_SUBPARAM_ID_NUMBER_OF_MESSAGES: case CDMA_SMS_SUBPARAM_ID_ALERT_ON_MESSAGE_DELIVERY: case CDMA_SMS_SUBPARAM_ID_LANGUAGE_INDICATOR: case CDMA_SMS_SUBPARAM_ID_CALL_BACK_NUMBER: case CDMA_SMS_SUBPARAM_ID_MESSAGE_DISPLAY_MODE: case CDMA_SMS_SUBPARAM_ID_MULTIPLE_ENCODING_USER_DATA: case CDMA_SMS_SUBPARAM_ID_MESSAGE_DEPOSIT_INDEX: case CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_DATA: case CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_RESULT: case CDMA_SMS_SUBPARAM_ID_MESSAGE_STATUS: case CDMA_SMS_SUBPARAM_ID_TP_FAILURE_CAUSE: case CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN: case CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN_ACK: return NULL; /* TODO */ } return NULL; } struct subparam_handler_entry { enum cdma_sms_subparam_id id; int flags; gboolean found; void *data; }; static gboolean decode_subparams(struct simple_iter *iter, guint32 *bitmap, void *data, ...) { GSList *entries = NULL; GSList *l; va_list args; gboolean decode_result = TRUE; va_start(args, data); while (data != NULL) { struct subparam_handler_entry *entry; entry = g_new0(struct subparam_handler_entry, 1); entry->data = data; entry->id = va_arg(args, enum cdma_sms_subparam_id); entry->flags = va_arg(args, int); data = va_arg(args, void *); entries = g_slist_prepend(entries, entry); } va_end(args); entries = g_slist_reverse(entries); l = entries; while (simple_iter_next(iter) == TRUE) { rec_handler handler; struct subparam_handler_entry *entry; guint8 subparam_len; const guint8 *subparam_buf; GSList *l2; for (l2 = l; l2; l2 = l2->next) { entry = l2->data; if (simple_iter_get_id(iter) == entry->id) break; } /* Ignore unexpected subparameter record */ if (l2 == NULL) continue; entry->found = TRUE; subparam_len = simple_iter_get_length(iter); subparam_buf = simple_iter_get_data(iter); handler = subparam_handler_for_id(entry->id); decode_result = handler(subparam_buf, subparam_len, entry->data); if (decode_result == FALSE) break; /* Stop if decoding failed */ set_bitmap(bitmap, entry->id); } for (; l; l = l->next) { struct subparam_handler_entry *entry = l->data; if ((entry->flags & CDMA_SMS_REC_FLAG_MANDATORY) && (entry->found == FALSE)) { decode_result = FALSE; break; } } g_slist_foreach(entries, (GFunc) g_free, NULL); g_slist_free(entries); return decode_result; } /* Decode WMT */ static gboolean cdma_sms_decode_wmt(struct simple_iter *iter, struct cdma_sms_bearer_data *bd) { switch (bd->id.msg_type) { case CDMA_SMS_MSG_TYPE_RESERVED: return FALSE; /* Invalid */ case CDMA_SMS_MSG_TYPE_DELIVER: /* * WMT DELIVER, table 4.3.4-1 of C.S0015-B v2.0 * TODO: Not all optional subparameters supported. */ return decode_subparams(iter, &bd->subparam_bitmap, &bd->wmt_deliver.ud, CDMA_SMS_SUBPARAM_ID_USER_DATA, 0, NULL); break; case CDMA_SMS_MSG_TYPE_SUBMIT: case CDMA_SMS_MSG_TYPE_CANCEL: return FALSE; /* Invalid for MT WMT */ case CDMA_SMS_MSG_TYPE_DELIVER_ACK: case CDMA_SMS_MSG_TYPE_USER_ACK: case CDMA_SMS_MSG_TYPE_READ_ACK: return FALSE; /* TODO: Not supported yet */ case CDMA_SMS_MSG_TYPE_DELIVER_REPORT: case CDMA_SMS_MSG_TYPE_SUBMIT_REPORT: return FALSE; /* Invalid for MT WMT */ } return FALSE; } static gboolean p2p_decode_bearer_data(const guint8 *buf, guint8 len, enum cdma_sms_teleservice_id tele_id, struct cdma_sms_bearer_data *bd) { struct simple_iter iter; simple_iter_init(&iter, buf, len); /* Message Identifier is mandatory, * Section 4 of C.S0015-B v2.0 */ if (find_and_decode(&iter, CDMA_SMS_SUBPARAM_ID_MESSAGE_ID, cdma_sms_decode_message_id, &bd->id) != TRUE) return FALSE; set_bitmap(&bd->subparam_bitmap, CDMA_SMS_SUBPARAM_ID_MESSAGE_ID); simple_iter_init(&iter, buf, len); switch (tele_id) { case CDMA_SMS_TELESERVICE_ID_CMT91: case CDMA_SMS_TELESERVICE_ID_WPT: return FALSE; /* TODO */ case CDMA_SMS_TELESERVICE_ID_WMT: return cdma_sms_decode_wmt(&iter, bd); case CDMA_SMS_TELESERVICE_ID_VMN: case CDMA_SMS_TELESERVICE_ID_WAP: case CDMA_SMS_TELESERVICE_ID_WEMT: case CDMA_SMS_TELESERVICE_ID_SCPT: case CDMA_SMS_TELESERVICE_ID_CATPT: return FALSE; /* TODO */ } return FALSE; } /* Decode Bearer Data */ static gboolean cdma_sms_decode_bearer_data(const guint8 *buf, guint8 len, void *data) { struct cdma_sms *msg = data; switch (msg->type) { case CDMA_SMS_TP_MSG_TYPE_P2P: return p2p_decode_bearer_data(buf, len, msg->p2p_msg.teleservice_id, &msg->p2p_msg.bd); case CDMA_SMS_TP_MSG_TYPE_BCAST: return FALSE; /* TODO */ case CDMA_SMS_TP_MSG_TYPE_ACK: return FALSE; /* Invalid */ } return FALSE; } static rec_handler param_handler_for_id(enum cdma_sms_param_id id, struct cdma_sms *incoming, void **data) { if (incoming->type != CDMA_SMS_TP_MSG_TYPE_P2P) return NULL; /* TODO: Other types not supported yet */ switch (id) { case CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER: *data = &incoming->p2p_msg.teleservice_id; return cdma_sms_decode_teleservice; case CDMA_SMS_PARAM_ID_SERVICE_CATEGORY: return NULL; /* TODO */ case CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS: *data = &incoming->p2p_msg.oaddr; return cdma_sms_decode_addr; case CDMA_SMS_PARAM_ID_ORIGINATING_SUBADDRESS: case CDMA_SMS_PARAM_ID_DESTINATION_ADDRESS: case CDMA_SMS_PARAM_ID_DESTINATION_SUBADDRESS: case CDMA_SMS_PARAM_ID_BEARER_REPLY_OPTION: case CDMA_SMS_PARAM_ID_CAUSE_CODE: return NULL; /* TODO */ case CDMA_SMS_PARAM_ID_BEARER_DATA: *data = incoming; return cdma_sms_decode_bearer_data; } return NULL; } static gboolean cdma_sms_p2p_decode(const guint8 *pdu, guint8 len, struct cdma_sms *incoming) { struct simple_iter iter; simple_iter_init(&iter, pdu, len); /* * Teleservice Identifier is mandatory, * Table 3.4.2.1-1 of C.S0015-B v2.0 */ if (find_and_decode(&iter, CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER, cdma_sms_decode_teleservice, &incoming->p2p_msg.teleservice_id) != TRUE) return FALSE; set_bitmap(&incoming->p2p_msg.param_bitmap, CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER); simple_iter_init(&iter, pdu, len); while (simple_iter_next(&iter) == TRUE) { rec_handler handler; enum cdma_sms_param_id rec_id; guint8 rec_len; const guint8 *rec_buf; void *uninitialized_var(dataobj); rec_id = simple_iter_get_id(&iter); if (rec_id == CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER) continue; rec_len = simple_iter_get_length(&iter); rec_buf = simple_iter_get_data(&iter); handler = param_handler_for_id(rec_id, incoming, &dataobj); if (handler != NULL) { if (handler(rec_buf, rec_len, dataobj) == FALSE) return FALSE; set_bitmap(&incoming->p2p_msg.param_bitmap, rec_id); } } /* * Originating Address is mandatory field, * Table 3.4.2.1-1 of C.S0015-B v2.0 */ if ((incoming->p2p_msg.param_bitmap & (1 << CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS)) == 0) return FALSE; return TRUE; } gboolean cdma_sms_decode(const guint8 *pdu, guint8 len, struct cdma_sms *incoming) { incoming->type = bit_field_unpack(pdu, 0, 8); pdu += 1; len -= 1; switch (incoming->type) { case CDMA_SMS_TP_MSG_TYPE_P2P: return cdma_sms_p2p_decode(pdu, len, incoming); case CDMA_SMS_TP_MSG_TYPE_BCAST: case CDMA_SMS_TP_MSG_TYPE_ACK: /* TODO: Not supported yet */ return FALSE; } return FALSE; } ofono-1.17.bzr6912+16.04.20160314.3/src/log.c0000644000015600001650000001303512671500024020115 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #ifdef __GLIBC__ #include #endif #include #include "ofono.h" static const char *program_exec; static const char *program_path; /** * ofono_info: * @format: format string * @Varargs: list of arguments * * Output general information */ void ofono_info(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_INFO, format, ap); va_end(ap); } /** * ofono_warn: * @format: format string * @Varargs: list of arguments * * Output warning messages */ void ofono_warn(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_WARNING, format, ap); va_end(ap); } /** * ofono_error: * @format: format string * @varargs: list of arguments * * Output error messages */ void ofono_error(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_ERR, format, ap); va_end(ap); } /** * ofono_debug: * @format: format string * @varargs: list of arguments * * Output debug message * * The actual output of the debug message is controlled via a command line * switch. If not enabled, these messages will be ignored. */ void ofono_debug(const char *format, ...) { va_list ap; va_start(ap, format); vsyslog(LOG_DEBUG, format, ap); va_end(ap); } #ifdef __GLIBC__ static void print_backtrace(unsigned int offset) __attribute__ ((used)); static void print_backtrace(unsigned int offset) { void *frames[99]; size_t n_ptrs; unsigned int i; int outfd[2], infd[2]; int pathlen; pid_t pid; if (program_exec == NULL) return; pathlen = strlen(program_path); n_ptrs = backtrace(frames, G_N_ELEMENTS(frames)); if (n_ptrs < offset) return; if (pipe(outfd) < 0) return; if (pipe(infd) < 0) { close(outfd[0]); close(outfd[1]); return; } pid = fork(); if (pid < 0) { close(outfd[0]); close(outfd[1]); close(infd[0]); close(infd[1]); return; } if (pid == 0) { close(outfd[1]); close(infd[0]); dup2(outfd[0], STDIN_FILENO); dup2(infd[1], STDOUT_FILENO); execlp("addr2line", "-C", "-f", "-e", program_exec, NULL); exit(EXIT_FAILURE); } close(outfd[0]); close(infd[1]); ofono_error("++++++++ backtrace ++++++++"); for (i = offset; i < n_ptrs - 1; i++) { Dl_info info; char addr[20], buf[PATH_MAX * 2]; int len, written; char *ptr, *pos; dladdr(frames[i], &info); len = snprintf(addr, sizeof(addr), "%p\n", frames[i]); if (len < 0) break; written = write(outfd[1], addr, len); if (written < 0) break; len = read(infd[0], buf, sizeof(buf)); if (len < 0) break; buf[len] = '\0'; pos = strchr(buf, '\n'); *pos++ = '\0'; if (strcmp(buf, "??") == 0) { ofono_error("#%-2u %p in %s", i - offset, frames[i], info.dli_fname); continue; } ptr = strchr(pos, '\n'); *ptr++ = '\0'; if (strncmp(pos, program_path, pathlen) == 0) pos += pathlen + 1; ofono_error("#%-2u %p in %s() at %s", i - offset, frames[i], buf, pos); } ofono_error("+++++++++++++++++++++++++++"); kill(pid, SIGTERM); close(outfd[1]); close(infd[0]); } #endif extern struct ofono_debug_desc __start___debug[]; extern struct ofono_debug_desc __stop___debug[]; static gchar **enabled = NULL; static ofono_bool_t is_enabled(struct ofono_debug_desc *desc) { int i; if (enabled == NULL) return FALSE; for (i = 0; enabled[i] != NULL; i++) { if (desc->name != NULL && g_pattern_match_simple(enabled[i], desc->name) == TRUE) return TRUE; if (desc->file != NULL && g_pattern_match_simple(enabled[i], desc->file) == TRUE) return TRUE; } return FALSE; } void __ofono_log_enable(struct ofono_debug_desc *start, struct ofono_debug_desc *stop) { struct ofono_debug_desc *desc; const char *name = NULL, *file = NULL; if (start == NULL || stop == NULL) return; for (desc = start; desc < stop; desc++) { if (file != NULL || name != NULL) { if (g_strcmp0(desc->file, file) == 0) { if (desc->name == NULL) desc->name = name; } else file = NULL; } if (is_enabled(desc) == TRUE) desc->flags |= OFONO_DEBUG_FLAG_PRINT; } } int __ofono_log_init(const char *program, const char *debug, ofono_bool_t detach) { static char path[PATH_MAX]; int option = LOG_NDELAY | LOG_PID; program_exec = program; program_path = getcwd(path, sizeof(path)); if (debug != NULL) enabled = g_strsplit_set(debug, ":, ", 0); __ofono_log_enable(__start___debug, __stop___debug); if (detach == FALSE) option |= LOG_PERROR; openlog(basename(program), option, LOG_DAEMON); syslog(LOG_INFO, "oFono version %s", VERSION); return 0; } void __ofono_log_cleanup(void) { syslog(LOG_INFO, "Exit"); closelog(); g_strfreev(enabled); } ofono-1.17.bzr6912+16.04.20160314.3/src/history.c0000644000015600001650000001436412671500024021043 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include "ofono.h" static GSList *history_drivers = NULL; struct history_call_foreach_data { const struct ofono_call *call; union { struct { time_t start; time_t end; }; time_t when; }; }; struct history_sms_foreach_data { const struct ofono_uuid *uuid; const char *address; const char *text; union { struct { const struct tm *remote; const struct tm *local; }; struct { time_t when; enum ofono_history_sms_status status; }; }; }; static struct ofono_history_context *history_context_create( struct ofono_modem *modem, struct ofono_history_driver *driver) { struct ofono_history_context *context; if (driver->probe == NULL) return NULL; context = g_try_new0(struct ofono_history_context, 1); if (context == NULL) return NULL; context->driver = driver; context->modem = modem; if (driver->probe(context) < 0) { g_free(context); return NULL; } return context; } static void context_remove(struct ofono_atom *atom) { struct ofono_history_context *context = __ofono_atom_get_data(atom); if (context->driver->remove) context->driver->remove(context); g_free(context); } void __ofono_history_probe_drivers(struct ofono_modem *modem) { struct ofono_history_driver *driver; struct ofono_history_context *context; GSList *l; for (l = history_drivers; l; l = l->next) { driver = l->data; context = history_context_create(modem, driver); if (context == NULL) continue; __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_HISTORY, context_remove, context); } } static void history_call_ended(struct ofono_atom *atom, void *data) { struct ofono_history_context *context = __ofono_atom_get_data(atom); struct history_call_foreach_data *hfd = data; if (context->driver->call_ended == NULL) return; context->driver->call_ended(context, hfd->call, hfd->start, hfd->end); } void __ofono_history_call_ended(struct ofono_modem *modem, const struct ofono_call *call, time_t start, time_t end) { struct history_call_foreach_data hfd; hfd.call = call; hfd.start = start; hfd.end = end; __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_HISTORY, history_call_ended, &hfd); } static void history_call_missed(struct ofono_atom *atom, void *data) { struct ofono_history_context *context = __ofono_atom_get_data(atom); struct history_call_foreach_data *hfd = data; if (context->driver->call_missed == NULL) return; context->driver->call_missed(context, hfd->call, hfd->when); } void __ofono_history_call_missed(struct ofono_modem *modem, const struct ofono_call *call, time_t when) { struct history_call_foreach_data hfd; hfd.call = call; hfd.when = when; __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_HISTORY, history_call_missed, &hfd); } static void history_sms_received(struct ofono_atom *atom, void *data) { struct ofono_history_context *context = __ofono_atom_get_data(atom); struct history_sms_foreach_data *hfd = data; if (context->driver->sms_received == NULL) return; context->driver->sms_received(context, hfd->uuid, hfd->address, hfd->remote, hfd->local, hfd->text); } void __ofono_history_sms_received(struct ofono_modem *modem, const struct ofono_uuid *uuid, const char *from, const struct tm *remote, const struct tm *local, const char *text) { struct history_sms_foreach_data hfd; hfd.uuid = uuid; hfd.address = from; hfd.remote = remote; hfd.local = local; hfd.text = text; __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_HISTORY, history_sms_received, &hfd); } static void history_sms_send_pending(struct ofono_atom *atom, void *data) { struct ofono_history_context *context = __ofono_atom_get_data(atom); struct history_sms_foreach_data *hfd = data; if (context->driver->sms_send_pending == NULL) return; context->driver->sms_send_pending(context, hfd->uuid, hfd->address, hfd->when, hfd->text); } void __ofono_history_sms_send_pending(struct ofono_modem *modem, const struct ofono_uuid *uuid, const char *to, time_t when, const char *text) { struct history_sms_foreach_data hfd; hfd.uuid = uuid; hfd.address = to; hfd.text = text; hfd.when = when; hfd.status = OFONO_HISTORY_SMS_STATUS_PENDING; __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_HISTORY, history_sms_send_pending, &hfd); } static void history_sms_send_status(struct ofono_atom *atom, void *data) { struct ofono_history_context *context = __ofono_atom_get_data(atom); struct history_sms_foreach_data *hfd = data; if (context->driver->sms_send_status == NULL) return; context->driver->sms_send_status(context, hfd->uuid, hfd->when, hfd->status); } void __ofono_history_sms_send_status(struct ofono_modem *modem, const struct ofono_uuid *uuid, time_t when, enum ofono_history_sms_status status) { struct history_sms_foreach_data hfd; hfd.uuid = uuid; hfd.address = NULL; hfd.text = NULL; hfd.when = when; hfd.status = status; __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_HISTORY, history_sms_send_status, &hfd); } int ofono_history_driver_register(const struct ofono_history_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); history_drivers = g_slist_prepend(history_drivers, (void *) driver); return 0; } void ofono_history_driver_unregister(const struct ofono_history_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); history_drivers = g_slist_remove(history_drivers, driver); } ofono-1.17.bzr6912+16.04.20160314.3/src/smsutil.h0000644000015600001650000003772212671500024021052 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #define CBS_MAX_GSM_CHARS 93 #define SMS_MSGID_LEN 20 enum sms_type { SMS_TYPE_DELIVER = 0, SMS_TYPE_DELIVER_REPORT_ACK, SMS_TYPE_DELIVER_REPORT_ERROR, SMS_TYPE_STATUS_REPORT, SMS_TYPE_SUBMIT, SMS_TYPE_SUBMIT_REPORT_ACK, SMS_TYPE_SUBMIT_REPORT_ERROR, SMS_TYPE_COMMAND }; /* 23.040 Section 9.1.2.5 */ enum sms_number_type { SMS_NUMBER_TYPE_UNKNOWN = 0, SMS_NUMBER_TYPE_INTERNATIONAL = 1, SMS_NUMBER_TYPE_NATIONAL = 2, SMS_NUMBER_TYPE_NETWORK_SPECIFIC = 3, SMS_NUMBER_TYPE_SUBSCRIBER = 4, SMS_NUMBER_TYPE_ALPHANUMERIC = 5, SMS_NUMBER_TYPE_ABBREVIATED = 6, SMS_NUMBER_TYPE_RESERVED = 7 }; /* 23.040 Section 9.1.2.5 */ enum sms_numbering_plan { SMS_NUMBERING_PLAN_UNKNOWN = 0, SMS_NUMBERING_PLAN_ISDN = 1, SMS_NUMBERING_PLAN_DATA = 3, SMS_NUMBERING_PLAN_TELEX = 4, SMS_NUMBERING_PLAN_SC1 = 5, SMS_NUMBERING_PLAN_SC2 = 6, SMS_NUMBERING_PLAN_NATIONAL = 8, SMS_NUMBERING_PLAN_PRIVATE = 9, SMS_NUMBERING_PLAN_ERMES = 10, SMS_NUMBERING_PLAN_RESERVED = 15 }; enum sms_validity_period_format { SMS_VALIDITY_PERIOD_FORMAT_ABSENT = 0, SMS_VALIDITY_PERIOD_FORMAT_ENHANCED = 1, SMS_VALIDITY_PERIOD_FORMAT_RELATIVE = 2, SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE = 3, }; enum sms_st { SMS_ST_COMPLETED_RECEIVED = 0x0, SMS_ST_COMPLETED_UNABLE_TO_CONFIRM = 0x1, SMS_ST_COMPLETED_REPLACED = 0x2, SMS_ST_COMPLETED_LAST = 0x1F, SMS_ST_TEMPORARY_CONGESTION = 0x20, SMS_ST_TEMPORARY_SME_BUSY = 0x21, SMS_ST_TEMPORARY_NO_RESPONSE = 0x22, SMS_ST_TEMPORARY_SERVICE_REJECTED = 0x23, SMS_ST_TEMPORARY_QOS_UNAVAILABLE = 0x24, SMS_ST_TEMPORARY_SME_ERROR = 0x25, SMS_ST_TEMPORARY_LAST = 0x2F, SMS_ST_PERMANENT_RP_ERROR = 0x40, SMS_ST_PERMANENT_INVALID_DESTINATION = 0x41, SMS_ST_PERMANENT_CONNECTION_REJECTED = 0x42, SMS_ST_PERMANENT_NOT_OBTAINABLE = 0x43, SMS_ST_PERMANENT_QOS_UNAVAILABLE = 0x44, SMS_ST_PERMANENT_INTERWORKING_UNAVAILABLE = 0x45, SMS_ST_PERMANENT_VALIDITY_PERIOD_EXPIRED = 0x46, SMS_ST_PERMANENT_DELETED = 0x47, SMS_ST_PERMANENT_SC_ADMIN_DELETED = 0x48, SMS_ST_PERMANENT_SM_DOES_NOT_EXIST = 0x49, SMS_ST_PERMANENT_LAST = 0x4F, SMS_ST_TEMPFINAL_CONGESTION = 0x60, SMS_ST_TEMPFINAL_SME_BUSY = 0x61, SMS_ST_TEMPFINAL_NO_RESPONSE = 0x62, SMS_ST_TEMPFINAL_SERVICE_REJECTED = 0x63, SMS_ST_TEMPFINAL_QOS_UNAVAILABLE = 0x64, SMS_ST_TEMPFINAL_SME_ERROR = 0x65, SMS_ST_TEMPFINAL_LAST = 0x6F, }; enum sms_ct { SMS_CT_ENQUIRY = 0, SMS_CT_CANCEL_SRR = 1, SMS_CT_DELETE_SM = 2, SMS_CT_ENABLE_SRR = 3 }; enum sms_iei { SMS_IEI_CONCATENATED_8BIT = 0x00, SMS_IEI_SPECIAL_MESSAGE_INDICATION = 0x01, SMS_IEI_APPLICATION_ADDRESS_8BIT = 0x04, SMS_IEI_APPLICATION_ADDRESS_16BIT = 0x05, SMS_IEI_SMSC_CONTROL_PARAMETERS = 0x06, SMS_IEI_UDH_SOURCE_INDICATOR = 0x07, SMS_IEI_CONCATENATED_16BIT = 0x08, SMS_IEI_WCMP = 0x09, SMS_IEI_TEXT_FORMAT = 0x0A, SMS_IEI_PREDEFINED_SOUND = 0x0B, SMS_IEI_USER_DEFINED_SOUND = 0x0C, SMS_IEI_PREDEFINED_ANIMATION = 0x0D, SMS_IEI_LARGE_ANIMATION = 0x0E, SMS_IEI_SMALL_ANIMATION = 0x0F, SMS_IEI_LARGE_PICTURE = 0x10, SMS_IEI_SMALL_PICTURE = 0x11, SMS_IEI_VARIABLE_PICTURE = 0x12, SMS_IEI_USER_PROMPT_INDICATOR = 0x13, SMS_IEI_EXTENDED_OBJECT = 0x14, SMS_IEI_REUSED_EXTENDED_OBJECT = 0x15, SMS_IEI_COMPRESSION_CONTROL = 0x16, SMS_IEI_OBJECT_DISTRIBUTION_INDICATOR = 0x17, SMS_IEI_STANDARD_WVG_OBJECT = 0x18, SMS_IEI_CHARACTER_SIZE_WVG_OBJECT = 0x19, SMS_IEI_EXTENDED_OBJECT_DATA_REQUEST_COMMAND = 0x1A, SMS_IEI_RFC822_EMAIL_HEADER = 0x20, SMS_IEI_HYPERLINK_ELEMENT = 0x21, SMS_IEI_REPLY_ADDRESS_ELEMENT = 0x22, SMS_IEI_ENHANCED_VOICE_MAIL_INFORMATION = 0x23, SMS_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT = 0x24, SMS_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT = 0x25, SMS_IEI_INVALID = 0xFFF }; enum sms_class { SMS_CLASS_0 = 0, SMS_CLASS_1 = 1, SMS_CLASS_2 = 2, SMS_CLASS_3 = 3, SMS_CLASS_UNSPECIFIED = 4, }; enum sms_charset { SMS_CHARSET_7BIT = 0, SMS_CHARSET_8BIT = 1, SMS_CHARSET_UCS2 = 2, }; enum sms_alphabet { SMS_ALPHABET_DEFAULT = 0, SMS_ALPHABET_TURKISH, SMS_ALPHABET_SPANISH, SMS_ALPHABET_PORTUGUESE, }; enum sms_mwi_type { SMS_MWI_TYPE_VOICE = 0, SMS_MWI_TYPE_FAX = 1, SMS_MWI_TYPE_EMAIL = 2, SMS_MWI_TYPE_OTHER = 3, SMS_MWI_TYPE_VIDEO = 4, }; enum sms_pid_type { SMS_PID_TYPE_SM_TYPE_0 = 0x40, SMS_PID_TYPE_REPLACE_SM_TYPE_1 = 0x41, SMS_PID_TYPE_REPLACE_SM_TYPE_2 = 0x42, SMS_PID_TYPE_REPLACE_SM_TYPE_3 = 0x43, SMS_PID_TYPE_REPLACE_SM_TYPE_4 = 0x44, SMS_PID_TYPE_REPLACE_SM_TYPE_5 = 0x45, SMS_PID_TYPE_REPLACE_SM_TYPE_6 = 0x46, SMS_PID_TYPE_REPLACE_SM_TYPE_7 = 0x47, SMS_PID_TYPE_ENHANCED_MESSAGE_SERVICE = 0x5e, SMS_PID_TYPE_RETURN_CALL = 0x5f, SMS_PID_TYPE_ANSI136 = 0x7c, SMS_PID_TYPE_ME_DOWNLOAD = 0x7d, SMS_PID_TYPE_ME_DEPERSONALIZATION = 0x7e, SMS_PID_TYPE_USIM_DOWNLOAD = 0x7f, }; enum cbs_language { CBS_LANGUAGE_GERMAN = 0x0, CBS_LANGUAGE_ENGLISH = 0x1, CBS_LANGUAGE_ITALIAN = 0x2, CBS_LANGUAGE_FRENCH = 0x3, CBS_LANGUAGE_SPANISH = 0x4, CBS_LANGUAGE_DUTCH = 0x5, CBS_LANGUAGE_SWEDISH = 0x6, CBS_LANGUAGE_DANISH = 0x7, CBS_LANGUAGE_PORTUGESE = 0x8, CBS_LANGUAGE_FINNISH = 0x9, CBS_LANGUAGE_NORWEGIAN = 0xA, CBS_LANGUAGE_GREEK = 0xB, CBS_LANGUAGE_TURKISH = 0xC, CBS_LANGUAGE_HUNGARIAN = 0xD, CBS_LANGUAGE_POLISH = 0xE, CBS_LANGUAGE_UNSPECIFIED = 0xF, CBS_LANGUAGE_CZECH = 0x20, CBS_LANGUAGE_HEBREW = 0x21, CBS_LANGUAGE_ARABIC = 0x22, CBS_LANGUAGE_RUSSIAN = 0x23, CBS_LANGUAGE_ICELANDIC = 0x24 }; enum cbs_geo_scope { CBS_GEO_SCOPE_CELL_IMMEDIATE, CBS_GEO_SCOPE_PLMN, CBS_GEO_SCOPE_SERVICE_AREA, CBS_GEO_SCOPE_CELL_NORMAL }; struct sms_address { enum sms_number_type number_type; enum sms_numbering_plan numbering_plan; /* * An alphanum TP-OA is 10 7-bit coded octets, which can carry * 11 8-bit characters. 22 bytes + terminator in UTF-8. */ char address[23]; }; struct sms_scts { guint8 year; guint8 month; guint8 day; guint8 hour; guint8 minute; guint8 second; gboolean has_timezone; gint8 timezone; }; struct sms_validity_period { union { guint8 relative; struct sms_scts absolute; guint8 enhanced[7]; }; }; struct sms_deliver { gboolean mms; gboolean sri; gboolean udhi; gboolean rp; struct sms_address oaddr; guint8 pid; guint8 dcs; struct sms_scts scts; guint8 udl; guint8 ud[140]; }; struct sms_deliver_err_report { gboolean udhi; guint8 fcs; guint8 pi; guint8 pid; guint8 dcs; guint8 udl; guint8 ud[158]; }; struct sms_deliver_ack_report { gboolean udhi; guint8 pi; guint8 pid; guint8 dcs; guint8 udl; guint8 ud[159]; }; struct sms_command { gboolean udhi; gboolean srr; guint8 mr; guint8 pid; enum sms_ct ct; guint8 mn; struct sms_address daddr; guint8 cdl; guint8 cd[156]; }; struct sms_status_report { gboolean udhi; gboolean mms; gboolean srq; guint8 mr; struct sms_address raddr; struct sms_scts scts; struct sms_scts dt; enum sms_st st; guint8 pi; guint8 pid; guint8 dcs; guint8 udl; guint8 ud[143]; }; struct sms_submit { gboolean rd; enum sms_validity_period_format vpf; gboolean rp; gboolean udhi; gboolean srr; guint8 mr; struct sms_address daddr; guint8 pid; guint8 dcs; struct sms_validity_period vp; guint8 udl; guint8 ud[140]; }; struct sms_submit_ack_report { gboolean udhi; guint8 pi; struct sms_scts scts; guint8 pid; guint8 dcs; guint8 udl; guint8 ud[152]; }; struct sms_submit_err_report { gboolean udhi; guint8 fcs; guint8 pi; struct sms_scts scts; guint8 pid; guint8 dcs; guint8 udl; guint8 ud[151]; }; struct sms { struct sms_address sc_addr; enum sms_type type; union { struct sms_deliver deliver; struct sms_deliver_ack_report deliver_ack_report; struct sms_deliver_err_report deliver_err_report; struct sms_submit submit; struct sms_submit_ack_report submit_ack_report; struct sms_submit_err_report submit_err_report; struct sms_command command; struct sms_status_report status_report; }; }; struct sms_udh_iter { const guint8 *data; guint8 offset; }; struct sms_assembly_node { struct sms_address addr; time_t ts; GSList *fragment_list; guint16 ref; guint8 max_fragments; guint8 num_fragments; unsigned int bitmap[8]; }; struct sms_assembly { const char *imsi; GSList *assembly_list; }; struct id_table_node { unsigned int mrs[8]; time_t expiration; unsigned char total_mrs; unsigned char sent_mrs; gboolean deliverable; } __attribute__((packed)); struct status_report_assembly { const char *imsi; GHashTable *assembly_table; }; struct cbs { enum cbs_geo_scope gs; /* 2 bits */ guint16 message_code; /* 10 bits */ guint8 update_number; /* 4 bits */ guint16 message_identifier; /* 16 bits */ guint8 dcs; /* 8 bits */ guint8 max_pages; /* 4 bits */ guint8 page; /* 4 bits */ guint8 ud[82]; }; struct cbs_assembly_node { guint32 serial; guint16 bitmap; GSList *pages; }; struct cbs_assembly { GSList *assembly_list; GSList *recv_plmn; GSList *recv_loc; GSList *recv_cell; }; struct cbs_topic_range { unsigned short min; unsigned short max; }; struct txq_backup_entry { GSList *msg_list; unsigned char uuid[SMS_MSGID_LEN]; unsigned long flags; }; static inline gboolean is_bit_set(unsigned char oct, int bit) { int mask = 1 << bit; return oct & mask ? TRUE : FALSE; } static inline unsigned char bit_field(unsigned char oct, int start, int num) { unsigned char mask = (1 << num) - 1; return (oct >> start) & mask; } void extract_bcd_number(const unsigned char *buf, int len, char *out); void encode_bcd_number(const char *number, unsigned char *out); gboolean sms_decode(const unsigned char *pdu, int len, gboolean outgoing, int tpdu_len, struct sms *out); gboolean sms_decode_unpacked_stk_pdu(const unsigned char *pdu, int len, struct sms *out); gboolean sms_encode(const struct sms *in, int *len, int *tpdu_len, unsigned char *pdu); /* * Length is based on the address being 12 hex characters plus a * terminating NUL char. See sms_assembly_extract_address(). */ #define DECLARE_SMS_ADDR_STR(a) char a[25] gboolean sms_decode_address_field(const unsigned char *pdu, int len, int *offset, gboolean sc, struct sms_address *out); gboolean sms_encode_address_field(const struct sms_address *in, gboolean sc, unsigned char *pdu, int *offset); guint8 sms_decode_semi_octet(guint8 in); gboolean sms_decode_scts(const unsigned char *pdu, int len, int *offset, struct sms_scts *out); gboolean sms_encode_scts(const struct sms_scts *in, unsigned char *pdu, int *offset); int sms_udl_in_bytes(guint8 ud_len, guint8 dcs); time_t sms_scts_to_time(const struct sms_scts *scts, struct tm *remote); const char *sms_address_to_string(const struct sms_address *addr); void sms_address_from_string(struct sms_address *addr, const char *str); const guint8 *sms_extract_common(const struct sms *sms, gboolean *out_udhi, guint8 *out_dcs, guint8 *out_udl, guint8 *out_max); gboolean sms_udh_iter_init(const struct sms *sms, struct sms_udh_iter *iter); gboolean sms_udh_iter_init_from_cbs(const struct cbs *cbs, struct sms_udh_iter *iter); guint8 sms_udh_iter_get_udh_length(struct sms_udh_iter *iter); const guint8 *sms_udh_iter_get_ud_after_header(struct sms_udh_iter *iter); enum sms_iei sms_udh_iter_get_ie_type(struct sms_udh_iter *iter); guint8 sms_udh_iter_get_ie_length(struct sms_udh_iter *iter); void sms_udh_iter_get_ie_data(struct sms_udh_iter *iter, guint8 *data); gboolean sms_udh_iter_has_next(struct sms_udh_iter *iter); gboolean sms_udh_iter_next(struct sms_udh_iter *iter); gboolean sms_dcs_decode(guint8 dcs, enum sms_class *cls, enum sms_charset *charset, gboolean *compressed, gboolean *autodelete); gboolean sms_mwi_dcs_decode(guint8 dcs, enum sms_mwi_type *type, enum sms_charset *charset, gboolean *active, gboolean *discard); gboolean sms_extract_app_port(const struct sms *sms, int *dst, int *src, gboolean *is_8bit); gboolean sms_extract_concatenation(const struct sms *sms, guint16 *ref_num, guint8 *max_msgs, guint8 *seq_num); gboolean sms_extract_language_variant(const struct sms *sms, guint8 *locking, guint8 *single); unsigned char *sms_decode_datagram(GSList *sms_list, long *out_len); char *sms_decode_text(GSList *sms_list); struct sms_assembly *sms_assembly_new(const char *imsi); void sms_assembly_free(struct sms_assembly *assembly); GSList *sms_assembly_add_fragment(struct sms_assembly *assembly, const struct sms *sms, time_t ts, const struct sms_address *addr, guint16 ref, guint8 max, guint8 seq); void sms_assembly_expire(struct sms_assembly *assembly, time_t before); gboolean sms_address_to_hex_string(const struct sms_address *in, char *straddr); struct status_report_assembly *status_report_assembly_new(const char *imsi); void status_report_assembly_free(struct status_report_assembly *assembly); gboolean status_report_assembly_report(struct status_report_assembly *assembly, const struct sms *status_report, unsigned char *out_msgid, gboolean *msg_delivered); void status_report_assembly_add_fragment(struct status_report_assembly *assembly, const unsigned char *msgid, const struct sms_address *to, unsigned char mr, time_t expiration, unsigned char total_mrs); void status_report_assembly_expire(struct status_report_assembly *assembly, time_t before); gboolean sms_tx_backup_store(const char *imsi, unsigned long id, unsigned long flags, const char *uuid, guint8 seq, const unsigned char *pdu, int pdu_len, int tpdu_len); void sms_tx_backup_remove(const char *imsi, unsigned long id, unsigned long flags, const char *uuid, guint8 seq); void sms_tx_backup_free(const char *imsi, unsigned long id, unsigned long flags, const char *uuid); GQueue *sms_tx_queue_load(const char *imsi); GSList *sms_text_prepare(const char *to, const char *utf8, guint16 ref, gboolean use_16bit, gboolean use_delivery_reports); GSList *sms_text_prepare_with_alphabet(const char *to, const char *utf8, guint16 ref, gboolean use_16bit, gboolean use_delivery_reports, enum sms_alphabet alphabet); GSList *sms_datagram_prepare(const char *to, const unsigned char *data, unsigned int len, guint16 ref, gboolean use_16bit_ref, unsigned short src, unsigned short dst, gboolean use_16bit_port, gboolean use_delivery_reports); gboolean cbs_dcs_decode(guint8 dcs, gboolean *udhi, enum sms_class *cls, enum sms_charset *charset, gboolean *compressed, enum cbs_language *language, gboolean *iso639); gboolean iso639_2_from_language(enum cbs_language lang, char *iso639); gboolean cbs_decode(const unsigned char *pdu, int len, struct cbs *out); gboolean cbs_encode(const struct cbs *cbs, int *len, unsigned char *pdu); gboolean cbs_extract_app_port(const struct cbs *cbs, int *dst, int *src, gboolean *is_8bit); char *cbs_decode_text(GSList *cbs_list, char *iso639_lang); struct cbs_assembly *cbs_assembly_new(void); void cbs_assembly_free(struct cbs_assembly *assembly); GSList *cbs_assembly_add_page(struct cbs_assembly *assembly, const struct cbs *cbs); void cbs_assembly_location_changed(struct cbs_assembly *assembly, gboolean plmn, gboolean lac, gboolean ci); char *cbs_topic_ranges_to_string(GSList *ranges); GSList *cbs_extract_topic_ranges(const char *ranges); GSList *cbs_optimize_ranges(GSList *ranges); gboolean cbs_topic_in_range(unsigned int topic, GSList *ranges); char *ussd_decode(int dcs, int len, const unsigned char *data); gboolean ussd_encode(const char *str, long *items_written, unsigned char *pdu); gboolean ussd_dcs_encode(const char *str, int *dcs, long *items_written, unsigned char *pdu); ofono-1.17.bzr6912+16.04.20160314.3/src/cdma-voicecall.c0000644000015600001650000003355112671500024022204 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" static GSList *g_drivers; struct ofono_cdma_voicecall { struct ofono_cdma_phone_number phone_number; struct ofono_cdma_phone_number waiting_number; int direction; enum cdma_call_status status; time_t start_time; DBusMessage *pending; const struct ofono_cdma_voicecall_driver *driver; void *driver_data; struct ofono_atom *atom; }; static const char *disconnect_reason_to_string(enum ofono_disconnect_reason r) { switch (r) { case OFONO_DISCONNECT_REASON_LOCAL_HANGUP: return "local"; case OFONO_DISCONNECT_REASON_REMOTE_HANGUP: return "remote"; default: return "network"; } } static const char *cdma_call_status_to_string(enum cdma_call_status status) { switch (status) { case CDMA_CALL_STATUS_ACTIVE: return "active"; case CDMA_CALL_STATUS_DIALING: return "dialing"; case CDMA_CALL_STATUS_ALERTING: return "alerting"; case CDMA_CALL_STATUS_INCOMING: return "incoming"; case CDMA_CALL_STATUS_DISCONNECTED: return "disconnected"; } return NULL; } static const char *time_to_str(const time_t *t) { static char buf[128]; struct tm tm; strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime_r(t, &tm)); buf[127] = '\0'; return buf; } static void generic_callback(const struct ofono_error *error, void *data) { struct ofono_cdma_voicecall *vc = data; DBusMessage *reply; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) reply = dbus_message_new_method_return(vc->pending); else reply = __ofono_error_failed(vc->pending); __ofono_dbus_pending_reply(&vc->pending, reply); } static void append_voicecall_properties(struct ofono_cdma_voicecall *vc, DBusMessageIter *dict) { const char *status; const char *lineid; const char *waiting_call; dbus_bool_t call_waiting = FALSE; status = cdma_call_status_to_string(vc->status); ofono_dbus_dict_append(dict, "State", DBUS_TYPE_STRING, &status); lineid = cdma_phone_number_to_string(&vc->phone_number); ofono_dbus_dict_append(dict, "LineIdentification", DBUS_TYPE_STRING, &lineid); if (vc->waiting_number.number[0] != '\0') { waiting_call = cdma_phone_number_to_string(&vc->waiting_number); ofono_dbus_dict_append(dict, "CallWaitingNumber", DBUS_TYPE_STRING, &waiting_call); call_waiting = TRUE; } ofono_dbus_dict_append(dict, "CallWaiting", DBUS_TYPE_BOOLEAN, &call_waiting); if (vc->status == CDMA_CALL_STATUS_ACTIVE) { const char *timestr = time_to_str(&vc->start_time); ofono_dbus_dict_append(dict, "StartTime", DBUS_TYPE_STRING, ×tr); } } static DBusMessage *voicecall_manager_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cdma_voicecall *vc = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); append_voicecall_properties(vc, &dict); dbus_message_iter_close_container(&iter, &dict); return reply; } static void voicecall_emit_disconnect_reason(struct ofono_cdma_voicecall *vc, enum ofono_disconnect_reason reason) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(vc->atom); const char *reason_str; reason_str = disconnect_reason_to_string(reason); g_dbus_emit_signal(conn, path, OFONO_CDMA_VOICECALL_MANAGER_INTERFACE, "DisconnectReason", DBUS_TYPE_STRING, &reason_str, DBUS_TYPE_INVALID); } static void voicecall_set_call_status(struct ofono_cdma_voicecall *vc, enum cdma_call_status status) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(vc->atom); const char *status_str; enum cdma_call_status old_status; DBG("status: %s", cdma_call_status_to_string(status)); if (vc->status == status) return; old_status = vc->status; vc->status = status; status_str = cdma_call_status_to_string(status); ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_VOICECALL_MANAGER_INTERFACE, "State", DBUS_TYPE_STRING, &status_str); if (status == CDMA_CALL_STATUS_ACTIVE && old_status == CDMA_CALL_STATUS_DIALING) { const char *timestr; vc->start_time = time(NULL); timestr = time_to_str(&vc->start_time); ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_VOICECALL_MANAGER_INTERFACE, "StartTime", DBUS_TYPE_STRING, ×tr); } /* TODO: Properly signal property changes here */ if (status == CDMA_CALL_STATUS_DISCONNECTED) { memset(&vc->phone_number, 0, sizeof(struct ofono_cdma_phone_number)); memset(&vc->waiting_number, 0, sizeof(struct ofono_cdma_phone_number)); } } static void voicecall_set_call_lineid(struct ofono_cdma_voicecall *vc) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(vc->atom); const char *lineid_str; /* For MO calls, LineID is the dialed phone number */ lineid_str = cdma_phone_number_to_string(&vc->phone_number); ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_VOICECALL_MANAGER_INTERFACE, "LineIdentification", DBUS_TYPE_STRING, &lineid_str); } static void manager_dial_callback(const struct ofono_error *error, void *data) { struct ofono_cdma_voicecall *vc = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { reply = __ofono_error_failed(vc->pending); __ofono_dbus_pending_reply(&vc->pending, reply); return; } voicecall_set_call_lineid(vc); vc->direction = CALL_DIRECTION_MOBILE_ORIGINATED; voicecall_set_call_status(vc, CDMA_CALL_STATUS_DIALING); reply = dbus_message_new_method_return(vc->pending); __ofono_dbus_pending_reply(&vc->pending, reply); } static DBusMessage *voicecall_manager_dial(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cdma_voicecall *vc = data; const char *number; if (vc->pending) return __ofono_error_busy(msg); if (vc->status != CDMA_CALL_STATUS_DISCONNECTED) return __ofono_error_failed(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!valid_cdma_phone_number_format(number)) return __ofono_error_invalid_format(msg); if (vc->driver->dial == NULL) return __ofono_error_not_implemented(msg); vc->pending = dbus_message_ref(msg); string_to_cdma_phone_number(number, &vc->phone_number); vc->driver->dial(vc, &vc->phone_number, manager_dial_callback, vc); return NULL; } static DBusMessage *voicecall_manager_hangup(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cdma_voicecall *vc = data; if (vc->pending) return __ofono_error_busy(msg); if (vc->driver->hangup == NULL) return __ofono_error_not_implemented(msg); if (vc->status == CDMA_CALL_STATUS_DISCONNECTED) return __ofono_error_failed(msg); vc->pending = dbus_message_ref(msg); vc->driver->hangup(vc, generic_callback, vc); return NULL; } static DBusMessage *voicecall_manager_answer(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cdma_voicecall *vc = data; if (vc->pending) return __ofono_error_busy(msg); if (vc->driver->answer == NULL) return __ofono_error_not_implemented(msg); if (vc->status != CDMA_CALL_STATUS_INCOMING) return __ofono_error_failed(msg); vc->pending = dbus_message_ref(msg); vc->driver->answer(vc, generic_callback, vc); return NULL; } static DBusMessage *voicecall_manager_flash(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cdma_voicecall *vc = data; const char *string; if (vc->pending) return __ofono_error_busy(msg); if (vc->driver->send_flash == NULL) return __ofono_error_not_implemented(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &string, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); vc->pending = dbus_message_ref(msg); vc->driver->send_flash(vc, string, generic_callback, vc); return NULL; } static ofono_bool_t is_valid_tones(const char *tones) { int len; int i; if (tones == NULL) return FALSE; len = strlen(tones); if (len == 0) return FALSE; for (i = 0; i < len; i++) { if (g_ascii_isdigit(tones[i]) || tones[i] == '*' || tones[i] == '#') continue; else return FALSE; } return TRUE; } static DBusMessage *voicecall_manager_tone(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cdma_voicecall *vc = data; const char *tones; if (vc->pending) return __ofono_error_busy(msg); if (vc->driver->send_tones == NULL) return __ofono_error_not_implemented(msg); if (vc->status != CDMA_CALL_STATUS_ACTIVE) return __ofono_error_failed(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &tones, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (is_valid_tones(tones) == FALSE) return __ofono_error_invalid_args(msg); vc->pending = dbus_message_ref(msg); vc->driver->send_tones(vc, tones, generic_callback, vc); return NULL; } static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), voicecall_manager_get_properties) }, { GDBUS_ASYNC_METHOD("Dial", GDBUS_ARGS({ "number", "s" }), NULL, voicecall_manager_dial) }, { GDBUS_ASYNC_METHOD("Hangup", NULL, NULL, voicecall_manager_hangup) }, { GDBUS_ASYNC_METHOD("Answer", NULL, NULL, voicecall_manager_answer) }, { GDBUS_ASYNC_METHOD("SendFlash", GDBUS_ARGS({ "flash_string", "s" }), NULL, voicecall_manager_flash) }, { GDBUS_ASYNC_METHOD("SendTones", GDBUS_ARGS({ "tones", "s" }), NULL, voicecall_manager_tone) }, { } }; static const GDBusSignalTable manager_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { GDBUS_SIGNAL("DisconnectReason", GDBUS_ARGS({ "reason", "s" })) }, { } }; void ofono_cdma_voicecall_disconnected(struct ofono_cdma_voicecall *vc, enum ofono_disconnect_reason reason, const struct ofono_error *error) { DBG("Got disconnection event for reason: %d", reason); if (reason != OFONO_DISCONNECT_REASON_UNKNOWN) voicecall_emit_disconnect_reason(vc, reason); voicecall_set_call_status(vc, CDMA_CALL_STATUS_DISCONNECTED); } int ofono_cdma_voicecall_driver_register( const struct ofono_cdma_voicecall_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *)d); return 0; } void ofono_cdma_voicecall_driver_unregister( const struct ofono_cdma_voicecall_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *)d); } static void cdma_voicecall_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); g_dbus_unregister_interface(conn, path, OFONO_CDMA_VOICECALL_MANAGER_INTERFACE); ofono_modem_remove_interface(modem, OFONO_CDMA_VOICECALL_MANAGER_INTERFACE); } static void voicecall_manager_remove(struct ofono_atom *atom) { struct ofono_cdma_voicecall *vc = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (vc == NULL) return; if (vc->driver && vc->driver->remove) vc->driver->remove(vc); g_free(vc); } struct ofono_cdma_voicecall *ofono_cdma_voicecall_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_cdma_voicecall *vc; GSList *l; if (driver == NULL) return NULL; vc = g_try_new0(struct ofono_cdma_voicecall, 1); if (vc == NULL) return NULL; vc->status = CDMA_CALL_STATUS_DISCONNECTED; vc->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CDMA_VOICECALL_MANAGER, voicecall_manager_remove, vc); for (l = g_drivers; l; l = l->next) { const struct ofono_cdma_voicecall_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(vc, vendor, data) < 0) continue; vc->driver = drv; break; } return vc; } void ofono_cdma_voicecall_register(struct ofono_cdma_voicecall *vc) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); const char *path = __ofono_atom_get_path(vc->atom); if (!g_dbus_register_interface(conn, path, OFONO_CDMA_VOICECALL_MANAGER_INTERFACE, manager_methods, manager_signals, NULL, vc, NULL)) { ofono_error("Could not create %s interface", OFONO_CDMA_VOICECALL_MANAGER_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_CDMA_VOICECALL_MANAGER_INTERFACE); __ofono_atom_register(vc->atom, cdma_voicecall_unregister); } void ofono_cdma_voicecall_remove(struct ofono_cdma_voicecall *vc) { __ofono_atom_free(vc->atom); } void ofono_cdma_voicecall_set_data(struct ofono_cdma_voicecall *vc, void *data) { vc->driver_data = data; } void *ofono_cdma_voicecall_get_data(struct ofono_cdma_voicecall *vc) { return vc->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/phonebook.c0000644000015600001650000003354612671500024021331 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" #define LEN_MAX 128 #define TYPE_INTERNATIONAL 145 #define PHONEBOOK_FLAG_CACHED 0x1 static GSList *g_drivers = NULL; enum phonebook_number_type { TEL_TYPE_HOME, TEL_TYPE_MOBILE, TEL_TYPE_FAX, TEL_TYPE_WORK, TEL_TYPE_OTHER, }; struct ofono_phonebook { DBusMessage *pending; int storage_index; /* go through all supported storage */ int flags; GString *vcards; /* entries with vcard 3.0 format */ GSList *merge_list; /* cache the entries that may need a merge */ const struct ofono_phonebook_driver *driver; void *driver_data; struct ofono_atom *atom; }; struct phonebook_number { char *number; int type; enum phonebook_number_type category; }; struct phonebook_person { GSList *number_list; /* one person may have more than one numbers */ char *text; int hidden; char *group; char *email; char *sip_uri; }; static const char *storage_support[] = { "SM", "ME", NULL }; static void export_phonebook(struct ofono_phonebook *pb); /* according to RFC 2425, the output string may need folding */ static void vcard_printf(GString *str, const char *fmt, ...) { char buf[1024]; va_list ap; int len_temp, line_number, i; unsigned int line_delimit = 75; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); line_number = strlen(buf) / line_delimit + 1; for (i = 0; i < line_number; i++) { len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i); g_string_append_len(str, buf + line_delimit * i, len_temp); if (i != line_number - 1) g_string_append(str, "\r\n "); } g_string_append(str, "\r\n"); } /* * According to RFC 2426, we need escape following characters: * '\n', '\r', ';', ',', '\'. */ static void add_slash(char *dest, const char *src, int len_max, int len) { int i, j; for (i = 0, j = 0; i < len && j < len_max; i++, j++) { switch (src[i]) { case '\n': dest[j++] = '\\'; dest[j] = 'n'; break; case '\r': dest[j++] = '\\'; dest[j] = 'r'; break; case '\\': case ';': case ',': dest[j++] = '\\'; default: dest[j] = src[i]; break; } } dest[j] = 0; return; } static void vcard_printf_begin(GString *vcards) { vcard_printf(vcards, "BEGIN:VCARD"); vcard_printf(vcards, "VERSION:3.0"); } static void vcard_printf_text(GString *vcards, const char *text) { char field[LEN_MAX]; add_slash(field, text, LEN_MAX, strlen(text)); vcard_printf(vcards, "FN:%s", field); } static void vcard_printf_number(GString *vcards, const char *number, int type, enum phonebook_number_type category) { char *pref = "", *intl = "", *category_string = ""; char buf[128]; if (number == NULL || !strlen(number) || !type) return; switch (category) { case TEL_TYPE_HOME: category_string = "HOME,VOICE"; break; case TEL_TYPE_MOBILE: category_string = "CELL,VOICE"; break; case TEL_TYPE_FAX: category_string = "FAX"; break; case TEL_TYPE_WORK: category_string = "WORK,VOICE"; break; case TEL_TYPE_OTHER: category_string = "VOICE"; break; } if ((type == TYPE_INTERNATIONAL) && (number[0] != '+')) intl = "+"; snprintf(buf, sizeof(buf), "TEL;TYPE=\%s%s:\%s\%s", pref, category_string, intl, number); vcard_printf(vcards, buf, number); } static void vcard_printf_group(GString *vcards, const char *group) { int len = 0; if (group) len = strlen(group); if (len) { char field[LEN_MAX]; add_slash(field, group, LEN_MAX, len); vcard_printf(vcards, "CATEGORIES:%s", field); } } static void vcard_printf_email(GString *vcards, const char *email) { int len = 0; if (email) len = strlen(email); if (len) { char field[LEN_MAX]; add_slash(field, email, LEN_MAX, len); vcard_printf(vcards, "EMAIL;TYPE=INTERNET:%s", field); } } static void vcard_printf_sip_uri(GString *vcards, const char *sip_uri) { int len = 0; if (sip_uri) len = strlen(sip_uri); if (len) { char field[LEN_MAX]; add_slash(field, sip_uri, LEN_MAX, len); vcard_printf(vcards, "IMPP;TYPE=SIP:%s", field); } } static void vcard_printf_end(GString *vcards) { vcard_printf(vcards, "END:VCARD"); vcard_printf(vcards, ""); } static void print_number(struct phonebook_number *pn, GString *vcards) { vcard_printf_number(vcards, pn->number, pn->type, pn->category); } static void destroy_number(struct phonebook_number *pn) { g_free(pn->number); g_free(pn); } static void print_merged_entry(struct phonebook_person *person, GString *vcards) { vcard_printf_begin(vcards); vcard_printf_text(vcards, person->text); g_slist_foreach(person->number_list, (GFunc) print_number, vcards); vcard_printf_group(vcards, person->group); vcard_printf_email(vcards, person->email); vcard_printf_sip_uri(vcards, person->sip_uri); vcard_printf_end(vcards); } static void destroy_merged_entry(struct phonebook_person *person) { g_free(person->text); g_free(person->group); g_free(person->email); g_free(person->sip_uri); g_slist_foreach(person->number_list, (GFunc) destroy_number, NULL); g_slist_free(person->number_list); g_free(person); } static DBusMessage *generate_export_entries_reply(struct ofono_phonebook *pb, DBusMessage *msg) { DBusMessage *reply; DBusMessageIter iter; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, pb->vcards); return reply; } static gboolean need_merge(const char *text) { int len; char c; if (text == NULL) return FALSE; len = strlen(text); if (len < 2) return FALSE; c = tolower(text[len-1]); if ((text[len-2] == '/') && ((c == 'w') || (c == 'h') || (c == 'm') || (c == 'o'))) return TRUE; return FALSE; } static void merge_field_generic(char **str1, const char *str2) { if ((*str1 == NULL) && (str2 != NULL) && (strlen(str2) != 0)) *str1 = g_strdup(str2); } static void merge_field_number(GSList **l, const char *number, int type, char c) { struct phonebook_number *pn = g_new0(struct phonebook_number, 1); enum phonebook_number_type category; pn->number = g_strdup(number); pn->type = type; switch (tolower(c)) { case 'w': category = TEL_TYPE_WORK; break; case 'h': category = TEL_TYPE_HOME; break; case 'm': category = TEL_TYPE_MOBILE; break; case 'f': category = TEL_TYPE_FAX; break; case 'o': default: category = TEL_TYPE_OTHER; break; } pn->category = category; *l = g_slist_append(*l, pn); } void ofono_phonebook_entry(struct ofono_phonebook *phonebook, int index, const char *number, int type, const char *text, int hidden, const char *group, const char *adnumber, int adtype, const char *secondtext, const char *email, const char *sip_uri, const char *tel_uri) { /* There's really nothing to do */ if ((number == NULL || number[0] == '\0') && (text == NULL || text[0] == '\0')) return; /* * We need to collect all the entries that belong to one person, * so that only one vCard will be generated at last. * Entries only differ with '/w', '/h', '/m', etc. in field text * are deemed as entries of one person. */ if (need_merge(text)) { GSList *l; size_t len_text = strlen(text) - 2; struct phonebook_person *person; for (l = phonebook->merge_list; l; l = l->next) { person = l->data; if (!strncmp(text, person->text, len_text) && (strlen(person->text) == len_text)) break; } if (l == NULL) { person = g_new0(struct phonebook_person, 1); phonebook->merge_list = g_slist_prepend(phonebook->merge_list, person); person->text = g_strndup(text, len_text); } merge_field_number(&(person->number_list), number, type, text[len_text + 1]); merge_field_number(&(person->number_list), adnumber, adtype, text[len_text + 1]); merge_field_generic(&(person->group), group); merge_field_generic(&(person->email), email); merge_field_generic(&(person->sip_uri), sip_uri); return; } vcard_printf_begin(phonebook->vcards); if (text == NULL || text[0] == '\0') vcard_printf_text(phonebook->vcards, number); else vcard_printf_text(phonebook->vcards, text); vcard_printf_number(phonebook->vcards, number, type, TEL_TYPE_OTHER); vcard_printf_number(phonebook->vcards, adnumber, adtype, TEL_TYPE_OTHER); vcard_printf_group(phonebook->vcards, group); vcard_printf_email(phonebook->vcards, email); vcard_printf_sip_uri(phonebook->vcards, sip_uri); vcard_printf_end(phonebook->vcards); } static void export_phonebook_cb(const struct ofono_error *error, void *data) { struct ofono_phonebook *phonebook = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) ofono_error("export_entries_one_storage_cb with %s failed", storage_support[phonebook->storage_index]); /* convert the collected entries that are already merged to vcard */ phonebook->merge_list = g_slist_reverse(phonebook->merge_list); g_slist_foreach(phonebook->merge_list, (GFunc) print_merged_entry, phonebook->vcards); g_slist_foreach(phonebook->merge_list, (GFunc) destroy_merged_entry, NULL); g_slist_free(phonebook->merge_list); phonebook->merge_list = NULL; phonebook->storage_index++; export_phonebook(phonebook); return; } static void export_phonebook(struct ofono_phonebook *phonebook) { DBusMessage *reply; const char *pb = storage_support[phonebook->storage_index]; if (pb) { phonebook->driver->export_entries(phonebook, pb, export_phonebook_cb, phonebook); return; } reply = generate_export_entries_reply(phonebook, phonebook->pending); if (reply == NULL) { dbus_message_unref(phonebook->pending); return; } __ofono_dbus_pending_reply(&phonebook->pending, reply); phonebook->flags |= PHONEBOOK_FLAG_CACHED; } static DBusMessage *import_entries(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_phonebook *phonebook = data; DBusMessage *reply; if (phonebook->pending) { reply = __ofono_error_busy(phonebook->pending); g_dbus_send_message(conn, reply); return NULL; } if (phonebook->flags & PHONEBOOK_FLAG_CACHED) { reply = generate_export_entries_reply(phonebook, msg); g_dbus_send_message(conn, reply); return NULL; } g_string_set_size(phonebook->vcards, 0); phonebook->storage_index = 0; phonebook->pending = dbus_message_ref(msg); export_phonebook(phonebook); return NULL; } static const GDBusMethodTable phonebook_methods[] = { { GDBUS_ASYNC_METHOD("Import", NULL, GDBUS_ARGS({ "entries", "s" }), import_entries) }, { } }; static const GDBusSignalTable phonebook_signals[] = { { } }; int ofono_phonebook_driver_register(const struct ofono_phonebook_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_phonebook_driver_unregister(const struct ofono_phonebook_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void phonebook_unregister(struct ofono_atom *atom) { struct ofono_phonebook *pb = __ofono_atom_get_data(atom); const char *path = __ofono_atom_get_path(pb->atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(pb->atom); ofono_modem_remove_interface(modem, OFONO_PHONEBOOK_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_PHONEBOOK_INTERFACE); } static void phonebook_remove(struct ofono_atom *atom) { struct ofono_phonebook *pb = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (pb == NULL) return; if (pb->driver && pb->driver->remove) pb->driver->remove(pb); g_string_free(pb->vcards, TRUE); g_free(pb); } struct ofono_phonebook *ofono_phonebook_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_phonebook *pb; GSList *l; if (driver == NULL) return NULL; pb = g_try_new0(struct ofono_phonebook, 1); if (pb == NULL) return NULL; pb->vcards = g_string_new(NULL); pb->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_PHONEBOOK, phonebook_remove, pb); for (l = g_drivers; l; l = l->next) { const struct ofono_phonebook_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(pb, vendor, data) < 0) continue; pb->driver = drv; break; } return pb; } void ofono_phonebook_register(struct ofono_phonebook *pb) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(pb->atom); struct ofono_modem *modem = __ofono_atom_get_modem(pb->atom); if (!g_dbus_register_interface(conn, path, OFONO_PHONEBOOK_INTERFACE, phonebook_methods, phonebook_signals, NULL, pb, NULL)) { ofono_error("Could not create %s interface", OFONO_PHONEBOOK_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_PHONEBOOK_INTERFACE); __ofono_atom_register(pb->atom, phonebook_unregister); } void ofono_phonebook_remove(struct ofono_phonebook *pb) { __ofono_atom_free(pb->atom); } void ofono_phonebook_set_data(struct ofono_phonebook *pb, void *data) { pb->driver_data = data; } void *ofono_phonebook_get_data(struct ofono_phonebook *pb) { return pb->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/spn-table.c0000644000015600001650000000340212671500024021216 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include "ofono.h" #include "spn-table.h" static GSList *g_drivers = NULL; const char *__ofono_spn_table_get_spn(const char *numeric) { GSList *d; const char *spn = NULL; for (d = g_drivers; d != NULL; d = d->next) { const struct ofono_spn_table_driver *driver = d->data; if (driver->get_spn == NULL) continue; DBG("Calling spntable plugin '%s'", driver->name); spn = driver->get_spn(numeric); if (spn == NULL) continue; return spn; } return spn; } int ofono_spn_table_driver_register(struct ofono_spn_table_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_prepend(g_drivers, driver); return 0; } void ofono_spn_table_driver_unregister( const struct ofono_spn_table_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_remove(g_drivers, driver); } ofono-1.17.bzr6912+16.04.20160314.3/src/call-barring.c0000644000015600001650000007034312671500024021676 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" #define CALL_BARRING_FLAG_CACHED 0x1 #define NUM_OF_BARRINGS 5 static GSList *g_drivers = NULL; static void cb_ss_query_next_lock(struct ofono_call_barring *cb); static void get_query_next_lock(struct ofono_call_barring *cb); static void set_query_next_lock(struct ofono_call_barring *cb); struct ofono_call_barring { int flags; DBusMessage *pending; int cur_locks[NUM_OF_BARRINGS]; int new_locks[NUM_OF_BARRINGS]; int query_start; int query_end; int query_next; int ss_req_type; int ss_req_cls; int ss_req_lock; struct ofono_ussd *ussd; unsigned int ussd_watch; const struct ofono_call_barring_driver *driver; void *driver_data; struct ofono_atom *atom; }; struct call_barring_lock { const char *name; const char *value; const char *fac; }; static struct call_barring_lock cb_locks[] = { { "AllOutgoing", "all", "AO" }, { "InternationalOutgoing", "international", "OI" }, { "InternationalOutgoingExceptHome", "internationalnothome", "OX" }, { "AllIncoming", "always", "AI" }, { "IncomingWhenRoaming", "whenroaming", "IR" }, { "AllBarringServices", NULL, "AB" }, { "AllOutgoingServices", NULL, "AG" }, { "AllIncomingServices", NULL, "AC" }, { NULL, NULL, NULL }, }; /* These are inclusive */ #define CB_OUTGOING_START 0 #define CB_OUTGOING_END 2 #define CB_INCOMING_START 3 #define CB_INCOMING_END 4 #define CB_ALL_START 0 #define CB_ALL_END 4 #define CB_ALL_OUTGOING 6 #define CB_ALL_INCOMING 7 static inline void emit_barring_changed(struct ofono_call_barring *cb, int start, int end, const char *type, int cls) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cb->atom); char property_name[64]; const char *value; int i; int j; for (i = start; i <= end; i++) if (cb->cur_locks[i] & cls) break; for (j = start; j <= end; j++) if (cb->new_locks[j] & cls) break; if (i == j) return; if (j > end) value = "disabled"; else value = cb_locks[j].value; snprintf(property_name, sizeof(property_name), "%s%s", bearer_class_to_string(cls), type); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_BARRING_INTERFACE, property_name, DBUS_TYPE_STRING, &value); } static void update_barrings(struct ofono_call_barring *cb, int mask) { int cls; int i; /* We're only interested in emitting signals for Voice, Fax & Data */ for (cls = 1; cls <= BEARER_CLASS_PAD; cls = cls << 1) { if ((cls & mask) == 0) continue; emit_barring_changed(cb, cb->query_start, CB_OUTGOING_END, "Outgoing", cls); emit_barring_changed(cb, CB_INCOMING_START, cb->query_end, "Incoming", cls); } for (i = cb->query_start; i <= cb->query_end; i++) cb->cur_locks[i] = cb->new_locks[i]; } static void cb_ss_property_append(struct ofono_call_barring *cb, DBusMessageIter *dict, int lock, int mask) { int i; char property_name[64]; const char *strvalue; for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) { if (!(mask & i)) continue; strvalue = (cb->new_locks[lock] & i) ? "enabled" : "disabled"; snprintf(property_name, sizeof(property_name), "%s%s", bearer_class_to_string(i), cb_locks[lock].name); ofono_dbus_dict_append(dict, property_name, DBUS_TYPE_STRING, &strvalue); } } static void cb_set_query_bounds(struct ofono_call_barring *cb, const char *fac, gboolean fac_only) { int i; if (!strcmp("AB", fac)) { cb->query_start = CB_ALL_START; cb->query_end = CB_ALL_END; cb->query_next = CB_ALL_START; return; } if (!strcmp("AG", fac)) goto outgoing; if (!strcmp("AC", fac)) goto incoming; for (i = 0; cb_locks[i].name; i++) { if (strcmp(cb_locks[i].fac, fac)) continue; if (fac_only) { cb->query_start = i; cb->query_end = i; cb->query_next = i; return; } if ((i >= CB_OUTGOING_START) && (i <= CB_OUTGOING_END)) goto outgoing; else if ((i >= CB_INCOMING_START) && (i <= CB_INCOMING_END)) goto incoming; } ofono_error("Unable to set query boundaries for %s", fac); return; outgoing: cb->query_start = CB_OUTGOING_START; cb->query_end = CB_OUTGOING_END; cb->query_next = CB_OUTGOING_START; return; incoming: cb->query_start = CB_INCOMING_START; cb->query_end = CB_INCOMING_END; cb->query_next = CB_INCOMING_START; return; } static void generate_ss_query_reply(struct ofono_call_barring *cb) { const char *context = "CallBarring"; const char *sig = "(ssa{sv})"; const char *ss_type = ss_control_type_to_string(cb->ss_req_type); const char *ss_fac = cb_locks[cb->ss_req_lock].name; DBusMessageIter iter; DBusMessageIter variant; DBusMessageIter vstruct; DBusMessageIter dict; DBusMessage *reply; int lock; int start, end; reply = dbus_message_new_method_return(cb->pending); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_STRUCT, NULL, &vstruct); dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &ss_type); dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &ss_fac); dbus_message_iter_open_container(&vstruct, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); /* We report all affected locks only for the special case ones */ if (cb->ss_req_lock <= CB_ALL_END) { start = cb->ss_req_lock; end = cb->ss_req_lock; } else { start = cb->query_start; end = cb->query_end; } for (lock = start; lock <= end; lock++) cb_ss_property_append(cb, &dict, lock, cb->ss_req_cls); dbus_message_iter_close_container(&vstruct, &dict); dbus_message_iter_close_container(&variant, &vstruct); dbus_message_iter_close_container(&iter, &variant); __ofono_dbus_pending_reply(&cb->pending, reply); } static void cb_ss_query_next_lock_callback(const struct ofono_error *error, int status, void *data) { struct ofono_call_barring *cb = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Query failed with error: %s", telephony_error_to_str(error)); cb->flags &= ~CALL_BARRING_FLAG_CACHED; __ofono_dbus_pending_reply(&cb->pending, __ofono_error_from_error(error, cb->pending)); return; } cb->new_locks[cb->query_next] = status; if (cb->query_next < cb->query_end) { cb->query_next += 1; cb_ss_query_next_lock(cb); return; } generate_ss_query_reply(cb); update_barrings(cb, BEARER_CLASS_VOICE); } static void cb_ss_query_next_lock(struct ofono_call_barring *cb) { int cls; cls = (cb->ss_req_type == SS_CONTROL_TYPE_QUERY) ? cb->ss_req_cls : cb->ss_req_cls | BEARER_CLASS_DEFAULT; cb->driver->query(cb, cb_locks[cb->query_next].fac, cls, cb_ss_query_next_lock_callback, cb); } static void cb_ss_set_lock_callback(const struct ofono_error *error, void *data) { struct ofono_call_barring *cb = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Enabling/disabling Call Barring via SS failed with err:%s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&cb->pending, __ofono_error_from_error(error, cb->pending)); return; } /* Assume we have query always */ cb_ss_query_next_lock(cb); } static const char *cb_ss_service_to_fac(const char *svc) { if (!strcmp(svc, "33")) return "AO"; else if (!strcmp(svc, "331")) return "OI"; else if (!strcmp(svc, "332")) return "OX"; else if (!strcmp(svc, "35")) return "AI"; else if (!strcmp(svc, "351")) return "IR"; else if (!strcmp(svc, "330")) return "AB"; else if (!strcmp(svc, "333")) return "AG"; else if (!strcmp(svc, "353")) return "AC"; return NULL; } static gboolean cb_ss_control(int type, const char *sc, const char *sia, const char *sib, const char *sic, const char *dn, DBusMessage *msg, void *data) { struct ofono_call_barring *cb = data; DBusConnection *conn = ofono_dbus_get_connection(); int cls = BEARER_CLASS_DEFAULT; const char *fac; DBusMessage *reply; void *operation = NULL; int i; if (__ofono_call_barring_is_busy(cb)) { reply = __ofono_error_busy(msg); g_dbus_send_message(conn, reply); return TRUE; } DBG("Received call barring ss control request"); DBG("type: %d, sc: %s, sia: %s, sib: %s, sic: %s, dn: %s", type, sc, sia, sib, sic, dn); fac = cb_ss_service_to_fac(sc); if (fac == NULL) return FALSE; cb_set_query_bounds(cb, fac, type == SS_CONTROL_TYPE_QUERY); i = 0; while (cb_locks[i].name && strcmp(cb_locks[i].fac, fac)) i++; cb->ss_req_lock = i; if (strlen(sic) > 0) goto bad_format; if (strlen(dn) > 0) goto bad_format; if (type != SS_CONTROL_TYPE_QUERY && !__ofono_is_valid_net_pin(sia)) goto bad_format; switch (type) { case SS_CONTROL_TYPE_ACTIVATION: case SS_CONTROL_TYPE_DEACTIVATION: case SS_CONTROL_TYPE_REGISTRATION: case SS_CONTROL_TYPE_ERASURE: operation = cb->driver->set; break; case SS_CONTROL_TYPE_QUERY: operation = cb->driver->query; break; default: break; } if (operation == NULL) { reply = __ofono_error_not_implemented(msg); g_dbus_send_message(conn, reply); return TRUE; } /* * According to 27.007, AG, AC and AB only work with mode = 0 * We support query by querying all relevant types, since we must * do this for the deactivation case anyway */ if ((!strcmp(fac, "AG") || !strcmp(fac, "AC") || !strcmp(fac, "AB")) && (type == SS_CONTROL_TYPE_ACTIVATION || type == SS_CONTROL_TYPE_REGISTRATION)) goto bad_format; if (strlen(sib) > 0) { long service_code; char *end; service_code = strtoul(sib, &end, 10); if (end == sib || *end != '\0') goto bad_format; cls = mmi_service_code_to_bearer_class(service_code); if (cls == 0) goto bad_format; } cb->ss_req_cls = cls; cb->pending = dbus_message_ref(msg); switch (type) { case SS_CONTROL_TYPE_ACTIVATION: case SS_CONTROL_TYPE_REGISTRATION: cb->ss_req_type = SS_CONTROL_TYPE_ACTIVATION; cb->driver->set(cb, fac, 1, sia, cls, cb_ss_set_lock_callback, cb); break; case SS_CONTROL_TYPE_ERASURE: case SS_CONTROL_TYPE_DEACTIVATION: cb->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION; cb->driver->set(cb, fac, 0, sia, cls, cb_ss_set_lock_callback, cb); break; case SS_CONTROL_TYPE_QUERY: cb->ss_req_type = SS_CONTROL_TYPE_QUERY; cb_ss_query_next_lock(cb); break; } return TRUE; bad_format: reply = __ofono_error_invalid_format(msg); g_dbus_send_message(conn, reply); return TRUE; } static void cb_set_passwd_callback(const struct ofono_error *error, void *data) { struct ofono_call_barring *cb = data; DBusMessage *reply; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) reply = dbus_message_new_method_return(cb->pending); else { DBG("Changing Call Barring password via SS failed with err: %s", telephony_error_to_str(error)); reply = __ofono_error_from_error(error, cb->pending); } __ofono_dbus_pending_reply(&cb->pending, reply); } static gboolean cb_ss_passwd(const char *sc, const char *old, const char *new, DBusMessage *msg, void *data) { struct ofono_call_barring *cb = data; DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *reply; const char *fac; if (__ofono_call_barring_is_busy(cb)) { reply = __ofono_error_busy(msg); g_dbus_send_message(conn, reply); return TRUE; } DBG("Received call barring ss password change request"); DBG("sc: %s", sc); if (!strcmp(sc, "")) fac = "AB"; else fac = cb_ss_service_to_fac(sc); if (fac == NULL) return FALSE; if (!__ofono_is_valid_net_pin(old) || !__ofono_is_valid_net_pin(new)) goto bad_format; cb->pending = dbus_message_ref(msg); cb->driver->set_passwd(cb, fac, old, new, cb_set_passwd_callback, cb); return TRUE; bad_format: reply = __ofono_error_invalid_format(msg); g_dbus_send_message(conn, reply); return TRUE; } static void cb_register_ss_controls(struct ofono_call_barring *cb) { __ofono_ussd_ssc_register(cb->ussd, "33", cb_ss_control, cb, NULL); __ofono_ussd_ssc_register(cb->ussd, "331", cb_ss_control, cb, NULL); __ofono_ussd_ssc_register(cb->ussd, "332", cb_ss_control, cb, NULL); __ofono_ussd_ssc_register(cb->ussd, "35", cb_ss_control, cb, NULL); __ofono_ussd_ssc_register(cb->ussd, "351", cb_ss_control, cb, NULL); __ofono_ussd_ssc_register(cb->ussd, "330", cb_ss_control, cb, NULL); __ofono_ussd_ssc_register(cb->ussd, "333", cb_ss_control, cb, NULL); __ofono_ussd_ssc_register(cb->ussd, "353", cb_ss_control, cb, NULL); __ofono_ussd_passwd_register(cb->ussd, "", cb_ss_passwd, cb, NULL); __ofono_ussd_passwd_register(cb->ussd, "33", cb_ss_passwd, cb, NULL); __ofono_ussd_passwd_register(cb->ussd, "331", cb_ss_passwd, cb, NULL); __ofono_ussd_passwd_register(cb->ussd, "332", cb_ss_passwd, cb, NULL); __ofono_ussd_passwd_register(cb->ussd, "35", cb_ss_passwd, cb, NULL); __ofono_ussd_passwd_register(cb->ussd, "351", cb_ss_passwd, cb, NULL); __ofono_ussd_passwd_register(cb->ussd, "330", cb_ss_passwd, cb, NULL); __ofono_ussd_passwd_register(cb->ussd, "333", cb_ss_passwd, cb, NULL); __ofono_ussd_passwd_register(cb->ussd, "353", cb_ss_passwd, cb, NULL); } static void cb_unregister_ss_controls(struct ofono_call_barring *cb) { __ofono_ussd_ssc_unregister(cb->ussd, "33"); __ofono_ussd_ssc_unregister(cb->ussd, "331"); __ofono_ussd_ssc_unregister(cb->ussd, "332"); __ofono_ussd_ssc_unregister(cb->ussd, "35"); __ofono_ussd_ssc_unregister(cb->ussd, "351"); __ofono_ussd_ssc_unregister(cb->ussd, "330"); __ofono_ussd_ssc_unregister(cb->ussd, "333"); __ofono_ussd_ssc_unregister(cb->ussd, "353"); __ofono_ussd_passwd_unregister(cb->ussd, ""); __ofono_ussd_passwd_unregister(cb->ussd, "33"); __ofono_ussd_passwd_unregister(cb->ussd, "331"); __ofono_ussd_passwd_unregister(cb->ussd, "332"); __ofono_ussd_passwd_unregister(cb->ussd, "35"); __ofono_ussd_passwd_unregister(cb->ussd, "351"); __ofono_ussd_passwd_unregister(cb->ussd, "330"); __ofono_ussd_passwd_unregister(cb->ussd, "333"); __ofono_ussd_passwd_unregister(cb->ussd, "353"); } gboolean __ofono_call_barring_is_busy(struct ofono_call_barring *cb) { return cb->pending ? TRUE : FALSE; } static inline void cb_append_property(struct ofono_call_barring *cb, DBusMessageIter *dict, int start, int end, int cls, const char *property) { char property_name[64]; const char *value = "disabled"; int i; for (i = start; i <= end; i++) if (cb->new_locks[i] & cls) break; if (i <= end) value = cb_locks[i].value; snprintf(property_name, sizeof(property_name), "%s%s", bearer_class_to_string(cls), property); ofono_dbus_dict_append(dict, property_name, DBUS_TYPE_STRING, &value); } static void cb_get_properties_reply(struct ofono_call_barring *cb, int mask) { DBusMessage *reply; DBusMessageIter iter, dict; int j; if (!(cb->flags & CALL_BARRING_FLAG_CACHED)) ofono_error("Generating a get_properties reply with no cache"); reply = dbus_message_new_method_return(cb->pending); if (reply == NULL) return; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) { if ((j & mask) == 0) continue; cb_append_property(cb, &dict, CB_OUTGOING_START, CB_OUTGOING_END, j, "Outgoing"); cb_append_property(cb, &dict, CB_INCOMING_START, CB_INCOMING_END, j, "Incoming"); } dbus_message_iter_close_container(&iter, &dict); __ofono_dbus_pending_reply(&cb->pending, reply); } static void get_query_lock_callback(const struct ofono_error *error, int status, void *data) { struct ofono_call_barring *cb = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) { cb->new_locks[cb->query_next] = status; if (cb->query_next == CB_ALL_END) cb->flags |= CALL_BARRING_FLAG_CACHED; } if (cb->query_next < CB_ALL_END) { cb->query_next = cb->query_next + 1; get_query_next_lock(cb); return; } cb_get_properties_reply(cb, BEARER_CLASS_VOICE); update_barrings(cb, BEARER_CLASS_VOICE); } static void get_query_next_lock(struct ofono_call_barring *cb) { cb->driver->query(cb, cb_locks[cb->query_next].fac, BEARER_CLASS_DEFAULT, get_query_lock_callback, cb); } static DBusMessage *cb_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_barring *cb = data; if (__ofono_call_barring_is_busy(cb) || __ofono_ussd_is_busy(cb->ussd)) return __ofono_error_busy(msg); if (cb->driver->query == NULL) return __ofono_error_not_implemented(msg); cb->pending = dbus_message_ref(msg); if (cb->flags & CALL_BARRING_FLAG_CACHED) cb_get_properties_reply(cb, BEARER_CLASS_VOICE); else { cb->query_next = CB_ALL_START; get_query_next_lock(cb); } return NULL; } static void set_query_lock_callback(const struct ofono_error *error, int status, void *data) { struct ofono_call_barring *cb = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Disabling all barring successful, " "but query was not"); cb->flags &= ~CALL_BARRING_FLAG_CACHED; __ofono_dbus_pending_reply(&cb->pending, __ofono_error_failed(cb->pending)); return; } cb->new_locks[cb->query_next] = status; if (cb->query_next < cb->query_end) { cb->query_next += 1; set_query_next_lock(cb); return; } __ofono_dbus_pending_reply(&cb->pending, dbus_message_new_method_return(cb->pending)); update_barrings(cb, BEARER_CLASS_VOICE); } static void set_query_next_lock(struct ofono_call_barring *cb) { cb->driver->query(cb, cb_locks[cb->query_next].fac, BEARER_CLASS_DEFAULT, set_query_lock_callback, cb); } static void set_lock_callback(const struct ofono_error *error, void *data) { struct ofono_call_barring *cb = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Enabling/disabling a lock failed"); __ofono_dbus_pending_reply(&cb->pending, __ofono_error_failed(cb->pending)); return; } /* * If we successfully set the value, we must query it back * Call Barring is a special case, since according to 22.088 2.2.1: * "The PLMN will ensure that only one of the barring programs is * active per basic service group. The activation of one specific * barring program will override an already active one (i.e. the * old one will be permanently deactivated)." * So we actually query all outgoing / incoming barrings depending * on what kind we set. */ set_query_next_lock(cb); } static gboolean cb_lock_property_lookup(const char *property, const char *value, int mask, int *out_which, int *out_cls, int *out_mode) { int i, j; const char *prefix; size_t len; int start, end; for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) { if ((i & mask) == 0) continue; prefix = bearer_class_to_string(i); len = strlen(prefix); if (!strncmp(property, prefix, len)) break; } if (i > BEARER_CLASS_PAD) return FALSE; property += len; if (!strcmp(property, "Outgoing")) { start = CB_OUTGOING_START; end = CB_OUTGOING_END; } else if (!strcmp(property, "Incoming")) { start = CB_INCOMING_START; end = CB_INCOMING_END; } else { return FALSE; } /* * Gah, this is a special case. If we're setting a barring to * disabled, then generate a disable all outgoing/incoming * request for a particular basic service */ if (!strcmp(value, "disabled")) { *out_mode = 0; *out_cls = i; if (!strcmp(property, "Outgoing")) *out_which = CB_ALL_OUTGOING; else *out_which = CB_ALL_INCOMING; return TRUE; } for (j = start; j <= end; j++) { if (strcmp(value, cb_locks[j].value)) continue; *out_mode = 1; *out_cls = i; *out_which = j; return TRUE; } return FALSE; } static DBusMessage *cb_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_barring *cb = data; DBusMessageIter iter; DBusMessageIter var; const char *name, *passwd = ""; const char *value; int lock; int cls; int mode; if (__ofono_call_barring_is_busy(cb) || __ofono_ussd_is_busy(cb->ussd)) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (!cb_lock_property_lookup(name, value, BEARER_CLASS_VOICE, &lock, &cls, &mode)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_next(&iter)) { if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &passwd); if (!__ofono_is_valid_net_pin(passwd)) return __ofono_error_invalid_format(msg); } if (cb->driver->set == NULL) return __ofono_error_not_implemented(msg); cb_set_query_bounds(cb, cb_locks[lock].fac, FALSE); cb->pending = dbus_message_ref(msg); cb->driver->set(cb, cb_locks[lock].fac, mode, passwd, cls, set_lock_callback, cb); return NULL; } static void disable_all_callback(const struct ofono_error *error, void *data) { struct ofono_call_barring *cb = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Disabling all barring failed"); __ofono_dbus_pending_reply(&cb->pending, __ofono_error_failed(cb->pending)); return; } /* Assume if we have set, we have query */ set_query_next_lock(cb); } static DBusMessage *cb_disable_all(DBusConnection *conn, DBusMessage *msg, void *data, const char *fac) { struct ofono_call_barring *cb = data; const char *passwd; if (cb->driver->set == NULL) return __ofono_error_not_implemented(msg); if (__ofono_call_barring_is_busy(cb) || __ofono_ussd_is_busy(cb->ussd)) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &passwd, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!__ofono_is_valid_net_pin(passwd)) return __ofono_error_invalid_format(msg); cb_set_query_bounds(cb, fac, FALSE); cb->pending = dbus_message_ref(msg); cb->driver->set(cb, fac, 0, passwd, BEARER_CLASS_DEFAULT, disable_all_callback, cb); return NULL; } static DBusMessage *cb_disable_ab(DBusConnection *conn, DBusMessage *msg, void *data) { return cb_disable_all(conn, msg, data, "AB"); } static DBusMessage *cb_disable_ac(DBusConnection *conn, DBusMessage *msg, void *data) { return cb_disable_all(conn, msg, data, "AC"); } static DBusMessage *cb_disable_ag(DBusConnection *conn, DBusMessage *msg, void *data) { return cb_disable_all(conn, msg, data, "AG"); } static DBusMessage *cb_set_passwd(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_barring *cb = data; const char *old_passwd; const char *new_passwd; if (cb->driver->set_passwd == NULL) return __ofono_error_not_implemented(msg); if (__ofono_call_barring_is_busy(cb) || __ofono_ussd_is_busy(cb->ussd)) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &old_passwd, DBUS_TYPE_STRING, &new_passwd, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!__ofono_is_valid_net_pin(old_passwd)) return __ofono_error_invalid_format(msg); if (!__ofono_is_valid_net_pin(new_passwd)) return __ofono_error_invalid_format(msg); cb->pending = dbus_message_ref(msg); cb->driver->set_passwd(cb, "AB", old_passwd, new_passwd, cb_set_passwd_callback, cb); return NULL; } static const GDBusMethodTable cb_methods[] = { { GDBUS_ASYNC_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), cb_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }, { "pin2", "s" }), NULL, cb_set_property) }, { GDBUS_ASYNC_METHOD("DisableAll", GDBUS_ARGS({ "password", "s" }), NULL, cb_disable_ab) }, { GDBUS_ASYNC_METHOD("DisableAllIncoming", GDBUS_ARGS({ "password", "s" }), NULL, cb_disable_ac) }, { GDBUS_ASYNC_METHOD("DisableAllOutgoing", GDBUS_ARGS({ "password", "s" }), NULL, cb_disable_ag) }, { GDBUS_ASYNC_METHOD("ChangePassword", GDBUS_ARGS({ "old", "s" }, { "new", "s" }), NULL, cb_set_passwd) }, { } }; static const GDBusSignalTable cb_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; int ofono_call_barring_driver_register(const struct ofono_call_barring_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_call_barring_driver_unregister(const struct ofono_call_barring_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void call_barring_unregister(struct ofono_atom *atom) { struct ofono_call_barring *cb = __ofono_atom_get_data(atom); const char *path = __ofono_atom_get_path(cb->atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(cb->atom); ofono_modem_remove_interface(modem, OFONO_CALL_BARRING_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_CALL_BARRING_INTERFACE); if (cb->ussd) cb_unregister_ss_controls(cb); if (cb->ussd_watch) __ofono_modem_remove_atom_watch(modem, cb->ussd_watch); } static void call_barring_remove(struct ofono_atom *atom) { struct ofono_call_barring *cb = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (cb == NULL) return; if (cb->driver != NULL && cb->driver->remove != NULL) cb->driver->remove(cb); g_free(cb); } struct ofono_call_barring *ofono_call_barring_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_call_barring *cb; GSList *l; if (driver == NULL) return NULL; cb = g_try_new0(struct ofono_call_barring, 1); if (cb == NULL) return NULL; cb->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CALL_BARRING, call_barring_remove, cb); for (l = g_drivers; l; l = l->next) { const struct ofono_call_barring_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(cb, vendor, data) < 0) continue; cb->driver = drv; break; } return cb; } static void ussd_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_call_barring *cb = data; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { cb->ussd = NULL; return; } cb->ussd = __ofono_atom_get_data(atom); cb_register_ss_controls(cb); } void ofono_call_barring_register(struct ofono_call_barring *cb) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cb->atom); struct ofono_modem *modem = __ofono_atom_get_modem(cb->atom); if (!g_dbus_register_interface(conn, path, OFONO_CALL_BARRING_INTERFACE, cb_methods, cb_signals, NULL, cb, NULL)) { ofono_error("Could not create %s interface", OFONO_CALL_BARRING_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_CALL_BARRING_INTERFACE); cb->ussd_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_USSD, ussd_watch, cb, NULL); __ofono_atom_register(cb->atom, call_barring_unregister); } void ofono_call_barring_remove(struct ofono_call_barring *cb) { __ofono_atom_free(cb->atom); } void ofono_call_barring_set_data(struct ofono_call_barring *cb, void *data) { cb->driver_data = data; } void *ofono_call_barring_get_data(struct ofono_call_barring *cb) { return cb->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/system-settings.c0000644000015600001650000000346012671500073022523 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2016 Canonical 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. * * 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 #endif #include #include #include #include "ofono.h" #include "system-settings.h" static GSList *g_drivers = NULL; char *__ofono_system_settings_get_string_value(const char *name) { GSList *d; char *value = NULL; for (d = g_drivers; d != NULL; d = d->next) { const struct ofono_system_settings_driver *driver = d->data; if (driver->get_string_value == NULL) continue; DBG("Calling system settings plugin '%s'", driver->name); value = driver->get_string_value(name); if (value == NULL) continue; DBG("property %s value %s", name, value); return value; } return value; } int ofono_system_settings_driver_register( struct ofono_system_settings_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_prepend(g_drivers, driver); return 0; } void ofono_system_settings_driver_unregister( const struct ofono_system_settings_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_remove(g_drivers, driver); } ofono-1.17.bzr6912+16.04.20160314.3/src/smsagent.h0000644000015600001650000000325112671500024021161 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 sms_agent; enum sms_agent_result { SMS_AGENT_RESULT_OK = 0, SMS_AGENT_RESULT_FAILED, SMS_AGENT_RESULT_TIMEOUT, }; typedef void (*sms_agent_dispatch_cb)(struct sms_agent *agent, enum sms_agent_result result, void *data); struct sms_agent *sms_agent_new(const char *interface, const char *service, const char *path); void sms_agent_set_removed_notify(struct sms_agent *agent, ofono_destroy_func destroy, void *user_data); ofono_bool_t sms_agent_matches(struct sms_agent *agent, const char *service, const char *path); void sms_agent_free(struct sms_agent *agent); int sms_agent_dispatch_datagram(struct sms_agent *agent, const char *method, const char *from, const struct tm *remote_sent_time, const struct tm *local_sent_time, const unsigned char *content, unsigned int len, sms_agent_dispatch_cb cb, void *user_data, ofono_destroy_func destroy); ofono-1.17.bzr6912+16.04.20160314.3/src/nettime.c0000644000015600001650000000557012671500024021006 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include "ofono.h" static GSList *nettime_drivers = NULL; static struct ofono_nettime_context *nettime_context_create( struct ofono_modem *modem, struct ofono_nettime_driver *driver) { struct ofono_nettime_context *context; if (driver->probe == NULL) return NULL; context = g_try_new0(struct ofono_nettime_context, 1); if (context == NULL) return NULL; context->driver = driver; context->modem = modem; if (driver->probe(context) < 0) { g_free(context); return NULL; } return context; } static void context_remove(struct ofono_atom *atom) { struct ofono_nettime_context *context = __ofono_atom_get_data(atom); if (context->driver->remove) context->driver->remove(context); g_free(context); } void __ofono_nettime_probe_drivers(struct ofono_modem *modem) { struct ofono_nettime_driver *driver; struct ofono_nettime_context *context; GSList *l; for (l = nettime_drivers; l; l = l->next) { driver = l->data; context = nettime_context_create(modem, driver); if (context == NULL) continue; __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_NETTIME, context_remove, context); } } static void nettime_info_received(struct ofono_atom *atom, void *data) { struct ofono_nettime_context *context = __ofono_atom_get_data(atom); struct ofono_network_time *info = data; if (context->driver->info_received == NULL) return; context->driver->info_received(context, info); } void __ofono_nettime_info_received(struct ofono_modem *modem, struct ofono_network_time *info) { __ofono_modem_foreach_atom(modem, OFONO_ATOM_TYPE_NETTIME, nettime_info_received, info); } int ofono_nettime_driver_register(const struct ofono_nettime_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); nettime_drivers = g_slist_prepend(nettime_drivers, (void *) driver); return 0; } void ofono_nettime_driver_unregister(const struct ofono_nettime_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); nettime_drivers = g_slist_remove(nettime_drivers, driver); } ofono-1.17.bzr6912+16.04.20160314.3/src/simfs.c0000644000015600001650000007046712671500024020471 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "ofono.h" #include "simfs.h" #include "simutil.h" #include "storage.h" #define SIM_CACHE_MODE 0600 #define SIM_CACHE_BASEPATH STORAGEDIR "/%s-%i" #define SIM_CACHE_VERSION SIM_CACHE_BASEPATH "/version" #define SIM_CACHE_PATH SIM_CACHE_BASEPATH "/%04x" #define SIM_CACHE_HEADER_SIZE 39 #define SIM_FILE_INFO_SIZE 7 #define SIM_IMAGE_CACHE_BASEPATH STORAGEDIR "/%s-%i/images" #define SIM_IMAGE_CACHE_PATH SIM_IMAGE_CACHE_BASEPATH "/%d.xpm" #define SIM_FS_VERSION 2 static gboolean sim_fs_op_next(gpointer user_data); static gboolean sim_fs_op_read_record(gpointer user); static gboolean sim_fs_op_read_block(gpointer user_data); struct sim_fs_op { int id; unsigned char *buffer; enum ofono_sim_file_structure structure; unsigned short offset; gboolean info_only; int num_bytes; int length; int record_length; int current; unsigned char path[6]; unsigned char path_len; gconstpointer cb; gboolean is_read; void *userdata; struct ofono_sim_context *context; }; static void sim_fs_op_free(struct sim_fs_op *node) { g_free(node->buffer); g_free(node); } struct sim_fs { GQueue *op_q; gint op_source; unsigned char bitmap[32]; int fd; struct ofono_sim *sim; const struct ofono_sim_driver *driver; GSList *contexts; }; void sim_fs_free(struct sim_fs *fs) { if (fs == NULL) return; if (fs->op_source) { g_source_remove(fs->op_source); fs->op_source = 0; } /* * Note: users of sim_fs must not assume that the callback happens * for operations still in progress */ if (fs->op_q) { g_queue_foreach(fs->op_q, (GFunc) sim_fs_op_free, NULL); g_queue_free(fs->op_q); fs->op_q = NULL; } while (fs->contexts) sim_fs_context_free(fs->contexts->data); g_free(fs); } struct file_watch { struct ofono_watchlist_item item; int ef; }; struct ofono_sim_context { struct sim_fs *fs; struct ofono_watchlist *file_watches; }; struct sim_fs *sim_fs_new(struct ofono_sim *sim, const struct ofono_sim_driver *driver) { struct sim_fs *fs; fs = g_try_new0(struct sim_fs, 1); if (fs == NULL) return NULL; fs->sim = sim; fs->driver = driver; fs->fd = -1; return fs; } struct ofono_sim_context *sim_fs_context_new(struct sim_fs *fs) { struct ofono_sim_context *context = g_try_new0(struct ofono_sim_context, 1); if (context == NULL) return NULL; context->fs = fs; fs->contexts = g_slist_prepend(fs->contexts, context); return context; } void sim_fs_context_free(struct ofono_sim_context *context) { struct sim_fs *fs = context->fs; int n = 0; struct sim_fs_op *op; if (fs->op_q) { while ((op = g_queue_peek_nth(fs->op_q, n)) != NULL) { if (op->context != context) { n += 1; continue; } if (n == 0) { op->cb = NULL; n += 1; continue; } sim_fs_op_free(op); g_queue_remove(fs->op_q, op); } } if (context->file_watches) __ofono_watchlist_free(context->file_watches); fs->contexts = g_slist_remove(fs->contexts, context); g_free(context); } unsigned int sim_fs_file_watch_add(struct ofono_sim_context *context, int id, ofono_sim_file_changed_cb_t cb, void *userdata, ofono_destroy_func destroy) { struct file_watch *watch; if (cb == NULL) return 0; if (context->file_watches == NULL) context->file_watches = __ofono_watchlist_new(g_free); watch = g_new0(struct file_watch, 1); watch->ef = id; watch->item.notify = cb; watch->item.notify_data = userdata; watch->item.destroy = destroy; return __ofono_watchlist_add_item(context->file_watches, (struct ofono_watchlist_item *) watch); } void sim_fs_file_watch_remove(struct ofono_sim_context *context, unsigned int id) { __ofono_watchlist_remove_item(context->file_watches, id); } void sim_fs_notify_file_watches(struct sim_fs *fs, int id) { GSList *l; for (l = fs->contexts; l; l = l->next) { struct ofono_sim_context *context = l->data; GSList *k; for (k = context->file_watches->items; k; k = k->next) { struct file_watch *w = k->data; ofono_sim_file_changed_cb_t notify = w->item.notify; if (id == -1 || w->ef == id) notify(w->ef, w->item.notify_data); } } } static void sim_fs_end_current(struct sim_fs *fs) { struct sim_fs_op *op = g_queue_pop_head(fs->op_q); if (g_queue_get_length(fs->op_q) > 0) fs->op_source = g_idle_add(sim_fs_op_next, fs); if (fs->fd != -1) { TFR(close(fs->fd)); fs->fd = -1; } memset(fs->bitmap, 0, sizeof(fs->bitmap)); sim_fs_op_free(op); } static void sim_fs_op_error(struct sim_fs *fs) { struct sim_fs_op *op = g_queue_peek_head(fs->op_q); if (op->cb == NULL) { sim_fs_end_current(fs); return; } if (op->info_only == TRUE) ((ofono_sim_read_info_cb_t) op->cb) (0, 0, 0, 0, op->userdata); else if (op->is_read == TRUE) ((ofono_sim_file_read_cb_t) op->cb) (0, 0, 0, 0, 0, op->userdata); else ((ofono_sim_file_write_cb_t) op->cb) (0, op->userdata); sim_fs_end_current(fs); } static gboolean cache_block(struct sim_fs *fs, int block, int block_len, const unsigned char *data, int num_bytes) { int offset; int bit; ssize_t r; unsigned char b; if (fs->fd == -1) return FALSE; if (lseek(fs->fd, block * block_len + SIM_CACHE_HEADER_SIZE, SEEK_SET) == (off_t) -1) return FALSE; r = TFR(write(fs->fd, data, num_bytes)); if (r != num_bytes) return FALSE; /* update present bit for this block */ offset = block / 8; bit = block % 8; /* lseek to correct byte (skip file info) */ lseek(fs->fd, offset + SIM_FILE_INFO_SIZE, SEEK_SET); b = fs->bitmap[offset]; b |= 1 << bit; r = TFR(write(fs->fd, &b, sizeof(b))); if (r != sizeof(b)) return FALSE; fs->bitmap[offset] = b; return TRUE; } static void sim_fs_op_write_cb(const struct ofono_error *error, void *data) { struct sim_fs *fs = data; struct sim_fs_op *op = g_queue_peek_head(fs->op_q); ofono_sim_file_write_cb_t cb = op->cb; if (cb == NULL) { sim_fs_end_current(fs); return; } if (error->type == OFONO_ERROR_TYPE_NO_ERROR) cb(1, op->userdata); else cb(0, op->userdata); sim_fs_end_current(fs); } static void sim_fs_op_read_record_cb(const struct ofono_error *error, const unsigned char *sdata, int length, void *data) { struct sim_fs *fs = data; struct sim_fs_op *op = g_queue_peek_head(fs->op_q); ofono_sim_file_read_cb_t cb = op->cb; if (cb == NULL) { sim_fs_end_current(fs); return; } if (error->type == OFONO_ERROR_TYPE_NO_ERROR) cb(1, -1, op->current, sdata, length, op->userdata); else cb(0, -1, op->current, NULL, 0, op->userdata); sim_fs_end_current(fs); } static void sim_fs_op_read_block_cb(const struct ofono_error *error, const unsigned char *data, int len, void *user) { struct sim_fs *fs = user; struct sim_fs_op *op = g_queue_peek_head(fs->op_q); int start_block; int end_block; int bufoff; int dataoff; int tocopy; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { sim_fs_op_error(fs); return; } start_block = op->offset / 256; end_block = (op->offset + (op->num_bytes - 1)) / 256; if (op->current == start_block) { bufoff = 0; dataoff = op->offset % 256; tocopy = MIN(256 - op->offset % 256, op->num_bytes - op->current * 256); } else { bufoff = (op->current - start_block - 1) * 256 + op->offset % 256; dataoff = 0; tocopy = MIN(256, op->num_bytes - op->current * 256); } DBG("bufoff: %d, dataoff: %d, tocopy: %d", bufoff, dataoff, tocopy); memcpy(op->buffer + bufoff, data + dataoff, tocopy); cache_block(fs, op->current, 256, data, len); if (op->cb == NULL) { sim_fs_end_current(fs); return; } op->current++; if (op->current > end_block) { ofono_sim_file_read_cb_t cb = op->cb; cb(1, op->num_bytes, 0, op->buffer, op->record_length, op->userdata); sim_fs_end_current(fs); } else { fs->op_source = g_idle_add(sim_fs_op_read_block, fs); } } static gboolean sim_fs_op_read_block(gpointer user_data) { struct sim_fs *fs = user_data; struct sim_fs_op *op = g_queue_peek_head(fs->op_q); int start_block; int end_block; unsigned short read_bytes; fs->op_source = 0; if (op->cb == NULL) { sim_fs_end_current(fs); return FALSE; } start_block = op->offset / 256; end_block = (op->offset + (op->num_bytes - 1)) / 256; if (op->current == start_block) { op->buffer = g_try_new0(unsigned char, op->num_bytes); if (op->buffer == NULL) { sim_fs_op_error(fs); return FALSE; } } while (fs->fd != -1 && op->current <= end_block) { int offset = op->current / 8; int bit = 1 << op->current % 8; int bufoff; int seekoff; int toread; if ((fs->bitmap[offset] & bit) == 0) break; if (op->current == start_block) { bufoff = 0; seekoff = SIM_CACHE_HEADER_SIZE + op->current * 256 + op->offset % 256; toread = MIN(256 - op->offset % 256, op->num_bytes - op->current * 256); } else { bufoff = (op->current - start_block - 1) * 256 + op->offset % 256; seekoff = SIM_CACHE_HEADER_SIZE + op->current * 256; toread = MIN(256, op->num_bytes - op->current * 256); } DBG("bufoff: %d, seekoff: %d, toread: %d", bufoff, seekoff, toread); if (lseek(fs->fd, seekoff, SEEK_SET) == (off_t) -1) break; if (TFR(read(fs->fd, op->buffer + bufoff, toread)) != toread) break; op->current += 1; } if (op->current > end_block) { ofono_sim_file_read_cb_t cb = op->cb; cb(1, op->num_bytes, 0, op->buffer, op->record_length, op->userdata); sim_fs_end_current(fs); return FALSE; } if (fs->driver->read_file_transparent == NULL) { sim_fs_op_error(fs); return FALSE; } read_bytes = MIN(op->length - op->current * 256, 256); fs->driver->read_file_transparent(fs->sim, op->id, op->current * 256, read_bytes, op->path_len ? op->path : NULL, op->path_len, sim_fs_op_read_block_cb, fs); return FALSE; } static void sim_fs_op_retrieve_cb(const struct ofono_error *error, const unsigned char *data, int len, void *user) { struct sim_fs *fs = user; struct sim_fs_op *op = g_queue_peek_head(fs->op_q); int total = op->length / op->record_length; ofono_sim_file_read_cb_t cb = op->cb; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { sim_fs_op_error(fs); return; } cache_block(fs, op->current - 1, op->record_length, data, op->record_length); if (cb == NULL) { sim_fs_end_current(fs); return; } cb(1, op->length, op->current, data, op->record_length, op->userdata); if (op->current < total) { op->current += 1; fs->op_source = g_idle_add(sim_fs_op_read_record, fs); } else { sim_fs_end_current(fs); } } static gboolean sim_fs_op_read_record(gpointer user) { struct sim_fs *fs = user; struct sim_fs_op *op = g_queue_peek_head(fs->op_q); const struct ofono_sim_driver *driver = fs->driver; int total = op->length / op->record_length; unsigned char buf[256]; fs->op_source = 0; if (op->cb == NULL) { sim_fs_end_current(fs); return FALSE; } while (fs->fd != -1 && op->current <= total) { int offset = (op->current - 1) / 8; int bit = 1 << ((op->current - 1) % 8); ofono_sim_file_read_cb_t cb = op->cb; if ((fs->bitmap[offset] & bit) == 0) break; if (lseek(fs->fd, (op->current - 1) * op->record_length + SIM_CACHE_HEADER_SIZE, SEEK_SET) == (off_t) -1) break; if (TFR(read(fs->fd, buf, op->record_length)) != op->record_length) break; cb(1, op->length, op->current, buf, op->record_length, op->userdata); op->current += 1; } if (op->current > total) { sim_fs_end_current(fs); return FALSE; } switch (op->structure) { case OFONO_SIM_FILE_STRUCTURE_FIXED: if (driver->read_file_linear == NULL) { sim_fs_op_error(fs); return FALSE; } driver->read_file_linear(fs->sim, op->id, op->current, op->record_length, op->path_len ? op->path : NULL, op->path_len, sim_fs_op_retrieve_cb, fs); break; case OFONO_SIM_FILE_STRUCTURE_CYCLIC: if (driver->read_file_cyclic == NULL) { sim_fs_op_error(fs); return FALSE; } driver->read_file_cyclic(fs->sim, op->id, op->current, op->record_length, op->path_len ? op->path : NULL, op->path_len, sim_fs_op_retrieve_cb, fs); break; default: ofono_error("Unrecognized file structure, this can't happen"); } return FALSE; } static void sim_fs_op_cache_fileinfo(struct sim_fs *fs, const struct ofono_error *error, int length, enum ofono_sim_file_structure structure, int record_length, const unsigned char access[3], unsigned char file_status) { struct sim_fs_op *op = g_queue_peek_head(fs->op_q); const char *imsi = ofono_sim_get_imsi(fs->sim); enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim); enum sim_file_access update; enum sim_file_access invalidate; enum sim_file_access rehabilitate; unsigned char fileinfo[SIM_CACHE_HEADER_SIZE]; gboolean cache; char *path; /* TS 11.11, Section 9.3 */ update = file_access_condition_decode(access[0] & 0xf); rehabilitate = file_access_condition_decode((access[2] >> 4) & 0xf); invalidate = file_access_condition_decode(access[2] & 0xf); /* Never cache card holder writable files */ cache = (update == SIM_FILE_ACCESS_ADM || update == SIM_FILE_ACCESS_NEVER) && (invalidate == SIM_FILE_ACCESS_ADM || invalidate == SIM_FILE_ACCESS_NEVER) && (rehabilitate == SIM_FILE_ACCESS_ADM || rehabilitate == SIM_FILE_ACCESS_NEVER); if (imsi == NULL || phase == OFONO_SIM_PHASE_UNKNOWN || cache == FALSE) return; memset(fileinfo, 0, SIM_CACHE_HEADER_SIZE); fileinfo[0] = error->type; fileinfo[1] = length >> 8; fileinfo[2] = length & 0xff; fileinfo[3] = structure; fileinfo[4] = record_length >> 8; fileinfo[5] = record_length & 0xff; fileinfo[6] = file_status; path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, op->id); fs->fd = TFR(open(path, O_WRONLY | O_CREAT | O_TRUNC, SIM_CACHE_MODE)); g_free(path); if (fs->fd == -1) return; if (TFR(write(fs->fd, fileinfo, SIM_CACHE_HEADER_SIZE)) == SIM_CACHE_HEADER_SIZE) return; TFR(close(fs->fd)); fs->fd = -1; } static void sim_fs_op_info_cb(const struct ofono_error *error, int length, enum ofono_sim_file_structure structure, int record_length, const unsigned char access[3], unsigned char file_status, void *data) { struct sim_fs *fs = data; struct sim_fs_op *op = g_queue_peek_head(fs->op_q); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { sim_fs_op_error(fs); return; } sim_fs_op_cache_fileinfo(fs, error, length, structure, record_length, access, file_status); if (structure != op->structure) { ofono_error("Requested file structure differs from SIM: %x", op->id); sim_fs_op_error(fs); return; } if (op->cb == NULL) { sim_fs_end_current(fs); return; } op->structure = structure; op->length = length; if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT) { if (op->num_bytes == 0) op->num_bytes = op->length; op->record_length = length; op->current = op->offset / 256; if (op->info_only == FALSE) fs->op_source = g_idle_add(sim_fs_op_read_block, fs); } else { op->record_length = record_length; op->current = 1; if (op->info_only == FALSE) fs->op_source = g_idle_add(sim_fs_op_read_record, fs); } if (op->info_only == TRUE) { /* * It's an info-only request, so there is no need to request * actual contents of the EF. Just return the EF-info. */ ofono_sim_read_info_cb_t cb = op->cb; cb(1, file_status, op->length, op->record_length, op->userdata); sim_fs_end_current(fs); } } static gboolean sim_fs_op_check_cached(struct sim_fs *fs) { const char *imsi = ofono_sim_get_imsi(fs->sim); enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim); struct sim_fs_op *op = g_queue_peek_head(fs->op_q); char *path; int fd; ssize_t len; unsigned char fileinfo[SIM_CACHE_HEADER_SIZE]; int error_type; int file_length; enum ofono_sim_file_structure structure; int record_length; unsigned char file_status; if (imsi == NULL || phase == OFONO_SIM_PHASE_UNKNOWN) return FALSE; path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, op->id); if (path == NULL) return FALSE; fd = TFR(open(path, O_RDWR)); g_free(path); if (fd == -1) { if (errno != ENOENT) DBG("Error %i opening cache file for " "fileid %04x, IMSI %s", errno, op->id, imsi); return FALSE; } len = TFR(read(fd, fileinfo, SIM_CACHE_HEADER_SIZE)); if (len != SIM_CACHE_HEADER_SIZE) goto error; error_type = fileinfo[0]; file_length = (fileinfo[1] << 8) | fileinfo[2]; structure = fileinfo[3]; record_length = (fileinfo[4] << 8) | fileinfo[5]; file_status = fileinfo[6]; if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT) record_length = file_length; if (record_length == 0 || file_length < record_length) goto error; op->length = file_length; op->record_length = record_length; memcpy(fs->bitmap, fileinfo + SIM_FILE_INFO_SIZE, SIM_CACHE_HEADER_SIZE - SIM_FILE_INFO_SIZE); fs->fd = fd; if (error_type != OFONO_ERROR_TYPE_NO_ERROR || structure != op->structure) { sim_fs_op_error(fs); return TRUE; } if (op->info_only == TRUE) { /* * It's an info-only request, so there is no need to request * actual contents of the EF. Just return the EF-info. */ ofono_sim_read_info_cb_t cb = op->cb; cb(1, file_status, op->length, op->record_length, op->userdata); sim_fs_end_current(fs); } else if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT) { if (op->num_bytes == 0) op->num_bytes = op->length; op->current = op->offset / 256; fs->op_source = g_idle_add(sim_fs_op_read_block, fs); } else { op->current = 1; fs->op_source = g_idle_add(sim_fs_op_read_record, fs); } return TRUE; error: TFR(close(fd)); return FALSE; } static gboolean sim_fs_op_next(gpointer user_data) { struct sim_fs *fs = user_data; const struct ofono_sim_driver *driver = fs->driver; struct sim_fs_op *op; fs->op_source = 0; if (fs->op_q == NULL) return FALSE; op = g_queue_peek_head(fs->op_q); if (op->cb == NULL) { sim_fs_end_current(fs); return FALSE; } if (op->is_read == TRUE && op->current > 0) { switch (op->structure) { case OFONO_SIM_FILE_STRUCTURE_FIXED: driver->read_file_linear(fs->sim, op->id, op->current, op->record_length, op->path_len ? op->path : NULL, op->path_len, sim_fs_op_read_record_cb, fs); break; case OFONO_SIM_FILE_STRUCTURE_CYCLIC: driver->read_file_cyclic(fs->sim, op->id, op->current, op->record_length, op->path_len ? op->path : NULL, op->path_len, sim_fs_op_read_record_cb, fs); break; case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT: default: ofono_error("Wrong file structure for reading record"); break; } } else if (op->is_read == TRUE) { if (sim_fs_op_check_cached(fs)) return FALSE; driver->read_file_info(fs->sim, op->id, op->path_len ? op->path : NULL, op->path_len, sim_fs_op_info_cb, fs); } else { switch (op->structure) { case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT: driver->write_file_transparent(fs->sim, op->id, 0, op->length, op->buffer, NULL, 0, sim_fs_op_write_cb, fs); break; case OFONO_SIM_FILE_STRUCTURE_FIXED: driver->write_file_linear(fs->sim, op->id, op->current, op->length, op->buffer, NULL, 0, sim_fs_op_write_cb, fs); break; case OFONO_SIM_FILE_STRUCTURE_CYCLIC: driver->write_file_cyclic(fs->sim, op->id, op->length, op->buffer, NULL, 0, sim_fs_op_write_cb, fs); break; default: ofono_error("Unrecognized file structure, " "this can't happen"); } g_free(op->buffer); op->buffer = NULL; } return FALSE; } int sim_fs_read_info(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, const unsigned char *path, unsigned int pth_len, ofono_sim_read_info_cb_t cb, void *data) { struct sim_fs *fs = context->fs; struct sim_fs_op *op; if (cb == NULL) return -EINVAL; if (fs->driver == NULL) return -EINVAL; if (fs->driver->read_file_info == NULL) return -ENOSYS; if (fs->op_q == NULL) fs->op_q = g_queue_new(); op = g_try_new0(struct sim_fs_op, 1); if (op == NULL) return -ENOMEM; op->id = id; op->structure = expected_type; op->cb = cb; op->userdata = data; op->is_read = TRUE; op->info_only = TRUE; op->context = context; memcpy(op->path, path, pth_len); op->path_len = pth_len; g_queue_push_tail(fs->op_q, op); if (g_queue_get_length(fs->op_q) == 1) fs->op_source = g_idle_add(sim_fs_op_next, fs); return 0; } int sim_fs_read(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, unsigned short offset, unsigned short num_bytes, const unsigned char *path, unsigned int path_len, ofono_sim_file_read_cb_t cb, void *data) { struct sim_fs *fs = context->fs; struct sim_fs_op *op; if (cb == NULL) return -EINVAL; if (fs->driver == NULL) return -EINVAL; if (fs->driver->read_file_info == NULL) { cb(0, 0, 0, NULL, 0, data); return -ENOSYS; } if (fs->op_q == NULL) fs->op_q = g_queue_new(); op = g_try_new0(struct sim_fs_op, 1); if (op == NULL) return -ENOMEM; op->id = id; op->structure = expected_type; op->cb = cb; op->userdata = data; op->is_read = TRUE; op->offset = offset; op->num_bytes = num_bytes; op->info_only = FALSE; op->context = context; memcpy(op->path, path, path_len); op->path_len = path_len; g_queue_push_tail(fs->op_q, op); if (g_queue_get_length(fs->op_q) == 1) fs->op_source = g_idle_add(sim_fs_op_next, fs); return 0; } int sim_fs_read_record(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, int record, int record_length, const unsigned char *path, unsigned int path_len, ofono_sim_file_read_cb_t cb, void *data) { struct sim_fs *fs = context->fs; struct sim_fs_op *op; if (cb == NULL) return -EINVAL; if (fs->driver == NULL) return -EINVAL; if (record < 1) return -EINVAL; if ((expected_type == OFONO_SIM_FILE_STRUCTURE_FIXED && fs->driver->read_file_linear == NULL) || (expected_type == OFONO_SIM_FILE_STRUCTURE_CYCLIC && fs->driver->read_file_cyclic == NULL)) { cb(0, 0, 0, NULL, 0, data); return -ENOSYS; } if (fs->op_q == NULL) fs->op_q = g_queue_new(); op = g_try_new0(struct sim_fs_op, 1); if (op == NULL) return -ENOMEM; op->id = id; op->structure = expected_type; op->cb = cb; op->userdata = data; op->is_read = TRUE; op->info_only = FALSE; op->context = context; op->record_length = record_length; op->current = record; memcpy(op->path, path, path_len); op->path_len = path_len; g_queue_push_tail(fs->op_q, op); if (g_queue_get_length(fs->op_q) == 1) fs->op_source = g_idle_add(sim_fs_op_next, fs); return 0; } int sim_fs_write(struct ofono_sim_context *context, int id, ofono_sim_file_write_cb_t cb, enum ofono_sim_file_structure structure, int record, const unsigned char *data, int length, void *userdata) { struct sim_fs *fs = context->fs; struct sim_fs_op *op; gconstpointer fn = NULL; if (cb == NULL) return -EINVAL; if (fs->driver == NULL) return -EINVAL; switch (structure) { case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT: fn = fs->driver->write_file_transparent; break; case OFONO_SIM_FILE_STRUCTURE_FIXED: fn = fs->driver->write_file_linear; break; case OFONO_SIM_FILE_STRUCTURE_CYCLIC: fn = fs->driver->write_file_cyclic; break; default: ofono_error("Unrecognized file structure, this can't happen"); } if (fn == NULL) return -ENOSYS; if (fs->op_q == NULL) fs->op_q = g_queue_new(); op = g_try_new0(struct sim_fs_op, 1); if (op == NULL) return -ENOMEM; op->id = id; op->cb = cb; op->userdata = userdata; op->is_read = FALSE; op->buffer = g_memdup(data, length); op->structure = structure; op->length = length; op->current = record; op->context = context; g_queue_push_tail(fs->op_q, op); if (g_queue_get_length(fs->op_q) == 1) fs->op_source = g_idle_add(sim_fs_op_next, fs); return 0; } void sim_fs_cache_image(struct sim_fs *fs, const char *image, int id) { const char *imsi; enum ofono_sim_phase phase; if (fs == NULL || image == NULL) return; imsi = ofono_sim_get_imsi(fs->sim); if (imsi == NULL) return; phase = ofono_sim_get_phase(fs->sim); if (phase == OFONO_SIM_PHASE_UNKNOWN) return; write_file((const unsigned char *) image, strlen(image), SIM_CACHE_MODE, SIM_IMAGE_CACHE_PATH, imsi, phase, id); } char *sim_fs_get_cached_image(struct sim_fs *fs, int id) { const char *imsi; enum ofono_sim_phase phase; unsigned short image_length; int fd; char *buffer; char *path; int len; struct stat st_buf; if (fs == NULL) return NULL; imsi = ofono_sim_get_imsi(fs->sim); if (imsi == NULL) return NULL; phase = ofono_sim_get_phase(fs->sim); if (phase == OFONO_SIM_PHASE_UNKNOWN) return NULL; path = g_strdup_printf(SIM_IMAGE_CACHE_PATH, imsi, phase, id); TFR(stat(path, &st_buf)); fd = TFR(open(path, O_RDONLY)); g_free(path); if (fd < 0) return NULL; image_length = st_buf.st_size; buffer = g_try_new0(char, image_length + 1); if (buffer == NULL) { TFR(close(fd)); return NULL; } len = TFR(read(fd, buffer, image_length)); TFR(close(fd)); if (len != image_length) { g_free(buffer); return NULL; } return buffer; } static void remove_cachefile(const char *imsi, enum ofono_sim_phase phase, const struct dirent *file) { int id; char *path; if (file->d_type != DT_REG) return; if (sscanf(file->d_name, "%4x", &id) != 1) return; path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, id); remove(path); g_free(path); } static void remove_imagefile(const char *imsi, enum ofono_sim_phase phase, const struct dirent *file) { int id; char *path; if (file->d_type != DT_REG) return; if (sscanf(file->d_name, "%d", &id) != 1) return; path = g_strdup_printf(SIM_IMAGE_CACHE_PATH, imsi, phase, id); remove(path); g_free(path); } void sim_fs_check_version(struct sim_fs *fs) { const char *imsi = ofono_sim_get_imsi(fs->sim); enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim); unsigned char version; if (imsi == NULL || phase == OFONO_SIM_PHASE_UNKNOWN) return; if (read_file(&version, 1, SIM_CACHE_VERSION, imsi, phase) == 1) if (version == SIM_FS_VERSION) return; sim_fs_cache_flush(fs); version = SIM_FS_VERSION; write_file(&version, 1, SIM_CACHE_MODE, SIM_CACHE_VERSION, imsi, phase); } void sim_fs_cache_flush(struct sim_fs *fs) { const char *imsi = ofono_sim_get_imsi(fs->sim); enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim); char *path = g_strdup_printf(SIM_CACHE_BASEPATH, imsi, phase); struct dirent **entries; int len = scandir(path, &entries, NULL, alphasort); g_free(path); if (len > 0) { /* Remove all file ids */ while (len--) { remove_cachefile(imsi, phase, entries[len]); g_free(entries[len]); } g_free(entries); } sim_fs_image_cache_flush(fs); } void sim_fs_cache_flush_file(struct sim_fs *fs, int id) { const char *imsi = ofono_sim_get_imsi(fs->sim); enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim); char *path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, id); remove(path); g_free(path); } void sim_fs_image_cache_flush(struct sim_fs *fs) { const char *imsi = ofono_sim_get_imsi(fs->sim); enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim); char *path = g_strdup_printf(SIM_IMAGE_CACHE_BASEPATH, imsi, phase); struct dirent **entries; int len = scandir(path, &entries, NULL, alphasort); g_free(path); if (len <= 0) return; /* Remove everything */ while (len--) { remove_imagefile(imsi, phase, entries[len]); g_free(entries[len]); } g_free(entries); } void sim_fs_image_cache_flush_file(struct sim_fs *fs, int id) { const char *imsi = ofono_sim_get_imsi(fs->sim); enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim); char *path = g_strdup_printf(SIM_IMAGE_CACHE_PATH, imsi, phase, id); remove(path); g_free(path); } ofono-1.17.bzr6912+16.04.20160314.3/src/dbus.c0000644000015600001650000003161412671500024020274 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include "ofono.h" #define OFONO_ERROR_INTERFACE "org.ofono.Error" static DBusConnection *g_connection; struct error_mapping_entry { int error; DBusMessage *(*ofono_error_func)(DBusMessage *); }; struct error_mapping_entry cme_errors_mapping[] = { { 3, __ofono_error_not_allowed }, { 4, __ofono_error_not_supported }, { 16, __ofono_error_incorrect_password }, { 30, __ofono_error_not_registered }, { 31, __ofono_error_timed_out }, { 32, __ofono_error_access_denied }, { 50, __ofono_error_invalid_args }, }; static void append_variant(DBusMessageIter *iter, int type, const void *value) { char sig[2]; DBusMessageIter valueiter; sig[0] = type; sig[1] = 0; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &valueiter); dbus_message_iter_append_basic(&valueiter, type, value); dbus_message_iter_close_container(iter, &valueiter); } void ofono_dbus_dict_append(DBusMessageIter *dict, const char *key, int type, const void *value) { DBusMessageIter keyiter; if (type == DBUS_TYPE_STRING) { const char *str = *((const char **) value); if (str == NULL) return; } dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &keyiter); dbus_message_iter_append_basic(&keyiter, DBUS_TYPE_STRING, &key); append_variant(&keyiter, type, value); dbus_message_iter_close_container(dict, &keyiter); } static void append_array_variant(DBusMessageIter *iter, int type, const void *val) { DBusMessageIter variant, array; char typesig[2]; char arraysig[3]; const char **str_array = *(const char ***) val; int i; arraysig[0] = DBUS_TYPE_ARRAY; arraysig[1] = typesig[0] = type; arraysig[2] = typesig[1] = '\0'; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, arraysig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, typesig, &array); for (i = 0; str_array[i]; i++) dbus_message_iter_append_basic(&array, type, &(str_array[i])); dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(iter, &variant); } void ofono_dbus_dict_append_array(DBusMessageIter *dict, const char *key, int type, const void *val) { DBusMessageIter entry; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); append_array_variant(&entry, type, val); dbus_message_iter_close_container(dict, &entry); } static void append_dict_variant(DBusMessageIter *iter, int type, const void *val) { DBusMessageIter variant, array, entry; char typesig[5]; char arraysig[6]; const void **val_array = *(const void ***) val; int i; arraysig[0] = DBUS_TYPE_ARRAY; arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR; arraysig[2] = typesig[1] = DBUS_TYPE_STRING; arraysig[3] = typesig[2] = type; arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR; arraysig[5] = typesig[4] = '\0'; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, arraysig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, typesig, &array); for (i = 0; val_array[i]; i += 2) { dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &(val_array[i + 0])); /* * D-Bus expects a char** or uint8* depending on the type * given. Since we are dealing with an array through a void** * (and thus val_array[i] is a pointer) we need to * differentiate DBUS_TYPE_STRING from the others. The other * option would be the user to pass the exact type to this * function, instead of a pointer to it. However in this case * a cast from type to void* would be needed, which is not * good. */ if (type == DBUS_TYPE_STRING) { dbus_message_iter_append_basic(&entry, type, &(val_array[i + 1])); } else { dbus_message_iter_append_basic(&entry, type, val_array[i + 1]); } dbus_message_iter_close_container(&array, &entry); } dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(iter, &variant); } void ofono_dbus_dict_append_dict(DBusMessageIter *dict, const char *key, int type, const void *val) { DBusMessageIter entry; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); append_dict_variant(&entry, type, val); dbus_message_iter_close_container(dict, &entry); } int ofono_dbus_signal_property_changed(DBusConnection *conn, const char *path, const char *interface, const char *name, int type, const void *value) { DBusMessage *signal; DBusMessageIter iter; signal = dbus_message_new_signal(path, interface, "PropertyChanged"); if (signal == NULL) { ofono_error("Unable to allocate new %s.PropertyChanged signal", interface); return -1; } dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); append_variant(&iter, type, value); return g_dbus_send_message(conn, signal); } int ofono_dbus_signal_array_property_changed(DBusConnection *conn, const char *path, const char *interface, const char *name, int type, const void *value) { DBusMessage *signal; DBusMessageIter iter; signal = dbus_message_new_signal(path, interface, "PropertyChanged"); if (signal == NULL) { ofono_error("Unable to allocate new %s.PropertyChanged signal", interface); return -1; } dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); append_array_variant(&iter, type, value); return g_dbus_send_message(conn, signal); } int ofono_dbus_signal_dict_property_changed(DBusConnection *conn, const char *path, const char *interface, const char *name, int type, const void *value) { DBusMessage *signal; DBusMessageIter iter; signal = dbus_message_new_signal(path, interface, "PropertyChanged"); if (signal == NULL) { ofono_error("Unable to allocate new %s.PropertyChanged signal", interface); return -1; } dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); append_dict_variant(&iter, type, value); return g_dbus_send_message(conn, signal); } DBusMessage *__ofono_error_invalid_args(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); } DBusMessage *__ofono_error_invalid_format(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".InvalidFormat", "Argument format is not recognized"); } DBusMessage *__ofono_error_not_implemented(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotImplemented", "Implementation not provided"); } DBusMessage *__ofono_error_failed(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".Failed", "Operation failed"); } DBusMessage *__ofono_error_busy(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".InProgress", "Operation already in progress"); } DBusMessage *__ofono_error_not_found(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotFound", "Object is not found or not valid for this operation"); } DBusMessage *__ofono_error_not_active(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotActive", "Operation is not active or in progress"); } DBusMessage *__ofono_error_not_supported(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotSupported", "Operation is not supported by the" " network / modem"); } DBusMessage *__ofono_error_not_available(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotAvailable", "Operation currently not available"); } DBusMessage *__ofono_error_timed_out(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".Timedout", "Operation failure due to timeout"); } DBusMessage *__ofono_error_sim_not_ready(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".SimNotReady", "SIM is not ready or not inserted"); } DBusMessage *__ofono_error_in_use(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".InUse", "The resource is currently in use"); } DBusMessage *__ofono_error_not_attached(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotAttached", "GPRS is not attached"); } DBusMessage *__ofono_error_attach_in_progress(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".AttachInProgress", "GPRS Attach is in progress"); } DBusMessage *__ofono_error_not_registered(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotRegistered", "Modem is not registered to the network"); } DBusMessage *__ofono_error_canceled(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".Canceled", "Operation has been canceled"); } DBusMessage *__ofono_error_access_denied(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".AccessDenied", "Operation not permitted"); } DBusMessage *__ofono_error_emergency_active(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".EmergencyActive", "Emergency mode active"); } DBusMessage *__ofono_error_incorrect_password(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".IncorrectPassword", "Password is incorrect"); } DBusMessage *__ofono_error_not_allowed(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotAllowed", "Operation is not allowed"); } DBusMessage *__ofono_error_not_recognized(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".NotRecognized", "String not recognized as USSD/SS"); } DBusMessage *__ofono_error_network_terminated(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR_INTERFACE ".Terminated", "Operation was terminated by the" " network"); } DBusMessage *__ofono_error_from_error(const struct ofono_error *error, DBusMessage *msg) { struct error_mapping_entry *e; int maxentries; int i; switch (error->type) { case OFONO_ERROR_TYPE_CME: e = cme_errors_mapping; maxentries = sizeof(cme_errors_mapping) / sizeof(struct error_mapping_entry); for (i = 0; i < maxentries; i++) if (e[i].error == error->error) return e[i].ofono_error_func(msg); break; case OFONO_ERROR_TYPE_CMS: return __ofono_error_failed(msg); case OFONO_ERROR_TYPE_CEER: return __ofono_error_failed(msg); default: return __ofono_error_failed(msg); } return __ofono_error_failed(msg); } void __ofono_dbus_pending_reply(DBusMessage **msg, DBusMessage *reply) { DBusConnection *conn = ofono_dbus_get_connection(); g_dbus_send_message(conn, reply); dbus_message_unref(*msg); *msg = NULL; } gboolean __ofono_dbus_valid_object_path(const char *path) { unsigned int i; char c = '\0'; if (path == NULL) return FALSE; if (path[0] == '\0') return FALSE; if (path[0] && !path[1] && path[0] == '/') return TRUE; if (path[0] != '/') return FALSE; for (i = 0; path[i]; i++) { if (path[i] == '/' && c == '/') return FALSE; c = path[i]; if (path[i] >= 'a' && path[i] <= 'z') continue; if (path[i] >= 'A' && path[i] <= 'Z') continue; if (path[i] >= '0' && path[i] <= '9') continue; if (path[i] == '_' || path[i] == '/') continue; return FALSE; } if (path[i-1] == '/') return FALSE; return TRUE; } DBusConnection *ofono_dbus_get_connection(void) { return g_connection; } static void dbus_gsm_set_connection(DBusConnection *conn) { if (conn && g_connection != NULL) ofono_error("Setting a connection when it is not NULL"); g_connection = conn; } int __ofono_dbus_init(DBusConnection *conn) { dbus_gsm_set_connection(conn); return 0; } void __ofono_dbus_cleanup(void) { DBusConnection *conn = ofono_dbus_get_connection(); if (conn == NULL || !dbus_connection_get_is_connected(conn)) return; dbus_gsm_set_connection(NULL); } ofono-1.17.bzr6912+16.04.20160314.3/src/stkutil.c0000644000015600001650000053371012671500024021042 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include "smsutil.h" #include "stkutil.h" #include "simutil.h" #include "util.h" enum stk_data_object_flag { DATAOBJ_FLAG_MANDATORY = 1, DATAOBJ_FLAG_MINIMUM = 2, DATAOBJ_FLAG_CR = 4, DATAOBJ_FLAG_LIST = 8, }; struct stk_file_iter { const unsigned char *start; unsigned int pos; unsigned int max; unsigned char len; const unsigned char *file; }; struct stk_tlv_builder { struct comprehension_tlv_builder ctlv; unsigned char *value; unsigned int len; unsigned int max_len; }; typedef gboolean (*dataobj_handler)(struct comprehension_tlv_iter *, void *); typedef gboolean (*dataobj_writer)(struct stk_tlv_builder *, const void *, gboolean); /* * Defined in TS 102.223 Section 8.13 * The type of gsm sms can be SMS-COMMAND AND SMS-SUBMIT. According to 23.040, * the maximum length is 164 bytes. But for SMS-SUBMIT, sms may be packed by * ME. Thus the maximum length of messsage could be 160 bytes, instead of 140 * bytes. So the total maximum length could be 184 bytes. Refer TS 31.111, * section 6.4.10 for details. */ struct gsm_sms_tpdu { unsigned int len; unsigned char tpdu[184]; }; #define CHECK_TEXT_AND_ICON(text, icon_id) \ if (status != STK_PARSE_RESULT_OK) \ return status; \ \ if ((text == NULL || text[0] == '\0') && icon_id != 0) \ status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; \ static char *decode_text(unsigned char dcs, int len, const unsigned char *data) { char *utf8; enum sms_charset charset; if (sms_dcs_decode(dcs, NULL, &charset, NULL, NULL) == FALSE) return NULL; switch (charset) { case SMS_CHARSET_7BIT: { long written; unsigned long max_to_unpack = len * 8 / 7; unsigned char *unpacked = unpack_7bit(data, len, 0, FALSE, max_to_unpack, &written, 0); if (unpacked == NULL) return NULL; utf8 = convert_gsm_to_utf8(unpacked, written, NULL, NULL, 0); g_free(unpacked); break; } case SMS_CHARSET_8BIT: utf8 = convert_gsm_to_utf8(data, len, NULL, NULL, 0); break; case SMS_CHARSET_UCS2: utf8 = g_convert((const gchar *) data, len, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); break; default: utf8 = NULL; } return utf8; } /* For data object only to indicate its existence */ static gboolean parse_dataobj_common_bool(struct comprehension_tlv_iter *iter, gboolean *out) { if (comprehension_tlv_iter_get_length(iter) != 0) return FALSE; *out = TRUE; return TRUE; } /* For data object that only has one byte */ static gboolean parse_dataobj_common_byte(struct comprehension_tlv_iter *iter, unsigned char *out) { const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); *out = data[0]; return TRUE; } /* For data object that only has text terminated by '\0' */ static gboolean parse_dataobj_common_text(struct comprehension_tlv_iter *iter, char **text) { const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); *text = g_try_malloc(len + 1); if (*text == NULL) return FALSE; memcpy(*text, data, len); (*text)[len] = '\0'; return TRUE; } /* For data object that only has a byte array with undetermined length */ static gboolean parse_dataobj_common_byte_array( struct comprehension_tlv_iter *iter, struct stk_common_byte_array *array) { const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); array->len = len; array->array = g_try_malloc(len); if (array->array == NULL) return FALSE; memcpy(array->array, data, len); return TRUE; } static void stk_file_iter_init(struct stk_file_iter *iter, const unsigned char *start, unsigned int len) { iter->start = start; iter->max = len; iter->pos = 0; } static gboolean stk_file_iter_next(struct stk_file_iter *iter) { unsigned int pos = iter->pos; const unsigned int max = iter->max; const unsigned char *start = iter->start; unsigned int i; unsigned char last_type; if (pos + 2 >= max) return FALSE; /* SIM EFs always start with ROOT MF, 0x3f */ if (start[iter->pos] != 0x3f) return FALSE; last_type = 0x3f; for (i = pos + 2; i < max; i += 2) { /* * Check the validity of file type. * According to TS 11.11, each file id contains of two bytes, * in which the first byte is the type of file. For GSM is: * 0x3f: master file * 0x7f: 1st level dedicated file * 0x5f: 2nd level dedicated file * 0x2f: elementary file under the master file * 0x6f: elementary file under 1st level dedicated file * 0x4f: elementary file under 2nd level dedicated file */ switch (start[i]) { case 0x2f: if (last_type != 0x3f) return FALSE; break; case 0x6f: if (last_type != 0x7f) return FALSE; break; case 0x4f: if (last_type != 0x5f) return FALSE; break; case 0x7f: if (last_type != 0x3f) return FALSE; break; case 0x5f: if (last_type != 0x7f) return FALSE; break; default: return FALSE; } if ((start[i] == 0x2f) || (start[i] == 0x6f) || (start[i] == 0x4f)) { if (i + 1 >= max) return FALSE; iter->file = start + pos; iter->len = i - pos + 2; iter->pos = i + 2; return TRUE; } last_type = start[i]; } return FALSE; } /* Defined in TS 102.223 Section 8.1 */ static gboolean parse_dataobj_address(struct comprehension_tlv_iter *iter, void *user) { struct stk_address *addr = user; const unsigned char *data; unsigned int len; char *number; len = comprehension_tlv_iter_get_length(iter); if (len < 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); number = g_try_malloc(len * 2 - 1); if (number == NULL) return FALSE; addr->ton_npi = data[0]; addr->number = number; sim_extract_bcd_number(data + 1, len - 1, addr->number); return TRUE; } /* Defined in TS 102.223 Section 8.2 */ static gboolean parse_dataobj_alpha_id(struct comprehension_tlv_iter *iter, void *user) { char **alpha_id = user; const unsigned char *data; unsigned int len; char *utf8; len = comprehension_tlv_iter_get_length(iter); if (len == 0) { *alpha_id = NULL; return TRUE; } data = comprehension_tlv_iter_get_data(iter); utf8 = sim_string_to_utf8(data, len); if (utf8 == NULL) return FALSE; *alpha_id = utf8; return TRUE; } /* Defined in TS 102.223 Section 8.3 */ static gboolean parse_dataobj_subaddress(struct comprehension_tlv_iter *iter, void *user) { struct stk_subaddress *subaddr = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; if (len > sizeof(subaddr->subaddr)) return FALSE; data = comprehension_tlv_iter_get_data(iter); subaddr->len = len; memcpy(subaddr->subaddr, data, len); subaddr->has_subaddr = TRUE; return TRUE; } /* Defined in TS 102.223 Section 8.4 */ static gboolean parse_dataobj_ccp(struct comprehension_tlv_iter *iter, void *user) { struct stk_ccp *ccp = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; if (len > sizeof(ccp->ccp)) return FALSE; data = comprehension_tlv_iter_get_data(iter); ccp->len = len; memcpy(ccp->ccp, data, len); return TRUE; } /* Defined in TS 31.111 Section 8.5 */ static gboolean parse_dataobj_cbs_page(struct comprehension_tlv_iter *iter, void *user) { struct stk_cbs_page *cp = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; if (len > sizeof(cp->page)) return FALSE; data = comprehension_tlv_iter_get_data(iter); cp->len = len; memcpy(cp->page, data, len); return TRUE; } /* Described in TS 102.223 Section 8.8 */ static gboolean parse_dataobj_duration(struct comprehension_tlv_iter *iter, void *user) { struct stk_duration *duration = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); if (data[0] > 0x02) return FALSE; if (data[1] == 0) return FALSE; duration->unit = data[0]; duration->interval = data[1]; return TRUE; } /* Defined in TS 102.223 Section 8.9 */ static gboolean parse_dataobj_item(struct comprehension_tlv_iter *iter, void *user) { struct stk_item *item = user; const unsigned char *data; unsigned int len; char *utf8; len = comprehension_tlv_iter_get_length(iter); if (len == 0) return TRUE; if (len == 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* The identifier is between 0x01 and 0xFF */ if (data[0] == 0) return FALSE; utf8 = sim_string_to_utf8(data + 1, len - 1); if (utf8 == NULL) return FALSE; item->id = data[0]; item->text = utf8; return TRUE; } /* Defined in TS 102.223 Section 8.10 */ static gboolean parse_dataobj_item_id(struct comprehension_tlv_iter *iter, void *user) { unsigned char *id = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); *id = data[0]; return TRUE; } /* Defined in TS 102.223 Section 8.11 */ static gboolean parse_dataobj_response_len(struct comprehension_tlv_iter *iter, void *user) { struct stk_response_length *response_len = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); response_len->min = data[0]; response_len->max = data[1]; return TRUE; } /* Defined in TS 102.223 Section 8.12 */ static gboolean parse_dataobj_result(struct comprehension_tlv_iter *iter, void *user) { struct stk_result *result = user; const unsigned char *data; unsigned int len; unsigned char *additional; len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); if ((len < 2) && ((data[0] == 0x20) || (data[0] == 0x21) || (data[0] == 0x26) || (data[0] == 0x38) || (data[0] == 0x39) || (data[0] == 0x3a) || (data[0] == 0x3c) || (data[0] == 0x3d))) return FALSE; additional = g_try_malloc(len - 1); if (additional == NULL) return FALSE; result->type = data[0]; result->additional_len = len - 1; result->additional = additional; memcpy(result->additional, data + 1, len - 1); return TRUE; } /* Defined in TS 102.223 Section 8.13 */ static gboolean parse_dataobj_gsm_sms_tpdu(struct comprehension_tlv_iter *iter, void *user) { struct gsm_sms_tpdu *tpdu = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len < 1 || len > sizeof(tpdu->tpdu)) return FALSE; data = comprehension_tlv_iter_get_data(iter); tpdu->len = len; memcpy(tpdu->tpdu, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.14 */ static gboolean parse_dataobj_ss(struct comprehension_tlv_iter *iter, void *user) { struct stk_ss *ss = user; const unsigned char *data; unsigned int len; char *s; len = comprehension_tlv_iter_get_length(iter); if (len < 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); s = g_try_malloc(len * 2 - 1); if (s == NULL) return FALSE; ss->ton_npi = data[0]; ss->ss = s; sim_extract_bcd_number(data + 1, len - 1, ss->ss); return TRUE; } /* Defined in TS 102.223 Section 8.15 */ static gboolean parse_dataobj_text(struct comprehension_tlv_iter *iter, void *user) { char **text = user; unsigned int len = comprehension_tlv_iter_get_length(iter); const unsigned char *data; char *utf8; if (len <= 1) { *text = g_try_malloc0(1); return TRUE; } data = comprehension_tlv_iter_get_data(iter); utf8 = decode_text(data[0], len - 1, data + 1); if (utf8 == NULL) return FALSE; *text = utf8; return TRUE; } /* Defined in TS 102.223 Section 8.16 */ static gboolean parse_dataobj_tone(struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.17 */ static gboolean parse_dataobj_ussd(struct comprehension_tlv_iter *iter, void *user) { struct stk_ussd_string *us = user; unsigned int len = comprehension_tlv_iter_get_length(iter); const unsigned char *data = comprehension_tlv_iter_get_data(iter); if (len <= 1 || len > 161) return FALSE; us->dcs = data[0]; us->len = len - 1; memcpy(us->string, data + 1, us->len); return TRUE; } /* Defined in TS 102.223 Section 8.18 */ static gboolean parse_dataobj_file_list(struct comprehension_tlv_iter *iter, void *user) { GSList **fl = user; const unsigned char *data; unsigned int len; struct stk_file *sf; struct stk_file_iter sf_iter; len = comprehension_tlv_iter_get_length(iter); if (len < 5) return FALSE; data = comprehension_tlv_iter_get_data(iter); stk_file_iter_init(&sf_iter, data + 1, len - 1); while (stk_file_iter_next(&sf_iter)) { sf = g_try_new0(struct stk_file, 1); if (sf == NULL) goto error; sf->len = sf_iter.len; memcpy(sf->file, sf_iter.file, sf_iter.len); *fl = g_slist_prepend(*fl, sf); } if (sf_iter.pos != sf_iter.max) goto error; *fl = g_slist_reverse(*fl); return TRUE; error: g_slist_foreach(*fl, (GFunc) g_free, NULL); g_slist_free(*fl); return FALSE; } /* Defined in TS 102.223 Section 8.19 */ static gboolean parse_dataobj_location_info(struct comprehension_tlv_iter *iter, void *user) { struct stk_location_info *li = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if ((len != 5) && (len != 7) && (len != 9)) return FALSE; data = comprehension_tlv_iter_get_data(iter); sim_parse_mcc_mnc(data, li->mcc, li->mnc); li->lac_tac = (data[3] << 8) + data[4]; if (len >= 7) { li->has_ci = TRUE; li->ci = (data[5] << 8) + data[6]; } if (len == 9) { li->has_ext_ci = TRUE; li->ext_ci = (data[7] << 8) + data[8]; } return TRUE; } /* * Defined in TS 102.223 Section 8.20. * * According to 3GPP TS 24.008, Section 10.5.1.4, IMEI is composed of * 15 digits and totally 8 bytes are used to represent it. * * Bits 1-3 of first byte represent the type of identity, and they * are 0 1 0 separately for IMEI. Bit 4 of first byte is the odd/even * indication, and it's 1 to indicate IMEI has odd number of digits (15). * The rest bytes are coded using BCD coding. * * For example, if the IMEI is "123456789012345", then it's coded as * "1A 32 54 76 98 10 32 54". */ static gboolean parse_dataobj_imei(struct comprehension_tlv_iter *iter, void *user) { char *imei = user; const unsigned char *data; unsigned int len; static const char digit_lut[] = "0123456789*#abc\0"; len = comprehension_tlv_iter_get_length(iter); if (len != 8) return FALSE; data = comprehension_tlv_iter_get_data(iter); if ((data[0] & 0x0f) != 0x0a) return FALSE; /* Assume imei is at least 16 bytes long (15 for imei + null) */ imei[0] = digit_lut[(data[0] & 0xf0) >> 4]; extract_bcd_number(data + 1, 7, imei + 1); return TRUE; } /* Defined in TS 102.223 Section 8.21 */ static gboolean parse_dataobj_help_request(struct comprehension_tlv_iter *iter, void *user) { gboolean *ret = user; return parse_dataobj_common_bool(iter, ret); } /* Defined in TS 102.223 Section 8.22 */ static gboolean parse_dataobj_network_measurement_results( struct comprehension_tlv_iter *iter, void *user) { unsigned char *nmr = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len != 0x10) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* Assume network measurement result is 16 bytes long */ memcpy(nmr, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.23 */ static gboolean parse_dataobj_default_text(struct comprehension_tlv_iter *iter, void *user) { char **text = user; unsigned int len = comprehension_tlv_iter_get_length(iter); const unsigned char *data = comprehension_tlv_iter_get_data(iter); char *utf8; /* DCS followed by some text, cannot be 1 */ if (len <= 1) return FALSE; utf8 = decode_text(data[0], len - 1, data + 1); if (utf8 == NULL) return FALSE; *text = utf8; return TRUE; } /* Defined in TS 102.223 Section 8.24 */ static gboolean parse_dataobj_items_next_action_indicator( struct comprehension_tlv_iter *iter, void *user) { struct stk_items_next_action_indicator *inai = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 1) || (len > sizeof(inai->list))) return FALSE; data = comprehension_tlv_iter_get_data(iter); inai->len = len; memcpy(inai->list, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.25 */ static gboolean parse_dataobj_event_list(struct comprehension_tlv_iter *iter, void *user) { struct stk_event_list *el = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len == 0) return TRUE; if (len > sizeof(el->list)) return FALSE; data = comprehension_tlv_iter_get_data(iter); el->len = len; memcpy(el->list, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.26 */ static gboolean parse_dataobj_cause(struct comprehension_tlv_iter *iter, void *user) { struct stk_cause *cause = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len == 1) || (len > sizeof(cause->cause))) return FALSE; cause->has_cause = TRUE; if (len == 0) return TRUE; data = comprehension_tlv_iter_get_data(iter); cause->len = len; memcpy(cause->cause, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.27 */ static gboolean parse_dataobj_location_status( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.28 */ static gboolean parse_dataobj_transaction_id( struct comprehension_tlv_iter *iter, void *user) { struct stk_transaction_id *ti = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 1) || (len > sizeof(ti->list))) return FALSE; data = comprehension_tlv_iter_get_data(iter); ti->len = len; memcpy(ti->list, data, len); return TRUE; } /* Defined in TS 31.111 Section 8.29 */ static gboolean parse_dataobj_bcch_channel_list( struct comprehension_tlv_iter *iter, void *user) { struct stk_bcch_channel_list *bcl = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); unsigned int i; if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); bcl->num = len * 8 / 10; for (i = 0; i < bcl->num; i++) { unsigned int index = i * 10 / 8; unsigned int occupied = i * 10 % 8; bcl->channels[i] = (data[index] << (2 + occupied)) + (data[index + 1] >> (6 - occupied)); } bcl->has_list = TRUE; return TRUE; } /* Defined in TS 102.223 Section 8.30 */ static gboolean parse_dataobj_call_control_requested_action( struct comprehension_tlv_iter *iter, void *user) { struct stk_common_byte_array *array = user; return parse_dataobj_common_byte_array(iter, array); } /* Defined in TS 102.223 Section 8.31 */ static gboolean parse_dataobj_icon_id(struct comprehension_tlv_iter *iter, void *user) { struct stk_icon_id *id = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); id->qualifier = data[0]; id->id = data[1]; return TRUE; } /* Defined in TS 102.223 Section 8.32 */ static gboolean parse_dataobj_item_icon_id_list( struct comprehension_tlv_iter *iter, void *user) { struct stk_item_icon_id_list *iiil = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 2) || (len > 127)) return FALSE; data = comprehension_tlv_iter_get_data(iter); iiil->qualifier = data[0]; iiil->len = len - 1; memcpy(iiil->list, data + 1, iiil->len); return TRUE; } /* Defined in TS 102.223 Section 8.33 */ static gboolean parse_dataobj_card_reader_status( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.34 */ static gboolean parse_dataobj_card_atr(struct comprehension_tlv_iter *iter, void *user) { struct stk_card_atr *ca = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 1) || (len > sizeof(ca->atr))) return FALSE; data = comprehension_tlv_iter_get_data(iter); ca->len = len; memcpy(ca->atr, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.35 */ static gboolean parse_dataobj_c_apdu(struct comprehension_tlv_iter *iter, void *user) { struct stk_c_apdu *ca = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); unsigned int pos; if ((len < 4) || (len > 241)) return FALSE; data = comprehension_tlv_iter_get_data(iter); ca->cla = data[0]; ca->ins = data[1]; ca->p1 = data[2]; ca->p2 = data[3]; pos = 4; /* * lc is 0 has the same meaning as lc is absent. But le is 0 means * the maximum number of bytes expected in the response data field * is 256. So we need to rely on has_le to know if it presents. */ if (len > 5) { ca->lc = data[4]; if (ca->lc > sizeof(ca->data)) return FALSE; pos += ca->lc + 1; if (len - pos > 1) return FALSE; memcpy(ca->data, data+5, ca->lc); } if (len - pos > 0) { ca->le = data[len - 1]; ca->has_le = TRUE; } return TRUE; } /* Defined in TS 102.223 Section 8.36 */ static gboolean parse_dataobj_r_apdu(struct comprehension_tlv_iter *iter, void *user) { struct stk_r_apdu *ra = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 2) || (len > 239)) return FALSE; data = comprehension_tlv_iter_get_data(iter); ra->sw1 = data[len-2]; ra->sw2 = data[len-1]; if (len > 2) { ra->len = len - 2; memcpy(ra->data, data, ra->len); } else ra->len = 0; return TRUE; } /* Defined in TS 102.223 Section 8.37 */ static gboolean parse_dataobj_timer_id(struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.38 */ static gboolean parse_dataobj_timer_value(struct comprehension_tlv_iter *iter, void *user) { struct stk_timer_value *tv = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 3) return FALSE; data = comprehension_tlv_iter_get_data(iter); tv->hour = sms_decode_semi_octet(data[0]); tv->minute = sms_decode_semi_octet(data[1]); tv->second = sms_decode_semi_octet(data[2]); tv->has_value = TRUE; return TRUE; } /* Defined in TS 102.223 Section 8.39 */ static gboolean parse_dataobj_datetime_timezone( struct comprehension_tlv_iter *iter, void *user) { struct sms_scts *scts = user; const unsigned char *data; int offset = 0; if (comprehension_tlv_iter_get_length(iter) != 7) return FALSE; data = comprehension_tlv_iter_get_data(iter); sms_decode_scts(data, 7, &offset, scts); return TRUE; } /* Defined in TS 102.223 Section 8.40 */ static gboolean parse_dataobj_at_command(struct comprehension_tlv_iter *iter, void *user) { char **command = user; return parse_dataobj_common_text(iter, command); } /* Defined in TS 102.223 Section 8.41 */ static gboolean parse_dataobj_at_response(struct comprehension_tlv_iter *iter, void *user) { char **response = user; return parse_dataobj_common_text(iter, response); } /* Defined in TS 102.223 Section 8.42 */ static gboolean parse_dataobj_bc_repeat_indicator( struct comprehension_tlv_iter *iter, void *user) { struct stk_bc_repeat *bc_repeat = user; if (parse_dataobj_common_byte(iter, &bc_repeat->value) != TRUE) return FALSE; bc_repeat->has_bc_repeat = TRUE; return TRUE; } /* Defined in 102.223 Section 8.43 */ static gboolean parse_dataobj_imm_resp(struct comprehension_tlv_iter *iter, void *user) { gboolean *ret = user; return parse_dataobj_common_bool(iter, ret); } /* Defined in 102.223 Section 8.44 */ static gboolean parse_dataobj_dtmf_string(struct comprehension_tlv_iter *iter, void *user) { char **dtmf = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); *dtmf = g_try_malloc(len * 2 + 1); if (*dtmf == NULL) return FALSE; sim_extract_bcd_number(data, len, *dtmf); return TRUE; } /* Defined in 102.223 Section 8.45 */ static gboolean parse_dataobj_language(struct comprehension_tlv_iter *iter, void *user) { char *lang = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len != 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* * This is a 2 character pair as defined in ISO 639, coded using * GSM default 7 bit alphabet with bit 8 set to 0. Since the english * letters have the same mapping in GSM as ASCII, no conversion * is required here */ memcpy(lang, data, len); lang[len] = '\0'; return TRUE; } /* Defined in 31.111 Section 8.46 */ static gboolean parse_dataobj_timing_advance( struct comprehension_tlv_iter *iter, void *user) { struct stk_timing_advance *ta = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len != 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); ta->has_value = TRUE; ta->status = data[0]; ta->advance = data[1]; return TRUE; } /* Defined in 102.223 Section 8.47 */ static gboolean parse_dataobj_browser_id(struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; if (parse_dataobj_common_byte(iter, byte) == FALSE || *byte > 4) return FALSE; return TRUE; } /* Defined in TS 102.223 Section 8.48 */ static gboolean parse_dataobj_url(struct comprehension_tlv_iter *iter, void *user) { char **url = user; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len == 0) { *url = NULL; return TRUE; } return parse_dataobj_common_text(iter, url); } /* Defined in TS 102.223 Section 8.49 */ static gboolean parse_dataobj_bearer(struct comprehension_tlv_iter *iter, void *user) { struct stk_common_byte_array *array = user; return parse_dataobj_common_byte_array(iter, array); } /* Defined in TS 102.223 Section 8.50 */ static gboolean parse_dataobj_provisioning_file_reference( struct comprehension_tlv_iter *iter, void *user) { struct stk_file *f = user; const unsigned char *data; struct stk_file_iter sf_iter; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len < 1) || (len > 8)) return FALSE; data = comprehension_tlv_iter_get_data(iter); stk_file_iter_init(&sf_iter, data, len); stk_file_iter_next(&sf_iter); if (sf_iter.pos != sf_iter.max) return FALSE; f->len = len; memcpy(f->file, data, len); return TRUE; } /* Defined in 102.223 Section 8.51 */ static gboolean parse_dataobj_browser_termination_cause( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.52 */ static gboolean parse_dataobj_bearer_description( struct comprehension_tlv_iter *iter, void *user) { struct stk_bearer_description *bd = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); bd->type = data[0]; /* Parse only the packet data service bearer parameters */ if (bd->type != STK_BEARER_TYPE_GPRS_UTRAN) return FALSE; if (len < 7) return FALSE; bd->gprs.precedence = data[1]; bd->gprs.delay = data[2]; bd->gprs.reliability = data[3]; bd->gprs.peak = data[4]; bd->gprs.mean = data[5]; bd->gprs.pdp_type = data[6]; return TRUE; } /* Defined in TS 102.223 Section 8.53 */ static gboolean parse_dataobj_channel_data(struct comprehension_tlv_iter *iter, void *user) { struct stk_common_byte_array *array = user; return parse_dataobj_common_byte_array(iter, array); } /* Defined in TS 102.223 Section 8.54 */ static gboolean parse_dataobj_channel_data_length( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.55 */ static gboolean parse_dataobj_buffer_size(struct comprehension_tlv_iter *iter, void *user) { unsigned short *size = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); *size = (data[0] << 8) + data[1]; return TRUE; } /* Defined in TS 102.223 Section 8.56 */ static gboolean parse_dataobj_channel_status( struct comprehension_tlv_iter *iter, void *user) { unsigned char *status = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* Assume channel status is 2 bytes long */ memcpy(status, data, 2); return TRUE; } /* Defined in TS 102.223 Section 8.57 */ static gboolean parse_dataobj_card_reader_id( struct comprehension_tlv_iter *iter, void *user) { struct stk_card_reader_id *cr_id = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); cr_id->len = len; memcpy(cr_id->id, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.58 */ static gboolean parse_dataobj_other_address( struct comprehension_tlv_iter *iter, void *user) { struct stk_other_address *oa = user; const unsigned char *data; unsigned char len = comprehension_tlv_iter_get_length(iter); if (len == 0) { oa->type = STK_ADDRESS_AUTO; return TRUE; } if ((len != 5) && (len != 17)) return FALSE; data = comprehension_tlv_iter_get_data(iter); if (data[0] != STK_ADDRESS_IPV4 && data[0] != STK_ADDRESS_IPV6) return FALSE; oa->type = data[0]; if (oa->type == STK_ADDRESS_IPV4) memcpy(&oa->addr.ipv4, data + 1, 4); else memcpy(&oa->addr.ipv6, data + 1, 16); return TRUE; } /* Defined in TS 102.223 Section 8.59 */ static gboolean parse_dataobj_uicc_te_interface( struct comprehension_tlv_iter *iter, void *user) { struct stk_uicc_te_interface *uti = user; const unsigned char *data; unsigned char len = comprehension_tlv_iter_get_length(iter); if (len != 3) return FALSE; data = comprehension_tlv_iter_get_data(iter); uti->protocol = data[0]; uti->port = (data[1] << 8) + data[2]; return TRUE; } /* Defined in TS 102.223 Section 8.60 */ static gboolean parse_dataobj_aid(struct comprehension_tlv_iter *iter, void *user) { struct stk_aid *aid = user; const unsigned char *data; unsigned char len = comprehension_tlv_iter_get_length(iter); if ((len > 16) || (len < 12)) return FALSE; data = comprehension_tlv_iter_get_data(iter); aid->len = len; memcpy(aid->aid, data, len); return TRUE; } /* * Defined in TS 102.223 Section 8.61. According to it, the technology field * can have at most 127 bytes. However, all the defined values are only 1 byte, * so we just use 1 byte to represent it. */ static gboolean parse_dataobj_access_technology( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.62 */ static gboolean parse_dataobj_display_parameters( struct comprehension_tlv_iter *iter, void *user) { struct stk_display_parameters *dp = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 3) return FALSE; data = comprehension_tlv_iter_get_data(iter); dp->height = data[0]; dp->width = data[1]; dp->effects = data[2]; return TRUE; } /* Defined in TS 102.223 Section 8.63 */ static gboolean parse_dataobj_service_record( struct comprehension_tlv_iter *iter, void *user) { struct stk_service_record *sr = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len < 3) return FALSE; data = comprehension_tlv_iter_get_data(iter); sr->tech_id = data[0]; sr->serv_id = data[1]; sr->len = len - 2; sr->serv_rec = g_try_malloc(sr->len); if (sr->serv_rec == NULL) return FALSE; memcpy(sr->serv_rec, data + 2, sr->len); return TRUE; } /* Defined in TS 102.223 Section 8.64 */ static gboolean parse_dataobj_device_filter(struct comprehension_tlv_iter *iter, void *user) { struct stk_device_filter *df = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* According to TS 102.223, everything except BT & IRDA is RFU */ if (data[0] != STK_TECHNOLOGY_BLUETOOTH && data[0] != STK_TECHNOLOGY_IRDA) return FALSE; df->tech_id = data[0]; df->len = len - 1; df->dev_filter = g_try_malloc(df->len); if (df->dev_filter == NULL) return FALSE; memcpy(df->dev_filter, data + 1, df->len); return TRUE; } /* Defined in TS 102.223 Section 8.65 */ static gboolean parse_dataobj_service_search( struct comprehension_tlv_iter *iter, void *user) { struct stk_service_search *ss = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* According to TS 102.223, everything except BT & IRDA is RFU */ if (data[0] != STK_TECHNOLOGY_BLUETOOTH && data[0] != STK_TECHNOLOGY_IRDA) return FALSE; ss->tech_id = data[0]; ss->len = len - 1; ss->ser_search = g_try_malloc(ss->len); if (ss->ser_search == NULL) return FALSE; memcpy(ss->ser_search, data + 1, ss->len); return TRUE; } /* Defined in TS 102.223 Section 8.66 */ static gboolean parse_dataobj_attribute_info( struct comprehension_tlv_iter *iter, void *user) { struct stk_attribute_info *ai = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* According to TS 102.223, everything except BT & IRDA is RFU */ if (data[0] != STK_TECHNOLOGY_BLUETOOTH && data[0] != STK_TECHNOLOGY_IRDA) return FALSE; ai->tech_id = data[0]; ai->len = len - 1; ai->attr_info = g_try_malloc(ai->len); if (ai->attr_info == NULL) return FALSE; memcpy(ai->attr_info, data + 1, ai->len); return TRUE; } /* Defined in TS 102.223 Section 8.67 */ static gboolean parse_dataobj_service_availability( struct comprehension_tlv_iter *iter, void *user) { struct stk_common_byte_array *array = user; return parse_dataobj_common_byte_array(iter, array); } /* Defined in TS 102.223 Section 8.68 */ static gboolean parse_dataobj_remote_entity_address( struct comprehension_tlv_iter *iter, void *user) { struct stk_remote_entity_address *rea = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); data = comprehension_tlv_iter_get_data(iter); switch (data[0]) { case 0x00: if (len != 7) return FALSE; break; case 0x01: if (len != 5) return FALSE; break; default: return FALSE; } rea->has_address = TRUE; rea->coding_type = data[0]; memcpy(&rea->addr, data + 1, len - 1); return TRUE; } /* Defined in TS 102.223 Section 8.69 */ static gboolean parse_dataobj_esn(struct comprehension_tlv_iter *iter, void *user) { unsigned char *esn = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len != 4) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* Assume esn is 4 bytes long */ memcpy(esn, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.70 */ static gboolean parse_dataobj_network_access_name( struct comprehension_tlv_iter *iter, void *user) { char **apn = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); unsigned char label_size; unsigned char offset = 0; char decoded_apn[100]; if (len == 0 || len > 100) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* * As specified in TS 23 003 Section 9 * The APN consists of one or more labels. Each label is coded as * a one octet length field followed by that number of octets coded * as 8 bit ASCII characters */ while (len) { label_size = *data; if (label_size == 0 || label_size > (len - 1)) return FALSE; memcpy(decoded_apn + offset, data + 1, label_size); data += label_size + 1; offset += label_size; len -= label_size + 1; if (len) decoded_apn[offset++] = '.'; } decoded_apn[offset] = '\0'; *apn = g_strdup(decoded_apn); return TRUE; } /* Defined in TS 102.223 Section 8.71 */ static gboolean parse_dataobj_cdma_sms_tpdu(struct comprehension_tlv_iter *iter, void *user) { struct stk_common_byte_array *array = user; return parse_dataobj_common_byte_array(iter, array); } /* Defined in TS 102.223 Section 8.72 */ static gboolean parse_dataobj_text_attr(struct comprehension_tlv_iter *iter, void *user) { struct stk_text_attribute *attr = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len > sizeof(attr->attributes)) return FALSE; data = comprehension_tlv_iter_get_data(iter); memcpy(attr->attributes, data, len); attr->len = len; return TRUE; } /* Defined in TS 31.111 Section 8.72 */ static gboolean parse_dataobj_pdp_act_par( struct comprehension_tlv_iter *iter, void *user) { struct stk_pdp_act_par *pcap = user; const unsigned char *data; unsigned int len; len = comprehension_tlv_iter_get_length(iter); if (len > sizeof(pcap->par)) return FALSE; data = comprehension_tlv_iter_get_data(iter); memcpy(pcap->par, data, len); pcap->len = len; return TRUE; } /* Defined in TS 102.223 Section 8.73 */ static gboolean parse_dataobj_item_text_attribute_list( struct comprehension_tlv_iter *iter, void *user) { struct stk_item_text_attribute_list *ital = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if ((len > sizeof(ital->list)) || (len % 4 != 0)) return FALSE; data = comprehension_tlv_iter_get_data(iter); memcpy(ital->list, data, len); ital->len = len; return TRUE; } /* Defined in TS 31.111 Section 8.73 */ static gboolean parse_dataobj_utran_meas_qualifier( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* * Defined in TS 102.223 Section 8.74. * * According to 3GPP TS 24.008, Section 10.5.1.4, IMEISV is composed of * 16 digits and totally 9 bytes are used to represent it. * * Bits 1-3 of first byte represent the type of identity, and they * are 0 1 1 separately for IMEISV. Bit 4 of first byte is the odd/even * indication, and it's 0 to indicate IMEISV has odd number of digits (16). * The rest bytes are coded using BCD coding. * * For example, if the IMEISV is "1234567890123456", then it's coded as * "13 32 54 76 98 10 32 54 F6". */ static gboolean parse_dataobj_imeisv(struct comprehension_tlv_iter *iter, void *user) { char *imeisv = user; const unsigned char *data; unsigned int len; static const char digit_lut[] = "0123456789*#abc\0"; len = comprehension_tlv_iter_get_length(iter); if (len != 9) return FALSE; data = comprehension_tlv_iter_get_data(iter); if ((data[0] & 0x0f) != 0x03) return FALSE; if (data[8] >> 4 != 0x0f) return FALSE; /* Assume imeisv is at least 17 bytes long (16 for imeisv + null) */ imeisv[0] = digit_lut[data[0] >> 4]; extract_bcd_number(data + 1, 7, imeisv + 1); imeisv[15] = digit_lut[data[8] & 0x0f]; imeisv[16] = '\0'; return TRUE; } /* Defined in TS 102.223 Section 8.75 */ static gboolean parse_dataobj_network_search_mode( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.76 */ static gboolean parse_dataobj_battery_state(struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.77 */ static gboolean parse_dataobj_browsing_status( struct comprehension_tlv_iter *iter, void *user) { struct stk_common_byte_array *array = user; return parse_dataobj_common_byte_array(iter, array); } /* Defined in TS 102.223 Section 8.78 */ static gboolean parse_dataobj_frame_layout(struct comprehension_tlv_iter *iter, void *user) { struct stk_frame_layout *fl = user; const unsigned char *data; unsigned char len = comprehension_tlv_iter_get_length(iter); if (len < 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); if (data[0] != STK_LAYOUT_HORIZONTAL && data[0] != STK_LAYOUT_VERTICAL) return FALSE; fl->layout = data[0]; fl->len = len - 1; memcpy(fl->size, data + 1, fl->len); return TRUE; } /* Defined in TS 102.223 Section 8.79 */ static gboolean parse_dataobj_frames_info(struct comprehension_tlv_iter *iter, void *user) { struct stk_frames_info *fi = user; const unsigned char *data; unsigned char len = comprehension_tlv_iter_get_length(iter); unsigned int i; if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); if (data[0] > 0x0f) return FALSE; if ((len == 1 && data[0] != 0) || (len > 1 && data[0] == 0)) return FALSE; if (len % 2 == 0) return FALSE; if (len == 1) return TRUE; fi->id = data[0]; fi->len = (len - 1) / 2; for (i = 0; i < len; i++) { fi->list[i].height = data[i * 2 + 1] & 0x1f; fi->list[i].width = data[i * 2 + 2] & 0x7f; } return TRUE; } /* Defined in TS 102.223 Section 8.80 */ static gboolean parse_dataobj_frame_id(struct comprehension_tlv_iter *iter, void *user) { struct stk_frame_id *fi = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); if (data[0] >= 0x10) return FALSE; fi->has_id = TRUE; fi->id = data[0]; return TRUE; } /* Defined in TS 102.223 Section 8.81 */ static gboolean parse_dataobj_meid(struct comprehension_tlv_iter *iter, void *user) { unsigned char *meid = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 8) return FALSE; data = comprehension_tlv_iter_get_data(iter); /* Assume meid is 8 bytes long */ memcpy(meid, data, 8); return TRUE; } /* Defined in TS 102.223 Section 8.82 */ static gboolean parse_dataobj_mms_reference(struct comprehension_tlv_iter *iter, void *user) { struct stk_mms_reference *mr = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); mr->len = len; memcpy(mr->ref, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.83 */ static gboolean parse_dataobj_mms_id(struct comprehension_tlv_iter *iter, void *user) { struct stk_mms_id *mi = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); mi->len = len; memcpy(mi->id, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.84 */ static gboolean parse_dataobj_mms_transfer_status( struct comprehension_tlv_iter *iter, void *user) { struct stk_mms_transfer_status *mts = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); mts->len = len; memcpy(mts->status, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.85 */ static gboolean parse_dataobj_mms_content_id( struct comprehension_tlv_iter *iter, void *user) { struct stk_mms_content_id *mci = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); mci->len = len; memcpy(mci->id, data, len); return TRUE; } /* Defined in TS 102.223 Section 8.86 */ static gboolean parse_dataobj_mms_notification( struct comprehension_tlv_iter *iter, void *user) { struct stk_common_byte_array *array = user; return parse_dataobj_common_byte_array(iter, array); } /* Defined in TS 102.223 Section 8.87 */ static gboolean parse_dataobj_last_envelope(struct comprehension_tlv_iter *iter, void *user) { gboolean *ret = user; return parse_dataobj_common_bool(iter, ret); } /* Defined in TS 102.223 Section 8.88 */ static gboolean parse_dataobj_registry_application_data( struct comprehension_tlv_iter *iter, void *user) { struct stk_registry_application_data *rad = user; const unsigned char *data; char *utf8; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 5) return FALSE; data = comprehension_tlv_iter_get_data(iter); utf8 = decode_text(data[2], len - 4, data + 4); if (utf8 == NULL) return FALSE; rad->name = utf8; rad->port = (data[0] << 8) + data[1]; rad->type = data[3]; return TRUE; } /* Defined in TS 102.223 Section 8.89 */ static gboolean parse_dataobj_activate_descriptor( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; const unsigned char *data; if (comprehension_tlv_iter_get_length(iter) != 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); if (data[0] != 0x01) return FALSE; *byte = data[0]; return TRUE; } /* Defined in TS 102.223 Section 8.90 */ static gboolean parse_dataobj_broadcast_network_info( struct comprehension_tlv_iter *iter, void *user) { struct stk_broadcast_network_information *bni = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 2) return FALSE; data = comprehension_tlv_iter_get_data(iter); if (data[0] > 0x03) return FALSE; bni->tech = data[0]; bni->len = len - 1; memcpy(bni->loc_info, data + 1, bni->len); return TRUE; } static dataobj_handler handler_for_type(enum stk_data_object_type type) { switch (type) { case STK_DATA_OBJECT_TYPE_ADDRESS: return parse_dataobj_address; case STK_DATA_OBJECT_TYPE_ALPHA_ID: return parse_dataobj_alpha_id; case STK_DATA_OBJECT_TYPE_SUBADDRESS: return parse_dataobj_subaddress; case STK_DATA_OBJECT_TYPE_CCP: return parse_dataobj_ccp; case STK_DATA_OBJECT_TYPE_CBS_PAGE: return parse_dataobj_cbs_page; case STK_DATA_OBJECT_TYPE_DURATION: return parse_dataobj_duration; case STK_DATA_OBJECT_TYPE_ITEM: return parse_dataobj_item; case STK_DATA_OBJECT_TYPE_ITEM_ID: return parse_dataobj_item_id; case STK_DATA_OBJECT_TYPE_RESPONSE_LENGTH: return parse_dataobj_response_len; case STK_DATA_OBJECT_TYPE_RESULT: return parse_dataobj_result; case STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU: return parse_dataobj_gsm_sms_tpdu; case STK_DATA_OBJECT_TYPE_SS_STRING: return parse_dataobj_ss; case STK_DATA_OBJECT_TYPE_TEXT: return parse_dataobj_text; case STK_DATA_OBJECT_TYPE_TONE: return parse_dataobj_tone; case STK_DATA_OBJECT_TYPE_USSD_STRING: return parse_dataobj_ussd; case STK_DATA_OBJECT_TYPE_FILE_LIST: return parse_dataobj_file_list; case STK_DATA_OBJECT_TYPE_LOCATION_INFO: return parse_dataobj_location_info; case STK_DATA_OBJECT_TYPE_IMEI: return parse_dataobj_imei; case STK_DATA_OBJECT_TYPE_HELP_REQUEST: return parse_dataobj_help_request; case STK_DATA_OBJECT_TYPE_NETWORK_MEASUREMENT_RESULTS: return parse_dataobj_network_measurement_results; case STK_DATA_OBJECT_TYPE_DEFAULT_TEXT: return parse_dataobj_default_text; case STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR: return parse_dataobj_items_next_action_indicator; case STK_DATA_OBJECT_TYPE_EVENT_LIST: return parse_dataobj_event_list; case STK_DATA_OBJECT_TYPE_CAUSE: return parse_dataobj_cause; case STK_DATA_OBJECT_TYPE_LOCATION_STATUS: return parse_dataobj_location_status; case STK_DATA_OBJECT_TYPE_TRANSACTION_ID: return parse_dataobj_transaction_id; case STK_DATA_OBJECT_TYPE_BCCH_CHANNEL_LIST: return parse_dataobj_bcch_channel_list; case STK_DATA_OBJECT_TYPE_CALL_CONTROL_REQUESTED_ACTION: return parse_dataobj_call_control_requested_action; case STK_DATA_OBJECT_TYPE_ICON_ID: return parse_dataobj_icon_id; case STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST: return parse_dataobj_item_icon_id_list; case STK_DATA_OBJECT_TYPE_CARD_READER_STATUS: return parse_dataobj_card_reader_status; case STK_DATA_OBJECT_TYPE_CARD_ATR: return parse_dataobj_card_atr; case STK_DATA_OBJECT_TYPE_C_APDU: return parse_dataobj_c_apdu; case STK_DATA_OBJECT_TYPE_R_APDU: return parse_dataobj_r_apdu; case STK_DATA_OBJECT_TYPE_TIMER_ID: return parse_dataobj_timer_id; case STK_DATA_OBJECT_TYPE_TIMER_VALUE: return parse_dataobj_timer_value; case STK_DATA_OBJECT_TYPE_DATETIME_TIMEZONE: return parse_dataobj_datetime_timezone; case STK_DATA_OBJECT_TYPE_AT_COMMAND: return parse_dataobj_at_command; case STK_DATA_OBJECT_TYPE_AT_RESPONSE: return parse_dataobj_at_response; case STK_DATA_OBJECT_TYPE_BC_REPEAT_INDICATOR: return parse_dataobj_bc_repeat_indicator; case STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE: return parse_dataobj_imm_resp; case STK_DATA_OBJECT_TYPE_DTMF_STRING: return parse_dataobj_dtmf_string; case STK_DATA_OBJECT_TYPE_LANGUAGE: return parse_dataobj_language; case STK_DATA_OBJECT_TYPE_BROWSER_ID: return parse_dataobj_browser_id; case STK_DATA_OBJECT_TYPE_TIMING_ADVANCE: return parse_dataobj_timing_advance; case STK_DATA_OBJECT_TYPE_URL: return parse_dataobj_url; case STK_DATA_OBJECT_TYPE_BEARER: return parse_dataobj_bearer; case STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF: return parse_dataobj_provisioning_file_reference; case STK_DATA_OBJECT_TYPE_BROWSER_TERMINATION_CAUSE: return parse_dataobj_browser_termination_cause; case STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION: return parse_dataobj_bearer_description; case STK_DATA_OBJECT_TYPE_CHANNEL_DATA: return parse_dataobj_channel_data; case STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH: return parse_dataobj_channel_data_length; case STK_DATA_OBJECT_TYPE_BUFFER_SIZE: return parse_dataobj_buffer_size; case STK_DATA_OBJECT_TYPE_CHANNEL_STATUS: return parse_dataobj_channel_status; case STK_DATA_OBJECT_TYPE_CARD_READER_ID: return parse_dataobj_card_reader_id; case STK_DATA_OBJECT_TYPE_OTHER_ADDRESS: return parse_dataobj_other_address; case STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE: return parse_dataobj_uicc_te_interface; case STK_DATA_OBJECT_TYPE_AID: return parse_dataobj_aid; case STK_DATA_OBJECT_TYPE_ACCESS_TECHNOLOGY: return parse_dataobj_access_technology; case STK_DATA_OBJECT_TYPE_DISPLAY_PARAMETERS: return parse_dataobj_display_parameters; case STK_DATA_OBJECT_TYPE_SERVICE_RECORD: return parse_dataobj_service_record; case STK_DATA_OBJECT_TYPE_DEVICE_FILTER: return parse_dataobj_device_filter; case STK_DATA_OBJECT_TYPE_SERVICE_SEARCH: return parse_dataobj_service_search; case STK_DATA_OBJECT_TYPE_ATTRIBUTE_INFO: return parse_dataobj_attribute_info; case STK_DATA_OBJECT_TYPE_SERVICE_AVAILABILITY: return parse_dataobj_service_availability; case STK_DATA_OBJECT_TYPE_REMOTE_ENTITY_ADDRESS: return parse_dataobj_remote_entity_address; case STK_DATA_OBJECT_TYPE_ESN: return parse_dataobj_esn; case STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME: return parse_dataobj_network_access_name; case STK_DATA_OBJECT_TYPE_CDMA_SMS_TPDU: return parse_dataobj_cdma_sms_tpdu; case STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE: return parse_dataobj_text_attr; case STK_DATA_OBJECT_TYPE_PDP_ACTIVATION_PARAMETER: return parse_dataobj_pdp_act_par; case STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST: return parse_dataobj_item_text_attribute_list; case STK_DATA_OBJECT_TYPE_UTRAN_MEASUREMENT_QUALIFIER: return parse_dataobj_utran_meas_qualifier; case STK_DATA_OBJECT_TYPE_IMEISV: return parse_dataobj_imeisv; case STK_DATA_OBJECT_TYPE_NETWORK_SEARCH_MODE: return parse_dataobj_network_search_mode; case STK_DATA_OBJECT_TYPE_BATTERY_STATE: return parse_dataobj_battery_state; case STK_DATA_OBJECT_TYPE_BROWSING_STATUS: return parse_dataobj_browsing_status; case STK_DATA_OBJECT_TYPE_FRAME_LAYOUT: return parse_dataobj_frame_layout; case STK_DATA_OBJECT_TYPE_FRAMES_INFO: return parse_dataobj_frames_info; case STK_DATA_OBJECT_TYPE_FRAME_ID: return parse_dataobj_frame_id; case STK_DATA_OBJECT_TYPE_MEID: return parse_dataobj_meid; case STK_DATA_OBJECT_TYPE_MMS_REFERENCE: return parse_dataobj_mms_reference; case STK_DATA_OBJECT_TYPE_MMS_ID: return parse_dataobj_mms_id; case STK_DATA_OBJECT_TYPE_MMS_TRANSFER_STATUS: return parse_dataobj_mms_transfer_status; case STK_DATA_OBJECT_TYPE_MMS_CONTENT_ID: return parse_dataobj_mms_content_id; case STK_DATA_OBJECT_TYPE_MMS_NOTIFICATION: return parse_dataobj_mms_notification; case STK_DATA_OBJECT_TYPE_LAST_ENVELOPE: return parse_dataobj_last_envelope; case STK_DATA_OBJECT_TYPE_REGISTRY_APPLICATION_DATA: return parse_dataobj_registry_application_data; case STK_DATA_OBJECT_TYPE_ACTIVATE_DESCRIPTOR: return parse_dataobj_activate_descriptor; case STK_DATA_OBJECT_TYPE_BROADCAST_NETWORK_INFO: return parse_dataobj_broadcast_network_info; default: return NULL; } } static void destroy_stk_item(struct stk_item *item) { g_free(item->text); g_free(item); } static gboolean parse_item_list(struct comprehension_tlv_iter *iter, void *data) { GSList **out = data; unsigned short tag = STK_DATA_OBJECT_TYPE_ITEM; struct comprehension_tlv_iter iter_old; struct stk_item item; GSList *list = NULL; unsigned int count = 0; gboolean has_empty = FALSE; do { comprehension_tlv_iter_copy(iter, &iter_old); memset(&item, 0, sizeof(item)); count++; if (parse_dataobj_item(iter, &item) == TRUE) { if (item.id == 0) { has_empty = TRUE; continue; } list = g_slist_prepend(list, g_memdup(&item, sizeof(item))); } } while (comprehension_tlv_iter_next(iter) == TRUE && comprehension_tlv_iter_get_tag(iter) == tag); comprehension_tlv_iter_copy(&iter_old, iter); if (!has_empty) { *out = g_slist_reverse(list); return TRUE; } if (count == 1) return TRUE; g_slist_foreach(list, (GFunc) destroy_stk_item, NULL); g_slist_free(list); return FALSE; } static gboolean parse_provisioning_list(struct comprehension_tlv_iter *iter, void *data) { GSList **out = data; unsigned short tag = STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF; struct comprehension_tlv_iter iter_old; struct stk_file file; GSList *list = NULL; do { comprehension_tlv_iter_copy(iter, &iter_old); memset(&file, 0, sizeof(file)); if (parse_dataobj_provisioning_file_reference(iter, &file) == TRUE) list = g_slist_prepend(list, g_memdup(&file, sizeof(file))); } while (comprehension_tlv_iter_next(iter) == TRUE && comprehension_tlv_iter_get_tag(iter) == tag); comprehension_tlv_iter_copy(&iter_old, iter); *out = g_slist_reverse(list); return TRUE; } static dataobj_handler list_handler_for_type(enum stk_data_object_type type) { switch (type) { case STK_DATA_OBJECT_TYPE_ITEM: return parse_item_list; case STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF: return parse_provisioning_list; default: return NULL; } } struct dataobj_handler_entry { enum stk_data_object_type type; int flags; void *data; }; static enum stk_command_parse_result parse_dataobj( struct comprehension_tlv_iter *iter, enum stk_data_object_type type, ...) { GSList *entries = NULL; GSList *l; va_list args; gboolean minimum_set = TRUE; gboolean parse_error = FALSE; va_start(args, type); while (type != STK_DATA_OBJECT_TYPE_INVALID) { struct dataobj_handler_entry *entry; entry = g_new0(struct dataobj_handler_entry, 1); entry->type = type; entry->flags = va_arg(args, int); entry->data = va_arg(args, void *); type = va_arg(args, enum stk_data_object_type); entries = g_slist_prepend(entries, entry); } va_end(args); entries = g_slist_reverse(entries); l = entries; while (comprehension_tlv_iter_next(iter) == TRUE) { dataobj_handler handler; struct dataobj_handler_entry *entry; GSList *l2; for (l2 = l; l2; l2 = l2->next) { entry = l2->data; if (comprehension_tlv_iter_get_tag(iter) == entry->type) break; /* Can't skip over mandatory objects */ if (entry->flags & DATAOBJ_FLAG_MANDATORY) { l2 = NULL; break; } } if (l2 == NULL) { if (comprehension_tlv_get_cr(iter) == TRUE) parse_error = TRUE; continue; } if (entry->flags & DATAOBJ_FLAG_LIST) handler = list_handler_for_type(entry->type); else handler = handler_for_type(entry->type); if (handler(iter, entry->data) == FALSE) parse_error = TRUE; l = l2->next; } for (; l; l = l->next) { struct dataobj_handler_entry *entry = l->data; if (entry->flags & DATAOBJ_FLAG_MANDATORY) minimum_set = FALSE; } g_slist_foreach(entries, (GFunc) g_free, NULL); g_slist_free(entries); if (minimum_set == FALSE) return STK_PARSE_RESULT_MISSING_VALUE; if (parse_error == TRUE) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return STK_PARSE_RESULT_OK; } static void destroy_display_text(struct stk_command *command) { g_free(command->display_text.text); } static enum stk_command_parse_result parse_display_text( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_display_text *obj = &command->display_text; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_DISPLAY) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_display_text; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->text, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE, 0, &obj->immediate_response, STK_DATA_OBJECT_TYPE_DURATION, 0, &obj->duration, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id); return status; } static void destroy_get_inkey(struct stk_command *command) { g_free(command->get_inkey.text); } static enum stk_command_parse_result parse_get_inkey( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_get_inkey *obj = &command->get_inkey; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_get_inkey; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->text, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_DURATION, 0, &obj->duration, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id); return status; } static void destroy_get_input(struct stk_command *command) { g_free(command->get_input.text); g_free(command->get_input.default_text); } static enum stk_command_parse_result parse_get_input( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_get_input *obj = &command->get_input; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_get_input; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->text, STK_DATA_OBJECT_TYPE_RESPONSE_LENGTH, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->resp_len, STK_DATA_OBJECT_TYPE_DEFAULT_TEXT, 0, &obj->default_text, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id); return status; } static enum stk_command_parse_result parse_more_time( struct stk_command *command, struct comprehension_tlv_iter *iter) { if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return STK_PARSE_RESULT_OK; } static void destroy_play_tone(struct stk_command *command) { g_free(command->play_tone.alpha_id); } static enum stk_command_parse_result parse_play_tone( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_play_tone *obj = &command->play_tone; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_EARPIECE) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_play_tone; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_TONE, 0, &obj->tone, STK_DATA_OBJECT_TYPE_DURATION, 0, &obj->duration, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static enum stk_command_parse_result parse_poll_interval( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_poll_interval *obj = &command->poll_interval; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_DURATION, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->duration, STK_DATA_OBJECT_TYPE_INVALID); } static void destroy_setup_menu(struct stk_command *command) { g_free(command->setup_menu.alpha_id); g_slist_foreach(command->setup_menu.items, (GFunc) destroy_stk_item, NULL); g_slist_free(command->setup_menu.items); } static enum stk_command_parse_result parse_setup_menu( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_setup_menu *obj = &command->setup_menu; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_setup_menu; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ITEM, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM | DATAOBJ_FLAG_LIST, &obj->items, STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR, 0, &obj->next_act, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST, 0, &obj->item_icon_id_list, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST, 0, &obj->item_text_attr_list, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static void destroy_select_item(struct stk_command *command) { g_free(command->select_item.alpha_id); g_slist_foreach(command->select_item.items, (GFunc) destroy_stk_item, NULL); g_slist_free(command->select_item.items); } static enum stk_command_parse_result parse_select_item( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_select_item *obj = &command->select_item; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ITEM, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM | DATAOBJ_FLAG_LIST, &obj->items, STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR, 0, &obj->next_act, STK_DATA_OBJECT_TYPE_ITEM_ID, 0, &obj->item_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST, 0, &obj->item_icon_id_list, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST, 0, &obj->item_text_attr_list, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); command->destructor = destroy_select_item; if (status == STK_PARSE_RESULT_OK && obj->items == NULL) status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static void destroy_send_sms(struct stk_command *command) { g_free(command->send_sms.alpha_id); g_free(command->send_sms.cdma_sms.array); } static enum stk_command_parse_result parse_send_sms( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_send_sms *obj = &command->send_sms; enum stk_command_parse_result status; struct gsm_sms_tpdu gsm_tpdu; struct stk_address sc_address = { 0, NULL }; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; memset(&gsm_tpdu, 0, sizeof(gsm_tpdu)); status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ADDRESS, 0, &sc_address, STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU, 0, &gsm_tpdu, STK_DATA_OBJECT_TYPE_CDMA_SMS_TPDU, 0, &obj->cdma_sms, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); command->destructor = destroy_send_sms; if (status != STK_PARSE_RESULT_OK) goto out; CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); if (status != STK_PARSE_RESULT_OK) goto out; if (gsm_tpdu.len == 0 && obj->cdma_sms.len == 0) { status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; goto out; } if (gsm_tpdu.len > 0 && obj->cdma_sms.len > 0) { status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; goto out; } /* We don't process CDMA pdus for now */ if (obj->cdma_sms.len > 0) goto out; /* packing is needed */ if (command->qualifier & 0x01) { if (sms_decode_unpacked_stk_pdu(gsm_tpdu.tpdu, gsm_tpdu.len, &obj->gsm_sms) != TRUE) { status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; goto out; } goto set_addr; } if (sms_decode(gsm_tpdu.tpdu, gsm_tpdu.len, TRUE, gsm_tpdu.len, &obj->gsm_sms) == FALSE) { status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; goto out; } if (obj->gsm_sms.type != SMS_TYPE_SUBMIT && obj->gsm_sms.type != SMS_TYPE_COMMAND) { status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; goto out; } set_addr: if (sc_address.number == NULL) goto out; if (strlen(sc_address.number) > 20) { status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; goto out; } strcpy(obj->gsm_sms.sc_addr.address, sc_address.number); obj->gsm_sms.sc_addr.numbering_plan = sc_address.ton_npi & 15; obj->gsm_sms.sc_addr.number_type = (sc_address.ton_npi >> 4) & 7; out: g_free(sc_address.number); return status; } static void destroy_send_ss(struct stk_command *command) { g_free(command->send_ss.alpha_id); g_free(command->send_ss.ss.ss); } static enum stk_command_parse_result parse_send_ss(struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_send_ss *obj = &command->send_ss; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_send_ss; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_SS_STRING, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->ss, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); } static void destroy_send_ussd(struct stk_command *command) { g_free(command->send_ussd.alpha_id); } static enum stk_command_parse_result parse_send_ussd( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_send_ussd *obj = &command->send_ussd; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_send_ussd; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_USSD_STRING, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->ussd_string, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); } static void destroy_setup_call(struct stk_command *command) { g_free(command->setup_call.alpha_id_usr_cfm); g_free(command->setup_call.addr.number); g_free(command->setup_call.alpha_id_call_setup); } static enum stk_command_parse_result parse_setup_call( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_setup_call *obj = &command->setup_call; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_setup_call; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id_usr_cfm, STK_DATA_OBJECT_TYPE_ADDRESS, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->addr, STK_DATA_OBJECT_TYPE_CCP, 0, &obj->ccp, STK_DATA_OBJECT_TYPE_SUBADDRESS, 0, &obj->subaddr, STK_DATA_OBJECT_TYPE_DURATION, 0, &obj->duration, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id_usr_cfm, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id_call_setup, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id_call_setup, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr_usr_cfm, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr_call_setup, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id_usr_cfm, obj->icon_id_usr_cfm.id); CHECK_TEXT_AND_ICON(obj->alpha_id_call_setup, obj->icon_id_call_setup.id); return status; } static void destroy_refresh(struct stk_command *command) { g_slist_foreach(command->refresh.file_list, (GFunc) g_free, NULL); g_slist_free(command->refresh.file_list); g_free(command->refresh.alpha_id); } static enum stk_command_parse_result parse_refresh( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_refresh *obj = &command->refresh; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_refresh; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_FILE_LIST, 0, &obj->file_list, STK_DATA_OBJECT_TYPE_AID, 0, &obj->aid, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static enum stk_command_parse_result parse_polling_off( struct stk_command *command, struct comprehension_tlv_iter *iter) { if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return STK_PARSE_RESULT_OK; } static enum stk_command_parse_result parse_provide_local_info( struct stk_command *command, struct comprehension_tlv_iter *iter) { if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return STK_PARSE_RESULT_OK; } static enum stk_command_parse_result parse_setup_event_list( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_setup_event_list *obj = &command->setup_event_list; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_EVENT_LIST, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->event_list, STK_DATA_OBJECT_TYPE_INVALID); } static enum stk_command_parse_result parse_perform_card_apdu( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_perform_card_apdu *obj = &command->perform_card_apdu; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) || (command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7)) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_C_APDU, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->c_apdu, STK_DATA_OBJECT_TYPE_INVALID); } static enum stk_command_parse_result parse_power_off_card( struct stk_command *command, struct comprehension_tlv_iter *iter) { if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) || (command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7)) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return STK_PARSE_RESULT_OK; } static enum stk_command_parse_result parse_power_on_card( struct stk_command *command, struct comprehension_tlv_iter *iter) { if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) || (command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7)) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return STK_PARSE_RESULT_OK; } static enum stk_command_parse_result parse_get_reader_status( struct stk_command *command, struct comprehension_tlv_iter *iter) { if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; switch (command->qualifier) { case STK_QUALIFIER_TYPE_CARD_READER_STATUS: if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; break; case STK_QUALIFIER_TYPE_CARD_READER_ID: if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) || (command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7)) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; break; default: return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; } return STK_PARSE_RESULT_OK; } static enum stk_command_parse_result parse_timer_mgmt( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_timer_mgmt *obj = &command->timer_mgmt; enum stk_data_object_flag value_flags = 0; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if ((command->qualifier & 3) == 0) /* Start a timer */ value_flags = DATAOBJ_FLAG_MANDATORY; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TIMER_ID, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->timer_id, STK_DATA_OBJECT_TYPE_TIMER_VALUE, value_flags, &obj->timer_value, STK_DATA_OBJECT_TYPE_INVALID); } static void destroy_setup_idle_mode_text(struct stk_command *command) { g_free(command->setup_idle_mode_text.text); } static enum stk_command_parse_result parse_setup_idle_mode_text( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_setup_idle_mode_text *obj = &command->setup_idle_mode_text; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_setup_idle_mode_text; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->text, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id); return status; } static void destroy_run_at_command(struct stk_command *command) { g_free(command->run_at_command.alpha_id); g_free(command->run_at_command.at_command); } static enum stk_command_parse_result parse_run_at_command( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_run_at_command *obj = &command->run_at_command; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_run_at_command; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_AT_COMMAND, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->at_command, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static void destroy_send_dtmf(struct stk_command *command) { g_free(command->send_dtmf.alpha_id); g_free(command->send_dtmf.dtmf); } static enum stk_command_parse_result parse_send_dtmf( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_send_dtmf *obj = &command->send_dtmf; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_send_dtmf; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_DTMF_STRING, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->dtmf, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static enum stk_command_parse_result parse_language_notification( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_language_notification *obj = &command->language_notification; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_LANGUAGE, 0, &obj->language, STK_DATA_OBJECT_TYPE_INVALID); } static void destroy_launch_browser(struct stk_command *command) { g_free(command->launch_browser.url); g_free(command->launch_browser.bearer.array); g_slist_foreach(command->launch_browser.prov_file_refs, (GFunc) g_free, NULL); g_slist_free(command->launch_browser.prov_file_refs); g_free(command->launch_browser.text_gateway_proxy_id); g_free(command->launch_browser.alpha_id); g_free(command->launch_browser.network_name.array); g_free(command->launch_browser.text_usr); g_free(command->launch_browser.text_passwd); } static enum stk_command_parse_result parse_launch_browser( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_launch_browser *obj = &command->launch_browser; if (command->qualifier > 3 || command->qualifier == 1) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_launch_browser; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_BROWSER_ID, 0, &obj->browser_id, STK_DATA_OBJECT_TYPE_URL, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->url, STK_DATA_OBJECT_TYPE_BEARER, 0, &obj->bearer, STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF, DATAOBJ_FLAG_LIST, &obj->prov_file_refs, STK_DATA_OBJECT_TYPE_TEXT, 0, &obj->text_gateway_proxy_id, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME, 0, &obj->network_name, STK_DATA_OBJECT_TYPE_TEXT, 0, &obj->text_usr, STK_DATA_OBJECT_TYPE_TEXT, 0, &obj->text_passwd, STK_DATA_OBJECT_TYPE_INVALID); } static void destroy_open_channel(struct stk_command *command) { g_free(command->open_channel.alpha_id); g_free(command->open_channel.apn); g_free(command->open_channel.text_usr); g_free(command->open_channel.text_passwd); } static enum stk_command_parse_result parse_open_channel( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_open_channel *obj = &command->open_channel; enum stk_command_parse_result status; if (command->qualifier >= 0x08) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_open_channel; /* * parse the Open Channel data objects related to packet data service * bearer */ status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->bearer_desc, STK_DATA_OBJECT_TYPE_BUFFER_SIZE, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->buf_size, STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME, 0, &obj->apn, STK_DATA_OBJECT_TYPE_OTHER_ADDRESS, 0, &obj->local_addr, STK_DATA_OBJECT_TYPE_TEXT, 0, &obj->text_usr, STK_DATA_OBJECT_TYPE_TEXT, 0, &obj->text_passwd, STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE, 0, &obj->uti, STK_DATA_OBJECT_TYPE_OTHER_ADDRESS, 0, &obj->data_dest_addr, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static void destroy_close_channel(struct stk_command *command) { g_free(command->close_channel.alpha_id); } static enum stk_command_parse_result parse_close_channel( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_close_channel *obj = &command->close_channel; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CHANNEL_1) || (command->dst > STK_DEVICE_IDENTITY_TYPE_CHANNEL_7)) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_close_channel; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static void destroy_receive_data(struct stk_command *command) { g_free(command->receive_data.alpha_id); } static enum stk_command_parse_result parse_receive_data( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_receive_data *obj = &command->receive_data; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CHANNEL_1) || (command->dst > STK_DEVICE_IDENTITY_TYPE_CHANNEL_7)) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_receive_data; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->data_len, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static void destroy_send_data(struct stk_command *command) { g_free(command->send_data.alpha_id); g_free(command->send_data.data.array); } static enum stk_command_parse_result parse_send_data( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_send_data *obj = &command->send_data; enum stk_command_parse_result status; if (command->qualifier > STK_SEND_DATA_IMMEDIATELY) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CHANNEL_1) || (command->dst > STK_DEVICE_IDENTITY_TYPE_CHANNEL_7)) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_send_data; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_CHANNEL_DATA, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->data, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static enum stk_command_parse_result parse_get_channel_status( struct stk_command *command, struct comprehension_tlv_iter *iter) { if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return STK_PARSE_RESULT_OK; } static void destroy_service_search(struct stk_command *command) { g_free(command->service_search.alpha_id); g_free(command->service_search.serv_search.ser_search); g_free(command->service_search.dev_filter.dev_filter); } static enum stk_command_parse_result parse_service_search( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_service_search *obj = &command->service_search; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_service_search; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_SERVICE_SEARCH, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->serv_search, STK_DATA_OBJECT_TYPE_DEVICE_FILTER, 0, &obj->dev_filter, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); } static void destroy_get_service_info(struct stk_command *command) { g_free(command->get_service_info.alpha_id); g_free(command->get_service_info.attr_info.attr_info); } static enum stk_command_parse_result parse_get_service_info( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_get_service_info *obj = &command->get_service_info; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_get_service_info; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_ATTRIBUTE_INFO, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->attr_info, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); } static void destroy_declare_service(struct stk_command *command) { g_free(command->declare_service.serv_rec.serv_rec); } static enum stk_command_parse_result parse_declare_service( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_declare_service *obj = &command->declare_service; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_declare_service; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_SERVICE_RECORD, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->serv_rec, STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE, 0, &obj->intf, STK_DATA_OBJECT_TYPE_INVALID); } static enum stk_command_parse_result parse_set_frames( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_set_frames *obj = &command->set_frames; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_FRAME_ID, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->frame_id, STK_DATA_OBJECT_TYPE_FRAME_LAYOUT, 0, &obj->frame_layout, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id_default, STK_DATA_OBJECT_TYPE_INVALID); } static enum stk_command_parse_result parse_get_frames_status( struct stk_command *command, struct comprehension_tlv_iter *iter) { if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return STK_PARSE_RESULT_OK; } static void destroy_retrieve_mms(struct stk_command *command) { g_free(command->retrieve_mms.alpha_id); g_slist_foreach(command->retrieve_mms.mms_rec_files, (GFunc) g_free, NULL); g_slist_free(command->retrieve_mms.mms_rec_files); } static enum stk_command_parse_result parse_retrieve_mms( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_retrieve_mms *obj = &command->retrieve_mms; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_retrieve_mms; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_MMS_REFERENCE, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->mms_ref, STK_DATA_OBJECT_TYPE_FILE_LIST, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->mms_rec_files, STK_DATA_OBJECT_TYPE_MMS_CONTENT_ID, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->mms_content_id, STK_DATA_OBJECT_TYPE_MMS_ID, 0, &obj->mms_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static void destroy_submit_mms(struct stk_command *command) { g_free(command->submit_mms.alpha_id); g_slist_foreach(command->submit_mms.mms_subm_files, (GFunc) g_free, NULL); g_slist_free(command->submit_mms.mms_subm_files); } static enum stk_command_parse_result parse_submit_mms( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_submit_mms *obj = &command->submit_mms; enum stk_command_parse_result status; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_submit_mms; status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0, &obj->alpha_id, STK_DATA_OBJECT_TYPE_ICON_ID, 0, &obj->icon_id, STK_DATA_OBJECT_TYPE_FILE_LIST, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->mms_subm_files, STK_DATA_OBJECT_TYPE_MMS_ID, 0, &obj->mms_id, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0, &obj->text_attr, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id); return status; } static void destroy_display_mms(struct stk_command *command) { g_slist_foreach(command->display_mms.mms_subm_files, (GFunc) g_free, NULL); g_slist_free(command->display_mms.mms_subm_files); } static enum stk_command_parse_result parse_display_mms( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_display_mms *obj = &command->display_mms; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; command->destructor = destroy_display_mms; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_FILE_LIST, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->mms_subm_files, STK_DATA_OBJECT_TYPE_MMS_ID, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->mms_id, STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE, 0, &obj->imd_resp, STK_DATA_OBJECT_TYPE_FRAME_ID, 0, &obj->frame_id, STK_DATA_OBJECT_TYPE_INVALID); } static enum stk_command_parse_result parse_activate( struct stk_command *command, struct comprehension_tlv_iter *iter) { struct stk_command_activate *obj = &command->activate; if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ACTIVATE_DESCRIPTOR, DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM, &obj->actv_desc, STK_DATA_OBJECT_TYPE_INVALID); } static enum stk_command_parse_result parse_command_body( struct stk_command *command, struct comprehension_tlv_iter *iter) { switch (command->type) { case STK_COMMAND_TYPE_DISPLAY_TEXT: return parse_display_text(command, iter); case STK_COMMAND_TYPE_GET_INKEY: return parse_get_inkey(command, iter); case STK_COMMAND_TYPE_GET_INPUT: return parse_get_input(command, iter); case STK_COMMAND_TYPE_MORE_TIME: return parse_more_time(command, iter); case STK_COMMAND_TYPE_PLAY_TONE: return parse_play_tone(command, iter); case STK_COMMAND_TYPE_POLL_INTERVAL: return parse_poll_interval(command, iter); case STK_COMMAND_TYPE_SETUP_MENU: return parse_setup_menu(command, iter); case STK_COMMAND_TYPE_SELECT_ITEM: return parse_select_item(command, iter); case STK_COMMAND_TYPE_SEND_SMS: return parse_send_sms(command, iter); case STK_COMMAND_TYPE_SEND_SS: return parse_send_ss(command, iter); case STK_COMMAND_TYPE_SEND_USSD: return parse_send_ussd(command, iter); case STK_COMMAND_TYPE_SETUP_CALL: return parse_setup_call(command, iter); case STK_COMMAND_TYPE_REFRESH: return parse_refresh(command, iter); case STK_COMMAND_TYPE_POLLING_OFF: return parse_polling_off(command, iter); case STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO: return parse_provide_local_info(command, iter); case STK_COMMAND_TYPE_SETUP_EVENT_LIST: return parse_setup_event_list(command, iter); case STK_COMMAND_TYPE_PERFORM_CARD_APDU: return parse_perform_card_apdu(command, iter); case STK_COMMAND_TYPE_POWER_OFF_CARD: return parse_power_off_card(command, iter); case STK_COMMAND_TYPE_POWER_ON_CARD: return parse_power_on_card(command, iter); case STK_COMMAND_TYPE_GET_READER_STATUS: return parse_get_reader_status(command, iter); case STK_COMMAND_TYPE_TIMER_MANAGEMENT: return parse_timer_mgmt(command, iter); case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT: return parse_setup_idle_mode_text(command, iter); case STK_COMMAND_TYPE_RUN_AT_COMMAND: return parse_run_at_command(command, iter); case STK_COMMAND_TYPE_SEND_DTMF: return parse_send_dtmf(command, iter); case STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION: return parse_language_notification(command, iter); case STK_COMMAND_TYPE_LAUNCH_BROWSER: return parse_launch_browser(command, iter); case STK_COMMAND_TYPE_OPEN_CHANNEL: return parse_open_channel(command, iter); case STK_COMMAND_TYPE_CLOSE_CHANNEL: return parse_close_channel(command, iter); case STK_COMMAND_TYPE_RECEIVE_DATA: return parse_receive_data(command, iter); case STK_COMMAND_TYPE_SEND_DATA: return parse_send_data(command, iter); case STK_COMMAND_TYPE_GET_CHANNEL_STATUS: return parse_get_channel_status(command, iter); case STK_COMMAND_TYPE_SERVICE_SEARCH: return parse_service_search(command, iter); case STK_COMMAND_TYPE_GET_SERVICE_INFO: return parse_get_service_info(command, iter); case STK_COMMAND_TYPE_DECLARE_SERVICE: return parse_declare_service(command, iter); case STK_COMMAND_TYPE_SET_FRAMES: return parse_set_frames(command, iter); case STK_COMMAND_TYPE_GET_FRAMES_STATUS: return parse_get_frames_status(command, iter); case STK_COMMAND_TYPE_RETRIEVE_MMS: return parse_retrieve_mms(command, iter); case STK_COMMAND_TYPE_SUBMIT_MMS: return parse_submit_mms(command, iter); case STK_COMMAND_TYPE_DISPLAY_MMS: return parse_display_mms(command, iter); case STK_COMMAND_TYPE_ACTIVATE: return parse_activate(command, iter); default: return STK_PARSE_RESULT_TYPE_NOT_UNDERSTOOD; }; } struct stk_command *stk_command_new_from_pdu(const unsigned char *pdu, unsigned int len) { struct ber_tlv_iter ber; struct comprehension_tlv_iter iter; const unsigned char *data; struct stk_command *command; ber_tlv_iter_init(&ber, pdu, len); if (ber_tlv_iter_next(&ber) != TRUE) return NULL; /* We should be wrapped in a Proactive UICC Command Tag 0xD0 */ if (ber_tlv_iter_get_short_tag(&ber) != 0xD0) return NULL; ber_tlv_iter_recurse_comprehension(&ber, &iter); /* * Now parse actual command details, they come in order with * Command Details TLV first, followed by Device Identities TLV */ if (comprehension_tlv_iter_next(&iter) != TRUE) return NULL; if (comprehension_tlv_iter_get_tag(&iter) != STK_DATA_OBJECT_TYPE_COMMAND_DETAILS) return NULL; if (comprehension_tlv_iter_get_length(&iter) != 0x03) return NULL; data = comprehension_tlv_iter_get_data(&iter); command = g_new0(struct stk_command, 1); command->number = data[0]; command->type = data[1]; command->qualifier = data[2]; if (comprehension_tlv_iter_next(&iter) != TRUE) { command->status = STK_PARSE_RESULT_MISSING_VALUE; goto out; } if (comprehension_tlv_iter_get_tag(&iter) != STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES) { command->status = STK_PARSE_RESULT_MISSING_VALUE; goto out; } if (comprehension_tlv_iter_get_length(&iter) != 0x02) { command->status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; goto out; } data = comprehension_tlv_iter_get_data(&iter); command->src = data[0]; command->dst = data[1]; command->status = parse_command_body(command, &iter); out: return command; } void stk_command_free(struct stk_command *command) { if (command->destructor) command->destructor(command); g_free(command); } static gboolean stk_tlv_builder_init(struct stk_tlv_builder *iter, unsigned char *pdu, unsigned int size) { iter->value = NULL; iter->len = 0; return comprehension_tlv_builder_init(&iter->ctlv, pdu, size); } static gboolean stk_tlv_builder_recurse(struct stk_tlv_builder *iter, struct ber_tlv_builder *btlv, unsigned char tag) { iter->value = NULL; iter->len = 0; if (ber_tlv_builder_next(btlv, tag >> 6, (tag >> 5) & 1, tag & 0x1f) != TRUE) return FALSE; return ber_tlv_builder_recurse_comprehension(btlv, &iter->ctlv); } static gboolean stk_tlv_builder_open_container(struct stk_tlv_builder *iter, gboolean cr, unsigned char shorttag, gboolean relocatable) { if (comprehension_tlv_builder_next(&iter->ctlv, cr, shorttag) != TRUE) return FALSE; iter->len = 0; iter->max_len = relocatable ? 0xff : 0x7f; if (comprehension_tlv_builder_set_length(&iter->ctlv, iter->max_len) != TRUE) return FALSE; iter->value = comprehension_tlv_builder_get_data(&iter->ctlv); return TRUE; } static gboolean stk_tlv_builder_close_container(struct stk_tlv_builder *iter) { return comprehension_tlv_builder_set_length(&iter->ctlv, iter->len); } static unsigned int stk_tlv_builder_get_length(struct stk_tlv_builder *iter) { return comprehension_tlv_builder_get_data(&iter->ctlv) - iter->ctlv.pdu + iter->len; } static gboolean stk_tlv_builder_append_byte(struct stk_tlv_builder *iter, unsigned char num) { if (iter->len >= iter->max_len) return FALSE; iter->value[iter->len++] = num; return TRUE; } static gboolean stk_tlv_builder_append_short(struct stk_tlv_builder *iter, unsigned short num) { if (iter->len + 2 > iter->max_len) return FALSE; iter->value[iter->len++] = num >> 8; iter->value[iter->len++] = num & 0xff; return TRUE; } static gboolean stk_tlv_builder_append_gsm_packed(struct stk_tlv_builder *iter, const char *text) { unsigned int len; unsigned char *gsm; long written = 0; if (text == NULL) return TRUE; len = strlen(text); gsm = convert_utf8_to_gsm(text, len, NULL, &written, 0); if (gsm == NULL && len > 0) return FALSE; if (iter->len + (written * 7 + 7) / 8 >= iter->max_len) { g_free(gsm); return FALSE; } pack_7bit_own_buf(gsm, len, 0, FALSE, &written, 0, iter->value + iter->len + 1); g_free(gsm); if (written < 1 && len > 0) return FALSE; iter->value[iter->len++] = 0x00; iter->len += written; return TRUE; } static gboolean stk_tlv_builder_append_gsm_unpacked( struct stk_tlv_builder *iter, const char *text) { unsigned int len; unsigned char *gsm; long written = 0; if (text == NULL) return TRUE; len = strlen(text); gsm = convert_utf8_to_gsm(text, len, NULL, &written, 0); if (gsm == NULL && len > 0) return FALSE; if (iter->len + written >= iter->max_len) { g_free(gsm); return FALSE; } iter->value[iter->len++] = 0x04; memcpy(iter->value + iter->len, gsm, written); iter->len += written; g_free(gsm); return TRUE; } static gboolean stk_tlv_builder_append_ucs2(struct stk_tlv_builder *iter, const char *text) { unsigned char *ucs2; gsize gwritten; ucs2 = (unsigned char *) g_convert((const gchar *) text, -1, "UCS-2BE", "UTF-8//TRANSLIT", NULL, &gwritten, NULL); if (ucs2 == NULL) return FALSE; if (iter->len + gwritten >= iter->max_len) { g_free(ucs2); return FALSE; } iter->value[iter->len++] = 0x08; memcpy(iter->value + iter->len, ucs2, gwritten); iter->len += gwritten; g_free(ucs2); return TRUE; } static gboolean stk_tlv_builder_append_text(struct stk_tlv_builder *iter, int dcs, const char *text) { gboolean ret; switch (dcs) { case 0x00: return stk_tlv_builder_append_gsm_packed(iter, text); case 0x04: return stk_tlv_builder_append_gsm_unpacked(iter, text); case 0x08: return stk_tlv_builder_append_ucs2(iter, text); case -1: ret = stk_tlv_builder_append_gsm_unpacked(iter, text); if (ret == TRUE) return ret; return stk_tlv_builder_append_ucs2(iter, text); } return FALSE; } static inline gboolean stk_tlv_builder_append_bytes(struct stk_tlv_builder *iter, const unsigned char *data, unsigned int length) { if (iter->len + length > iter->max_len) return FALSE; memcpy(iter->value + iter->len, data, length); iter->len += length; return TRUE; } /* Described in TS 102.223 Section 8.1 */ static gboolean build_dataobj_address(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_address *addr = data; unsigned char tag = STK_DATA_OBJECT_TYPE_ADDRESS; unsigned int len; unsigned char number[128]; if (addr->number == NULL) return TRUE; len = (strlen(addr->number) + 1) / 2; sim_encode_bcd_number(addr->number, number); return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, addr->ton_npi) && stk_tlv_builder_append_bytes(tlv, number, len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.2 */ static gboolean build_dataobj_alpha_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { unsigned char tag = STK_DATA_OBJECT_TYPE_ALPHA_ID; int len; unsigned char *string; if (data == NULL) return TRUE; if (strlen(data) == 0) return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_close_container(tlv); string = utf8_to_sim_string(data, -1, &len); if (string == NULL) return FALSE; return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) && stk_tlv_builder_append_bytes(tlv, string, len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.3 */ static gboolean build_dataobj_subaddress(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_subaddress *sa = data; unsigned char tag = STK_DATA_OBJECT_TYPE_SUBADDRESS; if (sa->has_subaddr == FALSE) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, sa->subaddr, sa->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.4 */ static gboolean build_dataobj_ccp(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_ccp *ccp = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CCP; if (ccp->len == 0) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, ccp->len) && stk_tlv_builder_append_bytes(tlv, ccp->ccp, ccp->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.5 */ static gboolean build_dataobj_cbs_page(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct cbs *page = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CBS_PAGE; unsigned char pdu[88]; if (cbs_encode(page, NULL, pdu) == FALSE) return FALSE; return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) && stk_tlv_builder_append_bytes(tlv, pdu, 88) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.6 */ static gboolean build_dataobj_item_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const unsigned char *item_id = data; unsigned char tag = STK_DATA_OBJECT_TYPE_ITEM_ID; if (*item_id == 0) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, *item_id) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.8 */ static gboolean build_dataobj_duration(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_duration *duration = data; unsigned char tag = STK_DATA_OBJECT_TYPE_DURATION; if (duration->interval == 0x00) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, duration->unit) && stk_tlv_builder_append_byte(tlv, duration->interval) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.12 */ static gboolean build_dataobj_result(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_result *result = data; unsigned char tag = STK_DATA_OBJECT_TYPE_RESULT; if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == FALSE) return FALSE; if (stk_tlv_builder_append_byte(tlv, result->type) == FALSE) return FALSE; if (result->additional_len > 0) if (stk_tlv_builder_append_bytes(tlv, result->additional, result->additional_len) == FALSE) return FALSE; return stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.13 */ static gboolean build_dataobj_gsm_sms_tpdu(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct sms_deliver *msg = data; struct sms sms; unsigned char tag = STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU; unsigned char tpdu[165]; int tpdu_len; sms.type = SMS_TYPE_DELIVER; memset(&sms.sc_addr, 0, sizeof(sms.sc_addr)); memcpy(&sms.deliver, msg, sizeof(sms.deliver)); if (sms_encode(&sms, NULL, &tpdu_len, tpdu) == FALSE) return FALSE; return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) && stk_tlv_builder_append_bytes(tlv, tpdu + 1, tpdu_len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.14 */ static gboolean build_dataobj_ss_string(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_address *addr = data; unsigned char tag = STK_DATA_OBJECT_TYPE_SS_STRING; unsigned int len; unsigned char number[128]; if (addr->number == NULL) return TRUE; len = (strlen(addr->number) + 1) / 2; sim_encode_bcd_number(addr->number, number); return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, addr->ton_npi) && stk_tlv_builder_append_bytes(tlv, number, len) && stk_tlv_builder_close_container(tlv); } /* Defined in TS 102.223 Section 8.15 */ static gboolean build_dataobj_text(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_answer_text *text = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TEXT; gboolean ret; if (text->text == NULL && !text->yesno) return TRUE; if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE) return FALSE; if (text->yesno == TRUE) { /* * Section 6.8.5: * When the terminal issues [...] command qualifier set * to "Yes/No", it shall supply the value "01" when the * answer is "positive" and the value '00' when the * answer is "negative" in the text string data object. */ if (stk_tlv_builder_append_byte(tlv, 0x04) != TRUE) return FALSE; ret = stk_tlv_builder_append_byte(tlv, text->text ? 0x01 : 0x00); } else if (text->packed) ret = stk_tlv_builder_append_gsm_packed(tlv, text->text); else ret = stk_tlv_builder_append_text(tlv, -1, text->text); if (ret != TRUE) return ret; return stk_tlv_builder_close_container(tlv); } /* Defined in TS 102.223 Section 8.15 - USSD specific case*/ static gboolean build_dataobj_ussd_text(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_ussd_text *text = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TEXT; if (text->has_text == FALSE) return TRUE; if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE) return FALSE; if (text->len > 0) { if (stk_tlv_builder_append_byte(tlv, text->dcs) != TRUE) return FALSE; if (stk_tlv_builder_append_bytes(tlv, text->text, text->len) != TRUE) return FALSE; } return stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.17 */ static gboolean build_dataobj_ussd_string(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_ussd_string *ussd = data; unsigned char tag = STK_DATA_OBJECT_TYPE_USSD_STRING; if (ussd->string == NULL) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, ussd->dcs) && stk_tlv_builder_append_bytes(tlv, ussd->string, ussd->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.18 */ static gboolean build_dataobj_file_list(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { GSList *l = (void *) data; const struct stk_file *file; unsigned char tag = STK_DATA_OBJECT_TYPE_FILE_LIST; if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE) return FALSE; if (stk_tlv_builder_append_byte(tlv, g_slist_length(l)) != TRUE) return FALSE; for (; l; l = l->next) { file = l->data; if (stk_tlv_builder_append_bytes(tlv, file->file, file->len) != TRUE) return FALSE; } return stk_tlv_builder_close_container(tlv); } /* Shortcut for a single File element */ static gboolean build_dataobj_file(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { GSList l = { .data = (void *) data, .next = NULL, }; return build_dataobj_file_list(tlv, &l, cr); } /* Described in TS 102.223 Section 8.19 */ static gboolean build_dataobj_location_info(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_location_info *li = data; unsigned char tag = STK_DATA_OBJECT_TYPE_LOCATION_INFO; guint8 mccmnc[3]; if (li->mcc[0] == '\0') return TRUE; sim_encode_mcc_mnc(mccmnc, li->mcc, li->mnc); if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == FALSE) return FALSE; if (stk_tlv_builder_append_bytes(tlv, mccmnc, 3) == FALSE) return FALSE; if (stk_tlv_builder_append_short(tlv, li->lac_tac) == FALSE) return FALSE; if (li->has_ci && stk_tlv_builder_append_short(tlv, li->ci) == FALSE) return FALSE; if (li->has_ext_ci && stk_tlv_builder_append_short(tlv, li->ext_ci) == FALSE) return FALSE; if (li->has_eutran_ci) { if (stk_tlv_builder_append_short(tlv, li->eutran_ci >> 12) == FALSE) return FALSE; if (stk_tlv_builder_append_short(tlv, (li->eutran_ci << 4) | 0xf) == FALSE) return FALSE; } return stk_tlv_builder_close_container(tlv); } static gboolean build_empty_dataobj_location_info(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { unsigned char tag = STK_DATA_OBJECT_TYPE_LOCATION_INFO; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_close_container(tlv); } /* * Described in TS 102.223 Section 8.20 * * See format note in parse_dataobj_imei. */ static gboolean build_dataobj_imei(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { char byte0[3]; const char *imei = data; unsigned char tag = STK_DATA_OBJECT_TYPE_IMEI; unsigned char value[8]; if (imei == NULL) return TRUE; if (strlen(imei) != 15) return FALSE; byte0[0] = '*'; byte0[1] = imei[0]; byte0[2] = '\0'; sim_encode_bcd_number(byte0, value); sim_encode_bcd_number(imei + 1, value + 1); return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, value, 8) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.21 */ static gboolean build_dataobj_help_request(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const ofono_bool_t *help = data; unsigned char tag = STK_DATA_OBJECT_TYPE_HELP_REQUEST; if (*help != TRUE) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.22 */ static gboolean build_dataobj_network_measurement_results( struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *nmr = data; unsigned char tag = STK_DATA_OBJECT_TYPE_NETWORK_MEASUREMENT_RESULTS; if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == FALSE) return FALSE; if (nmr->len > 0 && stk_tlv_builder_append_bytes(tlv, nmr->array, nmr->len) == FALSE) return FALSE; return stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.25 */ static gboolean build_dataobj_event_list(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_event_list *list = data; unsigned char tag = STK_DATA_OBJECT_TYPE_EVENT_LIST; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, list->list, list->len) && stk_tlv_builder_close_container(tlv); } /* Shortcut for a single Event type */ static gboolean build_dataobj_event_type(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_event_list list = { .list = { *(enum stk_event_type *) data }, .len = 1, }; return build_dataobj_event_list(tlv, &list, cr); } /* Described in TS 102.223 Section 8.26 */ static gboolean build_dataobj_cause(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_cause *cause = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CAUSE; if (cause->has_cause == FALSE) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, cause->cause, cause->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.27 */ static gboolean build_dataobj_location_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_service_state *state = data; unsigned char tag = STK_DATA_OBJECT_TYPE_LOCATION_STATUS; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, *state) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.28 */ static gboolean build_dataobj_transaction_ids(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_transaction_id *id = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TRANSACTION_ID; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, id->list, id->len) && stk_tlv_builder_close_container(tlv); } /* Shortcut for a single Transaction ID */ static gboolean build_dataobj_transaction_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_transaction_id ids = { .list = { *(uint8_t *) data }, .len = 1, }; return build_dataobj_transaction_ids(tlv, &ids, cr); } /* Described in 3GPP 31.111 Section 8.29 */ static gboolean build_dataobj_bcch_channel_list(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_bcch_channel_list *list = data; unsigned char tag = STK_DATA_OBJECT_TYPE_BCCH_CHANNEL_LIST; unsigned int i, bytes, pos, shift; unsigned char value; if (list->has_list == FALSE) return TRUE; if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE) return FALSE; bytes = (list->num * 10 + 7) / 8; for (i = 0; i < bytes; i++) { pos = (i * 8 + 7) / 10; shift = pos * 10 + 10 - i * 8 - 8; value = 0; if (pos < list->num) value |= list->channels[pos] >> shift; if (shift > 2) value |= list->channels[pos - 1] << (10 - shift); if (stk_tlv_builder_append_byte(tlv, value) != TRUE) return FALSE; } return stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.30 */ static gboolean build_dataobj_cc_requested_action(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *action = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CALL_CONTROL_REQUESTED_ACTION; if (action->array == NULL) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, action->array, action->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.33 */ static gboolean build_dataobj_card_reader_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_reader_status *status = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CARD_READER_STATUS; unsigned char byte; byte = status->id | (status->removable << 3) | (status->present << 4) | (status->id1_size << 5) | (status->card_present << 6) | (status->card_powered << 7); return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, byte) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.37 */ static gboolean build_dataobj_timer_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const unsigned char *id = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TIMER_ID; if (id[0] == 0) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, id[0]) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.38 */ static gboolean build_dataobj_timer_value(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_timer_value *value = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TIMER_VALUE; if (value->has_value == FALSE) return TRUE; #define TO_BCD(bin) ((((bin) / 10) & 0xf) | (((bin) % 10) << 4)) return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, TO_BCD(value->hour)) && stk_tlv_builder_append_byte(tlv, TO_BCD(value->minute)) && stk_tlv_builder_append_byte(tlv, TO_BCD(value->second)) && stk_tlv_builder_close_container(tlv); #undef TO_BCD } /* Described in TS 102.223 Section 8.39 */ static gboolean build_dataobj_datetime_timezone(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct sms_scts *scts = data; unsigned char value[7]; int offset = 0; unsigned char tag = STK_DATA_OBJECT_TYPE_DATETIME_TIMEZONE; if (scts->month == 0 && scts->day == 0) return TRUE; if (sms_encode_scts(scts, value, &offset) != TRUE) return FALSE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, value, 7) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.41 */ static gboolean build_dataobj_at_response(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { unsigned char tag = STK_DATA_OBJECT_TYPE_AT_RESPONSE; int len; if (data == NULL) return TRUE; /* * "If the AT Response string is longer than the maximum length * capable of being transmitted to the UICC then the AT Response * string shall be truncated to this length by the terminal." */ len = strlen(data); if (len > 240) /* Safe pick */ len = 240; return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) && stk_tlv_builder_append_bytes(tlv, data, len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.42 */ static gboolean build_dataobj_bc_repeat(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { unsigned char tag = STK_DATA_OBJECT_TYPE_BC_REPEAT_INDICATOR; const struct stk_bc_repeat *bcr = data; if (bcr->has_bc_repeat == FALSE) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) && stk_tlv_builder_append_byte(tlv, bcr->value) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.45 */ static gboolean build_dataobj_language(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { unsigned char tag = STK_DATA_OBJECT_TYPE_LANGUAGE; if (data == NULL) return TRUE; /* * Coded as two GSM 7-bit characters with eighth bit clear. Since * ISO 639-2 codes use only english alphabet letters, no conversion * from UTF-8 to GSM is needed. */ return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, data, 2) && stk_tlv_builder_close_container(tlv); } /* Described in 3GPP TS 31.111 Section 8.46 */ static gboolean build_dataobj_timing_advance(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_timing_advance *tadv = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TIMING_ADVANCE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, tadv->status) && stk_tlv_builder_append_byte(tlv, tadv->advance) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.51 */ static gboolean build_dataobj_browser_termination_cause( struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_browser_termination_cause *cause = data; unsigned char tag = STK_DATA_OBJECT_TYPE_BROWSER_TERMINATION_CAUSE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, *cause) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.52 */ static gboolean build_dataobj_bearer_description(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_bearer_description *bd = data; unsigned char tag = STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION; if (bd->type != STK_BEARER_TYPE_GPRS_UTRAN) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, bd->type) && stk_tlv_builder_append_byte(tlv, bd->gprs.precedence) && stk_tlv_builder_append_byte(tlv, bd->gprs.delay) && stk_tlv_builder_append_byte(tlv, bd->gprs.reliability) && stk_tlv_builder_append_byte(tlv, bd->gprs.peak) && stk_tlv_builder_append_byte(tlv, bd->gprs.mean) && stk_tlv_builder_append_byte(tlv, bd->gprs.pdp_type) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.53 */ static gboolean build_dataobj_channel_data(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *cd = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CHANNEL_DATA; return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) && stk_tlv_builder_append_bytes(tlv, cd->array, cd->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.54 */ static gboolean build_dataobj_channel_data_length( struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const unsigned short *length = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, MIN(*length, 255)) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.55 */ static gboolean build_dataobj_buffer_size(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const unsigned short *buf_size = data; unsigned char tag = STK_DATA_OBJECT_TYPE_BUFFER_SIZE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_short(tlv, *buf_size) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.56 */ static gboolean build_dataobj_channel_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_channel *channel = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CHANNEL_STATUS; unsigned char byte[2]; switch (channel->status) { case STK_CHANNEL_PACKET_DATA_SERVICE_NOT_ACTIVATED: case STK_CHANNEL_TCP_IN_CLOSED_STATE: byte[0] = channel->id; byte[1] = 0x00; break; case STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED: case STK_CHANNEL_TCP_IN_ESTABLISHED_STATE: byte[0] = channel->id | 0x80; byte[1] = 0x00; break; case STK_CHANNEL_TCP_IN_LISTEN_STATE: byte[0] = channel->id | 0x40; byte[1] = 0x00; break; case STK_CHANNEL_LINK_DROPPED: byte[0] = channel->id; byte[1] = 0x05; break; } return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, byte, 2) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.58 */ static gboolean build_dataobj_other_address(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_other_address *addr = data; unsigned char tag = STK_DATA_OBJECT_TYPE_OTHER_ADDRESS; gboolean ok = FALSE; if (!addr->type) return TRUE; if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == FALSE) return FALSE; switch (addr->type) { case STK_ADDRESS_AUTO: ok = TRUE; break; case STK_ADDRESS_IPV4: ok = stk_tlv_builder_append_byte(tlv, addr->type) && stk_tlv_builder_append_bytes(tlv, (const guint8 *) &addr->addr.ipv4, 4); break; case STK_ADDRESS_IPV6: ok = stk_tlv_builder_append_byte(tlv, addr->type) && stk_tlv_builder_append_bytes(tlv, addr->addr.ipv6, 16); break; } if (!ok) return FALSE; return stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.59 */ static gboolean build_dataobj_uicc_te_interface(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_uicc_te_interface *iface = data; unsigned char tag = STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE; if (iface->protocol == 0 && iface->port == 0) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, iface->protocol) && stk_tlv_builder_append_short(tlv, iface->port) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.61 */ static gboolean build_dataobj_access_technologies(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_access_technologies *techs = data; unsigned char tag = STK_DATA_OBJECT_TYPE_ACCESS_TECHNOLOGY; int i; if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) != TRUE) return FALSE; for (i = 0; i < techs->length; i++) if (stk_tlv_builder_append_byte(tlv, techs->techs[i]) != TRUE) return FALSE; return stk_tlv_builder_close_container(tlv); } /* Shortcut for a single Access Technology */ static gboolean build_dataobj_access_technology(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_access_technologies techs = { .techs = data, .length = 1, }; return build_dataobj_access_technologies(tlv, &techs, cr); } /* Described in TS 102.223 Section 8.62 */ static gboolean build_dataobj_display_parameters(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_display_parameters *params = data; unsigned char tag = STK_DATA_OBJECT_TYPE_DISPLAY_PARAMETERS; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, params->height) && stk_tlv_builder_append_byte(tlv, params->width) && stk_tlv_builder_append_byte(tlv, params->effects) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.63 */ static gboolean build_dataobj_service_record(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_service_record *rec = data; unsigned char tag = STK_DATA_OBJECT_TYPE_SERVICE_RECORD; return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) && stk_tlv_builder_append_byte(tlv, rec->tech_id) && stk_tlv_builder_append_byte(tlv, rec->serv_id) && stk_tlv_builder_append_bytes(tlv, rec->serv_rec, rec->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.68 */ static gboolean build_dataobj_remote_entity_address(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_remote_entity_address *addr = data; unsigned char tag = STK_DATA_OBJECT_TYPE_REMOTE_ENTITY_ADDRESS; gboolean ok = FALSE; if (addr->has_address != TRUE) return TRUE; if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE) return FALSE; if (stk_tlv_builder_append_byte(tlv, addr->coding_type) != TRUE) return FALSE; switch (addr->coding_type) { case 0x00: ok = stk_tlv_builder_append_bytes(tlv, addr->addr.ieee802, 6); break; case 0x01: ok = stk_tlv_builder_append_bytes(tlv, addr->addr.irda, 4); break; } if (!ok) return FALSE; return stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.69 */ static gboolean build_dataobj_esn(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const guint32 *esn = data; unsigned char tag = STK_DATA_OBJECT_TYPE_ESN; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_short(tlv, *esn >> 16) && stk_tlv_builder_append_short(tlv, *esn >> 0) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.72, 3GPP 24.008 Section 9.5.7 */ static gboolean build_dataobj_pdp_context_params(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *params = data; unsigned char tag = STK_DATA_OBJECT_TYPE_PDP_ACTIVATION_PARAMETER; if (params->len < 1) return TRUE; if (params->len > 0x7f) return FALSE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, params->array, params->len) && stk_tlv_builder_close_container(tlv); } /* * Described in TS 102.223 Section 8.74 * * See format note in parse_dataobj_imeisv. */ static gboolean build_dataobj_imeisv(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { char byte0[3]; const char *imeisv = data; unsigned char value[9]; unsigned char tag = STK_DATA_OBJECT_TYPE_IMEISV; if (imeisv == NULL) return TRUE; if (strlen(imeisv) != 16) return FALSE; byte0[0] = '3'; byte0[1] = imeisv[0]; byte0[2] = '\0'; sim_encode_bcd_number(byte0, value); sim_encode_bcd_number(imeisv + 1, value + 1); return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, value, 9) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.75 */ static gboolean build_dataobj_network_search_mode(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_network_search_mode *mode = data; unsigned char tag = STK_DATA_OBJECT_TYPE_NETWORK_SEARCH_MODE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, *mode) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.76 */ static gboolean build_dataobj_battery_state(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_battery_state *state = data; unsigned char tag = STK_DATA_OBJECT_TYPE_BATTERY_STATE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, *state) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.77 */ static gboolean build_dataobj_browsing_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *bs = data; unsigned char tag = STK_DATA_OBJECT_TYPE_BROWSING_STATUS; return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) && stk_tlv_builder_append_bytes(tlv, bs->array, bs->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.79 */ static gboolean build_dataobj_frames_information(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_frames_info *info = data; unsigned char tag = STK_DATA_OBJECT_TYPE_FRAMES_INFO; unsigned int i; if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) != TRUE) return FALSE; if (stk_tlv_builder_append_byte(tlv, info->id) != TRUE) return FALSE; for (i = 0; i < info->len; i++) { if (stk_tlv_builder_append_byte(tlv, info->list[i].height) != TRUE) return FALSE; if (stk_tlv_builder_append_byte(tlv, info->list[i].width) != TRUE) return FALSE; } return stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.81 */ static gboolean build_dataobj_meid(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const char *meid = data; unsigned char value[8]; unsigned char tag = STK_DATA_OBJECT_TYPE_MEID; if (meid == NULL) return TRUE; if (strlen(meid) != 16) return FALSE; sim_encode_bcd_number(meid, value); return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, value, 8) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.83 */ static gboolean build_dataobj_mms_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_mms_id *id = data; unsigned char tag = STK_DATA_OBJECT_TYPE_MMS_ID; /* Assume the length is never 0 for a valid ID, however the whole * data object's presence is conditional. */ if (id->len == 0) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, id->id, id->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.84 */ static gboolean build_dataobj_mms_transfer_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_mms_transfer_status *mts = data; unsigned char tag = STK_DATA_OBJECT_TYPE_MMS_TRANSFER_STATUS; /* * Assume the length is never 0 for a valid Result message, however * the whole data object's presence is conditional. */ if (mts->len == 0) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, mts->status, mts->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.84 */ static gboolean build_dataobj_i_wlan_access_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_i_wlan_access_status *status = data; unsigned char tag = STK_DATA_OBJECT_TYPE_I_WLAN_ACCESS_STATUS; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, *status) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.86 */ static gboolean build_dataobj_mms_notification(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *msg = data; unsigned char tag = STK_DATA_OBJECT_TYPE_MMS_NOTIFICATION; return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) && stk_tlv_builder_append_bytes(tlv, msg->array, msg->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.87 */ static gboolean build_dataobj_last_envelope(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const ofono_bool_t *last = data; unsigned char tag = STK_DATA_OBJECT_TYPE_LAST_ENVELOPE; if (!*last) return TRUE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.88 */ static gboolean build_dataobj_registry_application_data( struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_registry_application_data *rad = data; unsigned char tag = STK_DATA_OBJECT_TYPE_REGISTRY_APPLICATION_DATA; guint8 dcs, *name; gsize len; long gsmlen; name = convert_utf8_to_gsm(rad->name, -1, NULL, &gsmlen, 0); len = gsmlen; dcs = 0x04; if (name == NULL) { name = (guint8 *) g_convert((const gchar *) rad->name, -1, "UCS-2BE", "UTF-8//TRANSLIT", NULL, &len, NULL); dcs = 0x08; if (name == NULL) return FALSE; } return stk_tlv_builder_open_container(tlv, cr, tag, TRUE) && stk_tlv_builder_append_short(tlv, rad->port) && stk_tlv_builder_append_byte(tlv, dcs) && stk_tlv_builder_append_byte(tlv, rad->type) && stk_tlv_builder_append_bytes(tlv, name, len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.90 */ static gboolean build_dataobj_broadcast_network_information( struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_broadcast_network_information *bni = data; unsigned char tag = STK_DATA_OBJECT_TYPE_BROADCAST_NETWORK_INFO; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, bni->tech) && stk_tlv_builder_append_bytes(tlv, bni->loc_info, bni->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.91 / 3GPP 24.008 Section 10.5.5.15 */ static gboolean build_dataobj_routing_area_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_routing_area_info *rai = data; unsigned char tag = STK_DATA_OBJECT_TYPE_ROUTING_AREA_INFO; guint8 mccmnc[3]; if (rai->mcc[0] == 0) return TRUE; sim_encode_mcc_mnc(mccmnc, rai->mcc, rai->mnc); return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, mccmnc, 3) && stk_tlv_builder_append_short(tlv, rai->lac) && stk_tlv_builder_append_byte(tlv, rai->rac) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.92 */ static gboolean build_dataobj_update_attach_type(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_update_attach_type *type = data; unsigned char tag = STK_DATA_OBJECT_TYPE_UPDATE_ATTACH_TYPE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, *type) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.93 */ static gboolean build_dataobj_rejection_cause_code(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_rejection_cause_code *cause = data; unsigned char tag = STK_DATA_OBJECT_TYPE_REJECTION_CAUSE_CODE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, *cause) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.98, 3GPP 24.301 Section 6.5.1 */ static gboolean build_dataobj_eps_pdn_conn_params(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *params = data; unsigned char tag = STK_DATA_OBJECT_TYPE_EPS_PDN_CONN_ACTIVATION_REQ; if (params->len < 1) return TRUE; if (params->len > 0x7f) return FALSE; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, params->array, params->len) && stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.99 / 3GPP 24.301 Section 9.9.3.32 */ static gboolean build_dataobj_tracking_area_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_tracking_area_id *tai = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TRACKING_AREA_ID; guint8 mccmnc[3]; if (tai->mcc[0] == 0) return TRUE; sim_encode_mcc_mnc(mccmnc, tai->mcc, tai->mnc); return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_bytes(tlv, mccmnc, 3) && stk_tlv_builder_append_short(tlv, tai->tac) && stk_tlv_builder_close_container(tlv); } static gboolean build_dataobj(struct stk_tlv_builder *tlv, dataobj_writer builder_func, ...) { va_list args; va_start(args, builder_func); while (builder_func) { unsigned int flags = va_arg(args, enum stk_data_object_flag); const void *data = va_arg(args, const void *); gboolean cr = (flags & DATAOBJ_FLAG_CR) ? TRUE : FALSE; if (builder_func(tlv, data, cr) != TRUE) return FALSE; builder_func = va_arg(args, dataobj_writer); } va_end(args); return TRUE; } static gboolean build_setup_call(struct stk_tlv_builder *builder, const struct stk_response *response) { if (response->set_up_call.modified_result.cc_modified) return build_dataobj(builder, build_dataobj_cc_requested_action, DATAOBJ_FLAG_CR, &response->set_up_call.cc_requested_action, build_dataobj_result, DATAOBJ_FLAG_CR, &response->set_up_call.modified_result.result, NULL); else return build_dataobj(builder, build_dataobj_cc_requested_action, DATAOBJ_FLAG_CR, &response->set_up_call.cc_requested_action, NULL); } static gboolean build_local_info(struct stk_tlv_builder *builder, const struct stk_response *response) { const struct stk_response_local_info *info = &response->provide_local_info; int i; switch (response->qualifier) { case 0x00: /* Location Information according to current NAA */ return build_dataobj(builder, build_dataobj_location_info, DATAOBJ_FLAG_CR, &info->location, NULL); case 0x01: /* IMEI of the terminal */ return build_dataobj(builder, build_dataobj_imei, DATAOBJ_FLAG_CR, info->imei, NULL); case 0x02: /* Network Measurement results according to current NAA */ return build_dataobj(builder, build_dataobj_network_measurement_results, DATAOBJ_FLAG_CR, &info->nmr.nmr, build_dataobj_bcch_channel_list, DATAOBJ_FLAG_CR, &info->nmr.bcch_ch_list, NULL); case 0x03: /* Date, time and time zone */ return build_dataobj(builder, build_dataobj_datetime_timezone, DATAOBJ_FLAG_CR, &info->datetime, NULL); case 0x04: /* Language setting */ return build_dataobj(builder, build_dataobj_language, DATAOBJ_FLAG_CR, info->language, NULL); case 0x05: /* Timing Advance */ return build_dataobj(builder, build_dataobj_timing_advance, DATAOBJ_FLAG_CR, &info->tadv, NULL); case 0x06: /* Access Technology (single access technology) */ return build_dataobj(builder, build_dataobj_access_technology, 0, &info->access_technology, NULL); case 0x07: /* ESN of the terminal */ return build_dataobj(builder, build_dataobj_esn, DATAOBJ_FLAG_CR, &info->esn, NULL); case 0x08: /* IMEISV of the terminal */ return build_dataobj(builder, build_dataobj_imeisv, DATAOBJ_FLAG_CR, info->imeisv, NULL); case 0x09: /* Search Mode */ return build_dataobj(builder, build_dataobj_network_search_mode, 0, &info->search_mode, NULL); case 0x0a: /* Charge State of the Battery */ return build_dataobj(builder, build_dataobj_battery_state, DATAOBJ_FLAG_CR, &info->battery_charge, NULL); case 0x0b: /* MEID of the terminal */ return build_dataobj(builder, build_dataobj_meid, 0, info->meid, NULL); case 0x0d: /* Broadcast Network Information according to current tech */ return build_dataobj(builder, build_dataobj_broadcast_network_information, 0, &info->broadcast_network_info, NULL); case 0x0e: /* Multiple Access Technologies */ return build_dataobj(builder, build_dataobj_access_technologies, 0, &info->access_technologies, NULL); case 0x0f: /* Location Information for multiple NAAs */ if (build_dataobj(builder, build_dataobj_access_technologies, 0, &info->location_infos.access_techs, NULL) != TRUE) return FALSE; for (i = 0; i < info->location_infos.access_techs.length; i++) { dataobj_writer location = build_dataobj_location_info; /* * "If no location information is available for an * access technology, the respective data object * shall have length zero." */ if (info->location_infos.locations[i].mcc[0] == '\0') location = build_empty_dataobj_location_info; if (build_dataobj(builder, location, 0, &info->location_infos.locations[i], NULL) != TRUE) return FALSE; } return TRUE; case 0x10: /* Network Measurement results for multiple NAAs */ if (build_dataobj(builder, build_dataobj_access_technologies, 0, &info->nmrs.access_techs, NULL) != TRUE) return FALSE; for (i = 0; i < info->nmrs.access_techs.length; i++) if (build_dataobj(builder, build_dataobj_network_measurement_results, 0, &info->nmrs.nmrs[i].nmr, build_dataobj_bcch_channel_list, 0, &info->nmrs.nmrs[i].bcch_ch_list, NULL) != TRUE) return FALSE; return TRUE; } return FALSE; } static gboolean build_open_channel(struct stk_tlv_builder *builder, const struct stk_response *response) { const struct stk_response_open_channel *open_channel = &response->open_channel; /* insert channel identifier only in case of success */ if (response->result.type == STK_RESULT_TYPE_SUCCESS) { if (build_dataobj(builder, build_dataobj_channel_status, 0, &open_channel->channel, NULL) != TRUE) return FALSE; } return build_dataobj(builder, build_dataobj_bearer_description, 0, &open_channel->bearer_desc, build_dataobj_buffer_size, 0, &open_channel->buf_size, NULL); } static gboolean build_receive_data(struct stk_tlv_builder *builder, const struct stk_response *response) { const struct stk_response_receive_data *receive_data = &response->receive_data; if (response->result.type != STK_RESULT_TYPE_SUCCESS && response->result.type != STK_RESULT_TYPE_MISSING_INFO) return TRUE; if (receive_data->rx_data.len) { if (build_dataobj(builder, build_dataobj_channel_data, DATAOBJ_FLAG_CR, &response->receive_data.rx_data, NULL) != TRUE) return FALSE; } return build_dataobj(builder, build_dataobj_channel_data_length, DATAOBJ_FLAG_CR, &response->receive_data.rx_remaining, NULL); } static gboolean build_send_data(struct stk_tlv_builder *builder, const struct stk_response *response) { if (response->result.type != STK_RESULT_TYPE_SUCCESS) return TRUE; return build_dataobj(builder, build_dataobj_channel_data_length, DATAOBJ_FLAG_CR, &response->send_data.tx_avail, NULL); } const unsigned char *stk_pdu_from_response(const struct stk_response *response, unsigned int *out_length) { struct stk_tlv_builder builder; gboolean ok = TRUE; unsigned char tag; static unsigned char pdu[512]; stk_tlv_builder_init(&builder, pdu, sizeof(pdu)); /* * Encode command details, they come in order with * Command Details TLV first, followed by Device Identities TLV * and the Result TLV. Comprehension required everywhere. */ tag = STK_DATA_OBJECT_TYPE_COMMAND_DETAILS; if (stk_tlv_builder_open_container(&builder, TRUE, tag, FALSE) == FALSE) return NULL; if (stk_tlv_builder_append_byte(&builder, response->number) == FALSE) return NULL; if (stk_tlv_builder_append_byte(&builder, response->type) == FALSE) return NULL; if (stk_tlv_builder_append_byte(&builder, response->qualifier) == FALSE) return NULL; if (stk_tlv_builder_close_container(&builder) == FALSE) return NULL; /* * TS 102 223 section 6.8 states: * "For all COMPREHENSION-TLV objects with Min = N, the terminal * should set the CR flag to comprehension not required." * All the data objects except "Command Details" and "Result" have * Min = N. * * However comprehension required is set for many of the TLVs in * TS 102 384 conformance tests so we set it per command and per * data object type. */ tag = STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES; if (stk_tlv_builder_open_container(&builder, TRUE, tag, FALSE) == FALSE) return NULL; if (stk_tlv_builder_append_byte(&builder, response->src) == FALSE) return NULL; if (stk_tlv_builder_append_byte(&builder, response->dst) == FALSE) return NULL; if (stk_tlv_builder_close_container(&builder) == FALSE) return NULL; if (build_dataobj_result(&builder, &response->result, TRUE) != TRUE) return NULL; switch (response->type) { case STK_COMMAND_TYPE_DISPLAY_TEXT: break; case STK_COMMAND_TYPE_GET_INKEY: ok = build_dataobj(&builder, build_dataobj_text, DATAOBJ_FLAG_CR, &response->get_inkey.text, build_dataobj_duration, 0, &response->get_inkey.duration, NULL); break; case STK_COMMAND_TYPE_GET_INPUT: ok = build_dataobj(&builder, build_dataobj_text, DATAOBJ_FLAG_CR, &response->get_input.text, NULL); break; case STK_COMMAND_TYPE_MORE_TIME: case STK_COMMAND_TYPE_SEND_SMS: case STK_COMMAND_TYPE_PLAY_TONE: break; case STK_COMMAND_TYPE_POLL_INTERVAL: ok = build_dataobj(&builder, build_dataobj_duration, DATAOBJ_FLAG_CR, &response->poll_interval.max_interval, NULL); break; case STK_COMMAND_TYPE_REFRESH: case STK_COMMAND_TYPE_SETUP_MENU: break; case STK_COMMAND_TYPE_SELECT_ITEM: ok = build_dataobj(&builder, build_dataobj_item_id, DATAOBJ_FLAG_CR, &response->select_item.item_id, NULL); break; case STK_COMMAND_TYPE_SEND_SS: break; case STK_COMMAND_TYPE_SETUP_CALL: ok = build_setup_call(&builder, response); break; case STK_COMMAND_TYPE_POLLING_OFF: break; case STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO: ok = build_local_info(&builder, response); break; case STK_COMMAND_TYPE_SETUP_EVENT_LIST: break; case STK_COMMAND_TYPE_TIMER_MANAGEMENT: ok = build_dataobj(&builder, build_dataobj_timer_id, DATAOBJ_FLAG_CR, &response->timer_mgmt.id, build_dataobj_timer_value, DATAOBJ_FLAG_CR, &response->timer_mgmt.value, NULL); break; case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT: break; case STK_COMMAND_TYPE_RUN_AT_COMMAND: ok = build_dataobj(&builder, build_dataobj_at_response, DATAOBJ_FLAG_CR, response->run_at_command.at_response, NULL); break; case STK_COMMAND_TYPE_SEND_DTMF: case STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION: case STK_COMMAND_TYPE_LAUNCH_BROWSER: case STK_COMMAND_TYPE_CLOSE_CHANNEL: break; case STK_COMMAND_TYPE_SEND_USSD: ok = build_dataobj(&builder, build_dataobj_ussd_text, DATAOBJ_FLAG_CR, &response->send_ussd.text, NULL); break; case STK_COMMAND_TYPE_OPEN_CHANNEL: ok = build_open_channel(&builder, response); break; case STK_COMMAND_TYPE_RECEIVE_DATA: ok = build_receive_data(&builder, response); break; case STK_COMMAND_TYPE_SEND_DATA: ok = build_send_data(&builder, response); break; case STK_COMMAND_TYPE_GET_CHANNEL_STATUS: ok = build_dataobj(&builder, build_dataobj_channel_status, DATAOBJ_FLAG_CR, &response->channel_status.channel, NULL); break; default: return NULL; }; if (ok != TRUE) return NULL; if (out_length) *out_length = stk_tlv_builder_get_length(&builder); return pdu; } /* Described in TS 102.223 Section 8.7 */ static gboolean build_envelope_dataobj_device_ids(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_envelope *envelope = data; unsigned char tag = STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES; return stk_tlv_builder_open_container(tlv, cr, tag, FALSE) && stk_tlv_builder_append_byte(tlv, envelope->src) && stk_tlv_builder_append_byte(tlv, envelope->dst) && stk_tlv_builder_close_container(tlv); } static gboolean build_envelope_call_control( struct stk_tlv_builder *builder, const struct stk_envelope *envelope) { const struct stk_envelope_call_control *cc = &envelope->call_control; gboolean ok = FALSE; if (build_dataobj(builder, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, NULL) != TRUE) return FALSE; switch (cc->type) { case STK_CC_TYPE_CALL_SETUP: ok = build_dataobj(builder, build_dataobj_address, DATAOBJ_FLAG_CR, &cc->address, NULL); break; case STK_CC_TYPE_SUPPLEMENTARY_SERVICE: ok = build_dataobj(builder, build_dataobj_ss_string, DATAOBJ_FLAG_CR, &cc->ss_string, NULL); break; case STK_CC_TYPE_USSD_OP: ok = build_dataobj(builder, build_dataobj_ussd_string, DATAOBJ_FLAG_CR, &cc->ussd_string, NULL); break; case STK_CC_TYPE_PDP_CTX_ACTIVATION: ok = build_dataobj(builder, build_dataobj_pdp_context_params, DATAOBJ_FLAG_CR, &cc->pdp_ctx_params, NULL); break; case STK_CC_TYPE_EPS_PDN_CONNECTION_ACTIVATION: ok = build_dataobj(builder, build_dataobj_eps_pdn_conn_params, DATAOBJ_FLAG_CR, &cc->eps_pdn_params, NULL); break; } if (ok != TRUE) return FALSE; return build_dataobj(builder, build_dataobj_ccp, 0, &cc->ccp1, build_dataobj_subaddress, 0, &cc->subaddress, build_dataobj_location_info, 0, &cc->location, build_dataobj_ccp, 0, &cc->ccp2, build_dataobj_alpha_id, 0, cc->alpha_id, build_dataobj_bc_repeat, 0, &cc->bc_repeat, NULL); } static gboolean build_envelope_event_download(struct stk_tlv_builder *builder, const struct stk_envelope *envelope) { const struct stk_envelope_event_download *evt = &envelope->event_download; if (build_dataobj(builder, build_dataobj_event_type, DATAOBJ_FLAG_CR, &evt->type, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, NULL) == FALSE) return FALSE; switch (evt->type) { case STK_EVENT_TYPE_MT_CALL: return build_dataobj(builder, build_dataobj_transaction_id, DATAOBJ_FLAG_CR, &evt->mt_call.transaction_id, build_dataobj_address, 0, &evt->mt_call.caller_address, build_dataobj_subaddress, 0, &evt->mt_call.caller_subaddress, NULL); case STK_EVENT_TYPE_CALL_CONNECTED: return build_dataobj(builder, build_dataobj_transaction_id, DATAOBJ_FLAG_CR, &evt->call_connected.transaction_id, NULL); case STK_EVENT_TYPE_CALL_DISCONNECTED: return build_dataobj(builder, build_dataobj_transaction_ids, DATAOBJ_FLAG_CR, &evt->call_disconnected.transaction_ids, build_dataobj_cause, 0, &evt->call_disconnected.cause, NULL); case STK_EVENT_TYPE_LOCATION_STATUS: return build_dataobj(builder, build_dataobj_location_status, DATAOBJ_FLAG_CR, &evt->location_status.state, build_dataobj_location_info, 0, &evt->location_status.info, NULL); case STK_EVENT_TYPE_USER_ACTIVITY: case STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE: return TRUE; case STK_EVENT_TYPE_CARD_READER_STATUS: return build_dataobj(builder, build_dataobj_card_reader_status, DATAOBJ_FLAG_CR, &evt->card_reader_status, NULL); case STK_EVENT_TYPE_LANGUAGE_SELECTION: return build_dataobj(builder, build_dataobj_language, DATAOBJ_FLAG_CR, evt->language_selection, NULL); case STK_EVENT_TYPE_BROWSER_TERMINATION: return build_dataobj(builder, build_dataobj_browser_termination_cause, DATAOBJ_FLAG_CR, &evt->browser_termination.cause, NULL); case STK_EVENT_TYPE_DATA_AVAILABLE: return build_dataobj(builder, build_dataobj_channel_status, DATAOBJ_FLAG_CR, &evt->data_available.channel, build_dataobj_channel_data_length, DATAOBJ_FLAG_CR, &evt->data_available.channel_data_len, NULL); case STK_EVENT_TYPE_CHANNEL_STATUS: return build_dataobj(builder, build_dataobj_channel_status, DATAOBJ_FLAG_CR, &evt->channel_status.channel, build_dataobj_bearer_description, DATAOBJ_FLAG_CR, &evt->channel_status.bearer_desc, build_dataobj_other_address, DATAOBJ_FLAG_CR, &evt->channel_status.address, NULL); case STK_EVENT_TYPE_SINGLE_ACCESS_TECHNOLOGY_CHANGE: return build_dataobj(builder, build_dataobj_access_technology, DATAOBJ_FLAG_CR, &evt->access_technology_change, NULL); case STK_EVENT_TYPE_DISPLAY_PARAMETERS_CHANGED: return build_dataobj(builder, build_dataobj_display_parameters, DATAOBJ_FLAG_CR, &evt->display_params_changed, NULL); case STK_EVENT_TYPE_LOCAL_CONNECTION: return build_dataobj(builder, build_dataobj_service_record, DATAOBJ_FLAG_CR, &evt->local_connection.service_record, build_dataobj_remote_entity_address, 0, &evt->local_connection.remote_addr, build_dataobj_uicc_te_interface, 0, &evt->local_connection.transport_level, build_dataobj_other_address, 0, &evt->local_connection.transport_addr, NULL); case STK_EVENT_TYPE_NETWORK_SEARCH_MODE_CHANGE: return build_dataobj(builder, build_dataobj_network_search_mode, DATAOBJ_FLAG_CR, &evt->network_search_mode_change, NULL); case STK_EVENT_TYPE_BROWSING_STATUS: return build_dataobj(builder, build_dataobj_browsing_status, DATAOBJ_FLAG_CR, &evt->browsing_status, NULL); case STK_EVENT_TYPE_FRAMES_INFORMATION_CHANGE: return build_dataobj(builder, build_dataobj_frames_information, DATAOBJ_FLAG_CR, &evt->frames_information_change, NULL); case STK_EVENT_TYPE_I_WLAN_ACCESS_STATUS: return build_dataobj(builder, build_dataobj_i_wlan_access_status, DATAOBJ_FLAG_CR, &evt->i_wlan_access_status, NULL); case STK_EVENT_TYPE_NETWORK_REJECTION: return build_dataobj(builder, build_dataobj_location_info, 0, &evt->network_rejection.location, build_dataobj_routing_area_id, 0, &evt->network_rejection.rai, build_dataobj_tracking_area_id, 0, &evt->network_rejection.tai, build_dataobj_access_technology, DATAOBJ_FLAG_CR, &evt->network_rejection.access_tech, build_dataobj_update_attach_type, DATAOBJ_FLAG_CR, &evt->network_rejection.update_attach, build_dataobj_rejection_cause_code, DATAOBJ_FLAG_CR, &evt->network_rejection.cause, NULL); case STK_EVENT_TYPE_HCI_CONNECTIVITY_EVENT: return TRUE; default: return FALSE; } } static gboolean build_envelope_terminal_apps(struct stk_tlv_builder *builder, const struct stk_envelope *envelope) { const struct stk_envelope_terminal_apps *ta = &envelope->terminal_apps; int i; if (build_dataobj(builder, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, NULL) == FALSE) return FALSE; for (i = 0; i < ta->count; i++) if (build_dataobj(builder, build_dataobj_registry_application_data, 0, &ta->list[i], NULL) == FALSE) return FALSE; return build_dataobj(builder, build_dataobj_last_envelope, 0, &ta->last, NULL); } const unsigned char *stk_pdu_from_envelope(const struct stk_envelope *envelope, unsigned int *out_length) { struct ber_tlv_builder btlv; struct stk_tlv_builder builder; gboolean ok = TRUE; static unsigned char buffer[512]; unsigned char *pdu; if (ber_tlv_builder_init(&btlv, buffer, sizeof(buffer)) != TRUE) return NULL; if (stk_tlv_builder_recurse(&builder, &btlv, envelope->type) != TRUE) return NULL; switch (envelope->type) { case STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD: ok = build_dataobj(&builder, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, build_dataobj_address, 0, &envelope->sms_pp_download.address, build_dataobj_gsm_sms_tpdu, DATAOBJ_FLAG_CR, &envelope->sms_pp_download.message, NULL); break; case STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD: ok = build_dataobj(&builder, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, build_dataobj_cbs_page, DATAOBJ_FLAG_CR, &envelope->cbs_pp_download.page, NULL); break; case STK_ENVELOPE_TYPE_MENU_SELECTION: ok = build_dataobj(&builder, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, build_dataobj_item_id, DATAOBJ_FLAG_CR, &envelope->menu_selection.item_id, build_dataobj_help_request, 0, &envelope->menu_selection.help_request, NULL); break; case STK_ENVELOPE_TYPE_CALL_CONTROL: ok = build_envelope_call_control(&builder, envelope); break; case STK_ENVELOPE_TYPE_MO_SMS_CONTROL: /* * Comprehension Required according to the specs but not * enabled in conformance tests in 3GPP 31.124. */ ok = build_dataobj(&builder, build_envelope_dataobj_device_ids, 0, envelope, build_dataobj_address, 0, &envelope->sms_mo_control.sc_address, build_dataobj_address, 0, &envelope->sms_mo_control.dest_address, build_dataobj_location_info, 0, &envelope->sms_mo_control.location, NULL); break; case STK_ENVELOPE_TYPE_EVENT_DOWNLOAD: ok = build_envelope_event_download(&builder, envelope); break; case STK_ENVELOPE_TYPE_TIMER_EXPIRATION: ok = build_dataobj(&builder, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, build_dataobj_timer_id, DATAOBJ_FLAG_CR, &envelope->timer_expiration.id, build_dataobj_timer_value, DATAOBJ_FLAG_CR, &envelope->timer_expiration.value, NULL); break; case STK_ENVELOPE_TYPE_USSD_DOWNLOAD: ok = build_dataobj(&builder, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, build_dataobj_ussd_string, DATAOBJ_FLAG_CR, &envelope->ussd_data_download.string, NULL); break; case STK_ENVELOPE_TYPE_MMS_TRANSFER_STATUS: ok = build_dataobj(&builder, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, build_dataobj_file, DATAOBJ_FLAG_CR, &envelope->mms_status.transfer_file, build_dataobj_mms_id, 0, &envelope->mms_status.id, build_dataobj_mms_transfer_status, 0, &envelope->mms_status.transfer_status, NULL); break; case STK_ENVELOPE_TYPE_MMS_NOTIFICATION: ok = build_dataobj(&builder, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, build_dataobj_mms_notification, DATAOBJ_FLAG_CR, &envelope->mms_notification.msg, build_dataobj_last_envelope, 0, &envelope->mms_notification.last, NULL); break; case STK_ENVELOPE_TYPE_TERMINAL_APP: ok = build_envelope_terminal_apps(&builder, envelope); break; default: return NULL; }; if (ok != TRUE) return NULL; ber_tlv_builder_optimize(&btlv, &pdu, out_length); return pdu; } static const char *html_colors[] = { "#000000", /* Black */ "#808080", /* Dark Grey */ "#C11B17", /* Dark Red */ "#FBB117", /* Dark Yellow */ "#347235", /* Dark Green */ "#307D7E", /* Dark Cyan */ "#0000A0", /* Dark Blue */ "#C031C7", /* Dark Magenta */ "#C0C0C0", /* Grey */ "#FFFFFF", /* White */ "#FF0000", /* Bright Red */ "#FFFF00", /* Bright Yellow */ "#00FF00", /* Bright Green */ "#00FFFF", /* Bright Cyan */ "#0000FF", /* Bright Blue */ "#FF00FF", /* Bright Magenta */ }; #define STK_TEXT_FORMAT_ALIGN_MASK 0x03 #define STK_TEXT_FORMAT_FONT_MASK 0x0C #define STK_TEXT_FORMAT_STYLE_MASK 0xF0 #define STK_DEFAULT_TEXT_ALIGNMENT 0x00 #define STK_TEXT_FORMAT_INIT 0x9003 /* Defined in ETSI 123 40 9.2.3.24.10.1.1 */ enum stk_text_format_code { STK_TEXT_FORMAT_LEFT_ALIGN = 0x00, STK_TEXT_FORMAT_CENTER_ALIGN = 0x01, STK_TEXT_FORMAT_RIGHT_ALIGN = 0x02, STK_TEXT_FORMAT_NO_ALIGN = 0x03, STK_TEXT_FORMAT_FONT_SIZE_LARGE = 0x04, STK_TEXT_FORMAT_FONT_SIZE_SMALL = 0x08, STK_TEXT_FORMAT_FONT_SIZE_RESERVED = 0x0c, STK_TEXT_FORMAT_STYLE_BOLD = 0x10, STK_TEXT_FORMAT_STYLE_ITALIC = 0x20, STK_TEXT_FORMAT_STYLE_UNDERLINED = 0x40, STK_TEXT_FORMAT_STYLE_STRIKETHROUGH = 0x80, }; static void end_format(GString *string, guint16 attr) { guint code = attr & 0xFF; guint color = (attr >> 8) & 0xFF; if ((code & ~STK_TEXT_FORMAT_ALIGN_MASK) || color) g_string_append(string, ""); if ((code & STK_TEXT_FORMAT_ALIGN_MASK) != STK_TEXT_FORMAT_NO_ALIGN) g_string_append(string, ""); } static void start_format(GString *string, guint16 attr) { guint8 code = attr & 0xFF; guint8 color = (attr >> 8) & 0xFF; guint8 align = code & STK_TEXT_FORMAT_ALIGN_MASK; guint8 font = code & STK_TEXT_FORMAT_FONT_MASK; guint8 style = code & STK_TEXT_FORMAT_STYLE_MASK; int fg = color & 0x0f; int bg = (color >> 4) & 0x0f; /* align formatting applies to a block of text */ if (align != STK_TEXT_FORMAT_NO_ALIGN) g_string_append(string, "
"); break; case STK_TEXT_FORMAT_CENTER_ALIGN: g_string_append(string, "text-align: center;\">"); break; case STK_TEXT_FORMAT_LEFT_ALIGN: g_string_append(string, "text-align: left;\">"); break; } if ((font == 0) && (style == 0) && (color == 0)) return; /* font, style, and color are inline */ g_string_append(string, ""); } char *stk_text_to_html(const char *utf8, const unsigned short *attrs, int num_attrs) { long text_len = g_utf8_strlen(utf8, -1); GString *string = g_string_sized_new(strlen(utf8) + 1); short *formats; int pos, i, j; guint16 start, end, len, attr, prev_attr; guint8 code, color, align; const char *text = utf8; int attrs_len = num_attrs * 4; formats = g_try_new0(gint16, (text_len + 1)); if (formats == NULL) { g_string_free(string, TRUE); return NULL; } /* we will need formatting at the position beyond the last char */ for (i = 0; i <= text_len; i++) formats[i] = STK_TEXT_FORMAT_INIT; for (i = 0; i < attrs_len; i += 4) { start = attrs[i]; len = attrs[i + 1]; code = attrs[i + 2] & 0xFF; color = attrs[i + 3] & 0xFF; if (len == 0) end = text_len; else end = start + len; /* sanity check values */ if (start > end || end > text_len) continue; /* * if the alignment is the same as either the default * or the last alignment used, don't set any alignment * value. */ if (start == 0) align = STK_TEXT_FORMAT_NO_ALIGN; else { align = formats[start - 1] & STK_TEXT_FORMAT_ALIGN_MASK; } if ((code & STK_TEXT_FORMAT_ALIGN_MASK) == align) code |= STK_TEXT_FORMAT_NO_ALIGN; attr = code | (color << 8); for (j = start; j < end; j++) formats[j] = attr; } prev_attr = STK_TEXT_FORMAT_INIT; for (pos = 0; pos <= text_len; pos++) { attr = formats[pos]; if (attr != prev_attr) { if (prev_attr != STK_TEXT_FORMAT_INIT) end_format(string, prev_attr); if (attr != STK_TEXT_FORMAT_INIT) start_format(string, attr); prev_attr = attr; } if (pos == text_len) break; switch (g_utf8_get_char(text)) { case '\n': g_string_append(string, "
"); break; case '\r': { char *next = g_utf8_next_char(text); gunichar c = g_utf8_get_char(next); g_string_append(string, "
"); if ((pos + 1 < text_len) && (c == '\n')) { text = g_utf8_next_char(text); pos++; } break; } case '<': g_string_append(string, "<"); break; case '>': g_string_append(string, ">"); break; case '&': g_string_append(string, "&"); break; default: g_string_append_unichar(string, g_utf8_get_char(text)); } text = g_utf8_next_char(text); } g_free(formats); /* return characters from string. Caller must free char data */ return g_string_free(string, FALSE); } static const char chars_table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '+', '.' }; char *stk_image_to_xpm(const unsigned char *img, unsigned int len, enum stk_img_scheme scheme, const unsigned char *clut, unsigned short clut_len) { guint8 width, height; unsigned int ncolors, nbits, entry, cpp; unsigned int i, j; int bit, k; GString *xpm; unsigned int pos = 0; const char xpm_header[] = "/* XPM */\n"; const char declaration[] = "static char *xpm[] = {\n"; char c[3]; if (img == NULL) return NULL; /* sanity check length */ if (len < 3) return NULL; width = img[pos++]; height = img[pos++]; if (scheme == STK_IMG_SCHEME_BASIC) { nbits = 1; ncolors = 2; } else { /* sanity check length */ if ((pos + 4 > len) || (clut == NULL)) return NULL; nbits = img[pos++]; ncolors = img[pos++]; /* the value of zero should be interpreted as 256 */ if (ncolors == 0) ncolors = 256; /* skip clut offset bytes */ pos += 2; if ((ncolors * 3) > clut_len) return NULL; } if (pos + ((width * height + 7) / 8) > len) return NULL; /* determine the number of chars need to represent the pixel */ cpp = ncolors > 64 ? 2 : 1; /* * space needed: * header line * declaration and beginning of assignment line * values - max length of 19 * colors - ncolors * (cpp + whitespace + deliminators + color) * pixels - width * height * cpp + height deliminators "",\n * end of assignment - 2 chars "};" */ xpm = g_string_sized_new(strlen(xpm_header) + strlen(declaration) + 19 + ((cpp + 14) * ncolors) + (width * height * cpp) + (4 * height) + 2); if (xpm == NULL) return NULL; /* add header, declaration, values */ g_string_append(xpm, xpm_header); g_string_append(xpm, declaration); g_string_append_printf(xpm, "\"%d %d %d %d\",\n", width, height, ncolors, cpp); /* create colors */ if (scheme == STK_IMG_SCHEME_BASIC) { g_string_append(xpm, "\"0\tc #000000\",\n"); g_string_append(xpm, "\"1\tc #FFFFFF\",\n"); } else { for (i = 0; i < ncolors; i++) { /* lookup char representation of this number */ if (ncolors > 64) { c[0] = chars_table[i / 64]; c[1] = chars_table[i % 64]; c[2] = '\0'; } else { c[0] = chars_table[i % 64]; c[1] = '\0'; } if ((i == (ncolors - 1)) && scheme == STK_IMG_SCHEME_TRANSPARENCY) g_string_append_printf(xpm, "\"%s\tc None\",\n", c); else g_string_append_printf(xpm, "\"%s\tc #%02hhX%02hhX%02hhX\",\n", c, clut[0], clut[1], clut[2]); clut += 3; } } /* height rows of width pixels */ k = 7; for (i = 0; i < height; i++) { g_string_append(xpm, "\""); for (j = 0; j < width; j++) { entry = 0; for (bit = nbits - 1; bit >= 0; bit--) { entry |= (img[pos] >> k & 0x1) << bit; k--; /* see if we crossed a byte boundary */ if (k < 0) { k = 7; pos++; } } /* lookup char representation of this number */ if (ncolors > 64) { c[0] = chars_table[entry / 64]; c[1] = chars_table[entry % 64]; c[2] = '\0'; } else { c[0] = chars_table[entry % 64]; c[1] = '\0'; } g_string_append_printf(xpm, "%s", c); } g_string_append(xpm, "\",\n"); } g_string_append(xpm, "};"); /* Caller must free char data */ return g_string_free(xpm, FALSE); } ofono-1.17.bzr6912+16.04.20160314.3/src/sim-mnclength.c0000644000015600001650000000345712671500024022110 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2013 Canonical 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. * * 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 #endif #include #include #include #include "ofono.h" #include "sim-mnclength.h" static GSList *g_drivers = NULL; int __ofono_sim_mnclength_get_mnclength(const char *imsi) { GSList *d; int mnclen = -ENOTSUP; for (d = g_drivers; d != NULL; d = d->next) { const struct ofono_sim_mnclength_driver *driver = d->data; if (driver->get_mnclength == NULL) continue; DBG("Calling mnclength plugin '%s'", driver->name); mnclen = driver->get_mnclength(imsi); if (mnclen <= 0) continue; return mnclen; } return mnclen; } int ofono_sim_mnclength_driver_register( struct ofono_sim_mnclength_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_prepend(g_drivers, driver); return 0; } void ofono_sim_mnclength_driver_unregister( const struct ofono_sim_mnclength_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_remove(g_drivers, driver); } ofono-1.17.bzr6912+16.04.20160314.3/src/hfp.h0000644000015600001650000000553212671500024020121 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 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 * */ /* HFP AG supported features bitmap. Bluetooth HFP 1.6 spec page 88 */ enum hfp_ag_feature { HFP_AG_FEATURE_3WAY = 0x1, HFP_AG_FEATURE_ECNR = 0x2, HFP_AG_FEATURE_VOICE_RECOG = 0x4, HFP_AG_FEATURE_IN_BAND_RING_TONE = 0x8, HFP_AG_FEATURE_ATTACH_VOICE_TAG = 0x10, HFP_AG_FEATURE_REJECT_CALL = 0x20, HFP_AG_FEATURE_ENHANCED_CALL_STATUS = 0x40, HFP_AG_FEATURE_ENHANCED_CALL_CONTROL = 0x80, HFP_AG_FEATURE_EXTENDED_RES_CODE = 0x100, HFP_AG_FEATURE_CODEC_NEGOTIATION = 0x200, HFP_AG_FEATURE_HF_INDICATORS = 0x400, }; /* HFP HF supported features bitmap. Bluetooth HFP 1.6 spec page 88 */ enum hfp_hf_feature { HFP_HF_FEATURE_ECNR = 0x1, HFP_HF_FEATURE_3WAY = 0x2, HFP_HF_FEATURE_CLIP = 0x4, HFP_HF_FEATURE_VOICE_RECOGNITION = 0x8, HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL = 0x10, HFP_HF_FEATURE_ENHANCED_CALL_STATUS = 0x20, HFP_HF_FEATURE_ENHANCED_CALL_CONTROL = 0x40, HFP_HF_FEATURE_CODEC_NEGOTIATION = 0x80, HFP_HF_FEATURE_HF_INDICATORS = 0x100, }; /* HFP AG supported call hold and multiparty services bitmap. Bluetooth HFP 1.6 spec page 76 */ enum hfp_ag_chld_feature { HFP_AG_CHLD_0 = 0x1, HFP_AG_CHLD_1 = 0x2, HFP_AG_CHLD_1x = 0x4, HFP_AG_CHLD_2 = 0x8, HFP_AG_CHLD_2x = 0x10, HFP_AG_CHLD_3 = 0x20, HFP_AG_CHLD_4 = 0x40, }; enum hfp_sdp_hf_features { HFP_SDP_HF_FEATURE_ECNR = 0x1, HFP_SDP_HF_FEATURE_3WAY = 0x2, HFP_SDP_HF_FEATURE_CLIP = 0x4, HFP_SDP_HF_FEATURE_VOICE_RECOGNITION = 0x8, HFP_SDP_HF_FEATURE_REMOTE_VOLUME_CONTROL = 0x10, HFP_SDP_HF_FEATURE_WIDEBAND_SPEECH = 0x20, }; enum hfp_sdp_ag_features { HFP_SDP_AG_FEATURE_3WAY = 0x1, HFP_SDP_AG_FEATURE_ECNR = 0x2, HFP_SDP_AG_FEATURE_VOICE_RECOG = 0x4, HFP_SDP_AG_FEATURE_IN_BAND_RING_TONE = 0x8, HFP_SDP_AG_FEATURE_ATTACH_VOICE_TAG = 0x10, HFP_SDP_AG_FEATURE_WIDEBAND_SPEECH = 0x20, }; /* Supported agent codecs */ enum hfp_codec { HFP_CODEC_CVSD = 0x01, HFP_CODEC_MSBC = 0x02, }; enum hfp_version { HFP_VERSION_1_5 = 0x0105, HFP_VERSION_1_6 = 0x0106, HFP_VERSION_1_7 = 0x0107, HFP_VERSION_LATEST = HFP_VERSION_1_7, }; enum hfp_hf_indicator { HFP_HF_INDICATOR_ENHANCED_SAFETY = 0x0001, }; ofono-1.17.bzr6912+16.04.20160314.3/src/idmap.c0000644000015600001650000001013212671500024020421 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2004 Red Hat, Inc. 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 #endif #define _GNU_SOURCE #include #include #include "idmap.h" #define BITS_PER_LONG (sizeof(unsigned long) * 8) struct idmap { unsigned long *bits; unsigned int size; unsigned int min; unsigned int max; }; static inline int ffz(unsigned long word) { return __builtin_ctzl(~word); } /* * Stolen from linux kernel lib/find_next_bit.c */ static unsigned int find_next_zero_bit(const unsigned long *addr, unsigned int size, unsigned int offset) { const unsigned long *p = addr + offset / BITS_PER_LONG; unsigned int result = offset & ~(BITS_PER_LONG-1); unsigned long tmp; if (offset >= size) return size; size -= result; offset %= BITS_PER_LONG; if (offset) { tmp = *(p++); tmp |= ~0UL >> (BITS_PER_LONG - offset); if (size < BITS_PER_LONG) goto found_first; if (~tmp) goto found_middle; size -= BITS_PER_LONG; result += BITS_PER_LONG; } while (size & ~(BITS_PER_LONG-1)) { if (~(tmp = *(p++))) goto found_middle; size -= BITS_PER_LONG; result += BITS_PER_LONG; } if (!size) return result; tmp = *p; found_first: tmp |= ~0UL << size; if (tmp == ~0UL) /* Are any bits zero? */ return result + size; /* Nope. */ found_middle: return result + ffz(tmp); } struct idmap *idmap_new_from_range(unsigned int min, unsigned int max) { struct idmap *ret = g_new0(struct idmap, 1); unsigned int size = max - min + 1; ret->bits = g_new0(unsigned long, (size + BITS_PER_LONG - 1) / BITS_PER_LONG); ret->size = size; ret->min = min; ret->max = max; return ret; } struct idmap *idmap_new(unsigned int size) { return idmap_new_from_range(1, size); } void idmap_free(struct idmap *idmap) { g_free(idmap->bits); g_free(idmap); } void idmap_put(struct idmap *idmap, unsigned int id) { unsigned int offset = (id - idmap->min) / BITS_PER_LONG; id -= idmap->min; if (id > idmap->size) return; id %= BITS_PER_LONG; idmap->bits[offset] &= ~(1UL << id); } unsigned int idmap_alloc(struct idmap *idmap) { unsigned int bit; unsigned int offset; bit = find_next_zero_bit(idmap->bits, idmap->size, 0); if (bit >= idmap->size) return idmap->max + 1; offset = bit / BITS_PER_LONG; idmap->bits[offset] |= 1UL << (bit % BITS_PER_LONG); return bit + idmap->min; } void idmap_take(struct idmap *idmap, unsigned int id) { unsigned int bit = id - idmap->min; unsigned int offset; if (bit >= idmap->size) return; offset = bit / BITS_PER_LONG; idmap->bits[offset] |= 1UL << (bit % BITS_PER_LONG); } /* * Allocate the next bit skipping the ids up to and including last. If there * is no free ids until the max id is encountered, the counter is wrapped back * to min and the search starts again. */ unsigned int idmap_alloc_next(struct idmap *idmap, unsigned int last) { unsigned int bit; unsigned int offset; if (last < idmap->min || last > idmap->max) return idmap->max + 1; bit = find_next_zero_bit(idmap->bits, idmap->size, last - idmap->min + 1); if (bit >= idmap->size) return idmap_alloc(idmap); offset = bit / BITS_PER_LONG; idmap->bits[offset] |= 1UL << (bit % BITS_PER_LONG); return bit + idmap->min; } unsigned int idmap_get_min(struct idmap *idmap) { return idmap->min; } unsigned int idmap_get_max(struct idmap *idmap) { return idmap->max; } ofono-1.17.bzr6912+16.04.20160314.3/src/ofono.h0000644000015600001650000004244612671500024020471 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include #define OFONO_API_SUBJECT_TO_CHANGE #include void __ofono_exit(void); int __ofono_manager_init(void); void __ofono_manager_cleanup(void); int __ofono_handsfree_audio_manager_init(void); void __ofono_handsfree_audio_manager_cleanup(void); void __ofono_modem_shutdown(void); #include int __ofono_log_init(const char *program, const char *debug, ofono_bool_t detach); void __ofono_log_cleanup(void); void __ofono_log_enable(struct ofono_debug_desc *start, struct ofono_debug_desc *stop); #include int __ofono_dbus_init(DBusConnection *conn); void __ofono_dbus_cleanup(void); DBusMessage *__ofono_error_invalid_args(DBusMessage *msg); DBusMessage *__ofono_error_invalid_format(DBusMessage *msg); DBusMessage *__ofono_error_not_implemented(DBusMessage *msg); DBusMessage *__ofono_error_failed(DBusMessage *msg); DBusMessage *__ofono_error_busy(DBusMessage *msg); DBusMessage *__ofono_error_not_found(DBusMessage *msg); DBusMessage *__ofono_error_not_active(DBusMessage *msg); DBusMessage *__ofono_error_not_supported(DBusMessage *msg); DBusMessage *__ofono_error_not_available(DBusMessage *msg); DBusMessage *__ofono_error_timed_out(DBusMessage *msg); DBusMessage *__ofono_error_sim_not_ready(DBusMessage *msg); DBusMessage *__ofono_error_in_use(DBusMessage *msg); DBusMessage *__ofono_error_not_attached(DBusMessage *msg); DBusMessage *__ofono_error_attach_in_progress(DBusMessage *msg); DBusMessage *__ofono_error_not_registered(DBusMessage *msg); DBusMessage *__ofono_error_canceled(DBusMessage *msg); DBusMessage *__ofono_error_access_denied(DBusMessage *msg); DBusMessage *__ofono_error_emergency_active(DBusMessage *msg); DBusMessage *__ofono_error_incorrect_password(DBusMessage *msg); DBusMessage *__ofono_error_not_allowed(DBusMessage *msg); DBusMessage *__ofono_error_not_recognized(DBusMessage *msg); DBusMessage *__ofono_error_network_terminated(DBusMessage *msg); DBusMessage *__ofono_error_from_error(const struct ofono_error *error, DBusMessage *msg); void __ofono_dbus_pending_reply(DBusMessage **msg, DBusMessage *reply); gboolean __ofono_dbus_valid_object_path(const char *path); struct ofono_watchlist_item { unsigned int id; void *notify; void *notify_data; ofono_destroy_func destroy; }; struct ofono_watchlist { int next_id; GSList *items; ofono_destroy_func destroy; }; struct ofono_watchlist *__ofono_watchlist_new(ofono_destroy_func destroy); unsigned int __ofono_watchlist_add_item(struct ofono_watchlist *watchlist, struct ofono_watchlist_item *item); gboolean __ofono_watchlist_remove_item(struct ofono_watchlist *watchlist, unsigned int id); void __ofono_watchlist_free(struct ofono_watchlist *watchlist); #include int __ofono_plugin_init(const char *pattern, const char *exclude); void __ofono_plugin_cleanup(void); #include typedef void (*ofono_modem_foreach_func)(struct ofono_modem *modem, void *data); void __ofono_modem_foreach(ofono_modem_foreach_func cb, void *userdata); unsigned int __ofono_modem_callid_next(struct ofono_modem *modem); void __ofono_modem_callid_hold(struct ofono_modem *modem, int id); void __ofono_modem_callid_release(struct ofono_modem *modem, int id); void __ofono_modem_append_properties(struct ofono_modem *modem, DBusMessageIter *dict); struct ofono_atom; enum ofono_atom_type { OFONO_ATOM_TYPE_DEVINFO, OFONO_ATOM_TYPE_CALL_BARRING, OFONO_ATOM_TYPE_CALL_FORWARDING, OFONO_ATOM_TYPE_CALL_METER, OFONO_ATOM_TYPE_CALL_SETTINGS, OFONO_ATOM_TYPE_NETREG, OFONO_ATOM_TYPE_PHONEBOOK, OFONO_ATOM_TYPE_SMS, OFONO_ATOM_TYPE_SIM, OFONO_ATOM_TYPE_USSD, OFONO_ATOM_TYPE_VOICECALL, OFONO_ATOM_TYPE_HISTORY, OFONO_ATOM_TYPE_SSN, OFONO_ATOM_TYPE_MESSAGE_WAITING, OFONO_ATOM_TYPE_CBS, OFONO_ATOM_TYPES_CALL_VOLUME, OFONO_ATOM_TYPE_GPRS, OFONO_ATOM_TYPE_GPRS_CONTEXT, OFONO_ATOM_TYPE_RADIO_SETTINGS, OFONO_ATOM_TYPE_AUDIO_SETTINGS, OFONO_ATOM_TYPE_STK, OFONO_ATOM_TYPE_NETTIME, OFONO_ATOM_TYPE_CTM, OFONO_ATOM_TYPE_CDMA_VOICECALL_MANAGER, OFONO_ATOM_TYPE_CDMA_CONNMAN, OFONO_ATOM_TYPE_SIM_AUTH, OFONO_ATOM_TYPE_EMULATOR_DUN, OFONO_ATOM_TYPE_EMULATOR_HFP, OFONO_ATOM_TYPE_LOCATION_REPORTING, OFONO_ATOM_TYPE_GNSS, OFONO_ATOM_TYPE_CDMA_SMS, OFONO_ATOM_TYPE_CDMA_NETREG, OFONO_ATOM_TYPE_HANDSFREE, OFONO_ATOM_TYPE_SIRI, }; enum ofono_atom_watch_condition { OFONO_ATOM_WATCH_CONDITION_REGISTERED, OFONO_ATOM_WATCH_CONDITION_UNREGISTERED }; typedef void (*ofono_atom_watch_func)(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data); typedef void (*ofono_atom_func)(struct ofono_atom *atom, void *data); struct ofono_atom *__ofono_modem_add_atom(struct ofono_modem *modem, enum ofono_atom_type type, void (*destruct)(struct ofono_atom *), void *data); struct ofono_atom *__ofono_modem_add_atom_offline(struct ofono_modem *modem, enum ofono_atom_type type, void (*destruct)(struct ofono_atom *), void *data); struct ofono_atom *__ofono_modem_find_atom(struct ofono_modem *modem, enum ofono_atom_type type); void __ofono_modem_foreach_atom(struct ofono_modem *modem, enum ofono_atom_type type, ofono_atom_func callback, void *data); void __ofono_modem_foreach_registered_atom(struct ofono_modem *modem, enum ofono_atom_type type, ofono_atom_func callback, void *data); void *__ofono_atom_get_data(struct ofono_atom *atom); const char *__ofono_atom_get_path(struct ofono_atom *atom); struct ofono_modem *__ofono_atom_get_modem(struct ofono_atom *atom); #define __ofono_atom_find(enum_type, modem) \ ({ \ struct ofono_atom *tmp_atom = \ __ofono_modem_find_atom(modem, enum_type); \ \ tmp_atom ? __ofono_atom_get_data(tmp_atom) : NULL; \ }) void __ofono_atom_register(struct ofono_atom *atom, void (*unregister)(struct ofono_atom *)); void __ofono_atom_unregister(struct ofono_atom *atom); gboolean __ofono_atom_get_registered(struct ofono_atom *atom); unsigned int __ofono_modem_add_atom_watch(struct ofono_modem *modem, enum ofono_atom_type type, ofono_atom_watch_func notify, void *data, ofono_destroy_func destroy); gboolean __ofono_modem_remove_atom_watch(struct ofono_modem *modem, unsigned int id); void __ofono_atom_free(struct ofono_atom *atom); typedef void (*ofono_modemwatch_cb_t)(struct ofono_modem *modem, gboolean added, void *data); void __ofono_modemwatch_init(void); void __ofono_modemwatch_cleanup(void); unsigned int __ofono_modemwatch_add(ofono_modemwatch_cb_t cb, void *user, ofono_destroy_func destroy); gboolean __ofono_modemwatch_remove(unsigned int id); typedef void (*ofono_modem_online_notify_func)(struct ofono_modem *modem, ofono_bool_t online, void *data); unsigned int __ofono_modem_add_online_watch(struct ofono_modem *modem, ofono_modem_online_notify_func notify, void *data, ofono_destroy_func destroy); void __ofono_modem_remove_online_watch(struct ofono_modem *modem, unsigned int id); typedef void (*ofono_modem_powered_notify_func)(struct ofono_modem *modem, ofono_bool_t powered, void *data); unsigned int __ofono_modem_add_powered_watch(struct ofono_modem *modem, ofono_modem_online_notify_func notify, void *data, ofono_destroy_func destroy); void __ofono_modem_remove_powered_watch(struct ofono_modem *modem, unsigned int id); void __ofono_modem_sim_reset(struct ofono_modem *modem); void __ofono_modem_inc_emergency_mode(struct ofono_modem *modem); void __ofono_modem_dec_emergency_mode(struct ofono_modem *modem); #include gboolean __ofono_call_barring_is_busy(struct ofono_call_barring *cb); #include gboolean __ofono_call_forwarding_is_busy(struct ofono_call_forwarding *cf); #include #include gboolean __ofono_call_settings_is_busy(struct ofono_call_settings *cs); #include #include #include #include #include #include #include #include #include #include enum ofono_voicecall_interaction { OFONO_VOICECALL_INTERACTION_NONE = 0, OFONO_VOICECALL_INTERACTION_PUT_ON_HOLD = 1, OFONO_VOICECALL_INTERACTION_DISCONNECT = 2, }; typedef void (*ofono_voicecall_dial_cb_t)(struct ofono_call *call, void *data); typedef void (*ofono_voicecall_tone_cb_t)(int error, void *data); ofono_bool_t __ofono_voicecall_is_busy(struct ofono_voicecall *vc, enum ofono_voicecall_interaction type); int __ofono_voicecall_dial(struct ofono_voicecall *vc, const char *addr, int addr_type, const char *message, unsigned char icon_id, enum ofono_voicecall_interaction interaction, ofono_voicecall_dial_cb_t cb, void *user_data); void __ofono_voicecall_dial_cancel(struct ofono_voicecall *vc); void __ofono_voicecall_set_alpha_and_icon_id(struct ofono_voicecall *vc, const char *addr, int addr_type, const char *message, unsigned char icon_id); void __ofono_voicecall_clear_alpha_and_icon_id(struct ofono_voicecall *vc); int __ofono_voicecall_tone_send(struct ofono_voicecall *vc, const char *tone_str, ofono_voicecall_tone_cb_t cb, void *user_data); void __ofono_voicecall_tone_cancel(struct ofono_voicecall *vc, int id); struct ofono_call *__ofono_voicecall_find_call_with_status( struct ofono_voicecall *vc, int status); #include struct sms; enum ofono_sms_submit_flag { OFONO_SMS_SUBMIT_FLAG_REQUEST_SR = 0x1, OFONO_SMS_SUBMIT_FLAG_RECORD_HISTORY = 0x2, OFONO_SMS_SUBMIT_FLAG_RETRY = 0x4, OFONO_SMS_SUBMIT_FLAG_EXPOSE_DBUS = 0x8, OFONO_SMS_SUBMIT_FLAG_REUSE_UUID = 0x10, }; typedef void (*ofono_sms_txq_submit_cb_t)(gboolean ok, void *data); typedef void (*ofono_sms_txq_queued_cb_t)(struct ofono_sms *sms, const struct ofono_uuid *uuid, void *data); typedef void (*ofono_sms_text_notify_cb_t)(const char *from, const struct tm *remote, const struct tm *local, const char *text, void *data); typedef void (*ofono_sms_datagram_notify_cb_t)(const char *from, const struct tm *remote, const struct tm *local, int dst, int src, const unsigned char *buffer, unsigned int len, void *data); int __ofono_sms_txq_submit(struct ofono_sms *sms, GSList *list, unsigned int flags, struct ofono_uuid *uuid, ofono_sms_txq_queued_cb_t, void *data); int __ofono_sms_txq_set_submit_notify(struct ofono_sms *sms, struct ofono_uuid *uuid, ofono_sms_txq_submit_cb_t cb, void *data, ofono_destroy_func destroy); int __ofono_sms_txq_cancel(struct ofono_sms *sms, const struct ofono_uuid *uuid); const char *__ofono_sms_message_path_from_uuid(struct ofono_sms *sms, const struct ofono_uuid *uuid); unsigned int __ofono_sms_text_watch_add(struct ofono_sms *sms, ofono_sms_text_notify_cb_t cb, void *data, ofono_destroy_func destroy); gboolean __ofono_sms_text_watch_remove(struct ofono_sms *sms, unsigned int id); unsigned int __ofono_sms_datagram_watch_add(struct ofono_sms *sms, ofono_sms_datagram_notify_cb_t cb, int dst, int src, void *data, ofono_destroy_func destroy); gboolean __ofono_sms_datagram_watch_remove(struct ofono_sms *sms, unsigned int id); unsigned short __ofono_sms_get_next_ref(struct ofono_sms *sms); #include ofono_bool_t __ofono_sim_service_available(struct ofono_sim *sim, int ust_service, int sst_service); ofono_bool_t __ofono_sim_cphs_service_available(struct ofono_sim *sim, int cphs_service); ofono_bool_t __ofono_is_valid_sim_pin(const char *pin, enum ofono_sim_password_type type); ofono_bool_t __ofono_is_valid_net_pin(const char *pin); void __ofono_sim_refresh(struct ofono_sim *sim, GSList *file_list, ofono_bool_t full_file_change, ofono_bool_t naa_init); void __ofono_sim_recheck_pin(struct ofono_sim *sim); #include typedef void (*__ofono_sms_sim_download_cb_t)(ofono_bool_t ok, const unsigned char *tp_ud, int len, void *data); struct cbs; void __ofono_cbs_sim_download(struct ofono_stk *stk, const struct cbs *msg); struct sms; int __ofono_sms_sim_download(struct ofono_stk *stk, const struct sms *msg, __ofono_sms_sim_download_cb_t cb, void *data); #include typedef gboolean (*ofono_ussd_ssc_cb_t)(int type, const char *sc, const char *sia, const char *sib, const char *sic, const char *dn, DBusMessage *msg, void *data); typedef gboolean (*ofono_ussd_passwd_cb_t)(const char *sc, const char *old, const char *new, DBusMessage *msg, void *data); typedef void (*ofono_ussd_request_cb_t)(int error, int dcs, const unsigned char *pdu, int len, void *data); gboolean __ofono_ussd_ssc_register(struct ofono_ussd *ussd, const char *sc, ofono_ussd_ssc_cb_t cb, void *data, ofono_destroy_func destroy); void __ofono_ussd_ssc_unregister(struct ofono_ussd *ussd, const char *sc); gboolean __ofono_ussd_passwd_register(struct ofono_ussd *ussd, const char *sc, ofono_ussd_passwd_cb_t cb, void *data, ofono_destroy_func destroy); void __ofono_ussd_passwd_unregister(struct ofono_ussd *ussd, const char *sc); gboolean __ofono_ussd_is_busy(struct ofono_ussd *ussd); int __ofono_ussd_initiate(struct ofono_ussd *ussd, int dcs, const unsigned char *pdu, int len, ofono_ussd_request_cb_t cb, void *user_data); void __ofono_ussd_initiate_cancel(struct ofono_ussd *ussd); #include typedef void (*ofono_netreg_status_notify_cb_t)(int status, int lac, int ci, int tech, const char *mcc, const char *mnc, void *data); unsigned int __ofono_netreg_add_status_watch(struct ofono_netreg *netreg, ofono_netreg_status_notify_cb_t cb, void *data, ofono_destroy_func destroy); gboolean __ofono_netreg_remove_status_watch(struct ofono_netreg *netreg, unsigned int id); void __ofono_netreg_set_base_station_name(struct ofono_netreg *netreg, const char *name); #include void __ofono_history_probe_drivers(struct ofono_modem *modem); void __ofono_history_call_ended(struct ofono_modem *modem, const struct ofono_call *call, time_t start, time_t end); void __ofono_history_call_missed(struct ofono_modem *modem, const struct ofono_call *call, time_t when); void __ofono_history_sms_received(struct ofono_modem *modem, const struct ofono_uuid *uuid, const char *from, const struct tm *remote, const struct tm *local, const char *text); void __ofono_history_sms_send_pending(struct ofono_modem *modem, const struct ofono_uuid *uuid, const char *to, time_t when, const char *text); void __ofono_history_sms_send_status(struct ofono_modem *modem, const struct ofono_uuid *uuid, time_t when, enum ofono_history_sms_status status); #include struct sms; void __ofono_message_waiting_mwi(struct ofono_message_waiting *mw, struct sms *sms, gboolean *out_discard); const struct ofono_phone_number *__ofono_message_waiting_get_mbdn( struct ofono_message_waiting *mw, unsigned int index); #include void __ofono_nettime_probe_drivers(struct ofono_modem *modem); void __ofono_nettime_info_received(struct ofono_modem *modem, struct ofono_network_time *info); #include #include #include #include ofono_bool_t __ofono_gprs_provision_get_settings(const char *mcc, const char *mnc, const char *spn, const char *imsi, const char *gid1, struct ofono_gprs_provision_data **settings, int *count); void __ofono_gprs_provision_free_settings( struct ofono_gprs_provision_data *settings, int count); #include enum ofono_emulator_slc_condition { OFONO_EMULATOR_SLC_CONDITION_CMER, OFONO_EMULATOR_SLC_CONDITION_CHLD, OFONO_EMULATOR_SLC_CONDITION_BIND, }; void __ofono_emulator_set_indicator_forced(struct ofono_atom *atom, const char *name, int value); void __ofono_emulator_slc_condition(struct ofono_emulator *em, enum ofono_emulator_slc_condition cond); #include #include #include #include ofono_bool_t __ofono_cdma_provision_get_name(const char *sid, char **name); #include void __ofono_private_network_release(int id); ofono_bool_t __ofono_private_network_request(ofono_private_network_cb_t cb, int *id, void *data); #include int __ofono_sim_mnclength_get_mnclength(const char *imsi); int __ofono_wakelock_init(void); void __ofono_wakelock_cleanup(void); ofono-1.17.bzr6912+16.04.20160314.3/src/network.c0000644000015600001650000015443212671500024021034 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include "ofono.h" #include "common.h" #include "simutil.h" #include "util.h" #include "storage.h" #define SETTINGS_STORE "netreg" #define SETTINGS_GROUP "Settings" #define NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN 0x1 #define NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN 0x2 #define NETWORK_REGISTRATION_FLAG_READING_PNN 0x4 enum network_registration_mode { NETWORK_REGISTRATION_MODE_AUTO = 0, NETWORK_REGISTRATION_MODE_MANUAL = 2, NETWORK_REGISTRATION_MODE_AUTO_ONLY = 5, /* Out of range of 27.007 */ }; struct ofono_netreg { int status; int location; int cellid; int technology; int mode; char *base_station; struct network_operator_data *current_operator; GSList *operator_list; struct ofono_network_registration_ops *ops; int flags; DBusMessage *pending; int signal_strength; struct sim_spdi *spdi; struct sim_eons *eons; struct ofono_sim *sim; struct ofono_sim_context *sim_context; GKeyFile *settings; char *imsi; struct ofono_watchlist *status_watches; const struct ofono_netreg_driver *driver; void *driver_data; struct ofono_atom *atom; unsigned int hfp_watch; unsigned int spn_watch; }; struct network_operator_data { char name[OFONO_MAX_OPERATOR_NAME_LENGTH + 1]; char mcc[OFONO_MAX_MCC_LENGTH + 1]; char mnc[OFONO_MAX_MNC_LENGTH + 1]; int status; unsigned int techs; const struct sim_eons_operator_info *eons_info; struct ofono_netreg *netreg; }; static GSList *g_drivers = NULL; static const char *registration_mode_to_string(int mode) { switch (mode) { case NETWORK_REGISTRATION_MODE_AUTO: return "auto"; case NETWORK_REGISTRATION_MODE_AUTO_ONLY: return "auto-only"; case NETWORK_REGISTRATION_MODE_MANUAL: return "manual"; } return "unknown"; } static inline const char *network_operator_status_to_string(int status) { switch (status) { case OPERATOR_STATUS_AVAILABLE: return "available"; case OPERATOR_STATUS_CURRENT: return "current"; case OPERATOR_STATUS_FORBIDDEN: return "forbidden"; } return "unknown"; } static int get_display_status(struct ofono_netreg *netreg, int status) { struct network_operator_data *opd = netreg->current_operator; /* * Networks in EF_SPDI or EF_OPL are equivalent to a home network. Some * operators (MVNOs usually) store in one of these files their home * networks, so we remove the roaming flag if the network we are * registered in is present in the files. Although this is not strictly * conformant with the standard, seems to be common practice among * operators: in the case of EF_SPDI it seems obvious that when the SIM * owner wants to display the SPN (its name) is because it is not * roaming. In the case of EF_OPL, no operator would care about changing * the displayed name for a network which is not among its home networks * (EF_OPL stores the index for the EF_PNN entry for a PLMN). */ if (status == NETWORK_REGISTRATION_STATUS_ROAMING && opd != NULL && (sim_spdi_lookup(netreg->spdi, opd->mcc, opd->mnc) || sim_eons_lookup(netreg->eons, opd->mcc, opd->mnc))) { DBG("mcc+mnc found in SPDI or OPL, roaming -> registered"); status = NETWORK_REGISTRATION_STATUS_REGISTERED; } return status; } static char **network_operator_technologies(struct network_operator_data *opd) { unsigned int ntechs = 0; char **techs; unsigned int i; for (i = 0; i < sizeof(opd->techs) * 8; i++) { if (opd->techs & (1 << i)) ntechs += 1; } techs = g_new0(char *, ntechs + 1); ntechs = 0; for (i = 0; i < sizeof(opd->techs) * 8; i++) { if (!(opd->techs & (1 << i))) continue; techs[ntechs++] = g_strdup(registration_tech_to_string(i)); } return techs; } static void registration_status_callback(const struct ofono_error *error, int status, int lac, int ci, int tech, void *data) { struct ofono_netreg *netreg = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error during registration status query"); return; } ofono_netreg_status_notify(netreg, status, lac, ci, tech); } static void init_register(const struct ofono_error *error, void *data) { struct ofono_netreg *netreg = data; if (netreg->driver->registration_status == NULL) return; netreg->driver->registration_status(netreg, registration_status_callback, netreg); } static void enforce_auto_only(struct ofono_netreg *netreg) { if (netreg->mode != NETWORK_REGISTRATION_MODE_MANUAL) return; if (netreg->driver->register_auto == NULL) return; netreg->driver->register_auto(netreg, init_register, netreg); } static void set_registration_mode(struct ofono_netreg *netreg, int mode) { DBusConnection *conn; const char *strmode; const char *path; if (netreg->mode == mode) return; if (mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY) enforce_auto_only(netreg); netreg->mode = mode; if (netreg->settings) { const char *mode_str; if (netreg->mode == NETWORK_REGISTRATION_MODE_MANUAL) mode_str = "manual"; else mode_str = "auto"; g_key_file_set_string(netreg->settings, SETTINGS_GROUP, "Mode", mode_str); storage_sync(netreg->imsi, SETTINGS_STORE, netreg->settings); } strmode = registration_mode_to_string(mode); conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(netreg->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, "Mode", DBUS_TYPE_STRING, &strmode); } static void register_callback(const struct ofono_error *error, void *data) { struct ofono_netreg *netreg = data; DBusMessage *reply; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) reply = dbus_message_new_method_return(netreg->pending); else reply = __ofono_error_failed(netreg->pending); __ofono_dbus_pending_reply(&netreg->pending, reply); if (netreg->driver->registration_status == NULL) return; netreg->driver->registration_status(netreg, registration_status_callback, netreg); } static struct network_operator_data * network_operator_create(const struct ofono_network_operator *op) { struct network_operator_data *opd; opd = g_new0(struct network_operator_data, 1); memcpy(&opd->name, op->name, sizeof(opd->name)); memcpy(&opd->mcc, op->mcc, sizeof(opd->mcc)); memcpy(&opd->mnc, op->mnc, sizeof(opd->mnc)); opd->status = op->status; if (op->tech != -1) opd->techs |= 1 << op->tech; return opd; } static void network_operator_destroy(gpointer user_data) { struct network_operator_data *op = user_data; g_free(op); } static gint network_operator_compare(gconstpointer a, gconstpointer b) { const struct network_operator_data *opda = a; const struct ofono_network_operator *opb = b; int comp1; int comp2; comp1 = strcmp(opda->mcc, opb->mcc); comp2 = strcmp(opda->mnc, opb->mnc); return comp1 != 0 ? comp1 : comp2; } static gint network_operator_data_compare(gconstpointer a, gconstpointer b) { const struct network_operator_data *opa = a; const struct network_operator_data *opb = b; int comp1; int comp2; comp1 = strcmp(opa->mcc, opb->mcc); comp2 = strcmp(opa->mnc, opb->mnc); return comp1 != 0 ? comp1 : comp2; } static const char *network_operator_build_path(struct ofono_netreg *netreg, const char *mcc, const char *mnc) { static char path[256]; snprintf(path, sizeof(path), "%s/operator/%s%s", __ofono_atom_get_path(netreg->atom), mcc, mnc); return path; } static void set_network_operator_status(struct network_operator_data *opd, int status) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_netreg *netreg = opd->netreg; const char *status_str; const char *path; if (opd->status == status) return; opd->status = status; /* Don't emit for the case where only operator name is reported */ if (opd->mcc[0] == '\0' && opd->mnc[0] == '\0') return; status_str = network_operator_status_to_string(status); path = network_operator_build_path(netreg, opd->mcc, opd->mnc); ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_OPERATOR_INTERFACE, "Status", DBUS_TYPE_STRING, &status_str); } static void set_network_operator_techs(struct network_operator_data *opd, unsigned int techs) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_netreg *netreg = opd->netreg; char **technologies; const char *path; if (opd->techs == techs) return; opd->techs = techs; technologies = network_operator_technologies(opd); path = network_operator_build_path(netreg, opd->mcc, opd->mnc); ofono_dbus_signal_array_property_changed(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, "Technologies", DBUS_TYPE_STRING, &technologies); g_strfreev(technologies); } static char *get_operator_display_name(struct ofono_netreg *netreg) { struct network_operator_data *opd = netreg->current_operator; const char *plmn; const char *spn; static char name[1024]; static char mccmnc[OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1]; int len = sizeof(name); int home_or_spdi; /* * The name displayed to user depends on whether we're in a home * PLMN or roaming and on configuration bits from the SIM, all * together there are four cases to consider. */ if (opd == NULL) { g_strlcpy(name, "", len); return name; } plmn = opd->name; /* * This is a fallback on some really broken hardware which do not * report the COPS name */ if (plmn[0] == '\0') { snprintf(mccmnc, sizeof(mccmnc), "%s%s", opd->mcc, opd->mnc); plmn = mccmnc; } if (opd->eons_info && opd->eons_info->longname) plmn = opd->eons_info->longname; spn = ofono_sim_get_spn(netreg->sim); if (spn == NULL || strlen(spn) == 0) { g_strlcpy(name, plmn, len); return name; } if (netreg->status == NETWORK_REGISTRATION_STATUS_REGISTERED) home_or_spdi = TRUE; else home_or_spdi = sim_spdi_lookup(netreg->spdi, opd->mcc, opd->mnc); if (home_or_spdi) if (netreg->flags & NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN) /* Case 1 */ snprintf(name, len, "%s (%s)", spn, plmn); else /* Case 2 */ snprintf(name, len, "%s", spn); else if (netreg->flags & NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN) /* Case 3 */ snprintf(name, len, "%s (%s)", spn, plmn); else /* Case 4 */ snprintf(name, len, "%s", plmn); return name; } static void netreg_emit_operator_display_name(struct ofono_netreg *netreg) { const char *operator = get_operator_display_name(netreg); ofono_dbus_signal_property_changed(ofono_dbus_get_connection(), __ofono_atom_get_path(netreg->atom), OFONO_NETWORK_REGISTRATION_INTERFACE, "Name", DBUS_TYPE_STRING, &operator); } static void set_network_operator_name(struct network_operator_data *opd, const char *name) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_netreg *netreg = opd->netreg; const char *path; if (name[0] == '\0') return; if (!strncmp(opd->name, name, OFONO_MAX_OPERATOR_NAME_LENGTH)) return; strncpy(opd->name, name, OFONO_MAX_OPERATOR_NAME_LENGTH); opd->name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0'; /* * If we have Enhanced Operator Name info on the SIM, we always use * that, so do not need to emit the signal here */ if (opd->eons_info && opd->eons_info->longname) return; if (opd == netreg->current_operator) netreg_emit_operator_display_name(netreg); /* Don't emit when only operator name is reported */ if (opd->mcc[0] == '\0' && opd->mnc[0] == '\0') return; path = network_operator_build_path(netreg, opd->mcc, opd->mnc); ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_OPERATOR_INTERFACE, "Name", DBUS_TYPE_STRING, &name); } static void set_network_operator_eons_info(struct network_operator_data *opd, const struct sim_eons_operator_info *eons_info) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_netreg *netreg = opd->netreg; const struct sim_eons_operator_info *old_eons_info = opd->eons_info; const char *path; const char *oldname; const char *newname; const char *oldinfo; const char *newinfo; if (old_eons_info == NULL && eons_info == NULL) return; path = network_operator_build_path(netreg, opd->mcc, opd->mnc); opd->eons_info = eons_info; if (old_eons_info && old_eons_info->longname) oldname = old_eons_info->longname; else oldname = opd->name; if (eons_info && eons_info->longname) newname = eons_info->longname; else newname = opd->name; if (oldname != newname && strcmp(oldname, newname)) { ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_OPERATOR_INTERFACE, "Name", DBUS_TYPE_STRING, &newname); if (opd == netreg->current_operator) netreg_emit_operator_display_name(netreg); } if (old_eons_info && old_eons_info->info) oldinfo = old_eons_info->info; else oldinfo = ""; if (eons_info && eons_info->info) newinfo = eons_info->info; else newinfo = ""; if (oldinfo != newinfo && strcmp(oldinfo, newinfo)) ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_OPERATOR_INTERFACE, "AdditionalInformation", DBUS_TYPE_STRING, &newinfo); } static void append_operator_properties(struct network_operator_data *opd, DBusMessageIter *dict) { const char *name = opd->name; const char *status = network_operator_status_to_string(opd->status); char mccmnc[OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1]; if (opd->eons_info && opd->eons_info->longname) name = opd->eons_info->longname; if (name[0] == '\0') { snprintf(mccmnc, sizeof(mccmnc), "%s%s", opd->mcc, opd->mnc); name = mccmnc; } ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING, &name); ofono_dbus_dict_append(dict, "Status", DBUS_TYPE_STRING, &status); if (*opd->mcc != '\0') { const char *mcc = opd->mcc; ofono_dbus_dict_append(dict, "MobileCountryCode", DBUS_TYPE_STRING, &mcc); } if (*opd->mnc != '\0') { const char *mnc = opd->mnc; ofono_dbus_dict_append(dict, "MobileNetworkCode", DBUS_TYPE_STRING, &mnc); } if (opd->techs != 0) { char **technologies = network_operator_technologies(opd); ofono_dbus_dict_append_array(dict, "Technologies", DBUS_TYPE_STRING, &technologies); g_strfreev(technologies); } if (opd->eons_info && opd->eons_info->info) { const char *additional = opd->eons_info->info; ofono_dbus_dict_append(dict, "AdditionalInformation", DBUS_TYPE_STRING, &additional); } } static DBusMessage *network_operator_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct network_operator_data *opd = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); append_operator_properties(opd, &dict); dbus_message_iter_close_container(&iter, &dict); return reply; } static DBusMessage *network_operator_register(DBusConnection *conn, DBusMessage *msg, void *data) { struct network_operator_data *opd = data; struct ofono_netreg *netreg = opd->netreg; if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY) return __ofono_error_access_denied(msg); if (netreg->pending) return __ofono_error_busy(msg); if (netreg->driver->register_manual == NULL) return __ofono_error_not_implemented(msg); netreg->pending = dbus_message_ref(msg); netreg->driver->register_manual(netreg, opd->mcc, opd->mnc, register_callback, netreg); set_registration_mode(netreg, NETWORK_REGISTRATION_MODE_MANUAL); return NULL; } static const GDBusMethodTable network_operator_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), network_operator_get_properties) }, { GDBUS_ASYNC_METHOD("Register", NULL, NULL, network_operator_register) }, { } }; static const GDBusSignalTable network_operator_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static gboolean network_operator_dbus_register(struct ofono_netreg *netreg, struct network_operator_data *opd) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; path = network_operator_build_path(netreg, opd->mcc, opd->mnc); if (!g_dbus_register_interface(conn, path, OFONO_NETWORK_OPERATOR_INTERFACE, network_operator_methods, network_operator_signals, NULL, opd, network_operator_destroy)) { ofono_error("Could not register NetworkOperator %s", path); return FALSE; } opd->netreg = netreg; opd->eons_info = NULL; if (netreg->eons) opd->eons_info = sim_eons_lookup(netreg->eons, opd->mcc, opd->mnc); return TRUE; } static gboolean network_operator_dbus_unregister(struct ofono_netreg *netreg, struct network_operator_data *opd) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; path = network_operator_build_path(netreg, opd->mcc, opd->mnc); return g_dbus_unregister_interface(conn, path, OFONO_NETWORK_OPERATOR_INTERFACE); } static GSList *compress_operator_list(const struct ofono_network_operator *list, int total) { GSList *oplist = 0; GSList *o; int i; struct network_operator_data *opd; for (i = 0; i < total; i++) { o = NULL; if (list[i].mcc[0] == '\0' || list[i].mnc[0] == '\0') continue; if (oplist) o = g_slist_find_custom(oplist, &list[i], network_operator_compare); if (o == NULL) { opd = network_operator_create(&list[i]); oplist = g_slist_prepend(oplist, opd); } else if (o && list[i].tech != -1) { opd = o->data; opd->techs |= 1 << list[i].tech; } } if (oplist) oplist = g_slist_reverse(oplist); return oplist; } static gboolean update_operator_list(struct ofono_netreg *netreg, int total, const struct ofono_network_operator *list) { GSList *n = NULL; GSList *o; GSList *compressed; GSList *c; gboolean changed = FALSE; compressed = compress_operator_list(list, total); for (c = compressed; c; c = c->next) { struct network_operator_data *copd = c->data; o = g_slist_find_custom(netreg->operator_list, copd, network_operator_data_compare); if (o) { /* Update and move to a new list */ set_network_operator_status(o->data, copd->status); set_network_operator_techs(o->data, copd->techs); set_network_operator_name(o->data, copd->name); n = g_slist_prepend(n, o->data); netreg->operator_list = g_slist_remove(netreg->operator_list, o->data); } else { /* New operator */ struct network_operator_data *opd; opd = g_memdup(copd, sizeof(struct network_operator_data)); if (!network_operator_dbus_register(netreg, opd)) { g_free(opd); continue; } n = g_slist_prepend(n, opd); changed = TRUE; } } g_slist_foreach(compressed, (GFunc)g_free, NULL); g_slist_free(compressed); if (n) n = g_slist_reverse(n); if (netreg->operator_list) changed = TRUE; for (o = netreg->operator_list; o; o = o->next) network_operator_dbus_unregister(netreg, o->data); g_slist_free(netreg->operator_list); netreg->operator_list = n; return changed; } static DBusMessage *network_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_netreg *netreg = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; const char *status = registration_status_to_string(netreg->status); const char *operator; const char *mode = registration_mode_to_string(netreg->mode); reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "Status", DBUS_TYPE_STRING, &status); ofono_dbus_dict_append(&dict, "Mode", DBUS_TYPE_STRING, &mode); if (netreg->location != -1) { dbus_uint16_t location = netreg->location; ofono_dbus_dict_append(&dict, "LocationAreaCode", DBUS_TYPE_UINT16, &location); } if (netreg->cellid != -1) { dbus_uint32_t cellid = netreg->cellid; ofono_dbus_dict_append(&dict, "CellId", DBUS_TYPE_UINT32, &cellid); } if (netreg->technology != -1) { const char *technology = registration_tech_to_string(netreg->technology); ofono_dbus_dict_append(&dict, "Technology", DBUS_TYPE_STRING, &technology); } if (netreg->current_operator) { if (netreg->current_operator->mcc[0] != '\0') { const char *mcc = netreg->current_operator->mcc; ofono_dbus_dict_append(&dict, "MobileCountryCode", DBUS_TYPE_STRING, &mcc); } if (netreg->current_operator->mnc[0] != '\0') { const char *mnc = netreg->current_operator->mnc; ofono_dbus_dict_append(&dict, "MobileNetworkCode", DBUS_TYPE_STRING, &mnc); } } operator = get_operator_display_name(netreg); ofono_dbus_dict_append(&dict, "Name", DBUS_TYPE_STRING, &operator); if (netreg->signal_strength != -1) { unsigned char strength = netreg->signal_strength; ofono_dbus_dict_append(&dict, "Strength", DBUS_TYPE_BYTE, &strength); } if (netreg->base_station) ofono_dbus_dict_append(&dict, "BaseStation", DBUS_TYPE_STRING, &netreg->base_station); dbus_message_iter_close_container(&iter, &dict); return reply; } static DBusMessage *network_register(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_netreg *netreg = data; if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY) return __ofono_error_access_denied(msg); if (netreg->pending) return __ofono_error_busy(msg); if (netreg->driver->register_auto == NULL) return __ofono_error_not_implemented(msg); netreg->pending = dbus_message_ref(msg); netreg->driver->register_auto(netreg, register_callback, netreg); set_registration_mode(netreg, NETWORK_REGISTRATION_MODE_AUTO); return NULL; } static void append_operator_struct(struct ofono_netreg *netreg, struct network_operator_data *opd, DBusMessageIter *iter) { DBusMessageIter entry, dict; const char *path; path = network_operator_build_path(netreg, opd->mcc, opd->mnc); dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); append_operator_properties(opd, &dict); dbus_message_iter_close_container(&entry, &dict); dbus_message_iter_close_container(iter, &entry); } static void append_operator_struct_list(struct ofono_netreg *netreg, DBusMessageIter *array) { DBusConnection *conn = ofono_dbus_get_connection(); char **children; char path[256]; GSList *l; snprintf(path, sizeof(path), "%s/operator", __ofono_atom_get_path(netreg->atom)); if (!dbus_connection_list_registered(conn, path, &children)) { DBG("Unable to obtain registered NetworkOperator(s)"); return; } /* * Quoting 27.007: "The list of operators shall be in order: home * network, networks referenced in SIM or active application in the * UICC (GSM or USIM) in the following order: HPLMN selector, User * controlled PLMN selector, Operator controlled PLMN selector and * PLMN selector (in the SIM or GSM application), and other networks." * Thus we must make sure we return the list in the same order, * if possible. Luckily the operator_list is stored in order already */ for (l = netreg->operator_list; l; l = l->next) { struct network_operator_data *opd = l->data; char mnc[OFONO_MAX_MNC_LENGTH + 1]; char mcc[OFONO_MAX_MCC_LENGTH + 1]; int j; for (j = 0; children[j]; j++) { sscanf(children[j], "%3[0-9]%[0-9]", mcc, mnc); if (!strcmp(opd->mcc, mcc) && !strcmp(opd->mnc, mnc)) append_operator_struct(netreg, opd, array); } } dbus_free_string_array(children); } static void operator_list_callback(const struct ofono_error *error, int total, const struct ofono_network_operator *list, void *data) { struct ofono_netreg *netreg = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error occurred during operator list"); __ofono_dbus_pending_reply(&netreg->pending, __ofono_error_failed(netreg->pending)); return; } update_operator_list(netreg, total, list); reply = dbus_message_new_method_return(netreg->pending); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array); append_operator_struct_list(netreg, &array); dbus_message_iter_close_container(&iter, &array); __ofono_dbus_pending_reply(&netreg->pending, reply); } static DBusMessage *network_scan(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_netreg *netreg = data; if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY) return __ofono_error_access_denied(msg); if (netreg->pending) return __ofono_error_busy(msg); if (netreg->driver->list_operators == NULL) return __ofono_error_not_implemented(msg); netreg->pending = dbus_message_ref(msg); netreg->driver->list_operators(netreg, operator_list_callback, netreg); return NULL; } static DBusMessage *network_get_operators(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_netreg *netreg = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array); append_operator_struct_list(netreg, &array); dbus_message_iter_close_container(&iter, &array); return reply; } static const GDBusMethodTable network_registration_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), network_get_properties) }, { GDBUS_ASYNC_METHOD("Register", NULL, NULL, network_register) }, { GDBUS_METHOD("GetOperators", NULL, GDBUS_ARGS({ "operators_with_properties", "a(oa{sv})" }), network_get_operators) }, { GDBUS_ASYNC_METHOD("Scan", NULL, GDBUS_ARGS({ "operators_with_properties", "a(oa{sv})" }), network_scan) }, { } }; static const GDBusSignalTable network_registration_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static void set_registration_status(struct ofono_netreg *netreg, int status) { const char *str_status; const char *path = __ofono_atom_get_path(netreg->atom); DBusConnection *conn = ofono_dbus_get_connection(); /* Status depends also on SIM files */ netreg->status = get_display_status(netreg, status); str_status = registration_status_to_string(netreg->status); ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, "Status", DBUS_TYPE_STRING, &str_status); } static void set_registration_location(struct ofono_netreg *netreg, int lac) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(netreg->atom); dbus_uint16_t dbus_lac = lac; if (lac > 0xffff) return; netreg->location = lac; if (netreg->location == -1) return; ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, "LocationAreaCode", DBUS_TYPE_UINT16, &dbus_lac); } static void set_registration_cellid(struct ofono_netreg *netreg, int ci) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(netreg->atom); dbus_uint32_t dbus_ci = ci; netreg->cellid = ci; if (netreg->cellid == -1) return; ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, "CellId", DBUS_TYPE_UINT32, &dbus_ci); } static void set_registration_technology(struct ofono_netreg *netreg, int tech) { const char *tech_str = registration_tech_to_string(tech); DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(netreg->atom); netreg->technology = tech; if (netreg->technology == -1) return; ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, "Technology", DBUS_TYPE_STRING, &tech_str); } void __ofono_netreg_set_base_station_name(struct ofono_netreg *netreg, const char *name) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(netreg->atom); const char *base_station = name ? name : ""; /* Cell ID changed, but we don't have a cell name, nothing to do */ if (netreg->base_station == NULL && name == NULL) return; if (netreg->base_station) g_free(netreg->base_station); if (name == NULL) { netreg->base_station = NULL; /* * We just got unregistered, set name to NULL * but don't emit signal */ if (netreg->current_operator == NULL) return; } else { netreg->base_station = g_strdup(name); } ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, "BaseStation", DBUS_TYPE_STRING, &base_station); } unsigned int __ofono_netreg_add_status_watch(struct ofono_netreg *netreg, ofono_netreg_status_notify_cb_t notify, void *data, ofono_destroy_func destroy) { struct ofono_watchlist_item *item; DBG("%p", netreg); if (netreg == NULL) return 0; if (notify == NULL) return 0; item = g_new0(struct ofono_watchlist_item, 1); item->notify = notify; item->destroy = destroy; item->notify_data = data; return __ofono_watchlist_add_item(netreg->status_watches, item); } gboolean __ofono_netreg_remove_status_watch(struct ofono_netreg *netreg, unsigned int id) { DBG("%p", netreg); return __ofono_watchlist_remove_item(netreg->status_watches, id); } static void notify_status_watches(struct ofono_netreg *netreg) { struct ofono_watchlist_item *item; GSList *l; ofono_netreg_status_notify_cb_t notify; const char *mcc = NULL; const char *mnc = NULL; if (netreg->current_operator) { mcc = netreg->current_operator->mcc; mnc = netreg->current_operator->mnc; } for (l = netreg->status_watches->items; l; l = l->next) { item = l->data; notify = item->notify; notify(netreg->status, netreg->location, netreg->cellid, netreg->technology, mcc, mnc, item->notify_data); } } static void reset_available(struct network_operator_data *old, const struct ofono_network_operator *new) { if (old == NULL) return; if (new == NULL || network_operator_compare(old, new) != 0) set_network_operator_status(old, OPERATOR_STATUS_AVAILABLE); } static void current_operator_callback(const struct ofono_error *error, const struct ofono_network_operator *current, void *data) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_netreg *netreg = data; const char *path = __ofono_atom_get_path(netreg->atom); GSList *op = NULL; DBG("%p, %p", netreg, netreg->current_operator); /* * Sometimes we try to query COPS right when we roam off the cell, * in which case the operator information frequently comes in bogus. * We ignore it here */ if (netreg->status != NETWORK_REGISTRATION_STATUS_REGISTERED && netreg->status != NETWORK_REGISTRATION_STATUS_ROAMING) current = NULL; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error during current operator"); return; } if (netreg->current_operator == NULL && current == NULL) return; /* We got a new network operator, reset the previous one's status */ /* It will be updated properly later */ reset_available(netreg->current_operator, current); if (current) op = g_slist_find_custom(netreg->operator_list, current, network_operator_compare); if (op) { struct network_operator_data *opd = op->data; unsigned int techs = opd->techs; if (current->tech != -1) { techs |= 1 << current->tech; set_network_operator_techs(opd, techs); } set_network_operator_status(opd, OPERATOR_STATUS_CURRENT); set_network_operator_name(opd, current->name); if (netreg->current_operator == op->data) return; netreg->current_operator = op->data; goto emit; } if (current) { struct network_operator_data *opd; opd = network_operator_create(current); if (opd->mcc[0] != '\0' && opd->mnc[0] != '\0' && !network_operator_dbus_register(netreg, opd)) { g_free(opd); return; } else opd->netreg = netreg; netreg->current_operator = opd; netreg->operator_list = g_slist_append(netreg->operator_list, opd); } else { /* We don't free this here because operator is registered */ /* Taken care of elsewhere */ netreg->current_operator = NULL; } emit: netreg_emit_operator_display_name(netreg); if (netreg->current_operator) { if (netreg->current_operator->mcc[0] != '\0') { const char *mcc = netreg->current_operator->mcc; ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, "MobileCountryCode", DBUS_TYPE_STRING, &mcc); } if (netreg->current_operator->mnc[0] != '\0') { const char *mnc = netreg->current_operator->mnc; ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, "MobileNetworkCode", DBUS_TYPE_STRING, &mnc); } } /* Registration status might be affected for MVNOs */ set_registration_status(netreg, netreg->status); notify_status_watches(netreg); } static void signal_strength_callback(const struct ofono_error *error, int strength, void *data) { struct ofono_netreg *netreg = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error during signal strength query"); return; } ofono_netreg_strength_notify(netreg, strength); } static void notify_emulator_status(struct ofono_atom *atom, void *data) { switch (GPOINTER_TO_INT(data)) { case NETWORK_REGISTRATION_STATUS_REGISTERED: ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_SERVICE, 1); ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_ROAMING, 0); break; case NETWORK_REGISTRATION_STATUS_ROAMING: ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_SERVICE, 1); ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_ROAMING, 1); break; default: ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_SERVICE, 0); ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_ROAMING, 0); } } void ofono_netreg_status_notify(struct ofono_netreg *netreg, int status, int lac, int ci, int tech) { if (netreg == NULL) return; DBG("%s status %d tech %d", __ofono_atom_get_path(netreg->atom), status, tech); if (netreg->status != status) { struct ofono_modem *modem; set_registration_status(netreg, status); modem = __ofono_atom_get_modem(netreg->atom); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, notify_emulator_status, GINT_TO_POINTER(netreg->status)); } if (netreg->technology != tech) set_registration_technology(netreg, tech); if (netreg->location != lac) set_registration_location(netreg, lac); if (netreg->cellid != ci) set_registration_cellid(netreg, ci); if (netreg->status == NETWORK_REGISTRATION_STATUS_REGISTERED || netreg->status == NETWORK_REGISTRATION_STATUS_ROAMING) { if (netreg->driver->current_operator != NULL) netreg->driver->current_operator(netreg, current_operator_callback, netreg); if (netreg->driver->strength != NULL) netreg->driver->strength(netreg, signal_strength_callback, netreg); } else { struct ofono_error error; error.type = OFONO_ERROR_TYPE_NO_ERROR; error.error = 0; current_operator_callback(&error, NULL, netreg); __ofono_netreg_set_base_station_name(netreg, NULL); netreg->signal_strength = -1; } notify_status_watches(netreg); } void ofono_netreg_time_notify(struct ofono_netreg *netreg, struct ofono_network_time *info) { struct ofono_modem *modem = __ofono_atom_get_modem(netreg->atom); if (info == NULL) return; __ofono_nettime_info_received(modem, info); } static void sim_csp_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *user_data) { struct ofono_netreg *netreg = user_data; int i = 0; if (!ok) return; if (total_length < 18) return; /* * According to CPHS 4.2, EFcsp is an array of two-byte service * entries, each consisting of a one byte service group * identifier followed by 8 bits; each bit is indicating * availability of a specific service or feature. * * The PLMN mode bit, if present, indicates whether manual * operator selection should be disabled or enabled. When * unset, the device is forced to automatic mode; when set, * manual selection is to be enabled. The latter is also the * default. */ while (i < total_length && data[i] != SIM_CSP_ENTRY_VALUE_ADDED_SERVICES) i += 2; if (i == total_length) return; if ((data[i + 1] & 0x80) != 0) { if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY) set_registration_mode(netreg, NETWORK_REGISTRATION_MODE_AUTO); return; } set_registration_mode(netreg, NETWORK_REGISTRATION_MODE_AUTO_ONLY); } static void sim_csp_changed(int id, void *userdata) { struct ofono_netreg *netreg = userdata; ofono_sim_read(netreg->sim_context, SIM_EF_CPHS_CSP_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_csp_read_cb, netreg); } static void init_registration_status(const struct ofono_error *error, int status, int lac, int ci, int tech, void *data) { struct ofono_netreg *netreg = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error during registration status query"); return; } ofono_netreg_status_notify(netreg, status, lac, ci, tech); /* * Bootstrap our signal strength value without waiting for the * stack to report it */ if (netreg->status == NETWORK_REGISTRATION_STATUS_REGISTERED || netreg->status == NETWORK_REGISTRATION_STATUS_ROAMING) { if (netreg->driver->strength != NULL) netreg->driver->strength(netreg, signal_strength_callback, netreg); } if (netreg->mode != NETWORK_REGISTRATION_MODE_MANUAL && (status == NETWORK_REGISTRATION_STATUS_NOT_REGISTERED || status == NETWORK_REGISTRATION_STATUS_DENIED || status == NETWORK_REGISTRATION_STATUS_UNKNOWN)) { if (netreg->driver->register_auto != NULL) netreg->driver->register_auto(netreg, init_register, netreg); } if (netreg->driver->register_manual == NULL) { set_registration_mode(netreg, NETWORK_REGISTRATION_MODE_AUTO_ONLY); return; } if (netreg->sim_context) { ofono_sim_read(netreg->sim_context, SIM_EF_CPHS_CSP_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_csp_read_cb, netreg); ofono_sim_add_file_watch(netreg->sim_context, SIM_EF_CPHS_CSP_FILEID, sim_csp_changed, netreg, NULL); } } static void notify_emulator_strength(struct ofono_atom *atom, void *data) { int val = 0; if (GPOINTER_TO_INT(data) > 0) val = (GPOINTER_TO_INT(data) - 1) / 20 + 1; ofono_emulator_set_indicator(atom, OFONO_EMULATOR_IND_SIGNAL, val); } void ofono_netreg_strength_notify(struct ofono_netreg *netreg, int strength) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem; if (netreg->signal_strength == strength) return; /* * Theoretically we can get signal strength even when not registered * to any network. However, what do we do with it in that case? */ if (netreg->status != NETWORK_REGISTRATION_STATUS_REGISTERED && netreg->status != NETWORK_REGISTRATION_STATUS_ROAMING) return; DBG("strength %d", strength); netreg->signal_strength = strength; if (strength != -1) { const char *path = __ofono_atom_get_path(netreg->atom); unsigned char strength_byte = netreg->signal_strength; ofono_dbus_signal_property_changed(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, "Strength", DBUS_TYPE_BYTE, &strength_byte); } modem = __ofono_atom_get_modem(netreg->atom); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, notify_emulator_strength, GINT_TO_POINTER(netreg->signal_strength)); } static void sim_opl_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *user_data) { struct ofono_netreg *netreg = user_data; int total; GSList *l; if (!ok) { if (record > 0) goto optimize; return; } if (record_length < 8 || length < record_length) return; total = length / record_length; sim_eons_add_opl_record(netreg->eons, data, record_length); if (record != total) return; optimize: sim_eons_optimize(netreg->eons); for (l = netreg->operator_list; l; l = l->next) { struct network_operator_data *opd = l->data; const struct sim_eons_operator_info *eons_info; eons_info = sim_eons_lookup(netreg->eons, opd->mcc, opd->mnc); set_network_operator_eons_info(opd, eons_info); } /* Registration status might be affected for MVNOs */ set_registration_status(netreg, netreg->status); notify_status_watches(netreg); } static void sim_pnn_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *user_data) { struct ofono_netreg *netreg = user_data; int total; if (!ok) goto check; if (length < 3 || record_length < 3 || length < record_length) goto check; total = length / record_length; if (netreg->eons == NULL) netreg->eons = sim_eons_new(total); sim_eons_add_pnn_record(netreg->eons, record, data, record_length); if (record != total) return; check: netreg->flags &= ~NETWORK_REGISTRATION_FLAG_READING_PNN; /* * If PNN is not present then OPL is not useful, don't * retrieve it. If OPL is not there then PNN[1] will * still be used for the HPLMN and/or EHPLMN, if PNN * is present. */ if (netreg->eons && !sim_eons_pnn_is_empty(netreg->eons)) ofono_sim_read(netreg->sim_context, SIM_EFOPL_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, sim_opl_read_cb, netreg); } static void sim_spdi_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *user_data) { struct ofono_netreg *netreg = user_data; if (!ok) return; netreg->spdi = sim_spdi_new(data, length); if (netreg->current_operator == NULL) return; if (netreg->status != NETWORK_REGISTRATION_STATUS_ROAMING) return; if (!sim_spdi_lookup(netreg->spdi, netreg->current_operator->mcc, netreg->current_operator->mnc)) return; /* * SPDI contents affect the displayed operator AND whether we consider * that we are roaming or not. */ netreg_emit_operator_display_name(netreg); /* Registration status might be affected for MVNOs */ set_registration_status(netreg, netreg->status); notify_status_watches(netreg); } static void sim_spn_display_condition_parse(struct ofono_netreg *netreg, guint8 dcbyte) { if (dcbyte & SIM_EFSPN_DC_HOME_PLMN_BIT) netreg->flags |= NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN; if (!(dcbyte & SIM_EFSPN_DC_ROAMING_SPN_BIT)) netreg->flags |= NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN; } static void spn_read_cb(const char *spn, const char *dc, void *data) { struct ofono_netreg *netreg = data; netreg->flags &= ~(NETWORK_REGISTRATION_FLAG_HOME_SHOW_PLMN | NETWORK_REGISTRATION_FLAG_ROAMING_SHOW_SPN); if (dc) sim_spn_display_condition_parse(netreg, *dc); if (netreg->current_operator) netreg_emit_operator_display_name(netreg); } int ofono_netreg_get_location(struct ofono_netreg *netreg) { if (netreg == NULL) return -1; return netreg->location; } int ofono_netreg_get_cellid(struct ofono_netreg *netreg) { if (netreg == NULL) return -1; return netreg->cellid; } int ofono_netreg_get_status(struct ofono_netreg *netreg) { if (netreg == NULL) return -1; return netreg->status; } int ofono_netreg_get_technology(struct ofono_netreg *netreg) { if (netreg == NULL) return -1; return netreg->technology; } const char *ofono_netreg_get_mcc(struct ofono_netreg *netreg) { if (netreg == NULL) return NULL; if (netreg->current_operator == NULL) return NULL; return netreg->current_operator->mcc; } const char *ofono_netreg_get_mnc(struct ofono_netreg *netreg) { if (netreg == NULL) return NULL; if (netreg->current_operator == NULL) return NULL; return netreg->current_operator->mnc; } int ofono_netreg_driver_register(const struct ofono_netreg_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_netreg_driver_unregister(const struct ofono_netreg_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void emulator_remove_handler(struct ofono_atom *atom, void *data) { ofono_emulator_remove_handler(atom, data); } static void netreg_unregister(struct ofono_atom *atom) { struct ofono_netreg *netreg = __ofono_atom_get_data(atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); GSList *l; __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, notify_emulator_status, GINT_TO_POINTER(0)); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, notify_emulator_strength, GINT_TO_POINTER(0)); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_remove_handler, "+COPS"); __ofono_modem_remove_atom_watch(modem, netreg->hfp_watch); __ofono_watchlist_free(netreg->status_watches); netreg->status_watches = NULL; for (l = netreg->operator_list; l; l = l->next) { struct network_operator_data *opd = l->data; if (opd->mcc[0] == '\0' && opd->mnc[0] == '\0') { g_free(opd); continue; } network_operator_dbus_unregister(netreg, l->data); } g_slist_free(netreg->operator_list); netreg->operator_list = NULL; if (netreg->base_station) { g_free(netreg->base_station); netreg->base_station = NULL; } if (netreg->settings) { storage_close(netreg->imsi, SETTINGS_STORE, netreg->settings, TRUE); g_free(netreg->imsi); netreg->imsi = NULL; netreg->settings = NULL; } if (netreg->spn_watch) ofono_sim_remove_spn_watch(netreg->sim, &netreg->spn_watch); if (netreg->sim_context) { ofono_sim_context_free(netreg->sim_context); netreg->sim_context = NULL; } netreg->sim = NULL; g_dbus_unregister_interface(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE); ofono_modem_remove_interface(modem, OFONO_NETWORK_REGISTRATION_INTERFACE); } static void netreg_remove(struct ofono_atom *atom) { struct ofono_netreg *netreg = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (netreg == NULL) return; if (netreg->driver != NULL && netreg->driver->remove != NULL) netreg->driver->remove(netreg); sim_eons_free(netreg->eons); sim_spdi_free(netreg->spdi); g_free(netreg); } struct ofono_netreg *ofono_netreg_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_netreg *netreg; GSList *l; if (driver == NULL) return NULL; netreg = g_try_new0(struct ofono_netreg, 1); if (netreg == NULL) return NULL; netreg->status = NETWORK_REGISTRATION_STATUS_UNKNOWN; netreg->location = -1; netreg->cellid = -1; netreg->technology = -1; netreg->signal_strength = -1; netreg->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_NETREG, netreg_remove, netreg); for (l = g_drivers; l; l = l->next) { const struct ofono_netreg_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(netreg, vendor, data) < 0) continue; netreg->driver = drv; break; } return netreg; } static void netreg_load_settings(struct ofono_netreg *netreg) { const char *imsi; char *strmode; gboolean upgrade = FALSE; if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY) return; imsi = ofono_sim_get_imsi(netreg->sim); if (imsi == NULL) return; netreg->settings = storage_open(imsi, SETTINGS_STORE); if (netreg->settings == NULL) return; netreg->imsi = g_strdup(imsi); strmode = g_key_file_get_string(netreg->settings, SETTINGS_GROUP, "Mode", NULL); if (strmode == NULL) upgrade = TRUE; else if (g_str_equal(strmode, "auto")) netreg->mode = NETWORK_REGISTRATION_MODE_AUTO; else if (g_str_equal(strmode, "manual")) netreg->mode = NETWORK_REGISTRATION_MODE_MANUAL; else { int mode; mode = g_key_file_get_integer(netreg->settings, SETTINGS_GROUP, "Mode", NULL); switch (mode) { case NETWORK_REGISTRATION_MODE_AUTO: case NETWORK_REGISTRATION_MODE_MANUAL: netreg->mode = mode; break; } upgrade = TRUE; } g_free(strmode); if (upgrade == FALSE) return; if (netreg->mode == NETWORK_REGISTRATION_MODE_MANUAL) strmode = "manual"; else strmode = "auto"; g_key_file_set_string(netreg->settings, SETTINGS_GROUP, "Mode", strmode); } static void sim_pnn_opl_changed(int id, void *userdata) { struct ofono_netreg *netreg = userdata; GSList *l; if (netreg->flags & NETWORK_REGISTRATION_FLAG_READING_PNN) return; /* * Free references to structures on the netreg->eons list and * update the operator info on D-bus. If EFpnn/EFopl read succeeds, * operator info will be updated again, otherwise it won't be * updated again. */ for (l = netreg->operator_list; l; l = l->next) set_network_operator_eons_info(l->data, NULL); sim_eons_free(netreg->eons); netreg->eons = NULL; netreg->flags |= NETWORK_REGISTRATION_FLAG_READING_PNN; ofono_sim_read(netreg->sim_context, SIM_EFPNN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, sim_pnn_read_cb, netreg); } static void sim_spdi_changed(int id, void *userdata) { struct ofono_netreg *netreg = userdata; sim_spdi_free(netreg->spdi); netreg->spdi = NULL; if (netreg->current_operator && netreg->status == NETWORK_REGISTRATION_STATUS_ROAMING) netreg_emit_operator_display_name(netreg); ofono_sim_read(netreg->sim_context, SIM_EFSPDI_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_spdi_read_cb, netreg); } static void emulator_cops_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_netreg *netreg = userdata; struct ofono_error result; int val; char name[17]; char buf[32]; result.error = 0; switch (ofono_emulator_request_get_type(req)) { case OFONO_EMULATOR_REQUEST_TYPE_SET: ofono_emulator_request_next_number(req, &val); if (val != 3) goto fail; ofono_emulator_request_next_number(req, &val); if (val != 0) goto fail; result.type = OFONO_ERROR_TYPE_NO_ERROR; ofono_emulator_send_final(em, &result); break; case OFONO_EMULATOR_REQUEST_TYPE_QUERY: strncpy(name, get_operator_display_name(netreg), 16); name[16] = '\0'; sprintf(buf, "+COPS: %d,0,\"%s\"", netreg->mode, name); ofono_emulator_send_info(em, buf, TRUE); result.type = OFONO_ERROR_TYPE_NO_ERROR; ofono_emulator_send_final(em, &result); break; default: fail: result.type = OFONO_ERROR_TYPE_FAILURE; ofono_emulator_send_final(em, &result); }; } static void emulator_hfp_init(struct ofono_atom *atom, void *data) { struct ofono_netreg *netreg = data; notify_emulator_status(atom, GINT_TO_POINTER(netreg->status)); notify_emulator_strength(atom, GINT_TO_POINTER(netreg->signal_strength)); ofono_emulator_add_handler(atom, "+COPS", emulator_cops_cb, data, NULL); } static void emulator_hfp_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) emulator_hfp_init(atom, data); } void ofono_netreg_register(struct ofono_netreg *netreg) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(netreg->atom); const char *path = __ofono_atom_get_path(netreg->atom); if (!g_dbus_register_interface(conn, path, OFONO_NETWORK_REGISTRATION_INTERFACE, network_registration_methods, network_registration_signals, NULL, netreg, NULL)) { ofono_error("Could not create %s interface", OFONO_NETWORK_REGISTRATION_INTERFACE); return; } netreg->status_watches = __ofono_watchlist_new(g_free); ofono_modem_add_interface(modem, OFONO_NETWORK_REGISTRATION_INTERFACE); if (netreg->driver->registration_status != NULL) netreg->driver->registration_status(netreg, init_registration_status, netreg); netreg->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem); if (netreg->sim != NULL) { /* Assume that if sim atom exists, it is ready */ netreg->sim_context = ofono_sim_context_create(netreg->sim); netreg_load_settings(netreg); netreg->flags |= NETWORK_REGISTRATION_FLAG_READING_PNN; ofono_sim_read(netreg->sim_context, SIM_EFPNN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, sim_pnn_read_cb, netreg); ofono_sim_add_file_watch(netreg->sim_context, SIM_EFPNN_FILEID, sim_pnn_opl_changed, netreg, NULL); ofono_sim_add_file_watch(netreg->sim_context, SIM_EFOPL_FILEID, sim_pnn_opl_changed, netreg, NULL); ofono_sim_add_spn_watch(netreg->sim, &netreg->spn_watch, spn_read_cb, netreg, NULL); if (__ofono_sim_service_available(netreg->sim, SIM_UST_SERVICE_PROVIDER_DISPLAY_INFO, SIM_SST_SERVICE_PROVIDER_DISPLAY_INFO)) { ofono_sim_read(netreg->sim_context, SIM_EFSPDI_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_spdi_read_cb, netreg); ofono_sim_add_file_watch(netreg->sim_context, SIM_EFSPDI_FILEID, sim_spdi_changed, netreg, NULL); } } __ofono_atom_register(netreg->atom, netreg_unregister); netreg->hfp_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_hfp_watch, netreg, NULL); } void ofono_netreg_remove(struct ofono_netreg *netreg) { __ofono_atom_free(netreg->atom); } void ofono_netreg_set_data(struct ofono_netreg *netreg, void *data) { netreg->driver_data = data; } void *ofono_netreg_get_data(struct ofono_netreg *netreg) { return netreg->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/radio-settings.c0000644000015600001650000004631612671500024022300 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include "ofono.h" #include "common.h" #define RADIO_SETTINGS_FLAG_CACHED 0x1 static GSList *g_drivers = NULL; struct ofono_radio_settings { DBusMessage *pending; int flags; enum ofono_radio_access_mode mode; enum ofono_radio_band_gsm band_gsm; enum ofono_radio_band_umts band_umts; ofono_bool_t fast_dormancy; enum ofono_radio_access_mode pending_mode; enum ofono_radio_band_gsm pending_band_gsm; enum ofono_radio_band_umts pending_band_umts; ofono_bool_t fast_dormancy_pending; uint32_t available_rats; const struct ofono_radio_settings_driver *driver; void *driver_data; struct ofono_atom *atom; }; static const char *radio_access_mode_to_string(enum ofono_radio_access_mode m) { switch (m) { case OFONO_RADIO_ACCESS_MODE_ANY: return "any"; case OFONO_RADIO_ACCESS_MODE_GSM: return "gsm"; case OFONO_RADIO_ACCESS_MODE_UMTS: return "umts"; case OFONO_RADIO_ACCESS_MODE_LTE: return "lte"; default: return ""; } } static gboolean radio_access_mode_from_string(const char *str, enum ofono_radio_access_mode *mode) { if (g_str_equal(str, "any")) { *mode = OFONO_RADIO_ACCESS_MODE_ANY; return TRUE; } else if (g_str_equal(str, "gsm")) { *mode = OFONO_RADIO_ACCESS_MODE_GSM; return TRUE; } else if (g_str_equal(str, "umts")) { *mode = OFONO_RADIO_ACCESS_MODE_UMTS; return TRUE; } else if (g_str_equal(str, "lte")) { *mode = OFONO_RADIO_ACCESS_MODE_LTE; return TRUE; } return FALSE; } static const char *radio_band_gsm_to_string(enum ofono_radio_band_gsm band) { switch (band) { case OFONO_RADIO_BAND_GSM_ANY: return "any"; case OFONO_RADIO_BAND_GSM_850: return "850"; case OFONO_RADIO_BAND_GSM_900P: return "900P"; case OFONO_RADIO_BAND_GSM_900E: return "900E"; case OFONO_RADIO_BAND_GSM_1800: return "1800"; case OFONO_RADIO_BAND_GSM_1900: return "1900"; } return ""; } static gboolean radio_band_gsm_from_string(const char *str, enum ofono_radio_band_gsm *band) { if (g_str_equal(str, "any")) { *band = OFONO_RADIO_BAND_GSM_ANY; return TRUE; } else if (g_str_equal(str, "850")) { *band = OFONO_RADIO_BAND_GSM_850; return TRUE; } else if (g_str_equal(str, "900P")) { *band = OFONO_RADIO_BAND_GSM_900P; return TRUE; } else if (g_str_equal(str, "900E")) { *band = OFONO_RADIO_BAND_GSM_900E; return TRUE; } else if (g_str_equal(str, "1800")) { *band = OFONO_RADIO_BAND_GSM_1800; return TRUE; } else if (g_str_equal(str, "1900")) { *band = OFONO_RADIO_BAND_GSM_1900; return TRUE; } return FALSE; } static const char *radio_band_umts_to_string(enum ofono_radio_band_umts band) { switch (band) { case OFONO_RADIO_BAND_UMTS_ANY: return "any"; case OFONO_RADIO_BAND_UMTS_850: return "850"; case OFONO_RADIO_BAND_UMTS_900: return "900"; case OFONO_RADIO_BAND_UMTS_1700AWS: return "1700AWS"; case OFONO_RADIO_BAND_UMTS_1900: return "1900"; case OFONO_RADIO_BAND_UMTS_2100: return "2100"; } return ""; } static gboolean radio_band_umts_from_string(const char *str, enum ofono_radio_band_umts *band) { if (g_str_equal(str, "any")) { *band = OFONO_RADIO_BAND_GSM_ANY; return TRUE; } else if (g_str_equal(str, "850")) { *band = OFONO_RADIO_BAND_UMTS_850; return TRUE; } else if (g_str_equal(str, "900")) { *band = OFONO_RADIO_BAND_UMTS_900; return TRUE; } else if (g_str_equal(str, "1700AWS")) { *band = OFONO_RADIO_BAND_UMTS_1700AWS; return TRUE; } else if (g_str_equal(str, "1900")) { *band = OFONO_RADIO_BAND_UMTS_1900; return TRUE; } else if (g_str_equal(str, "2100")) { *band = OFONO_RADIO_BAND_UMTS_2100; return TRUE; } return FALSE; } static DBusMessage *radio_get_properties_reply(DBusMessage *msg, struct ofono_radio_settings *rs) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; const char *mode = radio_access_mode_to_string(rs->mode); reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "TechnologyPreference", DBUS_TYPE_STRING, &mode); if (rs->driver->query_band) { const char *band = radio_band_gsm_to_string(rs->band_gsm); ofono_dbus_dict_append(&dict, "GsmBand", DBUS_TYPE_STRING, &band); band = radio_band_umts_to_string(rs->band_umts); ofono_dbus_dict_append(&dict, "UmtsBand", DBUS_TYPE_STRING, &band); } if (rs->driver->query_fast_dormancy) { dbus_bool_t value = rs->fast_dormancy; ofono_dbus_dict_append(&dict, "FastDormancy", DBUS_TYPE_BOOLEAN, &value); } if (rs->available_rats) { const char *rats[sizeof(uint32_t) * CHAR_BIT + 1]; const char **dbus_rats = rats; int n = 0; unsigned int i; for (i = 0; i < sizeof(uint32_t) * CHAR_BIT; i++) { int tech = 1 << i; if (!(rs->available_rats & tech)) continue; rats[n++] = radio_access_mode_to_string(tech); } rats[n] = NULL; ofono_dbus_dict_append_array(&dict, "AvailableTechnologies", DBUS_TYPE_STRING, &dbus_rats); } dbus_message_iter_close_container(&iter, &dict); return reply; } static void radio_set_fast_dormancy(struct ofono_radio_settings *rs, ofono_bool_t enable) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(rs->atom); dbus_bool_t value = enable; if (rs->fast_dormancy == enable) return; ofono_dbus_signal_property_changed(conn, path, OFONO_RADIO_SETTINGS_INTERFACE, "FastDormancy", DBUS_TYPE_BOOLEAN, &value); rs->fast_dormancy = enable; } static void radio_fast_dormancy_set_callback(const struct ofono_error *error, void *data) { struct ofono_radio_settings *rs = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error setting fast dormancy"); rs->fast_dormancy_pending = rs->fast_dormancy; reply = __ofono_error_failed(rs->pending); __ofono_dbus_pending_reply(&rs->pending, reply); return; } reply = dbus_message_new_method_return(rs->pending); __ofono_dbus_pending_reply(&rs->pending, reply); radio_set_fast_dormancy(rs, rs->fast_dormancy_pending); } static void radio_set_band(struct ofono_radio_settings *rs) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; const char *str_band; path = __ofono_atom_get_path(rs->atom); if (rs->band_gsm != rs->pending_band_gsm) { rs->band_gsm = rs->pending_band_gsm; str_band = radio_band_gsm_to_string(rs->band_gsm); ofono_dbus_signal_property_changed(conn, path, OFONO_RADIO_SETTINGS_INTERFACE, "GsmBand", DBUS_TYPE_STRING, &str_band); } if (rs->band_umts != rs->pending_band_umts) { rs->band_umts = rs->pending_band_umts; str_band = radio_band_umts_to_string(rs->band_umts); ofono_dbus_signal_property_changed(conn, path, OFONO_RADIO_SETTINGS_INTERFACE, "UmtsBand", DBUS_TYPE_STRING, &str_band); } } static void radio_band_set_callback(const struct ofono_error *error, void *data) { struct ofono_radio_settings *rs = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error setting radio frequency band"); rs->pending_band_gsm = rs->band_gsm; rs->pending_band_umts = rs->band_umts; reply = __ofono_error_failed(rs->pending); __ofono_dbus_pending_reply(&rs->pending, reply); return; } reply = dbus_message_new_method_return(rs->pending); __ofono_dbus_pending_reply(&rs->pending, reply); radio_set_band(rs); } void ofono_radio_settings_set_rat_mode(struct ofono_radio_settings *rs, enum ofono_radio_access_mode mode) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; const char *str_mode; if (rs->mode == mode) return; rs->mode = mode; path = __ofono_atom_get_path(rs->atom); str_mode = radio_access_mode_to_string(rs->mode); ofono_dbus_signal_property_changed(conn, path, OFONO_RADIO_SETTINGS_INTERFACE, "TechnologyPreference", DBUS_TYPE_STRING, &str_mode); } static void radio_mode_set_callback(const struct ofono_error *error, void *data) { struct ofono_radio_settings *rs = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error setting radio access mode"); rs->pending_mode = rs->mode; reply = __ofono_error_failed(rs->pending); __ofono_dbus_pending_reply(&rs->pending, reply); return; } reply = dbus_message_new_method_return(rs->pending); __ofono_dbus_pending_reply(&rs->pending, reply); ofono_radio_settings_set_rat_mode(rs, rs->pending_mode); } static void radio_send_properties_reply(struct ofono_radio_settings *rs) { DBusMessage *reply; rs->flags |= RADIO_SETTINGS_FLAG_CACHED; reply = radio_get_properties_reply(rs->pending, rs); __ofono_dbus_pending_reply(&rs->pending, reply); } static void radio_available_rats_query_callback(const struct ofono_error *error, unsigned int available_rats, void *data) { struct ofono_radio_settings *rs = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) rs->available_rats = available_rats & 0x7; else DBG("Error while querying available rats"); radio_send_properties_reply(rs); } static void radio_query_available_rats(struct ofono_radio_settings *rs) { /* Modem technology is not supposed to change, so one query is enough */ if (rs->driver->query_available_rats == NULL || rs->available_rats) { radio_send_properties_reply(rs); return; } rs->driver->query_available_rats( rs, radio_available_rats_query_callback, rs); } static void radio_fast_dormancy_query_callback(const struct ofono_error *error, ofono_bool_t enable, void *data) { struct ofono_radio_settings *rs = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error during fast dormancy query"); reply = __ofono_error_failed(rs->pending); __ofono_dbus_pending_reply(&rs->pending, reply); return; } radio_set_fast_dormancy(rs, enable); radio_query_available_rats(rs); } static void radio_query_fast_dormancy(struct ofono_radio_settings *rs) { if (rs->driver->query_fast_dormancy == NULL) { radio_query_available_rats(rs); return; } rs->driver->query_fast_dormancy(rs, radio_fast_dormancy_query_callback, rs); } static void radio_band_query_callback(const struct ofono_error *error, enum ofono_radio_band_gsm band_gsm, enum ofono_radio_band_umts band_umts, void *data) { struct ofono_radio_settings *rs = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error during radio frequency band query"); reply = __ofono_error_failed(rs->pending); __ofono_dbus_pending_reply(&rs->pending, reply); return; } rs->pending_band_gsm = band_gsm; rs->pending_band_umts = band_umts; radio_set_band(rs); radio_query_fast_dormancy(rs); } static void radio_query_band(struct ofono_radio_settings *rs) { if (rs->driver->query_band == NULL) { radio_query_fast_dormancy(rs); return; } rs->driver->query_band(rs, radio_band_query_callback, rs); } static void radio_rat_mode_query_callback(const struct ofono_error *error, enum ofono_radio_access_mode mode, void *data) { struct ofono_radio_settings *rs = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error during radio access mode query"); reply = __ofono_error_failed(rs->pending); __ofono_dbus_pending_reply(&rs->pending, reply); return; } ofono_radio_settings_set_rat_mode(rs, mode); radio_query_band(rs); } static DBusMessage *radio_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_radio_settings *rs = data; if (rs->flags & RADIO_SETTINGS_FLAG_CACHED) return radio_get_properties_reply(msg, rs); if (rs->driver->query_rat_mode == NULL) return __ofono_error_not_implemented(msg); if (rs->pending) return __ofono_error_busy(msg); rs->pending = dbus_message_ref(msg); rs->driver->query_rat_mode(rs, radio_rat_mode_query_callback, rs); return NULL; } static DBusMessage *radio_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_radio_settings *rs = data; DBusMessageIter iter; DBusMessageIter var; const char *property; if (rs->pending) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (g_strcmp0(property, "TechnologyPreference") == 0) { const char *value; enum ofono_radio_access_mode mode; if (rs->driver->set_rat_mode == NULL) return __ofono_error_not_implemented(msg); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (radio_access_mode_from_string(value, &mode) == FALSE) return __ofono_error_invalid_args(msg); if (rs->mode == mode) return dbus_message_new_method_return(msg); rs->pending = dbus_message_ref(msg); rs->pending_mode = mode; rs->driver->set_rat_mode(rs, mode, radio_mode_set_callback, rs); return NULL; } else if (g_strcmp0(property, "GsmBand") == 0) { const char *value; enum ofono_radio_band_gsm band; if (rs->driver->set_band == NULL) return __ofono_error_not_implemented(msg); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (radio_band_gsm_from_string(value, &band) == FALSE) return __ofono_error_invalid_args(msg); if (rs->band_gsm == band) return dbus_message_new_method_return(msg); rs->pending = dbus_message_ref(msg); rs->pending_band_gsm = band; rs->driver->set_band(rs, band, rs->band_umts, radio_band_set_callback, rs); return NULL; } else if (g_strcmp0(property, "UmtsBand") == 0) { const char *value; enum ofono_radio_band_umts band; if (rs->driver->set_band == NULL) return __ofono_error_not_implemented(msg); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (radio_band_umts_from_string(value, &band) == FALSE) return __ofono_error_invalid_args(msg); if (rs->band_umts == band) return dbus_message_new_method_return(msg); rs->pending = dbus_message_ref(msg); rs->pending_band_umts = band; rs->driver->set_band(rs, rs->band_gsm, band, radio_band_set_callback, rs); return NULL; } else if (g_strcmp0(property, "FastDormancy") == 0) { dbus_bool_t value; int target; if (rs->driver->set_fast_dormancy == NULL) return __ofono_error_not_implemented(msg); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); target = value; if (rs->fast_dormancy_pending == target) return dbus_message_new_method_return(msg); rs->pending = dbus_message_ref(msg); rs->fast_dormancy_pending = target; rs->driver->set_fast_dormancy(rs, target, radio_fast_dormancy_set_callback, rs); return NULL; } return __ofono_error_invalid_args(msg); } static const GDBusMethodTable radio_methods[] = { { GDBUS_ASYNC_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), radio_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, radio_set_property) }, { } }; static const GDBusSignalTable radio_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; int ofono_radio_settings_driver_register(const struct ofono_radio_settings_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d == NULL || d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_radio_settings_driver_unregister(const struct ofono_radio_settings_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d == NULL) return; g_drivers = g_slist_remove(g_drivers, (void *) d); } static void radio_settings_unregister(struct ofono_atom *atom) { struct ofono_radio_settings *rs = __ofono_atom_get_data(atom); const char *path = __ofono_atom_get_path(rs->atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(rs->atom); ofono_modem_remove_interface(modem, OFONO_RADIO_SETTINGS_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_RADIO_SETTINGS_INTERFACE); } static void radio_settings_remove(struct ofono_atom *atom) { struct ofono_radio_settings *rs = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (rs == NULL) return; if (rs->driver && rs->driver->remove) rs->driver->remove(rs); g_free(rs); } struct ofono_radio_settings *ofono_radio_settings_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_radio_settings *rs; GSList *l; if (driver == NULL) return NULL; rs = g_try_new0(struct ofono_radio_settings, 1); if (rs == NULL) return NULL; rs->mode = -1; rs->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_RADIO_SETTINGS, radio_settings_remove, rs); for (l = g_drivers; l; l = l->next) { const struct ofono_radio_settings_driver *drv = l->data; if (g_strcmp0(drv->name, driver) != 0) continue; if (drv->probe(rs, vendor, data) < 0) continue; rs->driver = drv; break; } return rs; } void ofono_radio_settings_register(struct ofono_radio_settings *rs) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(rs->atom); const char *path = __ofono_atom_get_path(rs->atom); if (!g_dbus_register_interface(conn, path, OFONO_RADIO_SETTINGS_INTERFACE, radio_methods, radio_signals, NULL, rs, NULL)) { ofono_error("Could not create %s interface", OFONO_RADIO_SETTINGS_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_RADIO_SETTINGS_INTERFACE); __ofono_atom_register(rs->atom, radio_settings_unregister); } void ofono_radio_settings_remove(struct ofono_radio_settings *rs) { __ofono_atom_free(rs->atom); } void ofono_radio_settings_set_data(struct ofono_radio_settings *rs, void *data) { rs->driver_data = data; } void *ofono_radio_settings_get_data(struct ofono_radio_settings *rs) { return rs->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/handsfree.c0000644000015600001650000004516612671500024021305 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 BMW Car IT GmbH. 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" #include "hfp.h" static GSList *g_drivers = NULL; #define HANDSFREE_FLAG_CACHED 0x1 struct ofono_handsfree { ofono_bool_t nrec; ofono_bool_t inband_ringing; ofono_bool_t voice_recognition; ofono_bool_t voice_recognition_pending; ofono_bool_t ddr; ofono_bool_t ddr_pending; ofono_bool_t have_ddr; ofono_bool_t ddr_active; unsigned int ag_features; unsigned int ag_chld_features; unsigned char battchg; GSList *subscriber_numbers; const struct ofono_handsfree_driver *driver; void *driver_data; struct ofono_atom *atom; DBusMessage *pending; int flags; }; static const char **ag_features_list(unsigned int features, unsigned int chld_features) { /* * BRSF response is a 32-bit unsigned int. Only 32 entries are posible, * and we do not ever report the presence of bit 8. */ static const char *list[32]; unsigned int i = 0; if (features & HFP_AG_FEATURE_3WAY) list[i++] = "three-way-calling"; if (features & HFP_AG_FEATURE_ECNR) list[i++] = "echo-canceling-and-noise-reduction"; if (features & HFP_AG_FEATURE_VOICE_RECOG) list[i++] = "voice-recognition"; if (features & HFP_AG_FEATURE_ATTACH_VOICE_TAG) list[i++] = "attach-voice-tag"; if (chld_features & HFP_AG_CHLD_0) list[i++] = "release-all-held"; if (chld_features & HFP_AG_CHLD_1x) list[i++] = "release-specified-active-call"; if (chld_features & HFP_AG_CHLD_2x) list[i++] = "private-chat"; if (chld_features & HFP_AG_CHLD_3) list[i++] = "create-multiparty"; if (chld_features & HFP_AG_CHLD_4) list[i++] = "transfer"; if (features & HFP_AG_FEATURE_HF_INDICATORS) list[i++] = "hf-indicators"; list[i] = NULL; return list; } void ofono_handsfree_set_inband_ringing(struct ofono_handsfree *hf, ofono_bool_t enabled) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(hf->atom); dbus_bool_t dbus_enabled = enabled; if (hf->inband_ringing == enabled) return; hf->inband_ringing = enabled; if (__ofono_atom_get_registered(hf->atom) == FALSE) return; ofono_dbus_signal_property_changed(conn, path, OFONO_HANDSFREE_INTERFACE, "InbandRinging", DBUS_TYPE_BOOLEAN, &dbus_enabled); } void ofono_handsfree_voice_recognition_notify(struct ofono_handsfree *hf, ofono_bool_t enabled) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(hf->atom); dbus_bool_t dbus_enabled = enabled; if (hf->voice_recognition == enabled) return; hf->voice_recognition = enabled; ofono_dbus_signal_property_changed(conn, path, OFONO_HANDSFREE_INTERFACE, "VoiceRecognition", DBUS_TYPE_BOOLEAN, &dbus_enabled); } void ofono_handsfree_set_ag_features(struct ofono_handsfree *hf, unsigned int ag_features) { if (hf == NULL) return; hf->ag_features = ag_features; } void ofono_handsfree_set_ag_chld_features(struct ofono_handsfree *hf, unsigned int ag_chld_features) { if (hf == NULL) return; hf->ag_chld_features = ag_chld_features; } void ofono_handsfree_battchg_notify(struct ofono_handsfree *hf, unsigned char level) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(hf->atom); if (hf == NULL) return; if (hf->battchg == level) return; hf->battchg = level; if (__ofono_atom_get_registered(hf->atom) == FALSE) return; ofono_dbus_signal_property_changed(conn, path, OFONO_HANDSFREE_INTERFACE, "BatteryChargeLevel", DBUS_TYPE_BYTE, &level); } static void append_subscriber_numbers(GSList *subscriber_numbers, DBusMessageIter *iter) { DBusMessageIter entry; DBusMessageIter variant, array; GSList *l; const char *subscriber_number_string; char arraysig[3]; const char *key = "SubscriberNumbers"; arraysig[0] = DBUS_TYPE_ARRAY; arraysig[1] = DBUS_TYPE_STRING; arraysig[2] = '\0'; dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, arraysig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array); for (l = subscriber_numbers; l; l = l->next) { subscriber_number_string = phone_number_to_string(l->data); dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &subscriber_number_string); } dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(&entry, &variant); dbus_message_iter_close_container(iter, &entry); } static DBusMessage *generate_get_properties_reply(struct ofono_handsfree *hf, DBusMessage *msg) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; dbus_bool_t inband_ringing; dbus_bool_t voice_recognition; const char **features; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); inband_ringing = hf->inband_ringing; ofono_dbus_dict_append(&dict, "InbandRinging", DBUS_TYPE_BOOLEAN, &inband_ringing); if (hf->ag_features & HFP_AG_FEATURE_ECNR) ofono_dbus_dict_append(&dict, "EchoCancelingNoiseReduction", DBUS_TYPE_BOOLEAN, &hf->nrec); if (hf->ag_features & HFP_AG_FEATURE_HF_INDICATORS) ofono_dbus_dict_append(&dict, "DistractedDrivingReduction", DBUS_TYPE_BOOLEAN, &hf->ddr); voice_recognition = hf->voice_recognition; ofono_dbus_dict_append(&dict, "VoiceRecognition", DBUS_TYPE_BOOLEAN, &voice_recognition); features = ag_features_list(hf->ag_features, hf->ag_chld_features); ofono_dbus_dict_append_array(&dict, "Features", DBUS_TYPE_STRING, &features); ofono_dbus_dict_append(&dict, "BatteryChargeLevel", DBUS_TYPE_BYTE, &hf->battchg); if (hf->subscriber_numbers) append_subscriber_numbers(hf->subscriber_numbers, &dict); dbus_message_iter_close_container(&iter, &dict); return reply; } static void hf_cnum_callback(const struct ofono_error *error, int total, const struct ofono_phone_number *numbers, void *data) { struct ofono_handsfree *hf = data; int num; struct ofono_phone_number *subscriber_number; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) goto out; for (num = 0; num < total; num++) { subscriber_number = g_new0(struct ofono_phone_number, 1); subscriber_number->type = numbers[num].type; strncpy(subscriber_number->number, numbers[num].number, OFONO_MAX_PHONE_NUMBER_LENGTH + 1); hf->subscriber_numbers = g_slist_prepend(hf->subscriber_numbers, subscriber_number); } hf->subscriber_numbers = g_slist_reverse(hf->subscriber_numbers); out: hf->flags |= HANDSFREE_FLAG_CACHED; if (hf->pending) { DBusMessage *reply = generate_get_properties_reply(hf, hf->pending); __ofono_dbus_pending_reply(&hf->pending, reply); } } static void query_cnum(struct ofono_handsfree *hf) { DBusMessage *reply; if (hf->driver->cnum_query != NULL) { hf->driver->cnum_query(hf, hf_cnum_callback, hf); return; } if (hf->pending == NULL) return; reply = generate_get_properties_reply(hf, hf->pending); __ofono_dbus_pending_reply(&hf->pending, reply); } static DBusMessage *handsfree_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_handsfree *hf = data; if (hf->pending != NULL) return __ofono_error_busy(msg); if (hf->flags & HANDSFREE_FLAG_CACHED) return generate_get_properties_reply(hf, msg); /* Query the settings and report back */ hf->pending = dbus_message_ref(msg); query_cnum(hf); return NULL; } static void voicerec_set_cb(const struct ofono_error *error, void *data) { struct ofono_handsfree *hf = data; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(hf->atom); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { __ofono_dbus_pending_reply(&hf->pending, __ofono_error_failed(hf->pending)); return; } hf->voice_recognition = hf->voice_recognition_pending; __ofono_dbus_pending_reply(&hf->pending, dbus_message_new_method_return(hf->pending)); ofono_dbus_signal_property_changed(conn, path, OFONO_HANDSFREE_INTERFACE, "VoiceRecognition", DBUS_TYPE_BOOLEAN, &hf->voice_recognition); } static void ddr_set_cb(const struct ofono_error *error, void *data) { struct ofono_handsfree *hf = data; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(hf->atom); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { __ofono_dbus_pending_reply(&hf->pending, __ofono_error_failed(hf->pending)); return; } hf->ddr = hf->ddr_pending; __ofono_dbus_pending_reply(&hf->pending, dbus_message_new_method_return(hf->pending)); ofono_dbus_signal_property_changed(conn, path, OFONO_HANDSFREE_INTERFACE, "DistractedDrivingReduction", DBUS_TYPE_BOOLEAN, &hf->voice_recognition); } void ofono_handsfree_set_hf_indicators(struct ofono_handsfree *hf, const unsigned short *indicators, unsigned int num) { unsigned int i; for (i = 0; i < num; i++) { switch (indicators[i]) { case HFP_HF_INDICATOR_ENHANCED_SAFETY: hf->have_ddr = TRUE; break; } } } static void ddr_update_cb(const struct ofono_error *error, void *data) { if (error->type == OFONO_ERROR_TYPE_NO_ERROR) return; ofono_info("Failed to update DDR indicator"); } void ofono_handsfree_hf_indicator_active_notify(struct ofono_handsfree *hf, unsigned int indicator, ofono_bool_t active) { DBG("%d, %d", indicator, active); if (active) active = TRUE; else active = FALSE; switch (indicator) { case HFP_HF_INDICATOR_ENHANCED_SAFETY: if (!hf->have_ddr) return; if (hf->ddr_active == active) return; hf->ddr_active = active; if (hf->ddr_active && hf->driver && hf->driver->hf_indicator) hf->driver->hf_indicator(hf, HFP_HF_INDICATOR_ENHANCED_SAFETY, hf->ddr, ddr_update_cb, hf); break; } } static void nrec_set_cb(const struct ofono_error *error, void *data) { struct ofono_handsfree *hf = data; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(hf->atom); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { __ofono_dbus_pending_reply(&hf->pending, __ofono_error_failed(hf->pending)); return; } hf->nrec = FALSE; __ofono_dbus_pending_reply(&hf->pending, dbus_message_new_method_return(hf->pending)); ofono_dbus_signal_property_changed(conn, path, OFONO_HANDSFREE_INTERFACE, "EchoCancelingNoiseReduction", DBUS_TYPE_BOOLEAN, &hf->nrec); } static DBusMessage *handsfree_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_handsfree *hf = data; DBusMessageIter iter, var; ofono_bool_t enabled; const char *name; if (hf->pending) return __ofono_error_busy(msg); if (dbus_message_iter_init(msg, &iter) == FALSE) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &enabled); if (g_str_equal(name, "VoiceRecognition") == TRUE) { if (!hf->driver->voice_recognition) return __ofono_error_not_implemented(msg); if (hf->voice_recognition == enabled) return dbus_message_new_method_return(msg); hf->voice_recognition_pending = enabled; hf->pending = dbus_message_ref(msg); hf->driver->voice_recognition(hf, enabled, voicerec_set_cb, hf); } else if (g_str_equal(name, "EchoCancelingNoiseReduction") == TRUE) { if (!(hf->ag_features & HFP_AG_FEATURE_ECNR)) return __ofono_error_not_supported(msg); if (!hf->driver->disable_nrec || enabled == TRUE) return __ofono_error_not_implemented(msg); if (hf->nrec == FALSE) return dbus_message_new_method_return(msg); hf->pending = dbus_message_ref(msg); hf->driver->disable_nrec(hf, nrec_set_cb, hf); } else if (g_str_equal(name, "DistractedDrivingReduction") == TRUE) { if (!(hf->ag_features & HFP_AG_FEATURE_HF_INDICATORS)) return __ofono_error_not_supported(msg); if (!hf->driver->hf_indicator) return __ofono_error_not_implemented(msg); if (!hf->have_ddr) return __ofono_error_not_supported(msg); if (hf->ddr == enabled) return dbus_message_new_method_return(msg); if (!hf->ddr_active) { hf->ddr = enabled; g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, __ofono_atom_get_path(hf->atom), OFONO_HANDSFREE_INTERFACE, "DistractedDrivingReduction", DBUS_TYPE_BOOLEAN, &hf->voice_recognition); } else { hf->pending = dbus_message_ref(msg); hf->ddr_pending = enabled; hf->driver->hf_indicator(hf, HFP_HF_INDICATOR_ENHANCED_SAFETY, enabled, ddr_set_cb, hf); } } else return __ofono_error_invalid_args(msg); return NULL; } static void request_phone_number_cb(const struct ofono_error *error, const struct ofono_phone_number *number, void *data) { struct ofono_handsfree *hf = data; DBusMessage *reply; const char *phone_number; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Phone number request callback returned error: %s", telephony_error_to_str(error)); reply = __ofono_error_failed(hf->pending); __ofono_dbus_pending_reply(&hf->pending, reply); return; } phone_number = phone_number_to_string(number); reply = dbus_message_new_method_return(hf->pending); dbus_message_append_args(reply, DBUS_TYPE_STRING, &phone_number, DBUS_TYPE_INVALID); __ofono_dbus_pending_reply(&hf->pending, reply); } static DBusMessage *handsfree_request_phone_number(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_handsfree *hf = data; if (hf->pending) return __ofono_error_busy(msg); if (!hf->driver->request_phone_number) return __ofono_error_not_supported(msg); hf->pending = dbus_message_ref(msg); hf->driver->request_phone_number(hf, request_phone_number_cb, hf); return NULL; } static const GDBusMethodTable handsfree_methods[] = { { GDBUS_ASYNC_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), handsfree_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, handsfree_set_property) }, { GDBUS_ASYNC_METHOD("RequestPhoneNumber", NULL, GDBUS_ARGS({ "number", "s" }), handsfree_request_phone_number) }, { } }; static const GDBusSignalTable handsfree_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static void handsfree_remove(struct ofono_atom *atom) { struct ofono_handsfree *hf = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (hf == NULL) return; if (hf->driver != NULL && hf->driver->remove != NULL) hf->driver->remove(hf); g_free(hf); } struct ofono_handsfree *ofono_handsfree_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_handsfree *hf; GSList *l; if (driver == NULL) return NULL; hf = g_try_new0(struct ofono_handsfree, 1); if (hf == NULL) return NULL; hf->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_HANDSFREE, handsfree_remove, hf); hf->nrec = TRUE; for (l = g_drivers; l; l = l->next) { const struct ofono_handsfree_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(hf, vendor, data) < 0) continue; hf->driver = drv; break; } return hf; } static void handsfree_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); struct ofono_handsfree *hf = __ofono_atom_get_data(atom); if (hf->pending) { DBusMessage *reply = __ofono_error_failed(hf->pending); __ofono_dbus_pending_reply(&hf->pending, reply); } g_slist_foreach(hf->subscriber_numbers, (GFunc) g_free, NULL); g_slist_free(hf->subscriber_numbers); hf->subscriber_numbers = NULL; ofono_modem_remove_interface(modem, OFONO_HANDSFREE_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_HANDSFREE_INTERFACE); } void ofono_handsfree_register(struct ofono_handsfree *hf) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(hf->atom); const char *path = __ofono_atom_get_path(hf->atom); if (!g_dbus_register_interface(conn, path, OFONO_HANDSFREE_INTERFACE, handsfree_methods, handsfree_signals, NULL, hf, NULL)) { ofono_error("Could not create %s interface", OFONO_HANDSFREE_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_HANDSFREE_INTERFACE); __ofono_atom_register(hf->atom, handsfree_unregister); } int ofono_handsfree_driver_register(const struct ofono_handsfree_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_handsfree_driver_unregister( const struct ofono_handsfree_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } void ofono_handsfree_remove(struct ofono_handsfree *hf) { __ofono_atom_free(hf->atom); } void ofono_handsfree_set_data(struct ofono_handsfree *hf, void *data) { hf->driver_data = data; } void *ofono_handsfree_get_data(struct ofono_handsfree *hf) { return hf->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/call-volume.c0000644000015600001650000002556412671500024021566 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" static GSList *g_drivers = NULL; struct ofono_call_volume { DBusMessage *pending; unsigned char speaker_volume; unsigned char microphone_volume; unsigned char pending_volume; gboolean muted; gboolean muted_pending; const struct ofono_call_volume_driver *driver; void *driver_data; struct ofono_atom *atom; }; void ofono_call_volume_set_speaker_volume(struct ofono_call_volume *cv, unsigned char percent) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cv->atom); cv->speaker_volume = percent; if (__ofono_atom_get_registered(cv->atom) == FALSE) return; ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_VOLUME_INTERFACE, "SpeakerVolume", DBUS_TYPE_BYTE, &percent); } void ofono_call_volume_set_microphone_volume(struct ofono_call_volume *cv, unsigned char percent) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cv->atom); cv->microphone_volume = percent; if (__ofono_atom_get_registered(cv->atom) == FALSE) return; ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_VOLUME_INTERFACE, "MicrophoneVolume", DBUS_TYPE_BYTE, &percent); } void ofono_call_volume_set_muted(struct ofono_call_volume *cv, int muted) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cv->atom); dbus_bool_t m; cv->muted = muted; if (__ofono_atom_get_registered(cv->atom) == FALSE) return; m = muted; ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_VOLUME_INTERFACE, "Muted", DBUS_TYPE_BOOLEAN, &m); } static DBusMessage *cv_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_volume *cv = data; DBusMessage *reply; DBusMessageIter iter, dict; dbus_bool_t muted; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "SpeakerVolume", DBUS_TYPE_BYTE, &cv->speaker_volume); ofono_dbus_dict_append(&dict, "MicrophoneVolume", DBUS_TYPE_BYTE, &cv->microphone_volume); muted = cv->muted; ofono_dbus_dict_append(&dict, "Muted", DBUS_TYPE_BOOLEAN, &muted); dbus_message_iter_close_container(&iter, &dict); return reply; } static void sv_set_callback(const struct ofono_error *error, void *data) { struct ofono_call_volume *cv = data; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cv->atom); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { __ofono_dbus_pending_reply(&cv->pending, __ofono_error_failed(cv->pending)); return; } cv->speaker_volume = cv->pending_volume; __ofono_dbus_pending_reply(&cv->pending, dbus_message_new_method_return(cv->pending)); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_VOLUME_INTERFACE, "SpeakerVolume", DBUS_TYPE_BYTE, &cv->speaker_volume); } static void mv_set_callback(const struct ofono_error *error, void *data) { struct ofono_call_volume *cv = data; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cv->atom); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { __ofono_dbus_pending_reply(&cv->pending, __ofono_error_failed(cv->pending)); return; } cv->microphone_volume = cv->pending_volume; __ofono_dbus_pending_reply(&cv->pending, dbus_message_new_method_return(cv->pending)); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_VOLUME_INTERFACE, "MicrophoneVolume", DBUS_TYPE_BYTE, &cv->microphone_volume); } static void muted_set_callback(const struct ofono_error *error, void *data) { struct ofono_call_volume *cv = data; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cv->atom); dbus_bool_t m; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { cv->muted_pending = cv->muted; __ofono_dbus_pending_reply(&cv->pending, __ofono_error_failed(cv->pending)); return; } cv->muted = cv->muted_pending; m = cv->muted; __ofono_dbus_pending_reply(&cv->pending, dbus_message_new_method_return(cv->pending)); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_VOLUME_INTERFACE, "Muted", DBUS_TYPE_BOOLEAN, &m); } static DBusMessage *cv_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_volume *cv = data; DBusMessageIter iter; DBusMessageIter var; const char *property; if (cv->pending) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (g_str_equal(property, "SpeakerVolume") == TRUE) { unsigned char percent; if (cv->driver->speaker_volume == NULL) return __ofono_error_not_implemented(msg); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BYTE) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &percent); if (percent > 100) return __ofono_error_invalid_format(msg); if (percent == cv->speaker_volume) return dbus_message_new_method_return(msg); cv->pending_volume = percent; cv->pending = dbus_message_ref(msg); cv->driver->speaker_volume(cv, percent, sv_set_callback, cv); return NULL; } else if (g_str_equal(property, "MicrophoneVolume") == TRUE) { unsigned char percent; if (cv->driver->microphone_volume == NULL) return __ofono_error_not_implemented(msg); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BYTE) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &percent); if (percent > 100) return __ofono_error_invalid_format(msg); if (percent == cv->microphone_volume) return dbus_message_new_method_return(msg); cv->pending_volume = percent; cv->pending = dbus_message_ref(msg); cv->driver->microphone_volume(cv, percent, mv_set_callback, cv); return NULL; } else if (g_str_equal(property, "Muted") == TRUE) { dbus_bool_t muted; if (cv->driver->mute == NULL) return __ofono_error_not_implemented(msg); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &muted); if (muted == (dbus_bool_t) cv->muted) return dbus_message_new_method_return(msg); cv->muted_pending = muted; cv->pending = dbus_message_ref(msg); cv->driver->mute(cv, muted, muted_set_callback, cv); return NULL; } return __ofono_error_invalid_args(msg); } static const GDBusMethodTable cv_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), cv_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, cv_set_property) }, { } }; static const GDBusSignalTable cv_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "property", "s" }, { "value", "v" })) }, { } }; static void call_volume_remove(struct ofono_atom *atom) { struct ofono_call_volume *cv = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (cv == NULL) return; if (cv->driver != NULL && cv->driver->remove != NULL) cv->driver->remove(cv); g_free(cv); } struct ofono_call_volume *ofono_call_volume_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_call_volume *cv; GSList *l; if (driver == NULL) return NULL; cv = g_try_new0(struct ofono_call_volume, 1); if (cv == NULL) return NULL; cv->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPES_CALL_VOLUME, call_volume_remove, cv); for (l = g_drivers; l; l = l->next) { const struct ofono_call_volume_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(cv, vendor, data) < 0) continue; cv->driver = drv; break; } return cv; } static void call_volume_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); ofono_modem_remove_interface(modem, OFONO_CALL_VOLUME_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_CALL_VOLUME_INTERFACE); } void ofono_call_volume_register(struct ofono_call_volume *cv) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(cv->atom); const char *path = __ofono_atom_get_path(cv->atom); if (!g_dbus_register_interface(conn, path, OFONO_CALL_VOLUME_INTERFACE, cv_methods, cv_signals, NULL, cv, NULL)) { ofono_error("Could not create %s interface", OFONO_CALL_VOLUME_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_CALL_VOLUME_INTERFACE); __ofono_atom_register(cv->atom, call_volume_unregister); } int ofono_call_volume_driver_register(const struct ofono_call_volume_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_call_volume_driver_unregister( const struct ofono_call_volume_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } void ofono_call_volume_remove(struct ofono_call_volume *cv) { __ofono_atom_free(cv->atom); } void ofono_call_volume_set_data(struct ofono_call_volume *cv, void *data) { cv->driver_data = data; } void *ofono_call_volume_get_data(struct ofono_call_volume *cv) { return cv->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/emulator.c0000644000015600001650000012737312671500073021203 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include "ofono.h" #include "common.h" #include "hfp.h" #include "gatserver.h" #include "gatppp.h" #include "handsfree-audio.h" #include "system-settings.h" #define RING_TIMEOUT 3 #define CVSD_OFFSET 0 #define MSBC_OFFSET 1 #define CODECS_COUNT (MSBC_OFFSET + 1) struct hfp_codec_info { unsigned char type; ofono_bool_t supported; }; struct atom_callback { struct ofono_atom *atom; ofono_emulator_request_cb_t cb; void *data; ofono_destroy_func destroy; }; struct ofono_emulator { GSList *atoms; struct ofono_atom *active_atom; enum ofono_emulator_type type; GAtServer *server; GAtPPP *ppp; int l_features; int r_features; GSList *indicators; guint callsetup_source; int pns_id; struct ofono_handsfree_card *card; struct hfp_codec_info r_codecs[CODECS_COUNT]; unsigned char selected_codec; unsigned char negotiated_codec; unsigned char proposed_codec; ofono_emulator_codec_negotiation_cb codec_negotiation_cb; void *codec_negotiation_data; ofono_bool_t bac_received; /* Table of atom_callback, using prefixes as key */ GHashTable *prefixes; bool slc : 1; unsigned int events_mode : 2; bool events_ind : 1; unsigned int cmee_mode : 2; bool clip : 1; bool ccwa : 1; bool ddr_active : 1; }; struct indicator { char *name; int value; int min; int max; gboolean deferred; gboolean active; gboolean mandatory; }; static void emulator_debug(const char *str, void *data) { ofono_info("%s: %s\n", (char *)data, str); } static void emulator_disconnect(gpointer user_data) { struct ofono_emulator *em = user_data; DBG("%p", em); ofono_emulator_remove(em); } static void ppp_connect(const char *iface, const char *local, const char *remote, const char *dns1, const char *dns2, gpointer user_data) { DBG("Network Device: %s\n", iface); DBG("IP Address: %s\n", local); DBG("Remote IP Address: %s\n", remote); DBG("Primary DNS Server: %s\n", dns1); DBG("Secondary DNS Server: %s\n", dns2); } static void cleanup_ppp(struct ofono_emulator *em) { DBG(""); g_at_ppp_unref(em->ppp); em->ppp = NULL; __ofono_private_network_release(em->pns_id); em->pns_id = 0; g_at_server_resume(em->server); g_at_server_send_final(em->server, G_AT_SERVER_RESULT_NO_CARRIER); } static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data) { struct ofono_emulator *em = user_data; cleanup_ppp(em); } static void ppp_suspend(gpointer user_data) { struct ofono_emulator *em = user_data; DBG(""); g_at_server_resume(em->server); } static void suspend_server(gpointer user_data) { struct ofono_emulator *em = user_data; GAtIO *io = g_at_server_get_io(em->server); g_at_server_suspend(em->server); if (g_at_ppp_listen(em->ppp, io) == FALSE) cleanup_ppp(em); } static void request_private_network_cb( const struct ofono_private_network_settings *pns, void *data) { struct ofono_emulator *em = data; GAtIO *io = g_at_server_get_io(em->server); if (pns == NULL) goto error; em->ppp = g_at_ppp_server_new_full(pns->server_ip, pns->fd); if (em->ppp == NULL) { close(pns->fd); goto badalloc; } g_at_ppp_set_server_info(em->ppp, pns->peer_ip, pns->primary_dns, pns->secondary_dns); g_at_ppp_set_acfc_enabled(em->ppp, TRUE); g_at_ppp_set_pfc_enabled(em->ppp, TRUE); g_at_ppp_set_credentials(em->ppp, "", ""); g_at_ppp_set_debug(em->ppp, emulator_debug, "PPP"); g_at_ppp_set_connect_function(em->ppp, ppp_connect, em); g_at_ppp_set_disconnect_function(em->ppp, ppp_disconnect, em); g_at_ppp_set_suspend_function(em->ppp, ppp_suspend, em); g_at_server_send_intermediate(em->server, "CONNECT"); g_at_io_set_write_done(io, suspend_server, em); return; badalloc: __ofono_private_network_release(em->pns_id); error: em->pns_id = 0; g_at_server_send_final(em->server, G_AT_SERVER_RESULT_ERROR); } static gboolean dial_call(struct ofono_emulator *em, const char *dial_str) { char c = *dial_str; DBG("dial call %s", dial_str); if (c == '*' || c == '#' || c == 'T' || c == 't') { if (__ofono_private_network_request(request_private_network_cb, &em->pns_id, em) == FALSE) return FALSE; } return TRUE; } static void dial_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; GAtResultIter iter; const char *dial_str; DBG(""); if (type != G_AT_SERVER_REQUEST_TYPE_SET) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "")) goto error; dial_str = g_at_result_iter_raw_line(&iter); if (!dial_str) goto error; if (em->ppp) goto error; if (!dial_call(em, dial_str)) goto error; return; error: g_at_server_send_final(em->server, G_AT_SERVER_RESULT_ERROR); } static void dun_ath_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; GAtResultIter iter; int val; DBG(""); switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &val) == FALSE) goto error; if (val != 0) goto error; /* Fall through */ case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: if (em->ppp == NULL) goto error; g_at_ppp_unref(em->ppp); em->ppp = NULL; __ofono_private_network_release(em->pns_id); em->pns_id = 0; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: error: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void resume_ppp(gpointer user_data) { struct ofono_emulator *em = user_data; g_at_server_suspend(em->server); g_at_ppp_resume(em->ppp); } static void dun_ato_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; GAtIO *io = g_at_server_get_io(em->server); GAtResultIter iter; int val; DBG(""); switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &val) == FALSE) goto error; if (val != 0) goto error; /* Fall through */ case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: if (em->ppp == NULL) goto error; g_at_server_send_intermediate(em->server, "CONNECT"); g_at_io_set_write_done(io, resume_ppp, em); break; default: error: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static struct indicator *find_indicator(struct ofono_emulator *em, const char *name, int *index) { GSList *l; int i; for (i = 1, l = em->indicators; l; l = l->next, i++) { struct indicator *ind = l->data; if (g_str_equal(ind->name, name) == FALSE) continue; if (index) *index = i; return ind; } return NULL; } static struct ofono_call *find_call_with_status(struct ofono_emulator *em, int status) { struct ofono_modem *modem; struct ofono_voicecall *vc; if (em->active_atom == NULL) return NULL; modem = __ofono_atom_get_modem(em->active_atom); vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, modem); if (vc == NULL) return NULL; return __ofono_voicecall_find_call_with_status(vc, status); } static void notify_deferred_indicators(GAtServer *server, void *user_data) { struct ofono_emulator *em = user_data; int i; char buf[20]; GSList *l; struct indicator *ind; for (i = 1, l = em->indicators; l; l = l->next, i++) { ind = l->data; if (!ind->deferred) continue; if (em->events_mode == 3 && em->events_ind && em->slc && ind->active) { sprintf(buf, "+CIEV: %d,%d", i, ind->value); g_at_server_send_unsolicited(em->server, buf); } ind->deferred = FALSE; } } static gboolean notify_ccwa(void *user_data) { struct ofono_emulator *em = user_data; struct ofono_call *c; const char *phone; /* * '+CCWA: "+",' + phone number + phone type on 3 digits max * + terminating null */ char str[OFONO_MAX_PHONE_NUMBER_LENGTH + 14 + 1]; if ((em->type == OFONO_EMULATOR_TYPE_HFP && em->slc == FALSE) || !em->ccwa) goto end; c = find_call_with_status(em, CALL_STATUS_WAITING); if (c && c->clip_validity == CLIP_VALIDITY_VALID) { phone = phone_number_to_string(&c->phone_number); sprintf(str, "+CCWA: \"%s\",%d", phone, c->phone_number.type); g_at_server_send_unsolicited(em->server, str); } else g_at_server_send_unsolicited(em->server, "+CCWA: \"\",128"); end: em->callsetup_source = 0; return FALSE; } static gboolean notify_ring(void *user_data) { struct ofono_emulator *em = user_data; struct ofono_call *c; const char *phone; /* * '+CLIP: "+",' + phone number + phone type on 3 digits max * + terminating null */ char str[OFONO_MAX_PHONE_NUMBER_LENGTH + 14 + 1]; if (em->type == OFONO_EMULATOR_TYPE_HFP && em->slc == FALSE) return TRUE; g_at_server_send_unsolicited(em->server, "RING"); if (!em->clip) return TRUE; c = find_call_with_status(em, CALL_STATUS_INCOMING); if (c == NULL) return TRUE; switch (c->clip_validity) { case CLIP_VALIDITY_VALID: phone = phone_number_to_string(&c->phone_number); sprintf(str, "+CLIP: \"%s\",%d", phone, c->phone_number.type); g_at_server_send_unsolicited(em->server, str); break; case CLIP_VALIDITY_WITHHELD: g_at_server_send_unsolicited(em->server, "+CLIP: \"\",128"); break; } return TRUE; } static void brsf_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; GAtResultIter iter; int val; char buf[16]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &val) == FALSE) goto fail; if (val < 0 || val > 0xffff) goto fail; em->r_features = val; sprintf(buf, "+BRSF: %d", em->l_features); g_at_server_send_info(em->server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: fail: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void cind_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; GSList *l; struct indicator *ind; gsize size; int len; char *buf; char *tmp; switch (type) { case G_AT_SERVER_REQUEST_TYPE_QUERY: /* * "+CIND: " + terminating null + number of indicators * * (max of 3 digits in the value + separator) */ size = 7 + 1 + (g_slist_length(em->indicators) * 4); buf = g_try_malloc0(size); if (buf == NULL) goto fail; len = sprintf(buf, "+CIND: "); tmp = buf + len; for (l = em->indicators; l; l = l->next) { ind = l->data; len = sprintf(tmp, "%s%d", l == em->indicators ? "" : ",", ind->value); tmp = tmp + len; } g_at_server_send_info(em->server, buf, TRUE); g_free(buf); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: /* * '+CIND: ' + terminating null + number of indicators * * ( indicator name + '("",(000,000))' + separator) */ size = 8; for (l = em->indicators; l; l = l->next) { ind = l->data; size += strlen(ind->name) + 15; } buf = g_try_malloc0(size); if (buf == NULL) goto fail; len = sprintf(buf, "+CIND: "); tmp = buf + len; for (l = em->indicators; l; l = l->next) { ind = l->data; len = sprintf(tmp, "%s(\"%s\",(%d%c%d))", l == em->indicators ? "" : ",", ind->name, ind->min, (ind->max - ind->min) == 1 ? ',' : '-', ind->max); tmp = tmp + len; } g_at_server_send_info(server, buf, TRUE); g_free(buf); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: fail: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void cmer_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; char buf[32]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_QUERY: sprintf(buf, "+CMER: %d,0,0,%d,0", em->events_mode, em->events_ind); g_at_server_send_info(em->server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: sprintf(buf, "+CMER: (0,3),(0),(0),(0,1),(0)"); g_at_server_send_info(em->server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SET: { GAtResultIter iter; int mode = em->events_mode; int ind = em->events_ind; int val; g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); /* mode */ if (!g_at_result_iter_next_number_default(&iter, mode, &mode)) goto fail; if (mode != 0 && mode != 3) goto fail; /* keyp */ if (!g_at_result_iter_next_number_default(&iter, 0, &val)) { if (!g_at_result_iter_skip_next(&iter)) goto done; goto fail; } if (val != 0) goto fail; /* disp */ if (!g_at_result_iter_next_number_default(&iter, 0, &val)) { if (!g_at_result_iter_skip_next(&iter)) goto done; goto fail; } if (val != 0) goto fail; /* ind */ if (!g_at_result_iter_next_number_default(&iter, ind, &ind)) { if (!g_at_result_iter_skip_next(&iter)) goto done; goto fail; } if (ind != 0 && ind != 1) goto fail; /* bfr */ if (!g_at_result_iter_next_number_default(&iter, 0, &val)) { if (!g_at_result_iter_skip_next(&iter)) goto done; goto fail; } if (val != 0) goto fail; /* check that bfr is last parameter */ if (g_at_result_iter_skip_next(&iter)) goto fail; done: em->events_mode = mode; em->events_ind = ind; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); __ofono_emulator_slc_condition(em, OFONO_EMULATOR_SLC_CONDITION_CMER); break; } default: fail: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void clip_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; GAtResultIter iter; int val; if (em->slc == FALSE) goto fail; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); if (!g_at_result_iter_next_number(&iter, &val)) goto fail; if (val != 0 && val != 1) goto fail; /* check this is last parameter */ if (g_at_result_iter_skip_next(&iter)) goto fail; em->clip = val; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: fail: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); }; } static void ccwa_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; GAtResultIter iter; int val; struct indicator *call_ind; struct indicator *cs_ind; if (em->slc == FALSE) goto fail; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); if (!g_at_result_iter_next_number(&iter, &val)) goto fail; if (val != 0 && val != 1) goto fail; /* check this is last parameter */ if (g_at_result_iter_skip_next(&iter)) goto fail; call_ind = find_indicator(em, OFONO_EMULATOR_IND_CALL, NULL); cs_ind = find_indicator(em, OFONO_EMULATOR_IND_CALLSETUP, NULL); if (cs_ind->value == OFONO_EMULATOR_CALLSETUP_INCOMING && call_ind->value == OFONO_EMULATOR_CALL_ACTIVE && em->ccwa == FALSE && val == 1) em->callsetup_source = g_timeout_add_seconds(0, notify_ccwa, em); em->ccwa = val; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: fail: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); }; } static void cmee_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; GAtResultIter iter; int val; char buf[16]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &val) == FALSE) goto fail; if (val != 0 && val != 1) goto fail; em->cmee_mode = val; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: sprintf(buf, "+CMEE: %d", em->cmee_mode); g_at_server_send_info(em->server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: /* HFP only support 0 and 1 */ sprintf(buf, "+CMEE: (0,1)"); g_at_server_send_info(em->server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: fail: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void bia_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: { GAtResultIter iter; GSList *l; int val; g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); /* check validity of the request */ while (g_at_result_iter_next_number_default(&iter, 0, &val)) if (val != 0 && val != 1) goto fail; /* Check that we have no non-numbers in the stream */ if (g_at_result_iter_skip_next(&iter) == TRUE) goto fail; /* request is valid, update the indicator activation status */ g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); for (l = em->indicators; l; l = l->next) { struct indicator *ind = l->data; if (g_at_result_iter_next_number_default(&iter, ind->active, &val) == FALSE) break; if (ind->mandatory == TRUE) continue; ind->active = val; } g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; } default: fail: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void bind_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; char buf[128]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_info(em->server, "+BIND: 1,1", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); __ofono_emulator_slc_condition(em, OFONO_EMULATOR_SLC_CONDITION_BIND); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: sprintf(buf, "+BIND: (1)"); g_at_server_send_info(em->server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SET: { GAtResultIter iter; int hf_indicator; int num_hf_indicators = 0; g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); /* check validity of the request */ while (num_hf_indicators < 20 && g_at_result_iter_next_number(&iter, &hf_indicator)) { if (hf_indicator > 0xffff) goto fail; num_hf_indicators += 1; } /* Check that we have nothing extra in the stream */ if (g_at_result_iter_skip_next(&iter) == TRUE) goto fail; /* request is valid, update the indicator activation status */ g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); while (g_at_result_iter_next_number(&iter, &hf_indicator)) ofono_info("HF supports indicator: 0x%04x", hf_indicator); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; } default: fail: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void biev_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: { GAtResultIter iter; int hf_indicator; int val; g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &hf_indicator) == FALSE) goto fail; if (hf_indicator != HFP_HF_INDICATOR_ENHANCED_SAFETY) goto fail; if (em->ddr_active == FALSE) goto fail; if (g_at_result_iter_next_number(&iter, &val) == FALSE) goto fail; if (val < 0 || val > 1) goto fail; ofono_info("Enhanced Safety indicator: %d", val); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; } default: fail: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void finish_codec_negotiation(struct ofono_emulator *em, int err) { if (em->codec_negotiation_cb == NULL) return; em->codec_negotiation_cb(err, em->codec_negotiation_data); em->codec_negotiation_cb = NULL; em->codec_negotiation_data = NULL; } static void bac_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; GAtResultIter iter; int val; DBG(""); switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); /* * CVSD codec is mandatory and must come first. * See HFP v1.6 4.34.1 */ if (g_at_result_iter_next_number(&iter, &val) == FALSE || val != HFP_CODEC_CVSD) goto fail; em->bac_received = TRUE; em->negotiated_codec = 0; em->r_codecs[CVSD_OFFSET].supported = TRUE; while (g_at_result_iter_next_number(&iter, &val)) { switch (val) { case HFP_CODEC_MSBC: em->r_codecs[MSBC_OFFSET].supported = TRUE; break; default: DBG("Unsupported HFP codec %d", val); break; } } g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); /* * If we're currently in the process of selecting a codec * we have to restart that now */ if (em->proposed_codec) { em->proposed_codec = 0; ofono_emulator_start_codec_negotiation(em, NULL, NULL); } break; default: fail: DBG("Process AT+BAC failed"); g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); finish_codec_negotiation(em, -EIO); break; } } static void connect_sco(struct ofono_emulator *em) { int err; DBG(""); if (em->card == NULL) { finish_codec_negotiation(em, -EINVAL); return; } err = ofono_handsfree_card_connect_sco(em->card); if (err == 0) { finish_codec_negotiation(em, 0); return; } /* If we have another codec we can try then lets do that */ if (em->negotiated_codec != HFP_CODEC_CVSD) { em->selected_codec = HFP_CODEC_CVSD; ofono_emulator_start_codec_negotiation(em, em->codec_negotiation_cb, em->codec_negotiation_data); return; } finish_codec_negotiation(em, -EIO); } static void bcs_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; GAtResultIter iter; int val; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, ""); if (!g_at_result_iter_next_number(&iter, &val)) break; if (em->proposed_codec != val) { em->proposed_codec = 0; break; } em->proposed_codec = 0; em->negotiated_codec = val; DBG("negotiated codec %d", val); if (em->card != NULL) ofono_handsfree_card_set_codec(em->card, em->negotiated_codec); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); connect_sco(em); return; default: break; } finish_codec_negotiation(em, -EIO); g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); } static void bcc_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { struct ofono_emulator *em = user_data; switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); if (!em->negotiated_codec) { ofono_emulator_start_codec_negotiation(em, NULL, NULL); return; } connect_sco(em); return; default: break; } g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); } static void emulator_add_indicator(struct ofono_emulator *em, const char* name, int min, int max, int dflt, gboolean mandatory) { struct indicator *ind; ind = g_try_new0(struct indicator, 1); if (ind == NULL) { ofono_error("Unable to allocate indicator structure"); return; } ind->name = g_strdup(name); ind->min = min; ind->max = max; ind->value = dflt; ind->active = TRUE; ind->mandatory = mandatory; em->indicators = g_slist_append(em->indicators, ind); } static void free_atom_callback(gpointer data) { struct atom_callback *atom_cb = data; if (atom_cb->destroy) atom_cb->destroy(atom_cb->data); g_free(atom_cb); } static void atom_cbs_destroy(gpointer key, gpointer value, gpointer user_data) { GSList *atom_cbs = value; g_slist_free_full(atom_cbs, free_atom_callback); } static void emulator_unregister(struct ofono_atom *atom) { struct ofono_emulator *em = __ofono_atom_get_data(atom); GSList *l; DBG("%p %p", em, atom); /* Do the clean up when this is the last remaining atom */ if (em->atoms != NULL && em->atoms->next != NULL) return; DBG("Last atom unregistering"); if (em->callsetup_source) { g_source_remove(em->callsetup_source); em->callsetup_source = 0; } for (l = em->indicators; l; l = l->next) { struct indicator *ind = l->data; g_free(ind->name); g_free(ind); } g_slist_free(em->indicators); em->indicators = NULL; g_at_ppp_unref(em->ppp); em->ppp = NULL; if (em->pns_id > 0) { __ofono_private_network_release(em->pns_id); em->pns_id = 0; } g_at_server_unref(em->server); em->server = NULL; g_hash_table_foreach(em->prefixes, atom_cbs_destroy, NULL); g_hash_table_destroy(em->prefixes); em->prefixes = NULL; ofono_handsfree_card_remove(em->card); em->card = NULL; } void ofono_emulator_register(struct ofono_emulator *em, int fd) { GIOChannel *io; GSList *l; DBG("%p, %d", em, fd); if (fd < 0) return; io = g_io_channel_unix_new(fd); em->server = g_at_server_new(io); if (em->server == NULL) return; g_io_channel_unref(io); g_at_server_set_debug(em->server, emulator_debug, "Server"); g_at_server_set_disconnect_function(em->server, emulator_disconnect, em); g_at_server_set_finish_callback(em->server, notify_deferred_indicators, em); if (em->type == OFONO_EMULATOR_TYPE_HFP) { em->ddr_active = true; emulator_add_indicator(em, OFONO_EMULATOR_IND_SERVICE, 0, 1, 0, FALSE); emulator_add_indicator(em, OFONO_EMULATOR_IND_CALL, 0, 1, 0, TRUE); emulator_add_indicator(em, OFONO_EMULATOR_IND_CALLSETUP, 0, 3, 0, TRUE); emulator_add_indicator(em, OFONO_EMULATOR_IND_CALLHELD, 0, 2, 0, TRUE); emulator_add_indicator(em, OFONO_EMULATOR_IND_SIGNAL, 0, 5, 0, FALSE); emulator_add_indicator(em, OFONO_EMULATOR_IND_ROAMING, 0, 1, 0, FALSE); emulator_add_indicator(em, OFONO_EMULATOR_IND_BATTERY, 0, 5, 5, FALSE); g_at_server_register(em->server, "+BRSF", brsf_cb, em, NULL); g_at_server_register(em->server, "+CIND", cind_cb, em, NULL); g_at_server_register(em->server, "+CMER", cmer_cb, em, NULL); g_at_server_register(em->server, "+CLIP", clip_cb, em, NULL); g_at_server_register(em->server, "+CCWA", ccwa_cb, em, NULL); g_at_server_register(em->server, "+CMEE", cmee_cb, em, NULL); g_at_server_register(em->server, "+BIA", bia_cb, em, NULL); g_at_server_register(em->server, "+BIND", bind_cb, em, NULL); g_at_server_register(em->server, "+BIEV", biev_cb, em, NULL); g_at_server_register(em->server, "+BAC", bac_cb, em, NULL); g_at_server_register(em->server, "+BCC", bcc_cb, em, NULL); g_at_server_register(em->server, "+BCS", bcs_cb, em, NULL); } for (l = em->atoms; l; l = l->next) { struct ofono_atom *atom = l->data; __ofono_atom_register(atom, emulator_unregister); } switch (em->type) { case OFONO_EMULATOR_TYPE_DUN: g_at_server_register(em->server, "D", dial_cb, em, NULL); g_at_server_register(em->server, "H", dun_ath_cb, em, NULL); g_at_server_register(em->server, "O", dun_ato_cb, em, NULL); break; case OFONO_EMULATOR_TYPE_HFP: g_at_server_set_echo(em->server, FALSE); break; default: break; } } static void emulator_remove(struct ofono_atom *atom) { struct ofono_emulator *em = __ofono_atom_get_data(atom); DBG("em: %p, atom: %p", em, atom); em->atoms = g_slist_remove(em->atoms, atom); if (em->atoms != NULL) return; DBG("Removing emulator"); g_free(em); } struct ofono_emulator *ofono_emulator_create(GList *modems, enum ofono_emulator_type type) { struct ofono_emulator *em; enum ofono_atom_type atom_t; GList *l; if (type == OFONO_EMULATOR_TYPE_DUN) atom_t = OFONO_ATOM_TYPE_EMULATOR_DUN; else if (type == OFONO_EMULATOR_TYPE_HFP) atom_t = OFONO_ATOM_TYPE_EMULATOR_HFP; else return NULL; em = g_try_new0(struct ofono_emulator, 1); if (em == NULL) return NULL; em->type = type; em->l_features |= HFP_AG_FEATURE_3WAY; em->l_features |= HFP_AG_FEATURE_REJECT_CALL; em->l_features |= HFP_AG_FEATURE_ENHANCED_CALL_STATUS; em->l_features |= HFP_AG_FEATURE_ENHANCED_CALL_CONTROL; em->l_features |= HFP_AG_FEATURE_EXTENDED_RES_CODE; em->l_features |= HFP_AG_FEATURE_HF_INDICATORS; em->l_features |= HFP_AG_FEATURE_CODEC_NEGOTIATION; em->events_mode = 3; /* default mode is forwarding events */ em->cmee_mode = 0; /* CME ERROR disabled by default */ em->prefixes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); for (l = modems; l; l = l->next) { struct ofono_atom *atom; struct ofono_modem *modem = l->data; DBG("modem: %p, type: %d", modem, type); atom = __ofono_modem_add_atom_offline(modem, atom_t, emulator_remove, em); em->atoms = g_slist_prepend(em->atoms, atom); } return em; } static void free_atom(gpointer data) { struct ofono_atom *atom = data; __ofono_atom_free(atom); } void ofono_emulator_remove(struct ofono_emulator *em) { /* * emulator_remove removes the atom from the list and eventually removes * the emulator too, so we need to make this copy for a safe removal. */ GSList *atoms = g_slist_copy(em->atoms); g_slist_free_full(atoms, free_atom); } void ofono_emulator_send_final(struct ofono_emulator *em, const struct ofono_error *final) { char buf[256]; /* * TODO: Handle various CMEE modes and report error strings from * common.c */ switch (final->type) { case OFONO_ERROR_TYPE_CMS: sprintf(buf, "+CMS ERROR: %d", final->error); g_at_server_send_ext_final(em->server, buf); break; case OFONO_ERROR_TYPE_CME: switch (em->cmee_mode) { case 1: sprintf(buf, "+CME ERROR: %d", final->error); break; case 2: sprintf(buf, "+CME ERROR: %s", telephony_error_to_str(final)); break; default: goto failure; } g_at_server_send_ext_final(em->server, buf); break; case OFONO_ERROR_TYPE_NO_ERROR: g_at_server_send_final(em->server, G_AT_SERVER_RESULT_OK); break; case OFONO_ERROR_TYPE_CEER: case OFONO_ERROR_TYPE_SIM: case OFONO_ERROR_TYPE_FAILURE: failure: g_at_server_send_final(em->server, G_AT_SERVER_RESULT_ERROR); break; }; } void ofono_emulator_send_unsolicited(struct ofono_emulator *em, const char *result) { g_at_server_send_unsolicited(em->server, result); } void ofono_emulator_send_intermediate(struct ofono_emulator *em, const char *result) { g_at_server_send_intermediate(em->server, result); } void ofono_emulator_send_info(struct ofono_emulator *em, const char *line, ofono_bool_t last) { g_at_server_send_info(em->server, line, last); } static struct ofono_atom *get_preferred_atom(struct ofono_emulator *em) { char *path; GSList *l; struct ofono_atom *preferred = em->atoms->data; path = __ofono_system_settings_get_string_value(PREFERRED_VOICE_MODEM); if (path == NULL) goto end; for (l = em->atoms; l; l = l->next) { struct ofono_atom *atom = l->data; if (g_strcmp0(__ofono_atom_get_path(atom), path) == 0) { preferred = atom; break; } } g_free(path); end: return preferred; } static struct ofono_atom *get_active_atom(struct ofono_emulator *em) { if (em->active_atom) return em->active_atom; return get_preferred_atom(em); } static struct atom_callback *find_atom_callback(GSList *prefix_cbs, const struct ofono_atom *atom) { GSList *l; for (l = prefix_cbs; l; l = prefix_cbs->next) { struct atom_callback *atom_cb = l->data; if (atom_cb->atom != atom) continue; return atom_cb; } return NULL; } struct handler { char *prefix; struct ofono_emulator *em; }; struct ofono_emulator_request { GAtResultIter iter; enum ofono_emulator_request_type type; }; static void handler_proxy(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer userdata) { GSList *prefix_cbs; struct atom_callback *atom_cb; struct handler *h = userdata; struct ofono_emulator_request req; struct ofono_atom *atom; atom = get_active_atom(h->em); prefix_cbs = g_hash_table_lookup(h->em->prefixes, h->prefix); atom_cb = find_atom_callback(prefix_cbs, atom); if (atom_cb == NULL) { ofono_error("%s: No atom for prefix %s", __func__, h->prefix); return; } switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: req.type = OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY; break; case G_AT_SERVER_REQUEST_TYPE_SET: req.type = OFONO_EMULATOR_REQUEST_TYPE_SET; break; case G_AT_SERVER_REQUEST_TYPE_QUERY: req.type = OFONO_EMULATOR_REQUEST_TYPE_QUERY; break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: req.type = OFONO_EMULATOR_REQUEST_TYPE_SUPPORT; } g_at_result_iter_init(&req.iter, result); g_at_result_iter_next(&req.iter, ""); atom_cb->cb(h->em, &req, atom_cb->data); } static void handler_proxy_need_slc(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer userdata) { struct handler *h = userdata; if (h->em->slc == FALSE) { g_at_server_send_final(h->em->server, G_AT_SERVER_RESULT_ERROR); return; } handler_proxy(server, type, result, userdata); } static void handler_proxy_chld(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer userdata) { struct handler *h = userdata; if (h->em->slc == FALSE && type != G_AT_SERVER_REQUEST_TYPE_SUPPORT) { g_at_server_send_final(h->em->server, G_AT_SERVER_RESULT_ERROR); return; } handler_proxy(server, type, result, userdata); } static void handler_destroy(gpointer userdata) { struct handler *h = userdata; g_free(h->prefix); g_free(h); } ofono_bool_t ofono_emulator_add_handler(struct ofono_atom *atom, const char *prefix, ofono_emulator_request_cb_t cb, void *data, ofono_destroy_func destroy) { struct handler *h; GSList *prefix_cbs; struct atom_callback *atom_cb; GAtServerNotifyFunc func = handler_proxy; struct ofono_emulator *em = __ofono_atom_get_data(atom); gboolean already_registered; DBG("%p %s cb %p", atom, prefix, cb); prefix_cbs = g_hash_table_lookup(em->prefixes, prefix); already_registered = prefix_cbs ? TRUE : FALSE; if (find_atom_callback(prefix_cbs, atom)) { ofono_info("%s: Atom %p already registered for prefix %s", __func__, atom, prefix); return FALSE; } atom_cb = g_malloc0(sizeof(*atom_cb)); atom_cb->atom = atom; atom_cb->cb = cb; atom_cb->data = data; atom_cb->destroy = destroy; /* * Append to avoid modifying the head, which is the value in the hash * table. We refresh it for the case of the first insertion though. */ prefix_cbs = g_slist_append(prefix_cbs, atom_cb); if (already_registered) return TRUE; g_hash_table_insert(em->prefixes, g_strdup(prefix), prefix_cbs); h = g_new0(struct handler, 1); h->prefix = g_strdup(prefix); h->em = em; if (em->type == OFONO_EMULATOR_TYPE_HFP) { func = handler_proxy_need_slc; if (!strcmp(prefix, "+CHLD")) func = handler_proxy_chld; } if (g_at_server_register(em->server, prefix, func, h, handler_destroy) == TRUE) return TRUE; g_free(h); return FALSE; } ofono_bool_t ofono_emulator_remove_handler(struct ofono_atom *atom, const char *prefix) { struct ofono_emulator *em = __ofono_atom_get_data(atom); GSList *prefix_cbs; struct atom_callback *atom_cb; prefix_cbs = g_hash_table_lookup(em->prefixes, prefix); atom_cb = find_atom_callback(prefix_cbs, atom); if (atom_cb == NULL) return FALSE; prefix_cbs = g_slist_remove(prefix_cbs, atom_cb); free_atom_callback(atom_cb); if (prefix_cbs != NULL) { g_hash_table_replace(em->prefixes, g_strdup(prefix), prefix_cbs); return TRUE; } g_hash_table_remove(em->prefixes, prefix); return g_at_server_unregister(em->server, prefix); } ofono_bool_t ofono_emulator_request_next_string( struct ofono_emulator_request *req, const char **str) { return g_at_result_iter_next_string(&req->iter, str); } ofono_bool_t ofono_emulator_request_next_number( struct ofono_emulator_request *req, int *number) { return g_at_result_iter_next_number(&req->iter, number); } const char *ofono_emulator_request_get_raw(struct ofono_emulator_request *req) { return g_at_result_iter_raw_line(&req->iter); } enum ofono_emulator_request_type ofono_emulator_request_get_type( struct ofono_emulator_request *req) { return req->type; } static gboolean valid_indication(struct ofono_emulator *em, struct ofono_atom *atom, const char *name) { struct ofono_atom *preferred; if (g_strcmp0(name, OFONO_EMULATOR_IND_BATTERY) == 0) return TRUE; if (em->active_atom) { if (em->active_atom == atom) return TRUE; else return FALSE; } else if (g_strcmp0(name, OFONO_EMULATOR_IND_CALL) == 0 || g_strcmp0(name, OFONO_EMULATOR_IND_CALLSETUP) == 0) { return TRUE; } /* Return FALSE if the modem is not the preferred one */ preferred = get_preferred_atom(em); if (preferred == atom) return TRUE; else return FALSE; } void ofono_emulator_set_indicator(struct ofono_atom *atom, const char *name, int value) { int i; char buf[20]; struct indicator *ind; struct indicator *call_ind; struct indicator *cs_ind; gboolean call; gboolean callsetup; gboolean waiting; struct ofono_emulator *em = __ofono_atom_get_data(atom); if (!valid_indication(em, atom, name)) return; DBG("%s\t%d", name, value); ind = find_indicator(em, name, &i); if (ind == NULL || ind->value == value || value < ind->min || value > ind->max) return; ind->value = value; call_ind = find_indicator(em, OFONO_EMULATOR_IND_CALL, NULL); cs_ind = find_indicator(em, OFONO_EMULATOR_IND_CALLSETUP, NULL); call = ind == call_ind; callsetup = ind == cs_ind; if (call || callsetup) { if (call_ind->value == OFONO_EMULATOR_CALL_INACTIVE && cs_ind->value == OFONO_EMULATOR_CALLSETUP_INACTIVE) { DBG("Call finished for HFP atom %p", atom); em->active_atom = NULL; } else if (em->active_atom == NULL) { DBG("New call from HFP atom %p", atom); em->active_atom = atom; } } /* * When callsetup indicator goes to Incoming and there is an active * call a +CCWA should be sent before +CIEV */ waiting = (callsetup && value == OFONO_EMULATOR_CALLSETUP_INCOMING && call_ind->value == OFONO_EMULATOR_CALL_ACTIVE); if (waiting) notify_ccwa(em); if (em->events_mode == 3 && em->events_ind && em->slc && ind->active) { if (!g_at_server_command_pending(em->server)) { sprintf(buf, "+CIEV: %d,%d", i, ind->value); g_at_server_send_unsolicited(em->server, buf); } else ind->deferred = TRUE; } /* * Ring timer should be started when: * - callsetup indicator is set to Incoming and there is no active call * (not a waiting call) * - or call indicator is set to inactive while callsetup is already * set to Incoming. * In those cases, a first RING should be sent just after the +CIEV * Ring timer should be stopped for all other values of callsetup */ if (waiting) return; /* Call state went from active/held + waiting -> incoming */ if (call && value == OFONO_EMULATOR_CALL_INACTIVE && cs_ind->value == OFONO_EMULATOR_CALLSETUP_INCOMING) goto start_ring; if (!callsetup) return; if (value != OFONO_EMULATOR_CALLSETUP_INCOMING) { if (em->callsetup_source > 0) { g_source_remove(em->callsetup_source); em->callsetup_source = 0; } return; } start_ring: notify_ring(em); em->callsetup_source = g_timeout_add_seconds(RING_TIMEOUT, notify_ring, em); } void __ofono_emulator_set_indicator_forced(struct ofono_atom *atom, const char *name, int value) { int i; struct indicator *ind; char buf[20]; struct ofono_emulator *em = __ofono_atom_get_data(atom); if (!valid_indication(em, atom, name)) return; ind = find_indicator(em, name, &i); if (ind == NULL || value < ind->min || value > ind->max) return; ind->value = value; if (em->events_mode == 3 && em->events_ind && em->slc && ind->active) { if (!g_at_server_command_pending(em->server)) { sprintf(buf, "+CIEV: %d,%d", i, ind->value); g_at_server_send_unsolicited(em->server, buf); } else ind->deferred = TRUE; } } void __ofono_emulator_slc_condition(struct ofono_emulator *em, enum ofono_emulator_slc_condition cond) { if (em->slc == TRUE) return; switch (cond) { case OFONO_EMULATOR_SLC_CONDITION_CMER: if ((em->r_features & HFP_HF_FEATURE_3WAY) && (em->l_features & HFP_AG_FEATURE_3WAY)) return; /* Fall Through */ case OFONO_EMULATOR_SLC_CONDITION_CHLD: if ((em->r_features & HFP_HF_FEATURE_HF_INDICATORS) && (em->l_features & HFP_HF_FEATURE_HF_INDICATORS)) return; /* Fall Through */ case OFONO_EMULATOR_SLC_CONDITION_BIND: ofono_info("SLC reached"); em->slc = TRUE; ofono_handsfree_card_register(em->card); default: break; } } void ofono_emulator_set_hf_indicator_active(struct ofono_emulator *em, int indicator, ofono_bool_t active) { char buf[64]; if (!(em->l_features & HFP_HF_FEATURE_HF_INDICATORS)) return; if (!(em->r_features & HFP_HF_FEATURE_HF_INDICATORS)) return; if (indicator != HFP_HF_INDICATOR_ENHANCED_SAFETY) return; em->ddr_active = active; sprintf(buf, "+BIND: %d,%d", HFP_HF_INDICATOR_ENHANCED_SAFETY, active); g_at_server_send_unsolicited(em->server, buf); } void ofono_emulator_set_handsfree_card(struct ofono_emulator *em, struct ofono_handsfree_card *card) { if (em == NULL) return; em->card = card; } static unsigned char select_codec(struct ofono_emulator *em) { if (ofono_handsfree_audio_has_wideband() && em->r_codecs[MSBC_OFFSET].supported) return HFP_CODEC_MSBC; /* CVSD is mandatory for both sides */ return HFP_CODEC_CVSD; } int ofono_emulator_start_codec_negotiation(struct ofono_emulator *em, ofono_emulator_codec_negotiation_cb cb, void *data) { char buf[64]; unsigned char codec; int err; if (em == NULL) return -EINVAL; if (cb != NULL && em->codec_negotiation_cb != NULL) return -EALREADY; if (em->proposed_codec > 0) return -EALREADY; if (!em->bac_received || em->negotiated_codec > 0) { /* * Report we're done even if we don't have done any * negotiation as the other side may have to clean up. */ cb(0, data); /* * If we didn't received any +BAC during the SLC setup the * remote side doesn't support codec negotiation and we can * directly connect our card. Otherwise if we got +BAC and * already have a negotiated codec we can proceed here * without doing any negotiation again. */ err = ofono_handsfree_card_connect_sco(em->card); if (err < 0) { ofono_error("SCO connection failed"); return err; } return 0; } if (em->selected_codec > 0) { codec = em->selected_codec; em->selected_codec = 0; goto done; } codec = select_codec(em); if (!codec) { DBG("Failed to select HFP codec"); return -EINVAL; } done: em->proposed_codec = codec; em->codec_negotiation_cb = cb; em->codec_negotiation_data = data; snprintf(buf, 64, "+BCS: %d", em->proposed_codec); g_at_server_send_unsolicited(em->server, buf); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/src/sim.c0000644000015600001650000022704712671500024020136 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" #include "util.h" #include "smsutil.h" #include "simutil.h" #include "storage.h" #include "simfs.h" #include "stkutil.h" #define SIM_FLAG_READING_SPN 0x1 struct ofono_sim { int flags; /* Contents of the SIM file system, in rough initialization order */ char *iccid; char **language_prefs; unsigned char *efli; unsigned char efli_length; gboolean language_prefs_update; enum ofono_sim_password_type pin_type; gboolean locked_pins[OFONO_SIM_PASSWORD_SIM_PUK]; /* Number of PINs */ int pin_retries[OFONO_SIM_PASSWORD_INVALID]; enum ofono_sim_phase phase; unsigned char mnc_length; enum ofono_sim_cphs_phase cphs_phase; unsigned char cphs_service_table[2]; unsigned char *efust; unsigned char efust_length; unsigned char *efest; unsigned char efest_length; unsigned char *efsst; unsigned char efsst_length; gboolean fixed_dialing; gboolean barred_dialing; char *imsi; char mcc[OFONO_MAX_MCC_LENGTH + 1]; char mnc[OFONO_MAX_MNC_LENGTH + 1]; GSList *own_numbers; GSList *new_numbers; unsigned char efmsisdn_length; unsigned char efmsisdn_records; GSList *service_numbers; gboolean sdn_ready; unsigned char *efimg; unsigned short efimg_length; enum ofono_sim_state state; struct ofono_watchlist *state_watches; char *spn; char *spn_dc; struct ofono_watchlist *spn_watches; unsigned int ef_spn_watch; unsigned int cphs_spn_watch; unsigned int cphs_spn_short_watch; struct sim_fs *simfs; struct ofono_sim_context *context; struct ofono_sim_context *early_context; unsigned char *iidf_image; unsigned int *iidf_watch_ids; DBusMessage *pending; const struct ofono_sim_driver *driver; void *driver_data; struct ofono_atom *atom; unsigned int hfp_watch; }; struct msisdn_set_request { struct ofono_sim *sim; int pending; int failed; DBusMessage *msg; }; struct service_number { char *id; struct ofono_phone_number ph; }; static const char *const passwd_name[] = { [OFONO_SIM_PASSWORD_NONE] = "none", [OFONO_SIM_PASSWORD_SIM_PIN] = "pin", [OFONO_SIM_PASSWORD_SIM_PUK] = "puk", [OFONO_SIM_PASSWORD_PHSIM_PIN] = "phone", [OFONO_SIM_PASSWORD_PHFSIM_PIN] = "firstphone", [OFONO_SIM_PASSWORD_PHFSIM_PUK] = "firstphonepuk", [OFONO_SIM_PASSWORD_SIM_PIN2] = "pin2", [OFONO_SIM_PASSWORD_SIM_PUK2] = "puk2", [OFONO_SIM_PASSWORD_PHNET_PIN] = "network", [OFONO_SIM_PASSWORD_PHNET_PUK] = "networkpuk", [OFONO_SIM_PASSWORD_PHNETSUB_PIN] = "netsub", [OFONO_SIM_PASSWORD_PHNETSUB_PUK] = "netsubpuk", [OFONO_SIM_PASSWORD_PHSP_PIN] = "service", [OFONO_SIM_PASSWORD_PHSP_PUK] = "servicepuk", [OFONO_SIM_PASSWORD_PHCORP_PIN] = "corp", [OFONO_SIM_PASSWORD_PHCORP_PUK] = "corppuk", }; static void sim_own_numbers_update(struct ofono_sim *sim); static GSList *g_drivers = NULL; static const char *sim_passwd_name(enum ofono_sim_password_type type) { return passwd_name[type]; } static enum ofono_sim_password_type sim_string_to_passwd(const char *name) { int len = sizeof(passwd_name) / sizeof(*passwd_name); int i; for (i = 0; i < len; i++) if (!strcmp(passwd_name[i], name)) return i; return OFONO_SIM_PASSWORD_INVALID; } static gboolean password_is_pin(enum ofono_sim_password_type type) { switch (type) { case OFONO_SIM_PASSWORD_SIM_PIN: case OFONO_SIM_PASSWORD_PHSIM_PIN: case OFONO_SIM_PASSWORD_PHFSIM_PIN: case OFONO_SIM_PASSWORD_SIM_PIN2: case OFONO_SIM_PASSWORD_PHNET_PIN: case OFONO_SIM_PASSWORD_PHNETSUB_PIN: case OFONO_SIM_PASSWORD_PHSP_PIN: case OFONO_SIM_PASSWORD_PHCORP_PIN: return TRUE; case OFONO_SIM_PASSWORD_SIM_PUK: case OFONO_SIM_PASSWORD_PHFSIM_PUK: case OFONO_SIM_PASSWORD_SIM_PUK2: case OFONO_SIM_PASSWORD_PHNET_PUK: case OFONO_SIM_PASSWORD_PHNETSUB_PUK: case OFONO_SIM_PASSWORD_PHSP_PUK: case OFONO_SIM_PASSWORD_PHCORP_PUK: case OFONO_SIM_PASSWORD_INVALID: case OFONO_SIM_PASSWORD_NONE: return FALSE; } return FALSE; } static enum ofono_sim_password_type puk2pin(enum ofono_sim_password_type type) { switch (type) { case OFONO_SIM_PASSWORD_SIM_PUK: return OFONO_SIM_PASSWORD_SIM_PIN; case OFONO_SIM_PASSWORD_PHFSIM_PUK: return OFONO_SIM_PASSWORD_PHFSIM_PIN; case OFONO_SIM_PASSWORD_SIM_PUK2: return OFONO_SIM_PASSWORD_SIM_PIN2; case OFONO_SIM_PASSWORD_PHNET_PUK: return OFONO_SIM_PASSWORD_PHNET_PIN; case OFONO_SIM_PASSWORD_PHNETSUB_PUK: return OFONO_SIM_PASSWORD_PHNETSUB_PIN; case OFONO_SIM_PASSWORD_PHSP_PUK: return OFONO_SIM_PASSWORD_PHSP_PIN; case OFONO_SIM_PASSWORD_PHCORP_PUK: return OFONO_SIM_PASSWORD_PHCORP_PIN; default: return OFONO_SIM_PASSWORD_INVALID; } } static char **get_own_numbers(GSList *own_numbers) { int nelem = 0; GSList *l; struct ofono_phone_number *num; char **ret; if (own_numbers) nelem = g_slist_length(own_numbers); ret = g_new0(char *, nelem + 1); nelem = 0; for (l = own_numbers; l; l = l->next) { num = l->data; ret[nelem++] = g_strdup(phone_number_to_string(num)); } return ret; } static char **get_locked_pins(struct ofono_sim *sim) { int i; int nelem = 0; char **ret; for (i = 1; i < OFONO_SIM_PASSWORD_SIM_PUK; i++) { if (sim->locked_pins[i] == FALSE) continue; nelem += 1; } ret = g_new0(char *, nelem + 1); nelem = 0; for (i = 1; i < OFONO_SIM_PASSWORD_SIM_PUK; i++) { if (sim->locked_pins[i] == FALSE) continue; ret[nelem] = g_strdup(sim_passwd_name(i)); nelem += 1; } return ret; } static void get_pin_retries(struct ofono_sim *sim, void ***out_dict, unsigned char **out_retries) { int i, nelem; void **dict; unsigned char *retries; for (i = 1, nelem = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) { if (sim->pin_retries[i] == -1) continue; nelem += 1; } dict = g_new0(void *, nelem * 2 + 1); retries = g_new0(unsigned char, nelem); for (i = 1, nelem = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) { if (sim->pin_retries[i] == -1) continue; retries[nelem] = sim->pin_retries[i]; dict[nelem * 2] = (void *) sim_passwd_name(i); dict[nelem * 2 + 1] = &retries[nelem]; nelem += 1; } *out_dict = dict; *out_retries = retries; } static char **get_service_numbers(GSList *service_numbers) { int nelem; GSList *l; struct service_number *num; char **ret; nelem = g_slist_length(service_numbers) * 2; ret = g_new0(char *, nelem + 1); nelem = 0; for (l = service_numbers; l; l = l->next) { num = l->data; ret[nelem++] = g_strdup(num->id); ret[nelem++] = g_strdup(phone_number_to_string(&num->ph)); } return ret; } static void service_number_free(struct service_number *num) { g_free(num->id); g_free(num); } static void call_state_watches(struct ofono_sim *sim) { GSList *l; ofono_sim_state_event_cb_t notify; for (l = sim->state_watches->items; l; l = l->next) { struct ofono_watchlist_item *item = l->data; notify = item->notify; notify(sim->state, item->notify_data); } } static DBusMessage *sim_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sim *sim = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; char **own_numbers; char **service_numbers; char **locked_pins; const char *pin_name; void **pin_retries_dict; unsigned char *dbus_retries; dbus_bool_t present = sim->state != OFONO_SIM_STATE_NOT_PRESENT; dbus_bool_t fdn; dbus_bool_t bdn; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "Present", DBUS_TYPE_BOOLEAN, &present); if (!present) goto done; if (sim->iccid) ofono_dbus_dict_append(&dict, "CardIdentifier", DBUS_TYPE_STRING, &sim->iccid); if (sim->imsi) ofono_dbus_dict_append(&dict, "SubscriberIdentity", DBUS_TYPE_STRING, &sim->imsi); fdn = sim->fixed_dialing; ofono_dbus_dict_append(&dict, "FixedDialing", DBUS_TYPE_BOOLEAN, &fdn); bdn = sim->barred_dialing; ofono_dbus_dict_append(&dict, "BarredDialing", DBUS_TYPE_BOOLEAN, &bdn); if (sim->mcc[0] != '\0' && sim->mnc[0] != '\0') { const char *str; str = sim->mcc; ofono_dbus_dict_append(&dict, "MobileCountryCode", DBUS_TYPE_STRING, &str); str = sim->mnc; ofono_dbus_dict_append(&dict, "MobileNetworkCode", DBUS_TYPE_STRING, &str); } own_numbers = get_own_numbers(sim->own_numbers); ofono_dbus_dict_append_array(&dict, "SubscriberNumbers", DBUS_TYPE_STRING, &own_numbers); g_strfreev(own_numbers); locked_pins = get_locked_pins(sim); ofono_dbus_dict_append_array(&dict, "LockedPins", DBUS_TYPE_STRING, &locked_pins); g_strfreev(locked_pins); if (sim->service_numbers && sim->sdn_ready) { service_numbers = get_service_numbers(sim->service_numbers); ofono_dbus_dict_append_dict(&dict, "ServiceNumbers", DBUS_TYPE_STRING, &service_numbers); g_strfreev(service_numbers); } if (sim->language_prefs) ofono_dbus_dict_append_array(&dict, "PreferredLanguages", DBUS_TYPE_STRING, &sim->language_prefs); pin_name = sim_passwd_name(sim->pin_type); ofono_dbus_dict_append(&dict, "PinRequired", DBUS_TYPE_STRING, (void *) &pin_name); get_pin_retries(sim, &pin_retries_dict, &dbus_retries); ofono_dbus_dict_append_dict(&dict, "Retries", DBUS_TYPE_BYTE, &pin_retries_dict); g_free(pin_retries_dict); g_free(dbus_retries); done: dbus_message_iter_close_container(&iter, &dict); return reply; } static void sim_pin_retries_query_cb(const struct ofono_error *error, int retries[OFONO_SIM_PASSWORD_INVALID], void *data) { struct ofono_sim *sim = data; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sim->atom); void **pin_retries_dict; unsigned char *dbus_retries; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Querying remaining pin retries failed"); return; } if (!memcmp(retries, sim->pin_retries, sizeof(sim->pin_retries))) return; memcpy(sim->pin_retries, retries, sizeof(sim->pin_retries)); get_pin_retries(sim, &pin_retries_dict, &dbus_retries); ofono_dbus_signal_dict_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "Retries", DBUS_TYPE_BYTE, &pin_retries_dict); g_free(pin_retries_dict); g_free(dbus_retries); } static void sim_pin_retries_check(struct ofono_sim *sim) { if (sim->driver->query_pin_retries == NULL) return; sim->driver->query_pin_retries(sim, sim_pin_retries_query_cb, sim); } static void msisdn_set_done(struct msisdn_set_request *req) { DBusMessage *reply; if (req->failed) reply = __ofono_error_failed(req->msg); else reply = dbus_message_new_method_return(req->msg); __ofono_dbus_pending_reply(&req->msg, reply); /* Re-read the numbers and emit signal if needed */ sim_own_numbers_update(req->sim); g_free(req); } static void msisdn_set_cb(int ok, void *data) { struct msisdn_set_request *req = data; if (!ok) req->failed++; req->pending--; if (!req->pending) msisdn_set_done(req); } static gboolean set_own_numbers(struct ofono_sim *sim, GSList *new_numbers, DBusMessage *msg) { struct msisdn_set_request *req; int record; unsigned char efmsisdn[255]; struct ofono_phone_number *number; if (new_numbers && g_slist_length(new_numbers) > sim->efmsisdn_records) return FALSE; req = g_new0(struct msisdn_set_request, 1); req->sim = sim; req->msg = dbus_message_ref(msg); for (record = 1; record <= sim->efmsisdn_records; record++) { if (new_numbers) { number = new_numbers->data; sim_adn_build(efmsisdn, sim->efmsisdn_length, number, NULL); new_numbers = new_numbers->next; } else { memset(efmsisdn, 0xff, sim->efmsisdn_length); /* Set number length */ efmsisdn[sim->efmsisdn_length - 14] = 1; } if (ofono_sim_write(req->sim->context, SIM_EFMSISDN_FILEID, msisdn_set_cb, OFONO_SIM_FILE_STRUCTURE_FIXED, record, efmsisdn, sim->efmsisdn_length, req) == 0) req->pending++; else req->failed++; } if (!req->pending) msisdn_set_done(req); return TRUE; } static DBusMessage *sim_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sim *sim = data; DBusMessageIter iter; DBusMessageIter var; DBusMessageIter var_elem; const char *name, *value; if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &name); if (!strcmp(name, "SubscriberNumbers")) { gboolean set_ok = FALSE; struct ofono_phone_number *own; GSList *own_numbers = NULL; if (sim->efmsisdn_length == 0) return __ofono_error_busy(msg); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&var, &var_elem); /* Empty lists are supported */ while (dbus_message_iter_get_arg_type(&var_elem) != DBUS_TYPE_INVALID) { if (dbus_message_iter_get_arg_type(&var_elem) != DBUS_TYPE_STRING) goto error; dbus_message_iter_get_basic(&var_elem, &value); if (!valid_phone_number_format(value)) goto error; own = g_new0(struct ofono_phone_number, 1); string_to_phone_number(value, own); own_numbers = g_slist_prepend(own_numbers, own); dbus_message_iter_next(&var_elem); } own_numbers = g_slist_reverse(own_numbers); set_ok = set_own_numbers(sim, own_numbers, msg); error: g_slist_foreach(own_numbers, (GFunc) g_free, 0); g_slist_free(own_numbers); if (set_ok) return NULL; } return __ofono_error_invalid_args(msg); } static void sim_locked_cb(struct ofono_sim *sim, gboolean locked) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sim->atom); const char *typestr; const char *pin; char **locked_pins; enum ofono_sim_password_type type; DBusMessage *reply; reply = dbus_message_new_method_return(sim->pending); dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_STRING, &typestr, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID); type = sim_string_to_passwd(typestr); /* This is used by lock/unlock pin, no puks allowed */ sim->locked_pins[type] = locked; __ofono_dbus_pending_reply(&sim->pending, reply); locked_pins = get_locked_pins(sim); ofono_dbus_signal_array_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "LockedPins", DBUS_TYPE_STRING, &locked_pins); g_strfreev(locked_pins); sim_pin_retries_check(sim); } static void sim_unlock_cb(const struct ofono_error *error, void *data) { struct ofono_sim *sim = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBusMessage *reply = __ofono_error_failed(sim->pending); __ofono_dbus_pending_reply(&sim->pending, reply); __ofono_sim_recheck_pin(sim); return; } sim_locked_cb(sim, FALSE); } static void sim_lock_cb(const struct ofono_error *error, void *data) { struct ofono_sim *sim = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBusMessage *reply = __ofono_error_failed(sim->pending); __ofono_dbus_pending_reply(&sim->pending, reply); __ofono_sim_recheck_pin(sim); return; } sim_locked_cb(sim, TRUE); } static DBusMessage *sim_lock_or_unlock(struct ofono_sim *sim, int lock, DBusConnection *conn, DBusMessage *msg) { enum ofono_sim_password_type type; const char *typestr; const char *pin; if (sim->driver->lock == NULL) return __ofono_error_not_implemented(msg); if (sim->pending) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); type = sim_string_to_passwd(typestr); /* * SIM PIN2 cannot be locked / unlocked according to 27.007, * however the PIN combination can be changed */ if (password_is_pin(type) == FALSE || type == OFONO_SIM_PASSWORD_SIM_PIN2) return __ofono_error_invalid_format(msg); if (!__ofono_is_valid_sim_pin(pin, type)) return __ofono_error_invalid_format(msg); sim->pending = dbus_message_ref(msg); sim->driver->lock(sim, type, lock, pin, lock ? sim_lock_cb : sim_unlock_cb, sim); return NULL; } static DBusMessage *sim_lock_pin(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sim *sim = data; return sim_lock_or_unlock(sim, 1, conn, msg); } static DBusMessage *sim_unlock_pin(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sim *sim = data; return sim_lock_or_unlock(sim, 0, conn, msg); } static void sim_change_pin_cb(const struct ofono_error *error, void *data) { struct ofono_sim *sim = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { __ofono_dbus_pending_reply(&sim->pending, __ofono_error_failed(sim->pending)); __ofono_sim_recheck_pin(sim); return; } __ofono_dbus_pending_reply(&sim->pending, dbus_message_new_method_return(sim->pending)); sim_pin_retries_check(sim); } static DBusMessage *sim_change_pin(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sim *sim = data; enum ofono_sim_password_type type; const char *typestr; const char *old; const char *new; if (sim->driver->change_passwd == NULL) return __ofono_error_not_implemented(msg); if (sim->pending) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); type = sim_string_to_passwd(typestr); if (password_is_pin(type) == FALSE) return __ofono_error_invalid_format(msg); if (!__ofono_is_valid_sim_pin(old, type)) return __ofono_error_invalid_format(msg); if (!__ofono_is_valid_sim_pin(new, type)) return __ofono_error_invalid_format(msg); if (!strcmp(new, old)) return dbus_message_new_method_return(msg); sim->pending = dbus_message_ref(msg); sim->driver->change_passwd(sim, type, old, new, sim_change_pin_cb, sim); return NULL; } static void sim_enter_pin_cb(const struct ofono_error *error, void *data) { struct ofono_sim *sim = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) reply = __ofono_error_failed(sim->pending); else reply = dbus_message_new_method_return(sim->pending); __ofono_dbus_pending_reply(&sim->pending, reply); __ofono_sim_recheck_pin(sim); } static DBusMessage *sim_enter_pin(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sim *sim = data; const char *typestr; enum ofono_sim_password_type type; const char *pin; if (sim->driver->send_passwd == NULL) return __ofono_error_not_implemented(msg); if (sim->pending) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); type = sim_string_to_passwd(typestr); if (type == OFONO_SIM_PASSWORD_NONE || type != sim->pin_type) return __ofono_error_invalid_format(msg); if (password_is_pin(type) == FALSE) return __ofono_error_invalid_format(msg); if (!__ofono_is_valid_sim_pin(pin, type)) return __ofono_error_invalid_format(msg); sim->pending = dbus_message_ref(msg); sim->driver->send_passwd(sim, pin, sim_enter_pin_cb, sim); return NULL; } static void sim_get_image_cb(struct ofono_sim *sim, unsigned char id, char *xpm, gboolean cache) { DBusMessage *reply; DBusMessageIter iter, array; int xpm_len; if (xpm == NULL) { reply = __ofono_error_failed(sim->pending); __ofono_dbus_pending_reply(&sim->pending, reply); return; } xpm_len = strlen(xpm); reply = dbus_message_new_method_return(sim->pending); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &xpm, xpm_len); dbus_message_iter_close_container(&iter, &array); __ofono_dbus_pending_reply(&sim->pending, reply); if (cache) sim_fs_cache_image(sim->simfs, (const char *) xpm, id); g_free(xpm); } static void sim_iidf_read_clut_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; unsigned char id; unsigned char *efimg; unsigned short iidf_len; unsigned short clut_len; char *xpm; DBG("ok: %d", ok); dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_BYTE, &id, DBUS_TYPE_INVALID); id -= 1; efimg = &sim->efimg[id * 9]; if (!ok) { sim_get_image_cb(sim, id, NULL, FALSE); goto done; } iidf_len = efimg[7] << 8 | efimg[8]; if (sim->iidf_image[3] == 0) clut_len = 256 * 3; else clut_len = sim->iidf_image[3] * 3; xpm = stk_image_to_xpm(sim->iidf_image, iidf_len, efimg[2], data, clut_len); sim_get_image_cb(sim, id, xpm, TRUE); done: g_free(sim->iidf_image); sim->iidf_image = NULL; } static void sim_iidf_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; unsigned char id; unsigned char *efimg; unsigned short iidf_id; unsigned short offset; unsigned short clut_len; unsigned char path[6]; unsigned int path_len; DBG("ok: %d", ok); dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_BYTE, &id, DBUS_TYPE_INVALID); id -= 1; efimg = &sim->efimg[id * 9]; if (!ok) { sim_get_image_cb(sim, id, NULL, FALSE); return; } if (efimg[2] == STK_IMG_SCHEME_BASIC) { char *xpm = stk_image_to_xpm(data, length, efimg[2], NULL, 0); sim_get_image_cb(sim, id, xpm, TRUE); return; } offset = data[4] << 8 | data[5]; if (data[3] == 0) clut_len = 256 * 3; else clut_len = data[3] * 3; iidf_id = efimg[3] << 8 | efimg[4]; sim->iidf_image = g_memdup(data, length); /* The path it the same between 2G and 3G */ path_len = sim_ef_db_get_path_3g(SIM_EFIMG_FILEID, path); /* read the clut data */ ofono_sim_read_bytes(sim->context, iidf_id, offset, clut_len, path, path_len, sim_iidf_read_clut_cb, sim); } static void sim_image_data_changed(int id, void *userdata) { /* TODO: notify D-bus clients */ } static void sim_get_image(struct ofono_sim *sim, unsigned char id, gpointer user_data) { unsigned char *efimg; char *image; unsigned short iidf_id; unsigned short iidf_offset; unsigned short iidf_len; if (sim->efimg_length <= id * 9) { sim_get_image_cb(sim, id, NULL, FALSE); return; } image = sim_fs_get_cached_image(sim->simfs, id); if (image != NULL) sim_get_image_cb(sim, id, image, FALSE); efimg = &sim->efimg[id * 9]; iidf_id = efimg[3] << 8 | efimg[4]; iidf_offset = efimg[5] << 8 | efimg[6]; iidf_len = efimg[7] << 8 | efimg[8]; /* read the image data */ if (image == NULL) { unsigned char path[6]; unsigned int path_len; /* The path it the same between 2G and 3G */ path_len = sim_ef_db_get_path_3g(SIM_EFIMG_FILEID, path); ofono_sim_read_bytes(sim->context, iidf_id, iidf_offset, iidf_len, path, path_len, sim_iidf_read_cb, sim); } if (sim->iidf_watch_ids[id] > 0) return; sim->iidf_watch_ids[id] = ofono_sim_add_file_watch(sim->context, iidf_id, sim_image_data_changed, sim, NULL); } static DBusMessage *sim_get_icon(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sim *sim = data; unsigned char id; if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &id, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); /* zero means no icon */ if (id == 0) return __ofono_error_invalid_args(msg); if (sim->pending) return __ofono_error_busy(msg); if (sim->efimg == NULL) return __ofono_error_not_implemented(msg); sim->pending = dbus_message_ref(msg); sim_get_image(sim, id - 1, sim); return NULL; } static DBusMessage *sim_reset_pin(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_sim *sim = data; const char *typestr; enum ofono_sim_password_type type; const char *puk; const char *pin; if (sim->driver->reset_passwd == NULL) return __ofono_error_not_implemented(msg); if (sim->pending) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr, DBUS_TYPE_STRING, &puk, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); type = sim_string_to_passwd(typestr); if (type == OFONO_SIM_PASSWORD_NONE || type != sim->pin_type) return __ofono_error_invalid_format(msg); if (!__ofono_is_valid_sim_pin(puk, type)) return __ofono_error_invalid_format(msg); type = puk2pin(type); if (!__ofono_is_valid_sim_pin(pin, type)) return __ofono_error_invalid_format(msg); sim->pending = dbus_message_ref(msg); sim->driver->reset_passwd(sim, puk, pin, sim_enter_pin_cb, sim); return NULL; } static const GDBusMethodTable sim_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), sim_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, sim_set_property) }, { GDBUS_ASYNC_METHOD("ChangePin", GDBUS_ARGS({ "type", "s" }, { "oldpin", "s" }, { "newpin", "s" }), NULL, sim_change_pin) }, { GDBUS_ASYNC_METHOD("EnterPin", GDBUS_ARGS({ "type", "s" }, { "pin", "s" }), NULL, sim_enter_pin) }, { GDBUS_ASYNC_METHOD("ResetPin", GDBUS_ARGS({ "type", "s" }, { "puk", "s" }, { "newpin", "s" }), NULL, sim_reset_pin) }, { GDBUS_ASYNC_METHOD("LockPin", GDBUS_ARGS({ "type", "s" }, { "pin", "s" }), NULL, sim_lock_pin) }, { GDBUS_ASYNC_METHOD("UnlockPin", GDBUS_ARGS({ "type", "s" }, { "pin", "s" }), NULL, sim_unlock_pin) }, { GDBUS_ASYNC_METHOD("GetIcon", GDBUS_ARGS({ "id", "y" }), GDBUS_ARGS({ "icon", "ay" }), sim_get_icon) }, { } }; static const GDBusSignalTable sim_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static gboolean numbers_list_equal(GSList *a, GSList *b) { struct ofono_phone_number *num_a, *num_b; while (a || b) { if (a == NULL || b == NULL) return FALSE; num_a = a->data; num_b = b->data; if (!g_str_equal(num_a->number, num_b->number) || num_a->type != num_b->type) return FALSE; a = a->next; b = b->next; } return TRUE; } static void sim_msisdn_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; int total; struct ofono_phone_number ph; if (!ok) goto check; if (record_length < 14 || length < record_length) { ofono_error("EFmsidn shall at least contain 14 bytes"); return; } total = length / record_length; sim->efmsisdn_length = record_length; sim->efmsisdn_records = total; if (sim_adn_parse(data, record_length, &ph, NULL) == TRUE) { struct ofono_phone_number *own; own = g_new(struct ofono_phone_number, 1); memcpy(own, &ph, sizeof(struct ofono_phone_number)); sim->new_numbers = g_slist_prepend(sim->new_numbers, own); } if (record != total) return; check: /* All records retrieved */ if (sim->new_numbers) sim->new_numbers = g_slist_reverse(sim->new_numbers); if (!numbers_list_equal(sim->new_numbers, sim->own_numbers)) { const char *path = __ofono_atom_get_path(sim->atom); char **own_numbers; DBusConnection *conn = ofono_dbus_get_connection(); g_slist_foreach(sim->own_numbers, (GFunc) g_free, NULL); g_slist_free(sim->own_numbers); sim->own_numbers = sim->new_numbers; own_numbers = get_own_numbers(sim->own_numbers); ofono_dbus_signal_array_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "SubscriberNumbers", DBUS_TYPE_STRING, &own_numbers); g_strfreev(own_numbers); } else { g_slist_foreach(sim->new_numbers, (GFunc) g_free, NULL); g_slist_free(sim->new_numbers); } sim->new_numbers = NULL; } static gint service_number_compare(gconstpointer a, gconstpointer b) { const struct service_number *sdn = a; const char *id = b; return strcmp(sdn->id, id); } static void sim_sdn_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sim->atom); int total; struct ofono_phone_number ph; char *alpha; struct service_number *sdn; if (!ok) goto check; if (record_length < 14 || length < record_length) return; total = length / record_length; if (sim_adn_parse(data, record_length, &ph, &alpha) == FALSE) goto out; /* Use phone number if Id is unavailable */ if (alpha && alpha[0] == '\0') { g_free(alpha); alpha = NULL; } if (alpha == NULL) alpha = g_strdup(phone_number_to_string(&ph)); if (sim->service_numbers && g_slist_find_custom(sim->service_numbers, alpha, service_number_compare)) { ofono_error("Duplicate EFsdn entries for `%s'", alpha); g_free(alpha); goto out; } sdn = g_new(struct service_number, 1); sdn->id = alpha; memcpy(&sdn->ph, &ph, sizeof(struct ofono_phone_number)); sim->service_numbers = g_slist_prepend(sim->service_numbers, sdn); out: if (record != total) return; check: /* All records retrieved */ if (sim->service_numbers) { sim->service_numbers = g_slist_reverse(sim->service_numbers); sim->sdn_ready = TRUE; } if (sim->sdn_ready) { char **service_numbers; service_numbers = get_service_numbers(sim->service_numbers); ofono_dbus_signal_dict_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "ServiceNumbers", DBUS_TYPE_STRING, &service_numbers); g_strfreev(service_numbers); } } static void sim_service_numbers_changed(int id, void *userdata) { struct ofono_sim *sim = userdata; if (sim->service_numbers) { g_slist_foreach(sim->service_numbers, (GFunc)service_number_free, NULL); g_slist_free(sim->service_numbers); sim->service_numbers = NULL; } ofono_sim_read(sim->context, SIM_EFSDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, sim_sdn_read_cb, sim); } static void sim_own_numbers_update(struct ofono_sim *sim) { ofono_sim_read(sim->context, SIM_EFMSISDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, sim_msisdn_read_cb, sim); } static void sim_own_numbers_changed(int id, void *userdata) { struct ofono_sim *sim = userdata; sim_own_numbers_update(sim); } static void sim_efimg_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; unsigned char *efimg; int num_records; if (!ok) return; num_records = length / record_length; /* * EFimg descriptors are 9 bytes long. * Byte 1 of the record is the number of descriptors per record. */ if ((record_length < 10) || ((record_length % 9 != 2) && (record_length % 9 != 1))) return; if (sim->efimg == NULL) { sim->efimg = g_try_malloc0(num_records * 9); if (sim->efimg == NULL) return; sim->iidf_watch_ids = g_try_new0(unsigned int, num_records); if (sim->iidf_watch_ids == NULL) { g_free(sim->efimg); sim->efimg = NULL; return; } sim->efimg_length = num_records * 9; } /* * TBD - if we have more than one descriptor per record, * pick the nicest one. For now we use the first one. */ /* copy descriptor into slot for this record */ efimg = &sim->efimg[(record - 1) * 9]; memcpy(efimg, &data[1], 9); } static void sim_efimg_changed(int id, void *userdata) { struct ofono_sim *sim = userdata; int i, watch; if (sim->efimg != NULL) { for (i = sim->efimg_length / 9 - 1; i >= 0; i--) { watch = sim->iidf_watch_ids[i]; if (watch == 0) continue; ofono_sim_remove_file_watch(sim->context, watch); } g_free(sim->efimg); sim->efimg = NULL; sim->efimg_length = 0; g_free(sim->iidf_watch_ids); sim->iidf_watch_ids = NULL; } ofono_sim_read(sim->context, SIM_EFIMG_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, sim_efimg_read_cb, sim); /* TODO: notify D-bus clients */ } static void sim_ready(enum ofono_sim_state new_state, void *user) { struct ofono_sim *sim = user; if (new_state != OFONO_SIM_STATE_READY) return; sim_own_numbers_update(sim); ofono_sim_add_file_watch(sim->context, SIM_EFMSISDN_FILEID, sim_own_numbers_changed, sim, NULL); ofono_sim_read(sim->context, SIM_EFSDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, sim_sdn_read_cb, sim); ofono_sim_add_file_watch(sim->context, SIM_EFSDN_FILEID, sim_service_numbers_changed, sim, NULL); ofono_sim_read(sim->context, SIM_EFIMG_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, sim_efimg_read_cb, sim); ofono_sim_add_file_watch(sim->context, SIM_EFIMG_FILEID, sim_efimg_changed, sim, NULL); } static void sim_set_ready(struct ofono_sim *sim) { if (sim == NULL) return; if (sim->state != OFONO_SIM_STATE_INSERTED && sim->state != OFONO_SIM_STATE_LOCKED_OUT) return; sim->state = OFONO_SIM_STATE_READY; sim_fs_check_version(sim->simfs); call_state_watches(sim); } static void sim_imsi_obtained(struct ofono_sim *sim, const char *imsi) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sim->atom); sim->imsi = g_strdup(imsi); ofono_dbus_signal_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "SubscriberIdentity", DBUS_TYPE_STRING, &sim->imsi); /* * sim->mnc_length = 0 means that EFad was not present or that EFad did * not contain the MNC length field (MNC length is not mandatory for * SIMs (non-USIM) - see TS 51.011). * * MNC can have either 2 or 3 digits depending on the MCC: we will try * to find a correspondence in an MCC-MNC length database */ if (sim->mnc_length == 0) { int mnc_aux = __ofono_sim_mnclength_get_mnclength(sim->imsi); if (mnc_aux > 0) sim->mnc_length = mnc_aux; } if (sim->mnc_length) { const char *str; strncpy(sim->mcc, sim->imsi, OFONO_MAX_MCC_LENGTH); sim->mcc[OFONO_MAX_MCC_LENGTH] = '\0'; strncpy(sim->mnc, sim->imsi + OFONO_MAX_MCC_LENGTH, sim->mnc_length); sim->mnc[sim->mnc_length] = '\0'; str = sim->mcc; ofono_dbus_signal_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "MobileCountryCode", DBUS_TYPE_STRING, &str); str = sim->mnc; ofono_dbus_signal_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "MobileNetworkCode", DBUS_TYPE_STRING, &str); } sim_set_ready(sim); } static void sim_imsi_cb(const struct ofono_error *error, const char *imsi, void *data) { struct ofono_sim *sim = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Unable to read IMSI, emergency calls only"); return; } sim_imsi_obtained(sim, imsi); } static void sim_efimsi_cb(const struct ofono_error *error, const unsigned char *data, int len, void *user) { struct ofono_sim *sim = user; char imsi[17]; /* IMSI max length is 15 + 1 for NULL + 1 waste */ unsigned char imsi_len; unsigned char parity; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) goto error; if (len != 9) goto error; imsi_len = data[0]; if (imsi_len == 0 || imsi_len > 8) goto error; /* The low 3 bits of the first byte should be set to binary 001 */ if ((data[1] & 0x7) != 0x1) goto error; /* Save off the parity bit */ parity = (data[1] >> 3) & 1; extract_bcd_number(data + 1, imsi_len, imsi); imsi[16] = '\0'; if ((strlen(imsi + 1) % 2) != parity) goto error; sim_imsi_obtained(sim, imsi + 1); return; error: ofono_error("Unable to read IMSI, emergency calls only"); } static void sim_retrieve_imsi(struct ofono_sim *sim) { if (sim->driver->read_imsi) { sim->driver->read_imsi(sim, sim_imsi_cb, sim); return; } if (sim->driver->read_file_transparent == NULL) { ofono_error("IMSI retrieval not implemented," " only emergency calls will be available"); return; } sim->driver->read_file_transparent(sim, SIM_EFIMSI_FILEID, 0, 9, NULL, 0, sim_efimsi_cb, sim); } static void sim_fdn_enabled(struct ofono_sim *sim) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sim->atom); dbus_bool_t val; sim->fixed_dialing = TRUE; val = sim->fixed_dialing; ofono_dbus_signal_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "FixedDialing", DBUS_TYPE_BOOLEAN, &val); } static void sim_bdn_enabled(struct ofono_sim *sim) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sim->atom); dbus_bool_t val; sim->barred_dialing = TRUE; val = sim->barred_dialing; ofono_dbus_signal_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "BarredDialing", DBUS_TYPE_BOOLEAN, &val); } static void sim_efbdn_info_read_cb(int ok, unsigned char file_status, int total_length, int record_length, void *userdata) { struct ofono_sim *sim = userdata; if (!ok) goto out; if (file_status & SIM_FILE_STATUS_VALID) sim_bdn_enabled(sim); out: if (sim->fixed_dialing != TRUE && sim->barred_dialing != TRUE) sim_retrieve_imsi(sim); } static gboolean check_bdn_status(struct ofono_sim *sim) { /* * Check the status of Barred Dialing in the SIM-card * (TS 11.11/TS 51.011, Section 11.5.1: BDN capability request). * If BDN is allocated, activated in EFsst and EFbdn is validated, * halt the SIM initialization. */ if (sim_sst_is_active(sim->efsst, sim->efsst_length, SIM_SST_SERVICE_BDN)) { sim_fs_read_info(sim->context, SIM_EFBDN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, NULL, 0, sim_efbdn_info_read_cb, sim); return TRUE; } return FALSE; } static void sim_efadn_info_read_cb(int ok, unsigned char file_status, int total_length, int record_length, void *userdata) { struct ofono_sim *sim = userdata; if (!ok) goto out; if (!(file_status & SIM_FILE_STATUS_VALID)) sim_fdn_enabled(sim); out: if (check_bdn_status(sim) != TRUE) { if (sim->fixed_dialing != TRUE && sim->barred_dialing != TRUE) sim_retrieve_imsi(sim); } } static void sim_efsst_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; if (!ok) goto out; if (length < 2) { ofono_error("EFsst shall contain at least two bytes"); goto out; } sim->efsst = g_memdup(data, length); sim->efsst_length = length; /* * Check if Fixed Dialing is enabled in the SIM-card * (TS 11.11/TS 51.011, Section 11.5.1: FDN capability request). * If FDN is activated and ADN is invalidated, * don't continue initialization routine. */ if (sim_sst_is_active(sim->efsst, sim->efsst_length, SIM_SST_SERVICE_FDN)) { sim_fs_read_info(sim->context, SIM_EFADN_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, NULL, 0, sim_efadn_info_read_cb, sim); return; } if (check_bdn_status(sim) == TRUE) return; out: sim_retrieve_imsi(sim); } static void sim_efest_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; gboolean available; if (!ok) goto out; if (length < 1) { ofono_error("EFest shall contain at least one byte"); goto out; } sim->efest = g_memdup(data, length); sim->efest_length = length; /* * Check if Fixed Dialing is enabled in the USIM-card * (TS 31.102, Section 5.3.2: FDN capability request). * If FDN is activated, don't continue initialization routine. */ available = sim_ust_is_available(sim->efust, sim->efust_length, SIM_UST_SERVICE_FDN); if (available && sim_est_is_active(sim->efest, sim->efest_length, SIM_EST_SERVICE_FDN)) sim_fdn_enabled(sim); /* * Check the status of Barred Dialing in the USIM-card * (TS 31.102, Section 5.3.2: BDN capability request). * If BDN service is enabled, halt the USIM initialization. */ available = sim_ust_is_available(sim->efust, sim->efust_length, SIM_UST_SERVICE_BDN); if (available && sim_est_is_active(sim->efest, sim->efest_length, SIM_EST_SERVICE_BDN)) sim_bdn_enabled(sim); out: if (sim->fixed_dialing != TRUE && sim->barred_dialing != TRUE) sim_retrieve_imsi(sim); } static void sim_efust_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; if (!ok) goto out; if (length < 1) { ofono_error("EFust shall contain at least one byte"); goto out; } sim->efust = g_memdup(data, length); sim->efust_length = length; /* * Check whether the SIM provides EFest file * According to 3GPP TS 31.102 section 4.2.47, EFest file * shall be present if FDN or BDN or EST is available * Lets be paranoid and check for the special cases as well * where EST is not available(FDN or BDN available), but EFest * is present */ if (sim_ust_is_available(sim->efust, sim->efust_length, SIM_UST_SERVICE_ENABLED_SERVICE_TABLE) || sim_ust_is_available(sim->efust, sim->efust_length, SIM_UST_SERVICE_FDN) || sim_ust_is_available(sim->efust, sim->efust_length, SIM_UST_SERVICE_BDN)) { ofono_sim_read(sim->context, SIM_EFEST_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_efest_read_cb, sim); return; } out: sim_retrieve_imsi(sim); } static void sim_cphs_information_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; sim->cphs_phase = OFONO_SIM_CPHS_PHASE_NONE; if (!ok || length < 3) return; if (data[0] == 0x01) sim->cphs_phase = OFONO_SIM_CPHS_PHASE_1G; else if (data[0] >= 0x02) sim->cphs_phase = OFONO_SIM_CPHS_PHASE_2G; memcpy(sim->cphs_service_table, data + 1, 2); } static void sim_ad_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; int new_mnc_length; if (!ok) return; if (length < 3) { ofono_error("EFad should contain at least three bytes"); return; } if (length < 4) { ofono_info("EFad does not contain MNC length"); return; } new_mnc_length = data[3] & 0xf; /* sanity check for potential invalid values */ if (new_mnc_length < 2 || new_mnc_length > 3) return; sim->mnc_length = new_mnc_length; } static void sim_efphase_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; if (!ok || length != 1) { sim->phase = OFONO_SIM_PHASE_3G; ofono_sim_read(sim->context, SIM_EFUST_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_efust_read_cb, sim); return; } switch (data[0]) { case 0: sim->phase = OFONO_SIM_PHASE_1G; break; case 2: sim->phase = OFONO_SIM_PHASE_2G; break; case 3: sim->phase = OFONO_SIM_PHASE_2G_PLUS; break; default: ofono_error("Unknown phase"); return; } ofono_sim_read(sim->context, SIM_EFSST_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_efsst_read_cb, sim); } static void sim_initialize_after_pin(struct ofono_sim *sim) { sim->context = ofono_sim_context_create(sim); sim->spn_watches = __ofono_watchlist_new(g_free); ofono_sim_read(sim->context, SIM_EFPHASE_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_efphase_read_cb, sim); ofono_sim_read(sim->context, SIM_EFAD_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_ad_read_cb, sim); /* * Read CPHS-support bits, this is still part of the SIM * initialisation but no order is specified for it. */ ofono_sim_read(sim->context, SIM_EF_CPHS_INFORMATION_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_cphs_information_read_cb, sim); } static void sim_efli_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; if (!ok) return; sim->efli = g_memdup(data, length); sim->efli_length = length; } /* Detect whether the file is in EFli format, as opposed to 51.011 EFlp */ static gboolean sim_efli_format(const unsigned char *ef, int length) { int i; if (length & 1) return FALSE; for (i = 0; i < length; i += 2) { if (ef[i] == 0xff && ef[i+1] == 0xff) continue; /* * ISO 639 country codes are each two lower-case SMS 7-bit * characters while CB DCS language codes are in ranges * (0 - 15) or (32 - 47), so the ranges don't overlap */ if (g_ascii_isalpha(ef[i]) == 0) return FALSE; if (g_ascii_isalpha(ef[i+1]) == 0) return FALSE; } return TRUE; } static GSList *parse_language_list(const unsigned char *ef, int length) { int i; GSList *ret = NULL; for (i = 0; i < length; i += 2) { if (ef[i] > 0x7f || ef[i+1] > 0x7f) continue; /* * ISO 639 codes contain only characters that are coded * identically in SMS 7 bit charset, ASCII or UTF8 so * no conversion. */ ret = g_slist_prepend(ret, g_ascii_strdown((char *)ef + i, 2)); } if (ret) ret = g_slist_reverse(ret); return ret; } static GSList *parse_eflp(const unsigned char *eflp, int length) { int i; char code[3]; GSList *ret = NULL; for (i = 0; i < length; i++) { if (iso639_2_from_language(eflp[i], code) == FALSE) continue; ret = g_slist_prepend(ret, g_strdup(code)); } if (ret) ret = g_slist_reverse(ret); return ret; } static char **concat_lang_prefs(GSList *a, GSList *b) { GSList *l, *k; char **ret; int i = 0; int total = g_slist_length(a) + g_slist_length(b); if (total == 0) return NULL; ret = g_new0(char *, total + 1); for (l = a; l; l = l->next) ret[i++] = g_strdup(l->data); for (l = b; l; l = l->next) { gboolean duplicate = FALSE; for (k = a; k; k = k->next) if (!strcmp(k->data, l->data)) duplicate = TRUE; if (duplicate) continue; ret[i++] = g_strdup(l->data); } return ret; } static void sim_efpl_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; const char *path = __ofono_atom_get_path(sim->atom); DBusConnection *conn = ofono_dbus_get_connection(); gboolean efli_format = TRUE; GSList *efli = NULL; GSList *efpl = NULL; if (!ok || length < 2) goto skip_efpl; efpl = parse_language_list(data, length); skip_efpl: if (sim->efli && sim->efli_length > 0) { efli_format = sim_efli_format(sim->efli, sim->efli_length); if (efli_format) efli = parse_language_list(sim->efli, sim->efli_length); else efli = parse_eflp(sim->efli, sim->efli_length); } /* * If efli_format is TRUE, make a list of languages in both files in * order of preference following TS 31.102. * Quoting 31.102 Section 5.1.1.2: * The preferred language selection shall always use the EFLI in * preference to the EFPL at the MF unless: * - if the EFLI has the value 'FFFF' in its highest priority position, * then the preferred language selection shall be the language * preference in the EFPL at the MF level * Otherwise in order of preference according to TS 51.011 */ if (efli_format) { if (sim->efli_length >= 2 && sim->efli[0] == 0xff && sim->efli[1] == 0xff) sim->language_prefs = concat_lang_prefs(NULL, efpl); else sim->language_prefs = concat_lang_prefs(efli, efpl); } else { sim->language_prefs = concat_lang_prefs(efpl, efli); } if (sim->efli) { g_free(sim->efli); sim->efli = NULL; sim->efli_length = 0; } if (efli) { g_slist_foreach(efli, (GFunc)g_free, NULL); g_slist_free(efli); } if (efpl) { g_slist_foreach(efpl, (GFunc)g_free, NULL); g_slist_free(efpl); } if (sim->language_prefs != NULL) ofono_dbus_signal_array_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "PreferredLanguages", DBUS_TYPE_STRING, &sim->language_prefs); /* Proceed with sim initialization if we're not merely updating */ if (!sim->language_prefs_update) __ofono_sim_recheck_pin(sim); sim->language_prefs_update = FALSE; } static void sim_iccid_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_sim *sim = userdata; const char *path = __ofono_atom_get_path(sim->atom); DBusConnection *conn = ofono_dbus_get_connection(); char iccid[21]; /* ICCID max length is 20 + 1 for NULL */ if (!ok || length < 10) return; extract_bcd_number(data, length, iccid); iccid[20] = '\0'; sim->iccid = g_strdup(iccid); ofono_dbus_signal_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "CardIdentifier", DBUS_TYPE_STRING, &sim->iccid); } static void sim_iccid_changed(int id, void *userdata) { struct ofono_sim *sim = userdata; if (sim->iccid) { g_free(sim->iccid); sim->iccid = NULL; } ofono_sim_read(sim->early_context, SIM_EF_ICCID_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_iccid_read_cb, sim); } static void sim_efli_efpl_changed(int id, void *userdata) { struct ofono_sim *sim = userdata; if (sim->efli != NULL) /* This shouldn't happen */ return; if (sim->language_prefs) { g_strfreev(sim->language_prefs); sim->language_prefs = NULL; } sim->language_prefs_update = TRUE; ofono_sim_read(sim->early_context, SIM_EFLI_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_efli_read_cb, sim); ofono_sim_read(sim->early_context, SIM_EFPL_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_efpl_read_cb, sim); } static void sim_initialize(struct ofono_sim *sim) { /* * Perform SIM initialization according to 3GPP 31.102 Section 5.1.1.2 * The assumption here is that if sim manager is being initialized, * then sim commands are implemented, and the sim manager is then * responsible for checking the PIN, reading the IMSI and signaling * SIM ready condition. * * The procedure according to 31.102, 51.011, 11.11 and CPHS 4.2 is * roughly: * * Read EFecc * Read EFli and EFpl * SIM Pin check * Request SIM phase (only in 51.011) * Administrative information request (read EFad) * Request CPHS Information (only in CPHS 4.2) * Read EFsst (only in 11.11 & 51.011) * Read EFust (only in 31.102) * Read EFest (only in 31.102) * Read IMSI * * At this point we signal the SIM ready condition and allow * arbitrary files to be written or read, assuming their presence * in the EFust */ if (sim->early_context == NULL) sim->early_context = ofono_sim_context_create(sim); /* Grab the EFiccid which is always available */ ofono_sim_read(sim->early_context, SIM_EF_ICCID_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_iccid_read_cb, sim); ofono_sim_add_file_watch(sim->early_context, SIM_EF_ICCID_FILEID, sim_iccid_changed, sim, NULL); /* EFecc is read by the voicecall atom */ /* * According to 31.102 the EFli is read first and EFpl is then * only read if none of the EFli languages are supported by user * interface. 51.011 mandates the exact opposite, making EFpl/EFelp * preferred over EFlp (same EFid as EFli, different format). * However we don't depend on the user interface and so * need to read both files now. */ ofono_sim_read(sim->early_context, SIM_EFLI_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_efli_read_cb, sim); ofono_sim_add_file_watch(sim->early_context, SIM_EFLI_FILEID, sim_efli_efpl_changed, sim, NULL); ofono_sim_read(sim->early_context, SIM_EFPL_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_efpl_read_cb, sim); ofono_sim_add_file_watch(sim->early_context, SIM_EFPL_FILEID, sim_efli_efpl_changed, sim, NULL); } struct ofono_sim_context *ofono_sim_context_create(struct ofono_sim *sim) { if (sim == NULL || sim->simfs == NULL) return NULL; return sim_fs_context_new(sim->simfs); } void ofono_sim_context_free(struct ofono_sim_context *context) { return sim_fs_context_free(context); } int ofono_sim_read_bytes(struct ofono_sim_context *context, int id, unsigned short offset, unsigned short num_bytes, const unsigned char *path, unsigned int len, ofono_sim_file_read_cb_t cb, void *data) { if (num_bytes == 0) return -1; return sim_fs_read(context, id, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, offset, num_bytes, path, len, cb, data); } int ofono_sim_read(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, ofono_sim_file_read_cb_t cb, void *data) { return sim_fs_read(context, id, expected_type, 0, 0, NULL, 0, cb, data); } int ofono_sim_read_path(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, const unsigned char *path, unsigned int path_len, ofono_sim_file_read_cb_t cb, void *data) { return sim_fs_read(context, id, expected_type, 0, 0, path, path_len, cb, data); } int ofono_sim_read_info(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, const unsigned char *path, unsigned int pth_len, ofono_sim_read_info_cb_t cb, void *data) { return sim_fs_read_info(context, id, expected_type, path, pth_len, cb, data); } int ofono_sim_read_record(struct ofono_sim_context *context, int id, enum ofono_sim_file_structure expected_type, int record, int record_length, const unsigned char *path, unsigned int pth_len, ofono_sim_file_read_cb_t cb, void *data) { return sim_fs_read_record(context, id, expected_type, record, record_length, path, pth_len, cb, data); } int ofono_sim_write(struct ofono_sim_context *context, int id, ofono_sim_file_write_cb_t cb, enum ofono_sim_file_structure structure, int record, const unsigned char *data, int length, void *userdata) { return sim_fs_write(context, id, cb, structure, record, data, length, userdata); } unsigned int ofono_sim_add_file_watch(struct ofono_sim_context *context, int id, ofono_sim_file_changed_cb_t cb, void *userdata, ofono_destroy_func destroy) { return sim_fs_file_watch_add(context, id, cb, userdata, destroy); } void ofono_sim_remove_file_watch(struct ofono_sim_context *context, unsigned int id) { sim_fs_file_watch_remove(context, id); } const char *ofono_sim_get_imsi(struct ofono_sim *sim) { if (sim == NULL) return NULL; return sim->imsi; } const char *ofono_sim_get_mcc(struct ofono_sim *sim) { if (sim == NULL) return NULL; return sim->mcc; } const char *ofono_sim_get_mnc(struct ofono_sim *sim) { if (sim == NULL) return NULL; return sim->mnc; } const char *ofono_sim_get_spn(struct ofono_sim *sim) { if (sim == NULL) return NULL; return sim->spn; } enum ofono_sim_phase ofono_sim_get_phase(struct ofono_sim *sim) { if (sim == NULL) return OFONO_SIM_PHASE_UNKNOWN; return sim->phase; } enum ofono_sim_cphs_phase ofono_sim_get_cphs_phase(struct ofono_sim *sim) { if (sim == NULL) return OFONO_SIM_CPHS_PHASE_NONE; return sim->cphs_phase; } enum ofono_sim_password_type ofono_sim_get_password_type(struct ofono_sim *sim) { if (sim == NULL) return OFONO_SIM_PASSWORD_NONE; return sim->pin_type; } const unsigned char *ofono_sim_get_cphs_service_table(struct ofono_sim *sim) { if (sim == NULL) return NULL; return sim->cphs_service_table; } ofono_bool_t __ofono_sim_service_available(struct ofono_sim *sim, int ust_service, int sst_service) { if (sim->efust) return sim_ust_is_available(sim->efust, sim->efust_length, ust_service); if (sim->efsst) return sim_sst_is_active(sim->efsst, sim->efsst_length, sst_service); return FALSE; } ofono_bool_t __ofono_sim_cphs_service_available(struct ofono_sim *sim, int cphs_service) { return sim_cphs_is_active(sim->cphs_service_table, cphs_service); } static void sim_inserted_update(struct ofono_sim *sim) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sim->atom); dbus_bool_t present = sim->state != OFONO_SIM_STATE_NOT_PRESENT; ofono_dbus_signal_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "Present", DBUS_TYPE_BOOLEAN, &present); } static void sim_free_early_state(struct ofono_sim *sim) { if (sim->iccid) { g_free(sim->iccid); sim->iccid = NULL; } if (sim->efli) { g_free(sim->efli); sim->efli = NULL; sim->efli_length = 0; } if (sim->language_prefs) { g_strfreev(sim->language_prefs); sim->language_prefs = NULL; } if (sim->early_context) { ofono_sim_context_free(sim->early_context); sim->early_context = NULL; } } static void sim_spn_close(struct ofono_sim *sim) { if (sim->spn_watches) { __ofono_watchlist_free(sim->spn_watches); sim->spn_watches = NULL; } /* * We have not initialized SPN logic at all yet, either because * no netreg / gprs atom has been needed or we have not reached the * post_sim state */ if (sim->ef_spn_watch == 0) return; ofono_sim_remove_file_watch(sim->context, sim->ef_spn_watch); sim->ef_spn_watch = 0; ofono_sim_remove_file_watch(sim->context, sim->cphs_spn_watch); sim->cphs_spn_watch = 0; if (sim->cphs_spn_short_watch) { ofono_sim_remove_file_watch(sim->context, sim->cphs_spn_short_watch); sim->cphs_spn_short_watch = 0; } sim->flags &= ~SIM_FLAG_READING_SPN; g_free(sim->spn); sim->spn = NULL; g_free(sim->spn_dc); sim->spn_dc = NULL; } static void sim_free_main_state(struct ofono_sim *sim) { int i; for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) sim->pin_retries[i] = -1; memset(sim->locked_pins, 0, sizeof(sim->locked_pins)); if (sim->imsi) { g_free(sim->imsi); sim->imsi = NULL; } sim->mcc[0] = '\0'; sim->mnc[0] = '\0'; if (sim->own_numbers) { g_slist_foreach(sim->own_numbers, (GFunc)g_free, NULL); g_slist_free(sim->own_numbers); sim->own_numbers = NULL; } if (sim->service_numbers) { g_slist_foreach(sim->service_numbers, (GFunc)service_number_free, NULL); g_slist_free(sim->service_numbers); sim->service_numbers = NULL; sim->sdn_ready = FALSE; } if (sim->efust) { g_free(sim->efust); sim->efust = NULL; sim->efust_length = 0; } if (sim->efest) { g_free(sim->efest); sim->efest = NULL; sim->efest_length = 0; } if (sim->efsst) { g_free(sim->efsst); sim->efsst = NULL; sim->efsst_length = 0; } sim->phase = OFONO_SIM_PHASE_UNKNOWN; sim->cphs_phase = OFONO_SIM_CPHS_PHASE_NONE; sim->mnc_length = 0; memset(sim->cphs_service_table, 0, 2); if (sim->efimg) { g_free(sim->efimg); sim->efimg = NULL; sim->efimg_length = 0; g_free(sim->iidf_watch_ids); sim->iidf_watch_ids = NULL; } g_free(sim->iidf_image); sim->iidf_image = NULL; sim->fixed_dialing = FALSE; sim->barred_dialing = FALSE; sim_spn_close(sim); if (sim->context) { ofono_sim_context_free(sim->context); sim->context = NULL; } } static void sim_free_state(struct ofono_sim *sim) { sim_free_early_state(sim); sim_free_main_state(sim); } void ofono_sim_inserted_notify(struct ofono_sim *sim, ofono_bool_t inserted) { if (sim->state == OFONO_SIM_STATE_RESETTING && inserted) { /* * Start initialization procedure from after EFiccid, * EFli and EFpl are retrieved. */ sim->state = OFONO_SIM_STATE_INSERTED; __ofono_sim_recheck_pin(sim); return; } if (inserted == TRUE && sim->state == OFONO_SIM_STATE_NOT_PRESENT) sim->state = OFONO_SIM_STATE_INSERTED; else if (inserted == FALSE && sim->state != OFONO_SIM_STATE_NOT_PRESENT) sim->state = OFONO_SIM_STATE_NOT_PRESENT; else return; if (!__ofono_atom_get_registered(sim->atom)) return; sim_inserted_update(sim); call_state_watches(sim); if (inserted) { sim_initialize(sim); } else { sim->pin_type = OFONO_SIM_PASSWORD_NONE; sim_free_state(sim); } } unsigned int ofono_sim_add_state_watch(struct ofono_sim *sim, ofono_sim_state_event_cb_t notify, void *data, ofono_destroy_func destroy) { struct ofono_watchlist_item *item; DBG("%p", sim); if (sim == NULL) return 0; if (notify == NULL) return 0; item = g_new0(struct ofono_watchlist_item, 1); item->notify = notify; item->destroy = destroy; item->notify_data = data; return __ofono_watchlist_add_item(sim->state_watches, item); } void ofono_sim_remove_state_watch(struct ofono_sim *sim, unsigned int id) { __ofono_watchlist_remove_item(sim->state_watches, id); } enum ofono_sim_state ofono_sim_get_state(struct ofono_sim *sim) { if (sim == NULL) return OFONO_SIM_STATE_NOT_PRESENT; return sim->state; } static void spn_watch_cb(gpointer data, gpointer user_data) { struct ofono_watchlist_item *item = data; struct ofono_sim *sim = user_data; if (item->notify) ((ofono_sim_spn_cb_t) item->notify)(sim->spn, sim->spn_dc, item->notify_data); } static inline void spn_watches_notify(struct ofono_sim *sim) { if (sim->spn_watches->items) g_slist_foreach(sim->spn_watches->items, spn_watch_cb, sim); sim->flags &= ~SIM_FLAG_READING_SPN; } static void sim_spn_set(struct ofono_sim *sim, const void *data, int length, const unsigned char *dc) { g_free(sim->spn); sim->spn = NULL; g_free(sim->spn_dc); sim->spn_dc = NULL; if (data == NULL) goto notify; /* * TS 31.102 says: * * the string shall use: * * - either the SMS default 7-bit coded alphabet as defined in * TS 23.038 [5] with bit 8 set to 0. The string shall be left * justified. Unused bytes shall be set to 'FF'. * * - or one of the UCS2 code options defined in the annex of TS * 31.101 [11]. * * 31.101 has no such annex though. 51.101 refers to Annex B of * itself which is not there either. 11.11 contains the same * paragraph as 51.101 and has an Annex B which we implement. */ sim->spn = sim_string_to_utf8(data, length); if (sim->spn == NULL) { ofono_error("EFspn read successfully, but couldn't parse"); goto notify; } if (strlen(sim->spn) == 0) { g_free(sim->spn); sim->spn = NULL; goto notify; } if (dc) sim->spn_dc = g_memdup(dc, 1); notify: spn_watches_notify(sim); } static void sim_cphs_spn_short_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *user_data) { struct ofono_sim *sim = user_data; if (!ok) { sim_spn_set(sim, NULL, 0, NULL); return; } sim_spn_set(sim, data, length, NULL); } static void sim_cphs_spn_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *user_data) { struct ofono_sim *sim = user_data; if (!ok) { if (__ofono_sim_cphs_service_available(sim, SIM_CPHS_SERVICE_SHORT_SPN)) ofono_sim_read(sim->context, SIM_EF_CPHS_SPN_SHORT_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_cphs_spn_short_read_cb, sim); else sim_spn_set(sim, NULL, 0, NULL); return; } sim_spn_set(sim, data, length, NULL); } static void sim_spn_read_cb(int ok, int length, int record, const unsigned char *data, int record_length, void *user_data) { struct ofono_sim *sim = user_data; if (!ok) { ofono_sim_read(sim->context, SIM_EF_CPHS_SPN_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_cphs_spn_read_cb, sim); return; } sim_spn_set(sim, data + 1, length - 1, data); } static void sim_spn_changed(int id, void *userdata) { struct ofono_sim *sim = userdata; if (sim->flags & SIM_FLAG_READING_SPN) return; sim->flags |= SIM_FLAG_READING_SPN; ofono_sim_read(sim->context, SIM_EFSPN_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_spn_read_cb, sim); } static void sim_spn_init(struct ofono_sim *sim) { sim->ef_spn_watch = ofono_sim_add_file_watch(sim->context, SIM_EFSPN_FILEID, sim_spn_changed, sim, NULL); sim->cphs_spn_watch = ofono_sim_add_file_watch(sim->context, SIM_EF_CPHS_SPN_FILEID, sim_spn_changed, sim, NULL); if (__ofono_sim_cphs_service_available(sim, SIM_CPHS_SERVICE_SHORT_SPN)) sim->cphs_spn_short_watch = ofono_sim_add_file_watch( sim->context, SIM_EF_CPHS_SPN_SHORT_FILEID, sim_spn_changed, sim, NULL); } ofono_bool_t ofono_sim_add_spn_watch(struct ofono_sim *sim, unsigned int *id, ofono_sim_spn_cb_t cb, void *data, ofono_destroy_func destroy) { struct ofono_watchlist_item *item; unsigned int watch_id; DBG("%p", sim); if (sim == NULL) return 0; item = g_new0(struct ofono_watchlist_item, 1); item->notify = cb; item->destroy = destroy; item->notify_data = data; watch_id = __ofono_watchlist_add_item(sim->spn_watches, item); if (watch_id == 0) return FALSE; *id = watch_id; if (sim->ef_spn_watch == 0) { sim_spn_init(sim); sim_spn_changed(0, sim); return TRUE; } if (sim->flags & SIM_FLAG_READING_SPN) return TRUE; ((ofono_sim_spn_cb_t) item->notify)(sim->spn, sim->spn_dc, item->notify_data); return TRUE; } ofono_bool_t ofono_sim_remove_spn_watch(struct ofono_sim *sim, unsigned int *id) { gboolean ret; DBG("%p", sim); if (sim == NULL || sim->spn_watches == NULL) return FALSE; ret = __ofono_watchlist_remove_item(sim->spn_watches, *id); if (ret == TRUE) *id = 0; return ret; } static void sim_pin_query_cb(const struct ofono_error *error, enum ofono_sim_password_type pin_type, void *data) { struct ofono_sim *sim = data; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(sim->atom); const char *pin_name; char **locked_pins; gboolean lock_changed; DBG("sim->pin_type: %d, pin_type: %d", sim->pin_type, pin_type); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Querying PIN authentication state failed"); return; } if (sim->pin_type != pin_type) { sim->pin_type = pin_type; pin_name = sim_passwd_name(pin_type); if (pin_type != OFONO_SIM_PASSWORD_NONE && password_is_pin(pin_type) == FALSE) pin_type = puk2pin(pin_type); if (pin_type != OFONO_SIM_PASSWORD_INVALID && pin_type != OFONO_SIM_PASSWORD_NONE) { lock_changed = !sim->locked_pins[pin_type]; sim->locked_pins[pin_type] = TRUE; if (lock_changed) { locked_pins = get_locked_pins(sim); ofono_dbus_signal_array_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "LockedPins", DBUS_TYPE_STRING, &locked_pins); g_strfreev(locked_pins); } } ofono_dbus_signal_property_changed(conn, path, OFONO_SIM_MANAGER_INTERFACE, "PinRequired", DBUS_TYPE_STRING, &pin_name); } switch (pin_type) { case OFONO_SIM_PASSWORD_NONE: case OFONO_SIM_PASSWORD_SIM_PIN2: case OFONO_SIM_PASSWORD_SIM_PUK2: break; default: if (sim->state == OFONO_SIM_STATE_READY) { /* Force the sim state out of READY */ sim_free_main_state(sim); sim->state = OFONO_SIM_STATE_LOCKED_OUT; call_state_watches(sim); } break; } sim_pin_retries_check(sim); switch (pin_type) { case OFONO_SIM_PASSWORD_SIM_PIN2: case OFONO_SIM_PASSWORD_SIM_PUK2: case OFONO_SIM_PASSWORD_NONE: if (sim->state == OFONO_SIM_STATE_READY) break; /* Fall through */ sim_initialize_after_pin(sim); break; default: break; } } void __ofono_sim_recheck_pin(struct ofono_sim *sim) { if (sim->driver->query_passwd_state == NULL) { sim_initialize_after_pin(sim); return; } sim->driver->query_passwd_state(sim, sim_pin_query_cb, sim); } int ofono_sim_driver_register(const struct ofono_sim_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_sim_driver_unregister(const struct ofono_sim_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void emulator_remove_handler(struct ofono_atom *atom, void *data) { ofono_emulator_remove_handler(atom, data); } static void sim_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); struct ofono_sim *sim = __ofono_atom_get_data(atom); __ofono_modem_foreach_registered_atom(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_remove_handler, "+CNUM"); __ofono_modem_remove_atom_watch(modem, sim->hfp_watch); __ofono_watchlist_free(sim->state_watches); sim->state_watches = NULL; g_dbus_unregister_interface(conn, path, OFONO_SIM_MANAGER_INTERFACE); ofono_modem_remove_interface(modem, OFONO_SIM_MANAGER_INTERFACE); } static void sim_remove(struct ofono_atom *atom) { struct ofono_sim *sim = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (sim == NULL) return; if (sim->driver != NULL && sim->driver->remove != NULL) sim->driver->remove(sim); sim_free_state(sim); sim_fs_free(sim->simfs); sim->simfs = NULL; g_free(sim); } struct ofono_sim *ofono_sim_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_sim *sim; GSList *l; int i; if (driver == NULL) return NULL; sim = g_try_new0(struct ofono_sim, 1); if (sim == NULL) return NULL; sim->phase = OFONO_SIM_PHASE_UNKNOWN; sim->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_SIM, sim_remove, sim); for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) sim->pin_retries[i] = -1; for (l = g_drivers; l; l = l->next) { const struct ofono_sim_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(sim, vendor, data) < 0) continue; sim->driver = drv; break; } return sim; } static void emulator_cnum_cb(struct ofono_emulator *em, struct ofono_emulator_request *req, void *userdata) { struct ofono_sim *sim = userdata; struct ofono_error result; GSList *l; const char *phone; /* * '+CNUM: ,"+",,,4' + phone number + phone type on 3 digits max * + terminating null */ char buf[OFONO_MAX_PHONE_NUMBER_LENGTH + 18 + 1]; result.error = 0; switch (ofono_emulator_request_get_type(req)) { case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY: for (l = sim->own_numbers; l; l = l->next) { struct ofono_phone_number *ph = l->data; phone = phone_number_to_string(ph); sprintf(buf, "+CNUM: ,\"%s\",%d,,4", phone, ph->type); ofono_emulator_send_info(em, buf, l->next == NULL ? TRUE : FALSE); } result.type = OFONO_ERROR_TYPE_NO_ERROR; ofono_emulator_send_final(em, &result); break; default: result.type = OFONO_ERROR_TYPE_FAILURE; ofono_emulator_send_final(em, &result); }; } static void emulator_hfp_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) ofono_emulator_add_handler(atom, "+CNUM", emulator_cnum_cb, data, NULL); } void ofono_sim_register(struct ofono_sim *sim) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(sim->atom); const char *path = __ofono_atom_get_path(sim->atom); if (!g_dbus_register_interface(conn, path, OFONO_SIM_MANAGER_INTERFACE, sim_methods, sim_signals, NULL, sim, NULL)) { ofono_error("Could not create %s interface", OFONO_SIM_MANAGER_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_SIM_MANAGER_INTERFACE); sim->state_watches = __ofono_watchlist_new(g_free); sim->simfs = sim_fs_new(sim, sim->driver); __ofono_atom_register(sim->atom, sim_unregister); ofono_sim_add_state_watch(sim, sim_ready, sim, NULL); if (sim->state > OFONO_SIM_STATE_NOT_PRESENT) sim_initialize(sim); sim->hfp_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_EMULATOR_HFP, emulator_hfp_watch, sim, NULL); } void ofono_sim_remove(struct ofono_sim *sim) { __ofono_atom_free(sim->atom); } void ofono_sim_set_data(struct ofono_sim *sim, void *data) { sim->driver_data = data; } void *ofono_sim_get_data(struct ofono_sim *sim) { return sim->driver_data; } static ofono_bool_t is_valid_pin(const char *pin, unsigned int min, unsigned int max) { unsigned int i; /* Pin must not be empty */ if (pin == NULL || pin[0] == '\0') return FALSE; i = strlen(pin); if (i != strspn(pin, "0123456789")) return FALSE; if (min <= i && i <= max) return TRUE; return FALSE; } ofono_bool_t __ofono_is_valid_sim_pin(const char *pin, enum ofono_sim_password_type type) { switch (type) { case OFONO_SIM_PASSWORD_SIM_PIN: case OFONO_SIM_PASSWORD_SIM_PIN2: /* 11.11 Section 9.3 ("CHV"): 4..8 IA-5 digits */ return is_valid_pin(pin, 4, 8); break; case OFONO_SIM_PASSWORD_PHSIM_PIN: case OFONO_SIM_PASSWORD_PHFSIM_PIN: case OFONO_SIM_PASSWORD_PHNET_PIN: case OFONO_SIM_PASSWORD_PHNETSUB_PIN: case OFONO_SIM_PASSWORD_PHSP_PIN: case OFONO_SIM_PASSWORD_PHCORP_PIN: /* 22.022 Section 14 4..16 IA-5 digits */ return is_valid_pin(pin, 4, 16); break; case OFONO_SIM_PASSWORD_SIM_PUK: case OFONO_SIM_PASSWORD_SIM_PUK2: case OFONO_SIM_PASSWORD_PHFSIM_PUK: case OFONO_SIM_PASSWORD_PHNET_PUK: case OFONO_SIM_PASSWORD_PHNETSUB_PUK: case OFONO_SIM_PASSWORD_PHSP_PUK: case OFONO_SIM_PASSWORD_PHCORP_PUK: /* 11.11 Section 9.3 ("UNBLOCK CHV"), 8 IA-5 digits */ return is_valid_pin(pin, 8, 8); break; case OFONO_SIM_PASSWORD_NONE: return is_valid_pin(pin, 0, 8); break; case OFONO_SIM_PASSWORD_INVALID: break; } return FALSE; } ofono_bool_t __ofono_is_valid_net_pin(const char *pin) { return is_valid_pin(pin, 4, 4); } static void sim_file_changed_flush(struct ofono_sim *sim, int id) { int i, imgid; if (id == SIM_EFIMG_FILEID) /* All cached images become invalid */ sim_fs_image_cache_flush(sim->simfs); else if (sim->efimg) { /* * Data and CLUT for image instances stored in the changed * file need to be re-read. */ for (i = sim->efimg_length / 9 - 1; i >= 0; i--) { imgid = (sim->efimg[i * 9 + 3] << 8) | sim->efimg[i * 9 + 4]; if (imgid == id) sim_fs_image_cache_flush_file(sim->simfs, i); } } sim_fs_cache_flush_file(sim->simfs, id); } void __ofono_sim_refresh(struct ofono_sim *sim, GSList *file_list, ofono_bool_t full_file_change, ofono_bool_t naa_init) { GSList *l; gboolean reinit_naa = naa_init || full_file_change; /* * Check if any files used in SIM initialisation procedure * are affected, except EFiccid, EFpl, EFli. */ for (l = file_list; l; l = l->next) { struct stk_file *file = l->data; uint32_t mf, df, ef; if (file->len != 6) continue; mf = (file->file[0] << 8) | (file->file[1] << 0); df = (file->file[2] << 8) | (file->file[3] << 0); ef = (file->file[4] << 8) | (file->file[5] << 0); if (mf != 0x3f00) continue; /* * 8.18: "the path '3F007FFF' indicates the relevant * NAA Application dedicated file;". */ if (df == 0x7fff) df = 0x7f20; #define DFGSM (0x7f20 << 16) #define DFTEL (0x7f10 << 16) switch ((df << 16) | ef) { case DFGSM | SIM_EFEST_FILEID: case DFGSM | SIM_EFUST_FILEID: /* aka. EFSST */ case DFGSM | SIM_EFPHASE_FILEID: case DFGSM | SIM_EFAD_FILEID: case DFTEL | SIM_EFBDN_FILEID: case DFTEL | SIM_EFADN_FILEID: case DFGSM | SIM_EF_CPHS_INFORMATION_FILEID: reinit_naa = TRUE; break; } } /* Flush cached content for affected files */ if (full_file_change) sim_fs_cache_flush(sim->simfs); else { for (l = file_list; l; l = l->next) { struct stk_file *file = l->data; int id = (file->file[file->len - 2] << 8) | (file->file[file->len - 1] << 0); sim_file_changed_flush(sim, id); } } if (reinit_naa) { sim->state = OFONO_SIM_STATE_RESETTING; __ofono_modem_sim_reset(__ofono_atom_get_modem(sim->atom)); /* Force the sim state out of READY */ sim_free_main_state(sim); call_state_watches(sim); } /* * Notify the subscribers of files that have changed and who * haven't unsubsribed during the SIM state change. */ if (full_file_change) sim_fs_notify_file_watches(sim->simfs, -1); else { for (l = file_list; l; l = l->next) { struct stk_file *file = l->data; int id = (file->file[file->len - 2] << 8) | (file->file[file->len - 1] << 0); sim_fs_notify_file_watches(sim->simfs, id); } } } ofono-1.17.bzr6912+16.04.20160314.3/src/plugin.c0000644000015600001650000001041412671500024020630 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include "ofono.h" static GSList *plugins = NULL; struct ofono_plugin { void *handle; gboolean active; struct ofono_plugin_desc *desc; }; static gint compare_priority(gconstpointer a, gconstpointer b) { const struct ofono_plugin *plugin1 = a; const struct ofono_plugin *plugin2 = b; return plugin2->desc->priority - plugin1->desc->priority; } static gboolean add_plugin(void *handle, struct ofono_plugin_desc *desc) { struct ofono_plugin *plugin; if (desc->init == NULL) return FALSE; if (g_str_equal(desc->version, OFONO_VERSION) == FALSE) { ofono_error("Invalid version %s for %s", desc->version, desc->description); return FALSE; } plugin = g_try_new0(struct ofono_plugin, 1); if (plugin == NULL) return FALSE; plugin->handle = handle; plugin->active = FALSE; plugin->desc = desc; __ofono_log_enable(desc->debug_start, desc->debug_stop); plugins = g_slist_insert_sorted(plugins, plugin, compare_priority); return TRUE; } static gboolean check_plugin(struct ofono_plugin_desc *desc, char **patterns, char **excludes) { if (excludes) { for (; *excludes; excludes++) if (g_pattern_match_simple(*excludes, desc->name)) break; if (*excludes) { ofono_info("Excluding %s", desc->description); return FALSE; } } if (patterns) { for (; *patterns; patterns++) if (g_pattern_match_simple(*patterns, desc->name)) break; if (*patterns == NULL) { ofono_info("Ignoring %s", desc->description); return FALSE; } } return TRUE; } #include "builtin.h" int __ofono_plugin_init(const char *pattern, const char *exclude) { gchar **patterns = NULL; gchar **excludes = NULL; GSList *list; GDir *dir; const gchar *file; gchar *filename; unsigned int i; DBG(""); if (pattern) patterns = g_strsplit_set(pattern, ":, ", -1); if (exclude) excludes = g_strsplit_set(exclude, ":, ", -1); for (i = 0; __ofono_builtin[i]; i++) { if (check_plugin(__ofono_builtin[i], patterns, excludes) == FALSE) continue; add_plugin(NULL, __ofono_builtin[i]); } dir = g_dir_open(PLUGINDIR, 0, NULL); if (dir != NULL) { while ((file = g_dir_read_name(dir)) != NULL) { void *handle; struct ofono_plugin_desc *desc; if (g_str_has_prefix(file, "lib") == TRUE || g_str_has_suffix(file, ".so") == FALSE) continue; filename = g_build_filename(PLUGINDIR, file, NULL); handle = dlopen(filename, RTLD_NOW); if (handle == NULL) { ofono_error("Can't load %s: %s", filename, dlerror()); g_free(filename); continue; } g_free(filename); desc = dlsym(handle, "ofono_plugin_desc"); if (desc == NULL) { ofono_error("Can't load symbol: %s", dlerror()); dlclose(handle); continue; } if (check_plugin(desc, patterns, excludes) == FALSE) { dlclose(handle); continue; } if (add_plugin(handle, desc) == FALSE) dlclose(handle); } g_dir_close(dir); } for (list = plugins; list; list = list->next) { struct ofono_plugin *plugin = list->data; if (plugin->desc->init() < 0) continue; plugin->active = TRUE; } g_strfreev(patterns); g_strfreev(excludes); return 0; } void __ofono_plugin_cleanup(void) { GSList *list; DBG(""); for (list = plugins; list; list = list->next) { struct ofono_plugin *plugin = list->data; if (plugin->active == TRUE && plugin->desc->exit) plugin->desc->exit(); if (plugin->handle) dlclose(plugin->handle); g_free(plugin); } g_slist_free(plugins); } ofono-1.17.bzr6912+16.04.20160314.3/src/gnssagent.c0000644000015600001650000000701412671500024021325 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 ST-Ericsson AB. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include "ofono.h" #include "gnssagent.h" struct gnss_agent { char *path; char *bus; guint disconnect_watch; ofono_destroy_func removed_cb; void *removed_data; }; static void gnss_agent_send_noreply(struct gnss_agent *agent, const char *method, int type, ...) { DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *message; va_list args; message = dbus_message_new_method_call(agent->bus, agent->path, OFONO_GNSS_POSR_AGENT_INTERFACE, method); va_start(args, type); dbus_message_append_args_valist(message, type, args); va_end(args); dbus_message_set_no_reply(message, TRUE); g_dbus_send_message(conn, message); } static inline void gnss_agent_send_release(struct gnss_agent *agent) { gnss_agent_send_noreply(agent, "Release", DBUS_TYPE_INVALID); } void gnss_agent_receive_request(struct gnss_agent *agent, const char *xml) { gnss_agent_send_noreply(agent, "Request", DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID); } void gnss_agent_receive_reset(struct gnss_agent *agent) { gnss_agent_send_noreply(agent, "ResetAssistanceData", DBUS_TYPE_INVALID); } ofono_bool_t gnss_agent_matches(struct gnss_agent *agent, const char *path, const char *sender) { return g_str_equal(agent->path, path) && g_str_equal(agent->bus, sender); } ofono_bool_t gnss_agent_sender_matches(struct gnss_agent *agent, const char *sender) { return g_str_equal(agent->bus, sender); } void gnss_agent_set_removed_notify(struct gnss_agent *agent, ofono_destroy_func destroy, void *user_data) { agent->removed_cb = destroy; agent->removed_data = user_data; } void gnss_agent_free(struct gnss_agent *agent) { DBusConnection *conn = ofono_dbus_get_connection(); if (agent->disconnect_watch) { gnss_agent_send_release(agent); g_dbus_remove_watch(conn, agent->disconnect_watch); agent->disconnect_watch = 0; } if (agent->removed_cb) agent->removed_cb(agent->removed_data); g_free(agent->path); g_free(agent->bus); g_free(agent); } static void gnss_agent_disconnect_cb(DBusConnection *conn, void *user_data) { struct gnss_agent *agent = user_data; agent->disconnect_watch = 0; gnss_agent_free(agent); } struct gnss_agent *gnss_agent_new(const char *path, const char *sender) { struct gnss_agent *agent = g_try_new0(struct gnss_agent, 1); DBusConnection *conn = ofono_dbus_get_connection(); if (agent == NULL) return NULL; agent->path = g_strdup(path); agent->bus = g_strdup(sender); agent->disconnect_watch = g_dbus_add_disconnect_watch(conn, sender, gnss_agent_disconnect_cb, agent, NULL); return agent; } ofono-1.17.bzr6912+16.04.20160314.3/src/handsfree-audio.c0000644000015600001650000005042212671500024022373 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 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 #endif #include #include #include #include #include #include #include #include #include #include #include "bluetooth.h" #include "hfp.h" #include "ofono.h" #define HFP_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager" #define HFP_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent" #define HFP_AUDIO_CARD_INTERFACE OFONO_SERVICE ".HandsfreeAudioCard" struct ofono_handsfree_card { enum ofono_handsfree_card_type type; char *remote; char *local; char *path; DBusMessage *msg; unsigned char selected_codec; const struct ofono_handsfree_card_driver *driver; void *driver_data; }; struct agent { char *owner; char *path; guint watch; }; static struct agent *agent = NULL; static int ref_count = 0; static GSList *card_list = 0; static guint sco_watch = 0; static GSList *drivers = 0; static ofono_bool_t has_wideband = FALSE; static int defer_setup = 1; static ofono_bool_t transparent_sco = FALSE; static const char *card_type_to_string(enum ofono_handsfree_card_type type) { switch (type) { case OFONO_HANDSFREE_CARD_TYPE_HANDSFREE: return "handsfree"; case OFONO_HANDSFREE_CARD_TYPE_GATEWAY: return "gateway"; } return ""; } static uint16_t codec2setting(uint8_t codec) { switch (codec) { case HFP_CODEC_CVSD: return BT_VOICE_CVSD_16BIT; default: return BT_VOICE_TRANSPARENT; } } static ofono_bool_t apply_settings_from_codec(int fd, uint8_t codec) { struct bt_voice voice; memset(&voice, 0, sizeof(voice)); voice.setting = codec2setting(codec); /* CVSD is the default, no need to set BT_VOICE. */ if (voice.setting == BT_VOICE_CVSD_16BIT) return TRUE; if (setsockopt(fd, SOL_BLUETOOTH, BT_VOICE, &voice, sizeof(voice)) < 0) return FALSE; return TRUE; } static void send_new_connection(const char *card, int fd, uint8_t codec) { DBusMessage *msg; DBusMessageIter iter; DBG("%p, fd: %d, codec: %hu", card, fd, codec); msg = dbus_message_new_method_call(agent->owner, agent->path, HFP_AUDIO_AGENT_INTERFACE, "NewConnection"); if (msg == NULL) return; dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &card); dbus_message_iter_append_basic(&iter, DBUS_TYPE_UNIX_FD, &fd); dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE, &codec); g_dbus_send_message(ofono_dbus_get_connection(), msg); } static struct ofono_handsfree_card *card_find(const char *remote, const char *local) { GSList *list; for (list = card_list; list; list = g_slist_next(list)) { struct ofono_handsfree_card *card = list->data; if (g_str_equal(card->remote, remote) && g_str_equal(card->local, local)) return card; } return NULL; } static gboolean sco_accept(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct ofono_handsfree_card *card; struct sockaddr_sco saddr; socklen_t alen; int sk, nsk; char local[18], remote[18]; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) return FALSE; sk = g_io_channel_unix_get_fd(io); memset(&saddr, 0, sizeof(saddr)); alen = sizeof(saddr); nsk = accept(sk, (struct sockaddr *) &saddr, &alen); if (nsk < 0) return TRUE; if (agent == NULL) { ofono_error("Reject SCO: Agent not registered"); close(nsk); return TRUE; } bt_ba2str(&saddr.sco_bdaddr, remote); memset(&saddr, 0, sizeof(saddr)); alen = sizeof(saddr); if (getsockname(nsk, (struct sockaddr *) &saddr, &alen) < 0) { ofono_error("SCO getsockname(): %s (%d)", strerror(errno), errno); close(nsk); return TRUE; } bt_ba2str(&saddr.sco_bdaddr, local); card = card_find(remote, local); if (card == NULL || card->path == NULL) { ofono_error("Rejecting SCO: Audio Card not found!"); close(nsk); return TRUE; } if (apply_settings_from_codec(nsk, card->selected_codec) == FALSE) { close(nsk); return TRUE; } DBG("SCO connection setup between local: %s and remote: %s", local, remote); send_new_connection(card->path, nsk, card->selected_codec); close(nsk); if (card->driver->sco_connected_hint) card->driver->sco_connected_hint(card); return TRUE; } static int sco_init(void) { GIOChannel *sco_io; struct sockaddr_sco saddr; struct bt_voice voice; int sk; socklen_t len; sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET | O_NONBLOCK | SOCK_CLOEXEC, BTPROTO_SCO); if (sk < 0) return -errno; /* Bind to local address */ memset(&saddr, 0, sizeof(saddr)); saddr.sco_family = AF_BLUETOOTH; bt_bacpy(&saddr.sco_bdaddr, BDADDR_ANY); if (bind(sk, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { close(sk); return -errno; } if (setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP, &defer_setup, sizeof(defer_setup)) < 0) { int err = -errno; defer_setup = 0; ofono_warn("Can't enable deferred setup: %s (%d)", strerror(errno), errno); close(sk); return err; } memset(&voice, 0, sizeof(voice)); len = sizeof(voice); if (defer_setup && getsockopt(sk, SOL_BLUETOOTH, BT_VOICE, &voice, &len) == 0) transparent_sco = TRUE; if (listen(sk, 5) < 0) { close(sk); return -errno; } sco_io = g_io_channel_unix_new(sk); sco_watch = g_io_add_watch(sco_io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, sco_accept, NULL); g_io_channel_unref(sco_io); return 0; } static void card_append_properties(struct ofono_handsfree_card *card, DBusMessageIter *dict) { const char *type; type = card_type_to_string(card->type); ofono_dbus_dict_append(dict, "Type", DBUS_TYPE_STRING, &type); ofono_dbus_dict_append(dict, "RemoteAddress", DBUS_TYPE_STRING, &card->remote); ofono_dbus_dict_append(dict, "LocalAddress", DBUS_TYPE_STRING, &card->local); } static DBusMessage *card_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_handsfree_card *card = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); card_append_properties(card, &dict); dbus_message_iter_close_container(&iter, &dict); return reply; } static gboolean sco_connect_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct ofono_handsfree_card *card = user_data; DBusMessage *reply; int sk; if (agent == NULL) { /* There's no agent, so there's no one to reply to */ reply = NULL; goto done; } if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { if (card->msg) reply = __ofono_error_failed(card->msg); goto done; } sk = g_io_channel_unix_get_fd(io); send_new_connection(card->path, sk, card->selected_codec); close(sk); if (card->msg) reply = dbus_message_new_method_return(card->msg); done: if (card->msg == NULL) return FALSE; if (reply) g_dbus_send_message(ofono_dbus_get_connection(), reply); dbus_message_unref(card->msg); card->msg = NULL; return FALSE; } static void card_connect_reply_cb(const struct ofono_error *error, void *data) { struct ofono_handsfree_card *card = data; DBusMessage *reply; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) reply = dbus_message_new_method_return(card->msg); else reply = __ofono_error_failed(card->msg); __ofono_dbus_pending_reply(&card->msg, reply); } static DBusMessage *card_connect(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_handsfree_card *card = data; const struct ofono_handsfree_card_driver *driver = card->driver; const char *sender; int err; if (agent == NULL) return __ofono_error_not_available(msg); sender = dbus_message_get_sender(msg); if (!g_str_equal(sender, agent->owner)) return __ofono_error_not_allowed(msg); if (card->msg) return __ofono_error_busy(msg); if (!driver || !driver->connect) goto fallback; card->msg = dbus_message_ref(msg); driver->connect(card, card_connect_reply_cb, card); return NULL; fallback: /* There's no driver, fallback to direct SCO connection */ err = ofono_handsfree_card_connect_sco(card); if (err < 0) return __ofono_error_failed(msg); card->msg = dbus_message_ref(msg); return NULL; } static const GDBusMethodTable card_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), card_get_properties) }, { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, card_connect) }, { } }; static const GDBusSignalTable card_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; struct ofono_handsfree_card *ofono_handsfree_card_create(unsigned int vendor, enum ofono_handsfree_card_type type, const char *driver, void *data) { struct ofono_handsfree_card *card; GSList *l; card = g_new0(struct ofono_handsfree_card, 1); card->type = type; card->selected_codec = HFP_CODEC_CVSD; card_list = g_slist_prepend(card_list, card); for (l = drivers; l; l = l->next) { const struct ofono_handsfree_card_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(card, vendor, data) < 0) continue; card->driver = drv; break; } return card; } void ofono_handsfree_card_set_data(struct ofono_handsfree_card *card, void *data) { card->driver_data = data; } void *ofono_handsfree_card_get_data(struct ofono_handsfree_card *card) { return card->driver_data; } void ofono_handsfree_card_set_remote(struct ofono_handsfree_card *card, const char *remote) { if (card->remote) g_free(card->remote); card->remote = g_strdup(remote); } const char *ofono_handsfree_card_get_remote(struct ofono_handsfree_card *card) { return card->remote; } void ofono_handsfree_card_set_local(struct ofono_handsfree_card *card, const char *local) { if (card->local) g_free(card->local); card->local = g_strdup(local); } const char *ofono_handsfree_card_get_local(struct ofono_handsfree_card *card) { return card->local; } int ofono_handsfree_card_connect_sco(struct ofono_handsfree_card *card) { GIOChannel *io; struct sockaddr_sco addr; int sk, ret; sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET | O_NONBLOCK | SOCK_CLOEXEC, BTPROTO_SCO); if (sk < 0) return -1; /* Bind to local address */ memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bt_str2ba(card->local, &addr.sco_bdaddr); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { ofono_error("Could not bind SCO socket"); close(sk); return -1; } /* Connect to remote device */ memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bt_str2ba(card->remote, &addr.sco_bdaddr); if (apply_settings_from_codec(sk, card->selected_codec) == FALSE) { close(sk); return -1; } ret = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); if (ret < 0 && errno != EINPROGRESS) { close(sk); return -1; } io = g_io_channel_unix_new(sk); g_io_add_watch(io, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL, sco_connect_cb, card); g_io_channel_unref(io); return 0; } static void emit_card_added(struct ofono_handsfree_card *card) { DBusMessage *signal; DBusMessageIter iter; DBusMessageIter dict; const char *path; signal = dbus_message_new_signal(OFONO_MANAGER_PATH, HFP_AUDIO_MANAGER_INTERFACE, "CardAdded"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); path = card->path; dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); card_append_properties(card, &dict); dbus_message_iter_close_container(&iter, &dict); g_dbus_send_message(ofono_dbus_get_connection(), signal); } int ofono_handsfree_card_register(struct ofono_handsfree_card *card) { static int next_card_id = 1; char path[64]; if (card == NULL) return -EINVAL; snprintf(path, sizeof(path), "/card_%d", next_card_id); if (!g_dbus_register_interface(ofono_dbus_get_connection(), path, HFP_AUDIO_CARD_INTERFACE, card_methods, card_signals, NULL, card, NULL)) return -EIO; next_card_id += 1; card->path = g_strdup(path); emit_card_added(card); return 0; } static void emit_card_removed(struct ofono_handsfree_card *card) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = card->path; g_dbus_emit_signal(conn, OFONO_MANAGER_PATH, HFP_AUDIO_MANAGER_INTERFACE, "CardRemoved", DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); } static void card_unregister(struct ofono_handsfree_card *card) { DBusConnection *conn = ofono_dbus_get_connection(); g_dbus_unregister_interface(conn, card->path, HFP_AUDIO_CARD_INTERFACE); emit_card_removed(card); g_free(card->path); card->path = NULL; } void ofono_handsfree_card_remove(struct ofono_handsfree_card *card) { DBG("%p", card); if (card == NULL) return; if (card->path) card_unregister(card); card_list = g_slist_remove(card_list, card); g_free(card->remote); g_free(card->local); if (card->driver && card->driver->remove) card->driver->remove(card); g_free(card); } ofono_bool_t ofono_handsfree_card_set_codec(struct ofono_handsfree_card *card, unsigned char codec) { if (codec == HFP_CODEC_CVSD) goto done; if (codec == HFP_CODEC_MSBC && has_wideband) goto done; return FALSE; done: card->selected_codec = codec; return TRUE; } ofono_bool_t ofono_handsfree_audio_has_wideband(void) { return has_wideband; } ofono_bool_t ofono_handsfree_audio_has_transparent_sco(void) { return transparent_sco; } static void agent_free(struct agent *agent) { if (agent->watch > 0) g_dbus_remove_watch(ofono_dbus_get_connection(), agent->watch); g_free(agent->owner); g_free(agent->path); g_free(agent); } static void agent_release(struct agent *agent) { DBusMessage *msg; msg = dbus_message_new_method_call(agent->owner, agent->path, HFP_AUDIO_AGENT_INTERFACE, "Release"); g_dbus_send_message(ofono_dbus_get_connection(), msg); } static void agent_disconnect(DBusConnection *conn, void *user_data) { DBG("Agent %s disconnected", agent->owner); agent_free(agent); agent = NULL; has_wideband = FALSE; } static void append_card(void *data, void *userdata) { struct ofono_handsfree_card *card = data; struct DBusMessageIter *array = userdata; DBusMessageIter entry, dict; dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &card->path); dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); card_append_properties(card, &dict); dbus_message_iter_close_container(&entry, &dict); dbus_message_iter_close_container(array, &entry); } static DBusMessage *am_get_cards(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array); g_slist_foreach(card_list, append_card, &array); dbus_message_iter_close_container(&iter, &array); return reply; } static DBusMessage *am_agent_register(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *sender, *path; unsigned char *codecs; DBusMessageIter iter, array; int length, i; gboolean has_cvsd = FALSE, has_msbc = FALSE; if (agent) return __ofono_error_in_use(msg); sender = dbus_message_get_sender(msg); if (dbus_message_iter_init(msg, &iter) == FALSE) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &array); dbus_message_iter_get_fixed_array(&array, &codecs, &length); if (length == 0) return __ofono_error_invalid_args(msg); for (i = 0; i < length; i++) { if (codecs[i] == HFP_CODEC_CVSD) has_cvsd = TRUE; else if (codecs[i] == HFP_CODEC_MSBC) has_msbc = TRUE; else return __ofono_error_invalid_args(msg); } DBG("Agent %s registered with the CODECs:%s%s", sender, has_cvsd ? " CVSD" : "", has_msbc ? " mSBC" : ""); if (has_msbc && transparent_sco) has_wideband = TRUE; else { has_wideband = FALSE; DBG("Wideband speech disabled: %s", has_msbc ? "no Transparent SCO support" : "no mSBC support"); } if (has_cvsd == FALSE) { ofono_error("CVSD codec is mandatory"); return __ofono_error_invalid_args(msg); } agent = g_new0(struct agent, 1); agent->owner = g_strdup(sender); agent->path = g_strdup(path); agent->watch = g_dbus_add_disconnect_watch(conn, sender, agent_disconnect, NULL, NULL); return dbus_message_new_method_return(msg); } static DBusMessage *am_agent_unregister(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *sender, *path; DBusMessageIter iter; if (agent == NULL) return __ofono_error_not_found(msg); sender = dbus_message_get_sender(msg); if (dbus_message_iter_init(msg, &iter) == FALSE) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &path); if (strcmp(sender, agent->owner) != 0) return __ofono_error_not_allowed(msg); if (strcmp(path, agent->path) != 0) return __ofono_error_not_found(msg); agent_free(agent); agent = NULL; has_wideband = FALSE; DBG("Agent %s unregistered", sender); return dbus_message_new_method_return(msg); } static const GDBusMethodTable am_methods[] = { { GDBUS_METHOD("GetCards", NULL, GDBUS_ARGS({"cards", "a{oa{sv}}"}), am_get_cards) } , { GDBUS_METHOD("Register", GDBUS_ARGS({"path", "o"}, {"codecs", "ay"}), NULL, am_agent_register) }, { GDBUS_METHOD("Unregister", GDBUS_ARGS({"path", "o"}), NULL, am_agent_unregister) }, { } }; static const GDBusSignalTable am_signals[] = { { GDBUS_SIGNAL("CardAdded", GDBUS_ARGS({ "path", "o" }, { "properties", "a{sv}" })) }, { GDBUS_SIGNAL("CardRemoved", GDBUS_ARGS({ "path", "o" })) }, { } }; int ofono_handsfree_card_driver_register( const struct ofono_handsfree_card_driver *d) { DBG("driver: %p", d); if (defer_setup == 0) return -ENOSYS; if (d->probe == NULL) return -EINVAL; drivers = g_slist_prepend(drivers, (void *) d); return 0; } void ofono_handsfree_card_driver_unregister( const struct ofono_handsfree_card_driver *d) { DBG("driver: %p", d); drivers = g_slist_remove(drivers, (void *) d); } void ofono_handsfree_audio_ref(void) { ref_count += 1; if (ref_count != 1) return; __ofono_handsfree_audio_manager_init(); if (!g_dbus_register_interface(ofono_dbus_get_connection(), OFONO_MANAGER_PATH, HFP_AUDIO_MANAGER_INTERFACE, am_methods, am_signals, NULL, NULL, NULL)) ofono_error("Unable to register Handsfree Audio Manager"); } void ofono_handsfree_audio_unref(void) { if (ref_count == 0) { ofono_error("Error in handsfree audio manager ref counting"); return; } ref_count -= 1; if (ref_count > 0) return; g_dbus_unregister_interface(ofono_dbus_get_connection(), OFONO_MANAGER_PATH, HFP_AUDIO_MANAGER_INTERFACE); if (agent) { agent_release(agent); agent_free(agent); } __ofono_handsfree_audio_manager_cleanup(); } int __ofono_handsfree_audio_manager_init(void) { return sco_init(); } void __ofono_handsfree_audio_manager_cleanup(void) { if (ref_count != 0) return; if (sco_watch > 0) { g_source_remove(sco_watch); sco_watch = 0; } } ofono-1.17.bzr6912+16.04.20160314.3/src/cdma-provision.c0000644000015600001650000000402512671500024022265 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include "ofono.h" static GSList *g_drivers = NULL; ofono_bool_t __ofono_cdma_provision_get_name(const char *sid, char **name) { GSList *d; if (sid == NULL || strlen(sid) == 0) return FALSE; for (d = g_drivers; d != NULL; d = d->next) { const struct ofono_cdma_provision_driver *driver = d->data; if (driver->get_provider_name == NULL) continue; DBG("Calling cdma provision plugin '%s'", driver->name); if (driver->get_provider_name(sid, name) < 0) continue; return TRUE; } return FALSE; } static gint compare_priority(gconstpointer a, gconstpointer b) { const struct ofono_cdma_provision_driver *plugin1 = a; const struct ofono_cdma_provision_driver *plugin2 = b; return plugin2->priority - plugin1->priority; } int ofono_cdma_provision_driver_register( const struct ofono_cdma_provision_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_insert_sorted(g_drivers, (void *) driver, compare_priority); return 0; } void ofono_cdma_provision_driver_unregister( const struct ofono_cdma_provision_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_remove(g_drivers, driver); } ofono-1.17.bzr6912+16.04.20160314.3/src/util.h0000644000015600001650000000677612671500024020334 0ustar pbuserpbgroup00000000000000/* * * 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 * */ enum gsm_dialect { GSM_DIALECT_DEFAULT = 0, GSM_DIALECT_TURKISH, GSM_DIALECT_SPANISH, GSM_DIALECT_PORTUGUESE, }; char *convert_gsm_to_utf8(const unsigned char *text, long len, long *items_read, long *items_written, unsigned char terminator); char *convert_gsm_to_utf8_with_lang(const unsigned char *text, long len, long *items_read, long *items_written, unsigned char terminator, enum gsm_dialect locking_shift_lang, enum gsm_dialect single_shift_lang); unsigned char *convert_utf8_to_gsm(const char *text, long len, long *items_read, long *items_written, unsigned char terminator); unsigned char *convert_utf8_to_gsm_with_lang(const char *text, long len, long *items_read, long *items_written, unsigned char terminator, enum gsm_dialect locking_shift_lang, enum gsm_dialect single_shift_lang); unsigned char *convert_utf8_to_gsm_best_lang(const char *utf8, long len, long *items_read, long *items_written, unsigned char terminator, enum gsm_dialect hint, enum gsm_dialect *used_locking, enum gsm_dialect *used_single); unsigned char *decode_hex_own_buf(const char *in, long len, long *items_written, unsigned char terminator, unsigned char *buf); unsigned char *decode_hex(const char *in, long len, long *items_written, unsigned char terminator); char *encode_hex_own_buf(const unsigned char *in, long len, unsigned char terminator, char *buf); char *encode_hex(const unsigned char *in, long len, unsigned char terminator); unsigned char *unpack_7bit_own_buf(const unsigned char *in, long len, int byte_offset, gboolean ussd, long max_to_unpack, long *items_written, unsigned char terminator, unsigned char *buf); unsigned char *unpack_7bit(const unsigned char *in, long len, int byte_offset, gboolean ussd, long max_to_unpack, long *items_written, unsigned char terminator); unsigned char *pack_7bit_own_buf(const unsigned char *in, long len, int byte_offset, gboolean ussd, long *items_written, unsigned char terminator, unsigned char *buf); unsigned char *pack_7bit(const unsigned char *in, long len, int byte_offset, gboolean ussd, long *items_written, unsigned char terminator); char *sim_string_to_utf8(const unsigned char *buffer, int length); unsigned char *utf8_to_sim_string(const char *utf, int max_length, int *out_length); unsigned char *convert_ucs2_to_gsm_with_lang(const unsigned char *text, long len, long *items_read, long *items_written, unsigned char terminator, enum gsm_dialect locking_lang, enum gsm_dialect single_lang); unsigned char *convert_ucs2_to_gsm(const unsigned char *text, long len, long *items_read, long *items_written, unsigned char terminator); ofono-1.17.bzr6912+16.04.20160314.3/src/gnssagent.h0000644000015600001650000000260612671500024021334 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 ST-Ericsson AB. * * 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 gnss_agent; struct gnss_agent *gnss_agent_new(const char *path, const char *sender); void gnss_agent_free(struct gnss_agent *agent); void gnss_agent_receive_request(struct gnss_agent *agent, const char *xml); void gnss_agent_receive_reset(struct gnss_agent *agent); void gnss_agent_set_removed_notify(struct gnss_agent *agent, ofono_destroy_func removed_cb, void *user_data); ofono_bool_t gnss_agent_matches(struct gnss_agent *agent, const char *path, const char *sender); ofono_bool_t gnss_agent_sender_matches(struct gnss_agent *agent, const char *sender); ofono-1.17.bzr6912+16.04.20160314.3/src/gprs-provision.c0000644000015600001650000000512012671500024022331 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include "ofono.h" static GSList *g_drivers = NULL; void __ofono_gprs_provision_free_settings( struct ofono_gprs_provision_data *settings, int count) { int i; for (i = 0; i < count; i++) { g_free(settings[i].name); g_free(settings[i].apn); g_free(settings[i].username); g_free(settings[i].password); g_free(settings[i].message_proxy); g_free(settings[i].message_center); } g_free(settings); } ofono_bool_t __ofono_gprs_provision_get_settings(const char *mcc, const char *mnc, const char *spn, const char *imsi, const char *gid1, struct ofono_gprs_provision_data **settings, int *count) { GSList *d; if (mcc == NULL || strlen(mcc) == 0 || mnc == NULL || strlen(mnc) == 0) return FALSE; for (d = g_drivers; d != NULL; d = d->next) { const struct ofono_gprs_provision_driver *driver = d->data; if (driver->get_settings == NULL) continue; DBG("Calling provisioning plugin '%s'", driver->name); if (driver->get_settings(mcc, mnc, spn, imsi, gid1, settings, count) < 0) continue; return TRUE; } return FALSE; } static gint compare_priority(gconstpointer a, gconstpointer b) { const struct ofono_gprs_provision_driver *plugin1 = a; const struct ofono_gprs_provision_driver *plugin2 = b; return plugin2->priority - plugin1->priority; } int ofono_gprs_provision_driver_register( const struct ofono_gprs_provision_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_insert_sorted(g_drivers, (void *) driver, compare_priority); return 0; } void ofono_gprs_provision_driver_unregister( const struct ofono_gprs_provision_driver *driver) { DBG("driver: %p name: %s", driver, driver->name); g_drivers = g_slist_remove(g_drivers, driver); } ofono-1.17.bzr6912+16.04.20160314.3/src/util.c0000644000015600001650000011750612671500024020321 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include "util.h" /* Name: GSM 03.38 to Unicode Unicode version: 3.0 Table version: 1.1 Table format: Format A Date: 2000 May 30 Authors: Ken Whistler Kent Karlsson Markus Kuhn Copyright (c) 2000 Unicode, Inc. All Rights reserved. This file is provided as-is by Unicode, Inc. (The Unicode Consortium). No claims are made as to fitness for any particular purpose. No warranties of any kind are expressed or implied. The recipient agrees to determine applicability of information provided. If this file has been provided on optical media by Unicode, Inc., the sole remedy for any claim will be exchange of defective media within 90 days of receipt. Unicode, Inc. hereby grants the right to freely use the information supplied in this file in the creation of products supporting the Unicode Standard, and to make copies of this file in any form for internal or external distribution as long as this notice remains attached. */ #define GUND 0xFFFF #define UTF8_LENGTH(c) \ ((c) < 0x80 ? 1 : ((c) < 0x800 ? 2 : 3)) #define TABLE_SIZE(t) \ (sizeof((t)) / sizeof(struct codepoint)) struct codepoint { unsigned short from; unsigned short to; }; struct conversion_table { /* To unicode locking shift table */ const struct codepoint *locking_u; unsigned int locking_len_u; /* To unicode single shift table */ const struct codepoint *single_u; unsigned int single_len_u; /* To GSM locking shift table, fixed size */ const unsigned short *locking_g; /* To GSM single shift table */ const struct codepoint *single_g; unsigned int single_len_g; }; /* GSM to Unicode extension table, for GSM sequences starting with 0x1B */ static const struct codepoint def_ext_gsm[] = { { 0x0A, 0x000C }, /* See NOTE 3 in 23.038 */ { 0x14, 0x005E }, { 0x1B, 0x0020 }, /* See NOTE 1 in 23.038 */ { 0x28, 0x007B }, { 0x29, 0x007D }, { 0x2F, 0x005C }, { 0x3C, 0x005B }, { 0x3D, 0x007E }, { 0x3E, 0x005D }, { 0x40, 0x007C }, { 0x65, 0x20AC } }; static const struct codepoint def_ext_unicode[] = { { 0x000C, 0x1B0A }, { 0x005B, 0x1B3C }, { 0x005C, 0x1B2F }, { 0x005D, 0x1B3E }, { 0x005E, 0x1B14 }, { 0x007B, 0x1B28 }, { 0x007C, 0x1B40 }, { 0x007D, 0x1B29 }, { 0x007E, 0x1B3D }, { 0x20AC, 0x1B65 } }; /* Appendix A.2.1. in 3GPP TS23.038, V.8.2.0 */ static const struct codepoint tur_ext_gsm[] = { { 0x0A, 0x000C }, /* See NOTE 3 */ { 0x14, 0x005E }, { 0x1B, 0x0020 }, /* See NOTE 1 */ { 0x28, 0x007B }, { 0x29, 0x007D }, { 0x2F, 0x005C }, { 0x3C, 0x005B }, { 0x3D, 0x007E }, { 0x3E, 0x005D }, { 0x40, 0x007C }, { 0x47, 0x011E }, { 0x49, 0x0130 }, { 0x53, 0x015E }, { 0x63, 0x00E7 }, { 0x65, 0x20AC }, { 0x67, 0x011F }, { 0x69, 0x0131 }, { 0x73, 0x015F } }; static const struct codepoint tur_ext_unicode[] = { { 0x000C, 0x1B0A }, { 0x005B, 0x1B3C }, { 0x005C, 0x1B2F }, { 0x005D, 0x1B3E }, { 0x005E, 0x1B14 }, { 0x007B, 0x1B28 }, { 0x007C, 0x1B40 }, { 0x007D, 0x1B29 }, { 0x007E, 0x1B3D }, { 0x00E7, 0x1B63 }, { 0x011E, 0x1B47 }, { 0x011F, 0x1B67 }, { 0x0130, 0x1B49 }, { 0x0131, 0x1B69 }, { 0x015E, 0x1B53 }, { 0x015F, 0x1B73 }, { 0x20AC, 0x1B65 } }; /* Appendix A.2.2. in 3GPP TS23.038 V.8.2.0*/ static const struct codepoint spa_ext_gsm[] = { { 0x09, 0x00E7 }, { 0x0A, 0x000C }, /* See NOTE 3 */ { 0x14, 0x005E }, { 0x1B, 0x0020 }, /* See NOTE 1 */ { 0x28, 0x007B }, { 0x29, 0x007D }, { 0x2F, 0x005C }, { 0x3C, 0x005B }, { 0x3D, 0x007E }, { 0x3E, 0x005D }, { 0x40, 0x007C }, { 0x41, 0x00C1 }, { 0x49, 0x00CD }, { 0x4F, 0x00D3 }, { 0x55, 0x00DA }, { 0x61, 0x00E1 }, { 0x65, 0x20AC }, { 0x69, 0x00ED }, { 0x6F, 0x00F3 }, { 0x75, 0x00FA } }; static const struct codepoint spa_ext_unicode[] = { { 0x000C, 0x1B0A }, { 0x005B, 0x1B3C }, { 0x005C, 0x1B2F }, { 0x005D, 0x1B3E }, { 0x005E, 0x1B14 }, { 0x007B, 0x1B28 }, { 0x007C, 0x1B40 }, { 0x007D, 0x1B29 }, { 0x007E, 0x1B3D }, { 0x00C1, 0x1B41 }, { 0x00CD, 0x1B49 }, { 0x00D3, 0x1B4F }, { 0x00DA, 0x1B55 }, { 0x00E1, 0x1B61 }, { 0x00E7, 0x1B09 }, { 0x00ED, 0x1B69 }, { 0x00F3, 0x1B6F }, { 0x00FA, 0x1B75 }, { 0x20AC, 0x1B65 } }; /* Appendix A.2.3. in 3GPP TS23.038 V.8.2.0 */ static const struct codepoint por_ext_gsm[] = { { 0x05, 0x00EA }, { 0x09, 0x00E7 }, { 0x0A, 0x000C }, /* See NOTE 3 */ { 0x0B, 0x00D4 }, { 0x0C, 0x00F4 }, { 0x0E, 0x00C1 }, { 0x0F, 0x00E1 }, { 0x12, 0x03A6 }, { 0x13, 0x0393 }, { 0x14, 0x005E }, { 0x15, 0x03A9 }, { 0x16, 0x03A0 }, { 0x17, 0x03A8 }, { 0x18, 0x03A3 }, { 0x19, 0x0398 }, { 0x1B, 0x0020 }, /* See NOTE 1 */ { 0x1F, 0x00CA }, { 0x28, 0x007B }, { 0x29, 0x007D }, { 0x2F, 0x005C }, { 0x3C, 0x005B }, { 0x3D, 0x007E }, { 0x3E, 0x005D }, { 0x40, 0x007C }, { 0x41, 0x00C0 }, { 0x49, 0x00CD }, { 0x4F, 0x00D3 }, { 0x55, 0x00DA }, { 0x5B, 0x00C3 }, { 0x5C, 0x00D5 }, { 0x61, 0x00C2 }, { 0x65, 0x20AC }, { 0x69, 0x00ED }, { 0x6F, 0x00F3 }, { 0x75, 0x00FA }, { 0x7B, 0x00E3 }, { 0x7C, 0x00F5 }, { 0x7F, 0x00E2 } }; static const struct codepoint por_ext_unicode[] = { { 0x000C, 0x1B0A }, { 0x005B, 0x1B3C }, { 0x005C, 0x1B2F }, { 0x005D, 0x1B3E }, { 0x005E, 0x1B14 }, { 0x007B, 0x1B28 }, { 0x007C, 0x1B40 }, { 0x007D, 0x1B29 }, { 0x007E, 0x1B3D }, { 0x00C0, 0x1B41 }, { 0x00C1, 0x1B0E }, { 0x00C2, 0x1B61 }, { 0x00C3, 0x1B5B }, { 0x00CA, 0x1B1F }, { 0x00CD, 0x1B49 }, { 0x00D3, 0x1B4F }, { 0x00D4, 0x1B0B }, { 0x00D5, 0x1B5C }, { 0x00DA, 0x1B55 }, { 0x00E1, 0x1B0F }, { 0x00E2, 0x1B7F }, { 0x00E3, 0x1B7B }, { 0x00E7, 0x1B09 }, { 0x00EA, 0x1B05 }, { 0x00ED, 0x1B69 }, { 0x00F3, 0x1B6F }, { 0x00F4, 0x1B0C }, { 0x00F5, 0x1B7C }, { 0x00FA, 0x1B75 }, { 0x0393, 0x1B13 }, { 0x0398, 0x1B19 }, { 0x03A0, 0x1B16 }, { 0x03A3, 0x1B18 }, { 0x03A6, 0x1B12 }, { 0x03A8, 0x1B17 }, { 0x03A9, 0x1B15 }, { 0x20AC, 0x1B65 } }; /* Used for conversion of GSM to Unicode */ static const unsigned short def_gsm[] = { 0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC, 0x00F2, 0x00C7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5, 0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8, 0x03A3, 0x0398, 0x039E, 0x00A0, 0x00C6, 0x00E6, 0x00DF, 0x00C9, 0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x00A1, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00C4, 0x00D6, 0x00D1, 0x00DC, 0x00A7, 0x00BF, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E4, 0x00F6, 0x00F1, 0x00FC, 0x00E0 }; /* Used for conversion of Unicode to GSM */ static const struct codepoint def_unicode[] = { { 0x000A, 0x0A }, { 0x000D, 0x0D }, { 0x0020, 0x20 }, { 0x0021, 0x21 }, { 0x0022, 0x22 }, { 0x0023, 0x23 }, { 0x0024, 0x02 }, { 0x0025, 0x25 }, { 0x0026, 0x26 }, { 0x0027, 0x27 }, { 0x0028, 0x28 }, { 0x0029, 0x29 }, { 0x002A, 0x2A }, { 0x002B, 0x2B }, { 0x002C, 0x2C }, { 0x002D, 0x2D }, { 0x002E, 0x2E }, { 0x002F, 0x2F }, { 0x0030, 0x30 }, { 0x0031, 0x31 }, { 0x0032, 0x32 }, { 0x0033, 0x33 }, { 0x0034, 0x34 }, { 0x0035, 0x35 }, { 0x0036, 0x36 }, { 0x0037, 0x37 }, { 0x0038, 0x38 }, { 0x0039, 0x39 }, { 0x003A, 0x3A }, { 0x003B, 0x3B }, { 0x003C, 0x3C }, { 0x003D, 0x3D }, { 0x003E, 0x3E }, { 0x003F, 0x3F }, { 0x0040, 0x00 }, { 0x0041, 0x41 }, { 0x0042, 0x42 }, { 0x0043, 0x43 }, { 0x0044, 0x44 }, { 0x0045, 0x45 }, { 0x0046, 0x46 }, { 0x0047, 0x47 }, { 0x0048, 0x48 }, { 0x0049, 0x49 }, { 0x004A, 0x4A }, { 0x004B, 0x4B }, { 0x004C, 0x4C }, { 0x004D, 0x4D }, { 0x004E, 0x4E }, { 0x004F, 0x4F }, { 0x0050, 0x50 }, { 0x0051, 0x51 }, { 0x0052, 0x52 }, { 0x0053, 0x53 }, { 0x0054, 0x54 }, { 0x0055, 0x55 }, { 0x0056, 0x56 }, { 0x0057, 0x57 }, { 0x0058, 0x58 }, { 0x0059, 0x59 }, { 0x005A, 0x5A }, { 0x005F, 0x11 }, { 0x0061, 0x61 }, { 0x0062, 0x62 }, { 0x0063, 0x63 }, { 0x0064, 0x64 }, { 0x0065, 0x65 }, { 0x0066, 0x66 }, { 0x0067, 0x67 }, { 0x0068, 0x68 }, { 0x0069, 0x69 }, { 0x006A, 0x6A }, { 0x006B, 0x6B }, { 0x006C, 0x6C }, { 0x006D, 0x6D }, { 0x006E, 0x6E }, { 0x006F, 0x6F }, { 0x0070, 0x70 }, { 0x0071, 0x71 }, { 0x0072, 0x72 }, { 0x0073, 0x73 }, { 0x0074, 0x74 }, { 0x0075, 0x75 }, { 0x0076, 0x76 }, { 0x0077, 0x77 }, { 0x0078, 0x78 }, { 0x0079, 0x79 }, { 0x007A, 0x7A }, { 0x00A0, 0x20 }, { 0x00A1, 0x40 }, { 0x00A3, 0x01 }, { 0x00A4, 0x24 }, { 0x00A5, 0x03 }, { 0x00A7, 0x5F }, { 0x00BF, 0x60 }, { 0x00C4, 0x5B }, { 0x00C5, 0x0E }, { 0x00C6, 0x1C }, { 0x00C7, 0x09 }, { 0x00C9, 0x1F }, { 0x00D1, 0x5D }, { 0x00D6, 0x5C }, { 0x00D8, 0x0B }, { 0x00DC, 0x5E }, { 0x00DF, 0x1E }, { 0x00E0, 0x7F }, { 0x00E4, 0x7B }, { 0x00E5, 0x0F }, { 0x00E6, 0x1D }, { 0x00E8, 0x04 }, { 0x00E9, 0x05 }, { 0x00EC, 0x07 }, { 0x00F1, 0x7D }, { 0x00F2, 0x08 }, { 0x00F6, 0x7C }, { 0x00F8, 0x0C }, { 0x00F9, 0x06 }, { 0x00FC, 0x7E }, { 0x0393, 0x13 }, { 0x0394, 0x10 }, { 0x0398, 0x19 }, { 0x039B, 0x14 }, { 0x039E, 0x1A }, { 0x03A0, 0x16 }, { 0x03A3, 0x18 }, { 0x03A6, 0x12 }, { 0x03A8, 0x17 }, { 0x03A9, 0x15 } }; /* Appendix A.3.1 in 3GPP TS23.038 */ static const unsigned short tur_gsm[] = { 0x0040, 0x00A3, 0x0024, 0x00A5, 0x20AC, 0x00E9, 0x00F9, 0x0131, 0x00F2, 0x00C7, 0x000A, 0x011E, 0x011F, 0x000D, 0x00C5, 0x00E5, 0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8, 0x03A3, 0x0398, 0x039E, 0x00A0, 0x015E, 0x015F, 0x00DF, 0x00C9, 0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0130, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00C4, 0x00D6, 0x00D1, 0x00DC, 0x00A7, 0x00E7, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E4, 0x00F6, 0x00F1, 0x00FC, 0x00E0 }; static const struct codepoint tur_unicode[] = { { 0x000A, 0x0A }, { 0x000D, 0x0D }, { 0x0020, 0x20 }, { 0x0021, 0x21 }, { 0x0022, 0x22 }, { 0x0023, 0x23 }, { 0x0024, 0x02 }, { 0x0025, 0x25 }, { 0x0026, 0x26 }, { 0x0027, 0x27 }, { 0x0028, 0x28 }, { 0x0029, 0x29 }, { 0x002A, 0x2A }, { 0x002B, 0x2B }, { 0x002C, 0x2C }, { 0x002D, 0x2D }, { 0x002E, 0x2E }, { 0x002F, 0x2F }, { 0x0030, 0x30 }, { 0x0031, 0x31 }, { 0x0032, 0x32 }, { 0x0033, 0x33 }, { 0x0034, 0x34 }, { 0x0035, 0x35 }, { 0x0036, 0x36 }, { 0x0037, 0x37 }, { 0x0038, 0x38 }, { 0x0039, 0x39 }, { 0x003A, 0x3A }, { 0x003B, 0x3B }, { 0x003C, 0x3C }, { 0x003D, 0x3D }, { 0x003E, 0x3E }, { 0x003F, 0x3F }, { 0x0040, 0x00 }, { 0x0041, 0x41 }, { 0x0042, 0x42 }, { 0x0043, 0x43 }, { 0x0044, 0x44 }, { 0x0045, 0x45 }, { 0x0046, 0x46 }, { 0x0047, 0x47 }, { 0x0048, 0x48 }, { 0x0049, 0x49 }, { 0x004A, 0x4A }, { 0x004B, 0x4B }, { 0x004C, 0x4C }, { 0x004D, 0x4D }, { 0x004E, 0x4E }, { 0x004F, 0x4F }, { 0x0050, 0x50 }, { 0x0051, 0x51 }, { 0x0052, 0x52 }, { 0x0053, 0x53 }, { 0x0054, 0x54 }, { 0x0055, 0x55 }, { 0x0056, 0x56 }, { 0x0057, 0x57 }, { 0x0058, 0x58 }, { 0x0059, 0x59 }, { 0x005A, 0x5A }, { 0x005F, 0x11 }, { 0x0061, 0x61 }, { 0x0062, 0x62 }, { 0x0063, 0x63 }, { 0x0064, 0x64 }, { 0x0065, 0x65 }, { 0x0066, 0x66 }, { 0x0067, 0x67 }, { 0x0068, 0x68 }, { 0x0069, 0x69 }, { 0x006A, 0x6A }, { 0x006B, 0x6B }, { 0x006C, 0x6C }, { 0x006D, 0x6D }, { 0x006E, 0x6E }, { 0x006F, 0x6F }, { 0x0070, 0x70 }, { 0x0071, 0x71 }, { 0x0072, 0x72 }, { 0x0073, 0x73 }, { 0x0074, 0x74 }, { 0x0075, 0x75 }, { 0x0076, 0x76 }, { 0x0077, 0x77 }, { 0x0078, 0x78 }, { 0x0079, 0x79 }, { 0x007A, 0x7A }, { 0x00A0, 0x20 }, { 0x00A3, 0x01 }, { 0x00A4, 0x24 }, { 0x00A5, 0x03 }, { 0x00A7, 0x5F }, { 0x00C4, 0x5B }, { 0x00C5, 0x0E }, { 0x00C7, 0x09 }, { 0x00C9, 0x1F }, { 0x00D1, 0x5D }, { 0x00D6, 0x5C }, { 0x00DC, 0x5E }, { 0x00DF, 0x1E }, { 0x00E0, 0x7F }, { 0x00E4, 0x7B }, { 0x00E5, 0x0F }, { 0x00E7, 0x60 }, { 0x00E9, 0x05 }, { 0x00F1, 0x7D }, { 0x00F2, 0x08 }, { 0x00F6, 0x7C }, { 0x00F9, 0x06 }, { 0x00FC, 0x7E }, { 0x011E, 0x0B }, { 0x011F, 0x0C }, { 0x0130, 0x40 }, { 0x0131, 0x07 }, { 0x015E, 0x1C }, { 0x015F, 0x1D }, { 0x0393, 0x13 }, { 0x0394, 0x10 }, { 0x0398, 0x19 }, { 0x039B, 0x14 }, { 0x039E, 0x1A }, { 0x03A0, 0x16 }, { 0x03A3, 0x18 }, { 0x03A6, 0x12 }, { 0x03A8, 0x17 }, { 0x03A9, 0x15 }, { 0x20AC, 0x04 } }; /* Appendix A.3.2 in 3GPP TS23.038 */ static const unsigned short por_gsm[] = { 0x0040, 0x00A3, 0x0024, 0x00A5, 0x00EA, 0x00E9, 0x00FA, 0x00ED, 0x00F3, 0x00E7, 0x000A, 0x00D4, 0x00F4, 0x000D, 0x00C1, 0x00E1, 0x0394, 0x005F, 0x00AA, 0x00C7, 0x00C0, 0x221E, 0x005E, 0x005C, 0x20ac, 0x00D3, 0x007C, 0x00A0, 0x00C2, 0x00E2, 0x00CA, 0x00C9, 0x0020, 0x0021, 0x0022, 0x0023, 0x00BA, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x00CD, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00C3, 0x00D5, 0x00DA, 0x00DC, 0x00A7, 0x007E, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E3, 0x00F5, 0x0060, 0x00FC, 0x00E0 }; static const struct codepoint por_unicode[] = { { 0x000A, 0x0A }, { 0x000D, 0x0D }, { 0x0020, 0x20 }, { 0x0021, 0x21 }, { 0x0022, 0x22 }, { 0x0023, 0x23 }, { 0x0024, 0x02 }, { 0x0025, 0x25 }, { 0x0026, 0x26 }, { 0x0027, 0x27 }, { 0x0028, 0x28 }, { 0x0029, 0x29 }, { 0x002A, 0x2A }, { 0x002B, 0x2B }, { 0x002C, 0x2C }, { 0x002D, 0x2D }, { 0x002E, 0x2E }, { 0x002F, 0x2F }, { 0x0030, 0x30 }, { 0x0031, 0x31 }, { 0x0032, 0x32 }, { 0x0033, 0x33 }, { 0x0034, 0x34 }, { 0x0035, 0x35 }, { 0x0036, 0x36 }, { 0x0037, 0x37 }, { 0x0038, 0x38 }, { 0x0039, 0x39 }, { 0x003A, 0x3A }, { 0x003B, 0x3B }, { 0x003C, 0x3C }, { 0x003D, 0x3D }, { 0x003E, 0x3E }, { 0x003F, 0x3F }, { 0x0040, 0x00 }, { 0x0041, 0x41 }, { 0x0042, 0x42 }, { 0x0043, 0x43 }, { 0x0044, 0x44 }, { 0x0045, 0x45 }, { 0x0046, 0x46 }, { 0x0047, 0x47 }, { 0x0048, 0x48 }, { 0x0049, 0x49 }, { 0x004A, 0x4A }, { 0x004B, 0x4B }, { 0x004C, 0x4C }, { 0x004D, 0x4D }, { 0x004E, 0x4E }, { 0x004F, 0x4F }, { 0x0050, 0x50 }, { 0x0051, 0x51 }, { 0x0052, 0x52 }, { 0x0053, 0x53 }, { 0x0054, 0x54 }, { 0x0055, 0x55 }, { 0x0056, 0x56 }, { 0x0057, 0x57 }, { 0x0058, 0x58 }, { 0x0059, 0x59 }, { 0x005A, 0x5A }, { 0x005C, 0x17 }, { 0x005E, 0x16 }, { 0x005F, 0x11 }, { 0x0060, 0x7D }, { 0x0061, 0x61 }, { 0x0062, 0x62 }, { 0x0063, 0x63 }, { 0x0064, 0x64 }, { 0x0065, 0x65 }, { 0x0066, 0x66 }, { 0x0067, 0x67 }, { 0x0068, 0x68 }, { 0x0069, 0x69 }, { 0x006A, 0x6A }, { 0x006B, 0x6B }, { 0x006C, 0x6C }, { 0x006D, 0x6D }, { 0x006E, 0x6E }, { 0x006F, 0x6F }, { 0x0070, 0x70 }, { 0x0071, 0x71 }, { 0x0072, 0x72 }, { 0x0073, 0x73 }, { 0x0074, 0x74 }, { 0x0075, 0x75 }, { 0x0076, 0x76 }, { 0x0077, 0x77 }, { 0x0078, 0x78 }, { 0x0079, 0x79 }, { 0x007A, 0x7A }, { 0x007C, 0x1A }, { 0x007E, 0x60 }, { 0x00A0, 0x20 }, { 0x00A3, 0x01 }, { 0x00A5, 0x03 }, { 0x00A7, 0x5F }, { 0x00AA, 0x12 }, { 0x00BA, 0x24 }, { 0x00C0, 0x14 }, { 0x00C1, 0x0E }, { 0x00C2, 0x1C }, { 0x00C3, 0x5B }, { 0x00C7, 0x13 }, { 0x00C9, 0x1F }, { 0x00CA, 0x1E }, { 0x00CD, 0x40 }, { 0x00D3, 0x19 }, { 0x00D4, 0x0B }, { 0x00D5, 0x5C }, { 0x00DA, 0x5D }, { 0x00DC, 0x5E }, { 0x00E0, 0x7F }, { 0x00E1, 0x0F }, { 0x00E2, 0x1D }, { 0x00E3, 0x7B }, { 0x00E7, 0x09 }, { 0x00E9, 0x05 }, { 0x00EA, 0x04 }, { 0x00ED, 0x07 }, { 0x00F3, 0x08 }, { 0x00F4, 0x0C }, { 0x00F5, 0x7C }, { 0x00FA, 0x06 }, { 0x00FC, 0x7E }, { 0x0394, 0x10 }, { 0x20AC, 0x18 }, { 0x221E, 0x15 } }; static int compare_codepoints(const void *a, const void *b) { const struct codepoint *ca = (const struct codepoint *) a; const struct codepoint *cb = (const struct codepoint *) b; return (ca->from > cb->from) - (ca->from < cb->from); } static unsigned short codepoint_lookup(struct codepoint *key, const struct codepoint *table, unsigned int len) { struct codepoint *result = NULL; result = bsearch(key, table, len, sizeof(struct codepoint), compare_codepoints); return result ? result->to : GUND; } static unsigned short gsm_locking_shift_lookup(struct conversion_table *t, unsigned char k) { return t->locking_g[k]; } static unsigned short gsm_single_shift_lookup(struct conversion_table *t, unsigned char k) { struct codepoint key = { k, 0 }; return codepoint_lookup(&key, t->single_g, t->single_len_g); } static unsigned short unicode_locking_shift_lookup(struct conversion_table *t, unsigned short k) { struct codepoint key = { k, 0 }; return codepoint_lookup(&key, t->locking_u, t->locking_len_u); } static unsigned short unicode_single_shift_lookup(struct conversion_table *t, unsigned short k) { struct codepoint key = { k, 0 }; return codepoint_lookup(&key, t->single_u, t->single_len_u); } static gboolean populate_locking_shift(struct conversion_table *t, enum gsm_dialect lang) { switch (lang) { case GSM_DIALECT_DEFAULT: case GSM_DIALECT_SPANISH: t->locking_g = def_gsm; t->locking_u = def_unicode; t->locking_len_u = TABLE_SIZE(def_unicode); return TRUE; case GSM_DIALECT_TURKISH: t->locking_g = tur_gsm; t->locking_u = tur_unicode; t->locking_len_u = TABLE_SIZE(tur_unicode); return TRUE; case GSM_DIALECT_PORTUGUESE: t->locking_g = por_gsm; t->locking_u = por_unicode; t->locking_len_u = TABLE_SIZE(por_unicode); return TRUE; } return FALSE; } static gboolean populate_single_shift(struct conversion_table *t, enum gsm_dialect lang) { switch (lang) { case GSM_DIALECT_DEFAULT: t->single_g = def_ext_gsm; t->single_len_g = TABLE_SIZE(def_ext_gsm); t->single_u = def_ext_unicode; t->single_len_u = TABLE_SIZE(def_ext_unicode); return TRUE; case GSM_DIALECT_TURKISH: t->single_g = tur_ext_gsm; t->single_len_g = TABLE_SIZE(tur_ext_gsm); t->single_u = tur_ext_unicode; t->single_len_u = TABLE_SIZE(tur_ext_unicode); return TRUE; case GSM_DIALECT_SPANISH: t->single_g = spa_ext_gsm; t->single_len_g = TABLE_SIZE(spa_ext_gsm); t->single_u = spa_ext_unicode; t->single_len_u = TABLE_SIZE(spa_ext_unicode); return TRUE; case GSM_DIALECT_PORTUGUESE: t->single_g = por_ext_gsm; t->single_len_g = TABLE_SIZE(por_ext_gsm); t->single_u = por_ext_unicode; t->single_len_u = TABLE_SIZE(por_ext_unicode); return TRUE; } return FALSE; } static gboolean conversion_table_init(struct conversion_table *t, enum gsm_dialect locking, enum gsm_dialect single) { memset(t, 0, sizeof(struct conversion_table)); return populate_locking_shift(t, locking) && populate_single_shift(t, single); } /*! * Converts text coded using GSM codec into UTF8 encoded text, using * the given language identifiers for single shift and locking shift * tables. If len is less than 0, and terminator character is given, * the length is computed automatically. * * Returns newly-allocated UTF8 encoded string or NULL if the conversion * could not be performed. Returns the number of bytes read from the * GSM encoded string in items_read (if not NULL), not including the * terminator character. Returns the number of bytes written into the UTF8 * encoded string in items_written (if not NULL) not including the terminal * '\0' character. The caller is responsible for freeing the returned value. */ char *convert_gsm_to_utf8_with_lang(const unsigned char *text, long len, long *items_read, long *items_written, unsigned char terminator, enum gsm_dialect locking_lang, enum gsm_dialect single_lang) { char *res = NULL; char *out; long i = 0; long res_length; struct conversion_table t; if (conversion_table_init(&t, locking_lang, single_lang) == FALSE) return NULL; if (len < 0 && !terminator) goto error; if (len < 0) { i = 0; while (text[i] != terminator) i++; len = i; } for (i = 0, res_length = 0; i < len; i++) { unsigned short c; if (text[i] > 0x7f) goto error; if (text[i] == 0x1b) { ++i; if (i >= len) goto error; c = gsm_single_shift_lookup(&t, text[i]); /* * According to the comment in the table from * 3GPP 23.038, Section 6.2.1.1: * "In the event that an MS receives a code where * a symbol is not represented in the above table * then the MS shall display either the character * shown in the main GSM 7 bit default alphabet * table in subclause 6.2.1., or the character from * the National Language Locking Shift Table in the * case where the locking shift mechanism as defined * in subclause 6.2.1.2.3 is used." */ if (c == GUND) c = gsm_locking_shift_lookup(&t, text[i]); } else c = gsm_locking_shift_lookup(&t, text[i]); res_length += UTF8_LENGTH(c); } res = g_try_malloc(res_length + 1); if (res == NULL) goto error; out = res; i = 0; while (out < res + res_length) { unsigned short c; if (text[i] == 0x1b) { c = gsm_single_shift_lookup(&t, text[++i]); if (c == GUND) c = gsm_locking_shift_lookup(&t, text[i]); } else c = gsm_locking_shift_lookup(&t, text[i]); out += g_unichar_to_utf8(c, out); ++i; } *out = '\0'; if (items_written) *items_written = out - res; error: if (items_read) *items_read = i; return res; } char *convert_gsm_to_utf8(const unsigned char *text, long len, long *items_read, long *items_written, unsigned char terminator) { return convert_gsm_to_utf8_with_lang(text, len, items_read, items_written, terminator, GSM_DIALECT_DEFAULT, GSM_DIALECT_DEFAULT); } /*! * Converts UTF-8 encoded text to GSM alphabet. The result is unpacked, * with the 7th bit always 0. If terminator is not 0, a terminator character * is appended to the result. This should be in the range 0x80-0xf0 * * Returns the encoded data or NULL if the data could not be encoded. The * data must be freed by the caller. If items_read is not NULL, it contains * the actual number of bytes read. If items_written is not NULL, contains * the number of bytes written. */ unsigned char *convert_utf8_to_gsm_with_lang(const char *text, long len, long *items_read, long *items_written, unsigned char terminator, enum gsm_dialect locking_lang, enum gsm_dialect single_lang) { struct conversion_table t; long nchars = 0; const char *in; unsigned char *out; unsigned char *res = NULL; long res_len; long i; if (conversion_table_init(&t, locking_lang, single_lang) == FALSE) return NULL; in = text; res_len = 0; while ((len < 0 || text + len - in > 0) && *in) { long max = len < 0 ? 6 : text + len - in; gunichar c = g_utf8_get_char_validated(in, max); unsigned short converted = GUND; if (c & 0x80000000) goto err_out; if (c > 0xffff) goto err_out; converted = unicode_locking_shift_lookup(&t, c); if (converted == GUND) converted = unicode_single_shift_lookup(&t, c); if (converted == GUND) goto err_out; if (converted & 0x1b00) res_len += 2; else res_len += 1; in = g_utf8_next_char(in); nchars += 1; } res = g_try_malloc(res_len + (terminator ? 1 : 0)); if (res == NULL) goto err_out; in = text; out = res; for (i = 0; i < nchars; i++) { unsigned short converted; gunichar c = g_utf8_get_char(in); converted = unicode_locking_shift_lookup(&t, c); if (converted == GUND) converted = unicode_single_shift_lookup(&t, c); if (converted & 0x1b00) { *out = 0x1b; ++out; } *out = converted; ++out; in = g_utf8_next_char(in); } if (terminator) *out = terminator; if (items_written) *items_written = out - res; err_out: if (items_read) *items_read = in - text; return res; } unsigned char *convert_utf8_to_gsm(const char *text, long len, long *items_read, long *items_written, unsigned char terminator) { return convert_utf8_to_gsm_with_lang(text, len, items_read, items_written, terminator, GSM_DIALECT_DEFAULT, GSM_DIALECT_DEFAULT); } /*! * Converts UTF-8 encoded text to GSM alphabet. It finds an encoding * that uses the minimum set of GSM dialects based on the hint given. * * It first attempts to use the default dialect's single shift and * locking shift tables. It then tries with only the single shift * table of the hinted dialect, and finally with both the single shift * and locking shift tables of the hinted dialect. * * Returns the encoded data or NULL if no suitable encoding could be * found. The data must be freed by the caller. If items_read is not * NULL, it contains the actual number of bytes read. If items_written * is not NULL, it contains the number of bytes written. If * used_locking and used_single are not NULL, they will contain the * dialects used for the locking shift and single shift tables. */ unsigned char *convert_utf8_to_gsm_best_lang(const char *utf8, long len, long *items_read, long *items_written, unsigned char terminator, enum gsm_dialect hint, enum gsm_dialect *used_locking, enum gsm_dialect *used_single) { enum gsm_dialect locking = GSM_DIALECT_DEFAULT; enum gsm_dialect single = GSM_DIALECT_DEFAULT; unsigned char *encoded; encoded = convert_utf8_to_gsm_with_lang(utf8, len, items_read, items_written, terminator, locking, single); if (encoded != NULL) goto out; if (hint == GSM_DIALECT_DEFAULT) return NULL; single = hint; encoded = convert_utf8_to_gsm_with_lang(utf8, len, items_read, items_written, terminator, locking, single); if (encoded != NULL) goto out; /* Spanish dialect uses the default locking shift table */ if (hint == GSM_DIALECT_SPANISH) return NULL; locking = hint; encoded = convert_utf8_to_gsm_with_lang(utf8, len, items_read, items_written, terminator, locking, single); if (encoded == NULL) return NULL; out: if (used_locking != NULL) *used_locking = locking; if (used_single != NULL) *used_single = single; return encoded; } /*! * Decodes the hex encoded data and converts to a byte array. If terminator * is not 0, the terminator character is appended to the end of the result. * This might be useful for converting GSM encoded data if the CSCS is set * to HEX. * * Please note that this since GSM does allow embedded null characeters, use * of the terminator or the items_writen is encouraged to find the real size * of the result. */ unsigned char *decode_hex_own_buf(const char *in, long len, long *items_written, unsigned char terminator, unsigned char *buf) { long i, j; char c; unsigned char b; if (len < 0) len = strlen(in); len &= ~0x1; for (i = 0, j = 0; i < len; i++, j++) { c = toupper(in[i]); if (c >= '0' && c <= '9') b = c - '0'; else if (c >= 'A' && c <= 'F') b = 10 + c - 'A'; else return NULL; i += 1; c = toupper(in[i]); if (c >= '0' && c <= '9') b = b * 16 + c - '0'; else if (c >= 'A' && c <= 'F') b = b * 16 + 10 + c - 'A'; else return NULL; buf[j] = b; } if (terminator) buf[j] = terminator; if (items_written) *items_written = j; return buf; } unsigned char *decode_hex(const char *in, long len, long *items_written, unsigned char terminator) { long i; char c; unsigned char *buf; if (len < 0) len = strlen(in); len &= ~0x1; for (i = 0; i < len; i++) { c = toupper(in[i]); if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) continue; return NULL; } buf = g_new(unsigned char, (len >> 1) + (terminator ? 1 : 0)); return decode_hex_own_buf(in, len, items_written, terminator, buf); } /*! * Encodes the data using hexadecimal characters. len can be negative, * in that case the terminator is used to find the last character. This is * useful for handling GSM-encoded strings which allow ASCII NULL character * in the stream. */ char *encode_hex_own_buf(const unsigned char *in, long len, unsigned char terminator, char *buf) { long i, j; char c; if (len < 0) { i = 0; while (in[i] != terminator) i++; len = i; } for (i = 0, j = 0; i < len; i++, j++) { c = (in[i] >> 4) & 0xf; if (c <= 9) buf[j] = '0' + c; else buf[j] = 'A' + c - 10; j += 1; c = (in[i]) & 0xf; if (c <= 9) buf[j] = '0' + c; else buf[j] = 'A' + c - 10; } buf[j] = '\0'; return buf; } char *encode_hex(const unsigned char *in, long len, unsigned char terminator) { char *buf; int i; if (len < 0) { i = 0; while (in[i] != terminator) i++; len = i; } buf = g_new(char, len * 2 + 1); return encode_hex_own_buf(in, len, terminator, buf); } unsigned char *unpack_7bit_own_buf(const unsigned char *in, long len, int byte_offset, gboolean ussd, long max_to_unpack, long *items_written, unsigned char terminator, unsigned char *buf) { unsigned char rest = 0; unsigned char *out = buf; int bits = 7 - (byte_offset % 7); long i; if (len <= 0) return NULL; /* In the case of CB, unpack as much as possible */ if (ussd == TRUE) max_to_unpack = len * 8 / 7; for (i = 0; (i < len) && ((out-buf) < max_to_unpack); i++) { /* Grab what we have in the current octet */ *out = (in[i] & ((1 << bits) - 1)) << (7 - bits); /* Append what we have from the previous octet, if any */ *out |= rest; /* Figure out the remainder */ rest = (in[i] >> bits) & ((1 << (8-bits)) - 1); /* * We have the entire character, here we don't increate * out if this is we started at an offset. Instead * we effectively populate variable rest */ if (i != 0 || bits == 7) out++; if ((out-buf) == max_to_unpack) break; /* * We expected only 1 bit from this octet, means there's 7 * left, take care of them here */ if (bits == 1) { *out = rest; out++; bits = 7; rest = 0; } else { bits = bits - 1; } } /* * According to 23.038 6.1.2.3.1, last paragraph: * "If the total number of characters to be sent equals (8n-1) * where n=1,2,3 etc. then there are 7 spare bits at the end * of the message. To avoid the situation where the receiving * entity confuses 7 binary zero pad bits as the @ character, * the carriage return or character shall be used for * padding in this situation, just as for Cell Broadcast." * * "The receiving entity shall remove the final character where * the message ends on an octet boundary with as the last * character. */ if (ussd && (((out - buf) % 8) == 0) && (*(out - 1) == '\r')) out = out - 1; if (terminator) *out = terminator; if (items_written) *items_written = out - buf; return buf; } unsigned char *unpack_7bit(const unsigned char *in, long len, int byte_offset, gboolean ussd, long max_to_unpack, long *items_written, unsigned char terminator) { unsigned char *buf = g_new(unsigned char, len * 8 / 7 + (terminator ? 1 : 0)); return unpack_7bit_own_buf(in, len, byte_offset, ussd, max_to_unpack, items_written, terminator, buf); } unsigned char *pack_7bit_own_buf(const unsigned char *in, long len, int byte_offset, gboolean ussd, long *items_written, unsigned char terminator, unsigned char *buf) { int bits = 7 - (byte_offset % 7); unsigned char *out = buf; long i; long total_bits; if (len == 0) return NULL; if (len < 0) { i = 0; while (in[i] != terminator) i++; len = i; } total_bits = len * 7; if (bits != 7) { total_bits += bits; bits = bits - 1; *out = 0; } for (i = 0; i < len; i++) { if (bits != 7) { *out |= (in[i] & ((1 << (7 - bits)) - 1)) << (bits + 1); out++; } /* This is a no op when bits == 0, lets keep valgrind happy */ if (bits != 0) *out = in[i] >> (7 - bits); if (bits == 0) bits = 7; else bits = bits - 1; } /* * If is intended to be the last character and the message * (including the wanted ) ends on an octet boundary, then * another must be added together with a padding bit 0. The * receiving entity will perform the carriage return function twice, * but this will not result in misoperation as the definition of * in clause 6.1.1 is identical to the definition of . */ if (ussd && ((total_bits % 8) == 1)) *out |= '\r' << 1; if (bits != 7) out++; if (ussd && ((total_bits % 8) == 0) && (in[len - 1] == '\r')) { *out = '\r'; out++; } if (items_written) *items_written = out - buf; return buf; } unsigned char *pack_7bit(const unsigned char *in, long len, int byte_offset, gboolean ussd, long *items_written, unsigned char terminator) { int bits = 7 - (byte_offset % 7); long i; long total_bits; unsigned char *buf; if (len == 0 || items_written == NULL) return NULL; if (len < 0) { i = 0; while (in[i] != terminator) i++; len = i; } total_bits = len * 7; if (bits != 7) total_bits += bits; /* Round up number of bytes, must append if true */ if (ussd && ((total_bits % 8) == 0) && (in[len - 1] == '\r')) buf = g_new(unsigned char, (total_bits + 14) / 8); else buf = g_new(unsigned char, (total_bits + 7) / 8); return pack_7bit_own_buf(in, len, byte_offset, ussd, items_written, terminator, buf); } char *sim_string_to_utf8(const unsigned char *buffer, int length) { struct conversion_table t; int i; int j; int num_chars; unsigned short ucs2_offset; int res_len; int offset; char *utf8 = NULL; char *out; if (conversion_table_init(&t, GSM_DIALECT_DEFAULT, GSM_DIALECT_DEFAULT) == FALSE) return NULL; if (length < 1) return NULL; if (buffer[0] < 0x80) { /* * We have to find the real length, since on SIM file system * alpha fields are 0xff padded */ for (i = 0; i < length; i++) if (buffer[i] == 0xff) break; return convert_gsm_to_utf8(buffer, i, NULL, NULL, 0); } switch (buffer[0]) { case 0x80: if (((length - 1) % 2) == 1) { if (buffer[length - 1] != 0xff) return NULL; length = length - 1; } for (i = 1; i < length; i += 2) if (buffer[i] == 0xff && buffer[i + 1] == 0xff) break; return g_convert((char *) buffer + 1, i - 1, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); case 0x81: if (length < 3 || (buffer[1] > (length - 3))) return NULL; num_chars = buffer[1]; ucs2_offset = buffer[2] << 7; offset = 3; break; case 0x82: if (length < 4 || buffer[1] > length - 4) return NULL; num_chars = buffer[1]; ucs2_offset = (buffer[2] << 8) | buffer[3]; offset = 4; break; case 0xff: /* Special case of empty string */ num_chars = 0; ucs2_offset = 0; offset = 0; break; default: return NULL; } res_len = 0; i = offset; j = 0; while ((i < length) && (j < num_chars)) { unsigned short c; if (buffer[i] & 0x80) { c = (buffer[i++] & 0x7f) + ucs2_offset; if (c >= 0xd800 && c < 0xe000) return NULL; res_len += UTF8_LENGTH(c); j += 1; continue; } if (buffer[i] == 0x1b) { ++i; if (i >= length) return NULL; c = gsm_single_shift_lookup(&t, buffer[i++]); if (c == 0) return NULL; j += 2; } else { c = gsm_locking_shift_lookup(&t, buffer[i++]); j += 1; } res_len += UTF8_LENGTH(c); } if (j != num_chars) return NULL; /* Check that the string is padded out to the length by 0xff */ for (; i < length; i++) if (buffer[i] != 0xff) return NULL; utf8 = g_try_malloc(res_len + 1); if (utf8 == NULL) return NULL; i = offset; out = utf8; while (out < utf8 + res_len) { unsigned short c; if (buffer[i] & 0x80) c = (buffer[i++] & 0x7f) + ucs2_offset; else if (buffer[i] == 0x1b) { ++i; c = gsm_single_shift_lookup(&t, buffer[i++]); } else c = gsm_locking_shift_lookup(&t, buffer[i++]); out += g_unichar_to_utf8(c, out); } *out = '\0'; return utf8; } unsigned char *utf8_to_sim_string(const char *utf, int max_length, int *out_length) { unsigned char *result; unsigned char *ucs2; long gsm_bytes; gsize converted; result = convert_utf8_to_gsm(utf, -1, NULL, &gsm_bytes, 0); if (result) { if (gsm_bytes > max_length) { gsm_bytes = max_length; while (gsm_bytes && result[gsm_bytes - 1] == 0x1b) gsm_bytes -= 1; } *out_length = gsm_bytes; return result; } /* NOTE: UCS2 formats with an offset are never used */ ucs2 = (guint8 *) g_convert(utf, -1, "UCS-2BE//TRANSLIT", "UTF-8", NULL, &converted, NULL); if (ucs2 == NULL) return NULL; if (max_length != -1 && (int) converted + 1 > max_length) converted = (max_length - 1) & ~1; result = g_try_malloc(converted + 1); if (result == NULL) { g_free(ucs2); return NULL; } *out_length = converted + 1; result[0] = 0x80; memcpy(&result[1], ucs2, converted); g_free(ucs2); return result; } /*! * Converts UCS2 encoded text to GSM alphabet. The result is unpacked, * with the 7th bit always 0. If terminator is not 0, a terminator character * is appended to the result. * * Returns the encoded data or NULL if the data could not be encoded. The * data must be freed by the caller. If items_read is not NULL, it contains * the actual number of bytes read. If items_written is not NULL, contains * the number of bytes written. */ unsigned char *convert_ucs2_to_gsm_with_lang(const unsigned char *text, long len, long *items_read, long *items_written, unsigned char terminator, enum gsm_dialect locking_lang, enum gsm_dialect single_lang) { struct conversion_table t; long nchars = 0; const unsigned char *in; unsigned char *out; unsigned char *res = NULL; long res_len; long i; if (conversion_table_init(&t, locking_lang, single_lang) == FALSE) return NULL; if (len < 1 || len % 2) return NULL; in = text; res_len = 0; for (i = 0; i < len; i += 2) { gunichar c = (in[i] << 8) | in[i + 1]; unsigned short converted = GUND; if (c > 0xffff) goto err_out; converted = unicode_locking_shift_lookup(&t, c); if (converted == GUND) converted = unicode_single_shift_lookup(&t, c); if (converted == GUND) goto err_out; if (converted & 0x1b00) res_len += 2; else res_len += 1; nchars += 1; } res = g_try_malloc(res_len + (terminator ? 1 : 0)); if (res == NULL) goto err_out; in = text; out = res; for (i = 0; i < len; i += 2) { gunichar c = (in[i] << 8) | in[i + 1]; unsigned short converted = GUND; converted = unicode_locking_shift_lookup(&t, c); if (converted == GUND) converted = unicode_single_shift_lookup(&t, c); if (converted & 0x1b00) { *out = 0x1b; ++out; } *out = converted; ++out; } if (terminator) *out = terminator; if (items_written) *items_written = out - res; err_out: if (items_read) *items_read = i; return res; } unsigned char *convert_ucs2_to_gsm(const unsigned char *text, long len, long *items_read, long *items_written, unsigned char terminator) { return convert_ucs2_to_gsm_with_lang(text, len, items_read, items_written, terminator, GSM_DIALECT_DEFAULT, GSM_DIALECT_DEFAULT); } ofono-1.17.bzr6912+16.04.20160314.3/src/ofono.service.in0000644000015600001650000000027312671500024022277 0ustar pbuserpbgroup00000000000000[Unit] Description=Telephony service After=syslog.target [Service] Type=dbus BusName=org.ofono ExecStart=@prefix@/sbin/ofonod -n StandardError=null [Install] WantedBy=multi-user.target ofono-1.17.bzr6912+16.04.20160314.3/src/private-network.c0000644000015600001650000000366012671500024022500 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include "ofono.h" static GSList *g_drivers = NULL; void __ofono_private_network_release(int id) { GSList *d; DBG(""); for (d = g_drivers; d; d = d->next) { const struct ofono_private_network_driver *driver = d->data; if (!driver->release) continue; driver->release(id); break; } } ofono_bool_t __ofono_private_network_request(ofono_private_network_cb_t cb, int *id, void *data) { GSList *d; int uid; DBG(""); for (d = g_drivers; d; d = d->next) { const struct ofono_private_network_driver *driver = d->data; if (!driver->request) continue; uid = driver->request(cb, data); if (uid <= 0) continue; *id = uid; return TRUE; } return FALSE; } int ofono_private_network_driver_register( const struct ofono_private_network_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_private_network_driver_unregister( const struct ofono_private_network_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } ofono-1.17.bzr6912+16.04.20160314.3/src/cdma-sms.c0000644000015600001650000002014312671500024021036 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include "ofono.h" #include "cdma-smsutil.h" static GSList *g_drivers; struct ofono_cdma_sms { const struct ofono_cdma_sms_driver *driver; void *driver_data; struct ofono_atom *atom; }; static const GDBusMethodTable cdma_sms_manager_methods[] = { /* TODO */ { } }; static const GDBusSignalTable cdma_sms_manager_signals[] = { { GDBUS_SIGNAL("IncomingMessage", GDBUS_ARGS({ "message", "s"}, { "info", "a{sv}" })) }, /* TODO */ { } }; static void cdma_dispatch_text_message(struct ofono_cdma_sms *cdma_sms, const char *message, const char *oaddr) { const char *path = __ofono_atom_get_path(cdma_sms->atom); DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *signal; DBusMessageIter iter; DBusMessageIter dict; const char *signal_name; /* TODO: Support ImmediateMessage */ signal_name = "IncomingMessage"; signal = dbus_message_new_signal(path, OFONO_CDMA_MESSAGE_MANAGER_INTERFACE, signal_name); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &message); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "Sender", DBUS_TYPE_STRING, &oaddr); /* TODO: Other properties not supported yet */ dbus_message_iter_close_container(&iter, &dict); g_dbus_send_message(conn, signal); /*TODO: Add the message to history*/ } static void ofono_cdma_sms_process_wmt_deliver(struct ofono_cdma_sms *cdma_sms, const struct cdma_sms *incoming) { char *message; const char *oaddr; const struct cdma_sms_ud *ud; ud = &incoming->p2p_msg.bd.wmt_deliver.ud; /* * If incoming message does not contain USER DATA, still * send indication to upper layer but with empty string. */ if (check_bitmap(incoming->p2p_msg.bd.subparam_bitmap, CDMA_SMS_SUBPARAM_ID_USER_DATA) == FALSE) message = g_new0(char, 1); else message = cdma_sms_decode_text(ud); if (message == NULL) return; oaddr = cdma_sms_address_to_string(&incoming->p2p_msg.oaddr); if (oaddr == NULL) { g_free(message); return; } cdma_dispatch_text_message(cdma_sms, message, oaddr); g_free(message); } static void ofono_cdma_sms_process_wmt(struct ofono_cdma_sms *cdma_sms, struct cdma_sms *incoming) { /* TODO: Add duplicate detection support */ switch (incoming->p2p_msg.bd.id.msg_type) { case CDMA_SMS_MSG_TYPE_RESERVED: break; case CDMA_SMS_MSG_TYPE_DELIVER: ofono_cdma_sms_process_wmt_deliver(cdma_sms, incoming); break; case CDMA_SMS_MSG_TYPE_SUBMIT: case CDMA_SMS_MSG_TYPE_CANCEL: case CDMA_SMS_MSG_TYPE_DELIVER_ACK: case CDMA_SMS_MSG_TYPE_USER_ACK: case CDMA_SMS_MSG_TYPE_READ_ACK: case CDMA_SMS_MSG_TYPE_DELIVER_REPORT: case CDMA_SMS_MSG_TYPE_SUBMIT_REPORT: /* TODO */ break; } } static void ofono_cdma_sms_process_p2p(struct ofono_cdma_sms *cdma_sms, struct cdma_sms *incoming) { switch (incoming->p2p_msg.teleservice_id) { case CDMA_SMS_TELESERVICE_ID_CMT91: case CDMA_SMS_TELESERVICE_ID_WPT: break; /* TODO: Not supported yet */ case CDMA_SMS_TELESERVICE_ID_WMT: ofono_cdma_sms_process_wmt(cdma_sms, incoming); break; case CDMA_SMS_TELESERVICE_ID_VMN: case CDMA_SMS_TELESERVICE_ID_WAP: case CDMA_SMS_TELESERVICE_ID_WEMT: case CDMA_SMS_TELESERVICE_ID_SCPT: case CDMA_SMS_TELESERVICE_ID_CATPT: break; /* TODO: Not supported yet */ } } void ofono_cdma_sms_deliver_notify(struct ofono_cdma_sms *cdma_sms, const unsigned char *pdu, int tpdu_len) { static struct cdma_sms s; DBG("tpdu len %d", tpdu_len); memset(&s, 0, sizeof(struct cdma_sms)); if (cdma_sms_decode(pdu, tpdu_len, &s) == FALSE) return; switch (s.type) { case CDMA_SMS_TP_MSG_TYPE_P2P: ofono_cdma_sms_process_p2p(cdma_sms, &s); break; case CDMA_SMS_TP_MSG_TYPE_BCAST: case CDMA_SMS_TP_MSG_TYPE_ACK: /* * TODO: Support SMS Broadcast Message and SMS * Acknowledge Message. */ break; } } int ofono_cdma_sms_driver_register(const struct ofono_cdma_sms_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *)d); return 0; } void ofono_cdma_sms_driver_unregister(const struct ofono_cdma_sms_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *)d); } static void cdma_sms_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); g_dbus_unregister_interface(conn, path, OFONO_CDMA_MESSAGE_MANAGER_INTERFACE); ofono_modem_remove_interface(modem, OFONO_CDMA_MESSAGE_MANAGER_INTERFACE); } static void cdma_sms_remove(struct ofono_atom *atom) { struct ofono_cdma_sms *cdma_sms = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (cdma_sms == NULL) return; if (cdma_sms->driver && cdma_sms->driver->remove) cdma_sms->driver->remove(cdma_sms); g_free(cdma_sms); } /* * Create a CDMA SMS driver * * This creates a CDMA SMS driver that is hung off a @modem * object. However, for the driver to be used by the system, it has to * be registered with the oFono core using ofono_sms_register(). * * This is done once the modem driver determines that SMS is properly * supported by the hardware. */ struct ofono_cdma_sms *ofono_cdma_sms_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_cdma_sms *cdma_sms; GSList *l; if (driver == NULL) return NULL; cdma_sms = g_try_new0(struct ofono_cdma_sms, 1); if (cdma_sms == NULL) return NULL; cdma_sms->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CDMA_SMS, cdma_sms_remove, cdma_sms); for (l = g_drivers; l; l = l->next) { const struct ofono_cdma_sms_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(cdma_sms, vendor, data) < 0) continue; cdma_sms->driver = drv; break; } return cdma_sms; } /* * Indicate oFono that a CDMA SMS driver is ready for operation * * This is called after ofono_cdma_sms_create() was done and the modem * driver determined that a modem supports SMS correctly. Once this * call succeeds, the D-BUS interface for SMS goes live. */ void ofono_cdma_sms_register(struct ofono_cdma_sms *cdma_sms) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(cdma_sms->atom); const char *path = __ofono_atom_get_path(cdma_sms->atom); if (!g_dbus_register_interface(conn, path, OFONO_CDMA_MESSAGE_MANAGER_INTERFACE, cdma_sms_manager_methods, cdma_sms_manager_signals, NULL, cdma_sms, NULL)) { ofono_error("Could not create %s interface", OFONO_CDMA_MESSAGE_MANAGER_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_CDMA_MESSAGE_MANAGER_INTERFACE); __ofono_atom_register(cdma_sms->atom, cdma_sms_unregister); } void ofono_cdma_sms_remove(struct ofono_cdma_sms *cdma_sms) { __ofono_atom_free(cdma_sms->atom); } void ofono_cdma_sms_set_data(struct ofono_cdma_sms *cdma_sms, void *data) { cdma_sms->driver_data = data; } void *ofono_cdma_sms_get_data(struct ofono_cdma_sms *cdma_sms) { return cdma_sms->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/simutil.c0000644000015600001650000010652212671500024021026 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include "simutil.h" #include "util.h" #include "smsutil.h" struct sim_eons { struct sim_eons_operator_info *pnn_list; GSList *opl_list; gboolean pnn_valid; int pnn_max; }; struct spdi_operator { char mcc[OFONO_MAX_MCC_LENGTH + 1]; char mnc[OFONO_MAX_MNC_LENGTH + 1]; }; struct opl_operator { char mcc[OFONO_MAX_MCC_LENGTH + 1]; char mnc[OFONO_MAX_MNC_LENGTH + 1]; guint16 lac_tac_low; guint16 lac_tac_high; guint8 id; }; #define MF 1 #define DF 2 #define EF 4 #define BINARY 0 #define RECORD 1 #define CYCLIC 3 #define ALW 0 #define PIN 1 #define PIN2 2 #define ADM 4 #define NEV 15 #define ROOTMF 0x3F00 static struct sim_ef_info ef_db[] = { { 0x2F05, ROOTMF, ROOTMF, EF, BINARY, 0, ALW, PIN }, { 0x2FE2, ROOTMF, ROOTMF, EF, BINARY, 10, ALW, NEV }, { 0x4F20, 0x5F50, 0x5F50, EF, BINARY, 0, PIN, ADM }, { 0x4F30, 0x5F3A, 0x5F3A, EF, RECORD, 0, PIN, ADM }, { 0x5F3A, 0x7F10, 0x7F10, DF, 0, 0, PIN, PIN }, { 0x5F50, 0x7F10, 0x7F10, DF, 0, 0, PIN, ADM }, { 0x6F05, 0x7F20, 0x7FFF, EF, BINARY, 0, ALW, PIN }, { 0x6F07, 0x7F20, 0x7FFF, EF, BINARY, 9, PIN, ADM }, { 0x6F11, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, PIN }, { 0x6F13, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, PIN }, { 0x6F14, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, ADM }, { 0x6F15, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, PIN }, { 0x6F16, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, ADM }, { 0x6F17, 0x7F20, 0x7FFF, EF, RECORD, 0, PIN, PIN }, { 0x6F18, 0x7F20, 0x7FFF, EF, BINARY, 10, PIN, ADM }, { 0x6F19, 0x7F20, 0x7FFF, EF, RECORD, 0, PIN, PIN }, { 0x6F38, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, ADM }, { 0x6F3A, 0x7F10, 0x7F10, EF, RECORD, 0, PIN, PIN }, { 0x6F3B, 0x7F10, 0x7FFF, EF, RECORD, 0, PIN, PIN2 }, { 0x6F3E, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, ADM }, { 0x6F40, 0x7F10, 0x7FFF, EF, RECORD, 0, PIN, PIN }, { 0x6F45, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, PIN }, { 0x6F46, 0x7F20, 0x7FFF, EF, BINARY, 17, ALW, ADM }, { 0x6F48, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, ADM }, { 0x6F49, 0x7F10, 0x7FFF, EF, RECORD, 0, PIN, ADM }, { 0x6F4D, 0x7F20, 0x7FFF, EF, RECORD, 0, PIN, PIN2 }, { 0x6F50, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, PIN }, { 0x6F56, 0x0000, 0x7FFF, EF, BINARY, 0, PIN, PIN2 }, { 0x6FAD, 0x7F20, 0x7FFF, EF, BINARY, 0, ALW, ADM }, { 0x6FAE, 0x7F20, 0x0000, EF, BINARY, 1, ALW, ADM }, { 0x6FB7, 0x7F20, 0x7FFF, EF, BINARY, 0, ALW, ADM }, { 0x6FC5, 0x7F20, 0x7FFF, EF, RECORD, 0, ALW, ADM }, { 0x6FC6, 0x7F20, 0x7FFF, EF, RECORD, 0, ALW, ADM }, { 0x6FC7, 0x7F20, 0x7FFF, EF, RECORD, 0, PIN, PIN }, { 0x6FC9, 0x7F20, 0x7FFF, EF, RECORD, 0, PIN, PIN }, { 0x6FCA, 0x7F20, 0x7FFF, EF, RECORD, 0, PIN, PIN }, { 0x6FCB, 0x7F20, 0x7FFF, EF, RECORD, 16, PIN, PIN }, { 0x6FCD, 0x7F20, 0x7FFF, EF, BINARY, 0, PIN, ADM }, { 0x6FDE, 0x7F20, 0x7FFF, EF, BINARY, 0, ALW, ADM }, { 0x7F10, ROOTMF, ROOTMF, DF, 0, 0, 0, 0 }, { 0x7F20, ROOTMF, ROOTMF, DF, 0, 0, 0, 0 }, { 0x7FFF, 0x0000, ROOTMF, DF, 0, 0, 0, 0 } }; void simple_tlv_iter_init(struct simple_tlv_iter *iter, const unsigned char *pdu, unsigned int len) { iter->pdu = pdu; iter->max = len; iter->pos = 0; iter->tag = 0; iter->len = 0; iter->data = NULL; } gboolean simple_tlv_iter_next(struct simple_tlv_iter *iter) { const unsigned char *pdu = iter->pdu + iter->pos; const unsigned char *end = iter->pdu + iter->max; unsigned char tag; unsigned short len; if (pdu == end) return FALSE; tag = *pdu; pdu++; /* * ISO 7816-4, Section 5.2.1: * * The tag field consists of a single byte encoding a tag number from * 1 to 254. The values 00 and FF are invalid for tag fields. * * The length field consists of one or three consecutive bytes. * - If the first byte is not set to FF, then the length field * consists of a single byte encoding a number from zero to * 254 and denoted N. * - If the first byte is set to FF, then the length field * continues on the subsequent two bytes with any value * encoding a number from zero to 65535 and denoted N * * If N is zero, there is no value field, i.e. data object is empty. */ if (pdu == end) return FALSE; len = *pdu++; if (len == 0xFF) { if ((pdu + 2) > end) return FALSE; len = (pdu[0] << 8) | pdu[1]; pdu += 2; } if (pdu + len > end) return FALSE; iter->tag = tag; iter->len = len; iter->data = pdu; iter->pos = pdu + len - iter->pdu; return TRUE; } unsigned char simple_tlv_iter_get_tag(struct simple_tlv_iter *iter) { return iter->tag; } unsigned short simple_tlv_iter_get_length(struct simple_tlv_iter *iter) { return iter->len; } const unsigned char *simple_tlv_iter_get_data(struct simple_tlv_iter *iter) { return iter->data; } void comprehension_tlv_iter_init(struct comprehension_tlv_iter *iter, const unsigned char *pdu, unsigned int len) { iter->pdu = pdu; iter->max = len; iter->pos = 0; iter->tag = 0; iter->cr = FALSE; iter->data = 0; } /* Comprehension TLVs defined in Section 7 of ETSI TS 101.220 */ gboolean comprehension_tlv_iter_next(struct comprehension_tlv_iter *iter) { const unsigned char *pdu = iter->pdu + iter->pos; const unsigned char *end = iter->pdu + iter->max; unsigned short tag; unsigned short len; gboolean cr; if (pdu == end) return FALSE; if (*pdu == 0x00 || *pdu == 0xFF || *pdu == 0x80) return FALSE; cr = bit_field(*pdu, 7, 1); tag = bit_field(*pdu, 0, 7); pdu++; /* * ETSI TS 101.220, Section 7.1.1.2 * * If byte 1 of the tag is equal to 0x7F, then the tag is encoded * on the following two bytes, with bit 8 of the 2nd byte of the tag * being the CR flag. */ if (tag == 0x7F) { if ((pdu + 2) > end) return FALSE; cr = bit_field(pdu[0], 7, 1); tag = ((pdu[0] & 0x7f) << 8) | pdu[1]; if (tag < 0x0001 || tag > 0x7fff) return FALSE; pdu += 2; } if (pdu == end) return FALSE; len = *pdu++; if (len >= 0x80) { unsigned int extended_bytes = len - 0x80; unsigned int i; if (extended_bytes == 0 || extended_bytes > 3) return FALSE; if ((pdu + extended_bytes) > end) return FALSE; if (pdu[0] == 0) return FALSE; for (len = 0, i = 0; i < extended_bytes; i++) len = (len << 8) | *pdu++; } if (pdu + len > end) return FALSE; iter->tag = tag; iter->cr = cr; iter->len = len; iter->data = pdu; iter->pos = pdu + len - iter->pdu; return TRUE; } unsigned short comprehension_tlv_iter_get_tag( struct comprehension_tlv_iter *iter) { return iter->tag; } gboolean comprehension_tlv_get_cr(struct comprehension_tlv_iter *iter) { return iter->cr; } unsigned int comprehension_tlv_iter_get_length( struct comprehension_tlv_iter *iter) { return iter->len; } const unsigned char *comprehension_tlv_iter_get_data( struct comprehension_tlv_iter *iter) { return iter->data; } void comprehension_tlv_iter_copy(struct comprehension_tlv_iter *from, struct comprehension_tlv_iter *to) { to->max = from->max; to->pos = from->pos; to->pdu = from->pdu; to->tag = from->tag; to->cr = from->cr; to->len = from->len; to->data = from->data; } void ber_tlv_iter_init(struct ber_tlv_iter *iter, const unsigned char *pdu, unsigned int len) { iter->pdu = pdu; iter->max = len; iter->pos = 0; } unsigned int ber_tlv_iter_get_tag(struct ber_tlv_iter *iter) { return iter->tag; } enum ber_tlv_data_type ber_tlv_iter_get_class(struct ber_tlv_iter *iter) { return iter->class; } enum ber_tlv_data_encoding_type ber_tlv_iter_get_encoding(struct ber_tlv_iter *iter) { return iter->encoding; } unsigned char ber_tlv_iter_get_short_tag(struct ber_tlv_iter *iter) { if (iter->tag > 30) return 0; return iter->tag | (iter->encoding << 5) | (iter->class << 6); } unsigned int ber_tlv_iter_get_length(struct ber_tlv_iter *iter) { return iter->len; } const unsigned char *ber_tlv_iter_get_data(struct ber_tlv_iter *iter) { return iter->data; } /* BER TLV structure is defined in ISO/IEC 7816-4 */ gboolean ber_tlv_iter_next(struct ber_tlv_iter *iter) { const unsigned char *pdu = iter->pdu + iter->pos; const unsigned char *end = iter->pdu + iter->max; unsigned int tag; unsigned int len; enum ber_tlv_data_type class; enum ber_tlv_data_encoding_type encoding; while ((pdu < end) && (*pdu == 0x00 || *pdu == 0xff)) pdu++; if (pdu == end) return FALSE; class = bit_field(*pdu, 6, 2); encoding = bit_field(*pdu, 5, 1); tag = bit_field(*pdu, 0, 5); pdu++; /* * ISO 7816-4, Section 5.2.2.1: * "If bits 5 to 1 of the first byte of the tag are not * all set to 1, then they encode a tag number from zero * to thirty and the tag field consists of a single byte. * * Otherwise, the tag field continues on one or more * subsequent bytes * - Bit 8 of each subsequent byte shall be set to 1, * unless it is the last subsequent byte * - Bits 7 to 1 of the first subsequent byte shall not be * all set to 0 * - Bits 7 to 1 of the first subsequent byte, followed by * bits 7 to 1 of each further subsequent byte, up to * and including bits 7 to 1 of the last subsequent * byte encode a tag number. */ if (tag == 0x1f) { if (pdu == end) return FALSE; /* First byte of the extended tag cannot contain 0 */ if ((*pdu & 0x7f) == 0) return FALSE; tag = 0; while ((pdu < end) && (*pdu & 0x80)) { tag = (tag << 7) | (*pdu & 0x7f); pdu++; } if (pdu == end) return FALSE; tag = (tag << 7) | *pdu; pdu++; } if (pdu == end) return FALSE; len = *pdu++; if (len >= 0x80) { unsigned int extended_bytes = len - 0x80; unsigned int i; if (extended_bytes == 0 || extended_bytes > 4) return FALSE; if ((pdu + extended_bytes) > end) return FALSE; if (pdu[0] == 0) return FALSE; for (len = 0, i = 0; i < extended_bytes; i++) len = (len << 8) | *pdu++; } if (pdu + len > end) return FALSE; iter->tag = tag; iter->class = class; iter->encoding = encoding; iter->len = len; iter->data = pdu; iter->pos = pdu + len - iter->pdu; return TRUE; } void ber_tlv_iter_recurse(struct ber_tlv_iter *iter, struct ber_tlv_iter *recurse) { recurse->pdu = iter->data; recurse->max = iter->len; recurse->pos = 0; } void ber_tlv_iter_recurse_simple(struct ber_tlv_iter *iter, struct simple_tlv_iter *container) { simple_tlv_iter_init(container, iter->data, iter->len); } void ber_tlv_iter_recurse_comprehension(struct ber_tlv_iter *iter, struct comprehension_tlv_iter *recurse) { comprehension_tlv_iter_init(recurse, iter->data, iter->len); } static const guint8 *ber_tlv_find_by_tag(const guint8 *pdu, guint8 in_tag, int in_len, int *out_len) { struct ber_tlv_iter iter; ber_tlv_iter_init(&iter, pdu, in_len); while (ber_tlv_iter_next(&iter)) { if (ber_tlv_iter_get_short_tag(&iter) != in_tag) continue; if (out_len) *out_len = ber_tlv_iter_get_length(&iter); return ber_tlv_iter_get_data(&iter); } return NULL; } #define MAX_BER_TLV_HEADER 8 gboolean ber_tlv_builder_init(struct ber_tlv_builder *builder, unsigned char *pdu, unsigned int size) { if (size < MAX_BER_TLV_HEADER) return FALSE; builder->pdu = pdu; builder->pos = 0; builder->max = size; builder->parent = NULL; builder->tag = 0xff; builder->len = 0; return TRUE; } #define BTLV_LEN_FIELD_SIZE_NEEDED(a) \ ((a) <= 0x7f ? 1 : \ ((a) <= 0xff ? 2 : \ ((a) <= 0xffff ? 3 : \ ((a) <= 0xffffff ? 4 : 5)))) #define BTLV_TAG_FIELD_SIZE_NEEDED(a) \ ((a) <= 0x1e ? 1 : \ ((a) <= 0x7f ? 2 : 3)) static void ber_tlv_builder_write_header(struct ber_tlv_builder *builder) { int tag_size = BTLV_TAG_FIELD_SIZE_NEEDED(builder->tag); int len_size = BTLV_LEN_FIELD_SIZE_NEEDED(builder->len); int offset = MAX_BER_TLV_HEADER - tag_size - len_size; unsigned char *pdu = builder->pdu + builder->pos; /* Pad with stuff bytes */ memset(pdu, 0xff, offset); /* Write the tag */ pdu[offset++] = (builder->class << 6) | (builder->encoding << 5) | (tag_size == 1 ? builder->tag : 0x1f); if (tag_size == 3) pdu[offset++] = 0x80 | (builder->tag >> 7); if (tag_size > 2) pdu[offset++] = builder->tag & 0x7f; /* Write the length */ if (len_size > 1) { int i; pdu[offset++] = 0x80 + len_size - 1; for (i = len_size - 2; i >= 0; i--) pdu[offset++] = (builder->len >> (i * 8)) & 0xff; } else pdu[offset++] = builder->len; } gboolean ber_tlv_builder_next(struct ber_tlv_builder *builder, enum ber_tlv_data_type class, enum ber_tlv_data_encoding_type encoding, unsigned int new_tag) { if (builder->tag != 0xff) { ber_tlv_builder_write_header(builder); builder->pos += MAX_BER_TLV_HEADER + builder->len; } if (ber_tlv_builder_set_length(builder, 0) == FALSE) return FALSE; builder->class = class; builder->encoding = encoding; builder->tag = new_tag; return TRUE; } /* * Resize the TLV because the content of Value field needs more space. * If this TLV is part of another TLV, resize that one too. */ gboolean ber_tlv_builder_set_length(struct ber_tlv_builder *builder, unsigned int new_len) { unsigned int new_pos = builder->pos + MAX_BER_TLV_HEADER + new_len; if (new_pos > builder->max) return FALSE; if (builder->parent) ber_tlv_builder_set_length(builder->parent, new_pos); builder->len = new_len; return TRUE; } unsigned char *ber_tlv_builder_get_data(struct ber_tlv_builder *builder) { return builder->pdu + builder->pos + MAX_BER_TLV_HEADER; } gboolean ber_tlv_builder_recurse(struct ber_tlv_builder *builder, struct ber_tlv_builder *recurse) { unsigned char *end = builder->pdu + builder->max; unsigned char *data = ber_tlv_builder_get_data(builder); if (ber_tlv_builder_init(recurse, data, end - data) == FALSE) return FALSE; recurse->parent = builder; return TRUE; } gboolean ber_tlv_builder_recurse_comprehension(struct ber_tlv_builder *builder, struct comprehension_tlv_builder *recurse) { unsigned char *end = builder->pdu + builder->max; unsigned char *data = ber_tlv_builder_get_data(builder); if (comprehension_tlv_builder_init(recurse, data, end - data) == FALSE) return FALSE; recurse->parent = builder; return TRUE; } void ber_tlv_builder_optimize(struct ber_tlv_builder *builder, unsigned char **out_pdu, unsigned int *out_len) { unsigned int len; unsigned char *pdu; ber_tlv_builder_write_header(builder); len = builder->pos + MAX_BER_TLV_HEADER + builder->len; for (pdu = builder->pdu; *pdu == 0xff; pdu++) len--; if (out_pdu) *out_pdu = pdu; if (out_len) *out_len = len; } gboolean comprehension_tlv_builder_init( struct comprehension_tlv_builder *builder, unsigned char *pdu, unsigned int size) { if (size < 2) return FALSE; builder->pdu = pdu; builder->pos = 0; builder->max = size; builder->parent = NULL; builder->len = 0; builder->pdu[0] = 0; return TRUE; } #define CTLV_TAG_FIELD_SIZE(a) \ bit_field((a), 0, 7) == 0x7f ? 3 : 1 \ #define CTLV_LEN_FIELD_SIZE(a) \ (a) >= 0x80 ? (a) - 0x7f : 1 \ gboolean comprehension_tlv_builder_next( struct comprehension_tlv_builder *builder, gboolean cr, unsigned short tag) { unsigned char *tlv = builder->pdu + builder->pos; unsigned int prev_size = 0; unsigned int new_size; /* Tag is invalid when we start, means we've just been inited */ if (tlv[0] != 0) { unsigned int tag_size = CTLV_TAG_FIELD_SIZE(tlv[0]); prev_size = builder->len + tag_size; prev_size += CTLV_LEN_FIELD_SIZE(tlv[tag_size]); } new_size = (tag < 0x7f ? 1 : 3) + 1; if (builder->pos + prev_size + new_size > builder->max) return FALSE; builder->pos += prev_size; if (tag >= 0x7f) { builder->pdu[builder->pos + 0] = 0x7f; builder->pdu[builder->pos + 1] = (cr ? 0x80 : 0) | (tag >> 8); builder->pdu[builder->pos + 2] = tag & 0xff; } else builder->pdu[builder->pos + 0] = (cr ? 0x80 : 0x00) | tag; builder->len = 0; builder->pdu[builder->pos + new_size - 1] = 0; /* Length */ return TRUE; } /* * Resize the TLV because the content of Value field needs more space. * If this TLV is part of another TLV, resize that one too. */ gboolean comprehension_tlv_builder_set_length( struct comprehension_tlv_builder *builder, unsigned int new_len) { unsigned char *tlv = builder->pdu + builder->pos; unsigned int tag_size = CTLV_TAG_FIELD_SIZE(tlv[0]); unsigned int len_size, new_len_size; unsigned int new_ctlv_len; unsigned int len; len_size = CTLV_LEN_FIELD_SIZE(tlv[tag_size]); new_len_size = BTLV_LEN_FIELD_SIZE_NEEDED(new_len); new_ctlv_len = tag_size + new_len_size + new_len; /* Check there is enough space */ if (builder->pos + new_ctlv_len > builder->max) return FALSE; if (builder->parent) ber_tlv_builder_set_length(builder->parent, builder->pos + new_ctlv_len); len = MIN(builder->len, new_len); if (len > 0 && new_len_size != len_size) memmove(tlv + tag_size + new_len_size, tlv + tag_size + len_size, len); builder->len = new_len; /* Write new length */ if (new_len_size > 1) { int i; unsigned int offset = tag_size; tlv[offset++] = 0x80 + new_len_size - 1; for (i = new_len_size - 2; i >= 0; i--) tlv[offset++] = (builder->len >> (i * 8)) & 0xff; } else tlv[tag_size] = builder->len; return TRUE; } unsigned char *comprehension_tlv_builder_get_data( struct comprehension_tlv_builder *builder) { unsigned char *tlv = builder->pdu + builder->pos; unsigned int tag_size = CTLV_TAG_FIELD_SIZE(*tlv); unsigned int len_size = CTLV_LEN_FIELD_SIZE(tlv[tag_size]); return tlv + tag_size + len_size; } static char *sim_network_name_parse(const unsigned char *buffer, int length, gboolean *add_ci) { char *ret = NULL; unsigned char dcs; int i; gboolean ci = FALSE; unsigned char *unpacked_buf; long num_char, written; int spare_bits; if (length < 2) return NULL; dcs = *buffer++; length--; /* * "The MS should add the letters for the Country's Initials and a * separator (e.g. a space)" */ if (is_bit_set(dcs, 4)) ci = TRUE; switch (dcs & (7 << 4)) { case 0x00: spare_bits = dcs & 0x07; num_char = (length * 8 - spare_bits) / 7; unpacked_buf = unpack_7bit(buffer, length, 0, FALSE, num_char, &written, 0); if (unpacked_buf == NULL) break; ret = convert_gsm_to_utf8(unpacked_buf, written, NULL, NULL, 0); g_free(unpacked_buf); break; case 0x10: if ((length % 2) == 1) { if (buffer[length - 1] != 0xff) return NULL; length = length - 1; } for (i = 0; i < length; i += 2) if (buffer[i] == 0xff && buffer[i + 1] == 0xff) break; ret = g_convert((const char *) buffer, length, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); break; } if (add_ci) *add_ci = ci; return ret; } void sim_parse_mcc_mnc(const guint8 *bcd, char *mcc, char *mnc) { static const char digit_lut[] = "0123456789*#abd\0"; guint8 digit; digit = (bcd[0] >> 0) & 0xf; *mcc++ = digit_lut[digit]; digit = (bcd[0] >> 4) & 0xf; *mcc++ = digit_lut[digit]; digit = (bcd[1] >> 0) & 0xf; *mcc++ = digit_lut[digit]; digit = (bcd[2] >> 0) & 0xf; *mnc++ = digit_lut[digit]; digit = (bcd[2] >> 4) & 0xf; *mnc++ = digit_lut[digit]; digit = (bcd[1] >> 4) & 0xf; *mnc++ = digit_lut[digit]; } static inline int to_semi_oct(char in) { int digit; switch (in) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': digit = in - '0'; break; case '*': digit = 10; break; case '#': digit = 11; break; case 'C': case 'c': digit = 12; break; case '?': digit = 13; break; case 'E': case 'e': digit = 14; break; default: digit = -1; break; } return digit; } void sim_encode_mcc_mnc(guint8 *out, const char *mcc, const char *mnc) { out[0] = to_semi_oct(mcc[0]); out[0] |= to_semi_oct(mcc[1]) << 4; out[1] = mcc[2] ? to_semi_oct(mcc[2]) : 0xf; out[1] |= (mnc[2] ? to_semi_oct(mnc[2]) : 0xf) << 4; out[2] = to_semi_oct(mnc[0]); out[2] |= to_semi_oct(mnc[1]) << 4; } static gint spdi_operator_compare(gconstpointer a, gconstpointer b) { const struct spdi_operator *opa = a; const struct spdi_operator *opb = b; gint r = strcmp(opa->mcc, opb->mcc); if (r) return r; return strcmp(opa->mnc, opb->mnc); } struct sim_spdi { GSList *operators; }; struct sim_spdi *sim_spdi_new(const guint8 *tlv, int length) { const guint8 *plmn_list_tlv; const guint8 *plmn_list; struct sim_spdi *spdi; struct spdi_operator *oper; int tlv_length; int list_length; if (length < 7) return NULL; plmn_list_tlv = ber_tlv_find_by_tag(tlv, 0xA3, length, &tlv_length); if (plmn_list_tlv == NULL) return NULL; plmn_list = ber_tlv_find_by_tag(plmn_list_tlv, 0x80, tlv_length, &list_length); if (plmn_list == NULL) return NULL; spdi = g_new0(struct sim_spdi, 1); for (list_length /= 3; list_length--; plmn_list += 3) { if ((plmn_list[0] & plmn_list[1] & plmn_list[2]) == 0xff) continue; oper = g_new0(struct spdi_operator, 1); sim_parse_mcc_mnc(plmn_list, oper->mcc, oper->mnc); spdi->operators = g_slist_insert_sorted(spdi->operators, oper, spdi_operator_compare); } return spdi; } gboolean sim_spdi_lookup(struct sim_spdi *spdi, const char *mcc, const char *mnc) { struct spdi_operator spdi_op; if (spdi == NULL) return FALSE; g_strlcpy(spdi_op.mcc, mcc, sizeof(spdi_op.mcc)); g_strlcpy(spdi_op.mnc, mnc, sizeof(spdi_op.mnc)); return g_slist_find_custom(spdi->operators, &spdi_op, spdi_operator_compare) != NULL; } void sim_spdi_free(struct sim_spdi *spdi) { if (spdi == NULL) return; g_slist_foreach(spdi->operators, (GFunc)g_free, NULL); g_slist_free(spdi->operators); g_free(spdi); } static void pnn_operator_free(struct sim_eons_operator_info *oper) { if (oper == NULL) return; g_free(oper->info); g_free(oper->shortname); g_free(oper->longname); } struct sim_eons *sim_eons_new(int pnn_records) { struct sim_eons *eons = g_new0(struct sim_eons, 1); eons->pnn_list = g_new0(struct sim_eons_operator_info, pnn_records); eons->pnn_max = pnn_records; return eons; } gboolean sim_eons_pnn_is_empty(struct sim_eons *eons) { return !eons->pnn_valid; } void sim_eons_add_pnn_record(struct sim_eons *eons, int record, const guint8 *tlv, int length) { const unsigned char *name; int namelength; struct sim_eons_operator_info *oper = &eons->pnn_list[record-1]; name = ber_tlv_find_by_tag(tlv, 0x43, length, &namelength); if (name == NULL || !namelength) return; oper->longname = sim_network_name_parse(name, namelength, &oper->long_ci); name = ber_tlv_find_by_tag(tlv, 0x45, length, &namelength); if (name && namelength) oper->shortname = sim_network_name_parse(name, namelength, &oper->short_ci); name = ber_tlv_find_by_tag(tlv, 0x80, length, &namelength); if (name && namelength) oper->info = sim_string_to_utf8(name, namelength); eons->pnn_valid = TRUE; } static struct opl_operator *opl_operator_alloc(const guint8 *record) { struct opl_operator *oper = g_new0(struct opl_operator, 1); sim_parse_mcc_mnc(record, oper->mcc, oper->mnc); record += 3; oper->lac_tac_low = (record[0] << 8) | record[1]; record += 2; oper->lac_tac_high = (record[0] << 8) | record[1]; record += 2; oper->id = record[0]; return oper; } void sim_eons_add_opl_record(struct sim_eons *eons, const guint8 *contents, int length) { struct opl_operator *oper; oper = opl_operator_alloc(contents); if (oper->id > eons->pnn_max) { g_free(oper); return; } eons->opl_list = g_slist_prepend(eons->opl_list, oper); } void sim_eons_optimize(struct sim_eons *eons) { eons->opl_list = g_slist_reverse(eons->opl_list); } void sim_eons_free(struct sim_eons *eons) { int i; if (eons == NULL) return; for (i = 0; i < eons->pnn_max; i++) pnn_operator_free(eons->pnn_list + i); g_free(eons->pnn_list); g_slist_foreach(eons->opl_list, (GFunc)g_free, NULL); g_slist_free(eons->opl_list); g_free(eons); } static const struct sim_eons_operator_info * sim_eons_lookup_common(struct sim_eons *eons, const char *mcc, const char *mnc, gboolean have_lac, guint16 lac) { GSList *l; const struct opl_operator *opl; int i; if (eons == NULL) return NULL; for (l = eons->opl_list; l; l = l->next) { opl = l->data; for (i = 0; i < OFONO_MAX_MCC_LENGTH; i++) if (mcc[i] != opl->mcc[i] && !(opl->mcc[i] == 'b' && mcc[i])) break; if (i < OFONO_MAX_MCC_LENGTH) continue; for (i = 0; i < OFONO_MAX_MNC_LENGTH; i++) if (mnc[i] != opl->mnc[i] && !(opl->mnc[i] == 'b' && mnc[i])) break; if (i < OFONO_MAX_MNC_LENGTH) continue; if (opl->lac_tac_low == 0 && opl->lac_tac_high == 0xfffe) break; if (have_lac == FALSE) continue; if ((lac >= opl->lac_tac_low) && (lac <= opl->lac_tac_high)) break; } if (l == NULL) return NULL; opl = l->data; /* 0 is not a valid record id */ if (opl->id == 0) return NULL; return &eons->pnn_list[opl->id - 1]; } const struct sim_eons_operator_info *sim_eons_lookup(struct sim_eons *eons, const char *mcc, const char *mnc) { return sim_eons_lookup_common(eons, mcc, mnc, FALSE, 0); } const struct sim_eons_operator_info *sim_eons_lookup_with_lac( struct sim_eons *eons, const char *mcc, const char *mnc, guint16 lac) { return sim_eons_lookup_common(eons, mcc, mnc, TRUE, lac); } /* * Extract extended BCD format defined in 3GPP 11.11, 31.102. The format * is different from what is defined in 3GPP 24.008 and 23.040 (sms). * * Here the digits with values 'C', 'D' and 'E' are treated differently, * for more details see 31.102 Table 4.4 * * 'C' - DTMF Control Digit Separator, represented as 'c' by this function * 'D' - Wild Value, represented as a '?' by this function * 'E' - RFU, used to be used as a Shift Operator in 11.11 * 'F' - Endmark * * Note that a second or subsequent 'C' BCD value will be interpreted as a * 3 second pause. */ void sim_extract_bcd_number(const unsigned char *buf, int len, char *out) { static const char digit_lut[] = "0123456789*#c?e\0"; unsigned char oct; int i; for (i = 0; i < len; i++) { oct = buf[i]; out[i*2] = digit_lut[oct & 0x0f]; out[i*2+1] = digit_lut[(oct & 0xf0) >> 4]; } out[i*2] = '\0'; } void sim_encode_bcd_number(const char *number, unsigned char *out) { while (number[0] != '\0' && number[1] != '\0') { *out = to_semi_oct(*number++); *out++ |= to_semi_oct(*number++) << 4; } if (*number) *out = to_semi_oct(*number) | 0xf0; } gboolean sim_adn_parse(const unsigned char *data, int length, struct ofono_phone_number *ph, char **identifier) { int number_len; int ton_npi; const unsigned char *alpha; int alpha_length; if (length < 14) return FALSE; alpha = data; alpha_length = length - 14; data += alpha_length; number_len = *data++; ton_npi = *data++; if (number_len > 11 || ton_npi == 0xff) return FALSE; ph->type = ton_npi; /* BCD coded, however the TON/NPI is given by the first byte */ number_len -= 1; sim_extract_bcd_number(data, number_len, ph->number); if (identifier == NULL) return TRUE; /* Alpha-Identifier field */ if (alpha_length > 0) *identifier = sim_string_to_utf8(alpha, alpha_length); else *identifier = NULL; return TRUE; } void sim_adn_build(unsigned char *data, int length, const struct ofono_phone_number *ph, const char *identifier) { int number_len = strlen(ph->number); unsigned char *alpha = NULL; int alpha_written = 0; int alpha_length; alpha_length = length - 14; /* Alpha-Identifier field */ if (alpha_length > 0) { if (identifier) alpha = utf8_to_sim_string(identifier, alpha_length, &alpha_written); if (alpha) { memcpy(data, alpha, alpha_written); g_free(alpha); } memset(data + alpha_written, 0xff, alpha_length - alpha_written); data += alpha_length; } number_len = (number_len + 1) / 2; *data++ = number_len + 1; *data++ = ph->type; sim_encode_bcd_number(ph->number, data); memset(data + number_len, 0xff, 10 - number_len); data += 10; /* CCP1 unused */ *data++ = 0xff; /* Ext1 unused */ *data++ = 0xff; } static int find_ef_by_id(const void *key, const void *value) { unsigned short id = GPOINTER_TO_UINT(key); const struct sim_ef_info *info = value; return id - info->id; } struct sim_ef_info *sim_ef_db_lookup(unsigned short id) { struct sim_ef_info *result; unsigned int nelem = sizeof(ef_db) / sizeof(struct sim_ef_info); result = bsearch(GUINT_TO_POINTER((unsigned int) id), ef_db, nelem, sizeof(struct sim_ef_info), find_ef_by_id); return result; } unsigned int sim_ef_db_get_path_2g(unsigned short id, unsigned char out_path[]) { struct sim_ef_info *info; unsigned int nelem = sizeof(ef_db) / sizeof(struct sim_ef_info); unsigned char path[6]; int i = 0; int j; info = bsearch(GUINT_TO_POINTER((unsigned int) id), ef_db, nelem, sizeof(struct sim_ef_info), find_ef_by_id); if (info == NULL) return 0; path[i++] = info->parent2g & 0xff; path[i++] = info->parent2g >> 8; while (info->parent2g != ROOTMF) { info = bsearch(GUINT_TO_POINTER((unsigned int) info->parent2g), ef_db, nelem, sizeof(struct sim_ef_info), find_ef_by_id); if (info == NULL) return 0; path[i++] = info->parent2g & 0xff; path[i++] = info->parent2g >> 8; } for (j = 0; j < i; j++) out_path[j] = path[i - j - 1]; return i; } unsigned int sim_ef_db_get_path_3g(unsigned short id, unsigned char out_path[]) { struct sim_ef_info *info; unsigned int nelem = sizeof(ef_db) / sizeof(struct sim_ef_info); unsigned char path[6]; int i = 0; int j; info = bsearch(GUINT_TO_POINTER((unsigned int) id), ef_db, nelem, sizeof(struct sim_ef_info), find_ef_by_id); if (info == NULL) return 0; path[i++] = info->parent3g & 0xff; path[i++] = info->parent3g >> 8; while (info->parent3g != ROOTMF) { info = bsearch(GUINT_TO_POINTER((unsigned int) info->parent3g), ef_db, nelem, sizeof(struct sim_ef_info), find_ef_by_id); if (info == NULL) return 0; path[i++] = info->parent3g & 0xff; path[i++] = info->parent3g >> 8; } for (j = 0; j < i; j++) out_path[j] = path[i - j - 1]; return i; } gboolean sim_parse_3g_get_response(const unsigned char *data, int len, int *file_len, int *record_len, int *structure, unsigned char *access, unsigned short *efid) { const unsigned char *fcp; int fcp_length; const unsigned char *tlv; int tlv_length; int i; int flen, rlen, str; unsigned short id; unsigned char acc[3]; struct sim_ef_info *info; fcp = ber_tlv_find_by_tag(data, 0x62, len, &fcp_length); if (fcp == NULL) return FALSE; /* * Find the file size tag 0x80 according to * ETSI 102.221 Section 11.1.1.3.2 */ tlv = ber_tlv_find_by_tag(fcp, 0x80, fcp_length, &tlv_length); if (tlv == NULL || tlv_length < 2) return FALSE; flen = tlv[0]; for (i = 1; i < tlv_length; i++) flen = (flen << 8) | tlv[i]; tlv = ber_tlv_find_by_tag(fcp, 0x83, fcp_length, &tlv_length); if (tlv == NULL || tlv_length != 2) return FALSE; id = (tlv[0] << 8) | tlv[1]; tlv = ber_tlv_find_by_tag(fcp, 0x82, fcp_length, &tlv_length); if (tlv == NULL || (tlv_length != 2 && tlv_length != 5)) return FALSE; if (tlv[1] != 0x21) return FALSE; switch (tlv[0] & 0x3) { case 1: /* Transparent */ str = 0x00; break; case 2: /* Linear Fixed */ str = 0x01; break; case 6: /* Cyclic */ str = 0x03; break; default: return FALSE; }; /* For cyclic or linear fixed we need record size & num records */ if (str != 0x00 && tlv_length != 5) return FALSE; /* * strictly speaking the record length is 16 bit, but the valid * range is 0x01 to 0xFF according to 102.221 */ if (str != 0x00) rlen = tlv[3]; else rlen = 0; /* * The 3G response data contains references to EFarr which actually * contains the security attributes. These are usually not carried * along with the response data unlike in 2G. Instead of querying * this, we simply look it up in our database. We fudge it somewhat * and guess if the file isn't found. */ info = sim_ef_db_lookup(id); if (str == 0x03) acc[1] = 0x1f; else acc[1] = 0xff; acc[2] = 0x44; if (info == NULL) acc[0] = 0x11; else acc[0] = (info->perm_read << 4) | info->perm_update; if (file_len) *file_len = flen; if (record_len) *record_len = rlen; if (efid) *efid = id; if (structure) *structure = str; if (access) memcpy(access, acc, 3); return TRUE; } gboolean sim_parse_2g_get_response(const unsigned char *response, int len, int *file_len, int *record_len, int *structure, unsigned char *access, unsigned char *file_status) { if (len < 14 || response[6] != 0x04) return FALSE; if ((response[13] == 0x01 || response[13] == 0x03) && len < 15) return FALSE; *file_len = (response[2] << 8) | response[3]; *structure = response[13]; access[0] = response[8]; access[1] = response[9]; access[2] = response[10]; *file_status = response[11]; if (response[13] == 0x01 || response[13] == 0x03) *record_len = response[14]; else *record_len = 0; return TRUE; } gboolean sim_ust_is_available(unsigned char *efust, unsigned char len, enum sim_ust_service index) { if (index >= len * 8u) return FALSE; return (efust[index / 8] >> (index % 8)) & 1; } gboolean sim_est_is_active(unsigned char *efest, unsigned char len, enum sim_est_service index) { if (index >= len * 8u) return FALSE; return (efest[index / 8] >> (index % 8)) & 1; } gboolean sim_sst_is_available(unsigned char *efsst, unsigned char len, enum sim_sst_service index) { if (index >= len * 4u) return FALSE; return (efsst[index / 4] >> ((index % 4) * 2)) & 1; } gboolean sim_sst_is_active(unsigned char *efsst, unsigned char len, enum sim_sst_service index) { if (index >= len * 4u) return FALSE; return (efsst[index / 4] >> (((index % 4) * 2) + 1)) & 1; } gboolean sim_cphs_is_active(unsigned char *cphs, enum sim_cphs_service index) { if (index >= 2 * 4u) return FALSE; return ((cphs[index / 4] >> ((index % 4) * 2)) & 3) == 3; } GSList *sim_parse_app_template_entries(const unsigned char *buffer, int len) { GSList *ret = NULL; const unsigned char *dataobj; int dataobj_len; /* Find all the application entries */ while ((dataobj = ber_tlv_find_by_tag(buffer, 0x61, len, &dataobj_len)) != NULL) { struct sim_app_record app; const unsigned char *aid, *label; int label_len; /* Find the aid (mandatory) */ aid = ber_tlv_find_by_tag(dataobj, 0x4f, dataobj_len, &app.aid_len); if (!aid || app.aid_len < 0x01 || app.aid_len > 0x10) goto error; memcpy(app.aid, aid, app.aid_len); /* Find the label (optional) */ label = ber_tlv_find_by_tag(dataobj, 0x50, dataobj_len, &label_len); if (label) { /* * Label field uses the extra complicated * encoding in 102.221 Annex A */ app.label = sim_string_to_utf8(label, label_len); if (app.label == NULL) goto error; } else app.label = NULL; ret = g_slist_prepend(ret, g_memdup(&app, sizeof(app))); len -= (dataobj - buffer) + dataobj_len; buffer = dataobj + dataobj_len; } return ret; error: while (ret) { GSList *t = ret; struct sim_app_record *app = ret->data; g_free(app->label); g_free(app); ret = ret->next; g_slist_free_1(t); } return NULL; } ofono-1.17.bzr6912+16.04.20160314.3/src/ofono.ver0000644000015600001650000000006212671500024021022 0ustar pbuserpbgroup00000000000000{ global: ofono_*; g_dbus_*; local: *; }; ofono-1.17.bzr6912+16.04.20160314.3/src/stkutil.h0000644000015600001650000015033412671500024021044 0ustar pbuserpbgroup00000000000000/* * * 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 * */ /* * TS 101.220, Section 7.2, Card Application Toolkit assigned templates, * These are the same as 3GPP 11.14 Sections 13.1 and 13.2 */ enum stk_envelope_type { STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD = 0xD1, STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD = 0xD2, STK_ENVELOPE_TYPE_MENU_SELECTION = 0xD3, STK_ENVELOPE_TYPE_CALL_CONTROL = 0xD4, STK_ENVELOPE_TYPE_MO_SMS_CONTROL = 0xD5, STK_ENVELOPE_TYPE_EVENT_DOWNLOAD = 0xD6, STK_ENVELOPE_TYPE_TIMER_EXPIRATION = 0xD7, STK_ENVELOPE_TYPE_USSD_DOWNLOAD = 0xD9, STK_ENVELOPE_TYPE_MMS_TRANSFER_STATUS = 0xDA, STK_ENVELOPE_TYPE_MMS_NOTIFICATION = 0xDB, STK_ENVELOPE_TYPE_TERMINAL_APP = 0xDC, STK_ENVELOPE_TYPE_GEOLOCATION_REPORT = 0xDD, }; /* TS 102.223 Section 9.4 */ enum stk_command_type { STK_COMMAND_TYPE_REFRESH = 0x01, STK_COMMAND_TYPE_MORE_TIME = 0x02, STK_COMMAND_TYPE_POLL_INTERVAL = 0x03, STK_COMMAND_TYPE_POLLING_OFF = 0x04, STK_COMMAND_TYPE_SETUP_EVENT_LIST = 0x05, STK_COMMAND_TYPE_SETUP_CALL = 0x10, STK_COMMAND_TYPE_SEND_SS = 0x11, STK_COMMAND_TYPE_SEND_USSD = 0x12, STK_COMMAND_TYPE_SEND_SMS = 0x13, STK_COMMAND_TYPE_SEND_DTMF = 0x14, STK_COMMAND_TYPE_LAUNCH_BROWSER = 0x15, STK_COMMAND_TYPE_GEOGRAPICAL_LOCATION_REQUEST = 0x16, STK_COMMAND_TYPE_PLAY_TONE = 0x20, STK_COMMAND_TYPE_DISPLAY_TEXT = 0x21, STK_COMMAND_TYPE_GET_INKEY = 0x22, STK_COMMAND_TYPE_GET_INPUT = 0x23, STK_COMMAND_TYPE_SELECT_ITEM = 0x24, STK_COMMAND_TYPE_SETUP_MENU = 0x25, STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO = 0x26, STK_COMMAND_TYPE_TIMER_MANAGEMENT = 0x27, STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT = 0x28, STK_COMMAND_TYPE_PERFORM_CARD_APDU = 0x30, STK_COMMAND_TYPE_POWER_ON_CARD = 0x31, STK_COMMAND_TYPE_POWER_OFF_CARD = 0x32, STK_COMMAND_TYPE_GET_READER_STATUS = 0x33, STK_COMMAND_TYPE_RUN_AT_COMMAND = 0x34, STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION = 0x35, STK_COMMAND_TYPE_OPEN_CHANNEL = 0x40, STK_COMMAND_TYPE_CLOSE_CHANNEL = 0x41, STK_COMMAND_TYPE_RECEIVE_DATA = 0x42, STK_COMMAND_TYPE_SEND_DATA = 0x43, STK_COMMAND_TYPE_GET_CHANNEL_STATUS = 0x44, STK_COMMAND_TYPE_SERVICE_SEARCH = 0x45, STK_COMMAND_TYPE_GET_SERVICE_INFO = 0x46, STK_COMMAND_TYPE_DECLARE_SERVICE = 0x47, STK_COMMAND_TYPE_SET_FRAMES = 0x50, STK_COMMAND_TYPE_GET_FRAMES_STATUS = 0x51, STK_COMMAND_TYPE_RETRIEVE_MMS = 0x60, STK_COMMAND_TYPE_SUBMIT_MMS = 0x61, STK_COMMAND_TYPE_DISPLAY_MMS = 0x62, STK_COMMAND_TYPE_ACTIVATE = 0x70, STK_COMMAND_TYPE_END_SESSION = 0x81, }; enum stk_data_object_type { STK_DATA_OBJECT_TYPE_INVALID = 0x00, STK_DATA_OBJECT_TYPE_COMMAND_DETAILS = 0x01, STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES = 0x02, STK_DATA_OBJECT_TYPE_RESULT = 0x03, STK_DATA_OBJECT_TYPE_DURATION = 0x04, STK_DATA_OBJECT_TYPE_ALPHA_ID = 0x05, STK_DATA_OBJECT_TYPE_ADDRESS = 0x06, STK_DATA_OBJECT_TYPE_CCP = 0x07, STK_DATA_OBJECT_TYPE_SUBADDRESS = 0x08, STK_DATA_OBJECT_TYPE_SS_STRING = 0x09, STK_DATA_OBJECT_TYPE_USSD_STRING = 0x0A, STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU = 0x0B, STK_DATA_OBJECT_TYPE_CBS_PAGE = 0x0C, STK_DATA_OBJECT_TYPE_TEXT = 0x0D, STK_DATA_OBJECT_TYPE_TONE = 0x0E, STK_DATA_OBJECT_TYPE_ITEM = 0x0F, STK_DATA_OBJECT_TYPE_ITEM_ID = 0x10, STK_DATA_OBJECT_TYPE_RESPONSE_LENGTH = 0x11, STK_DATA_OBJECT_TYPE_FILE_LIST = 0x12, STK_DATA_OBJECT_TYPE_LOCATION_INFO = 0x13, STK_DATA_OBJECT_TYPE_IMEI = 0x14, STK_DATA_OBJECT_TYPE_HELP_REQUEST = 0x15, STK_DATA_OBJECT_TYPE_NETWORK_MEASUREMENT_RESULTS = 0x16, STK_DATA_OBJECT_TYPE_DEFAULT_TEXT = 0x17, STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR = 0x18, STK_DATA_OBJECT_TYPE_EVENT_LIST = 0x19, STK_DATA_OBJECT_TYPE_CAUSE = 0x1A, STK_DATA_OBJECT_TYPE_LOCATION_STATUS = 0x1B, STK_DATA_OBJECT_TYPE_TRANSACTION_ID = 0x1C, STK_DATA_OBJECT_TYPE_BCCH_CHANNEL_LIST = 0x1D, STK_DATA_OBJECT_TYPE_ICON_ID = 0x1E, STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST = 0x1F, STK_DATA_OBJECT_TYPE_CARD_READER_STATUS = 0x20, STK_DATA_OBJECT_TYPE_CARD_ATR = 0x21, STK_DATA_OBJECT_TYPE_C_APDU = 0x22, STK_DATA_OBJECT_TYPE_R_APDU = 0x23, STK_DATA_OBJECT_TYPE_TIMER_ID = 0x24, STK_DATA_OBJECT_TYPE_TIMER_VALUE = 0x25, STK_DATA_OBJECT_TYPE_DATETIME_TIMEZONE = 0x26, STK_DATA_OBJECT_TYPE_CALL_CONTROL_REQUESTED_ACTION = 0x27, STK_DATA_OBJECT_TYPE_AT_COMMAND = 0x28, STK_DATA_OBJECT_TYPE_AT_RESPONSE = 0x29, STK_DATA_OBJECT_TYPE_BC_REPEAT_INDICATOR = 0x2A, STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE = 0x2B, STK_DATA_OBJECT_TYPE_DTMF_STRING = 0x2C, STK_DATA_OBJECT_TYPE_LANGUAGE = 0x2D, STK_DATA_OBJECT_TYPE_TIMING_ADVANCE = 0x2E, STK_DATA_OBJECT_TYPE_AID = 0x2F, STK_DATA_OBJECT_TYPE_BROWSER_ID = 0x30, STK_DATA_OBJECT_TYPE_URL = 0x31, STK_DATA_OBJECT_TYPE_BEARER = 0x32, STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF = 0x33, STK_DATA_OBJECT_TYPE_BROWSER_TERMINATION_CAUSE = 0x34, STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION = 0x35, STK_DATA_OBJECT_TYPE_CHANNEL_DATA = 0x36, STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH = 0x37, STK_DATA_OBJECT_TYPE_CHANNEL_STATUS = 0x38, STK_DATA_OBJECT_TYPE_BUFFER_SIZE = 0x39, STK_DATA_OBJECT_TYPE_CARD_READER_ID = 0x3A, STK_DATA_OBJECT_TYPE_FILE_UPDATE_INFO = 0x3B, STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE = 0x3C, STK_DATA_OBJECT_TYPE_OTHER_ADDRESS = 0x3E, STK_DATA_OBJECT_TYPE_ACCESS_TECHNOLOGY = 0x3F, STK_DATA_OBJECT_TYPE_DISPLAY_PARAMETERS = 0x40, STK_DATA_OBJECT_TYPE_SERVICE_RECORD = 0x41, STK_DATA_OBJECT_TYPE_DEVICE_FILTER = 0x42, STK_DATA_OBJECT_TYPE_SERVICE_SEARCH = 0x43, STK_DATA_OBJECT_TYPE_ATTRIBUTE_INFO = 0x44, STK_DATA_OBJECT_TYPE_SERVICE_AVAILABILITY = 0x45, STK_DATA_OBJECT_TYPE_ESN = 0x46, STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME = 0x47, STK_DATA_OBJECT_TYPE_CDMA_SMS_TPDU = 0x48, STK_DATA_OBJECT_TYPE_REMOTE_ENTITY_ADDRESS = 0x49, STK_DATA_OBJECT_TYPE_I_WLAN_ID_TAG = 0x4A, STK_DATA_OBJECT_TYPE_I_WLAN_ACCESS_STATUS = 0x4B, STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE = 0x50, STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST = 0x51, STK_DATA_OBJECT_TYPE_PDP_ACTIVATION_PARAMETER = 0x52, STK_DATA_OBJECT_TYPE_IMEISV = 0x62, STK_DATA_OBJECT_TYPE_BATTERY_STATE = 0x63, STK_DATA_OBJECT_TYPE_BROWSING_STATUS = 0x64, STK_DATA_OBJECT_TYPE_NETWORK_SEARCH_MODE = 0x65, STK_DATA_OBJECT_TYPE_FRAME_LAYOUT = 0x66, STK_DATA_OBJECT_TYPE_FRAMES_INFO = 0x67, STK_DATA_OBJECT_TYPE_FRAME_ID = 0x68, STK_DATA_OBJECT_TYPE_UTRAN_MEASUREMENT_QUALIFIER = 0x69, STK_DATA_OBJECT_TYPE_MMS_REFERENCE = 0x6A, STK_DATA_OBJECT_TYPE_MMS_ID = 0x6B, STK_DATA_OBJECT_TYPE_MMS_TRANSFER_STATUS = 0x6C, STK_DATA_OBJECT_TYPE_MEID = 0x6D, STK_DATA_OBJECT_TYPE_MMS_CONTENT_ID = 0x6E, STK_DATA_OBJECT_TYPE_MMS_NOTIFICATION = 0x6F, STK_DATA_OBJECT_TYPE_LAST_ENVELOPE = 0x70, STK_DATA_OBJECT_TYPE_REGISTRY_APPLICATION_DATA = 0x71, STK_DATA_OBJECT_TYPE_ROUTING_AREA_INFO = 0x73, STK_DATA_OBJECT_TYPE_UPDATE_ATTACH_TYPE = 0x74, STK_DATA_OBJECT_TYPE_REJECTION_CAUSE_CODE = 0x75, STK_DATA_OBJECT_TYPE_NMEA_SENTENCE = 0x78, STK_DATA_OBJECT_TYPE_PLMN_LIST = 0x79, STK_DATA_OBJECT_TYPE_BROADCAST_NETWORK_INFO = 0x7A, STK_DATA_OBJECT_TYPE_ACTIVATE_DESCRIPTOR = 0x7B, STK_DATA_OBJECT_TYPE_EPS_PDN_CONN_ACTIVATION_REQ = 0x7C, STK_DATA_OBJECT_TYPE_TRACKING_AREA_ID = 0x7D, }; enum stk_device_identity_type { STK_DEVICE_IDENTITY_TYPE_KEYPAD = 0x01, STK_DEVICE_IDENTITY_TYPE_DISPLAY = 0x02, STK_DEVICE_IDENTITY_TYPE_EARPIECE = 0x03, STK_DEVICE_IDENTITY_TYPE_CARD_READER_0 = 0x10, STK_DEVICE_IDENTITY_TYPE_CARD_READER_1 = 0x11, STK_DEVICE_IDENTITY_TYPE_CARD_READER_2 = 0x12, STK_DEVICE_IDENTITY_TYPE_CARD_READER_3 = 0x13, STK_DEVICE_IDENTITY_TYPE_CARD_READER_4 = 0x14, STK_DEVICE_IDENTITY_TYPE_CARD_READER_5 = 0x15, STK_DEVICE_IDENTITY_TYPE_CARD_READER_6 = 0x16, STK_DEVICE_IDENTITY_TYPE_CARD_READER_7 = 0x17, STK_DEVICE_IDENTITY_TYPE_CHANNEL_1 = 0x21, STK_DEVICE_IDENTITY_TYPE_CHANNEL_7 = 0x27, STK_DEVICE_IDENTITY_TYPE_UICC = 0x81, STK_DEVICE_IDENTITY_TYPE_TERMINAL = 0x82, STK_DEVICE_IDENTITY_TYPE_NETWORK = 0x83, }; enum stk_qualifier_get_reader_status_type { STK_QUALIFIER_TYPE_CARD_READER_STATUS = 0x00, STK_QUALIFIER_TYPE_CARD_READER_ID = 0x01, }; enum stk_duration_type { STK_DURATION_TYPE_MINUTES = 0x00, STK_DURATION_TYPE_SECONDS = 0x01, STK_DURATION_TYPE_SECOND_TENTHS = 0x02, }; /* Defined according to TS 102.223 Section 8.12 */ enum stk_result_type { /* 0x00 to 0x1F are used to indicate that command has been performed */ STK_RESULT_TYPE_SUCCESS = 0x00, STK_RESULT_TYPE_PARTIAL = 0x01, STK_RESULT_TYPE_MISSING_INFO = 0x02, STK_RESULT_TYPE_REFRESH_WITH_EFS = 0x03, STK_RESULT_TYPE_NO_ICON = 0x04, STK_RESULT_TYPE_CALL_CONTROL = 0x05, STK_RESULT_TYPE_NO_SERVICE = 0x06, STK_RESULT_TYPE_MODIFED = 0x07, STK_RESULT_TYPE_REFRES_NO_NAA = 0x08, STK_RESULT_TYPE_NO_TONE = 0x09, STK_RESULT_TYPE_USER_TERMINATED = 0x10, STK_RESULT_TYPE_GO_BACK = 0x11, STK_RESULT_TYPE_NO_RESPONSE = 0x12, STK_RESULT_TYPE_HELP_REQUESTED = 0x13, STK_RESULT_TYPE_USSD_OR_SS_USER_TERMINATION = 0x14, /* 0x20 to 0x2F are used to indicate that SIM should retry */ STK_RESULT_TYPE_TERMINAL_BUSY = 0x20, STK_RESULT_TYPE_NETWORK_UNAVAILABLE = 0x21, STK_RESULT_TYPE_USER_REJECT = 0x22, STK_RESULT_TYPE_USER_CANCEL = 0x23, STK_RESULT_TYPE_TIMER_CONFLICT = 0x24, STK_RESULT_TYPE_CALL_CONTROL_TEMPORARY = 0x25, STK_RESULT_TYPE_BROWSER_TEMPORARY = 0x26, STK_RESULT_TYPE_MMS_TEMPORARY = 0x27, /* 0x30 to 0x3F are used to indicate permanent problems */ STK_RESULT_TYPE_NOT_CAPABLE = 0x30, STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD = 0x31, STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD = 0x32, STK_RESULT_TYPE_COMMAND_ID_UNKNOWN = 0x33, STK_RESULT_TYPE_SS_RETURN_ERROR = 0x34, STK_RESULT_TYPE_SMS_RP_ERROR = 0x35, STK_RESULT_TYPE_MINIMUM_NOT_MET = 0x36, STK_RESULT_TYPE_USSD_RETURN_ERROR = 0x37, STK_RESULT_TYPE_CALL_CONTROL_PERMANENT = 0x39, STK_RESULT_TYPE_BIP_ERROR = 0x3A, STK_RESULT_TYPE_ACCESS_TECHNOLOGY_ERROR = 0x3B, STK_RESULT_TYPE_FRAMES_ERROR = 0x3C, STK_RESULT_TYPE_MMS_ERROR = 0x3D, }; /* Defined according to TS 102.223 Section 8.12.2 */ enum stk_result_addnl_me_pb { STK_RESULT_ADDNL_ME_PB_NO_SPECIFIC_CAUSE = 0x00, STK_RESULT_ADDNL_ME_PB_SCREEN_BUSY = 0x01, STK_RESULT_ADDNL_ME_PB_BUSY_ON_CALL = 0x02, STK_RESULT_ADDNL_ME_PB_SS_BUSY = 0x03, STK_RESULT_ADDNL_ME_PB_NO_SERVICE = 0x04, STK_RESULT_ADDNL_ME_PB_NO_ACCESS = 0x05, STK_RESULT_ADDNL_ME_PB_NO_RADIO_RESOURCE = 0x06, STK_RESULT_ADDNL_ME_PB_NOT_IN_SPEECH_CALL = 0x07, STK_RESULT_ADDNL_ME_PB_USSD_BUSY = 0x08, STK_RESULT_ADDNL_ME_PB_BUSY_ON_SEND_DTMF = 0x09, STK_RESULT_ADDNL_ME_PB_NO_NAA_ACTIVE = 0x0A }; /* Defined according to TS 31.111 Section 8.12.4 */ enum stk_result_addnl_ss_pb { STK_RESULT_ADDNL_SS_PB_NO_SPECIFIC_CAUSE = 0x00 }; /* Defined according to TS 31.111 Section 8.12.4 */ enum stk_result_addnl_bip_pb { STK_RESULT_ADDNL_BIP_PB_NO_SPECIFIC_CAUSE = 0x00, STK_RESULT_ADDNL_BIP_PB_NO_CHANNEL_AVAIL = 0x01, STK_RESULT_ADDNL_BIP_PB_CHANNEL_CLOSED = 0x02, STK_RESULT_ADDNL_BIP_PB_CHANNEL_ID_NOT_VALID = 0x03, STK_RESULT_ADDNL_BIP_PB_BUFFER_SIZE_NOT_AVAIL = 0x04, STK_RESULT_ADDNL_BIP_PB_SECURITY_ERROR = 0x05, STK_RESULT_ADDNL_BIP_PB_INTERFACE_NOT_AVAIL = 0x06, STK_RESULT_ADDNL_BIP_PB_DEVICE_NOT_REACHABLE = 0x07, STK_RESULT_ADDNL_BIP_PB_SERVICE_ERROR = 0x08, STK_RESULT_ADDNL_BIP_PB_SERVICE_ID_UNKNOWN = 0x09, STK_RESULT_ADDNL_BIP_PB_PORT_NOT_AVAIL = 0x10, STK_RESULT_ADDNL_BIP_PB_LAUNCH_PARAMETERS_MISSING = 0x11, STK_RESULT_ADDNL_BIP_PB_APPLICATION_LAUNCH_FAILED = 0x12 }; enum stk_tone_type { STK_TONE_TYPE_DIAL_TONE = 0x01, STK_TONE_TYPE_BUSY_TONE = 0x02, STK_TONE_TYPE_CONGESTION = 0x03, STK_TONE_TYPE_RP_ACK = 0x04, STK_TONE_TYPE_CALL_DROPPED = 0x05, STK_TONE_TYPE_ERROR = 0x06, STK_TONE_TYPE_CALL_WAITING = 0x07, STK_TONE_TYPE_RINGING = 0x08, STK_TONE_TYPE_GENERAL_BEEP = 0x10, STK_TONE_TYPE_POSITIVE_ACK = 0x11, STK_TONE_TYPE_NEGATIVE_ACK = 0x12, STK_TONE_TYPE_INCOMING_CALL = 0x13, STK_TONE_TYPE_INCOMING_SMS = 0x14, STK_TONE_TYPE_CRITICAL_ALERT = 0x15, STK_TONE_TYPE_VIBRATE = 0x20, STK_TONE_TYPE_HAPPY_TONE = 0x31, STK_TONE_TYPE_SAD_TONE = 0x32, STK_TONE_TYPE_URGENT_TONE = 0x33, STK_TONE_TYPE_QUESTION_TONE = 0x34, STK_TONE_TYPE_MESSAGE_TONE = 0x35, STK_TONE_TYPE_MELODY_1 = 0x40, STK_TONE_TYPE_MELODY_2 = 0x41, STK_TONE_TYPE_MELODY_3 = 0x42, STK_TONE_TYPE_MELODY_4 = 0x43, STK_TONE_TYPE_MELODY_5 = 0x44, STK_TONE_TYPE_MELODY_6 = 0x45, STK_TONE_TYPE_MELODY_7 = 0x46, STK_TONE_TYPE_MELODY_8 = 0x47 }; enum stk_event_type { STK_EVENT_TYPE_MT_CALL = 0x00, STK_EVENT_TYPE_CALL_CONNECTED = 0x01, STK_EVENT_TYPE_CALL_DISCONNECTED = 0x02, STK_EVENT_TYPE_LOCATION_STATUS = 0x03, STK_EVENT_TYPE_USER_ACTIVITY = 0x04, STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE = 0x05, STK_EVENT_TYPE_CARD_READER_STATUS = 0x06, STK_EVENT_TYPE_LANGUAGE_SELECTION = 0x07, STK_EVENT_TYPE_BROWSER_TERMINATION = 0x08, STK_EVENT_TYPE_DATA_AVAILABLE = 0x09, STK_EVENT_TYPE_CHANNEL_STATUS = 0x0A, STK_EVENT_TYPE_SINGLE_ACCESS_TECHNOLOGY_CHANGE = 0x0B, STK_EVENT_TYPE_DISPLAY_PARAMETERS_CHANGED = 0x0C, STK_EVENT_TYPE_LOCAL_CONNECTION = 0x0D, STK_EVENT_TYPE_NETWORK_SEARCH_MODE_CHANGE = 0x0E, STK_EVENT_TYPE_BROWSING_STATUS = 0x0F, STK_EVENT_TYPE_FRAMES_INFORMATION_CHANGE = 0x10, STK_EVENT_TYPE_I_WLAN_ACCESS_STATUS = 0x11, STK_EVENT_TYPE_NETWORK_REJECTION = 0x12, STK_EVENT_TYPE_HCI_CONNECTIVITY_EVENT = 0x13, STK_EVENT_TYPE_MULTIPLE_ACCESS_TECHNOLOGIES_CHANGE = 0x14 }; enum stk_service_state { STK_NORMAL_SERVICE = 0x00, STK_LIMITED_SERVICE = 0x01, STK_NO_SERVICE = 0x02 }; enum stk_icon_qualifier { STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY = 0x00, STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY = 0x01 }; enum stk_ins { STK_INS_DEACTIVATE_FILE = 0x04, STK_INS_ERASE_RECORDS = 0x0C, STK_INS_ERASE_BINARY_0E = 0x0E, STK_INS_ERASE_BINARY_0F = 0x0F, STK_INS_PERFORM_SCQL_OPERATION = 0x10, STK_INS_PERFORM_TRANSACTION_OPERATION = 0x12, STK_INS_PERFORM_USER_OPERATION = 0x14, STK_INS_VERIFY_20 = 0x20, STK_INS_VERIFY_21 = 0x21, STK_INS_MANAGE_SECURITY_ENVIRONMENT = 0x22, STK_INS_CHANGE_REFERENCE_DATA = 0x24, STK_INS_DISABLE_VERIFICATION_REQUIREMENT = 0x26, STK_INS_ENABLE_VERIFICATION_REQUIREMENT = 0x28, STK_INS_PERFORM_SECURITY_OPERATION = 0x2A, STK_INS_RESET_RETRY_COUNTER = 0x2C, STK_INS_ACTIVATE_FILE = 0x44, STK_INS_GENERATE_ASYMMETRIC_KEY_PAIR = 0x46, STK_INS_MANAGE_CHANNEL = 0x70, STK_INS_EXTERNAL_AUTHENTICATE = 0x82, STK_INS_GET_CHALLENGE = 0x84, STK_INS_GENERAL_AUTHENTICATE_86 = 0x86, STK_INS_GENERAL_AUTHENTICATE_87 = 0x87, STK_INS_INTERNAL_AUTHENTICATE = 0x88, STK_INS_SEARCH_BINARY_A0 = 0xA0, STK_INS_SEARCH_BINARY_A1 = 0xA1, STK_INS_SEARCH_RECORD = 0xA2, STK_INS_SELECT = 0xA4, STK_INS_READ_BINARY_B0 = 0xB0, STK_INS_READ_BINARY_B1 = 0xB1, STK_INS_READ_RECORDS_B2 = 0xB2, STK_INS_READ_RECORDS_B3 = 0xB3, STK_INS_GET_RESPONSE = 0xC0, STK_INS_ENVELOPE_C2 = 0xC2, STK_INS_ENVELOPE_C3 = 0xC3, STK_INS_GET_DATA_CA = 0xCA, STK_INS_GET_DATA_CB = 0xCB, STK_INS_WRITE_BINARY_D0 = 0xD0, STK_INS_WRITE_BINARY_D1 = 0xD1, STK_INS_WRITE_RECORD = 0xD2, STK_INS_UPDATE_BINARY_D6 = 0xD6, STK_INS_UPDATE_BINARY_D7 = 0xD7, STK_INS_PUT_DATA_DA = 0xDA, STK_INS_PUT_DATA_DB = 0xDB, STK_INS_UPDATE_RECORD_DC = 0xDC, STK_INS_UPDATE_RECORD_DD = 0xDD, STK_INS_CREATE_FILE = 0xE0, STK_INS_APPEND_RECORD = 0xE2, STK_INS_DELETE_FILE = 0xE4, STK_INS_TERMINATE_DF = 0xE6, STK_INS_TERMINATE_EF = 0xE8, STK_INS_TERMINATE_CARD_USAGE = 0xFE }; enum stk_browser_id { STK_BROWSER_ID_DEFAULT = 0x00, STK_BROWSER_ID_WML = 0x01, STK_BROWSER_ID_HTML = 0x02, STK_BROWSER_ID_XHTML = 0x03, STK_BROWSER_ID_CHTML = 0x04 }; enum stk_bearer { STK_BEARER_SMS = 0x00, STK_BEARER_CS_DATA = 0x01, STK_BEARER_GSM_3G = 0x02, STK_BEARER_PS = 0x03 }; enum stk_browser_termination_cause { STK_BROWSER_USER_TERMINATION = 0x00, STK_BROWSER_ERROR_TERMINATION = 0x01 }; /* Defined in TS 31.111 Section 8.52 */ enum stk_bearer_type { STK_BEARER_TYPE_CS = 0x01, STK_BEARER_TYPE_GPRS_UTRAN = 0x02, STK_BEARER_TYPE_DEFAULT = 0x03, STK_BEARER_TYPE_INDEPENDENT = 0x04, STK_BEARER_TYPE_BLUETOOTH = 0x05, STK_BEARER_TYPE_IRDA = 0x06, STK_BEARER_TYPE_RS232 = 0x07, STK_BEARER_TYPE_TIA_EIA_IS_820 = 0x08, STK_BEARER_TYPE_UTRAN_WITH_EXT_PARAMS = 0x09, STK_BEARER_TYPE_I_WLAN = 0x0A, STK_BEARER_TYPE_EUTRAN_MAPPED_UTRAN = 0x0B, STK_BEARER_TYPE_USB = 0x10 }; enum stk_address_type { STK_ADDRESS_AUTO = -1, STK_ADDRESS_IPV4 = 0x21, STK_ADDRESS_IPV6 = 0x57 }; enum stk_access_technology_type { STK_ACCESS_TECHNOLOGY_GSM = 0x00, STK_ACCESS_TECHNOLOGY_TIA_EIA_553 = 0x01, STK_ACCESS_TECHNOLOGY_TIA_EIA_136_C = 0x02, STK_ACCESS_TECHNOLOGY_UTRAN = 0x03, STK_ACCESS_TECHNOLOGY_TETRA = 0x04, STK_ACCESS_TECHNOLOGY_TIA_EIA_95 = 0x05, STK_ACCESS_TECHNOLOGY_CDMA2000_1X = 0x06, STK_ACCESS_TECHNOLOGY_CDMA2000_HRPD = 0x07, STK_ACCESS_TECHNOLOGY_EUTRAN = 0x08 }; enum stk_technology_id { STK_TECHNOLOGY_INDEPENDENT = 0x00, STK_TECHNOLOGY_BLUETOOTH = 0x01, STK_TECHNOLOGY_IRDA = 0x02, STK_TECHNOLOGY_RS232 = 0x03, STK_TECHNOLOGY_USB = 0x04 }; enum stk_battery_state { STK_BATTERY_VERY_LOW = 0x00, STK_BATTERY_LOW = 0x01, STK_BATTERY_AVERAGE = 0x02, STK_BATTERY_GOOD = 0x03, STK_BATTERY_FULL = 0x04 }; enum stk_frame_layout_type { STK_LAYOUT_HORIZONTAL = 0x01, STK_LAYOUT_VERTICAL = 0x02 }; enum stk_broadcast_network_technology { STK_BROADCAST_NETWORK_DVB_H = 0x00, STK_BROADCAST_NETWORK_DVB_T = 0x01, STK_BROADCAST_NETWORK_DVB_SH = 0x02, STK_BROADCAST_NETWORK_T_DMB = 0x03 }; enum stk_i_wlan_access_status { STK_I_WLAN_STATUS_NO_COVERAGE = 0x00, STK_I_WLAN_STATUS_NOT_CONNECTED = 0x01, STK_I_WLAN_STATUS_CONNECTED = 0x02, }; enum stk_update_attach_type { STK_UPDATE_ATTACH_NORMAL_LOCATION_UPDATING = 0x00, STK_UPDATE_ATTACH_PERIODIC_UPDATING = 0x01, STK_UPDATE_ATTACH_IMSI_ATTACH = 0x02, STK_UPDATE_ATTACH_GPRS_ATTACH = 0x03, STK_UPDATE_ATTACH_GPRS_IMSI_ATTACH = 0x04, STK_UPDATE_ATTACH_RA_UPDATING = 0x05, STK_UPDATE_ATTACH_RA_LA_UPDATING = 0x06, STK_UPDATE_ATTACH_RA_LA_UPDATING_IMSI_ATTACH = 0x07, STK_UPDATE_ATTACH_PERIODIC_RA_UPDATING = 0x08, STK_UPDATE_ATTACH_EPS_ATTACH = 0x09, STK_UPDATE_ATTACH_EPS_IMSI_ATTACH = 0x0a, STK_UPDATE_ATTACH_TA_UPDATING = 0x0b, STK_UPDATE_ATTACH_TA_LA_UPDATING = 0x0c, STK_UPDATE_ATTACH_TA_LA_UPDATING_IMSI_ATTACH = 0x0d, STK_UPDATE_ATTACH_PERIDIC_TA_UPDATING = 0x0e, }; enum stk_rejection_cause_code { /* MM and GMM codes (GERAN/UTRAN) */ STK_CAUSE_GMM_IMSI_UNKNOWN_IN_HLR = 0x02, STK_CAUSE_GMM_ILLEGAL_MS = 0x03, STK_CAUSE_GMM_IMSI_UNKNOWN_IN_VLR = 0x04, STK_CAUSE_GMM_IMEI_NOT_ACCEPTED = 0x05, STK_CAUSE_GMM_ILLEGAL_ME = 0x06, STK_CAUSE_GMM_GPRS_NOT_ALLOWED = 0x07, STK_CAUSE_GMM_GPRS_AND_NON_GPRS_NOT_ALLOWED = 0x08, STK_CAUSE_GMM_IMEI_NOT_DERIVED_BY_NETWORK = 0x09, STK_CAUSE_GMM_IMPLICITLY_DETACHED = 0x0a, STK_CAUSE_GMM_PLMN_NOT_ALLOWED = 0x0b, STK_CAUSE_GMM_LAC_NOT_ALLOWED = 0x0c, STK_CAUSE_GMM_ROAMING_NOT_ALLOWED = 0x0d, STK_CAUSE_GMM_GPRS_NOT_ALLOWED_IN_PLMN = 0x0e, STK_CAUSE_GMM_NO_SUITABLE_CELLS = 0x0f, STK_CAUSE_GMM_MSC_TEMPORARILY_UNREACHABLE = 0x10, STK_CAUSE_GMM_NETWORK_FAILURE = 0x11, STK_CAUSE_GMM_MAC_FAILURE = 0x14, STK_CAUSE_GMM_SYNCH_FAILURE = 0x15, STK_CAUSE_GMM_CONGESTION = 0x16, STK_CAUSE_GMM_GSM_AUTHENTICATION_UNACCEPTABLE = 0x17, STK_CAUSE_GMM_NOT_AUTHORISED_FOR_CSG = 0x19, STK_CAUSE_GMM_SERVICE_OPTION_NOT_SUPPORTED = 0x20, STK_CAUSE_GMM_SERVICE_OPTION_NOT_SUBSCRIBED = 0x21, STK_CAUSE_GMM_SERVICE_OPTION_TEMPORARY_DEFUNC = 0x22, STK_CAUSE_GMM_CALL_NOT_IDENTIFIED = 0x26, STK_CAUSE_GMM_NO_PDP_CONTEXT_ACTIVATED = 0x28, STK_CAUSE_GMM_RETRY_ON_NEW_CELL = 0x30, /* to 0x3f */ STK_CAUSE_GMM_SEMANTICALLY_INCORRECT_MESSAGE = 0x5f, STK_CAUSE_GMM_INVALID_MANDATORY_INFO = 0x60, STK_CAUSE_GMM_MESSAGE_TYPE_UNKNOWN = 0x61, STK_CAUSE_GMM_MESSAGE_TYPE_INCOMPATIBLE_STATE = 0x62, STK_CAUSE_GMM_IE_UNKNOWN = 0x63, STK_CAUSE_GMM_CONDITIONAL_IE_ERROR = 0x64, STK_CAUSE_GMM_MESSAGE_INCOMPATIBLE_WITH_STATE = 0x65, STK_CAUSE_GMM_PROTOCOL_ERROR = 0x6f, /* EMM codes (E-UTRAN) */ STK_CAUSE_EMM_IMSI_UNKNOWN_IN_HSS = 0x02, STK_CAUSE_EMM_ILLEGAL_UE = 0x03, STK_CAUSE_EMM_ILLEGAL_ME = 0x06, STK_CAUSE_EMM_EPS_NOT_ALLOWED = 0x07, STK_CAUSE_EMM_EPS_AND_NON_EPS_NOT_ALLOWED = 0x08, STK_CAUSE_EMM_IMEI_NOT_DERIVED_BY_NETWORK = 0x09, STK_CAUSE_EMM_IMPLICITLY_DETACHED = 0x0a, STK_CAUSE_EMM_PLMN_NOT_ALLOWED = 0x0b, STK_CAUSE_EMM_TAC_NOT_ALLOWED = 0x0c, STK_CAUSE_EMM_ROAMING_NOT_ALLOWED = 0x0d, STK_CAUSE_EMM_EPS_NOT_ALLOWED_IN_PLMN = 0x0e, STK_CAUSE_EMM_NO_SUITABLE_CELLS = 0x0f, STK_CAUSE_EMM_MSC_TEMPORARILY_UNREACHABLE = 0x10, STK_CAUSE_EMM_NETWORK_FAILURE = 0x11, STK_CAUSE_EMM_MAC_FAILURE = 0x14, STK_CAUSE_EMM_SYNCH_FAILURE = 0x15, STK_CAUSE_EMM_CONGESTION = 0x16, STK_CAUSE_EMM_SECURITY_MODE_REJECTED = 0x18, STK_CAUSE_EMM_NOT_AUTHORISED_FOR_CSG = 0x19, STK_CAUSE_EMM_CS_FALLBACK_NOT_ALLOWED = 0x26, STK_CAUSE_EMM_CS_DOMAIN_TEMPORARY_UNAVAILABLE = 0x27, STK_CAUSE_EMM_NO_EPS_BEARER_CONTEXT_ACTIVATED = 0x28, STK_CAUSE_EMM_SEMANTICALLY_INCORRECT_MESSAGE = 0x5f, STK_CAUSE_EMM_INVALID_MANDATORY_INFO = 0x60, STK_CAUSE_EMM_MESSAGE_TYPE_UNKNOWN = 0x61, STK_CAUSE_EMM_MESSAGE_TYPE_INCOMPATIBLE_STATE = 0x62, STK_CAUSE_EMM_IE_UNKNOWN = 0x63, STK_CAUSE_EMM_CONDITIONAL_IE_ERROR = 0x64, STK_CAUSE_EMM_MESSAGE_INCOMPATIBLE_WITH_STATE = 0x65, STK_CAUSE_EMM_PROTOCOL_ERROR = 0x6f, }; enum stk_me_status { STK_ME_STATUS_IDLE = 0x00, STK_ME_STATUS_NOT_IDLE = 0x01 }; enum stk_img_scheme { STK_IMG_SCHEME_BASIC = 0x11, STK_IMG_SCHEME_COLOR = 0x21, STK_IMG_SCHEME_TRANSPARENCY = 0x22, }; /* Defined in TS 102.223 Section 8.6 */ enum stk_qualifier_open_channel { STK_OPEN_CHANNEL_FLAG_IMMEDIATE = 0x01, STK_OPEN_CHANNEL_FLAG_AUTO_RECONNECT = 0x02, STK_OPEN_CHANNEL_FLAG_BACKGROUND = 0x04, }; /* Defined in TS 102.223 Section 8.6 */ enum stk_qualifier_send_data { STK_SEND_DATA_STORE_DATA = 0x00, STK_SEND_DATA_IMMEDIATELY = 0x01, }; /* Defined in TS 102.223 Section 8.56 */ enum stk_channel_status { STK_CHANNEL_PACKET_DATA_SERVICE_NOT_ACTIVATED = 0x00, STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED = 0x01, STK_CHANNEL_TCP_IN_CLOSED_STATE = 0x02, STK_CHANNEL_TCP_IN_LISTEN_STATE = 0x03, STK_CHANNEL_TCP_IN_ESTABLISHED_STATE = 0x04, STK_CHANNEL_LINK_DROPPED = 0x05, }; /* Defined in TS 102.223 Section 8.59 */ enum stk_transport_protocol_type { STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE = 0x01, STK_TRANSPORT_PROTOCOL_TCP_CLIENT_REMOTE = 0x02, STK_TRANSPORT_PROTOCOL_TCP_SERVER = 0x03, STK_TRANSPORT_PROTOCOL_UDP_CLIENT_LOCAL = 0x04, STK_TRANSPORT_PROTOCOL_TCP_CLIENT_LOCAL = 0x05, STK_TRANSPORT_PROTOCOL_DIRECT = 0x06, }; /* For data object that only has a byte array with undetermined length */ struct stk_common_byte_array { unsigned char *array; unsigned int len; }; /* Defined in TS 102.223 Section 8.1 */ struct stk_address { unsigned char ton_npi; char *number; }; /* * Defined in TS 102.223 Section 8.3 * * The maximum size of the subaddress is different depending on the referenced * specification. According to TS 24.008 Section 10.5.4.8: "The called party * subaddress is a type 4 information element with a minimum length of 2 octets * and a maximum length of 23 octets" * * According to TS 31.102 Section 4.4.2.4: "The subaddress data contains * information as defined for this purpose in TS 24.008 [9]. All information * defined in TS 24.008, except the information element identifier, shall be * stored in the USIM. The length of this subaddress data can be up to 22 * bytes." */ struct stk_subaddress { ofono_bool_t has_subaddr; unsigned char len; unsigned char subaddr[23]; }; /* * Defined in TS 102.223 Section 8.4 * * According to 24.008 Section 10.5.4.5 "The bearer capability is a type 4 * information element with a minimum length of 3 octets and a maximum length * of 16 octets." * * According to TS 31.102 Section 4.2.38 the CCP length is 15 bytes. * * The CCP structure is not decoded, but stored as is from the CTLV */ struct stk_ccp { unsigned char len; unsigned char ccp[16]; }; /* Defined in TS 31.111 Section 8.5 */ struct stk_cbs_page { unsigned char len; unsigned char page[88]; }; /* * According to 102.223 Section 8.8 interval values of 0x00 are reserved. * We use this to denote empty duration objects. */ struct stk_duration { enum stk_duration_type unit; unsigned char interval; }; /* Defined in TS 102.223 Section 8.9 */ struct stk_item { unsigned char id; char *text; }; /* * According to 102.223 Section 8.11, the maximum length should never be set * to 0. */ struct stk_response_length { unsigned char min; unsigned char max; }; /* Defined in TS 102.223 Section 8.12 */ struct stk_result { enum stk_result_type type; unsigned int additional_len; unsigned char *additional; }; /* Defined in TS 102.223 Section 8.14 */ struct stk_ss { unsigned char ton_npi; char *ss; }; /* Defined in TS 131.111 Section 8.17. Length limit of 160 chars in 23.028 */ struct stk_ussd_string { unsigned char dcs; unsigned char string[160]; int len; }; /* * Define the struct of single file in TS102.223 Section 8.18. * According to TS 11.11 Section 6.2, each file id has two bytes, and the * maximum Dedicated File level is 2. So the maximum size of file is 8, which * contains two bytes of Master File, 2 bytes of 1st level Dedicated File, * 2 bytes of 2nd level Dedicated File and 2 bytes of Elementary File. */ struct stk_file { unsigned char file[8]; unsigned int len; }; /* Defined in TS 102.223 Section 8.19 */ struct stk_location_info { char mnc[OFONO_MAX_MNC_LENGTH + 1]; char mcc[OFONO_MAX_MCC_LENGTH + 1]; unsigned short lac_tac; ofono_bool_t has_ci; unsigned short ci; ofono_bool_t has_ext_ci; unsigned short ext_ci; ofono_bool_t has_eutran_ci; guint32 eutran_ci; }; /* * According to 102.223 Section 8.24 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. */ struct stk_items_next_action_indicator { unsigned char list[127]; unsigned int len; }; /* * According to 102.223 Section 8.25, there are 21 kinds of event type and no * one should appear more than once. */ struct stk_event_list { unsigned char list[21]; unsigned int len; }; /* * According to 102.223 Section 8.26, the maximum length of cause is 30. */ struct stk_cause { unsigned char cause[30]; unsigned int len; ofono_bool_t has_cause; }; /* * According to 102.223 Section 8.28 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. */ struct stk_transaction_id { unsigned char list[127]; unsigned int len; }; /* * According to 31.111 Section 8.29 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. Each channel * is represented as 10 bits, so the maximum number of channel is 127*8/10=101. */ struct stk_bcch_channel_list { unsigned short channels[101]; unsigned int num; ofono_bool_t has_list; }; /* * Defined in TS 102.223 Section 8.31 * Icon ID denotes a file on the SIM filesystem. Since EF cannot have record * ids of 0, we use icon_id with 0 to denote empty icon_identifier objects */ struct stk_icon_id { unsigned char qualifier; unsigned char id; }; /* * According to 102.223 Section 8.32 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. This size also * includes icon list qualifier for 1 byte, so the maxmimum size of icon * identifier list is 126. */ struct stk_item_icon_id_list { unsigned char qualifier; unsigned char list[126]; unsigned int len; }; /* Defined in TS 102.223 Section 8.33 */ struct stk_reader_status { int id; ofono_bool_t removable; ofono_bool_t present; ofono_bool_t id1_size; ofono_bool_t card_present; ofono_bool_t card_powered; }; /* * According to 102.223 Section 8.34 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. */ struct stk_card_atr { unsigned char atr[127]; unsigned int len; }; /* * Defined in TS 102.223 Section 8.35. According to it, the maximum size * of data is 236. */ struct stk_c_apdu { unsigned char cla; unsigned char ins; unsigned char p1; unsigned char p2; unsigned char lc; unsigned char data[236]; ofono_bool_t has_le; unsigned char le; }; /* Defined in TS 102.223 Section 8.36. According to it, the maximum size * of data is 237. */ struct stk_r_apdu { unsigned char sw1; unsigned char sw2; unsigned char data[237]; unsigned int len; }; /* Defined in TS 102.223 Section 8.38 */ struct stk_timer_value { ofono_bool_t has_value; unsigned char hour; unsigned char minute; unsigned char second; }; /* Defined in TS 102.223 Section 8.42 */ struct stk_bc_repeat { ofono_bool_t has_bc_repeat; unsigned char value; }; /* Defined in TS 31.111 Section 8.46 */ struct stk_timing_advance { ofono_bool_t has_value; enum stk_me_status status; /* * Contains bit periods number according to 3GPP TS * 44.118 Section 9.3.106 / 3GPP TS 44.018 Section * 10.5.2.40.1, not microseconds */ unsigned char advance; }; /* Bearer parameters for GPRS/UTRAN Packet Service/E-UTRAN */ struct stk_gprs_bearer_parameters { unsigned char precedence; unsigned char delay; unsigned char reliability; unsigned char peak; unsigned char mean; unsigned char pdp_type; }; /* Defined in TS 31.111 Section 8.52 */ struct stk_bearer_description { enum stk_bearer_type type; struct stk_gprs_bearer_parameters gprs; }; /* * According to 102.223 Section 8.57 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. */ struct stk_card_reader_id { unsigned char id[127]; unsigned char len; }; /* * According to 102.223 Section 8.58 the address can be either ipv4 or ipv6. * So the maximum size is 16 (for ipv6). */ struct stk_other_address { union { /* Network Byte Order */ guint32 ipv4; unsigned char ipv6[16]; } addr; enum stk_address_type type; }; /* Defined in TS 102.223 Section 8.59 */ struct stk_uicc_te_interface { enum stk_transport_protocol_type protocol; unsigned short port; }; /* * Defined in TS 102.223 Section 8.60. * According to 101.220, Section 4, aid contains two fields RID and PIX. * RID has 5 bytes, while PIX contains information between 7 to 11 bytes. * So the maximum size of aid is 16 bytes. */ struct stk_aid { unsigned char aid[16]; unsigned int len; }; /* Defined in TS 102.223 Section 8.62 */ struct stk_display_parameters { unsigned char height; unsigned char width; unsigned char effects; }; /* Defined in TS 102.223 Section 8.63 */ struct stk_service_record { unsigned char tech_id; unsigned char serv_id; unsigned char *serv_rec; unsigned int len; }; /* Defined in TS 102.223 Section 8.64 */ struct stk_device_filter { unsigned char tech_id; unsigned char *dev_filter; unsigned int len; }; /* Defined in TS 102.223 Section 8.65 */ struct stk_service_search { unsigned char tech_id; unsigned char *ser_search; unsigned int len; }; /* Defined in TS 102.223 Section 8.66 */ struct stk_attribute_info { unsigned char tech_id; unsigned char *attr_info; unsigned int len; }; /* * According to TS 102.223 Section 8.68, remote entity address can be either * 6-bytes IEEE-802 address, or 4-bytes IrDA device address. */ struct stk_remote_entity_address { unsigned char coding_type; ofono_bool_t has_address; union { unsigned char ieee802[6]; unsigned char irda[4]; } addr; }; /* * According to 102.223 Section 8.72 the length of text attribute CTLV is 1 * byte. This means that the maximum size is 127 according to the rules * of CTLVs. Empty attribute options will have len of 0. */ struct stk_text_attribute { unsigned char attributes[127]; unsigned char len; }; /* Defined in TS 31.111 Section 8.72 */ struct stk_pdp_act_par { unsigned char par[127]; unsigned char len; }; /* * According to 102.223 Section 8.73 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. In addition, * the length should be also the number multiplied by 4, so the maximum number * is 124. */ struct stk_item_text_attribute_list { unsigned char list[124]; unsigned char len; }; /* * According to 102.223 Section 8.78 the length of CTLV is 1 byte. This means * that the maximum length is 127 bytes for the total length of layout and * relative-sized frame. Thus the maximum length of relative size is 126 bytes. */ struct stk_frame_layout { unsigned char layout; unsigned char size[126]; unsigned int len; }; /* * According to 102.223 Section 8.79 the length of CTLV is 1 byte. This means * that the maximum length is 127 bytes for the total length of default frame * id and frame information list. Thus the maximum length of frame information * list is 126 bytes. */ struct stk_frames_info { unsigned char id; struct { unsigned char width, height; } list[63]; unsigned int len; }; /* Defined in TS 102.223 Section 8.80 */ struct stk_frame_id { ofono_bool_t has_id; unsigned char id; }; /* * According to 102.223 Section 8.82 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. */ struct stk_mms_reference { unsigned char ref[127]; unsigned char len; }; /* * According to 102.223 Section 8.83 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. */ struct stk_mms_id { unsigned char id[127]; unsigned char len; }; /* * According to 102.223 Section 8.84 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. */ struct stk_mms_transfer_status { unsigned char status[127]; unsigned char len; }; /* * According to 102.223 Section 8.85 the length of CTLV is 1 byte. This means * that the maximum size is 127 according to the rules of CTLVs. */ struct stk_mms_content_id { unsigned char id[127]; unsigned char len; }; /* Defined in TS 102.223 Section 8.88 */ struct stk_registry_application_data { unsigned short port; unsigned char type; char *name; }; /* * According to 102.223 Section 8.90 the length of CTLV is 1 byte. This means * that the maximum length is 127 bytes for the total length of broadcast * network technology and location information. Thus the maximum length of * location information is 126 bytes. */ struct stk_broadcast_network_information { unsigned char tech; unsigned char loc_info[126]; unsigned int len; }; /* Defined in TS 131.111 Section 8.91 */ struct stk_routing_area_info { char mnc[OFONO_MAX_MNC_LENGTH + 1]; char mcc[OFONO_MAX_MCC_LENGTH + 1]; unsigned short lac; unsigned char rac; }; /* Defined in TS 131.111 Section 8.99 */ struct stk_tracking_area_id { char mnc[OFONO_MAX_MNC_LENGTH + 1]; char mcc[OFONO_MAX_MCC_LENGTH + 1]; unsigned short tac; }; struct stk_command_display_text { char *text; struct stk_icon_id icon_id; ofono_bool_t immediate_response; struct stk_duration duration; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_get_inkey { char *text; struct stk_icon_id icon_id; struct stk_duration duration; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_get_input { char *text; struct stk_response_length resp_len; char *default_text; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_play_tone { char *alpha_id; unsigned char tone; struct stk_duration duration; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_poll_interval { struct stk_duration duration; }; struct stk_command_setup_menu { char *alpha_id; GSList *items; struct stk_items_next_action_indicator next_act; struct stk_icon_id icon_id; struct stk_item_icon_id_list item_icon_id_list; struct stk_text_attribute text_attr; struct stk_item_text_attribute_list item_text_attr_list; }; struct stk_command_select_item { char *alpha_id; GSList *items; struct stk_items_next_action_indicator next_act; unsigned char item_id; struct stk_icon_id icon_id; struct stk_item_icon_id_list item_icon_id_list; struct stk_text_attribute text_attr; struct stk_item_text_attribute_list item_text_attr_list; struct stk_frame_id frame_id; }; struct stk_command_send_sms { char *alpha_id; struct sms gsm_sms; struct stk_common_byte_array cdma_sms; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_send_ss { char *alpha_id; struct stk_ss ss; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_send_ussd { char *alpha_id; struct stk_ussd_string ussd_string; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_setup_call { char *alpha_id_usr_cfm; struct stk_address addr; struct stk_ccp ccp; struct stk_subaddress subaddr; struct stk_duration duration; struct stk_icon_id icon_id_usr_cfm; char *alpha_id_call_setup; struct stk_icon_id icon_id_call_setup; struct stk_text_attribute text_attr_usr_cfm; struct stk_text_attribute text_attr_call_setup; struct stk_frame_id frame_id; }; struct stk_command_refresh { GSList *file_list; struct stk_aid aid; char *alpha_id; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_setup_event_list { struct stk_event_list event_list; }; struct stk_command_perform_card_apdu { struct stk_c_apdu c_apdu; }; struct stk_command_timer_mgmt { unsigned char timer_id; struct stk_timer_value timer_value; }; struct stk_command_setup_idle_mode_text { char *text; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_run_at_command { char *alpha_id; char *at_command; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_send_dtmf { char *alpha_id; char *dtmf; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_language_notification { char language[3]; }; struct stk_command_launch_browser { unsigned char browser_id; char *url; struct stk_common_byte_array bearer; GSList *prov_file_refs; char *text_gateway_proxy_id; char *alpha_id; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; struct stk_common_byte_array network_name; char *text_usr; char *text_passwd; }; struct stk_command_open_channel { char *alpha_id; struct stk_icon_id icon_id; struct stk_bearer_description bearer_desc; unsigned short buf_size; char *apn; struct stk_other_address local_addr; char *text_usr; char *text_passwd; struct stk_uicc_te_interface uti; struct stk_other_address data_dest_addr; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_close_channel { char *alpha_id; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_receive_data { char *alpha_id; struct stk_icon_id icon_id; unsigned char data_len; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_send_data { char *alpha_id; struct stk_icon_id icon_id; struct stk_common_byte_array data; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_service_search { char *alpha_id; struct stk_icon_id icon_id; struct stk_service_search serv_search; struct stk_device_filter dev_filter; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_get_service_info { char *alpha_id; struct stk_icon_id icon_id; struct stk_attribute_info attr_info; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_declare_service { struct stk_service_record serv_rec; struct stk_uicc_te_interface intf; }; struct stk_command_set_frames { struct stk_frame_id frame_id; struct stk_frame_layout frame_layout; struct stk_frame_id frame_id_default; }; struct stk_command_retrieve_mms { char *alpha_id; struct stk_icon_id icon_id; struct stk_mms_reference mms_ref; GSList *mms_rec_files; struct stk_mms_content_id mms_content_id; struct stk_mms_id mms_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_submit_mms { char *alpha_id; struct stk_icon_id icon_id; GSList *mms_subm_files; struct stk_mms_id mms_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; struct stk_command_display_mms { GSList *mms_subm_files; struct stk_mms_id mms_id; ofono_bool_t imd_resp; struct stk_frame_id frame_id; }; struct stk_command_activate { unsigned char actv_desc; }; enum stk_command_parse_result { STK_PARSE_RESULT_OK, STK_PARSE_RESULT_TYPE_NOT_UNDERSTOOD, STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD, STK_PARSE_RESULT_MISSING_VALUE, }; struct stk_command { unsigned char number; unsigned char type; unsigned char qualifier; enum stk_device_identity_type src; enum stk_device_identity_type dst; enum stk_command_parse_result status; union { struct stk_command_display_text display_text; struct stk_command_get_inkey get_inkey; struct stk_command_get_input get_input; struct stk_command_play_tone play_tone; struct stk_command_poll_interval poll_interval; struct stk_command_refresh refresh; struct stk_command_setup_menu setup_menu; struct stk_command_select_item select_item; struct stk_command_send_sms send_sms; struct stk_command_send_ss send_ss; struct stk_command_send_ussd send_ussd; struct stk_command_setup_call setup_call; struct stk_command_setup_event_list setup_event_list; struct stk_command_perform_card_apdu perform_card_apdu; struct stk_command_timer_mgmt timer_mgmt; struct stk_command_setup_idle_mode_text setup_idle_mode_text; struct stk_command_run_at_command run_at_command; struct stk_command_send_dtmf send_dtmf; struct stk_command_language_notification language_notification; struct stk_command_launch_browser launch_browser; struct stk_command_open_channel open_channel; struct stk_command_close_channel close_channel; struct stk_command_receive_data receive_data; struct stk_command_send_data send_data; struct stk_command_service_search service_search; struct stk_command_get_service_info get_service_info; struct stk_command_declare_service declare_service; struct stk_command_set_frames set_frames; struct stk_command_retrieve_mms retrieve_mms; struct stk_command_submit_mms submit_mms; struct stk_command_display_mms display_mms; struct stk_command_activate activate; }; void (*destructor)(struct stk_command *command); }; /* TERMINAL RESPONSEs defined in TS 102.223 Section 6.8 */ struct stk_response_generic { }; struct stk_answer_text { char *text; ofono_bool_t packed; ofono_bool_t yesno; /* * If a "Yes/No" answer was requested in a GET INKEY command, * .yesno must be TRUE and text should be non-NULL to indicate * a Yes response or NULL to indicate a No response. */ }; struct stk_ussd_text { ofono_bool_t has_text; const unsigned char *text; int dcs; int len; }; struct stk_channel { unsigned char id; enum stk_channel_status status; }; struct stk_response_get_inkey { struct stk_answer_text text; struct stk_duration duration; }; struct stk_response_get_input { struct stk_answer_text text; }; struct stk_response_poll_interval { struct stk_duration max_interval; }; struct stk_response_select_item { unsigned char item_id; }; struct stk_response_set_up_call { struct stk_common_byte_array cc_requested_action; struct { ofono_bool_t cc_modified; struct stk_result result; } modified_result; }; struct stk_response_local_info { union { struct stk_location_info location; const char *imei; struct stk_network_measurement_results { struct stk_common_byte_array nmr; struct stk_bcch_channel_list bcch_ch_list; } nmr; struct sms_scts datetime; const char *language; enum stk_battery_state battery_charge; enum stk_access_technology_type access_technology; struct stk_timing_advance tadv; /* Bits[31:24]: manufacturer, bits[23:0]: serial number */ guint32 esn; const char *imeisv; enum stk_network_search_mode { STK_NETWORK_SEARCH_MODE_MANUAL = 0x00, STK_NETWORK_SEARCH_MODE_AUTOMATIC = 0x01, } search_mode; const char *meid; struct stk_broadcast_network_information broadcast_network_info; struct stk_access_technologies { const enum stk_access_technology_type *techs; int length; } access_technologies; struct { struct stk_access_technologies access_techs; struct stk_location_info *locations; } location_infos; struct { struct stk_access_technologies access_techs; struct stk_network_measurement_results *nmrs; } nmrs; }; }; struct stk_response_timer_mgmt { unsigned char id; struct stk_timer_value value; }; struct stk_response_run_at_command { const char *at_response; }; struct stk_response_send_ussd { struct stk_ussd_text text; }; struct stk_response_open_channel { struct stk_channel channel; struct stk_bearer_description bearer_desc; unsigned short buf_size; }; struct stk_response_receive_data { struct stk_common_byte_array rx_data; unsigned short rx_remaining; }; struct stk_response_send_data { unsigned short tx_avail; }; struct stk_response_channel_status { struct stk_channel channel; }; struct stk_response { unsigned char number; unsigned char type; unsigned char qualifier; enum stk_device_identity_type src; enum stk_device_identity_type dst; struct stk_result result; union { struct stk_response_generic display_text; struct stk_response_get_inkey get_inkey; struct stk_response_get_input get_input; struct stk_response_generic more_time; struct stk_response_generic play_tone; struct stk_response_poll_interval poll_interval; struct stk_response_generic refresh; struct stk_response_generic set_up_menu; struct stk_response_select_item select_item; struct stk_response_generic send_sms; struct stk_response_set_up_call set_up_call; struct stk_response_generic polling_off; struct stk_response_local_info provide_local_info; struct stk_response_generic set_up_event_list; struct stk_response_timer_mgmt timer_mgmt; struct stk_response_generic set_up_idle_mode_text; struct stk_response_run_at_command run_at_command; struct stk_response_generic send_dtmf; struct stk_response_generic language_notification; struct stk_response_generic launch_browser; struct stk_response_send_ussd send_ussd; struct stk_response_open_channel open_channel; struct stk_response_receive_data receive_data; struct stk_response_send_data send_data; struct stk_response_channel_status channel_status; }; void (*destructor)(struct stk_response *response); }; /* ENVELOPEs defined in TS 102.223 Section 7 */ struct stk_envelope_sms_pp_download { struct stk_address address; struct sms_deliver message; }; struct stk_envelope_cbs_pp_download { struct cbs page; }; struct stk_envelope_menu_selection { unsigned char item_id; ofono_bool_t help_request; }; struct stk_envelope_sms_mo_control { struct stk_address sc_address; struct stk_address dest_address; struct stk_location_info location; }; enum stk_call_control_type { STK_CC_TYPE_CALL_SETUP, STK_CC_TYPE_SUPPLEMENTARY_SERVICE, STK_CC_TYPE_USSD_OP, STK_CC_TYPE_PDP_CTX_ACTIVATION, STK_CC_TYPE_EPS_PDN_CONNECTION_ACTIVATION, }; /* Used both in the ENVELOPE message to UICC and response from UICC */ struct stk_envelope_call_control { enum stk_call_control_type type; union { struct stk_address address; struct stk_address ss_string; struct stk_ussd_string ussd_string; struct stk_common_byte_array pdp_ctx_params; struct stk_common_byte_array eps_pdn_params; }; /* * At least one of the following two fields must be present in a * response indicating modification of the call. * In an EVELOPE message, only allowed for a call setup. */ struct stk_ccp ccp1; struct stk_subaddress subaddress; struct stk_location_info location; /* Only allowed when ccp1 is present */ struct stk_ccp ccp2; char *alpha_id; /* Only allowed when both ccp1 and ccp2 are present */ struct stk_bc_repeat bc_repeat; }; struct stk_envelope_event_download { enum stk_event_type type; union { struct { unsigned char transaction_id; struct stk_address caller_address; struct stk_subaddress caller_subaddress; } mt_call; struct { unsigned char transaction_id; } call_connected; struct { struct stk_transaction_id transaction_ids; struct stk_cause cause; } call_disconnected; struct { enum stk_service_state state; /* Present when state indicated Normal Service */ struct stk_location_info info; } location_status; struct stk_reader_status card_reader_status; char language_selection[3]; struct { enum stk_browser_termination_cause cause; } browser_termination; struct { struct stk_channel channel; unsigned short channel_data_len; } data_available; struct { struct stk_channel channel; struct stk_bearer_description bearer_desc; struct stk_other_address address; } channel_status; struct stk_access_technologies access_technology_change; struct stk_display_parameters display_params_changed; struct { /* * Note the service record subfield is not required, * only the Technology id and Service id. */ struct stk_service_record service_record; struct stk_remote_entity_address remote_addr; struct stk_uicc_te_interface transport_level; /* Only present if transport_level present */ struct stk_other_address transport_addr; } local_connection; enum stk_network_search_mode network_search_mode_change; struct stk_common_byte_array browsing_status; struct stk_frames_info frames_information_change; enum stk_i_wlan_access_status i_wlan_access_status; struct { struct stk_location_info location; struct stk_routing_area_info rai; struct stk_tracking_area_id tai; enum stk_access_technology_type access_tech; enum stk_update_attach_type update_attach; enum stk_rejection_cause_code cause; } network_rejection; }; }; struct stk_envelope_timer_expiration { unsigned char id; struct stk_timer_value value; }; struct stk_envelope_ussd_data_download { struct stk_ussd_string string; }; struct stk_envelope_mms_transfer_status { struct stk_file transfer_file; struct stk_mms_id id; struct stk_mms_transfer_status transfer_status; }; struct stk_envelope_mms_notification_download { struct stk_common_byte_array msg; ofono_bool_t last; }; struct stk_envelope_terminal_apps { struct stk_registry_application_data *list; int count; ofono_bool_t last; }; struct stk_envelope { enum stk_envelope_type type; enum stk_device_identity_type src; enum stk_device_identity_type dst; union { struct stk_envelope_sms_pp_download sms_pp_download; struct stk_envelope_cbs_pp_download cbs_pp_download; struct stk_envelope_menu_selection menu_selection; struct stk_envelope_call_control call_control; struct stk_envelope_sms_mo_control sms_mo_control; struct stk_envelope_event_download event_download; struct stk_envelope_timer_expiration timer_expiration; struct stk_envelope_ussd_data_download ussd_data_download; struct stk_envelope_mms_transfer_status mms_status; struct stk_envelope_mms_notification_download mms_notification; struct stk_envelope_terminal_apps terminal_apps; }; }; struct stk_command *stk_command_new_from_pdu(const unsigned char *pdu, unsigned int len); void stk_command_free(struct stk_command *command); const unsigned char *stk_pdu_from_response(const struct stk_response *response, unsigned int *out_length); const unsigned char *stk_pdu_from_envelope(const struct stk_envelope *envelope, unsigned int *out_length); char *stk_text_to_html(const char *text, const unsigned short *attrs, int num_attrs); char *stk_image_to_xpm(const unsigned char *img, unsigned int len, enum stk_img_scheme scheme, const unsigned char *clut, unsigned short clut_len); ofono-1.17.bzr6912+16.04.20160314.3/src/message.c0000644000015600001650000001414212671500024020760 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include "ofono.h" #include "message.h" struct message { struct ofono_uuid uuid; enum message_state state; struct ofono_atom *atom; void *data; }; static const char *message_state_to_string(enum message_state s) { switch (s) { case MESSAGE_STATE_PENDING: return "pending"; case MESSAGE_STATE_SENT: return "sent"; case MESSAGE_STATE_FAILED: return "failed"; case MESSAGE_STATE_CANCELLED: return "cancelled"; } return NULL; } static DBusMessage *message_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct message *m = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); message_append_properties(m, &dict); dbus_message_iter_close_container(&iter, &dict); return reply; } static DBusMessage *message_cancel(DBusConnection *conn, DBusMessage *msg, void *data) { struct message *m = data; int res; if (dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (m->state != MESSAGE_STATE_PENDING) return __ofono_error_not_available(msg); res = __ofono_sms_txq_cancel(__ofono_atom_get_data(m->atom), &m->uuid); switch (res) { case -ENOENT: return __ofono_error_not_found(msg); case -EPERM: return __ofono_error_access_denied(msg); case 0: return dbus_message_new_method_return(msg); default: return __ofono_error_failed(msg); } } static const GDBusMethodTable message_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), message_get_properties) }, { GDBUS_METHOD("Cancel", NULL, NULL, message_cancel) }, { } }; static const GDBusSignalTable message_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; struct message *message_create(const struct ofono_uuid *uuid, struct ofono_atom *atom) { struct message *v; v = g_try_new0(struct message, 1); if (v == NULL) return NULL; memcpy(&v->uuid, uuid, sizeof(*uuid)); v->atom = atom; return v; } static void message_destroy(gpointer userdata) { struct message *m = userdata; g_free(m); } gboolean message_dbus_register(struct message *m) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = message_path_from_uuid(m->atom, &m->uuid); if (!g_dbus_register_interface(conn, path, OFONO_MESSAGE_INTERFACE, message_methods, message_signals, NULL, m, message_destroy)) { ofono_error("Could not register Message %s", path); message_destroy(m); return FALSE; } return TRUE; } void message_dbus_unregister(struct message *m) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = message_path_from_uuid(m->atom, &m->uuid); g_dbus_unregister_interface(conn, path, OFONO_MESSAGE_INTERFACE); return; } const struct ofono_uuid *message_get_uuid(const struct message *m) { return &m->uuid; } void message_set_state(struct message *m, enum message_state new_state) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; const char *state; if (m->state == new_state) return; path = message_path_from_uuid(m->atom, &m->uuid); m->state = new_state; state = message_state_to_string(m->state); ofono_dbus_signal_property_changed(conn, path, OFONO_MESSAGE_INTERFACE, "State", DBUS_TYPE_STRING, &state); } void message_append_properties(struct message *m, DBusMessageIter *dict) { const char *state = message_state_to_string(m->state); ofono_dbus_dict_append(dict, "State", DBUS_TYPE_STRING, &state); } void message_emit_added(struct message *m, const char *interface) { DBusMessage *signal; DBusMessageIter iter; DBusMessageIter dict; const char *path; const char *atompath = __ofono_atom_get_path(m->atom); signal = dbus_message_new_signal(atompath, interface, "MessageAdded"); if (signal == NULL) return; path = message_path_from_uuid(m->atom, &m->uuid); dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); message_append_properties(m, &dict); dbus_message_iter_close_container(&iter, &dict); g_dbus_send_message(ofono_dbus_get_connection(), signal); } void message_emit_removed(struct message *m, const char *interface) { DBusConnection *conn = ofono_dbus_get_connection(); const char *atompath = __ofono_atom_get_path(m->atom); const char *path = message_path_from_uuid(m->atom, &m->uuid); g_dbus_emit_signal(conn, atompath, interface, "MessageRemoved", DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); } const char *message_path_from_uuid(struct ofono_atom *atom, const struct ofono_uuid *uuid) { static char path[256]; const char *atompath = __ofono_atom_get_path(atom); snprintf(path, sizeof(path), "%s/message_%s", atompath, ofono_uuid_to_str(uuid)); return path; } void *message_get_data(struct message *m) { return m->data; } void message_set_data(struct message *m, void *data) { m->data = data; } ofono-1.17.bzr6912+16.04.20160314.3/src/ussd.c0000644000015600001650000005177312671500024020325 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include "ofono.h" #include "common.h" #include "smsutil.h" #include "util.h" #define MAX_USSD_LENGTH 160 static GSList *g_drivers = NULL; enum ussd_state { USSD_STATE_IDLE = 0, USSD_STATE_ACTIVE = 1, USSD_STATE_USER_ACTION = 2, USSD_STATE_RESPONSE_SENT, }; struct ussd_request { ofono_ussd_request_cb_t cb; void *user_data; }; struct ofono_ussd { int state; DBusMessage *pending; DBusMessage *cancel; int flags; GSList *ss_control_list; GSList *ss_passwd_list; const struct ofono_ussd_driver *driver; void *driver_data; struct ofono_atom *atom; struct ussd_request *req; }; struct ssc_entry { char *service; void *cb; void *user; ofono_destroy_func destroy; }; gboolean __ofono_ussd_is_busy(struct ofono_ussd *ussd) { if (ussd == NULL) return FALSE; if (ussd->pending || ussd->state != USSD_STATE_IDLE || ussd->req) return TRUE; return FALSE; } static struct ssc_entry *ssc_entry_create(const char *sc, void *cb, void *data, ofono_destroy_func destroy) { struct ssc_entry *r; r = g_try_new0(struct ssc_entry, 1); if (r == NULL) return r; r->service = g_strdup(sc); r->cb = cb; r->user = data; r->destroy = destroy; return r; } static void ssc_entry_destroy(struct ssc_entry *ca) { if (ca->destroy) ca->destroy(ca->user); g_free(ca->service); g_free(ca); } static gint ssc_entry_find_by_service(gconstpointer a, gconstpointer b) { const struct ssc_entry *ca = a; return strcmp(ca->service, b); } gboolean __ofono_ussd_ssc_register(struct ofono_ussd *ussd, const char *sc, ofono_ussd_ssc_cb_t cb, void *data, ofono_destroy_func destroy) { struct ssc_entry *entry; if (ussd == NULL) return FALSE; entry = ssc_entry_create(sc, cb, data, destroy); if (entry == NULL) return FALSE; ussd->ss_control_list = g_slist_prepend(ussd->ss_control_list, entry); return TRUE; } void __ofono_ussd_ssc_unregister(struct ofono_ussd *ussd, const char *sc) { GSList *l; if (ussd == NULL) return; l = g_slist_find_custom(ussd->ss_control_list, sc, ssc_entry_find_by_service); if (l == NULL) return; ssc_entry_destroy(l->data); ussd->ss_control_list = g_slist_remove(ussd->ss_control_list, l->data); } gboolean __ofono_ussd_passwd_register(struct ofono_ussd *ussd, const char *sc, ofono_ussd_passwd_cb_t cb, void *data, ofono_destroy_func destroy) { struct ssc_entry *entry; if (ussd == NULL) return FALSE; entry = ssc_entry_create(sc, cb, data, destroy); if (entry == NULL) return FALSE; ussd->ss_passwd_list = g_slist_prepend(ussd->ss_passwd_list, entry); return TRUE; } void __ofono_ussd_passwd_unregister(struct ofono_ussd *ussd, const char *sc) { GSList *l; if (ussd == NULL) return; l = g_slist_find_custom(ussd->ss_passwd_list, sc, ssc_entry_find_by_service); if (l == NULL) return; ssc_entry_destroy(l->data); ussd->ss_passwd_list = g_slist_remove(ussd->ss_passwd_list, l->data); } static gboolean recognized_passwd_change_string(struct ofono_ussd *ussd, int type, char *sc, char *sia, char *sib, char *sic, char *sid, char *dn, DBusMessage *msg) { GSList *l = ussd->ss_passwd_list; switch (type) { case SS_CONTROL_TYPE_ACTIVATION: case SS_CONTROL_TYPE_REGISTRATION: break; default: return FALSE; } if (strcmp(sc, "03") || strlen(dn)) return FALSE; /* If SIC & SID don't match, then we just bail out here */ if (strcmp(sic, sid)) { DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *reply = __ofono_error_invalid_format(msg); g_dbus_send_message(conn, reply); return TRUE; } while ((l = g_slist_find_custom(l, sia, ssc_entry_find_by_service)) != NULL) { struct ssc_entry *entry = l->data; ofono_ussd_passwd_cb_t cb = entry->cb; if (cb(sia, sib, sic, msg, entry->user)) return TRUE; l = l->next; } return FALSE; } static gboolean recognized_control_string(struct ofono_ussd *ussd, const char *ss_str, DBusMessage *msg) { char *str = g_strdup(ss_str); char *sc, *sia, *sib, *sic, *sid, *dn; int type; gboolean ret = FALSE; DBG("parsing control string"); if (parse_ss_control_string(str, &type, &sc, &sia, &sib, &sic, &sid, &dn)) { GSList *l = ussd->ss_control_list; DBG("Got parse result: %d, %s, %s, %s, %s, %s, %s", type, sc, sia, sib, sic, sid, dn); /* * A password change string needs to be treated separately * because it uses a fourth SI and is thus not a valid * control string. */ if (recognized_passwd_change_string(ussd, type, sc, sia, sib, sic, sid, dn, msg)) { ret = TRUE; goto out; } if (*sid != '\0') goto out; while ((l = g_slist_find_custom(l, sc, ssc_entry_find_by_service)) != NULL) { struct ssc_entry *entry = l->data; ofono_ussd_ssc_cb_t cb = entry->cb; if (cb(type, sc, sia, sib, sic, dn, msg, entry->user)) { ret = TRUE; goto out; } l = l->next; } } /* TODO: Handle all strings that control voice calls */ /* TODO: Handle Multiple subscriber profile DN*59#SEND and *59#SEND */ /* * Note: SIM PIN/PIN2 change and unblock and IMEI presentation * procedures are not handled by the daemon since they are not followed * by SEND and are not valid USSD requests. */ out: g_free(str); return ret; } static const char *ussd_get_state_string(struct ofono_ussd *ussd) { switch (ussd->state) { case USSD_STATE_IDLE: return "idle"; case USSD_STATE_ACTIVE: case USSD_STATE_RESPONSE_SENT: return "active"; case USSD_STATE_USER_ACTION: return "user-response"; } return ""; } static void ussd_change_state(struct ofono_ussd *ussd, int state) { const char *value; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(ussd->atom); if (state == ussd->state) return; ussd->state = state; value = ussd_get_state_string(ussd); ofono_dbus_signal_property_changed(conn, path, OFONO_SUPPLEMENTARY_SERVICES_INTERFACE, "State", DBUS_TYPE_STRING, &value); } static void ussd_request_finish(struct ofono_ussd *ussd, int error, int dcs, const unsigned char *pdu, int len) { struct ussd_request *req = ussd->req; if (req && req->cb) req->cb(error, dcs, pdu, len, req->user_data); g_free(req); ussd->req = NULL; } static int ussd_status_to_failure_code(int status) { switch (status) { case OFONO_USSD_STATUS_TIMED_OUT: return -ETIMEDOUT; case OFONO_USSD_STATUS_NOT_SUPPORTED: return -ENOSYS; } return 0; } static char const *ussd_status_name(int status) { switch (status) { case OFONO_USSD_STATUS_NOTIFY: return "NOTIFY"; case OFONO_USSD_STATUS_ACTION_REQUIRED: return "ACTION_REQUIRED"; case OFONO_USSD_STATUS_TERMINATED: return "TERMINATED"; case OFONO_USSD_STATUS_LOCAL_CLIENT_RESPONDED: return "LOCAL_CLIENT_RESPONDED"; case OFONO_USSD_STATUS_NOT_SUPPORTED: return "NOT_SUPPORTED"; case OFONO_USSD_STATUS_TIMED_OUT: return "TIMED_OUT"; } return "????"; } static const char *ussd_state_name(enum ussd_state state) { switch (state) { case USSD_STATE_IDLE: return "IDLE"; case USSD_STATE_ACTIVE: return "ACTIVE"; case USSD_STATE_RESPONSE_SENT: return "RESPONSE_SENT"; case USSD_STATE_USER_ACTION: return "USER_ACTION"; } return "????"; } void ofono_ussd_notify(struct ofono_ussd *ussd, int status, int dcs, const unsigned char *data, int data_len) { DBusConnection *conn = ofono_dbus_get_connection(); const char *ussdstr = "USSD"; char *utf8_str = NULL; const char *str; const char sig[] = { DBUS_TYPE_STRING, 0 }; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter variant; DBG("status: %d %s, state: %d %s", status, ussd_status_name(status), ussd->state, ussd_state_name(ussd->state)); if (ussd->req && (status == OFONO_USSD_STATUS_NOTIFY || status == OFONO_USSD_STATUS_TERMINATED || status == OFONO_USSD_STATUS_TIMED_OUT || status == OFONO_USSD_STATUS_NOT_SUPPORTED)) { ussd_request_finish(ussd, ussd_status_to_failure_code(status), dcs, data, data_len); ussd_change_state(ussd, USSD_STATE_IDLE); return; } if (status == OFONO_USSD_STATUS_TERMINATED) { ussd_change_state(ussd, USSD_STATE_IDLE); if (ussd->pending == NULL) return; reply = __ofono_error_network_terminated(ussd->pending); goto out; } if (status == OFONO_USSD_STATUS_NOT_SUPPORTED) { ussd_change_state(ussd, USSD_STATE_IDLE); if (ussd->pending == NULL) return; reply = __ofono_error_not_supported(ussd->pending); goto out; } if (status == OFONO_USSD_STATUS_TIMED_OUT) { ussd_change_state(ussd, USSD_STATE_IDLE); if (ussd->pending == NULL) return; reply = __ofono_error_timed_out(ussd->pending); goto out; } if (data && data_len > 0) utf8_str = ussd_decode(dcs, data_len, data); str = utf8_str; /* TODO: Rework this in the Agent framework */ if (ussd->state == USSD_STATE_ACTIVE) { reply = dbus_message_new_method_return(ussd->pending); if (str == NULL) str = ""; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ussdstr); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &variant); dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &str); dbus_message_iter_close_container(&iter, &variant); if (status == OFONO_USSD_STATUS_ACTION_REQUIRED) ussd_change_state(ussd, USSD_STATE_USER_ACTION); else ussd_change_state(ussd, USSD_STATE_IDLE); } else if (ussd->state == USSD_STATE_RESPONSE_SENT) { reply = dbus_message_new_method_return(ussd->pending); if (str == NULL) str = ""; dbus_message_append_args(reply, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID); if (status == OFONO_USSD_STATUS_ACTION_REQUIRED) ussd_change_state(ussd, USSD_STATE_USER_ACTION); else ussd_change_state(ussd, USSD_STATE_IDLE); } else if (ussd->state == USSD_STATE_IDLE) { const char *signal_name; const char *path = __ofono_atom_get_path(ussd->atom); int new_state; if (status == OFONO_USSD_STATUS_ACTION_REQUIRED) { new_state = USSD_STATE_USER_ACTION; signal_name = "RequestReceived"; } else { new_state = USSD_STATE_IDLE; signal_name = "NotificationReceived"; } if (str == NULL) str = ""; g_dbus_emit_signal(conn, path, OFONO_SUPPLEMENTARY_SERVICES_INTERFACE, signal_name, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID); ussd_change_state(ussd, new_state); goto free; } else { ofono_error("Received an unsolicited USSD but can't handle."); DBG("USSD is: status: %d, %s", status, str); goto free; } out: g_dbus_send_message(conn, reply); dbus_message_unref(ussd->pending); ussd->pending = NULL; free: g_free(utf8_str); } static void ussd_callback(const struct ofono_error *error, void *data) { struct ofono_ussd *ussd = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) DBG("ussd request failed with error: %s", telephony_error_to_str(error)); if (error->type == OFONO_ERROR_TYPE_NO_ERROR) { ussd_change_state(ussd, USSD_STATE_ACTIVE); return; } if (ussd->pending == NULL) return; reply = __ofono_error_failed(ussd->pending); __ofono_dbus_pending_reply(&ussd->pending, reply); } static DBusMessage *ussd_initiate(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_ussd *ussd = data; struct ofono_modem *modem = __ofono_atom_get_modem(ussd->atom); struct ofono_voicecall *vc; gboolean call_in_progress; const char *str; int dcs; unsigned char buf[160]; long num_packed; if (__ofono_ussd_is_busy(ussd)) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (strlen(str) == 0) return __ofono_error_invalid_format(msg); DBG("checking if this is a recognized control string"); if (recognized_control_string(ussd, str, msg)) return NULL; vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, modem); if (vc) call_in_progress = __ofono_voicecall_is_busy(vc, OFONO_VOICECALL_INTERACTION_NONE); else call_in_progress = FALSE; DBG("No.., checking if this is a USSD string"); if (!valid_ussd_string(str, call_in_progress)) return __ofono_error_not_recognized(msg); if (!ussd_dcs_encode(str, &dcs, &num_packed, buf)) return __ofono_error_invalid_format(msg); if (ussd->driver->request == NULL) return __ofono_error_not_implemented(msg); DBG("OK, running USSD request"); ussd->pending = dbus_message_ref(msg); ussd->driver->request(ussd, dcs, buf, num_packed, ussd_callback, ussd); return NULL; } static void ussd_response_callback(const struct ofono_error *error, void *data) { struct ofono_ussd *ussd = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) DBG("ussd response failed with error: %s", telephony_error_to_str(error)); if (error->type == OFONO_ERROR_TYPE_NO_ERROR) { ussd_change_state(ussd, USSD_STATE_RESPONSE_SENT); return; } if (ussd->pending == NULL) return; reply = __ofono_error_failed(ussd->pending); __ofono_dbus_pending_reply(&ussd->pending, reply); } static DBusMessage *ussd_respond(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_ussd *ussd = data; const char *str; int dcs; unsigned char buf[160]; long num_packed; if (ussd->pending) return __ofono_error_busy(msg); if (ussd->state != USSD_STATE_USER_ACTION) return __ofono_error_not_active(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (strlen(str) == 0) return __ofono_error_invalid_format(msg); if (!ussd_dcs_encode(str, &dcs, &num_packed, buf)) return __ofono_error_invalid_format(msg); if (ussd->driver->request == NULL) return __ofono_error_not_implemented(msg); ussd->pending = dbus_message_ref(msg); ussd->driver->request(ussd, dcs, buf, num_packed, ussd_response_callback, ussd); return NULL; } static void ussd_cancel_callback(const struct ofono_error *error, void *data) { struct ofono_ussd *ussd = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("ussd cancel failed with error: %s", telephony_error_to_str(error)); reply = __ofono_error_failed(ussd->cancel); __ofono_dbus_pending_reply(&ussd->cancel, reply); return; } if (ussd->pending) { reply = __ofono_error_canceled(ussd->pending); __ofono_dbus_pending_reply(&ussd->pending, reply); } reply = dbus_message_new_method_return(ussd->cancel); __ofono_dbus_pending_reply(&ussd->cancel, reply); if (ussd->req) ussd_request_finish(ussd, -ECANCELED, 0, NULL, 0); ussd_change_state(ussd, USSD_STATE_IDLE); } static DBusMessage *ussd_cancel(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_ussd *ussd = data; if (ussd->state == USSD_STATE_IDLE) return __ofono_error_not_active(msg); /* We have called Respond() but not returned from its callback yet */ if (ussd->state == USSD_STATE_USER_ACTION && ussd->pending) return __ofono_error_busy(msg); if (ussd->cancel) return __ofono_error_busy(msg); if (ussd->driver->cancel == NULL) return __ofono_error_not_implemented(msg); ussd->cancel = dbus_message_ref(msg); ussd->driver->cancel(ussd, ussd_cancel_callback, ussd); return NULL; } static DBusMessage *ussd_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_ussd *ussd = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; const char *value; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); value = ussd_get_state_string(ussd); ofono_dbus_dict_append(&dict, "State", DBUS_TYPE_STRING, &value); dbus_message_iter_close_container(&iter, &dict); return reply; } static const GDBusMethodTable ussd_methods[] = { { GDBUS_ASYNC_METHOD("Initiate", GDBUS_ARGS({ "command", "s" }), GDBUS_ARGS({ "result_name", "s" }, { "value", "v" }), ussd_initiate) }, { GDBUS_ASYNC_METHOD("Respond", GDBUS_ARGS({ "reply", "s" }), GDBUS_ARGS({ "result", "s" }), ussd_respond) }, { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL, ussd_cancel) }, { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), ussd_get_properties) }, { } }; static const GDBusSignalTable ussd_signals[] = { { GDBUS_SIGNAL("NotificationReceived", GDBUS_ARGS({ "message", "s" })) }, { GDBUS_SIGNAL("RequestReceived", GDBUS_ARGS({ "message", "s" })) }, { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; int ofono_ussd_driver_register(const struct ofono_ussd_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_ussd_driver_unregister(const struct ofono_ussd_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void ussd_unregister(struct ofono_atom *atom) { struct ofono_ussd *ussd = __ofono_atom_get_data(atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); g_slist_foreach(ussd->ss_control_list, (GFunc) ssc_entry_destroy, NULL); g_slist_free(ussd->ss_control_list); ussd->ss_control_list = NULL; g_slist_foreach(ussd->ss_passwd_list, (GFunc) ssc_entry_destroy, NULL); g_slist_free(ussd->ss_passwd_list); ussd->ss_passwd_list = NULL; ofono_modem_remove_interface(modem, OFONO_SUPPLEMENTARY_SERVICES_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_SUPPLEMENTARY_SERVICES_INTERFACE); } static void ussd_remove(struct ofono_atom *atom) { struct ofono_ussd *ussd = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (ussd == NULL) return; if (ussd->driver && ussd->driver->remove) ussd->driver->remove(ussd); g_free(ussd); } struct ofono_ussd *ofono_ussd_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_ussd *ussd; GSList *l; if (driver == NULL) return NULL; ussd = g_try_new0(struct ofono_ussd, 1); if (ussd == NULL) return NULL; ussd->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_USSD, ussd_remove, ussd); for (l = g_drivers; l; l = l->next) { const struct ofono_ussd_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(ussd, vendor, data) < 0) continue; ussd->driver = drv; break; } return ussd; } void ofono_ussd_register(struct ofono_ussd *ussd) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(ussd->atom); const char *path = __ofono_atom_get_path(ussd->atom); if (!g_dbus_register_interface(conn, path, OFONO_SUPPLEMENTARY_SERVICES_INTERFACE, ussd_methods, ussd_signals, NULL, ussd, NULL)) { ofono_error("Could not create %s interface", OFONO_SUPPLEMENTARY_SERVICES_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_SUPPLEMENTARY_SERVICES_INTERFACE); __ofono_atom_register(ussd->atom, ussd_unregister); } void ofono_ussd_remove(struct ofono_ussd *ussd) { __ofono_atom_free(ussd->atom); } void ofono_ussd_set_data(struct ofono_ussd *ussd, void *data) { ussd->driver_data = data; } void *ofono_ussd_get_data(struct ofono_ussd *ussd) { return ussd->driver_data; } static void ussd_request_callback(const struct ofono_error *error, void *data) { struct ofono_ussd *ussd = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) ussd_request_finish(ussd, -EINVAL, 0, NULL, 0); else ussd_change_state(ussd, USSD_STATE_ACTIVE); } int __ofono_ussd_initiate(struct ofono_ussd *ussd, int dcs, const unsigned char *pdu, int len, ofono_ussd_request_cb_t cb, void *user_data) { struct ussd_request *req; if (ussd->driver->request == NULL) return -ENOSYS; if (__ofono_ussd_is_busy(ussd)) return -EBUSY; req = g_try_new0(struct ussd_request, 1); if (req == NULL) return -ENOMEM; req->cb = cb; req->user_data = user_data; ussd->req = req; ussd->driver->request(ussd, dcs, pdu, len, ussd_request_callback, ussd); return 0; } void __ofono_ussd_initiate_cancel(struct ofono_ussd *ussd) { if (ussd->req == NULL || ussd->req->cb == NULL) return; ussd->req->cb = NULL; } ofono-1.17.bzr6912+16.04.20160314.3/src/cdma-connman.c0000644000015600001650000004115212671500024021670 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" static GSList *g_drivers; struct cdma_connman_settings { char *interface; gboolean static_ip; char *ip; char *netmask; char *gateway; char **dns; }; struct ofono_cdma_connman { ofono_bool_t powered; ofono_bool_t dormant; struct cdma_connman_settings *settings; DBusMessage *pending; const struct ofono_cdma_connman_driver *driver; void *driver_data; struct ofono_atom *atom; char username[OFONO_CDMA_CONNMAN_MAX_USERNAME_LENGTH + 1]; char password[OFONO_CDMA_CONNMAN_MAX_PASSWORD_LENGTH + 1]; }; static void cdma_connman_settings_free(struct cdma_connman_settings *settings) { DBG(""); g_free(settings->interface); g_free(settings->ip); g_free(settings->netmask); g_free(settings->gateway); g_strfreev(settings->dns); g_free(settings); } static void cdma_connman_ifupdown(const char *interface, ofono_bool_t active) { struct ifreq ifr; int sk; DBG(""); if (interface == NULL) return; sk = socket(PF_INET, SOCK_DGRAM, 0); if (sk < 0) return; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, interface, IFNAMSIZ - 1); if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) goto done; if (active == TRUE) { if (ifr.ifr_flags & IFF_UP) goto done; ifr.ifr_flags |= IFF_UP; } else { if (!(ifr.ifr_flags & IFF_UP)) goto done; ifr.ifr_flags &= ~IFF_UP; } if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) ofono_error("Failed to change interface flags"); done: close(sk); } static void cdma_connman_settings_append_variant( struct cdma_connman_settings *settings, DBusMessageIter *iter) { DBusMessageIter variant; DBusMessageIter array; char typesig[5]; char arraysig[6]; const char *method; DBG(""); arraysig[0] = DBUS_TYPE_ARRAY; arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR; arraysig[2] = typesig[1] = DBUS_TYPE_STRING; arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT; arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR; arraysig[5] = typesig[4] = '\0'; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, arraysig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, typesig, &array); if (settings == NULL) goto done; ofono_dbus_dict_append(&array, "Interface", DBUS_TYPE_STRING, &settings->interface); if (settings->static_ip == TRUE) method = "static"; else method = "dhcp"; ofono_dbus_dict_append(&array, "Method", DBUS_TYPE_STRING, &method); if (settings->ip) ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING, &settings->ip); if (settings->netmask) ofono_dbus_dict_append(&array, "Netmask", DBUS_TYPE_STRING, &settings->netmask); if (settings->gateway) ofono_dbus_dict_append(&array, "Gateway", DBUS_TYPE_STRING, &settings->gateway); if (settings->dns) ofono_dbus_dict_append_array(&array, "DomainNameServers", DBUS_TYPE_STRING, &settings->dns); done: dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(iter, &variant); } static void cdma_connman_settings_signal(struct ofono_cdma_connman *cm) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; DBusMessage *signal; DBusMessageIter iter; const char *prop = "Settings"; DBG(""); path = __ofono_atom_get_path(cm->atom); signal = dbus_message_new_signal(path, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE, "PropertyChanged"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &prop); cdma_connman_settings_append_variant(cm->settings, &iter); g_dbus_send_message(conn, signal); } static void cdma_connman_settings_update(struct ofono_cdma_connman *cm, const char *interface, ofono_bool_t static_ip, const char *ip, const char *netmask, const char *gateway, const char **dns) { DBG(""); if (cm->settings) cdma_connman_settings_free(cm->settings); cm->settings = g_try_new0(struct cdma_connman_settings, 1); if (cm->settings == NULL) return; cm->settings->interface = g_strdup(interface); cm->settings->static_ip = static_ip; cm->settings->ip = g_strdup(ip); cm->settings->netmask = g_strdup(netmask); cm->settings->gateway = g_strdup(gateway); cm->settings->dns = g_strdupv((char **)dns); cdma_connman_ifupdown(interface, TRUE); cdma_connman_settings_signal(cm); } static void cdma_connman_settings_reset(struct ofono_cdma_connman *cm) { char *interface; DBG(""); if (cm->settings == NULL) return; interface = cm->settings->interface; cm->settings->interface = NULL; cdma_connman_settings_free(cm->settings); cm->settings = NULL; cdma_connman_settings_signal(cm); cdma_connman_ifupdown(interface, FALSE); g_free(interface); } static void activate_callback(const struct ofono_error *error, const char *interface, ofono_bool_t static_ip, const char *ip, const char *netmask, const char *gateway, const char **dns, void *data) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_cdma_connman *cm = data; dbus_bool_t value; const char *path; DBG("%p %s", cm, interface); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Activating packet data service failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&cm->pending, __ofono_error_failed(cm->pending)); return; } cm->powered = TRUE; __ofono_dbus_pending_reply(&cm->pending, dbus_message_new_method_return(cm->pending)); /* * If we don't have the interface, don't bother emitting any settings, * as nobody can make use of them */ if (interface != NULL) cdma_connman_settings_update(cm, interface, static_ip, ip, netmask, gateway, dns); path = __ofono_atom_get_path(cm->atom); value = cm->powered; ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &value); } static void deactivate_callback(const struct ofono_error *error, void *data) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_cdma_connman *cm = data; dbus_bool_t value; const char *path; DBG(""); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Deactivating packet data service failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&cm->pending, __ofono_error_failed(cm->pending)); return; } cm->powered = FALSE; __ofono_dbus_pending_reply(&cm->pending, dbus_message_new_method_return(cm->pending)); cdma_connman_settings_reset(cm); path = __ofono_atom_get_path(cm->atom); value = cm->powered; ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &value); } static void cdma_connman_settings_append_properties( struct ofono_cdma_connman *cm, DBusMessageIter *dict) { DBusMessageIter entry; const char *key = "Settings"; DBG(""); dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); cdma_connman_settings_append_variant(cm->settings, &entry); dbus_message_iter_close_container(dict, &entry); } static ofono_bool_t network_registered(struct ofono_cdma_connman *cm) { int status; struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom); struct ofono_cdma_netreg *cdma_netreg; cdma_netreg = __ofono_atom_find(OFONO_ATOM_TYPE_CDMA_NETREG, modem); if (cdma_netreg == NULL) return FALSE; status = ofono_cdma_netreg_get_status(cdma_netreg); switch (status) { case NETWORK_REGISTRATION_STATUS_REGISTERED: case NETWORK_REGISTRATION_STATUS_ROAMING: return TRUE; default: break; } return FALSE; } static DBusMessage *cdma_connman_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cdma_connman *cm = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; dbus_bool_t value; DBG(""); reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); value = cm->powered; ofono_dbus_dict_append(&dict, "Powered", DBUS_TYPE_BOOLEAN, &value); value = cm->dormant; ofono_dbus_dict_append(&dict, "Dormant", DBUS_TYPE_BOOLEAN, &value); if (cm->settings) cdma_connman_settings_append_properties(cm, &dict); dbus_message_iter_close_container(&iter, &dict); return reply; } static DBusMessage *cdma_connman_set_username(struct ofono_cdma_connman *cm, DBusConnection *conn, DBusMessage *msg, const char *username) { const char *path; if (strlen(username) > OFONO_CDMA_CONNMAN_MAX_USERNAME_LENGTH) return __ofono_error_invalid_format(msg); if (g_str_equal(username, cm->username)) return dbus_message_new_method_return(msg); strcpy(cm->username, username); g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); path = __ofono_atom_get_path(cm->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE, "Username", DBUS_TYPE_STRING, &username); return NULL; } static DBusMessage *cdma_connman_set_password(struct ofono_cdma_connman *cm, DBusConnection *conn, DBusMessage *msg, const char *password) { const char *path; if (strlen(password) > OFONO_CDMA_CONNMAN_MAX_PASSWORD_LENGTH) return __ofono_error_invalid_format(msg); if (g_str_equal(password, cm->password)) return dbus_message_new_method_return(msg); strcpy(cm->password, password); g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); path = __ofono_atom_get_path(cm->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE, "Password", DBUS_TYPE_STRING, &password); return NULL; } static DBusMessage *cdma_connman_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cdma_connman *cm = data; DBusMessageIter iter; DBusMessageIter var; const char *property; dbus_bool_t value; const char *str; DBG(""); if (cm->pending) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (!strcmp(property, "Powered")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (cm->powered == (ofono_bool_t) value) return dbus_message_new_method_return(msg); if (cm->driver == NULL || cm->driver->activate == NULL || cm->driver->deactivate == NULL) return __ofono_error_not_implemented(msg); if (network_registered(cm) == FALSE) return __ofono_error_not_registered(msg); cm->pending = dbus_message_ref(msg); if (value) cm->driver->activate(cm, cm->username, cm->password, activate_callback, cm); else cm->driver->deactivate(cm, deactivate_callback, cm); return NULL; } else if (!strcmp(property, "Username")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return cdma_connman_set_username(cm, conn, msg, str); } else if (!strcmp(property, "Password")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return cdma_connman_set_password(cm, conn, msg, str); } /* TODO: Dormant property. Not yet supported. */ return __ofono_error_invalid_args(msg); } static const GDBusMethodTable cdma_connman_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), cdma_connman_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, cdma_connman_set_property) }, { } }; static const GDBusSignalTable cdma_connman_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; int ofono_cdma_connman_driver_register( const struct ofono_cdma_connman_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_cdma_connman_driver_unregister( const struct ofono_cdma_connman_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } void ofono_cdma_connman_deactivated(struct ofono_cdma_connman *cm) { DBusConnection *conn = ofono_dbus_get_connection(); ofono_bool_t value; const char *path; if (cm == NULL) return; cdma_connman_settings_reset(cm); cm->powered = FALSE; value = cm->powered; path = __ofono_atom_get_path(cm->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &value); } void ofono_cdma_connman_dormant_notify(struct ofono_cdma_connman *cm, ofono_bool_t dormant) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; if (cm == NULL) return; cm->dormant = dormant; path = __ofono_atom_get_path(cm->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE, "Dormant", DBUS_TYPE_BOOLEAN, &dormant); } static void cdma_connman_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); DBG(""); g_dbus_unregister_interface(conn, path, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE); ofono_modem_remove_interface(modem, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE); } static void cdma_connman_remove(struct ofono_atom *atom) { struct ofono_cdma_connman *cm = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (cm == NULL) return; if (cm->driver && cm->driver->remove) cm->driver->remove(cm); g_free(cm); } struct ofono_cdma_connman *ofono_cdma_connman_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_cdma_connman *cm; GSList *l; DBG(""); if (driver == NULL) return NULL; cm = g_try_new0(struct ofono_cdma_connman, 1); if (cm == NULL) return NULL; cm->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CDMA_CONNMAN, cdma_connman_remove, cm); for (l = g_drivers; l; l = l->next) { const struct ofono_cdma_connman_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(cm, vendor, data) < 0) continue; cm->driver = drv; break; } return cm; } void ofono_cdma_connman_register(struct ofono_cdma_connman *cm) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(cm->atom); const char *path = __ofono_atom_get_path(cm->atom); DBG(""); if (!g_dbus_register_interface(conn, path, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE, cdma_connman_methods, cdma_connman_signals, NULL, cm, NULL)) { ofono_error("Could not create %s interface", OFONO_CDMA_CONNECTION_MANAGER_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_CDMA_CONNECTION_MANAGER_INTERFACE); __ofono_atom_register(cm->atom, cdma_connman_unregister); } void ofono_cdma_connman_remove(struct ofono_cdma_connman *cm) { __ofono_atom_free(cm->atom); } void ofono_cdma_connman_set_data(struct ofono_cdma_connman *cm, void *data) { cm->driver_data = data; } void *ofono_cdma_connman_get_data(struct ofono_cdma_connman *cm) { return cm->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/bluetooth.h0000644000015600001650000000400412671500024021342 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 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 * */ #ifndef AF_BLUETOOTH #define AF_BLUETOOTH 31 #define PF_BLUETOOTH AF_BLUETOOTH #endif #define BTPROTO_SCO 2 #define SOL_SCO 17 #ifndef SOL_BLUETOOTH #define SOL_BLUETOOTH 274 #endif #define BT_DEFER_SETUP 7 #define BT_VOICE 11 struct bt_voice { uint16_t setting; }; #define BT_VOICE_TRANSPARENT 0x0003 #define BT_VOICE_CVSD_16BIT 0x0060 /* BD Address */ typedef struct { uint8_t b[6]; } __attribute__((packed)) bdaddr_t; #define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) /* RFCOMM socket address */ struct sockaddr_rc { sa_family_t rc_family; bdaddr_t rc_bdaddr; uint8_t rc_channel; }; /* SCO socket address */ struct sockaddr_sco { sa_family_t sco_family; bdaddr_t sco_bdaddr; }; static inline void bt_bacpy(bdaddr_t *dst, const bdaddr_t *src) { memcpy(dst, src, sizeof(bdaddr_t)); } static inline int bt_ba2str(const bdaddr_t *ba, char *str) { return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]); } static inline int bt_bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2) { return memcmp(ba1, ba2, sizeof(bdaddr_t)); } static inline void bt_str2ba(const char *str, bdaddr_t *ba) { int i; for (i = 5; i >= 0; i--, str += 3) ba->b[i] = strtol(str, NULL, 16); } ofono-1.17.bzr6912+16.04.20160314.3/src/manager.c0000644000015600001650000000621412671500024020747 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include "ofono.h" static void append_modem(struct ofono_modem *modem, void *userdata) { DBusMessageIter *array = userdata; const char *path = ofono_modem_get_path(modem); DBusMessageIter entry, dict; if (ofono_modem_is_registered(modem) == FALSE) return; dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); __ofono_modem_append_properties(modem, &dict); dbus_message_iter_close_container(&entry, &dict); dbus_message_iter_close_container(array, &entry); } static DBusMessage *manager_get_modems(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array); __ofono_modem_foreach(append_modem, &array); dbus_message_iter_close_container(&iter, &array); return reply; } static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetModems", NULL, GDBUS_ARGS({ "modems", "a(oa{sv})" }), manager_get_modems) }, { } }; static const GDBusSignalTable manager_signals[] = { { GDBUS_SIGNAL("ModemAdded", GDBUS_ARGS({ "path", "o" }, { "properties", "a{sv}" })) }, { GDBUS_SIGNAL("ModemRemoved", GDBUS_ARGS({ "path", "o" })) }, { } }; int __ofono_manager_init(void) { DBusConnection *conn = ofono_dbus_get_connection(); gboolean ret; ret = g_dbus_register_interface(conn, OFONO_MANAGER_PATH, OFONO_MANAGER_INTERFACE, manager_methods, manager_signals, NULL, NULL, NULL); if (ret == FALSE) return -1; return 0; } void __ofono_manager_cleanup(void) { DBusConnection *conn = ofono_dbus_get_connection(); g_dbus_unregister_interface(conn, OFONO_MANAGER_PATH, OFONO_MANAGER_INTERFACE); } ofono-1.17.bzr6912+16.04.20160314.3/src/call-settings.c0000644000015600001650000010405112671500024022104 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include "ofono.h" #include "common.h" #define CALL_SETTINGS_FLAG_CACHED 0x1 static GSList *g_drivers = NULL; /* 27.007 Section 7.7 */ enum clir_status { CLIR_STATUS_NOT_PROVISIONED = 0, CLIR_STATUS_PROVISIONED_PERMANENT = 1, CLIR_STATUS_UNKNOWN = 2, CLIR_STATUS_TEMPORARY_RESTRICTED = 3, CLIR_STATUS_TEMPORARY_ALLOWED = 4 }; /* 27.007 Section 7.6 */ enum clip_status { CLIP_STATUS_NOT_PROVISIONED = 0, CLIP_STATUS_PROVISIONED = 1, CLIP_STATUS_UNKNOWN = 2 }; /* 27.007 Section 7.30 */ enum cnap_status { CNAP_STATUS_NOT_PROVISIONED = 0, CNAP_STATUS_PROVISIONED = 1, CNAP_STATUS_UNKNOWN = 2 }; /* 27.007 Section 7.8 */ enum colp_status { COLP_STATUS_NOT_PROVISIONED = 0, COLP_STATUS_PROVISIONED = 1, COLP_STATUS_UNKNOWN = 2 }; /* 27.007 Section 7.9 */ enum cdip_status { CDIP_STATUS_NOT_PROVISIONED = 0, CDIP_STATUS_PROVISIONED = 1, CDIP_STATUS_UNKNOWN = 2 }; /* This is not defined in 27.007, but presumably the same as CLIP/COLP */ enum colr_status { COLR_STATUS_NOT_PROVISIONED = 0, COLR_STATUS_PROVISIONED = 1, COLR_STATUS_UNKNOWN = 2 }; enum call_setting_type { CALL_SETTING_TYPE_CLIP = 0, CALL_SETTING_TYPE_CNAP, CALL_SETTING_TYPE_CDIP, CALL_SETTING_TYPE_COLP, CALL_SETTING_TYPE_COLR, CALL_SETTING_TYPE_CLIR, CALL_SETTING_TYPE_CW }; struct ofono_call_settings { int clir; int colr; int clip; int cnap; int cdip; int colp; int clir_setting; int cw; int flags; DBusMessage *pending; int ss_req_type; int ss_req_cls; enum call_setting_type ss_setting; struct ofono_ussd *ussd; unsigned int ussd_watch; const struct ofono_call_settings_driver *driver; void *driver_data; struct ofono_atom *atom; }; static const char *clip_status_to_string(int status) { switch (status) { case CLIP_STATUS_NOT_PROVISIONED: return "disabled"; case CLIP_STATUS_PROVISIONED: return "enabled"; } return "unknown"; } static const char *cdip_status_to_string(int status) { switch (status) { case CDIP_STATUS_NOT_PROVISIONED: return "disabled"; case CDIP_STATUS_PROVISIONED: return "enabled"; } return "unknown"; } static const char *cnap_status_to_string(int status) { switch (status) { case CNAP_STATUS_NOT_PROVISIONED: return "disabled"; case CNAP_STATUS_PROVISIONED: return "enabled"; } return "unknown"; } static const char *colp_status_to_string(int status) { switch (status) { case COLP_STATUS_NOT_PROVISIONED: return "disabled"; case COLP_STATUS_PROVISIONED: return "enabled"; } return "unknown"; } static const char *colr_status_to_string(int status) { switch (status) { case COLR_STATUS_NOT_PROVISIONED: return "disabled"; case COLR_STATUS_PROVISIONED: return "enabled"; } return "unknown"; } static const char *hide_callerid_to_string(int status) { switch (status) { case OFONO_CLIR_OPTION_DEFAULT: return "default"; case OFONO_CLIR_OPTION_INVOCATION: return "enabled"; case OFONO_CLIR_OPTION_SUPPRESSION: return "disabled"; } return "default"; } static const char *clir_status_to_string(int status) { switch (status) { case CLIR_STATUS_NOT_PROVISIONED: return "disabled"; case CLIR_STATUS_PROVISIONED_PERMANENT: return "permanent"; case CLIR_STATUS_TEMPORARY_RESTRICTED: return "on"; case CLIR_STATUS_TEMPORARY_ALLOWED: return "off"; } return "unknown"; } static void set_clir_network(struct ofono_call_settings *cs, int clir) { DBusConnection *conn; const char *path; const char *str; if (cs->clir == clir) return; cs->clir = clir; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cs->atom); str = clir_status_to_string(clir); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_SETTINGS_INTERFACE, "CallingLineRestriction", DBUS_TYPE_STRING, &str); } static void set_clir_override(struct ofono_call_settings *cs, int override) { DBusConnection *conn; const char *path; const char *str; if (cs->clir_setting == override) return; cs->clir_setting = override; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cs->atom); str = hide_callerid_to_string(override); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_SETTINGS_INTERFACE, "HideCallerId", DBUS_TYPE_STRING, &str); } static void set_cdip(struct ofono_call_settings *cs, int cdip) { DBusConnection *conn; const char *path; const char *str; if (cs->cdip == cdip) return; cs->cdip = cdip; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cs->atom); str = cdip_status_to_string(cdip); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_SETTINGS_INTERFACE, "CalledLinePresentation", DBUS_TYPE_STRING, &str); } static void set_clip(struct ofono_call_settings *cs, int clip) { DBusConnection *conn; const char *path; const char *str; if (cs->clip == clip) return; cs->clip = clip; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cs->atom); str = clip_status_to_string(clip); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_SETTINGS_INTERFACE, "CallingLinePresentation", DBUS_TYPE_STRING, &str); } static void set_cnap(struct ofono_call_settings *cs, int cnap) { DBusConnection *conn; const char *path; const char *str; if (cs->cnap == cnap) return; cs->cnap = cnap; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cs->atom); str = cnap_status_to_string(cnap); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_SETTINGS_INTERFACE, "CallingNamePresentation", DBUS_TYPE_STRING, &str); } static void set_colp(struct ofono_call_settings *cs, int colp) { DBusConnection *conn; const char *path; const char *str; if (cs->colp == colp) return; cs->colp = colp; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cs->atom); str = colp_status_to_string(colp); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_SETTINGS_INTERFACE, "ConnectedLinePresentation", DBUS_TYPE_STRING, &str); } static void set_colr(struct ofono_call_settings *cs, int colr) { DBusConnection *conn; const char *path; const char *str; if (cs->colr == colr) return; cs->colr = colr; conn = ofono_dbus_get_connection(); path = __ofono_atom_get_path(cs->atom); str = colr_status_to_string(colr); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_SETTINGS_INTERFACE, "ConnectedLineRestriction", DBUS_TYPE_STRING, &str); } static void set_cw(struct ofono_call_settings *cs, int new_cw, int mask) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cs->atom); char buf[64]; int j; const char *value; for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) { if ((j & mask) == 0) continue; if ((cs->cw & j) == (new_cw & j)) continue; if (new_cw & j) value = "enabled"; else value = "disabled"; snprintf(buf, sizeof(buf), "%sCallWaiting", bearer_class_to_string(j)); ofono_dbus_signal_property_changed(conn, path, OFONO_CALL_SETTINGS_INTERFACE, buf, DBUS_TYPE_STRING, &value); } cs->cw = new_cw; } static void property_append_cw_conditions(DBusMessageIter *dict, int conditions, int mask) { int i; char prop[128]; const char *value; for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) { if (!(mask & i)) continue; snprintf(prop, sizeof(prop), "%sCallWaiting", bearer_class_to_string(i)); if (conditions & i) value = "enabled"; else value = "disabled"; ofono_dbus_dict_append(dict, prop, DBUS_TYPE_STRING, &value); } } static void generate_cw_ss_query_reply(struct ofono_call_settings *cs) { const char *sig = "(sa{sv})"; const char *ss_type = ss_control_type_to_string(cs->ss_req_type); const char *context = "CallWaiting"; DBusMessageIter iter; DBusMessageIter var; DBusMessageIter vstruct; DBusMessageIter dict; DBusMessage *reply; reply = dbus_message_new_method_return(cs->pending); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &var); dbus_message_iter_open_container(&var, DBUS_TYPE_STRUCT, NULL, &vstruct); dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &ss_type); dbus_message_iter_open_container(&vstruct, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); property_append_cw_conditions(&dict, cs->cw, cs->ss_req_cls); dbus_message_iter_close_container(&vstruct, &dict); dbus_message_iter_close_container(&var, &vstruct); dbus_message_iter_close_container(&iter, &var); __ofono_dbus_pending_reply(&cs->pending, reply); } static void cw_ss_query_callback(const struct ofono_error *error, int status, void *data) { struct ofono_call_settings *cs = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("setting CW via SS failed"); cs->flags &= ~CALL_SETTINGS_FLAG_CACHED; __ofono_dbus_pending_reply(&cs->pending, __ofono_error_failed(cs->pending)); return; } set_cw(cs, status, BEARER_CLASS_VOICE); generate_cw_ss_query_reply(cs); } static void cw_ss_set_callback(const struct ofono_error *error, void *data) { struct ofono_call_settings *cs = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("setting CW via SS failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&cs->pending, __ofono_error_from_error(error, cs->pending)); return; } cs->driver->cw_query(cs, BEARER_CLASS_DEFAULT, cw_ss_query_callback, cs); } static gboolean cw_ss_control(int type, const char *sc, const char *sia, const char *sib, const char *sic, const char *dn, DBusMessage *msg, void *data) { struct ofono_call_settings *cs = data; DBusConnection *conn = ofono_dbus_get_connection(); int cls = BEARER_CLASS_SS_DEFAULT; DBusMessage *reply; if (cs == NULL) return FALSE; if (strcmp(sc, "43")) return FALSE; if (__ofono_call_settings_is_busy(cs)) { reply = __ofono_error_busy(msg); goto error; } if (strlen(sib) || strlen(sib) || strlen(dn)) goto bad_format; if ((type == SS_CONTROL_TYPE_QUERY && cs->driver->cw_query == NULL) || (type != SS_CONTROL_TYPE_QUERY && cs->driver->cw_set == NULL)) { reply = __ofono_error_not_implemented(msg); goto error; } if (strlen(sia) > 0) { long service_code; char *end; service_code = strtoul(sia, &end, 10); if (end == sia || *end != '\0') goto bad_format; cls = mmi_service_code_to_bearer_class(service_code); if (cls == 0) goto bad_format; } cs->ss_req_cls = cls; cs->pending = dbus_message_ref(msg); /* For the default case use the more readily accepted value */ if (cls == BEARER_CLASS_SS_DEFAULT) cls = BEARER_CLASS_DEFAULT; switch (type) { case SS_CONTROL_TYPE_REGISTRATION: case SS_CONTROL_TYPE_ACTIVATION: cs->ss_req_type = SS_CONTROL_TYPE_ACTIVATION; cs->driver->cw_set(cs, 1, cls, cw_ss_set_callback, cs); break; case SS_CONTROL_TYPE_QUERY: cs->ss_req_type = SS_CONTROL_TYPE_QUERY; /* * Always query the entire set, SMS not applicable * according to 22.004 Appendix A, so CLASS_DEFAULT * is safe to use here */ cs->driver->cw_query(cs, BEARER_CLASS_DEFAULT, cw_ss_query_callback, cs); break; case SS_CONTROL_TYPE_DEACTIVATION: case SS_CONTROL_TYPE_ERASURE: cs->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION; cs->driver->cw_set(cs, 0, cls, cw_ss_set_callback, cs); break; } return TRUE; bad_format: reply = __ofono_error_invalid_format(msg); error: g_dbus_send_message(conn, reply); return TRUE; } static void generate_ss_query_reply(struct ofono_call_settings *cs, const char *context, const char *value) { const char *sig = "(ss)"; const char *ss_type = ss_control_type_to_string(cs->ss_req_type); DBusMessageIter iter; DBusMessageIter var; DBusMessageIter vstruct; DBusMessage *reply; reply = dbus_message_new_method_return(cs->pending); dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &var); dbus_message_iter_open_container(&var, DBUS_TYPE_STRUCT, NULL, &vstruct); dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &ss_type); dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &value); dbus_message_iter_close_container(&var, &vstruct); dbus_message_iter_close_container(&iter, &var); __ofono_dbus_pending_reply(&cs->pending, reply); } static void clip_cnap_colp_colr_ss_query_cb(const struct ofono_error *error, int status, void *data) { struct ofono_call_settings *cs = data; const char *context; const char *value; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("SS control query failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&cs->pending, __ofono_error_from_error(error, cs->pending)); return; } switch (cs->ss_setting) { case CALL_SETTING_TYPE_CLIP: set_clip(cs, status); value = clip_status_to_string(status); context = "CallingLinePresentation"; break; case CALL_SETTING_TYPE_CNAP: set_cnap(cs, status); value = cnap_status_to_string(status); context = "CallingNamePresentation"; break; case CALL_SETTING_TYPE_COLP: set_colp(cs, status); value = colp_status_to_string(status); context = "ConnectedLinePresentation"; break; case CALL_SETTING_TYPE_COLR: set_colr(cs, status); value = colr_status_to_string(status); context = "ConnectedLineRestriction"; break; default: __ofono_dbus_pending_reply(&cs->pending, __ofono_error_failed(cs->pending)); ofono_error("Unknown type during COLR/COLP/CLIP/CNAP ss"); return; }; generate_ss_query_reply(cs, context, value); } static gboolean clip_cnap_colp_colr_ss(int type, const char *sc, const char *sia, const char *sib, const char *sic, const char *dn, DBusMessage *msg, void *data) { struct ofono_call_settings *cs = data; DBusConnection *conn = ofono_dbus_get_connection(); void (*query_op)(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data); if (cs == NULL) return FALSE; if (__ofono_call_settings_is_busy(cs)) { DBusMessage *reply = __ofono_error_busy(msg); g_dbus_send_message(conn, reply); return TRUE; } if (!strcmp(sc, "30")) { cs->ss_setting = CALL_SETTING_TYPE_CLIP; query_op = cs->driver->clip_query; } else if (!strcmp(sc, "300")) { cs->ss_setting = CALL_SETTING_TYPE_CNAP; query_op = cs->driver->cnap_query; } else if (!strcmp(sc, "76")) { cs->ss_setting = CALL_SETTING_TYPE_COLP; query_op = cs->driver->colp_query; } else if (!strcmp(sc, "77")) { cs->ss_setting = CALL_SETTING_TYPE_COLR; query_op = cs->driver->colr_query; } else { return FALSE; } if (type != SS_CONTROL_TYPE_QUERY || strlen(sia) || strlen(sib) || strlen(sic) || strlen(dn)) { DBusMessage *reply = __ofono_error_invalid_format(msg); g_dbus_send_message(conn, reply); return TRUE; } if (query_op == NULL) { DBusMessage *reply = __ofono_error_not_implemented(msg); g_dbus_send_message(conn, reply); return TRUE; } DBG("Received CLIP/CNAP/COLR/COLP query ss control"); cs->pending = dbus_message_ref(msg); query_op(cs, clip_cnap_colp_colr_ss_query_cb, cs); return TRUE; } static void clir_ss_query_callback(const struct ofono_error *error, int override, int network, void *data) { struct ofono_call_settings *cs = data; const char *value; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("clir query via SS failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&cs->pending, __ofono_error_from_error(error, cs->pending)); return; } switch (network) { case CLIR_STATUS_UNKNOWN: value = "unknown"; break; case CLIR_STATUS_PROVISIONED_PERMANENT: value = "enabled"; break; case CLIR_STATUS_NOT_PROVISIONED: value = "disabled"; break; case CLIR_STATUS_TEMPORARY_RESTRICTED: if (override == OFONO_CLIR_OPTION_SUPPRESSION) value = "enabled"; else value = "disabled"; break; case CLIR_STATUS_TEMPORARY_ALLOWED: if (override == OFONO_CLIR_OPTION_INVOCATION) value = "enabled"; else value = "disabled"; break; default: value = "unknown"; }; generate_ss_query_reply(cs, "CallingLineRestriction", value); set_clir_network(cs, network); set_clir_override(cs, override); } static void clir_ss_set_callback(const struct ofono_error *error, void *data) { struct ofono_call_settings *cs = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("setting clir via SS failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&cs->pending, __ofono_error_from_error(error, cs->pending)); return; } cs->driver->clir_query(cs, clir_ss_query_callback, cs); } static gboolean clir_ss_control(int type, const char *sc, const char *sia, const char *sib, const char *sic, const char *dn, DBusMessage *msg, void *data) { struct ofono_call_settings *cs = data; DBusConnection *conn = ofono_dbus_get_connection(); if (cs == NULL) return FALSE; if (strcmp(sc, "31")) return FALSE; if (__ofono_call_settings_is_busy(cs)) { DBusMessage *reply = __ofono_error_busy(msg); g_dbus_send_message(conn, reply); return TRUE; } /* This is the temporary form of CLIR, handled in voicecalls */ if (!strlen(sia) && !strlen(sib) & !strlen(sic) && strlen(dn) && type != SS_CONTROL_TYPE_QUERY) return FALSE; if (strlen(sia) || strlen(sib) || strlen(sic) || strlen(dn)) { DBusMessage *reply = __ofono_error_invalid_format(msg); g_dbus_send_message(conn, reply); return TRUE; } if (type == SS_CONTROL_TYPE_QUERY && cs->driver->clir_query == NULL) { DBusMessage *reply = __ofono_error_not_implemented(msg); g_dbus_send_message(conn, reply); return TRUE; } if (type != SS_CONTROL_TYPE_QUERY && cs->driver->clir_set == NULL) { DBusMessage *reply = __ofono_error_not_implemented(msg); g_dbus_send_message(conn, reply); return TRUE; } cs->ss_setting = CALL_SETTING_TYPE_CLIR; cs->pending = dbus_message_ref(msg); switch (type) { case SS_CONTROL_TYPE_REGISTRATION: case SS_CONTROL_TYPE_ACTIVATION: cs->ss_req_type = SS_CONTROL_TYPE_ACTIVATION; cs->driver->clir_set(cs, OFONO_CLIR_OPTION_SUPPRESSION, clir_ss_set_callback, cs); break; case SS_CONTROL_TYPE_QUERY: cs->ss_req_type = SS_CONTROL_TYPE_QUERY; cs->driver->clir_query(cs, clir_ss_query_callback, cs); break; case SS_CONTROL_TYPE_DEACTIVATION: case SS_CONTROL_TYPE_ERASURE: cs->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION; cs->driver->clir_set(cs, OFONO_CLIR_OPTION_INVOCATION, clir_ss_set_callback, cs); break; }; return TRUE; } static void cs_register_ss_controls(struct ofono_call_settings *cs) { __ofono_ussd_ssc_register(cs->ussd, "30", clip_cnap_colp_colr_ss, cs, NULL); __ofono_ussd_ssc_register(cs->ussd, "31", clir_ss_control, cs, NULL); __ofono_ussd_ssc_register(cs->ussd, "76", clip_cnap_colp_colr_ss, cs, NULL); __ofono_ussd_ssc_register(cs->ussd, "300", clip_cnap_colp_colr_ss, cs, NULL); __ofono_ussd_ssc_register(cs->ussd, "43", cw_ss_control, cs, NULL); if (cs->driver->colr_query != NULL) __ofono_ussd_ssc_register(cs->ussd, "77", clip_cnap_colp_colr_ss, cs, NULL); } static void cs_unregister_ss_controls(struct ofono_call_settings *cs) { __ofono_ussd_ssc_unregister(cs->ussd, "30"); __ofono_ussd_ssc_unregister(cs->ussd, "31"); __ofono_ussd_ssc_unregister(cs->ussd, "76"); __ofono_ussd_ssc_unregister(cs->ussd, "300"); __ofono_ussd_ssc_unregister(cs->ussd, "43"); if (cs->driver->colr_query != NULL) __ofono_ussd_ssc_unregister(cs->ussd, "77"); } gboolean __ofono_call_settings_is_busy(struct ofono_call_settings *cs) { return cs->pending ? TRUE : FALSE; } static DBusMessage *generate_get_properties_reply(struct ofono_call_settings *cs, DBusMessage *msg) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; const char *str; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); str = clip_status_to_string(cs->clip); ofono_dbus_dict_append(&dict, "CallingLinePresentation", DBUS_TYPE_STRING, &str); str = cnap_status_to_string(cs->cnap); ofono_dbus_dict_append(&dict, "CallingNamePresentation", DBUS_TYPE_STRING, &str); str = colp_status_to_string(cs->colp); ofono_dbus_dict_append(&dict, "ConnectedLinePresentation", DBUS_TYPE_STRING, &str); str = colr_status_to_string(cs->colr); ofono_dbus_dict_append(&dict, "ConnectedLineRestriction", DBUS_TYPE_STRING, &str); str = cdip_status_to_string(cs->cdip); ofono_dbus_dict_append(&dict, "CalledLinePresentation", DBUS_TYPE_STRING, &str); str = clir_status_to_string(cs->clir); ofono_dbus_dict_append(&dict, "CallingLineRestriction", DBUS_TYPE_STRING, &str); str = hide_callerid_to_string(cs->clir_setting); ofono_dbus_dict_append(&dict, "HideCallerId", DBUS_TYPE_STRING, &str); property_append_cw_conditions(&dict, cs->cw, BEARER_CLASS_VOICE); dbus_message_iter_close_container(&iter, &dict); return reply; } static void cs_clir_callback(const struct ofono_error *error, int override_setting, int network_setting, void *data) { struct ofono_call_settings *cs = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) goto out; set_clir_network(cs, network_setting); set_clir_override(cs, override_setting); cs->flags |= CALL_SETTINGS_FLAG_CACHED; out: if (cs->pending) { DBusMessage *reply = generate_get_properties_reply(cs, cs->pending); __ofono_dbus_pending_reply(&cs->pending, reply); } } static void query_clir(struct ofono_call_settings *cs) { if (cs->driver->clir_query == NULL) { if (cs->pending) { DBusMessage *reply = generate_get_properties_reply(cs, cs->pending); __ofono_dbus_pending_reply(&cs->pending, reply); } return; } cs->driver->clir_query(cs, cs_clir_callback, cs); } static void cs_cdip_callback(const struct ofono_error *error, int state, void *data) { struct ofono_call_settings *cs = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) set_cdip(cs, state); query_clir(cs); } static void query_cdip(struct ofono_call_settings *cs) { if (cs->driver->cdip_query == NULL) { query_clir(cs); return; } cs->driver->cdip_query(cs, cs_cdip_callback, cs); } static void cs_cnap_callback(const struct ofono_error *error, int state, void *data) { struct ofono_call_settings *cs = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) set_cnap(cs, state); query_cdip(cs); } static void query_cnap(struct ofono_call_settings *cs) { if (cs->driver->cnap_query == NULL) { query_cdip(cs); return; } cs->driver->cnap_query(cs, cs_cnap_callback, cs); } static void cs_clip_callback(const struct ofono_error *error, int state, void *data) { struct ofono_call_settings *cs = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) set_clip(cs, state); query_cnap(cs); } static void query_clip(struct ofono_call_settings *cs) { if (cs->driver->clip_query == NULL) { query_clir(cs); return; } cs->driver->clip_query(cs, cs_clip_callback, cs); } static void cs_colp_callback(const struct ofono_error *error, int state, void *data) { struct ofono_call_settings *cs = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) set_colp(cs, state); query_clip(cs); } static void query_colp(struct ofono_call_settings *cs) { if (cs->driver->colp_query == NULL) { query_clip(cs); return; } cs->driver->colp_query(cs, cs_colp_callback, cs); } static void cs_colr_callback(const struct ofono_error *error, int state, void *data) { struct ofono_call_settings *cs = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) set_colr(cs, state); query_colp(cs); } static void query_colr(struct ofono_call_settings *cs) { if (cs->driver->colr_query == NULL) { query_colp(cs); return; } cs->driver->colr_query(cs, cs_colr_callback, cs); } static void cs_cw_callback(const struct ofono_error *error, int status, void *data) { struct ofono_call_settings *cs = data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) set_cw(cs, status, BEARER_CLASS_VOICE); query_colr(cs); } static void query_cw(struct ofono_call_settings *cs) { if (cs->driver->cw_query == NULL) { query_colr(cs); return; } cs->driver->cw_query(cs, BEARER_CLASS_DEFAULT, cs_cw_callback, cs); } static DBusMessage *cs_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_settings *cs = data; if (__ofono_call_settings_is_busy(cs) || __ofono_ussd_is_busy(cs->ussd)) return __ofono_error_busy(msg); if (cs->flags & CALL_SETTINGS_FLAG_CACHED) return generate_get_properties_reply(cs, msg); /* Query the settings and report back */ cs->pending = dbus_message_ref(msg); query_cw(cs); return NULL; } static void clir_set_query_callback(const struct ofono_error *error, int override_setting, int network_setting, void *data) { struct ofono_call_settings *cs = data; DBusMessage *reply; if (!__ofono_call_settings_is_busy(cs)) return; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("set clir successful, but the query was not"); cs->flags &= ~CALL_SETTINGS_FLAG_CACHED; reply = __ofono_error_failed(cs->pending); __ofono_dbus_pending_reply(&cs->pending, reply); return; } reply = dbus_message_new_method_return(cs->pending); __ofono_dbus_pending_reply(&cs->pending, reply); set_clir_override(cs, override_setting); set_clir_network(cs, network_setting); } static void clir_set_callback(const struct ofono_error *error, void *data) { struct ofono_call_settings *cs = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("setting clir failed"); __ofono_dbus_pending_reply(&cs->pending, __ofono_error_failed(cs->pending)); return; } /* Assume that if we have clir_set, we have clir_query */ cs->driver->clir_query(cs, clir_set_query_callback, cs); } static DBusMessage *set_clir(DBusMessage *msg, struct ofono_call_settings *cs, const char *setting) { int clir = -1; if (cs->driver->clir_set == NULL) return __ofono_error_not_implemented(msg); if (!strcmp(setting, "default")) clir = CLIR_STATUS_NOT_PROVISIONED; else if (!strcmp(setting, "enabled")) clir = CLIR_STATUS_PROVISIONED_PERMANENT; else if (!strcmp(setting, "disabled")) clir = CLIR_STATUS_UNKNOWN; if (clir == -1) return __ofono_error_invalid_format(msg); cs->pending = dbus_message_ref(msg); cs->driver->clir_set(cs, clir, clir_set_callback, cs); return NULL; } static void cw_set_query_callback(const struct ofono_error *error, int status, void *data) { struct ofono_call_settings *cs = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("CW set succeeded, but query failed!"); cs->flags &= ~CALL_SETTINGS_FLAG_CACHED; __ofono_dbus_pending_reply(&cs->pending, __ofono_error_failed(cs->pending)); return; } __ofono_dbus_pending_reply(&cs->pending, dbus_message_new_method_return(cs->pending)); set_cw(cs, status, BEARER_CLASS_VOICE); } static void cw_set_callback(const struct ofono_error *error, void *data) { struct ofono_call_settings *cs = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error occurred during CW set"); __ofono_dbus_pending_reply(&cs->pending, __ofono_error_failed(cs->pending)); return; } cs->driver->cw_query(cs, BEARER_CLASS_DEFAULT, cw_set_query_callback, cs); } static DBusMessage *set_cw_req(DBusMessage *msg, struct ofono_call_settings *cs, const char *setting, int cls) { int cw; if (cs->driver->cw_set == NULL) return __ofono_error_not_implemented(msg); if (!strcmp(setting, "enabled")) cw = 1; else if (!strcmp(setting, "disabled")) cw = 0; else return __ofono_error_invalid_format(msg); cs->pending = dbus_message_ref(msg); cs->driver->cw_set(cs, cw, cls, cw_set_callback, cs); return NULL; } static gboolean is_cw_property(const char *property, int mask, int *out_cls) { int i; int len; const char *prefix; for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) { if ((i & mask) == 0) continue; prefix = bearer_class_to_string(i); len = strlen(prefix); if (strncmp(property, prefix, len)) continue; if (!strcmp(property+len, "CallWaiting")) { *out_cls = i; return TRUE; } } return FALSE; } static DBusMessage *cs_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_call_settings *cs = data; DBusMessageIter iter; DBusMessageIter var; const char *property; int cls; if (__ofono_call_settings_is_busy(cs) || __ofono_ussd_is_busy(cs->ussd)) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (!strcmp(property, "HideCallerId")) { const char *setting; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &setting); return set_clir(msg, cs, setting); } else if (is_cw_property(property, BEARER_CLASS_VOICE, &cls)) { const char *setting; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &setting); return set_cw_req(msg, cs, setting, cls); } return __ofono_error_invalid_args(msg); } static const GDBusMethodTable cs_methods[] = { { GDBUS_ASYNC_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), cs_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, cs_set_property) }, { } }; static const GDBusSignalTable cs_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "property", "s" }, { "value", "v" })) }, { } }; int ofono_call_settings_driver_register(const struct ofono_call_settings_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_call_settings_driver_unregister(const struct ofono_call_settings_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void call_settings_unregister(struct ofono_atom *atom) { struct ofono_call_settings *cs = __ofono_atom_get_data(atom); const char *path = __ofono_atom_get_path(cs->atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(cs->atom); ofono_modem_remove_interface(modem, OFONO_CALL_SETTINGS_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_CALL_SETTINGS_INTERFACE); if (cs->ussd) cs_unregister_ss_controls(cs); if (cs->ussd_watch) __ofono_modem_remove_atom_watch(modem, cs->ussd_watch); } static void call_settings_remove(struct ofono_atom *atom) { struct ofono_call_settings *cs = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (cs == NULL) return; if (cs->driver != NULL && cs->driver->remove != NULL) cs->driver->remove(cs); g_free(cs); } struct ofono_call_settings *ofono_call_settings_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_call_settings *cs; GSList *l; if (driver == NULL) return NULL; cs = g_try_new0(struct ofono_call_settings, 1); if (cs == NULL) return NULL; /* Set all the settings to unknown state */ cs->clip = CLIP_STATUS_UNKNOWN; cs->cnap = CNAP_STATUS_UNKNOWN; cs->clir = CLIR_STATUS_UNKNOWN; cs->colp = COLP_STATUS_UNKNOWN; cs->colr = COLR_STATUS_UNKNOWN; cs->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CALL_SETTINGS, call_settings_remove, cs); for (l = g_drivers; l; l = l->next) { const struct ofono_call_settings_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(cs, vendor, data) < 0) continue; cs->driver = drv; break; } return cs; } static void ussd_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_call_settings *cs = data; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { cs->ussd = NULL; return; } cs->ussd = __ofono_atom_get_data(atom); cs_register_ss_controls(cs); } void ofono_call_settings_register(struct ofono_call_settings *cs) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(cs->atom); struct ofono_modem *modem = __ofono_atom_get_modem(cs->atom); if (!g_dbus_register_interface(conn, path, OFONO_CALL_SETTINGS_INTERFACE, cs_methods, cs_signals, NULL, cs, NULL)) { ofono_error("Could not create %s interface", OFONO_CALL_SETTINGS_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_CALL_SETTINGS_INTERFACE); cs->ussd_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_USSD, ussd_watch, cs, NULL); __ofono_atom_register(cs->atom, call_settings_unregister); } void ofono_call_settings_remove(struct ofono_call_settings *cs) { __ofono_atom_free(cs->atom); } void ofono_call_settings_set_data(struct ofono_call_settings *cs, void *data) { cs->driver_data = data; } void *ofono_call_settings_get_data(struct ofono_call_settings *cs) { return cs->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/gprs.c0000644000015600001650000025245112671500024020316 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" #include "storage.h" #include "idmap.h" #include "simutil.h" #include "util.h" #include "dns-client.h" #define GPRS_FLAG_ATTACHING 0x1 #define GPRS_FLAG_RECHECK 0x2 #define GPRS_FLAG_ATTACHED_UPDATE 0x4 #define SETTINGS_STORE "gprs" #define SETTINGS_GROUP "Settings" #define MAX_CONTEXT_NAME_LENGTH 127 #define MAX_MESSAGE_PROXY_LENGTH 255 #define MAX_MESSAGE_CENTER_LENGTH 255 #define MAX_CONTEXTS 256 #define SUSPEND_TIMEOUT 8 #define DNS_LOOKUP_TOUT_MS 15000 struct ofono_gprs { GSList *contexts; ofono_bool_t attached; ofono_bool_t driver_attached; ofono_bool_t roaming_allowed; ofono_bool_t powered; ofono_bool_t suspended; int status; int flags; int bearer; guint suspend_timeout; struct idmap *pid_map; unsigned int last_context_id; struct idmap *cid_map; int netreg_status; struct ofono_netreg *netreg; unsigned int netreg_watch; unsigned int status_watch; GKeyFile *settings; char *imsi; DBusMessage *pending; GSList *context_drivers; const struct ofono_gprs_driver *driver; void *driver_data; struct ofono_atom *atom; unsigned int spn_watch; struct ofono_sim *sim; struct ofono_sim_context *sim_context; char *gid1; }; struct ipv4_settings { ofono_bool_t static_ip; char *ip; char *netmask; char *gateway; char **dns; char *proxy; uint16_t proxy_port; }; struct ipv6_settings { char *ip; unsigned char prefix_len; char *gateway; char **dns; }; struct context_settings { char *interface; struct ipv4_settings *ipv4; struct ipv6_settings *ipv6; }; struct ofono_gprs_context { struct ofono_gprs *gprs; enum ofono_gprs_context_type type; ofono_bool_t inuse; const struct ofono_gprs_context_driver *driver; void *driver_data; struct context_settings *settings; struct ofono_atom *atom; }; struct pri_context { ofono_bool_t active; enum ofono_gprs_context_type type; gboolean preferred; char name[MAX_CONTEXT_NAME_LENGTH + 1]; char message_proxy[MAX_MESSAGE_PROXY_LENGTH + 1]; char message_center[MAX_MESSAGE_CENTER_LENGTH + 1]; unsigned int id; char *path; char *key; char *proxy_host; uint16_t proxy_port; DBusMessage *pending; struct ofono_gprs_primary_context context; struct ofono_gprs_context *context_driver; struct ofono_gprs *gprs; ofono_dns_client_request_t lookup_req; }; static void gprs_netreg_update(struct ofono_gprs *gprs); static void gprs_deactivate_next(struct ofono_gprs *gprs); static GSList *g_drivers = NULL; static GSList *g_context_drivers = NULL; static const char *gprs_context_default_name(enum ofono_gprs_context_type type) { switch (type) { case OFONO_GPRS_CONTEXT_TYPE_ANY: return NULL; case OFONO_GPRS_CONTEXT_TYPE_INTERNET: return "Internet"; case OFONO_GPRS_CONTEXT_TYPE_MMS: return "MMS"; case OFONO_GPRS_CONTEXT_TYPE_WAP: return "WAP"; case OFONO_GPRS_CONTEXT_TYPE_IMS: return "IMS"; case OFONO_GPRS_CONTEXT_TYPE_IA: return "IA"; } return NULL; } static const char *gprs_context_type_to_string( enum ofono_gprs_context_type type) { switch (type) { case OFONO_GPRS_CONTEXT_TYPE_ANY: return NULL; case OFONO_GPRS_CONTEXT_TYPE_INTERNET: return "internet"; case OFONO_GPRS_CONTEXT_TYPE_MMS: return "mms"; case OFONO_GPRS_CONTEXT_TYPE_WAP: return "wap"; case OFONO_GPRS_CONTEXT_TYPE_IMS: return "ims"; case OFONO_GPRS_CONTEXT_TYPE_IA: return "ia"; } return NULL; } static gboolean gprs_context_string_to_type(const char *str, enum ofono_gprs_context_type *out) { if (g_str_equal(str, "internet")) { *out = OFONO_GPRS_CONTEXT_TYPE_INTERNET; return TRUE; } else if (g_str_equal(str, "wap")) { *out = OFONO_GPRS_CONTEXT_TYPE_WAP; return TRUE; } else if (g_str_equal(str, "mms")) { *out = OFONO_GPRS_CONTEXT_TYPE_MMS; return TRUE; } else if (g_str_equal(str, "ims")) { *out = OFONO_GPRS_CONTEXT_TYPE_IMS; return TRUE; } else if (g_str_equal(str, "ia")) { *out = OFONO_GPRS_CONTEXT_TYPE_IA; return TRUE; } return FALSE; } static const char *gprs_proto_to_string(enum ofono_gprs_proto proto) { switch (proto) { case OFONO_GPRS_PROTO_IP: return "ip"; case OFONO_GPRS_PROTO_IPV6: return "ipv6"; case OFONO_GPRS_PROTO_IPV4V6: return "dual"; }; return NULL; } static gboolean gprs_proto_from_string(const char *str, enum ofono_gprs_proto *proto) { if (g_str_equal(str, "ip")) { *proto = OFONO_GPRS_PROTO_IP; return TRUE; } else if (g_str_equal(str, "ipv6")) { *proto = OFONO_GPRS_PROTO_IPV6; return TRUE; } else if (g_str_equal(str, "dual")) { *proto = OFONO_GPRS_PROTO_IPV4V6; return TRUE; } return FALSE; } static const char *gprs_auth_method_to_string(enum ofono_gprs_auth_method auth) { switch (auth) { case OFONO_GPRS_AUTH_METHOD_CHAP: return "chap"; case OFONO_GPRS_AUTH_METHOD_PAP: return "pap"; }; return NULL; } static gboolean gprs_auth_method_from_string(const char *str, enum ofono_gprs_auth_method *auth) { if (g_str_equal(str, "chap")) { *auth = OFONO_GPRS_AUTH_METHOD_CHAP; return TRUE; } else if (g_str_equal(str, "pap")) { *auth = OFONO_GPRS_AUTH_METHOD_PAP; return TRUE; } return FALSE; } static unsigned int gprs_cid_alloc(struct ofono_gprs *gprs) { return idmap_alloc(gprs->cid_map); } static void gprs_cid_release(struct ofono_gprs *gprs, unsigned int id) { idmap_put(gprs->cid_map, id); } static gboolean assign_context(struct pri_context *ctx) { struct idmap *cidmap = ctx->gprs->cid_map; GSList *l; if (cidmap == NULL) return FALSE; ctx->context.cid = gprs_cid_alloc(ctx->gprs); if (ctx->context.cid == 0) return FALSE; for (l = ctx->gprs->context_drivers; l; l = l->next) { struct ofono_gprs_context *gc = l->data; if (gc->inuse == TRUE) continue; if (gc->driver == NULL) continue; if (gc->driver->activate_primary == NULL || gc->driver->deactivate_primary == NULL) continue; if (gc->type != OFONO_GPRS_CONTEXT_TYPE_ANY && gc->type != ctx->type) continue; ctx->context_driver = gc; ctx->context_driver->inuse = TRUE; if (ctx->context.proto == OFONO_GPRS_PROTO_IPV4V6 || ctx->context.proto == OFONO_GPRS_PROTO_IP) gc->settings->ipv4 = g_new0(struct ipv4_settings, 1); if (ctx->context.proto == OFONO_GPRS_PROTO_IPV4V6 || ctx->context.proto == OFONO_GPRS_PROTO_IPV6) gc->settings->ipv6 = g_new0(struct ipv6_settings, 1); return TRUE; } return FALSE; } static void release_context(struct pri_context *ctx) { if (ctx == NULL || ctx->gprs == NULL || ctx->context_driver == NULL) return; gprs_cid_release(ctx->gprs, ctx->context.cid); ctx->context.cid = 0; ctx->context_driver->inuse = FALSE; ctx->context_driver = NULL; ctx->active = FALSE; } static struct pri_context *gprs_context_by_path(struct ofono_gprs *gprs, const char *ctx_path) { GSList *l; for (l = gprs->contexts; l; l = l->next) { struct pri_context *ctx = l->data; if (g_str_equal(ctx_path, ctx->path)) return ctx; } return NULL; } static void context_settings_free(struct context_settings *settings) { if (settings->ipv4) { g_free(settings->ipv4->ip); g_free(settings->ipv4->netmask); g_free(settings->ipv4->gateway); g_strfreev(settings->ipv4->dns); g_free(settings->ipv4->proxy); g_free(settings->ipv4); settings->ipv4 = NULL; } if (settings->ipv6) { g_free(settings->ipv6->ip); g_free(settings->ipv6->gateway); g_strfreev(settings->ipv6->dns); g_free(settings->ipv6); settings->ipv6 = NULL; } g_free(settings->interface); settings->interface = NULL; } static void context_settings_append_ipv4(struct context_settings *settings, DBusMessageIter *iter) { DBusMessageIter variant; DBusMessageIter array; char typesig[5]; char arraysig[6]; const char *method; arraysig[0] = DBUS_TYPE_ARRAY; arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR; arraysig[2] = typesig[1] = DBUS_TYPE_STRING; arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT; arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR; arraysig[5] = typesig[4] = '\0'; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, arraysig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, typesig, &array); if (settings == NULL || settings->ipv4 == NULL) goto done; ofono_dbus_dict_append(&array, "Interface", DBUS_TYPE_STRING, &settings->interface); if (settings->ipv4->proxy) { ofono_dbus_dict_append(&array, "Proxy", DBUS_TYPE_STRING, &settings->ipv4->proxy); ofono_dbus_dict_append(&array, "ProxyPort", DBUS_TYPE_UINT16, &settings->ipv4->proxy_port); } if (settings->ipv4->static_ip == TRUE) method = "static"; else method = "dhcp"; ofono_dbus_dict_append(&array, "Method", DBUS_TYPE_STRING, &method); if (settings->ipv4->ip) ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING, &settings->ipv4->ip); if (settings->ipv4->netmask) ofono_dbus_dict_append(&array, "Netmask", DBUS_TYPE_STRING, &settings->ipv4->netmask); if (settings->ipv4->gateway) ofono_dbus_dict_append(&array, "Gateway", DBUS_TYPE_STRING, &settings->ipv4->gateway); if (settings->ipv4->dns) ofono_dbus_dict_append_array(&array, "DomainNameServers", DBUS_TYPE_STRING, &settings->ipv4->dns); done: dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(iter, &variant); } static void context_settings_append_ipv4_dict(struct context_settings *settings, DBusMessageIter *dict) { DBusMessageIter entry; const char *key = "Settings"; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); context_settings_append_ipv4(settings, &entry); dbus_message_iter_close_container(dict, &entry); } static void context_settings_append_ipv6(struct context_settings *settings, DBusMessageIter *iter) { DBusMessageIter variant; DBusMessageIter array; char typesig[5]; char arraysig[6]; arraysig[0] = DBUS_TYPE_ARRAY; arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR; arraysig[2] = typesig[1] = DBUS_TYPE_STRING; arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT; arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR; arraysig[5] = typesig[4] = '\0'; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, arraysig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, typesig, &array); if (settings == NULL || settings->ipv6 == NULL) goto done; ofono_dbus_dict_append(&array, "Interface", DBUS_TYPE_STRING, &settings->interface); if (settings->ipv6->ip) ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING, &settings->ipv6->ip); if (settings->ipv6->prefix_len) ofono_dbus_dict_append(&array, "PrefixLength", DBUS_TYPE_BYTE, &settings->ipv6->prefix_len); if (settings->ipv6->gateway) ofono_dbus_dict_append(&array, "Gateway", DBUS_TYPE_STRING, &settings->ipv6->gateway); if (settings->ipv6->dns) ofono_dbus_dict_append_array(&array, "DomainNameServers", DBUS_TYPE_STRING, &settings->ipv6->dns); done: dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(iter, &variant); } static void context_settings_append_ipv6_dict(struct context_settings *settings, DBusMessageIter *dict) { DBusMessageIter entry; const char *key = "IPv6.Settings"; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); context_settings_append_ipv6(settings, &entry); dbus_message_iter_close_container(dict, &entry); } static void signal_settings(struct pri_context *ctx, const char *prop, void (*append)(struct context_settings *, DBusMessageIter *)) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = ctx->path; DBusMessage *signal; DBusMessageIter iter; struct context_settings *settings; signal = dbus_message_new_signal(path, OFONO_CONNECTION_CONTEXT_INTERFACE, "PropertyChanged"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &prop); if (ctx->context_driver) settings = ctx->context_driver->settings; else settings = NULL; append(settings, &iter); g_dbus_send_message(conn, signal); } static void pri_context_signal_settings(struct pri_context *ctx, gboolean ipv4, gboolean ipv6) { if (ipv4) signal_settings(ctx, "Settings", context_settings_append_ipv4); if (ipv6) signal_settings(ctx, "IPv6.Settings", context_settings_append_ipv6); } static void set_route(const struct context_settings *settings, const char *ipstr, gboolean create) { struct rtentry rt; struct sockaddr_in addr; int sk; const char *debug_str = create ? "create" : "remove"; /* TODO Handle IPv6 case */ DBG("%s for %s", ipstr, debug_str); if (settings->interface == NULL) return; sk = socket(PF_INET, SOCK_DGRAM, 0); if (sk < 0) return; memset(&rt, 0, sizeof(rt)); rt.rt_dev = (char *) settings->interface; rt.rt_flags = RTF_HOST; if (create) rt.rt_flags |= RTF_UP; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(ipstr); if (addr.sin_addr.s_addr == INADDR_NONE) { ofono_error("Cannot %s route for invalid IP %s", debug_str, ipstr); return; } memcpy(&rt.rt_dst, &addr, sizeof(rt.rt_dst)); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; memcpy(&rt.rt_gateway, &addr, sizeof(rt.rt_gateway)); memcpy(&rt.rt_genmask, &addr, sizeof(rt.rt_genmask)); if (ioctl(sk, create ? SIOCADDRT : SIOCDELRT, &rt) < 0) ofono_error("Failed to %s proxy host route: %s (%d)", debug_str, strerror(errno), errno); close(sk); } static void pri_activate_finish(struct pri_context *ctx) { struct ofono_gprs_context *gc = ctx->context_driver; struct context_settings *settings = gc->settings; DBusConnection *conn = ofono_dbus_get_connection(); dbus_bool_t value; DBG("proxy %s port %u", ctx->proxy_host ? ctx->proxy_host : "NULL", ctx->proxy_port); if (ctx->proxy_host) { settings->ipv4->proxy = g_strdup(ctx->proxy_host); settings->ipv4->proxy_port = ctx->proxy_port; if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) set_route(settings, ctx->proxy_host, TRUE); } ctx->active = TRUE; __ofono_dbus_pending_reply(&ctx->pending, dbus_message_new_method_return(ctx->pending)); if (gc->settings->interface != NULL) pri_context_signal_settings(ctx, settings->ipv4 != NULL, settings->ipv6 != NULL); value = ctx->active; ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &value); } static void lookup_address_cb(void *data, ofono_dns_client_status_t status, struct sockaddr *ip_addr) { struct pri_context *ctx = data; char str[INET_ADDRSTRLEN]; if (status == OFONO_DNS_CLIENT_SUCCESS) { void *addr; if (ip_addr->sa_family == AF_INET) { struct sockaddr_in *ip4 = (void *) ip_addr; addr = &ip4->sin_addr; } else { /* Assume ipv6 */ struct sockaddr_in6 *ip6 = (void *) ip_addr; addr = &ip6->sin6_addr; } if (inet_ntop(ip_addr->sa_family, addr, str, sizeof(str))) ctx->proxy_host = g_strdup(str); else ofono_error("%s: Cannot convert type %d to address", __func__, ip_addr->sa_family); } else { ofono_error("DNS error %s", __ofono_dns_client_strerror(status)); } ctx->lookup_req = NULL; pri_activate_finish(ctx); } static void lookup_address(struct pri_context *ctx, const char *proxy) { struct context_settings *settings = ctx->context_driver->settings; DBG("hostname is %s", proxy); ctx->lookup_req = __ofono_dns_client_submit_request( proxy, settings->interface, (const char **) settings->ipv4->dns, DNS_LOOKUP_TOUT_MS, lookup_address_cb, ctx); } static void get_proxy_ip(struct pri_context *ctx, const char *host) { struct context_settings *settings = ctx->context_driver->settings; struct in_addr addr; if (inet_pton(AF_INET, host, &addr) == 1) { ctx->proxy_host = g_strdup(host); return; } /* Not an IP -> use DNS if possible */ if (settings->ipv4 == NULL || settings->ipv4->dns == NULL || settings->ipv4->dns[0] == NULL) { ofono_error("No DNS to find IP for MMS proxy/MMSC %s", host); return; } lookup_address(ctx, host); } static void pri_parse_proxy(struct pri_context *ctx) { char *proxy, *scheme, *host, *port, *path; g_free(ctx->proxy_host); ctx->proxy_host = NULL; if (ctx->message_proxy[0] != '\0') proxy = ctx->message_proxy; else if (ctx->message_center[0] != '\0') proxy = ctx->message_center; else return; scheme = g_strdup(proxy); if (scheme == NULL) return; host = strstr(scheme, "://"); if (host != NULL) { *host = '\0'; host += 3; if (strcasecmp(scheme, "https") == 0) ctx->proxy_port = 443; else if (strcasecmp(scheme, "http") == 0) ctx->proxy_port = 80; else { g_free(scheme); return; } } else { host = scheme; ctx->proxy_port = 80; } path = strchr(host, '/'); if (path != NULL) *(path++) = '\0'; port = strrchr(host, ':'); if (port != NULL) { char *end; int tmp = strtol(port + 1, &end, 10); if (*end == '\0') { *port = '\0'; ctx->proxy_port = tmp; } } get_proxy_ip(ctx, host); g_free(scheme); } static void pri_ifupdown(const char *interface, ofono_bool_t active) { struct ifreq ifr; int sk; if (interface == NULL) return; sk = socket(PF_INET, SOCK_DGRAM, 0); if (sk < 0) return; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, interface, IFNAMSIZ); if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) goto done; if (active == TRUE) { if (ifr.ifr_flags & IFF_UP) goto done; ifr.ifr_flags |= IFF_UP; } else { if (!(ifr.ifr_flags & IFF_UP)) goto done; ifr.ifr_flags &= ~IFF_UP; } if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) ofono_error("Failed to change interface flags"); done: close(sk); } static void pri_set_ipv4_addr(const char *interface, const char *address) { struct ifreq ifr; struct sockaddr_in addr; int sk; if (interface == NULL) return; sk = socket(PF_INET, SOCK_DGRAM, 0); if (sk < 0) return; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, interface, IFNAMSIZ); if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) goto done; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = address ? inet_addr(address) : INADDR_ANY; memcpy(&ifr.ifr_addr, &addr, sizeof(ifr.ifr_addr)); if (ioctl(sk, SIOCSIFADDR, &ifr) < 0) { ofono_error("Failed to set interface address"); goto done; } if (address == NULL) goto done; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("255.255.255.255"); memcpy(&ifr.ifr_netmask, &addr, sizeof(ifr.ifr_netmask)); if (ioctl(sk, SIOCSIFNETMASK, &ifr) < 0) ofono_error("Failed to set interface netmask"); done: close(sk); } static void pri_reset_context_settings(struct pri_context *ctx) { struct context_settings *settings; char *interface; gboolean signal_ipv4; gboolean signal_ipv6; if (ctx->context_driver == NULL) return; settings = ctx->context_driver->settings; interface = settings->interface; settings->interface = NULL; signal_ipv4 = settings->ipv4 != NULL; signal_ipv6 = settings->ipv6 != NULL; context_settings_free(settings); pri_context_signal_settings(ctx, signal_ipv4, signal_ipv6); if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) pri_set_ipv4_addr(interface, NULL); if (ctx->proxy_host != NULL) { g_free(ctx->proxy_host); ctx->proxy_host = NULL; ctx->proxy_port = 0; } pri_ifupdown(interface, FALSE); g_free(interface); } static void append_context_properties(struct pri_context *ctx, DBusMessageIter *dict) { const char *type = gprs_context_type_to_string(ctx->type); const char *proto = gprs_proto_to_string(ctx->context.proto); const char *name = ctx->name; dbus_bool_t value, preferred; const char *strvalue; struct context_settings *settings; ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING, &name); value = ctx->active; ofono_dbus_dict_append(dict, "Active", DBUS_TYPE_BOOLEAN, &value); preferred = ctx->preferred; ofono_dbus_dict_append(dict, "Preferred", DBUS_TYPE_BOOLEAN, &preferred); ofono_dbus_dict_append(dict, "Type", DBUS_TYPE_STRING, &type); ofono_dbus_dict_append(dict, "Protocol", DBUS_TYPE_STRING, &proto); strvalue = ctx->context.apn; ofono_dbus_dict_append(dict, "AccessPointName", DBUS_TYPE_STRING, &strvalue); strvalue = ctx->context.username; ofono_dbus_dict_append(dict, "Username", DBUS_TYPE_STRING, &strvalue); strvalue = ctx->context.password; ofono_dbus_dict_append(dict, "Password", DBUS_TYPE_STRING, &strvalue); strvalue = gprs_auth_method_to_string(ctx->context.auth_method); ofono_dbus_dict_append(dict, "AuthenticationMethod", DBUS_TYPE_STRING, &strvalue); if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS || (ctx->message_center && strlen(ctx->message_center) > 0)) { strvalue = ctx->message_proxy; ofono_dbus_dict_append(dict, "MessageProxy", DBUS_TYPE_STRING, &strvalue); strvalue = ctx->message_center; ofono_dbus_dict_append(dict, "MessageCenter", DBUS_TYPE_STRING, &strvalue); } if (ctx->context_driver) settings = ctx->context_driver->settings; else settings = NULL; context_settings_append_ipv4_dict(settings, dict); context_settings_append_ipv6_dict(settings, dict); } static DBusMessage *pri_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct pri_context *ctx = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); append_context_properties(ctx, &dict); dbus_message_iter_close_container(&iter, &dict); return reply; } static void pri_activate_callback(const struct ofono_error *error, void *data) { struct pri_context *ctx = data; struct ofono_gprs_context *gc = ctx->context_driver; DBG("%p", ctx); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Activating context failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&ctx->pending, __ofono_error_failed(ctx->pending)); context_settings_free(ctx->context_driver->settings); release_context(ctx); return; } if (gc->settings->interface != NULL) { pri_ifupdown(gc->settings->interface, TRUE); if (gc->settings->ipv4) { if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) pri_set_ipv4_addr(gc->settings->interface, gc->settings->ipv4->ip); pri_parse_proxy(ctx); /* Not answer yet if waiting for DNS lookup */ if (ctx->lookup_req != NULL) return; } } pri_activate_finish(ctx); } static void pri_deactivate_callback(const struct ofono_error *error, void *data) { struct pri_context *ctx = data; DBusConnection *conn = ofono_dbus_get_connection(); dbus_bool_t value; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Deactivating context failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&ctx->pending, __ofono_error_failed(ctx->pending)); return; } __ofono_dbus_pending_reply(&ctx->pending, dbus_message_new_method_return(ctx->pending)); pri_reset_context_settings(ctx); release_context(ctx); value = ctx->active; ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &value); } static void set_preferred(struct pri_context *ctx, DBusConnection *conn, gboolean preferred) { GKeyFile *settings = ctx->gprs->settings; ctx->preferred = preferred; if (settings) { g_key_file_set_boolean(settings, ctx->key, "Preferred", preferred); storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings); } ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Preferred", DBUS_TYPE_BOOLEAN, &preferred); } static DBusMessage *pri_set_preferred(struct pri_context *ctx, DBusConnection *conn, DBusMessage *msg, gboolean preferred) { if (ctx->preferred == preferred) return dbus_message_new_method_return(msg); g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); set_preferred(ctx, conn, preferred); return NULL; } static DBusMessage *pri_set_apn(struct pri_context *ctx, DBusConnection *conn, DBusMessage *msg, const char *apn) { GKeyFile *settings = ctx->gprs->settings; if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH) return __ofono_error_invalid_format(msg); if (g_str_equal(apn, ctx->context.apn)) return dbus_message_new_method_return(msg); if (is_valid_apn(apn) == FALSE) return __ofono_error_invalid_format(msg); strcpy(ctx->context.apn, apn); if (settings) { g_key_file_set_string(settings, ctx->key, "AccessPointName", apn); storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings); } g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "AccessPointName", DBUS_TYPE_STRING, &apn); return NULL; } static DBusMessage *pri_set_username(struct pri_context *ctx, DBusConnection *conn, DBusMessage *msg, const char *username) { GKeyFile *settings = ctx->gprs->settings; if (strlen(username) > OFONO_GPRS_MAX_USERNAME_LENGTH) return __ofono_error_invalid_format(msg); if (g_str_equal(username, ctx->context.username)) return dbus_message_new_method_return(msg); strcpy(ctx->context.username, username); if (settings) { g_key_file_set_string(settings, ctx->key, "Username", username); storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings); } g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Username", DBUS_TYPE_STRING, &username); return NULL; } static DBusMessage *pri_set_password(struct pri_context *ctx, DBusConnection *conn, DBusMessage *msg, const char *password) { GKeyFile *settings = ctx->gprs->settings; if (strlen(password) > OFONO_GPRS_MAX_PASSWORD_LENGTH) return __ofono_error_invalid_format(msg); if (g_str_equal(password, ctx->context.password)) return dbus_message_new_method_return(msg); strcpy(ctx->context.password, password); if (settings) { g_key_file_set_string(settings, ctx->key, "Password", password); storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings); } g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Password", DBUS_TYPE_STRING, &password); return NULL; } static DBusMessage *pri_set_type(struct pri_context *ctx, DBusConnection *conn, DBusMessage *msg, const char *type) { GKeyFile *settings = ctx->gprs->settings; enum ofono_gprs_context_type context_type; if (gprs_context_string_to_type(type, &context_type) == FALSE) return __ofono_error_invalid_format(msg); if (ctx->type == context_type) return dbus_message_new_method_return(msg); ctx->type = context_type; if (settings) { g_key_file_set_string(settings, ctx->key, "Type", type); storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings); } g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Type", DBUS_TYPE_STRING, &type); return NULL; } static DBusMessage *pri_set_proto(struct pri_context *ctx, DBusConnection *conn, DBusMessage *msg, const char *str) { GKeyFile *settings = ctx->gprs->settings; enum ofono_gprs_proto proto; if (gprs_proto_from_string(str, &proto) == FALSE) return __ofono_error_invalid_format(msg); if (ctx->context.proto == proto) return dbus_message_new_method_return(msg); ctx->context.proto = proto; if (settings) { g_key_file_set_string(settings, ctx->key, "Protocol", str); storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings); } g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Protocol", DBUS_TYPE_STRING, &str); return NULL; } static DBusMessage *pri_set_name(struct pri_context *ctx, DBusConnection *conn, DBusMessage *msg, const char *name) { GKeyFile *settings = ctx->gprs->settings; if (strlen(name) > MAX_CONTEXT_NAME_LENGTH) return __ofono_error_invalid_format(msg); if (ctx->name && g_str_equal(ctx->name, name)) return dbus_message_new_method_return(msg); strcpy(ctx->name, name); if (settings) { g_key_file_set_string(settings, ctx->key, "Name", ctx->name); storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings); } g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Name", DBUS_TYPE_STRING, &name); return NULL; } static DBusMessage *pri_set_message_proxy(struct pri_context *ctx, DBusConnection *conn, DBusMessage *msg, const char *proxy) { GKeyFile *settings = ctx->gprs->settings; if (strlen(proxy) > MAX_MESSAGE_PROXY_LENGTH) return __ofono_error_invalid_format(msg); if (ctx->message_proxy && g_str_equal(ctx->message_proxy, proxy)) return dbus_message_new_method_return(msg); strcpy(ctx->message_proxy, proxy); if (settings) { g_key_file_set_string(settings, ctx->key, "MessageProxy", ctx->message_proxy); storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings); } g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "MessageProxy", DBUS_TYPE_STRING, &proxy); return NULL; } static DBusMessage *pri_set_message_center(struct pri_context *ctx, DBusConnection *conn, DBusMessage *msg, const char *center) { GKeyFile *settings = ctx->gprs->settings; if (strlen(center) > MAX_MESSAGE_CENTER_LENGTH) return __ofono_error_invalid_format(msg); if (ctx->message_center && g_str_equal(ctx->message_center, center)) return dbus_message_new_method_return(msg); strcpy(ctx->message_center, center); if (settings) { g_key_file_set_string(settings, ctx->key, "MessageCenter", ctx->message_center); storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings); } g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "MessageCenter", DBUS_TYPE_STRING, ¢er); return NULL; } static DBusMessage *pri_set_auth_method(struct pri_context *ctx, DBusConnection *conn, DBusMessage *msg, const char *str) { GKeyFile *settings = ctx->gprs->settings; enum ofono_gprs_auth_method auth; if (gprs_auth_method_from_string(str, &auth) == FALSE) return __ofono_error_invalid_format(msg); if (ctx->context.auth_method == auth) return dbus_message_new_method_return(msg); ctx->context.auth_method = auth; if (settings) { g_key_file_set_string(settings, ctx->key, "AuthenticationMethod", str); storage_sync(ctx->gprs->imsi, SETTINGS_STORE, settings); } g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "AuthenticationMethod", DBUS_TYPE_STRING, &str); return NULL; } static DBusMessage *pri_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct pri_context *ctx = data; DBusMessageIter iter; DBusMessageIter var; const char *property; dbus_bool_t value; const char *str; if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (g_str_equal(property, "Active")) { struct ofono_gprs_context *gc; if (ctx->gprs->pending) return __ofono_error_busy(msg); if (ctx->pending) return __ofono_error_busy(msg); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (ctx->active == (ofono_bool_t) value) return dbus_message_new_method_return(msg); if (value && !ctx->gprs->attached) return __ofono_error_not_attached(msg); if (ctx->gprs->flags & GPRS_FLAG_ATTACHING) return __ofono_error_attach_in_progress(msg); if (value && assign_context(ctx) == FALSE) return __ofono_error_not_implemented(msg); gc = ctx->context_driver; ctx->pending = dbus_message_ref(msg); if (value) gc->driver->activate_primary(gc, &ctx->context, pri_activate_callback, ctx); else gc->driver->deactivate_primary(gc, ctx->context.cid, pri_deactivate_callback, ctx); return NULL; } if (!strcmp(property, "Preferred")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); return pri_set_preferred(ctx, conn, msg, value); } /* All other properties are read-only when context is active */ if (ctx->active == TRUE) return __ofono_error_in_use(msg); if (!strcmp(property, "AccessPointName")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return pri_set_apn(ctx, conn, msg, str); } else if (!strcmp(property, "Type")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return pri_set_type(ctx, conn, msg, str); } else if (!strcmp(property, "Protocol")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return pri_set_proto(ctx, conn, msg, str); } else if (!strcmp(property, "Username")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return pri_set_username(ctx, conn, msg, str); } else if (!strcmp(property, "Password")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return pri_set_password(ctx, conn, msg, str); } else if (!strcmp(property, "Name")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return pri_set_name(ctx, conn, msg, str); } else if (!strcmp(property, "AuthenticationMethod")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return pri_set_auth_method(ctx, conn, msg, str); } if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_ANY || ctx->type == OFONO_GPRS_CONTEXT_TYPE_WAP || ctx->type == OFONO_GPRS_CONTEXT_TYPE_IMS || ctx->type == OFONO_GPRS_CONTEXT_TYPE_IA) return __ofono_error_invalid_args(msg); if (!strcmp(property, "MessageProxy")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return pri_set_message_proxy(ctx, conn, msg, str); } else if (!strcmp(property, "MessageCenter")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &str); return pri_set_message_center(ctx, conn, msg, str); } return __ofono_error_invalid_args(msg); } static const GDBusMethodTable context_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), pri_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, pri_set_property) }, { } }; static const GDBusSignalTable context_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static struct pri_context *pri_context_create(struct ofono_gprs *gprs, const char *name, enum ofono_gprs_context_type type) { struct pri_context *context = g_try_new0(struct pri_context, 1); if (context == NULL) return NULL; if (name == NULL) { name = gprs_context_default_name(type); if (name == NULL) { g_free(context); return NULL; } } context->gprs = gprs; strcpy(context->name, name); context->type = type; return context; } static void pri_context_destroy(gpointer userdata) { struct pri_context *ctx = userdata; g_free(ctx->proxy_host); g_free(ctx->path); g_free(ctx); } static gboolean context_dbus_register(struct pri_context *ctx) { DBusConnection *conn = ofono_dbus_get_connection(); char path[256]; const char *basepath; basepath = __ofono_atom_get_path(ctx->gprs->atom); snprintf(path, sizeof(path), "%s/context%u", basepath, ctx->id); if (!g_dbus_register_interface(conn, path, OFONO_CONNECTION_CONTEXT_INTERFACE, context_methods, context_signals, NULL, ctx, pri_context_destroy)) { ofono_error("Could not register PrimaryContext %s", path); idmap_put(ctx->gprs->pid_map, ctx->id); pri_context_destroy(ctx); return FALSE; } ctx->path = g_strdup(path); ctx->key = ctx->path + strlen(basepath) + 1; return TRUE; } static gboolean context_dbus_unregister(struct pri_context *ctx) { DBusConnection *conn = ofono_dbus_get_connection(); char path[256]; if (ctx->active == TRUE) { const char *interface = ctx->context_driver->settings->interface; if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) pri_set_ipv4_addr(interface, NULL); pri_ifupdown(interface, FALSE); } strcpy(path, ctx->path); idmap_put(ctx->gprs->pid_map, ctx->id); return g_dbus_unregister_interface(conn, path, OFONO_CONNECTION_CONTEXT_INTERFACE); } static void update_suspended_property(struct ofono_gprs *gprs, ofono_bool_t suspended) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(gprs->atom); dbus_bool_t value = suspended; if (gprs->suspend_timeout) { g_source_remove(gprs->suspend_timeout); gprs->suspend_timeout = 0; } if (gprs->suspended == suspended) return; DBG("%s GPRS service %s", __ofono_atom_get_path(gprs->atom), suspended ? "suspended" : "resumed"); gprs->suspended = suspended; if (gprs->attached) ofono_dbus_signal_property_changed(conn, path, OFONO_CONNECTION_MANAGER_INTERFACE, "Suspended", DBUS_TYPE_BOOLEAN, &value); } static gboolean suspend_timeout(gpointer data) { struct ofono_gprs *gprs = data; gprs->suspend_timeout = 0; update_suspended_property(gprs, TRUE); return FALSE; } void ofono_gprs_suspend_notify(struct ofono_gprs *gprs, int cause) { switch (cause) { case GPRS_SUSPENDED_DETACHED: case GPRS_SUSPENDED_CALL: case GPRS_SUSPENDED_NO_COVERAGE: update_suspended_property(gprs, TRUE); break; case GPRS_SUSPENDED_SIGNALLING: case GPRS_SUSPENDED_UNKNOWN_CAUSE: if (gprs->suspend_timeout) g_source_remove(gprs->suspend_timeout); gprs->suspend_timeout = g_timeout_add_seconds(SUSPEND_TIMEOUT, suspend_timeout, gprs); break; } } void ofono_gprs_resume_notify(struct ofono_gprs *gprs) { update_suspended_property(gprs, FALSE); } static gboolean have_active_contexts(struct ofono_gprs *gprs) { GSList *l; struct pri_context *ctx; for (l = gprs->contexts; l; l = l->next) { ctx = l->data; if (ctx->active == TRUE) return TRUE; } return FALSE; } static void release_active_contexts(struct ofono_gprs *gprs) { GSList *l; struct pri_context *ctx; for (l = gprs->contexts; l; l = l->next) { struct ofono_gprs_context *gc; ctx = l->data; if (ctx->active == FALSE) continue; /* This context is already being messed with */ if (ctx->pending) continue; gc = ctx->context_driver; if (gc->driver->detach_shutdown != NULL) gc->driver->detach_shutdown(gc, ctx->context.cid); } } static void gprs_attached_update(struct ofono_gprs *gprs) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; ofono_bool_t attached; dbus_bool_t value; attached = gprs->driver_attached && (gprs->status == NETWORK_REGISTRATION_STATUS_REGISTERED || gprs->status == NETWORK_REGISTRATION_STATUS_ROAMING); if (attached == gprs->attached) return; /* * If an active context is found, a PPP session might be still active * at driver level. "Attached" = TRUE property can't be signalled to * the applications registered on GPRS properties. * Active contexts have to be release at driver level. */ if (attached == FALSE) { release_active_contexts(gprs); gprs->bearer = -1; } else if (have_active_contexts(gprs) == TRUE) { gprs->flags |= GPRS_FLAG_ATTACHED_UPDATE; return; } gprs->attached = attached; path = __ofono_atom_get_path(gprs->atom); value = attached; ofono_dbus_signal_property_changed(conn, path, OFONO_CONNECTION_MANAGER_INTERFACE, "Attached", DBUS_TYPE_BOOLEAN, &value); } static void registration_status_cb(const struct ofono_error *error, int status, void *data) { struct ofono_gprs *gprs = data; DBG("%s error %d status %d", __ofono_atom_get_path(gprs->atom), error->type, status); gprs->flags &= ~GPRS_FLAG_ATTACHING; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) ofono_gprs_status_notify(gprs, status); else gprs_attached_update(gprs); if (gprs->flags & GPRS_FLAG_RECHECK) { gprs->flags &= ~GPRS_FLAG_RECHECK; gprs_netreg_update(gprs); } } static void gprs_attach_callback(const struct ofono_error *error, void *data) { struct ofono_gprs *gprs = data; DBG("%s error = %d", __ofono_atom_get_path(gprs->atom), error->type); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) gprs->driver_attached = !gprs->driver_attached; if (gprs->driver->attached_status == NULL) { struct ofono_error status_error; status_error.type = OFONO_ERROR_TYPE_FAILURE; status_error.error = 0; registration_status_cb(&status_error, -1, gprs); return; } gprs->driver->attached_status(gprs, registration_status_cb, gprs); } static void gprs_netreg_removed(struct ofono_gprs *gprs) { gprs->netreg = NULL; gprs->flags &= ~(GPRS_FLAG_RECHECK | GPRS_FLAG_ATTACHING); gprs->status_watch = 0; gprs->netreg_status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED; gprs->driver_attached = FALSE; gprs_attached_update(gprs); } static void gprs_netreg_update(struct ofono_gprs *gprs) { ofono_bool_t attach; attach = gprs->netreg_status == NETWORK_REGISTRATION_STATUS_REGISTERED; attach = attach || (gprs->roaming_allowed && gprs->netreg_status == NETWORK_REGISTRATION_STATUS_ROAMING); attach = attach && gprs->powered; DBG("attach: %u, driver_attached: %u", attach, gprs->driver_attached); if (gprs->driver_attached == attach) return; if (gprs->flags & GPRS_FLAG_ATTACHING) { gprs->flags |= GPRS_FLAG_RECHECK; return; } gprs->flags |= GPRS_FLAG_ATTACHING; gprs->driver->set_attached(gprs, attach, gprs_attach_callback, gprs); gprs->driver_attached = attach; } static void netreg_status_changed(int status, int lac, int ci, int tech, const char *mcc, const char *mnc, void *data) { struct ofono_gprs *gprs = data; DBG("%d", status); gprs->netreg_status = status; gprs_netreg_update(gprs); } static void notify_connection_powered(struct ofono_modem *modem, void *data) { struct ofono_atom *atom; struct ofono_gprs *gprs; struct ofono_modem *modem_notif = data; DBusConnection *conn; const char *path = ofono_modem_get_path(modem); if (strcmp(path, ofono_modem_get_path(modem_notif)) == 0) return; if (!ofono_modem_is_standby(modem)) return; atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_GPRS); if (atom == NULL) return; gprs = __ofono_atom_get_data(atom); if (gprs->driver->set_attached == NULL) return; if (gprs->powered == FALSE) return; gprs->powered = FALSE; if (gprs->settings) { g_key_file_set_integer(gprs->settings, SETTINGS_GROUP, "Powered", gprs->powered); storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings); } gprs_netreg_update(gprs); conn = ofono_dbus_get_connection(); ofono_dbus_signal_property_changed(conn, path, OFONO_CONNECTION_MANAGER_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &gprs->powered); } static void notify_powered_change(struct ofono_gprs *gprs) { if (gprs->powered) { struct ofono_modem *modem = __ofono_atom_get_modem(gprs->atom); __ofono_modem_foreach(notify_connection_powered, modem); } } static DBusMessage *gprs_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_gprs *gprs = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; dbus_bool_t value; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); value = gprs->attached; ofono_dbus_dict_append(&dict, "Attached", DBUS_TYPE_BOOLEAN, &value); if (gprs->bearer != -1) { const char *bearer = packet_bearer_to_string(gprs->bearer); ofono_dbus_dict_append(&dict, "Bearer", DBUS_TYPE_STRING, &bearer); } value = gprs->roaming_allowed; ofono_dbus_dict_append(&dict, "RoamingAllowed", DBUS_TYPE_BOOLEAN, &value); value = gprs->powered; ofono_dbus_dict_append(&dict, "Powered", DBUS_TYPE_BOOLEAN, &value); if (gprs->attached) { value = gprs->suspended; ofono_dbus_dict_append(&dict, "Suspended", DBUS_TYPE_BOOLEAN, &value); } dbus_message_iter_close_container(&iter, &dict); return reply; } static DBusMessage *gprs_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_gprs *gprs = data; DBusMessageIter iter; DBusMessageIter var; const char *property; dbus_bool_t value; const char *path; if (gprs->pending) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (!strcmp(property, "RoamingAllowed")) { if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (gprs->roaming_allowed == (ofono_bool_t) value) return dbus_message_new_method_return(msg); gprs->roaming_allowed = value; if (gprs->settings) { g_key_file_set_integer(gprs->settings, SETTINGS_GROUP, "RoamingAllowed", gprs->roaming_allowed); storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings); } gprs_netreg_update(gprs); } else if (!strcmp(property, "Powered")) { if (gprs->driver->set_attached == NULL) return __ofono_error_not_implemented(msg); if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (gprs->powered == (ofono_bool_t) value) return dbus_message_new_method_return(msg); gprs->powered = value; if (gprs->settings) { g_key_file_set_integer(gprs->settings, SETTINGS_GROUP, "Powered", gprs->powered); storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings); } gprs_netreg_update(gprs); notify_powered_change(gprs); } else { return __ofono_error_invalid_args(msg); } path = __ofono_atom_get_path(gprs->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_CONNECTION_MANAGER_INTERFACE, property, DBUS_TYPE_BOOLEAN, &value); return dbus_message_new_method_return(msg); } static void write_context_settings(struct ofono_gprs *gprs, struct pri_context *context) { const char *auth_method; g_key_file_set_string(gprs->settings, context->key, "Name", context->name); g_key_file_set_string(gprs->settings, context->key, "AccessPointName", context->context.apn); g_key_file_set_string(gprs->settings, context->key, "Username", context->context.username); g_key_file_set_string(gprs->settings, context->key, "Password", context->context.password); auth_method = gprs_auth_method_to_string(context->context.auth_method); g_key_file_set_string(gprs->settings, context->key, "AuthenticationMethod", auth_method); g_key_file_set_string(gprs->settings, context->key, "Type", gprs_context_type_to_string(context->type)); g_key_file_set_string(gprs->settings, context->key, "Protocol", gprs_proto_to_string(context->context.proto)); g_key_file_set_boolean(gprs->settings, context->key, "Preferred", context->preferred); if (context->type == OFONO_GPRS_CONTEXT_TYPE_MMS || (context->message_center && strlen(context->message_center) > 0)) { g_key_file_set_string(gprs->settings, context->key, "MessageProxy", context->message_proxy); g_key_file_set_string(gprs->settings, context->key, "MessageCenter", context->message_center); } } static struct pri_context *add_context(struct ofono_gprs *gprs, const char *name, enum ofono_gprs_context_type type) { unsigned int id; struct pri_context *context; if (gprs->last_context_id) id = idmap_alloc_next(gprs->pid_map, gprs->last_context_id); else id = idmap_alloc(gprs->pid_map); if (id > idmap_get_max(gprs->pid_map)) return NULL; context = pri_context_create(gprs, name, type); if (context == NULL) { idmap_put(gprs->pid_map, id); ofono_error("Unable to allocate context struct"); return NULL; } context->id = id; DBG("Registering new context"); if (!context_dbus_register(context)) { ofono_error("Unable to register primary context"); return NULL; } gprs->last_context_id = id; if (gprs->settings) { write_context_settings(gprs, context); storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings); } gprs->contexts = g_slist_append(gprs->contexts, context); return context; } static void send_context_added_signal(struct ofono_gprs *gprs, struct pri_context *context, DBusConnection *conn) { const char *path; DBusMessage *signal; DBusMessageIter iter; DBusMessageIter dict; path = __ofono_atom_get_path(gprs->atom); signal = dbus_message_new_signal(path, OFONO_CONNECTION_MANAGER_INTERFACE, "ContextAdded"); if (!signal) return; dbus_message_iter_init_append(signal, &iter); path = context->path; dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); append_context_properties(context, &dict); dbus_message_iter_close_container(&iter, &dict); g_dbus_send_message(conn, signal); } static DBusMessage *gprs_add_context(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_gprs *gprs = data; struct pri_context *context; const char *typestr; const char *name; const char *path; enum ofono_gprs_context_type type; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr, DBUS_TYPE_INVALID)) return __ofono_error_invalid_args(msg); if (gprs_context_string_to_type(typestr, &type) == FALSE) return __ofono_error_invalid_format(msg); name = gprs_context_default_name(type); if (name == NULL) name = typestr; context = add_context(gprs, name, type); if (context == NULL) return __ofono_error_failed(msg); path = context->path; g_dbus_send_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); send_context_added_signal(gprs, context, conn); return NULL; } static void gprs_deactivate_for_remove(const struct ofono_error *error, void *data) { struct pri_context *ctx = data; struct ofono_gprs *gprs = ctx->gprs; DBusConnection *conn = ofono_dbus_get_connection(); char *path; const char *atompath; dbus_bool_t value; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Removing context failed with error: %s", telephony_error_to_str(error)); __ofono_dbus_pending_reply(&gprs->pending, __ofono_error_failed(gprs->pending)); return; } pri_reset_context_settings(ctx); release_context(ctx); value = FALSE; ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &value); if (gprs->settings) { g_key_file_remove_group(gprs->settings, ctx->key, NULL); storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings); } /* Make a backup copy of path for signal emission below */ path = g_strdup(ctx->path); context_dbus_unregister(ctx); gprs->contexts = g_slist_remove(gprs->contexts, ctx); __ofono_dbus_pending_reply(&gprs->pending, dbus_message_new_method_return(gprs->pending)); atompath = __ofono_atom_get_path(gprs->atom); g_dbus_emit_signal(conn, atompath, OFONO_CONNECTION_MANAGER_INTERFACE, "ContextRemoved", DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); g_free(path); } static DBusMessage *gprs_remove_context(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_gprs *gprs = data; struct pri_context *ctx; const char *path; const char *atompath; if (gprs->pending) return __ofono_error_busy(msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return __ofono_error_invalid_args(msg); if (path[0] == '\0') return __ofono_error_invalid_format(msg); ctx = gprs_context_by_path(gprs, path); if (ctx == NULL) return __ofono_error_not_found(msg); if (ctx->active) { struct ofono_gprs_context *gc = ctx->context_driver; /* This context is already being messed with */ if (ctx->pending) return __ofono_error_busy(msg); gprs->pending = dbus_message_ref(msg); gc->driver->deactivate_primary(gc, ctx->context.cid, gprs_deactivate_for_remove, ctx); return NULL; } if (gprs->settings) { g_key_file_remove_group(gprs->settings, ctx->key, NULL); storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings); } DBG("Unregistering context: %s", ctx->path); context_dbus_unregister(ctx); gprs->contexts = g_slist_remove(gprs->contexts, ctx); g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID); atompath = __ofono_atom_get_path(gprs->atom); g_dbus_emit_signal(conn, atompath, OFONO_CONNECTION_MANAGER_INTERFACE, "ContextRemoved", DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); return NULL; } static void gprs_deactivate_for_all(const struct ofono_error *error, void *data) { struct pri_context *ctx = data; struct ofono_gprs *gprs = ctx->gprs; DBusConnection *conn; dbus_bool_t value; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { __ofono_dbus_pending_reply(&gprs->pending, __ofono_error_failed(gprs->pending)); return; } pri_reset_context_settings(ctx); release_context(ctx); value = ctx->active; conn = ofono_dbus_get_connection(); ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &value); gprs_deactivate_next(gprs); } static void gprs_deactivate_next(struct ofono_gprs *gprs) { GSList *l; struct pri_context *ctx; struct ofono_gprs_context *gc; for (l = gprs->contexts; l; l = l->next) { ctx = l->data; if (ctx->active == FALSE) continue; gc = ctx->context_driver; gc->driver->deactivate_primary(gc, ctx->context.cid, gprs_deactivate_for_all, ctx); return; } __ofono_dbus_pending_reply(&gprs->pending, dbus_message_new_method_return(gprs->pending)); } static DBusMessage *gprs_deactivate_all(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_gprs *gprs = data; GSList *l; struct pri_context *ctx; if (gprs->pending) return __ofono_error_busy(msg); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) return __ofono_error_invalid_args(msg); for (l = gprs->contexts; l; l = l->next) { ctx = l->data; if (ctx->pending) return __ofono_error_busy(msg); } gprs->pending = dbus_message_ref(msg); gprs_deactivate_next(gprs); return NULL; } static DBusMessage *gprs_get_contexts(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_gprs *gprs = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; DBusMessageIter entry, dict; const char *path; GSList *l; struct pri_context *ctx; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array); for (l = gprs->contexts; l; l = l->next) { ctx = l->data; path = ctx->path; dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); append_context_properties(ctx, &dict); dbus_message_iter_close_container(&entry, &dict); dbus_message_iter_close_container(&array, &entry); } dbus_message_iter_close_container(&iter, &array); return reply; } static void provision_context(const struct ofono_gprs_provision_data *ap, struct ofono_gprs *gprs) { unsigned int id; struct pri_context *context = NULL; /* Sanity check */ if (ap == NULL) return; if (ap->name && strlen(ap->name) > MAX_CONTEXT_NAME_LENGTH) return; if (ap->apn == NULL || strlen(ap->apn) > OFONO_GPRS_MAX_APN_LENGTH) return; if (is_valid_apn(ap->apn) == FALSE) return; if (ap->username && strlen(ap->username) > OFONO_GPRS_MAX_USERNAME_LENGTH) return; if (ap->password && strlen(ap->password) > OFONO_GPRS_MAX_PASSWORD_LENGTH) return; if (ap->message_proxy && strlen(ap->message_proxy) > MAX_MESSAGE_PROXY_LENGTH) return; if (ap->message_center && strlen(ap->message_center) > MAX_MESSAGE_CENTER_LENGTH) return; if (gprs->last_context_id) id = idmap_alloc_next(gprs->pid_map, gprs->last_context_id); else id = idmap_alloc(gprs->pid_map); if (id > idmap_get_max(gprs->pid_map)) return; context = pri_context_create(gprs, ap->name, ap->type); if (context == NULL) { idmap_put(gprs->pid_map, id); return; } context->id = id; if (ap->username != NULL) strcpy(context->context.username, ap->username); if (ap->password != NULL) strcpy(context->context.password, ap->password); context->context.auth_method = ap->auth_method; strcpy(context->context.apn, ap->apn); context->context.proto = ap->proto; if (ap->type == OFONO_GPRS_CONTEXT_TYPE_MMS || ap->type == OFONO_GPRS_CONTEXT_TYPE_INTERNET) { if (ap->message_proxy != NULL) strcpy(context->message_proxy, ap->message_proxy); if (ap->message_center != NULL) strcpy(context->message_center, ap->message_center); } if (context_dbus_register(context) == FALSE) return; gprs->last_context_id = id; if (gprs->settings) { write_context_settings(gprs, context); storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings); } gprs->contexts = g_slist_append(gprs->contexts, context); } static void provision_contexts(struct ofono_gprs *gprs, const char *mcc, const char *mnc, const char *spn, const char *gid1) { struct ofono_gprs_provision_data *settings; int count; int i; if (__ofono_gprs_provision_get_settings(mcc, mnc, spn, gprs->imsi, gid1, &settings, &count) == FALSE) { ofono_warn("Provisioning failed"); return; } for (i = 0; i < count; i++) provision_context(&settings[i], gprs); __ofono_gprs_provision_free_settings(settings, count); } static void remove_non_active_context(struct ofono_gprs *gprs, struct pri_context *ctx, DBusConnection *conn) { char *path; const char *atompath; if (gprs->settings) { g_key_file_remove_group(gprs->settings, ctx->key, NULL); storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings); } /* Make a backup copy of path for signal emission below */ path = g_strdup(ctx->path); context_dbus_unregister(ctx); gprs->contexts = g_slist_remove(gprs->contexts, ctx); atompath = __ofono_atom_get_path(gprs->atom); g_dbus_emit_signal(conn, atompath, OFONO_CONNECTION_MANAGER_INTERFACE, "ContextRemoved", DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); g_free(path); } static DBusMessage *gprs_reset_contexts(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_gprs *gprs = data; DBusMessage *reply; GSList *l; if (gprs->pending) return __ofono_error_busy(msg); /* * We want __ofono_error_busy to take precedence over * __ofono_error_not_allowed errors, so we check it first. */ for (l = gprs->contexts; l; l = l->next) { struct pri_context *ctx = l->data; if (ctx->pending) return __ofono_error_busy(msg); } if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) return __ofono_error_invalid_args(msg); if (gprs->powered) return __ofono_error_not_allowed(msg); for (l = gprs->contexts; l; l = l->next) { struct pri_context *ctx = l->data; if (ctx->active) return __ofono_error_not_allowed(msg); } reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; /* Remove first the current contexts, re-provision after */ while (gprs->contexts != NULL) { struct pri_context *ctx = gprs->contexts->data; remove_non_active_context(gprs, ctx, conn); } gprs->last_context_id = 0; provision_contexts(gprs, ofono_sim_get_mcc(gprs->sim), ofono_sim_get_mnc(gprs->sim), ofono_sim_get_spn(gprs->sim), gprs->gid1); if (gprs->contexts == NULL) /* Automatic provisioning failed */ add_context(gprs, NULL, OFONO_GPRS_CONTEXT_TYPE_INTERNET); for (l = gprs->contexts; l; l = l->next) { struct pri_context *ctx = l->data; send_context_added_signal(gprs, ctx, conn); } return reply; } static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), gprs_get_properties) }, { GDBUS_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, gprs_set_property) }, { GDBUS_ASYNC_METHOD("AddContext", GDBUS_ARGS({ "type", "s" }), GDBUS_ARGS({ "path", "o" }), gprs_add_context) }, { GDBUS_ASYNC_METHOD("RemoveContext", GDBUS_ARGS({ "path", "o" }), NULL, gprs_remove_context) }, { GDBUS_ASYNC_METHOD("DeactivateAll", NULL, NULL, gprs_deactivate_all) }, { GDBUS_METHOD("GetContexts", NULL, GDBUS_ARGS({ "contexts_with_properties", "a(oa{sv})" }), gprs_get_contexts) }, { GDBUS_ASYNC_METHOD("ResetContexts", NULL, NULL, gprs_reset_contexts) }, { } }; static const GDBusSignalTable manager_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { GDBUS_SIGNAL("ContextAdded", GDBUS_ARGS({ "path", "o" }, { "properties", "v" })) }, { GDBUS_SIGNAL("ContextRemoved", GDBUS_ARGS({ "path", "o" })) }, { } }; void ofono_gprs_detached_notify(struct ofono_gprs *gprs) { DBG("%s", __ofono_atom_get_path(gprs->atom)); gprs->driver_attached = FALSE; gprs_attached_update(gprs); /* * TODO: The network forced a detach, we should wait for some time * and try to re-attach. This might also be related to a suspend * event while voicecall is active. */ } void ofono_gprs_status_notify(struct ofono_gprs *gprs, int status) { DBG("%s status %d", __ofono_atom_get_path(gprs->atom), status); if (status == NETWORK_REGISTRATION_STATUS_ROAMING && ofono_netreg_get_status(gprs->netreg) == NETWORK_REGISTRATION_STATUS_REGISTERED) { DBG("MVNO: roaming -> registered"); status = NETWORK_REGISTRATION_STATUS_REGISTERED; } gprs->status = status; if (status != NETWORK_REGISTRATION_STATUS_REGISTERED && status != NETWORK_REGISTRATION_STATUS_ROAMING) { gprs_attached_update(gprs); return; } /* * If we're already taking action, e.g. attaching or detaching, then * ignore this notification for now, we will take appropriate action * after the set_attach operation has completed */ if (gprs->flags & GPRS_FLAG_ATTACHING) return; /* We registered without being powered */ if (gprs->powered == FALSE) goto detach; if (gprs->roaming_allowed == FALSE && status == NETWORK_REGISTRATION_STATUS_ROAMING) goto detach; gprs->driver_attached = TRUE; gprs_attached_update(gprs); return; detach: gprs->flags |= GPRS_FLAG_ATTACHING; gprs->driver->set_attached(gprs, FALSE, gprs_attach_callback, gprs); } void ofono_gprs_set_cid_range(struct ofono_gprs *gprs, unsigned int min, unsigned int max) { if (gprs == NULL) return; if (gprs->cid_map) idmap_free(gprs->cid_map); gprs->cid_map = idmap_new_from_range(min, max); } static void gprs_context_unregister(struct ofono_atom *atom) { struct ofono_gprs_context *gc = __ofono_atom_get_data(atom); DBusConnection *conn = ofono_dbus_get_connection(); GSList *l; struct pri_context *ctx; dbus_bool_t value; DBG("%p, %p", gc, gc->gprs); if (gc->gprs == NULL) goto done; for (l = gc->gprs->contexts; l; l = l->next) { ctx = l->data; if (ctx->context_driver != gc) continue; if (ctx->pending != NULL) __ofono_dbus_pending_reply(&ctx->pending, __ofono_error_failed(ctx->pending)); if (ctx->active == FALSE) break; pri_reset_context_settings(ctx); release_context(ctx); value = FALSE; ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &value); } gc->gprs->context_drivers = g_slist_remove(gc->gprs->context_drivers, gc); gc->gprs = NULL; done: if (gc->settings) { context_settings_free(gc->settings); g_free(gc->settings); gc->settings = NULL; } } void ofono_gprs_add_context(struct ofono_gprs *gprs, struct ofono_gprs_context *gc) { if (gc->driver == NULL) return; gc->gprs = gprs; gc->settings = g_new0(struct context_settings, 1); gprs->context_drivers = g_slist_append(gprs->context_drivers, gc); __ofono_atom_register(gc->atom, gprs_context_unregister); } void ofono_gprs_bearer_notify(struct ofono_gprs *gprs, int bearer) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path; const char *value; if (gprs->bearer == bearer) return; gprs->bearer = bearer; path = __ofono_atom_get_path(gprs->atom); value = packet_bearer_to_string(bearer); ofono_dbus_signal_property_changed(conn, path, OFONO_CONNECTION_MANAGER_INTERFACE, "Bearer", DBUS_TYPE_STRING, &value); } void ofono_gprs_context_deactivated(struct ofono_gprs_context *gc, unsigned int cid) { DBusConnection *conn = ofono_dbus_get_connection(); GSList *l; struct pri_context *ctx; dbus_bool_t value; if (gc->gprs == NULL) return; for (l = gc->gprs->contexts; l; l = l->next) { ctx = l->data; if (ctx->context.cid != cid) continue; if (ctx->active == FALSE) break; pri_reset_context_settings(ctx); release_context(ctx); value = FALSE; ofono_dbus_signal_property_changed(conn, ctx->path, OFONO_CONNECTION_CONTEXT_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &value); } /* * If "Attached" property was about to be signalled as TRUE but there * were still active contexts, try again to signal "Attached" property * to registered applications after active contexts have been released. */ if (gc->gprs->flags & GPRS_FLAG_ATTACHED_UPDATE) { gc->gprs->flags &= ~GPRS_FLAG_ATTACHED_UPDATE; gprs_attached_update(gc->gprs); } } int ofono_gprs_context_driver_register( const struct ofono_gprs_context_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_context_drivers = g_slist_prepend(g_context_drivers, (void *) d); return 0; } void ofono_gprs_context_driver_unregister( const struct ofono_gprs_context_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_context_drivers = g_slist_remove(g_context_drivers, (void *) d); } static void gprs_context_remove(struct ofono_atom *atom) { struct ofono_gprs_context *gc = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (gc == NULL) return; if (gc->driver && gc->driver->remove) gc->driver->remove(gc); g_free(gc); } struct ofono_gprs_context *ofono_gprs_context_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_gprs_context *gc; GSList *l; if (driver == NULL) return NULL; gc = g_try_new0(struct ofono_gprs_context, 1); if (gc == NULL) return NULL; gc->type = OFONO_GPRS_CONTEXT_TYPE_ANY; gc->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_GPRS_CONTEXT, gprs_context_remove, gc); for (l = g_context_drivers; l; l = l->next) { const struct ofono_gprs_context_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(gc, vendor, data) < 0) continue; gc->driver = drv; break; } return gc; } void ofono_gprs_context_remove(struct ofono_gprs_context *gc) { if (gc == NULL) return; __ofono_atom_free(gc->atom); } void ofono_gprs_context_set_data(struct ofono_gprs_context *gc, void *data) { gc->driver_data = data; } void *ofono_gprs_context_get_data(struct ofono_gprs_context *gc) { return gc->driver_data; } struct ofono_modem *ofono_gprs_context_get_modem(struct ofono_gprs_context *gc) { return __ofono_atom_get_modem(gc->atom); } void ofono_gprs_context_set_type(struct ofono_gprs_context *gc, enum ofono_gprs_context_type type) { DBG("type %d", type); gc->type = type; } void ofono_gprs_context_set_interface(struct ofono_gprs_context *gc, const char *interface) { struct context_settings *settings = gc->settings; g_free(settings->interface); settings->interface = g_strdup(interface); } void ofono_gprs_context_set_ipv4_address(struct ofono_gprs_context *gc, const char *address, ofono_bool_t static_ip) { struct context_settings *settings = gc->settings; if (settings->ipv4 == NULL) return; g_free(settings->ipv4->ip); settings->ipv4->ip = g_strdup(address); settings->ipv4->static_ip = static_ip; } void ofono_gprs_context_set_ipv4_netmask(struct ofono_gprs_context *gc, const char *netmask) { struct context_settings *settings = gc->settings; if (settings->ipv4 == NULL) return; g_free(settings->ipv4->netmask); settings->ipv4->netmask = g_strdup(netmask); } void ofono_gprs_context_set_ipv4_gateway(struct ofono_gprs_context *gc, const char *gateway) { struct context_settings *settings = gc->settings; if (settings->ipv4 == NULL) return; g_free(settings->ipv4->gateway); settings->ipv4->gateway = g_strdup(gateway); } void ofono_gprs_context_set_ipv4_dns_servers(struct ofono_gprs_context *gc, const char **dns) { struct context_settings *settings = gc->settings; if (settings->ipv4 == NULL) return; g_strfreev(settings->ipv4->dns); settings->ipv4->dns = g_strdupv((char **) dns); } void ofono_gprs_context_set_ipv6_address(struct ofono_gprs_context *gc, const char *address) { struct context_settings *settings = gc->settings; if (settings->ipv6 == NULL) return; g_free(settings->ipv6->ip); settings->ipv6->ip = g_strdup(address); } void ofono_gprs_context_set_ipv6_prefix_length(struct ofono_gprs_context *gc, unsigned char length) { struct context_settings *settings = gc->settings; if (settings->ipv6 == NULL) return; settings->ipv6->prefix_len = length; } void ofono_gprs_context_set_ipv6_gateway(struct ofono_gprs_context *gc, const char *gateway) { struct context_settings *settings = gc->settings; if (settings->ipv6 == NULL) return; g_free(settings->ipv6->gateway); settings->ipv6->gateway = g_strdup(gateway); } void ofono_gprs_context_set_ipv6_dns_servers(struct ofono_gprs_context *gc, const char **dns) { struct context_settings *settings = gc->settings; if (settings->ipv6 == NULL) return; g_strfreev(settings->ipv6->dns); settings->ipv6->dns = g_strdupv((char **) dns); } int ofono_gprs_driver_register(const struct ofono_gprs_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *)d); return 0; } void ofono_gprs_driver_unregister(const struct ofono_gprs_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *)d); } static void free_contexts(struct ofono_gprs *gprs) { GSList *l; if (gprs->settings) { storage_close(gprs->imsi, SETTINGS_STORE, gprs->settings, TRUE); g_free(gprs->imsi); gprs->imsi = NULL; gprs->settings = NULL; } for (l = gprs->contexts; l; l = l->next) { struct pri_context *context = l->data; context_dbus_unregister(context); } g_slist_free(gprs->contexts); } static void gprs_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_gprs *gprs = __ofono_atom_get_data(atom); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); DBG("%p", gprs); free_contexts(gprs); if (gprs->cid_map) { idmap_free(gprs->cid_map); gprs->cid_map = NULL; } if (gprs->netreg_watch) { if (gprs->status_watch) { __ofono_netreg_remove_status_watch(gprs->netreg, gprs->status_watch); gprs->status_watch = 0; } __ofono_modem_remove_atom_watch(modem, gprs->netreg_watch); gprs->netreg_watch = 0; gprs->netreg = NULL; } if (gprs->spn_watch) ofono_sim_remove_spn_watch(gprs->sim, &gprs->spn_watch); if (gprs->sim_context) { ofono_sim_context_free(gprs->sim_context); gprs->sim_context = NULL; } ofono_modem_remove_interface(modem, OFONO_CONNECTION_MANAGER_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_CONNECTION_MANAGER_INTERFACE); } static void gprs_remove(struct ofono_atom *atom) { struct ofono_gprs *gprs = __ofono_atom_get_data(atom); GSList *l; DBG("atom: %p", atom); if (gprs == NULL) return; if (gprs->suspend_timeout) g_source_remove(gprs->suspend_timeout); if (gprs->pid_map) { idmap_free(gprs->pid_map); gprs->pid_map = NULL; } for (l = gprs->context_drivers; l; l = l->next) { struct ofono_gprs_context *gc = l->data; gc->gprs = NULL; } g_free(gprs->gid1); g_slist_free(gprs->context_drivers); if (gprs->driver && gprs->driver->remove) gprs->driver->remove(gprs); g_free(gprs); } struct ofono_gprs *ofono_gprs_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_gprs *gprs; GSList *l; if (driver == NULL) return NULL; gprs = g_try_new0(struct ofono_gprs, 1); if (gprs == NULL) return NULL; gprs->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_GPRS, gprs_remove, gprs); for (l = g_drivers; l; l = l->next) { const struct ofono_gprs_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(gprs, vendor, data) < 0) continue; gprs->driver = drv; break; } gprs->status = NETWORK_REGISTRATION_STATUS_UNKNOWN; gprs->netreg_status = NETWORK_REGISTRATION_STATUS_UNKNOWN; gprs->pid_map = idmap_new(MAX_CONTEXTS); return gprs; } static void netreg_watch(struct ofono_atom *atom, enum ofono_atom_watch_condition cond, void *data) { struct ofono_gprs *gprs = data; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { gprs_netreg_removed(gprs); return; } gprs->netreg = __ofono_atom_get_data(atom); gprs->netreg_status = ofono_netreg_get_status(gprs->netreg); gprs->status_watch = __ofono_netreg_add_status_watch(gprs->netreg, netreg_status_changed, gprs, NULL); gprs_netreg_update(gprs); } static gboolean load_context(struct ofono_gprs *gprs, const char *group) { char *name = NULL; char *typestr = NULL; char *protostr = NULL; char *username = NULL; char *password = NULL; char *apn = NULL; char *msgproxy = NULL; char *msgcenter = NULL; char *authstr = NULL; gboolean ret = FALSE; gboolean legacy = FALSE; gboolean preferred; struct pri_context *context; enum ofono_gprs_context_type type; enum ofono_gprs_proto proto; enum ofono_gprs_auth_method auth; unsigned int id; if (sscanf(group, "context%d", &id) != 1) { if (sscanf(group, "primarycontext%d", &id) != 1) goto error; legacy = TRUE; } if (id < 1 || id > MAX_CONTEXTS) goto error; name = g_key_file_get_string(gprs->settings, group, "Name", NULL); if (name == NULL) goto error; typestr = g_key_file_get_string(gprs->settings, group, "Type", NULL); if (typestr == NULL) goto error; if (gprs_context_string_to_type(typestr, &type) == FALSE) goto error; protostr = g_key_file_get_string(gprs->settings, group, "Protocol", NULL); if (protostr == NULL) protostr = g_strdup("ip"); if (gprs_proto_from_string(protostr, &proto) == FALSE) goto error; preferred = g_key_file_get_boolean(gprs->settings, group, "Preferred", NULL); username = g_key_file_get_string(gprs->settings, group, "Username", NULL); if (username == NULL) goto error; if (strlen(username) > OFONO_GPRS_MAX_USERNAME_LENGTH) goto error; password = g_key_file_get_string(gprs->settings, group, "Password", NULL); if (password == NULL) goto error; authstr = g_key_file_get_string(gprs->settings, group, "AuthenticationMethod", NULL); if (authstr == NULL) authstr = g_strdup("chap"); if (gprs_auth_method_from_string(authstr, &auth) == FALSE) goto error; if (strlen(password) > OFONO_GPRS_MAX_PASSWORD_LENGTH) goto error; apn = g_key_file_get_string(gprs->settings, group, "AccessPointName", NULL); if (apn == NULL) goto error; if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH) goto error; if (type == OFONO_GPRS_CONTEXT_TYPE_MMS || type == OFONO_GPRS_CONTEXT_TYPE_INTERNET) { msgproxy = g_key_file_get_string(gprs->settings, group, "MessageProxy", NULL); msgcenter = g_key_file_get_string(gprs->settings, group, "MessageCenter", NULL); } /* * Accept empty (just created) APNs, but don't allow other * invalid ones */ if (apn[0] != '\0' && is_valid_apn(apn) == FALSE) goto error; context = pri_context_create(gprs, name, type); if (context == NULL) goto error; idmap_take(gprs->pid_map, id); context->id = id; strcpy(context->context.username, username); strcpy(context->context.password, password); strcpy(context->context.apn, apn); context->context.proto = proto; context->preferred = preferred; context->context.auth_method = auth; if (msgproxy != NULL) strcpy(context->message_proxy, msgproxy); if (msgcenter != NULL) strcpy(context->message_center, msgcenter); if (context_dbus_register(context) == FALSE) goto error; gprs->last_context_id = id; gprs->contexts = g_slist_append(gprs->contexts, context); ret = TRUE; if (legacy) { write_context_settings(gprs, context); g_key_file_remove_group(gprs->settings, group, NULL); } error: g_free(name); g_free(typestr); g_free(protostr); g_free(username); g_free(password); g_free(apn); g_free(msgproxy); g_free(msgcenter); g_free(authstr); return ret; } static void gprs_load_settings(struct ofono_gprs *gprs, const char *imsi) { GError *error; gboolean legacy = FALSE; char **groups; int i; gprs->settings = storage_open(imsi, SETTINGS_STORE); if (gprs->settings == NULL) return; gprs->imsi = g_strdup(imsi); error = NULL; gprs->powered = g_key_file_get_boolean(gprs->settings, SETTINGS_GROUP, "Powered", &error); /* * If any error occurs, simply switch to defaults. * Default to Powered = True * and RoamingAllowed = False */ if (error) { g_error_free(error); gprs->powered = TRUE; g_key_file_set_boolean(gprs->settings, SETTINGS_GROUP, "Powered", gprs->powered); } notify_powered_change(gprs); error = NULL; gprs->roaming_allowed = g_key_file_get_boolean(gprs->settings, SETTINGS_GROUP, "RoamingAllowed", &error); if (error) { g_error_free(error); gprs->roaming_allowed = FALSE; g_key_file_set_boolean(gprs->settings, SETTINGS_GROUP, "RoamingAllowed", gprs->roaming_allowed); } groups = g_key_file_get_groups(gprs->settings, NULL); for (i = 0; groups[i]; i++) { if (g_str_equal(groups[i], SETTINGS_GROUP)) continue; if (!g_str_has_prefix(groups[i], "context")) { if (!g_str_has_prefix(groups[i], "primarycontext")) goto remove; legacy = TRUE; } if (load_context(gprs, groups[i]) == TRUE) continue; remove: g_key_file_remove_group(gprs->settings, groups[i], NULL); } g_strfreev(groups); if (legacy) storage_sync(imsi, SETTINGS_STORE, gprs->settings); } static struct pri_context *gprs_context_for_ia(struct ofono_gprs *gprs) { GSList *l; struct pri_context *ctx_ia = NULL; struct pri_context *ctx_inet_pref = NULL; struct pri_context *ctx_inet = NULL; struct pri_context *ctx_other_pref = NULL; for (l = gprs->contexts; l; l = l->next) { struct pri_context *ctx = l->data; if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_IA) { if (ctx->preferred) return ctx; if (ctx_ia == NULL) ctx_ia = ctx; } else if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_INTERNET) { if (ctx->preferred && ctx_inet_pref == NULL) ctx_inet_pref = ctx; else if (ctx_inet == NULL) ctx_inet = ctx; } else if (ctx->preferred && ctx_other_pref == NULL) { ctx_other_pref = ctx; } } if (ctx_ia != NULL) { set_preferred(ctx_ia, ofono_dbus_get_connection(), TRUE); return ctx_ia; } if (ctx_inet_pref != NULL) return ctx_inet_pref; if (ctx_inet != NULL) return ctx_inet; if (ctx_other_pref != NULL) return ctx_other_pref; if (gprs->contexts == NULL) return NULL; return gprs->contexts->data; } static void set_ia_apn_cb(const struct ofono_error *error, void *data) { if (error->type != OFONO_ERROR_TYPE_NO_ERROR) ofono_error("Could not set IA APN"); } static void set_ia_apn(struct ofono_gprs *gprs) { struct pri_context *ctx = gprs_context_for_ia(gprs); struct ofono_gprs_primary_context *ofono_ctx; char mccmnc[OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1]; const char *mcc = ofono_sim_get_mcc(gprs->sim); const char *mnc = ofono_sim_get_mnc(gprs->sim); if (ctx == NULL) return; ofono_ctx = &ctx->context; strcpy(mccmnc, mcc); strcpy(mccmnc + strlen(mcc), mnc); gprs->driver->set_ia_apn(gprs, ofono_ctx->apn, ofono_ctx->proto, ofono_ctx->username, ofono_ctx->password, mccmnc, set_ia_apn_cb, gprs); } static void ofono_gprs_finish_register(struct ofono_gprs *gprs) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(gprs->atom); const char *path = __ofono_atom_get_path(gprs->atom); if (gprs->contexts == NULL) /* Automatic provisioning failed */ add_context(gprs, NULL, OFONO_GPRS_CONTEXT_TYPE_INTERNET); if (gprs->driver->set_ia_apn) set_ia_apn(gprs); if (!g_dbus_register_interface(conn, path, OFONO_CONNECTION_MANAGER_INTERFACE, manager_methods, manager_signals, NULL, gprs, NULL)) { ofono_error("Could not create %s interface", OFONO_CONNECTION_MANAGER_INTERFACE); free_contexts(gprs); return; } ofono_modem_add_interface(modem, OFONO_CONNECTION_MANAGER_INTERFACE); gprs->netreg_watch = __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_NETREG, netreg_watch, gprs, NULL); __ofono_atom_register(gprs->atom, gprs_unregister); } static void sim_gid1_read_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct ofono_gprs *gprs = userdata; if (ok) { gprs->gid1 = encode_hex(data, record_length, '\0'); } else { ofono_error("%s: error reading EF_GID1", __func__); gprs->gid1 = NULL; } provision_contexts(gprs, ofono_sim_get_mcc(gprs->sim), ofono_sim_get_mnc(gprs->sim), ofono_sim_get_spn(gprs->sim), gprs->gid1); ofono_gprs_finish_register(gprs); } static void spn_read_cb(const char *spn, const char *dc, void *data) { struct ofono_gprs *gprs = data; ofono_sim_remove_spn_watch(gprs->sim, &gprs->spn_watch); if (__ofono_sim_service_available(gprs->sim, SIM_UST_SERVICE_GROUP_ID_LEVEL_1, SIM_SST_SERVICE_GROUP_ID_LEVEL_1) == TRUE) { DBG("GID1 file available"); ofono_sim_read(gprs->sim_context, SIM_EFGID1_FILEID, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT, sim_gid1_read_cb, gprs); } else { provision_contexts(gprs, ofono_sim_get_mcc(gprs->sim), ofono_sim_get_mnc(gprs->sim), spn, NULL); ofono_gprs_finish_register(gprs); } } void ofono_gprs_register(struct ofono_gprs *gprs) { struct ofono_modem *modem = __ofono_atom_get_modem(gprs->atom); gprs->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem); if (gprs->sim == NULL) goto finish; gprs_load_settings(gprs, ofono_sim_get_imsi(gprs->sim)); if (gprs->contexts) goto finish; gprs->sim_context = ofono_sim_context_create(gprs->sim); ofono_sim_add_spn_watch(gprs->sim, &gprs->spn_watch, spn_read_cb, gprs, NULL); return; finish: ofono_gprs_finish_register(gprs); } void ofono_gprs_remove(struct ofono_gprs *gprs) { __ofono_atom_free(gprs->atom); } void ofono_gprs_set_data(struct ofono_gprs *gprs, void *data) { gprs->driver_data = data; } void *ofono_gprs_get_data(struct ofono_gprs *gprs) { return gprs->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/watch.c0000644000015600001650000000427212671500024020445 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include "ofono.h" struct ofono_watchlist *__ofono_watchlist_new(ofono_destroy_func destroy) { struct ofono_watchlist *watchlist; watchlist = g_new0(struct ofono_watchlist, 1); watchlist->destroy = destroy; return watchlist; } unsigned int __ofono_watchlist_add_item(struct ofono_watchlist *watchlist, struct ofono_watchlist_item *item) { item->id = ++watchlist->next_id; watchlist->items = g_slist_prepend(watchlist->items, item); return item->id; } gboolean __ofono_watchlist_remove_item(struct ofono_watchlist *watchlist, unsigned int id) { struct ofono_watchlist_item *item; GSList *p; GSList *c; p = NULL; c = watchlist->items; while (c) { item = c->data; if (item->id != id) { p = c; c = c->next; continue; } if (p) p->next = c->next; else watchlist->items = c->next; if (item->destroy) item->destroy(item->notify_data); if (watchlist->destroy) watchlist->destroy(item); g_slist_free_1(c); return TRUE; } return FALSE; } void __ofono_watchlist_free(struct ofono_watchlist *watchlist) { struct ofono_watchlist_item *item; GSList *l; for (l = watchlist->items; l; l = l->next) { item = l->data; if (item->destroy) item->destroy(item->notify_data); if (watchlist->destroy) watchlist->destroy(item); } g_slist_free(watchlist->items); watchlist->items = NULL; g_free(watchlist); } ofono-1.17.bzr6912+16.04.20160314.3/src/stk.c0000644000015600001650000023360712671500024020146 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "ofono.h" #include "common.h" #include "smsutil.h" #include "stkutil.h" #include "stkagent.h" #include "util.h" static GSList *g_drivers = NULL; struct stk_timer { time_t expiry; time_t start; }; struct ofono_stk { const struct ofono_stk_driver *driver; void *driver_data; struct ofono_atom *atom; struct stk_command *pending_cmd; void (*cancel_cmd)(struct ofono_stk *stk); GQueue *envelope_q; DBusMessage *pending; struct stk_timer timers[8]; guint timers_source; int timeout; int short_timeout; struct stk_agent *session_agent; struct stk_agent *default_agent; struct stk_agent *current_agent; /* Always equals one of the above */ struct stk_menu *main_menu, *select_item_menu; gboolean respond_on_exit; ofono_bool_t immediate_response; guint remove_agent_source; struct extern_req *extern_req; char *idle_mode_text; struct stk_icon_id idle_mode_icon; struct timeval get_inkey_start_ts; int dtmf_id; __ofono_sms_sim_download_cb_t sms_pp_cb; void *sms_pp_userdata; }; struct envelope_op { uint8_t tlv[256]; unsigned int tlv_len; int retries; void (*cb)(struct ofono_stk *stk, gboolean ok, const unsigned char *data, int length); }; struct extern_req { struct ofono_stk *stk; gboolean cancelled; }; #define ENVELOPE_RETRIES_DEFAULT 5 static void envelope_queue_run(struct ofono_stk *stk); static void timers_update(struct ofono_stk *stk); #define ADD_ERROR_RESULT(result, error, addn_info) \ result.type = error; \ result.additional_len = sizeof(addn_info); \ result.additional = addn_info; \ static gboolean convert_to_phone_number_format(const char *input_str, char *output_str) { char *digit; char *digit_from = "01234567890abcABC"; char *digit_to = "01234567890*#p*#p"; int pos; for (pos = 0; input_str[pos] != '\0'; pos++) { digit = strchr(digit_from, input_str[pos]); if (digit == NULL) return FALSE; output_str[pos] = digit_to[digit - digit_from]; } output_str[pos] = '\0'; return TRUE; } static int stk_respond(struct ofono_stk *stk, struct stk_response *rsp, ofono_stk_generic_cb_t cb) { const guint8 *tlv; unsigned int tlv_len; DBG(""); if (stk->driver->terminal_response == NULL) return -ENOSYS; rsp->src = STK_DEVICE_IDENTITY_TYPE_TERMINAL; rsp->dst = STK_DEVICE_IDENTITY_TYPE_UICC; rsp->number = stk->pending_cmd->number; rsp->type = stk->pending_cmd->type; rsp->qualifier = stk->pending_cmd->qualifier; tlv = stk_pdu_from_response(rsp, &tlv_len); if (tlv == NULL) return -EINVAL; stk_command_free(stk->pending_cmd); stk->pending_cmd = NULL; stk->cancel_cmd = NULL; stk->respond_on_exit = FALSE; stk->driver->terminal_response(stk, tlv_len, tlv, cb, stk); return 0; } static void stk_command_cb(const struct ofono_error *error, void *data) { if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("TERMINAL RESPONSE to a UICC command failed"); return; } DBG("TERMINAL RESPONSE to a command reported no errors"); } static void send_simple_response(struct ofono_stk *stk, enum stk_result_type result) { struct stk_response rsp; static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; DBG("result %d", result); memset(&rsp, 0, sizeof(rsp)); rsp.result.type = result; if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); } static void envelope_cb(const struct ofono_error *error, const uint8_t *data, int length, void *user_data) { struct ofono_stk *stk = user_data; struct envelope_op *op = g_queue_peek_head(stk->envelope_q); gboolean result = TRUE; DBG("length %d", length); if (op->retries > 0 && error->type == OFONO_ERROR_TYPE_SIM && error->error == 0x9300) { op->retries--; goto out; } if (error->type != OFONO_ERROR_TYPE_NO_ERROR) result = FALSE; g_queue_pop_head(stk->envelope_q); if (op->cb) op->cb(stk, result, data, length); g_free(op); out: envelope_queue_run(stk); } static void envelope_queue_run(struct ofono_stk *stk) { if (g_queue_get_length(stk->envelope_q) > 0) { struct envelope_op *op = g_queue_peek_head(stk->envelope_q); stk->driver->envelope(stk, op->tlv_len, op->tlv, envelope_cb, stk); } } static int stk_send_envelope(struct ofono_stk *stk, struct stk_envelope *e, void (*cb)(struct ofono_stk *stk, gboolean ok, const uint8_t *data, int length), int retries) { const uint8_t *tlv; unsigned int tlv_len; struct envelope_op *op; DBG(""); if (stk->driver->envelope == NULL) return -ENOSYS; e->dst = STK_DEVICE_IDENTITY_TYPE_UICC; tlv = stk_pdu_from_envelope(e, &tlv_len); if (tlv == NULL) return -EINVAL; op = g_new0(struct envelope_op, 1); op->cb = cb; op->retries = retries; memcpy(op->tlv, tlv, tlv_len); op->tlv_len = tlv_len; g_queue_push_tail(stk->envelope_q, op); if (g_queue_get_length(stk->envelope_q) == 1) envelope_queue_run(stk); return 0; } static void stk_cbs_download_cb(struct ofono_stk *stk, gboolean ok, const unsigned char *data, int len) { if (!ok) { ofono_error("CellBroadcast download to UICC failed"); return; } if (len) ofono_error("CellBroadcast download returned %i bytes of data", len); DBG("CellBroadcast download to UICC reported no error"); } void __ofono_cbs_sim_download(struct ofono_stk *stk, const struct cbs *msg) { struct stk_envelope e; int err; DBG(""); memset(&e, 0, sizeof(e)); e.type = STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD; e.src = STK_DEVICE_IDENTITY_TYPE_NETWORK; memcpy(&e.cbs_pp_download.page, msg, sizeof(*msg)); err = stk_send_envelope(stk, &e, stk_cbs_download_cb, ENVELOPE_RETRIES_DEFAULT); if (err) stk_cbs_download_cb(stk, FALSE, NULL, -1); } static void stk_sms_download_cb(struct ofono_stk *stk, gboolean ok, const unsigned char *data, int len) { DBG("SMS-PP download to UICC reported %s", ok ? "success" : "error"); if (stk->sms_pp_cb) stk->sms_pp_cb(ok, data, len, stk->sms_pp_userdata); } int __ofono_sms_sim_download(struct ofono_stk *stk, const struct sms *msg, __ofono_sms_sim_download_cb_t cb, void *data) { struct stk_envelope e; if (msg->type != SMS_TYPE_DELIVER) return -EINVAL; DBG(""); memset(&e, 0, sizeof(e)); e.type = STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD; e.src = STK_DEVICE_IDENTITY_TYPE_NETWORK; e.sms_pp_download.address.number = (char *) msg->sc_addr.address; e.sms_pp_download.address.ton_npi = msg->sc_addr.numbering_plan | (msg->sc_addr.number_type << 4); memcpy(&e.sms_pp_download.message, &msg->deliver, sizeof(msg->deliver)); stk->sms_pp_cb = cb; stk->sms_pp_userdata = data; return stk_send_envelope(stk, &e, stk_sms_download_cb, ENVELOPE_RETRIES_DEFAULT); } static char *dbus_apply_text_attributes(const char *text, const struct stk_text_attribute *attr) { uint16_t buf[256], *i = buf; const uint8_t *j = attr->attributes; const uint8_t *end = j + attr->len; if (text == NULL) return NULL; if (attr->len & 3) return NULL; while (j < end) *i++ = *j++; return stk_text_to_html(text, buf, attr->len / 4); } static struct stk_menu *stk_menu_create(const char *title, const struct stk_text_attribute *title_attr, const struct stk_icon_id *icon, GSList *items, const struct stk_item_text_attribute_list *item_attrs, const struct stk_item_icon_id_list *item_icon_ids, int default_id, gboolean soft_key, gboolean has_help) { unsigned int len = g_slist_length(items); struct stk_menu *ret; GSList *l; int i; struct stk_text_attribute attr; DBG(""); if (item_attrs && item_attrs->len && item_attrs->len != len * 4) return NULL; if (item_icon_ids && item_icon_ids->len && item_icon_ids->len != len) return NULL; ret = g_try_new(struct stk_menu, 1); if (ret == NULL) return NULL; ret->title = dbus_apply_text_attributes(title ? title : "", title_attr); if (ret->title == NULL) ret->title = g_strdup(title ? title : ""); memcpy(&ret->icon, icon, sizeof(ret->icon)); ret->items = g_new0(struct stk_menu_item, len + 1); ret->default_item = -1; ret->soft_key = soft_key; ret->has_help = has_help; for (l = items, i = 0; l; l = l->next, i++) { struct stk_item *item = l->data; char *text; ret->items[i].item_id = item->id; text = NULL; if (item_attrs && item_attrs->len) { memcpy(attr.attributes, &item_attrs->list[i * 4], 4); attr.len = 4; text = dbus_apply_text_attributes(item->text, &attr); } if (text == NULL) text = strdup(item->text); ret->items[i].text = text; if (item_icon_ids && item_icon_ids->len) ret->items[i].icon_id = item_icon_ids->list[i]; if (ret->items[i].icon_id != 0 && item_icon_ids->qualifier == STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY) ret->items[i].text[0]='\0'; if (item->id == default_id) ret->default_item = i; } return ret; } static struct stk_menu *stk_menu_create_from_set_up_menu( const struct stk_command *cmd) { gboolean soft_key = (cmd->qualifier & (1 << 0)) != 0; gboolean has_help = (cmd->qualifier & (1 << 7)) != 0; return stk_menu_create(cmd->setup_menu.alpha_id, &cmd->setup_menu.text_attr, &cmd->setup_menu.icon_id, cmd->setup_menu.items, &cmd->setup_menu.item_text_attr_list, &cmd->setup_menu.item_icon_id_list, 0, soft_key, has_help); } static struct stk_menu *stk_menu_create_from_select_item( const struct stk_command *cmd) { gboolean soft_key = (cmd->qualifier & (1 << 2)) != 0; gboolean has_help = (cmd->qualifier & (1 << 7)) != 0; return stk_menu_create(cmd->select_item.alpha_id, &cmd->select_item.text_attr, &cmd->select_item.icon_id, cmd->select_item.items, &cmd->select_item.item_text_attr_list, &cmd->select_item.item_icon_id_list, cmd->select_item.item_id, soft_key, has_help); } static void stk_menu_free(struct stk_menu *menu) { struct stk_menu_item *i; for (i = menu->items; i->text; i++) g_free(i->text); g_free(menu->items); g_free(menu->title); g_free(menu); } static void emit_menu_changed(struct ofono_stk *stk) { static struct stk_menu_item end_item = {}; static struct stk_menu no_menu = { .title = "", .items = &end_item, .has_help = FALSE, .default_item = -1, }; static char *name = "MainMenu"; DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(stk->atom); struct stk_menu *menu = stk->main_menu ? stk->main_menu : &no_menu; DBusMessage *signal; DBusMessageIter iter; ofono_dbus_signal_property_changed(conn, path, OFONO_STK_INTERFACE, "MainMenuTitle", DBUS_TYPE_STRING, &menu->title); ofono_dbus_signal_property_changed(conn, path, OFONO_STK_INTERFACE, "MainMenuIcon", DBUS_TYPE_BYTE, &menu->icon.id); signal = dbus_message_new_signal(path, OFONO_STK_INTERFACE, "PropertyChanged"); if (signal == NULL) { ofono_error("Unable to allocate new %s.PropertyChanged signal", OFONO_SIM_APP_INTERFACE); return; } dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); append_menu_items_variant(&iter, menu->items); g_dbus_send_message(conn, signal); } static void cancel_pending_dtmf(struct ofono_stk *stk) { struct ofono_voicecall *vc; vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, __ofono_atom_get_modem(stk->atom)); if (vc) /* Should be always true here */ __ofono_voicecall_tone_cancel(vc, stk->dtmf_id); } static void user_termination_cb(enum stk_agent_result result, void *user_data) { struct ofono_stk *stk = user_data; if (result != STK_AGENT_RESULT_TERMINATE) return; switch (stk->pending_cmd->type) { case STK_COMMAND_TYPE_SEND_DTMF: cancel_pending_dtmf(stk); break; } send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED); } static gboolean stk_alpha_id_set(struct ofono_stk *stk, const char *text, const struct stk_text_attribute *attr, const struct stk_icon_id *icon) { char *alpha = dbus_apply_text_attributes(text, attr); /* * Currently, we are treating null data object(len = 0, no value part) * and no alpha identifier cases equally. This may be changed once * better idea is found out. */ if (alpha == NULL) return FALSE; if (stk->current_agent == NULL) return FALSE; /* * According to 3GPP TS 102.223 section 8.31: * If icon is self-explanatory, it replaces the alpha identifier or * text string. * If icon is not self-explanatory, it shall be displayed together * with the alpha identifier or text string. */ if (icon->id != 0 && icon->qualifier == STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY) alpha[0]='\0'; if (stk->respond_on_exit) stk_agent_display_action(stk->current_agent, alpha, icon, user_termination_cb, stk, NULL); else stk_agent_display_action_info(stk->current_agent, alpha, icon); g_free(alpha); return TRUE; } static void stk_alpha_id_unset(struct ofono_stk *stk) { /* * If there is no default agent, then current agent also will be NULL. * So, call request cancel only when there is a valid current agent. */ if (stk->current_agent) stk_agent_request_cancel(stk->current_agent); } static int duration_to_msecs(const struct stk_duration *duration) { int msecs = duration->interval; switch (duration->unit) { case STK_DURATION_TYPE_MINUTES: msecs *= 60; /* Fall through. */ case STK_DURATION_TYPE_SECONDS: msecs *= 10; /* Fall through. */ case STK_DURATION_TYPE_SECOND_TENTHS: msecs *= 100; } return msecs; } static DBusMessage *stk_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_stk *stk = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; DBusMessageIter entry; const char *key = "MainMenu"; const char *str; unsigned char icon; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); str = stk->idle_mode_text ? stk->idle_mode_text : ""; ofono_dbus_dict_append(&dict, "IdleModeText", DBUS_TYPE_STRING, &str); icon = stk->idle_mode_icon.id; ofono_dbus_dict_append(&dict, "IdleModeIcon", DBUS_TYPE_BYTE, &icon); str = stk->main_menu ? stk->main_menu->title : ""; ofono_dbus_dict_append(&dict, "MainMenuTitle", DBUS_TYPE_STRING, &str); icon = stk->main_menu ? stk->main_menu->icon.id : 0; ofono_dbus_dict_append(&dict, "MainMenuIcon", DBUS_TYPE_BYTE, &icon); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); append_menu_items_variant(&entry, stk->main_menu ? stk->main_menu->items : NULL); dbus_message_iter_close_container(&dict, &entry); dbus_message_iter_close_container(&iter, &dict); return reply; } static void stk_request_cancel(struct ofono_stk *stk) { if (stk->session_agent) stk_agent_request_cancel(stk->session_agent); if (stk->default_agent) stk_agent_request_cancel(stk->default_agent); } static void default_agent_notify(gpointer user_data) { struct ofono_stk *stk = user_data; if (stk->current_agent == stk->default_agent && stk->respond_on_exit) { if (stk->pending_cmd) stk->cancel_cmd(stk); send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED); } stk->default_agent = NULL; stk->current_agent = stk->session_agent; } static void session_agent_notify(gpointer user_data) { struct ofono_stk *stk = user_data; DBG("Session Agent removed"); if (stk->current_agent == stk->session_agent && stk->respond_on_exit) { if (stk->pending_cmd) stk->cancel_cmd(stk); DBG("Sending Terminate response for session agent"); send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED); } stk->session_agent = NULL; stk->current_agent = stk->default_agent; if (stk->remove_agent_source) { g_source_remove(stk->remove_agent_source); stk->remove_agent_source = 0; } } static gboolean session_agent_remove_cb(gpointer user_data) { struct ofono_stk *stk = user_data; stk->remove_agent_source = 0; stk_agent_free(stk->session_agent); return FALSE; } /* Safely remove the agent even inside a callback */ static void session_agent_remove(struct ofono_stk *stk) { if (!stk->remove_agent_source) stk->remove_agent_source = g_timeout_add(0, session_agent_remove_cb, stk); } static DBusMessage *stk_register_agent(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_stk *stk = data; const char *agent_path; if (stk->default_agent) return __ofono_error_busy(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!__ofono_dbus_valid_object_path(agent_path)) return __ofono_error_invalid_format(msg); stk->default_agent = stk_agent_new(agent_path, dbus_message_get_sender(msg), FALSE); if (stk->default_agent == NULL) return __ofono_error_failed(msg); stk_agent_set_removed_notify(stk->default_agent, default_agent_notify, stk); if (stk->session_agent == NULL) stk->current_agent = stk->default_agent; return dbus_message_new_method_return(msg); } static DBusMessage *stk_unregister_agent(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_stk *stk = data; const char *agent_path; const char *agent_bus = dbus_message_get_sender(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (stk->default_agent == NULL) return __ofono_error_failed(msg); if (!stk_agent_matches(stk->default_agent, agent_path, agent_bus)) return __ofono_error_failed(msg); stk_agent_free(stk->default_agent); return dbus_message_new_method_return(msg); } static void menu_selection_envelope_cb(struct ofono_stk *stk, gboolean ok, const unsigned char *data, int len) { unsigned char selection; const char *agent_path; DBusMessage *reply; DBG(""); if (!ok) { ofono_error("Sending Menu Selection to UICC failed"); reply = __ofono_error_failed(stk->pending); goto out; } if (len) ofono_error("Menu Selection returned %i bytes of unwanted data", len); DBG("Menu Selection envelope submission gave no error"); dbus_message_get_args(stk->pending, NULL, DBUS_TYPE_BYTE, &selection, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID); stk->session_agent = stk_agent_new(agent_path, dbus_message_get_sender(stk->pending), TRUE); if (stk->session_agent == NULL) { reply = __ofono_error_failed(stk->pending); goto out; } stk_agent_set_removed_notify(stk->session_agent, session_agent_notify, stk); stk->current_agent = stk->session_agent; reply = dbus_message_new_method_return(stk->pending); out: __ofono_dbus_pending_reply(&stk->pending, reply); } static DBusMessage *stk_select_item(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_stk *stk = data; const char *agent_path; unsigned char selection, i; struct stk_envelope e; struct stk_menu *menu = stk->main_menu; DBG(""); if (stk->pending || stk->session_agent) return __ofono_error_busy(msg); if (menu == NULL) return __ofono_error_not_supported(msg); if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &selection, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_INVALID) == FALSE) return __ofono_error_invalid_args(msg); if (!__ofono_dbus_valid_object_path(agent_path)) return __ofono_error_invalid_format(msg); for (i = 0; i < selection && menu->items[i].text; i++); if (i != selection) return __ofono_error_invalid_format(msg); memset(&e, 0, sizeof(e)); e.type = STK_ENVELOPE_TYPE_MENU_SELECTION; e.src = STK_DEVICE_IDENTITY_TYPE_KEYPAD, e.menu_selection.item_id = menu->items[selection].item_id; e.menu_selection.help_request = FALSE; DBG(""); stk->pending = dbus_message_ref(msg); if (stk_send_envelope(stk, &e, menu_selection_envelope_cb, 0)) __ofono_dbus_pending_reply(&stk->pending, __ofono_error_failed(stk->pending)); return NULL; } static const GDBusMethodTable stk_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), stk_get_properties) }, { GDBUS_ASYNC_METHOD("SelectItem", GDBUS_ARGS({ "item", "y" }, { "agent", "o" }), NULL, stk_select_item) }, { GDBUS_METHOD("RegisterAgent", GDBUS_ARGS({ "path", "o" }), NULL, stk_register_agent) }, { GDBUS_METHOD("UnregisterAgent", GDBUS_ARGS({ "path", "o" }), NULL, stk_unregister_agent) }, { } }; static const GDBusSignalTable stk_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static gboolean handle_command_more_time(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { /* Do nothing */ return TRUE; } static void send_sms_cancel(struct ofono_stk *stk) { stk->extern_req->cancelled = TRUE; stk_alpha_id_unset(stk); } static void send_sms_submit_cb(gboolean ok, void *data) { struct extern_req *req = data; struct ofono_stk *stk = req->stk; struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE }; struct stk_response rsp; DBG("SMS submission %s", ok ? "successful" : "failed"); if (req->cancelled) { DBG("Received an SMS submitted callback after the " "proactive command was cancelled"); return; } stk_alpha_id_unset(stk); memset(&rsp, 0, sizeof(rsp)); if (ok == FALSE) rsp.result.type = STK_RESULT_TYPE_NETWORK_UNAVAILABLE; if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&failure, stk); } static void extern_req_start(struct ofono_stk *stk) { stk->extern_req = g_new0(struct extern_req, 1); stk->extern_req->stk = stk; } static gboolean handle_command_send_sms(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom); struct ofono_sms *sms; GSList msg_list; struct ofono_uuid uuid; sms = __ofono_atom_find(OFONO_ATOM_TYPE_SMS, modem); if (sms == NULL) { rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } extern_req_start(stk); msg_list.data = (void *) &cmd->send_sms.gsm_sms; msg_list.next = NULL; if (__ofono_sms_txq_submit(sms, &msg_list, 0, &uuid, NULL, NULL) < 0) { unsigned char no_cause_result[] = { 0x00 }; ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause_result); return TRUE; } __ofono_sms_txq_set_submit_notify(sms, &uuid, send_sms_submit_cb, stk->extern_req, g_free); stk->cancel_cmd = send_sms_cancel; stk_alpha_id_set(stk, cmd->send_sms.alpha_id, &cmd->send_sms.text_attr, &cmd->send_sms.icon_id); return FALSE; } /* Note: may be called from ofono_stk_proactive_command_handled_notify */ static gboolean handle_command_set_idle_text(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(stk->atom); const struct stk_command_setup_idle_mode_text *sim = &cmd->setup_idle_mode_text; char *idle_mode_text; idle_mode_text = dbus_apply_text_attributes(sim->text, &sim->text_attr); if (idle_mode_text == NULL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } if (stk->idle_mode_text) g_free(stk->idle_mode_text); if (sim->icon_id.id != 0 && sim->icon_id.qualifier == STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY) idle_mode_text[0]='\0'; stk->idle_mode_text = idle_mode_text; ofono_dbus_signal_property_changed(conn, path, OFONO_STK_INTERFACE, "IdleModeText", DBUS_TYPE_STRING, &idle_mode_text); if (stk->idle_mode_icon.id != sim->icon_id.id) { memcpy(&stk->idle_mode_icon, &sim->icon_id, sizeof(stk->idle_mode_icon)); ofono_dbus_signal_property_changed(conn, path, OFONO_STK_INTERFACE, "IdleModeIcon", DBUS_TYPE_BYTE, &stk->idle_mode_icon.id); } return TRUE; } static void timer_expiration_cb(struct ofono_stk *stk, gboolean ok, const unsigned char *data, int len) { if (!ok) { ofono_error("Timer Expiration reporting failed"); return; } if (len) ofono_error("Timer Expiration returned %i bytes of data", len); DBG("Timer Expiration reporting to UICC reported no error"); } static gboolean timers_cb(gpointer user_data) { struct ofono_stk *stk = user_data; stk->timers_source = 0; timers_update(stk); return FALSE; } static void timer_value_from_seconds(struct stk_timer_value *val, int seconds) { val->has_value = TRUE; val->hour = seconds / 3600; seconds -= val->hour * 3600; val->minute = seconds / 60; seconds -= val->minute * 60; val->second = seconds; } static void timers_update(struct ofono_stk *stk) { time_t min = 0, now = time(NULL); int i; if (stk->timers_source) { g_source_remove(stk->timers_source); stk->timers_source = 0; } for (i = 0; i < 8; i++) { if (!stk->timers[i].expiry) continue; if (stk->timers[i].expiry <= now) { struct stk_envelope e; int seconds = now - stk->timers[i].start; stk->timers[i].expiry = 0; memset(&e, 0, sizeof(e)); e.type = STK_ENVELOPE_TYPE_TIMER_EXPIRATION; e.src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, e.timer_expiration.id = i + 1; timer_value_from_seconds(&e.timer_expiration.value, seconds); /* * TODO: resubmit until success, providing current * time difference every time we re-send. */ if (stk_send_envelope(stk, &e, timer_expiration_cb, 0)) timer_expiration_cb(stk, FALSE, NULL, -1); continue; } if (stk->timers[i].expiry < now + min || min == 0) min = stk->timers[i].expiry - now; } if (min) stk->timers_source = g_timeout_add_seconds(min, timers_cb, stk); } static gboolean handle_command_timer_mgmt(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { int op = cmd->qualifier & 3; time_t seconds, now = time(NULL); struct stk_timer *tmr; if (cmd->timer_mgmt.timer_id < 1 || cmd->timer_mgmt.timer_id > 8) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } tmr = &stk->timers[cmd->timer_mgmt.timer_id - 1]; switch (op) { case 0: /* Start */ seconds = cmd->timer_mgmt.timer_value.second + cmd->timer_mgmt.timer_value.minute * 60 + cmd->timer_mgmt.timer_value.hour * 3600; tmr->expiry = now + seconds; tmr->start = now; timers_update(stk); break; case 1: /* Deactivate */ if (!tmr->expiry) { rsp->result.type = STK_RESULT_TYPE_TIMER_CONFLICT; return TRUE; } seconds = MAX(0, tmr->expiry - now); tmr->expiry = 0; timers_update(stk); timer_value_from_seconds(&rsp->timer_mgmt.value, seconds); break; case 2: /* Get current value */ if (!tmr->expiry) { rsp->result.type = STK_RESULT_TYPE_TIMER_CONFLICT; return TRUE; } seconds = MAX(0, tmr->expiry - now); timer_value_from_seconds(&rsp->timer_mgmt.value, seconds); break; default: rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } rsp->timer_mgmt.id = cmd->timer_mgmt.timer_id; return TRUE; } static gboolean handle_command_poll_interval(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom); int seconds; if (!cmd->poll_interval.duration.interval) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } seconds = MAX(duration_to_msecs(&cmd->poll_interval.duration) / 1000, 1); ofono_modem_set_integer(modem, "status-poll-interval", seconds); if (seconds > 255) { rsp->poll_interval.max_interval.unit = STK_DURATION_TYPE_MINUTES; rsp->poll_interval.max_interval.interval = seconds / 60; } else { rsp->poll_interval.max_interval.unit = STK_DURATION_TYPE_SECONDS; rsp->poll_interval.max_interval.interval = seconds; } return TRUE; } /* Note: may be called from ofono_stk_proactive_command_handled_notify */ static gboolean handle_command_set_up_menu(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { struct stk_menu *menu = NULL; if (cmd->setup_menu.items) { menu = stk_menu_create_from_set_up_menu(cmd); if (menu == NULL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } } if (menu == NULL && stk->main_menu == NULL) return TRUE; if (stk->main_menu) stk_menu_free(stk->main_menu); stk->main_menu = menu; emit_menu_changed(stk); return TRUE; } static void request_selection_destroy(void *user_data) { struct ofono_stk *stk = user_data; stk_menu_free(stk->select_item_menu); stk->select_item_menu = NULL; } static void request_selection_cb(enum stk_agent_result result, uint8_t id, void *user_data) { struct ofono_stk *stk = user_data; switch (result) { case STK_AGENT_RESULT_OK: { static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; struct stk_response rsp; memset(&rsp, 0, sizeof(rsp)); rsp.result.type = STK_RESULT_TYPE_SUCCESS; rsp.select_item.item_id = id; if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); break; } case STK_AGENT_RESULT_BACK: send_simple_response(stk, STK_RESULT_TYPE_GO_BACK); break; case STK_AGENT_RESULT_TIMEOUT: send_simple_response(stk, STK_RESULT_TYPE_NO_RESPONSE); break; case STK_AGENT_RESULT_TERMINATE: default: send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED); break; } } static gboolean handle_command_select_item(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { stk->select_item_menu = stk_menu_create_from_select_item(cmd); if (stk->select_item_menu == NULL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } /* We most likely got an out of memory error, tell SIM to retry */ if (stk_agent_request_selection(stk->current_agent, stk->select_item_menu, request_selection_cb, stk, request_selection_destroy, stk->short_timeout * 1000) < 0) { unsigned char no_cause_result[] = { 0x00 }; request_selection_destroy(stk); ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause_result); return TRUE; } stk->cancel_cmd = stk_request_cancel; stk->respond_on_exit = TRUE; return FALSE; } static void display_text_destroy(void *user_data) { struct ofono_stk *stk = user_data; stk->immediate_response = FALSE; } static void display_text_cb(enum stk_agent_result result, void *user_data) { struct ofono_stk *stk = user_data; gboolean confirm; struct stk_response rsp; static unsigned char screen_busy_result[] = { 0x01 }; static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; /* * There are four possible paths for DisplayText with immediate * response flag set: * 1. Agent drops off the bus. In that case regular removal * semantics apply and the agent is removed. * * 2. A new SIM command arrives. In this case the agent is * canceled and a new command is processed. This function is * not called in this case. * * 3. The session is ended by the SIM. This case is ignored, * and will result in either case 1, 2 or 4 occurring. * * 4. Agent reports an error or success. This function is called * with the result. * * NOTE: If the agent reports a TERMINATE result, the agent will * be removed. Since the response has been already sent, there * is no way to signal the end of session to the SIM. Hence * it is assumed that immediate response flagged commands will * only occur at the end of session. */ if (stk->immediate_response) { if (stk->session_agent) session_agent_remove(stk); return; } switch (result) { case STK_AGENT_RESULT_OK: send_simple_response(stk, STK_RESULT_TYPE_SUCCESS); break; case STK_AGENT_RESULT_BACK: send_simple_response(stk, STK_RESULT_TYPE_GO_BACK); break; case STK_AGENT_RESULT_TIMEOUT: confirm = (stk->pending_cmd->qualifier & (1 << 7)) != 0; send_simple_response(stk, confirm ? STK_RESULT_TYPE_NO_RESPONSE : STK_RESULT_TYPE_SUCCESS); break; case STK_AGENT_RESULT_BUSY: memset(&rsp, 0, sizeof(rsp)); ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_TERMINAL_BUSY, screen_busy_result); if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); break; case STK_AGENT_RESULT_TERMINATE: default: send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED); break; } } static gboolean handle_command_display_text(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { int timeout = stk->short_timeout * 1000; struct stk_command_display_text *dt = &stk->pending_cmd->display_text; uint8_t qualifier = stk->pending_cmd->qualifier; ofono_bool_t priority = (qualifier & (1 << 0)) != 0; char *text = dbus_apply_text_attributes(dt->text, &dt->text_attr); int err; if (text == NULL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } if (qualifier & (1 << 7)) timeout = stk->short_timeout * 1000; if (dt->duration.interval) timeout = duration_to_msecs(&dt->duration); if (cmd->display_text.immediate_response) timeout = stk->timeout * 1000; if (dt->icon_id.id != 0 && dt->icon_id.qualifier == STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY) text[0]='\0'; err = stk_agent_display_text(stk->current_agent, text, &dt->icon_id, priority, display_text_cb, stk, display_text_destroy, timeout); g_free(text); /* We most likely got an out of memory error, tell SIM to retry */ if (err < 0) { unsigned char no_cause_result[] = { 0x00 }; ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause_result); return TRUE; } if (cmd->display_text.immediate_response) stk->immediate_response = TRUE; DBG("Immediate Response: %d", stk->immediate_response); if (stk->immediate_response == FALSE) { stk->respond_on_exit = TRUE; stk->cancel_cmd = stk_request_cancel; } return stk->immediate_response; } static void set_get_inkey_duration(struct stk_duration *duration, struct timeval *start_ts) { struct timeval end_ts; int interval; gettimeofday(&end_ts, NULL); interval = (end_ts.tv_usec + 1099999 - start_ts->tv_usec) / 100000; interval += (end_ts.tv_sec - start_ts->tv_sec) * 10; interval -= 10; switch (duration->unit) { case STK_DURATION_TYPE_MINUTES: interval = (interval + 59) / 60; case STK_DURATION_TYPE_SECONDS: interval = (interval + 9) / 10; case STK_DURATION_TYPE_SECOND_TENTHS: break; } duration->interval = interval; } static void request_confirmation_cb(enum stk_agent_result result, gboolean confirm, void *user_data) { struct ofono_stk *stk = user_data; static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; struct stk_command_get_inkey *cmd = &stk->pending_cmd->get_inkey; struct stk_response rsp; switch (result) { case STK_AGENT_RESULT_OK: memset(&rsp, 0, sizeof(rsp)); rsp.result.type = STK_RESULT_TYPE_SUCCESS; rsp.get_inkey.text.text = confirm ? "" : NULL; rsp.get_inkey.text.yesno = TRUE; if (cmd->duration.interval) { rsp.get_inkey.duration.unit = cmd->duration.unit; set_get_inkey_duration(&rsp.get_inkey.duration, &stk->get_inkey_start_ts); } if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); break; case STK_AGENT_RESULT_BACK: send_simple_response(stk, STK_RESULT_TYPE_GO_BACK); break; case STK_AGENT_RESULT_TIMEOUT: memset(&rsp, 0, sizeof(rsp)); rsp.result.type = STK_RESULT_TYPE_NO_RESPONSE; if (cmd->duration.interval) { rsp.get_inkey.duration.unit = cmd->duration.unit; set_get_inkey_duration(&rsp.get_inkey.duration, &stk->get_inkey_start_ts); } if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); break; case STK_AGENT_RESULT_TERMINATE: default: send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED); break; } } static void request_key_cb(enum stk_agent_result result, char *string, void *user_data) { struct ofono_stk *stk = user_data; static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; struct stk_command_get_inkey *cmd = &stk->pending_cmd->get_inkey; struct stk_response rsp; switch (result) { case STK_AGENT_RESULT_OK: memset(&rsp, 0, sizeof(rsp)); rsp.result.type = STK_RESULT_TYPE_SUCCESS; rsp.get_inkey.text.text = string; if (cmd->duration.interval) { rsp.get_inkey.duration.unit = cmd->duration.unit; set_get_inkey_duration(&rsp.get_inkey.duration, &stk->get_inkey_start_ts); } if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); break; case STK_AGENT_RESULT_BACK: send_simple_response(stk, STK_RESULT_TYPE_GO_BACK); break; case STK_AGENT_RESULT_TIMEOUT: memset(&rsp, 0, sizeof(rsp)); rsp.result.type = STK_RESULT_TYPE_NO_RESPONSE; if (cmd->duration.interval) { rsp.get_inkey.duration.unit = cmd->duration.unit; set_get_inkey_duration(&rsp.get_inkey.duration, &stk->get_inkey_start_ts); } if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); break; case STK_AGENT_RESULT_TERMINATE: default: send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED); break; } } static gboolean handle_command_get_inkey(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { int timeout = stk->short_timeout * 1000; const struct stk_command_get_inkey *gi = &cmd->get_inkey; char *text = dbus_apply_text_attributes(gi->text, &gi->text_attr); uint8_t qualifier = stk->pending_cmd->qualifier; gboolean alphabet = (qualifier & (1 << 0)) != 0; gboolean ucs2 = (qualifier & (1 << 1)) != 0; gboolean yesno = (qualifier & (1 << 2)) != 0; gboolean immediate = (qualifier & (1 << 3)) != 0; /* Note: help parameter value is not provided by current api. */ int err; if (text == NULL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } if (gi->duration.interval) timeout = duration_to_msecs(&gi->duration); gettimeofday(&stk->get_inkey_start_ts, NULL); if (gi->icon_id.id != 0 && gi->icon_id.qualifier == STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY) text[0]='\0'; if (yesno) err = stk_agent_request_confirmation(stk->current_agent, text, &gi->icon_id, request_confirmation_cb, stk, NULL, timeout); else if (alphabet) err = stk_agent_request_key(stk->current_agent, text, &gi->icon_id, ucs2, request_key_cb, stk, NULL, timeout); else if (immediate) err = stk_agent_request_quick_digit(stk->current_agent, text, &gi->icon_id, request_key_cb, stk, NULL, timeout); else err = stk_agent_request_digit(stk->current_agent, text, &gi->icon_id, request_key_cb, stk, NULL, timeout); g_free(text); if (err < 0) { unsigned char no_cause_result[] = { 0x00 }; /* * We most likely got an out of memory error, tell SIM * to retry */ ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause_result); return TRUE; } stk->respond_on_exit = TRUE; stk->cancel_cmd = stk_request_cancel; return FALSE; } static void request_string_cb(enum stk_agent_result result, char *string, void *user_data) { struct ofono_stk *stk = user_data; static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; uint8_t qualifier = stk->pending_cmd->qualifier; gboolean packed = (qualifier & (1 << 3)) != 0; struct stk_response rsp; switch (result) { case STK_AGENT_RESULT_OK: memset(&rsp, 0, sizeof(rsp)); rsp.result.type = STK_RESULT_TYPE_SUCCESS; rsp.get_input.text.text = string; rsp.get_input.text.packed = packed; if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); break; case STK_AGENT_RESULT_BACK: send_simple_response(stk, STK_RESULT_TYPE_GO_BACK); break; case STK_AGENT_RESULT_TIMEOUT: send_simple_response(stk, STK_RESULT_TYPE_NO_RESPONSE); break; case STK_AGENT_RESULT_TERMINATE: default: send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED); break; } } static gboolean handle_command_get_input(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { int timeout = stk->timeout * 1000; const struct stk_command_get_input *gi = &cmd->get_input; char *text = dbus_apply_text_attributes(gi->text, &gi->text_attr); uint8_t qualifier = stk->pending_cmd->qualifier; gboolean alphabet = (qualifier & (1 << 0)) != 0; gboolean ucs2 = (qualifier & (1 << 1)) != 0; gboolean hidden = (qualifier & (1 << 2)) != 0; int err; if (text == NULL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } if (gi->icon_id.id != 0 && gi->icon_id.qualifier == STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY) text[0]='\0'; if (alphabet) err = stk_agent_request_input(stk->current_agent, text, &gi->icon_id, gi->default_text, ucs2, gi->resp_len.min, gi->resp_len.max, hidden, request_string_cb, stk, NULL, timeout); else err = stk_agent_request_digits(stk->current_agent, text, &gi->icon_id, gi->default_text, gi->resp_len.min, gi->resp_len.max, hidden, request_string_cb, stk, NULL, timeout); g_free(text); if (err < 0) { unsigned char no_cause_result[] = { 0x00 }; /* * We most likely got an out of memory error, tell SIM * to retry */ ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause_result); return TRUE; } stk->respond_on_exit = TRUE; stk->cancel_cmd = stk_request_cancel; return FALSE; } static void call_setup_connected(struct ofono_call *call, void *data) { struct ofono_stk *stk = data; struct stk_response rsp; static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; static unsigned char facility_rejected_result[] = { 0x9d }; if (call == NULL || call->status == CALL_STATUS_DISCONNECTED) { memset(&rsp, 0, sizeof(rsp)); ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_NETWORK_UNAVAILABLE, facility_rejected_result); if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); return; } if (call->status == CALL_STATUS_ACTIVE) send_simple_response(stk, STK_RESULT_TYPE_SUCCESS); else send_simple_response(stk, STK_RESULT_TYPE_USER_CANCEL); } static void call_setup_cancel(struct ofono_stk *stk) { struct ofono_voicecall *vc; vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, __ofono_atom_get_modem(stk->atom)); if (vc) __ofono_voicecall_dial_cancel(vc); } static void confirm_call_cb(enum stk_agent_result result, gboolean confirm, void *user_data) { struct ofono_stk *stk = user_data; static struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; const struct stk_command_setup_call *sc = &stk->pending_cmd->setup_call; uint8_t qualifier = stk->pending_cmd->qualifier; static unsigned char busy_on_call_result[] = { 0x02 }; static unsigned char no_cause_result[] = { 0x00 }; char *alpha_id = NULL; struct ofono_voicecall *vc; struct stk_response rsp; char number[256]; char *pause_chr; int err; switch (result) { case STK_AGENT_RESULT_TIMEOUT: confirm = FALSE; /* Fall through */ case STK_AGENT_RESULT_OK: if (confirm) break; send_simple_response(stk, STK_RESULT_TYPE_USER_REJECT); return; case STK_AGENT_RESULT_TERMINATE: default: send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED); return; } vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, __ofono_atom_get_modem(stk->atom)); if (vc == NULL) { send_simple_response(stk, STK_RESULT_TYPE_NOT_CAPABLE); return; } if (sc->alpha_id_call_setup) { alpha_id = dbus_apply_text_attributes(sc->alpha_id_call_setup, &sc->text_attr_call_setup); if (alpha_id == NULL) { send_simple_response(stk, STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD); return; } } /* Convert the setup call number to phone number format */ if (convert_to_phone_number_format(sc->addr.number, number) == FALSE) { send_simple_response(stk, STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD); return; } /* Remove the DTMF string from the phone number */ pause_chr = strchr(number, 'p'); if (pause_chr) number[pause_chr - number] = '\0'; /* TODO: send the DTMF after call is connected */ err = __ofono_voicecall_dial(vc, number, sc->addr.ton_npi, alpha_id, sc->icon_id_call_setup.id, qualifier >> 1, call_setup_connected, stk); g_free(alpha_id); if (err >= 0) { stk->cancel_cmd = call_setup_cancel; return; } if (err == -EBUSY) { memset(&rsp, 0, sizeof(rsp)); ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_TERMINAL_BUSY, busy_on_call_result); if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); return; } if (err == -ENOSYS) { send_simple_response(stk, STK_RESULT_TYPE_NOT_CAPABLE); return; } memset(&rsp, 0, sizeof(rsp)); ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_NETWORK_UNAVAILABLE, no_cause_result); if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&error, stk); } static void confirm_handled_call_cb(enum stk_agent_result result, gboolean confirm, void *user_data) { struct ofono_stk *stk = user_data; const struct stk_command_setup_call *sc = &stk->pending_cmd->setup_call; struct ofono_voicecall *vc; char number[256]; char *pause_chr; if (stk->driver->user_confirmation == NULL) goto out; if (result != STK_AGENT_RESULT_OK) { stk->driver->user_confirmation(stk, FALSE); goto out; } if (convert_to_phone_number_format(sc->addr.number, number) == FALSE) { stk->driver->user_confirmation(stk, FALSE); goto out; } /* Remove the DTMF string from the phone number */ pause_chr = strchr(number, 'p'); if (pause_chr) number[pause_chr - number] = '\0'; stk->driver->user_confirmation(stk, confirm); vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, __ofono_atom_get_modem(stk->atom)); if (vc == NULL) goto out; __ofono_voicecall_set_alpha_and_icon_id(vc, number, sc->addr.ton_npi, sc->alpha_id_call_setup, sc->icon_id_call_setup.id); return; out: stk_command_free(stk->pending_cmd); stk->pending_cmd = NULL; } static gboolean handle_command_set_up_call(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { const struct stk_command_setup_call *sc = &cmd->setup_call; uint8_t qualifier = cmd->qualifier; static unsigned char busy_on_call_result[] = { 0x02 }; char *alpha_id = NULL; struct ofono_voicecall *vc = NULL; int err; if (qualifier > 5) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } /* * Passing called party subaddress and establishing non-speech * calls are not supported. */ if (sc->ccp.len || sc->subaddr.len) { rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, __ofono_atom_get_modem(stk->atom)); if (vc == NULL) { rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } if (__ofono_voicecall_is_busy(vc, qualifier >> 1)) { ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, busy_on_call_result); return TRUE; } alpha_id = dbus_apply_text_attributes(sc->alpha_id_usr_cfm ? sc->alpha_id_usr_cfm : "", &sc->text_attr_usr_cfm); if (alpha_id == NULL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } err = stk_agent_confirm_call(stk->current_agent, alpha_id, &sc->icon_id_usr_cfm, confirm_call_cb, stk, NULL, stk->timeout * 1000); g_free(alpha_id); if (err < 0) { unsigned char no_cause_result[] = { 0x00 }; /* * We most likely got an out of memory error, tell SIM * to retry */ ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause_result); return TRUE; } stk->respond_on_exit = TRUE; stk->cancel_cmd = stk_request_cancel; return FALSE; } static void send_ussd_cancel(struct ofono_stk *stk) { struct ofono_ussd *ussd; ussd = __ofono_atom_find(OFONO_ATOM_TYPE_USSD, __ofono_atom_get_modem(stk->atom)); if (ussd) __ofono_ussd_initiate_cancel(ussd); stk_alpha_id_unset(stk); } static void send_ussd_callback(int error, int dcs, const unsigned char *msg, int msg_len, void *userdata) { struct ofono_stk *stk = userdata; struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE }; struct stk_response rsp; enum sms_charset charset; unsigned char no_cause[] = { 0x00 }; stk_alpha_id_unset(stk); memset(&rsp, 0, sizeof(rsp)); switch (error) { case 0: if (cbs_dcs_decode(dcs, NULL, NULL, &charset, NULL, NULL, NULL)) { if (charset == SMS_CHARSET_7BIT) rsp.send_ussd.text.dcs = 0x00; else if (charset == SMS_CHARSET_8BIT) rsp.send_ussd.text.dcs = 0x04; else if (charset == SMS_CHARSET_UCS2) rsp.send_ussd.text.dcs = 0x08; rsp.result.type = STK_RESULT_TYPE_SUCCESS; rsp.send_ussd.text.text = msg; rsp.send_ussd.text.len = msg_len; rsp.send_ussd.text.has_text = TRUE; } else rsp.result.type = STK_RESULT_TYPE_USSD_RETURN_ERROR; if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&failure, stk); break; case -ECANCELED: send_simple_response(stk, STK_RESULT_TYPE_USSD_OR_SS_USER_TERMINATION); break; case -ETIMEDOUT: send_simple_response(stk, STK_RESULT_TYPE_NETWORK_UNAVAILABLE); break; default: ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_USSD_RETURN_ERROR, no_cause); if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&failure, stk); break; } } static gboolean ss_is_busy(struct ofono_modem *modem) { struct ofono_atom *atom; atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_CALL_FORWARDING); if (atom != NULL) { struct ofono_call_forwarding *cf = __ofono_atom_get_data(atom); if (__ofono_call_forwarding_is_busy(cf)) return TRUE; } atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_CALL_BARRING); if (atom != NULL) { struct ofono_call_barring *cb = __ofono_atom_get_data(atom); if (__ofono_call_barring_is_busy(cb)) return TRUE; } atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_CALL_SETTINGS); if (atom != NULL) { struct ofono_call_settings *cs = __ofono_atom_get_data(atom); if (__ofono_call_settings_is_busy(cs)) return TRUE; } return FALSE; } static gboolean handle_command_send_ussd(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom); static unsigned char busy_on_ss_result[] = { 0x03 }; static unsigned char busy_on_ussd_result[] = { 0x08 }; struct ofono_ussd *ussd; int err; ussd = __ofono_atom_find(OFONO_ATOM_TYPE_USSD, modem); if (ussd == NULL) { rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } if (__ofono_ussd_is_busy(ussd)) { ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, busy_on_ussd_result); return TRUE; } if (ss_is_busy(modem)) { ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, busy_on_ss_result); return TRUE; } err = __ofono_ussd_initiate(ussd, cmd->send_ussd.ussd_string.dcs, cmd->send_ussd.ussd_string.string, cmd->send_ussd.ussd_string.len, send_ussd_callback, stk); if (err == -ENOSYS) { rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } if (err == -EBUSY) { ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, busy_on_ussd_result); return TRUE; } if (err < 0) { unsigned char no_cause_result[] = { 0x00 }; /* * We most likely got an out of memory error, tell SIM * to retry */ ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause_result); return TRUE; } stk->cancel_cmd = send_ussd_cancel; stk_alpha_id_set(stk, cmd->send_ussd.alpha_id, &cmd->send_ussd.text_attr, &cmd->send_ussd.icon_id); return FALSE; } static void free_idle_mode_text(struct ofono_stk *stk) { g_free(stk->idle_mode_text); stk->idle_mode_text = NULL; memset(&stk->idle_mode_icon, 0, sizeof(stk->idle_mode_icon)); } /* Note: may be called from ofono_stk_proactive_command_handled_notify */ static gboolean handle_command_refresh(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE }; struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom); struct ofono_sim *sim; uint8_t addnl_info[1]; int err; GSList *l; DBG(""); switch (cmd->qualifier) { case 0: DBG("NAA Initialization and " "Full File Change Notification"); break; case 1: DBG("File Change Notification"); break; case 2: DBG("NAA Initialization and File Change Notification"); break; case 3: DBG("NAA Initialization"); break; case 4: DBG("UICC Reset"); break; case 5: DBG("NAA Application Reset"); break; case 6: DBG("NAA Session Reset"); break; default: ofono_info("Undefined Refresh qualifier: %d", cmd->qualifier); if (rsp != NULL) rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } DBG("Files:"); for (l = cmd->refresh.file_list; l; l = l->next) { struct stk_file *file = l->data; char buf[17]; encode_hex_own_buf(file->file, file->len, 0, buf); DBG("%s", buf); } DBG("Icon: %d, qualifier: %d", cmd->refresh.icon_id.id, cmd->refresh.icon_id.qualifier); DBG("Alpha ID: %s", cmd->refresh.alpha_id); sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem); if (sim == NULL) { if (rsp != NULL) rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } if (rsp != NULL) { struct ofono_ussd *ussd; struct ofono_voicecall *vc; ussd = __ofono_atom_find(OFONO_ATOM_TYPE_USSD, modem); if (ussd && __ofono_ussd_is_busy(ussd)) { addnl_info[0] = STK_RESULT_ADDNL_ME_PB_USSD_BUSY; ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, addnl_info); return TRUE; } vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, modem); if (vc && __ofono_voicecall_is_busy(vc, OFONO_VOICECALL_INTERACTION_NONE)) { addnl_info[0] = STK_RESULT_ADDNL_ME_PB_BUSY_ON_CALL; ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, addnl_info); return TRUE; } if (ss_is_busy(__ofono_atom_get_modem(stk->atom))) { addnl_info[0] = STK_RESULT_ADDNL_ME_PB_SS_BUSY; ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, addnl_info); return TRUE; } } /* * For now we can handle the Refresh types that don't require * a SIM reset except if that part of the task has been already * handled by modem firmware (indicated by rsp == NULL) in which * case we just restart our SIM initialisation. */ if (cmd->qualifier < 4 || rsp == NULL) { int qualifier = stk->pending_cmd->qualifier; GSList *file_list = stk->pending_cmd->refresh.file_list; /* Don't free the list yet */ stk->pending_cmd->refresh.file_list = NULL; /* * Queue the TERMINAL RESPONSE before triggering potential * file accesses. * * TODO: Find out if we need to send the "Refresh performed * with additional EFs read" response. */ if (rsp != NULL) { err = stk_respond(stk, rsp, stk_command_cb); if (err) stk_command_cb(&failure, stk); } /* TODO: use the alphaId / icon */ /* TODO: if AID is supplied, check its value */ /* TODO: possibly check if a D-bus call is pending or * an STK session ongoing. */ /* TODO: free some elements of the atom state */ switch (qualifier) { case 0: free_idle_mode_text(stk); __ofono_sim_refresh(sim, file_list, TRUE, TRUE); break; case 1: __ofono_sim_refresh(sim, file_list, FALSE, FALSE); break; case 2: case 3: case 4: case 5: case 6: free_idle_mode_text(stk); __ofono_sim_refresh(sim, file_list, FALSE, TRUE); break; } g_slist_foreach(file_list, (GFunc) g_free, NULL); g_slist_free(file_list); return FALSE; } rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } static void get_time(struct stk_response *rsp) { time_t now; struct tm *t; time(&now); t = localtime(&now); rsp->result.type = STK_RESULT_TYPE_SUCCESS; if (t->tm_year > 100) rsp->provide_local_info.datetime.year = t->tm_year - 100; else rsp->provide_local_info.datetime.year = t->tm_year; rsp->provide_local_info.datetime.month = t->tm_mon + 1; rsp->provide_local_info.datetime.day = t->tm_mday; rsp->provide_local_info.datetime.hour = t->tm_hour; rsp->provide_local_info.datetime.minute = t->tm_min; rsp->provide_local_info.datetime.second = t->tm_sec; rsp->provide_local_info.datetime.timezone = t->tm_gmtoff / 900; rsp->provide_local_info.datetime.has_timezone = TRUE; return; } static void get_lang(struct stk_response *rsp, struct ofono_stk *stk) { char *l; char lang[3]; struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE }; l = getenv("LANG"); if (l == NULL) { l = "en"; ofono_warn("LANG environment variable not set" " - defaulting to en"); } memcpy(lang, l, 2); lang[2] = '\0'; rsp->result.type = STK_RESULT_TYPE_SUCCESS; rsp->provide_local_info.language = lang; if (stk_respond(stk, rsp, stk_command_cb)) stk_command_cb(&failure, stk); } static gboolean handle_command_provide_local_info(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { switch (cmd->qualifier) { case 0: DBG("Unhandled - Local information according to current NAA"); break; case 1: DBG("Unhandled - IMEI"); break; case 2: DBG("Unhandled - Network measurement results"); break; case 3: DBG("Date, time and time zone"); get_time(rsp); return TRUE; case 4: DBG("Language setting"); get_lang(rsp, stk); return FALSE; case 6: DBG("Unhandled - Access Technology"); break; case 7: DBG("Unhandled - ESN of the terminal"); break; case 8: DBG("Unhandled - IMEISV of the terminal"); break; case 9: DBG("Unhandled - Search mode"); break; case 10: DBG("Unhandled - Charge state of Battery"); break; case 11: DBG("Unhandled - MEID"); break; case 13: DBG("Unhandled - Broadcast Network information"); break; case 14: DBG("Unhandled - Multiple Access Technologies"); break; case 15: DBG("Unhandled - Location information for multiple access" " technologies"); break; case 16: DBG("Unhandled - Measurement results for multiple access" " technologies"); break; default: ofono_info("Unsupported Provide Local Info qualifier: %d", cmd->qualifier); break; } rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } static void send_dtmf_cancel(struct ofono_stk *stk) { cancel_pending_dtmf(stk); stk_alpha_id_unset(stk); } static void dtmf_sent_cb(int error, void *user_data) { struct ofono_stk *stk = user_data; stk_alpha_id_unset(stk); if (error == ENOENT) { struct stk_response rsp; static unsigned char not_in_speech_call_result[] = { 0x07 }; static struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE }; memset(&rsp, 0, sizeof(rsp)); ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_TERMINAL_BUSY, not_in_speech_call_result); if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&failure, stk); return; } if (error != 0) send_simple_response(stk, STK_RESULT_TYPE_NOT_CAPABLE); else send_simple_response(stk, STK_RESULT_TYPE_SUCCESS); } static gboolean handle_command_send_dtmf(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { static unsigned char not_in_speech_call_result[] = { 0x07 }; struct ofono_voicecall *vc = NULL; char dtmf[256]; int err; vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, __ofono_atom_get_modem(stk->atom)); if (vc == NULL) { rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } /* Convert the DTMF string to phone number format */ if (convert_to_phone_number_format(cmd->send_dtmf.dtmf, dtmf) == FALSE) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } err = __ofono_voicecall_tone_send(vc, dtmf, dtmf_sent_cb, stk); if (err == -ENOSYS) { rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE; return TRUE; } if (err == -ENOENT) { ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, not_in_speech_call_result); return TRUE; } if (err == -EINVAL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } if (err < 0) { unsigned char no_cause_result[] = { 0x00 }; /* * We most likely got an out of memory error, tell SIM * to retry */ ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause_result); return TRUE; } /* * Note that we don't strictly require an agent to be connected, * but to comply with 6.4.24 we need to send a End Session when * the user decides so. */ stk->respond_on_exit = TRUE; stk->cancel_cmd = send_dtmf_cancel; stk->dtmf_id = err; stk_alpha_id_set(stk, cmd->send_dtmf.alpha_id, &cmd->send_dtmf.text_attr, &cmd->send_dtmf.icon_id); return FALSE; } static void play_tone_cb(enum stk_agent_result result, void *user_data) { struct ofono_stk *stk = user_data; switch (result) { case STK_AGENT_RESULT_OK: case STK_AGENT_RESULT_TIMEOUT: send_simple_response(stk, STK_RESULT_TYPE_SUCCESS); break; default: send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED); break; } } static gboolean handle_command_play_tone(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { static int manufacturer_timeout = 10000; /* 10 seconds */ static const struct { const char *name; /* Continuous true/false according to 02.40 */ gboolean repeatable; } tone_infos[] = { /* Default */ [0x00] = { "general-beep", FALSE }, /* Standard */ [0x01] = { "dial-tone", TRUE }, [0x02] = { "busy", TRUE }, [0x03] = { "congestion", TRUE }, [0x04] = { "radio-path-acknowledge", FALSE }, [0x05] = { "radio-path-not-available", TRUE }, [0x06] = { "error", TRUE }, [0x07] = { "call-waiting", TRUE }, [0x08] = { "ringing-tone", TRUE }, /* Proprietary */ [0x10] = { "general-beep", FALSE }, [0x11] = { "positive-acknowledgement", FALSE }, [0x12] = { "negative-acknowledgement", FALSE }, [0x13] = { "user-ringing-tone", TRUE }, [0x14] = { "user-sms-alert", FALSE }, [0x15] = { "critical", FALSE }, [0x20] = { "vibrate", TRUE }, /* Themed */ [0x30] = { "happy", FALSE }, [0x31] = { "sad", FALSE }, [0x32] = { "urgent-action", FALSE }, [0x33] = { "question", FALSE }, [0x34] = { "message-received", FALSE }, /* Melody */ [0x40] = { "melody-1", FALSE }, [0x41] = { "melody-2", FALSE }, [0x42] = { "melody-3", FALSE }, [0x43] = { "melody-4", FALSE }, [0x44] = { "melody-5", FALSE }, [0x45] = { "melody-6", FALSE }, [0x46] = { "melody-7", FALSE }, [0x47] = { "melody-8", FALSE }, }; const struct stk_command_play_tone *pt = &cmd->play_tone; uint8_t qualifier = stk->pending_cmd->qualifier; gboolean vibrate = (qualifier & (1 << 0)) != 0; char *text; int timeout; int err; if (pt->tone > sizeof(tone_infos) / sizeof(*tone_infos) || tone_infos[pt->tone].name == NULL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } text = dbus_apply_text_attributes(pt->alpha_id ? pt->alpha_id : "", &pt->text_attr); if (text == NULL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } if (pt->duration.interval) timeout = duration_to_msecs(&pt->duration); else timeout = manufacturer_timeout; if (pt->icon_id.id != 0 && pt->icon_id.qualifier == STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY) text[0]='\0'; /* * According to TS 102.223 section 6.6.6: * "the length of time for which the Terminal shall generate the tone, * if the tone is contunious or repeatable. For single tones, the * value of this data object shall be ignored by the Terminal. If no * duration is specified, the Terminal shall default to a duration * determined by the Terminal manufacturer */ if (!tone_infos[pt->tone].repeatable) /* Duration ignored */ err = stk_agent_play_tone(stk->current_agent, text, &pt->icon_id, vibrate, tone_infos[pt->tone].name, play_tone_cb, stk, NULL, stk->timeout * 1000); else err = stk_agent_loop_tone(stk->current_agent, text, &pt->icon_id, vibrate, tone_infos[pt->tone].name, play_tone_cb, stk, NULL, timeout); g_free(text); if (err < 0) { unsigned char no_cause_result[] = { 0x00 }; /* * We most likely got an out of memory error, tell SIM * to retry */ ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause_result); return TRUE; } stk->respond_on_exit = TRUE; stk->cancel_cmd = stk_request_cancel; return FALSE; } static void confirm_launch_browser_cb(enum stk_agent_result result, gboolean confirm, void *user_data) { struct ofono_stk *stk = user_data; unsigned char no_cause[] = { 0x00 }; struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE }; struct stk_response rsp; switch (result) { case STK_AGENT_RESULT_TIMEOUT: confirm = FALSE; /* Fall through */ case STK_AGENT_RESULT_OK: if (confirm) break; /* Fall through */ default: memset(&rsp, 0, sizeof(rsp)); ADD_ERROR_RESULT(rsp.result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause); if (stk_respond(stk, &rsp, stk_command_cb)) stk_command_cb(&failure, stk); return; } send_simple_response(stk, STK_RESULT_TYPE_SUCCESS); } static gboolean handle_command_launch_browser(const struct stk_command *cmd, struct stk_response *rsp, struct ofono_stk *stk) { const struct stk_command_launch_browser *lb = &cmd->launch_browser; char *alpha_id; int err; alpha_id = dbus_apply_text_attributes(lb->alpha_id ? lb->alpha_id : "", &lb->text_attr); if (alpha_id == NULL) { rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD; return TRUE; } err = stk_agent_confirm_launch_browser(stk->current_agent, alpha_id, lb->icon_id.id, lb->url, confirm_launch_browser_cb, stk, NULL, stk->timeout * 1000); g_free(alpha_id); if (err < 0) { unsigned char no_cause_result[] = { 0x00 }; /* * We most likely got an out of memory error, tell SIM * to retry */ ADD_ERROR_RESULT(rsp->result, STK_RESULT_TYPE_TERMINAL_BUSY, no_cause_result); return TRUE; } stk->respond_on_exit = TRUE; stk->cancel_cmd = stk_request_cancel; return FALSE; } static void setup_call_handled_cancel(struct ofono_stk *stk) { struct ofono_voicecall *vc; vc = __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, __ofono_atom_get_modem(stk->atom)); if (vc != NULL) __ofono_voicecall_clear_alpha_and_icon_id(vc); } static gboolean handle_setup_call_confirmation_req(struct stk_command *cmd, struct ofono_stk *stk) { const struct stk_command_setup_call *sc = &cmd->setup_call; int err; char *alpha_id = dbus_apply_text_attributes( sc->alpha_id_usr_cfm ? sc->alpha_id_usr_cfm : "", &sc->text_attr_usr_cfm); if (alpha_id == NULL) goto out; if (stk->current_agent == FALSE) goto out; err = stk_agent_confirm_call(stk->current_agent, alpha_id, &sc->icon_id_usr_cfm, confirm_handled_call_cb, stk, NULL, stk->timeout * 1000); g_free(alpha_id); if (err < 0) goto out; stk->cancel_cmd = setup_call_handled_cancel; return TRUE; out: if (stk->driver->user_confirmation) stk->driver->user_confirmation(stk, FALSE); return FALSE; } static void stk_proactive_command_cancel(struct ofono_stk *stk) { if (stk->immediate_response) stk_request_cancel(stk); if (stk->pending_cmd) { stk->cancel_cmd(stk); stk_command_free(stk->pending_cmd); stk->pending_cmd = NULL; stk->cancel_cmd = NULL; stk->respond_on_exit = FALSE; } } void ofono_stk_proactive_session_end_notify(struct ofono_stk *stk) { /* Wait until we receive the next command */ if (stk->immediate_response) return; stk_proactive_command_cancel(stk); if (stk->session_agent) stk_agent_free(stk->session_agent); } void ofono_stk_proactive_command_notify(struct ofono_stk *stk, int length, const unsigned char *pdu) { struct ofono_error error = { .type = OFONO_ERROR_TYPE_FAILURE }; struct stk_response rsp; int err; gboolean respond = TRUE; /* * Depending on the hardware we may have received a new * command before we managed to send a TERMINAL RESPONSE to * the previous one. 3GPP says in the current revision only * one command can be executing at any time, so assume that * the previous one is being cancelled and the card just * expects a response to the new one. */ stk_proactive_command_cancel(stk); stk->pending_cmd = stk_command_new_from_pdu(pdu, length); if (stk->pending_cmd == NULL) { ofono_error("Can't parse proactive command"); /* * Nothing we can do, we'd need at least Command Details * to be able to respond with an error. */ return; } switch (stk->pending_cmd->status) { case STK_PARSE_RESULT_OK: break; case STK_PARSE_RESULT_MISSING_VALUE: send_simple_response(stk, STK_RESULT_TYPE_MINIMUM_NOT_MET); return; case STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD: send_simple_response(stk, STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD); return; case STK_PARSE_RESULT_TYPE_NOT_UNDERSTOOD: default: send_simple_response(stk, STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD); return; } /* * In case no agent is registered, we should reject commands destined * to the Agent with a NOT_CAPABLE error. */ if (stk->current_agent == NULL) { switch (stk->pending_cmd->type) { case STK_COMMAND_TYPE_SELECT_ITEM: case STK_COMMAND_TYPE_DISPLAY_TEXT: case STK_COMMAND_TYPE_GET_INKEY: case STK_COMMAND_TYPE_GET_INPUT: case STK_COMMAND_TYPE_PLAY_TONE: case STK_COMMAND_TYPE_SETUP_CALL: case STK_COMMAND_TYPE_SEND_SMS: case STK_COMMAND_TYPE_SEND_USSD: case STK_COMMAND_TYPE_SEND_DTMF: send_simple_response(stk, STK_RESULT_TYPE_NOT_CAPABLE); return; default: break; } } memset(&rsp, 0, sizeof(rsp)); switch (stk->pending_cmd->type) { case STK_COMMAND_TYPE_MORE_TIME: respond = handle_command_more_time(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_SEND_SMS: respond = handle_command_send_sms(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT: respond = handle_command_set_idle_text(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_TIMER_MANAGEMENT: respond = handle_command_timer_mgmt(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_POLL_INTERVAL: respond = handle_command_poll_interval(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_SETUP_MENU: respond = handle_command_set_up_menu(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_SELECT_ITEM: respond = handle_command_select_item(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_DISPLAY_TEXT: respond = handle_command_display_text(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_GET_INKEY: respond = handle_command_get_inkey(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_GET_INPUT: respond = handle_command_get_input(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_SETUP_CALL: respond = handle_command_set_up_call(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_SEND_USSD: respond = handle_command_send_ussd(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION: /* * If any clients are interested, then the ISO639 * 2-letter codes has to be convered to language strings. * Converted language strings has to be added to the * property list. */ ofono_info("Language Code: %s", stk->pending_cmd->language_notification.language); break; case STK_COMMAND_TYPE_REFRESH: respond = handle_command_refresh(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO: respond = handle_command_provide_local_info(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_SEND_DTMF: respond = handle_command_send_dtmf(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_PLAY_TONE: respond = handle_command_play_tone(stk->pending_cmd, &rsp, stk); break; case STK_COMMAND_TYPE_LAUNCH_BROWSER: respond = handle_command_launch_browser(stk->pending_cmd, &rsp, stk); break; default: rsp.result.type = STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD; break; } if (respond == FALSE) return; err = stk_respond(stk, &rsp, stk_command_cb); if (err) stk_command_cb(&error, stk); } static gboolean handled_alpha_id_set(struct ofono_stk *stk, const char *text, const struct stk_text_attribute *attr, const struct stk_icon_id *icon) { if (stk_alpha_id_set(stk, text, attr, icon) == FALSE) return FALSE; stk->cancel_cmd = stk_alpha_id_unset; return TRUE; } void ofono_stk_proactive_command_handled_notify(struct ofono_stk *stk, int length, const unsigned char *pdu) { struct stk_response dummyrsp; gboolean ok = FALSE; /* * Modems send us the proactive command details and terminal responses * sent by the modem as a response to the command. Terminal responses * start with the Command Details CTLV tag (0x81). We filter terminal * responses here */ if (length > 0 && pdu[0] == 0x81) { stk_proactive_command_cancel(stk); return; } stk_proactive_command_cancel(stk); stk->pending_cmd = stk_command_new_from_pdu(pdu, length); if (stk->pending_cmd == NULL) return; if (stk->pending_cmd->status != STK_PARSE_RESULT_OK) { ofono_error("Can't parse modem-handled proactive command"); ok = FALSE; goto out; } DBG("type: %d", stk->pending_cmd->type); switch (stk->pending_cmd->type) { case STK_COMMAND_TYPE_SEND_SMS: ok = handled_alpha_id_set(stk, stk->pending_cmd->send_sms.alpha_id, &stk->pending_cmd->send_sms.text_attr, &stk->pending_cmd->send_sms.icon_id); break; case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT: handle_command_set_idle_text(stk->pending_cmd, &dummyrsp, stk); break; case STK_COMMAND_TYPE_SETUP_MENU: handle_command_set_up_menu(stk->pending_cmd, &dummyrsp, stk); break; case STK_COMMAND_TYPE_SETUP_CALL: ok = handle_setup_call_confirmation_req(stk->pending_cmd, stk); break; case STK_COMMAND_TYPE_SEND_USSD: ok = handled_alpha_id_set(stk, stk->pending_cmd->send_ussd.alpha_id, &stk->pending_cmd->send_ussd.text_attr, &stk->pending_cmd->send_ussd.icon_id); break; case STK_COMMAND_TYPE_SEND_SS: ok = handled_alpha_id_set(stk, stk->pending_cmd->send_ss.alpha_id, &stk->pending_cmd->send_ss.text_attr, &stk->pending_cmd->send_ss.icon_id); break; case STK_COMMAND_TYPE_SEND_DTMF: ok = handled_alpha_id_set(stk, stk->pending_cmd->send_dtmf.alpha_id, &stk->pending_cmd->send_dtmf.text_attr, &stk->pending_cmd->send_dtmf.icon_id); break; case STK_COMMAND_TYPE_REFRESH: /* * On a refresh we should not try to free the pending command, * as the stk atom itself likely disappeared as a result. * If it has not, then any subsequent proactive command, or * session end notification will free it anyway */ handle_command_refresh(stk->pending_cmd, NULL, stk); return; } out: if (ok == FALSE) { stk_command_free(stk->pending_cmd); stk->pending_cmd = NULL; } } int ofono_stk_driver_register(const struct ofono_stk_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_stk_driver_unregister(const struct ofono_stk_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *) d); } static void stk_unregister(struct ofono_atom *atom) { struct ofono_stk *stk = __ofono_atom_get_data(atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); if (stk->session_agent) stk_agent_free(stk->session_agent); if (stk->default_agent) stk_agent_free(stk->default_agent); if (stk->pending_cmd) { stk_command_free(stk->pending_cmd); stk->pending_cmd = NULL; stk->cancel_cmd = NULL; } g_free(stk->idle_mode_text); stk->idle_mode_text = NULL; if (stk->timers_source) { g_source_remove(stk->timers_source); stk->timers_source = 0; } if (stk->main_menu) { stk_menu_free(stk->main_menu); stk->main_menu = NULL; } g_queue_foreach(stk->envelope_q, (GFunc) g_free, NULL); g_queue_free(stk->envelope_q); ofono_modem_remove_interface(modem, OFONO_STK_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_STK_INTERFACE); } static void stk_remove(struct ofono_atom *atom) { struct ofono_stk *stk = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (stk == NULL) return; if (stk->driver && stk->driver->remove) stk->driver->remove(stk); g_free(stk); } struct ofono_stk *ofono_stk_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_stk *stk; GSList *l; if (driver == NULL) return NULL; stk = g_try_new0(struct ofono_stk, 1); if (stk == NULL) return NULL; stk->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_STK, stk_remove, stk); for (l = g_drivers; l; l = l->next) { const struct ofono_stk_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(stk, vendor, data) < 0) continue; stk->driver = drv; break; } return stk; } void ofono_stk_register(struct ofono_stk *stk) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(stk->atom); const char *path = __ofono_atom_get_path(stk->atom); if (!g_dbus_register_interface(conn, path, OFONO_STK_INTERFACE, stk_methods, stk_signals, NULL, stk, NULL)) { ofono_error("Could not create %s interface", OFONO_STK_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_STK_INTERFACE); __ofono_atom_register(stk->atom, stk_unregister); stk->timeout = 180; /* 3 minutes */ stk->short_timeout = 25; /* 25 seconds */ stk->envelope_q = g_queue_new(); } void ofono_stk_remove(struct ofono_stk *stk) { __ofono_atom_free(stk->atom); } void ofono_stk_set_data(struct ofono_stk *stk, void *data) { stk->driver_data = data; } void *ofono_stk_get_data(struct ofono_stk *stk) { return stk->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/cdma-netreg.c0000644000015600001650000002275612671500024021534 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include "ofono.h" static GSList *g_drivers; struct ofono_cdma_netreg { enum cdma_netreg_status status; int strength; int hdr_strength; const struct ofono_cdma_netreg_driver *driver; void *driver_data; struct ofono_atom *atom; char *provider_name; char *sid; }; static const char *cdma_netreg_status_to_string(enum cdma_netreg_status status) { switch (status) { case CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED: return "unregistered"; case CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED: return "registered"; case CDMA_NETWORK_REGISTRATION_STATUS_ROAMING: return "roaming"; } return ""; } static DBusMessage *network_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_cdma_netreg *cdma_netreg = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; const char *status = cdma_netreg_status_to_string(cdma_netreg->status); reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "Status", DBUS_TYPE_STRING, &status); if (cdma_netreg->strength != -1) { unsigned char strength = cdma_netreg->strength; ofono_dbus_dict_append(&dict, "Strength", DBUS_TYPE_BYTE, &strength); } if (cdma_netreg->hdr_strength != -1) { unsigned char strength = cdma_netreg->hdr_strength; ofono_dbus_dict_append(&dict, "DataStrength", DBUS_TYPE_BYTE, &strength); } if (cdma_netreg->sid) ofono_dbus_dict_append(&dict, "SystemIdentifier", DBUS_TYPE_STRING, &cdma_netreg->sid); if (cdma_netreg->provider_name) ofono_dbus_dict_append(&dict, "Name", DBUS_TYPE_STRING, &cdma_netreg->provider_name); dbus_message_iter_close_container(&iter, &dict); return reply; } static const GDBusMethodTable cdma_netreg_manager_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), network_get_properties) }, { } }; static const GDBusSignalTable cdma_netreg_manager_signals[] = { { } }; static void serving_system_callback(const struct ofono_error *error, const char *sid, void *data) { struct ofono_cdma_netreg *cdma_netreg = data; const char *path = __ofono_atom_get_path(cdma_netreg->atom); DBusConnection *conn = ofono_dbus_get_connection(); if (cdma_netreg->status != CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED && cdma_netreg->status != CDMA_NETWORK_REGISTRATION_STATUS_ROAMING) return; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error during serving system query"); return; } DBG("Serving system Identifier: %s", sid); if (cdma_netreg->sid != NULL && !strcmp(cdma_netreg->sid, sid)) return; g_free(cdma_netreg->provider_name); g_free(cdma_netreg->sid); cdma_netreg->provider_name = NULL; cdma_netreg->sid = g_strdup(sid); ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE, "SystemIdentifier", DBUS_TYPE_STRING, &cdma_netreg->sid); if (__ofono_cdma_provision_get_name(sid, &cdma_netreg->provider_name) == FALSE) { ofono_warn("Provider name not found"); return; } ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE, "Name", DBUS_TYPE_STRING, &cdma_netreg->provider_name); } static void set_registration_status(struct ofono_cdma_netreg *cdma_netreg, enum cdma_netreg_status status) { const char *str_status = cdma_netreg_status_to_string(status); const char *path = __ofono_atom_get_path(cdma_netreg->atom); DBusConnection *conn = ofono_dbus_get_connection(); cdma_netreg->status = status; ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE, "Status", DBUS_TYPE_STRING, &str_status); if (cdma_netreg->status == CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED || cdma_netreg->status == CDMA_NETWORK_REGISTRATION_STATUS_ROAMING) if (cdma_netreg->driver->serving_system != NULL) cdma_netreg->driver->serving_system(cdma_netreg, serving_system_callback, cdma_netreg); } void ofono_cdma_netreg_status_notify(struct ofono_cdma_netreg *cdma_netreg, enum cdma_netreg_status status) { if (cdma_netreg == NULL) return; if (cdma_netreg->status != status) set_registration_status(cdma_netreg, status); } static void strength_notify_common(struct ofono_cdma_netreg *netreg, int strength, const char *property, int *dest) { if (netreg == NULL) return; if (*dest == strength) return; /* * Theoretically we can get signal strength even when not registered * to any network. However, what do we do with it in that case? */ if (netreg->status == CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED) return; *dest = strength; if (strength != -1) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(netreg->atom); unsigned char val = strength; ofono_dbus_signal_property_changed(conn, path, OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE, property, DBUS_TYPE_BYTE, &val); } } void ofono_cdma_netreg_strength_notify(struct ofono_cdma_netreg *netreg, int strength) { return strength_notify_common(netreg, strength, "Strength", &netreg->strength); } void ofono_cdma_netreg_data_strength_notify(struct ofono_cdma_netreg *netreg, int data_strength) { return strength_notify_common(netreg, data_strength, "DataStrength", &netreg->hdr_strength); } int ofono_cdma_netreg_get_status(struct ofono_cdma_netreg *netreg) { if (netreg == NULL) return -1; return netreg->status; } int ofono_cdma_netreg_driver_register(const struct ofono_cdma_netreg_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *)d); return 0; } void ofono_cdma_netreg_driver_unregister( const struct ofono_cdma_netreg_driver *d) { DBG("driver: %p, name: %s", d, d->name); g_drivers = g_slist_remove(g_drivers, (void *)d); } static void cdma_netreg_unregister(struct ofono_atom *atom) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(atom); const char *path = __ofono_atom_get_path(atom); g_dbus_unregister_interface(conn, path, OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE); ofono_modem_remove_interface(modem, OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE); } static void cdma_netreg_remove(struct ofono_atom *atom) { struct ofono_cdma_netreg *cdma_netreg = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (cdma_netreg == NULL) return; if (cdma_netreg->driver && cdma_netreg->driver->remove) cdma_netreg->driver->remove(cdma_netreg); g_free(cdma_netreg->sid); g_free(cdma_netreg->provider_name); g_free(cdma_netreg); } struct ofono_cdma_netreg *ofono_cdma_netreg_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_cdma_netreg *cdma_netreg; GSList *l; if (driver == NULL) return NULL; cdma_netreg = g_try_new0(struct ofono_cdma_netreg, 1); if (cdma_netreg == NULL) return NULL; cdma_netreg->status = CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED; cdma_netreg->strength = -1; cdma_netreg->hdr_strength = -1; cdma_netreg->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_CDMA_NETREG, cdma_netreg_remove, cdma_netreg); for (l = g_drivers; l; l = l->next) { const struct ofono_cdma_netreg_driver *drv = l->data; if (g_strcmp0(drv->name, driver)) continue; if (drv->probe(cdma_netreg, vendor, data) < 0) continue; cdma_netreg->driver = drv; break; } return cdma_netreg; } void ofono_cdma_netreg_register(struct ofono_cdma_netreg *cdma_netreg) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(cdma_netreg->atom); const char *path = __ofono_atom_get_path(cdma_netreg->atom); if (!g_dbus_register_interface(conn, path, OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE, cdma_netreg_manager_methods, cdma_netreg_manager_signals, NULL, cdma_netreg, NULL)) { ofono_error("Could not create %s interface", OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_CDMA_NETWORK_REGISTRATION_INTERFACE); __ofono_atom_register(cdma_netreg->atom, cdma_netreg_unregister); } void ofono_cdma_netreg_remove(struct ofono_cdma_netreg *cdma_netreg) { __ofono_atom_free(cdma_netreg->atom); } void ofono_cdma_netreg_set_data(struct ofono_cdma_netreg *cdma_netreg, void *data) { cdma_netreg->driver_data = data; } void *ofono_cdma_netreg_get_data(struct ofono_cdma_netreg *cdma_netreg) { return cdma_netreg->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/message.h0000644000015600001650000000326012671500024020764 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include enum message_state { MESSAGE_STATE_PENDING, MESSAGE_STATE_SENT, MESSAGE_STATE_FAILED, MESSAGE_STATE_CANCELLED, }; struct ofono_atom; struct message; struct message *message_create(const struct ofono_uuid *uuid, struct ofono_atom *atom); gboolean message_dbus_register(struct message *m); void message_dbus_unregister(struct message *m); const struct ofono_uuid *message_get_uuid(const struct message *m); void message_set_state(struct message *m, enum message_state new_state); void message_append_properties(struct message *m, DBusMessageIter *dict); void message_emit_added(struct message *m, const char *interface); void message_emit_removed(struct message *m, const char *interface); void *message_get_data(struct message *m); void message_set_data(struct message *m, void *data); const char *message_path_from_uuid(struct ofono_atom *atom, const struct ofono_uuid *uuid); ofono-1.17.bzr6912+16.04.20160314.3/src/smsagent.c0000644000015600001650000001665412671500024021167 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include "ofono.h" #include "common.h" #include "smsagent.h" struct sms_agent { char *interface; char *path; char *service; guint disconnect_watch; ofono_destroy_func removed_cb; void *removed_data; GSList *reqs; }; struct sms_agent_request { struct sms_agent *agent; DBusMessage *msg; DBusPendingCall *call; sms_agent_dispatch_cb dispatch_cb; void *dispatch_data; ofono_destroy_func destroy; }; static struct sms_agent_request *sms_agent_request_new(struct sms_agent *agent, sms_agent_dispatch_cb cb, void *user_data, ofono_destroy_func destroy) { struct sms_agent_request *req; req = g_try_new0(struct sms_agent_request, 1); if (req == NULL) return NULL; req->agent = agent; req->dispatch_cb = cb; req->dispatch_data = user_data; req->destroy = destroy; return req; } static void sms_agent_request_free(struct sms_agent_request *req) { if (req->msg) { dbus_message_unref(req->msg); req->msg = NULL; } if (req->call) { dbus_pending_call_unref(req->call); req->call = NULL; } if (req->destroy) req->destroy(req->dispatch_data); g_free(req); } static void sms_agent_send_noreply(struct sms_agent *agent, const char *method) { DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *message; message = dbus_message_new_method_call(agent->service, agent->path, agent->interface, method); if (message == NULL) return; dbus_message_set_no_reply(message, TRUE); DBG("Sending: '%s.%s' to '%s' at '%s'", agent->interface, method, agent->service, agent->path); g_dbus_send_message(conn, message); } static inline void sms_agent_send_release(struct sms_agent *agent) { sms_agent_send_noreply(agent, "Release"); } static void sms_agent_disconnect_cb(DBusConnection *conn, void *data) { struct sms_agent *agent = data; agent->disconnect_watch = 0; sms_agent_free(agent); } struct sms_agent *sms_agent_new(const char *interface, const char *service, const char *path) { struct sms_agent *agent = g_try_new0(struct sms_agent, 1); DBusConnection *conn = ofono_dbus_get_connection(); if (agent == NULL) return NULL; agent->interface = g_strdup(interface); agent->service = g_strdup(service); agent->path = g_strdup(path); agent->disconnect_watch = g_dbus_add_disconnect_watch(conn, service, sms_agent_disconnect_cb, agent, NULL); return agent; } void sms_agent_set_removed_notify(struct sms_agent *agent, ofono_destroy_func destroy, void *user_data) { agent->removed_cb = destroy; agent->removed_data = user_data; } static void sms_agent_request_cancel(gpointer element, gpointer userdata) { struct sms_agent_request *req = element; dbus_pending_call_cancel(req->call); sms_agent_request_free(req); } void sms_agent_free(struct sms_agent *agent) { DBusConnection *conn = ofono_dbus_get_connection(); if (agent == NULL) return; if (agent->disconnect_watch) { sms_agent_send_release(agent); g_dbus_remove_watch(conn, agent->disconnect_watch); agent->disconnect_watch = 0; } if (agent->removed_cb) agent->removed_cb(agent->removed_data); g_slist_foreach(agent->reqs, sms_agent_request_cancel, NULL); g_slist_free(agent->reqs); g_free(agent->path); g_free(agent->service); g_free(agent->interface); g_free(agent); } ofono_bool_t sms_agent_matches(struct sms_agent *agent, const char *service, const char *path) { if (path == NULL || service == NULL) return FALSE; return g_str_equal(agent->path, path) && g_str_equal(agent->service, service); } static int check_error(struct sms_agent *agent, DBusMessage *reply, enum sms_agent_result *out_result) { DBusError err; int result = 0; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == FALSE) { *out_result = SMS_AGENT_RESULT_OK; return 0; } DBG("SmsAgent %s replied with error %s, %s", agent->path, err.name, err.message); /* Timeout is always valid */ if (g_str_equal(err.name, DBUS_ERROR_NO_REPLY)) { *out_result = SMS_AGENT_RESULT_TIMEOUT; goto out; } result = -EINVAL; out: dbus_error_free(&err); return result; } static void sms_agent_dispatch_reply_cb(DBusPendingCall *call, void *data) { struct sms_agent_request *req = data; struct sms_agent *agent = req->agent; sms_agent_dispatch_cb cb = req->dispatch_cb; void *dispatch_data = req->dispatch_data; DBusMessage *reply = dbus_pending_call_steal_reply(req->call); enum sms_agent_result result; if (check_error(agent, reply, &result) == -EINVAL) { dbus_message_unref(reply); sms_agent_free(agent); return; } agent->reqs = g_slist_remove(agent->reqs, req); sms_agent_request_free(req); if (cb) cb(agent, result, dispatch_data); dbus_message_unref(reply); } int sms_agent_dispatch_datagram(struct sms_agent *agent, const char *method, const char *from, const struct tm *remote_sent_time, const struct tm *local_sent_time, const unsigned char *content, unsigned int len, sms_agent_dispatch_cb cb, void *user_data, ofono_destroy_func destroy) { struct sms_agent_request *req; DBusConnection *conn = ofono_dbus_get_connection(); DBusMessageIter iter; DBusMessageIter dict; DBusMessageIter array; char buf[128]; const char *str = buf; req = sms_agent_request_new(agent, cb, user_data, destroy); if (req == NULL) return -ENOMEM; req->msg = dbus_message_new_method_call(agent->service, agent->path, agent->interface, method); if (req->msg == NULL) { sms_agent_request_free(req); return -ENOMEM; } dbus_message_iter_init_append(req->msg, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &content, len); dbus_message_iter_close_container(&iter, &array); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", local_sent_time); buf[127] = '\0'; ofono_dbus_dict_append(&dict, "LocalSentTime", DBUS_TYPE_STRING, &str); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", remote_sent_time); buf[127] = '\0'; ofono_dbus_dict_append(&dict, "SentTime", DBUS_TYPE_STRING, &str); ofono_dbus_dict_append(&dict, "Sender", DBUS_TYPE_STRING, &from); dbus_message_iter_close_container(&iter, &dict); if (!dbus_connection_send_with_reply(conn, req->msg, &req->call, -1)) { ofono_error("Sending D-Bus method failed"); sms_agent_request_free(req); return -EIO; } agent->reqs = g_slist_append(agent->reqs, req); dbus_pending_call_set_notify(req->call, sms_agent_dispatch_reply_cb, req, NULL); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/src/simutil.h0000644000015600001650000004104412671500024021030 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #define SIM_EFSPN_DC_HOME_PLMN_BIT 0x1 #define SIM_EFSPN_DC_ROAMING_SPN_BIT 0x2 enum sim_fileid { SIM_EFPL_FILEID = 0x2F05, SIM_EF_ICCID_FILEID = 0x2FE2, SIM_MF_FILEID = 0x3F00, SIM_EFIMG_FILEID = 0x4F20, SIM_EFPBR_FILEID = 0x4F30, SIM_DFPHONEBOOK_FILEID = 0x5F3A, SIM_EFLI_FILEID = 0x6F05, SIM_EFARR_FILEID = 0x6F06, SIM_EFIMSI_FILEID = 0x6F07, SIM_EF_CPHS_MWIS_FILEID = 0x6F11, SIM_EF_CPHS_CFF_FILEID = 0x6F13, SIM_EF_CPHS_SPN_FILEID = 0x6F14, SIM_EF_CPHS_CSP_FILEID = 0x6F15, SIM_EF_CPHS_INFORMATION_FILEID = 0x6F16, SIM_EF_CPHS_MBDN_FILEID = 0x6F17, SIM_EF_CPHS_SPN_SHORT_FILEID = 0x6F18, SIM_EFUST_FILEID = 0x6F38, SIM_EFSST_FILEID = 0x6F38, /* same as EFust */ SIM_EFADN_FILEID = 0x6F3A, SIM_EFGID1_FILEID = 0x6F3E, SIM_EFMSISDN_FILEID = 0x6F40, SIM_EFSMSP_FILEID = 0x6F42, SIM_EFCBMI_FILEID = 0x6F45, SIM_EFSPN_FILEID = 0x6F46, SIM_EFCBMID_FILEID = 0x6F48, SIM_EFSDN_FILEID = 0x6F49, SIM_EFEXT1_FILEID = 0x6F4A, SIM_EFBDN_FILEID = 0x6F4D, SIM_EFCBMIR_FILEID = 0x6F50, SIM_EFEST_FILEID = 0x6F56, SIM_EFAD_FILEID = 0x6FAD, SIM_EFPHASE_FILEID = 0x6FAE, SIM_EFECC_FILEID = 0x6FB7, SIM_EFPNN_FILEID = 0x6FC5, SIM_EFOPL_FILEID = 0x6FC6, SIM_EFMBDN_FILEID = 0x6FC7, SIM_EFMBI_FILEID = 0x6FC9, SIM_EFMWIS_FILEID = 0x6FCA, SIM_EFCFIS_FILEID = 0x6FCB, SIM_EFSPDI_FILEID = 0x6FCD, SIM_DFTELECOM_FILEID = 0x7F10, SIM_DFGSM_FILEID = 0x7F20, }; /* 51.011 Section 9.3 */ enum sim_file_access { SIM_FILE_ACCESS_ALWAYS = 0, SIM_FILE_ACCESS_CHV1 = 1, SIM_FILE_ACCESS_CHV2 = 2, SIM_FILE_ACCESS_RESERVED = 3, SIM_FILE_ACCESS_ADM = 4, SIM_FILE_ACCESS_NEVER = 15, }; /* 51.011 Section 9.3 */ enum sim_file_status { SIM_FILE_STATUS_VALID = 0x01, SIM_FILE_STATUS_RW_WHEN_INVALID = 0x04, }; /* 131.102 Section 4.2.8 */ enum sim_ust_service { SIM_UST_SERVICE_LOCAL_PHONE_BOOK = 0, SIM_UST_SERVICE_FDN = 1, SIM_UST_SERVICE_EXT_2 = 2, SIM_UST_SERVICE_SDN = 3, SIM_UST_SERVICE_EXT_3 = 4, SIM_UST_SERVICE_BDN = 5, SIM_UST_SERVICE_EXT_4 = 6, SIM_UST_SERVICE_OCI_OCT = 7, SIM_UST_SERVICE_ICI_ICT = 8, SIM_UST_SERVICE_SMS = 9, SIM_UST_SERVICE_SMSR = 10, SIM_UST_SERVICE_SMSP = 11, SIM_UST_SERVICE_AOC = 12, SIM_UST_SERVICE_CCP2 = 13, SIM_UST_SERVICE_CBS_ID = 14, SIM_UST_SERVICE_CBS_ID_RANGE = 15, SIM_UST_SERVICE_GROUP_ID_LEVEL_1 = 16, SIM_UST_SERVICE_GROUP_ID_LEVEL_2 = 17, SIM_UST_SERVICE_PROVIDER_NAME = 18, SIM_UST_SERVICE_USER_PLMN = 19, SIM_UST_SERVICE_MSISDN = 20, SIM_UST_SERVICE_IMG = 21, SIM_UST_SERVICE_SOLSA = 22, SIM_UST_SERVICE_PRECEDENCE_PREEMPTION = 23, SIM_UST_SERVICE_EMLPP = 24, SIM_UST_SERVICE_GSM_ACCESS = 26, SIM_UST_SERVICE_DATA_DOWNLOAD_SMS_PP = 27, SIM_UST_SERVICE_DATA_DOWNLOAD_SMS_CB = 28, SIM_UST_SERVICE_CALL_CONTROL_USIM = 29, SIM_UST_SERVICE_MO_SMS_USIM = 30, SIM_UST_SERVICE_RUN_AT_COMMAND = 31, SIM_UST_SERVICE_ENABLED_SERVICE_TABLE = 33, SIM_UST_SERVICE_ACL = 34, SIM_UST_SERVICE_DEPERSONALISATION_CTRL_KEY = 35, SIM_UST_SERVICE_NETWORK_LIST = 36, SIM_UST_SERVICE_GSM_SECURITY_CONTEXT = 37, SIM_UST_SERVICE_CPBCCH = 38, SIM_UST_SERVICE_INVESTIGATION_SCAN = 39, SIM_UST_SERVICE_MEXE = 40, SIM_UST_SERVICE_OPERATOR_PLMN = 41, SIM_UST_SERVICE_HPLMN = 42, SIM_UST_SERVICE_EXT_5 = 43, SIM_UST_SERVICE_PLMN_NETWORK_NAME = 44, SIM_UST_SERVICE_OPERATOR_PLMN_LIST = 45, SIM_UST_SERVICE_MAILBOX_DIALLING_NUMBERS = 46, SIM_UST_SERVICE_MWIS = 47, SIM_UST_SERVICE_CFIS = 48, SIM_UST_SERVICE_PROVIDER_DISPLAY_INFO = 50, SIM_UST_SERVICE_MMS = 51, SIM_UST_SERVICE_EXT_8 = 52, SIM_UST_SERVICE_CALL_CONTROL_GPRS_USIM = 53, SIM_UST_SERVICE_MMS_USER_CONN_PARAM = 54, SIM_UST_SERVICE_NIA = 55, SIM_UST_SERVICE_EFVGCS_EFVGCSS = 56, SIM_UST_SERVICE_EFVBS_EFVBSS = 57, SIM_UST_SERVICE_PSEUDONYM = 58, SIM_UST_SERVICE_USER_PLMN_I_WLAN = 59, SIM_UST_SERVICE_OPERATOR_PLMN_I_WLAN = 60, SIM_UST_SERVICE_USER_WSID = 61, SIM_UST_SERVICE_OPERATOR_WSID = 62, SIM_UST_SERVICE_VGCS_SECURITY = 63, SIM_UST_SERVICE_VBS_SECURITY = 64, SIM_UST_SERVICE_WLAN_REAUTH_ID = 65, SIM_UST_SERVICE_MMS_STORAGE = 66, SIM_UST_SERVICE_GBA = 67, SIM_UST_SERVICE_MBMS_SECURITY = 68, SIM_UST_SERVICE_USSD_APPLICATION_MODE = 69, SIM_UST_SERVICE_EQUIVALENT_HPLMN = 70, SIM_UST_SERVICE_ADDITIONAL_TERMINAL_PROFILE = 71, SIM_UST_SERVICE_EQUIVALENT_HPLMN_IND = 72, SIM_UST_SERVICE_LAST_RPLMN_IND = 73, SIM_UST_SERVICE_OMA_BCAST_SC_PROFILE = 74, SIM_UST_SERVICE_BGA_LOCAL_KEY = 75, SIM_UST_SERVICE_TERMINAL_APPLICATIONS = 76, SIM_UST_SERVICE_PROVIDER_NAME_ICON = 77, SIM_UST_SERVICE_PLMN_NETWORK_NAME_ICON = 78, SIM_UST_SERVICE_CONN_PARAM_USIM_IP = 79, SIM_UST_SERVICE_HOME_I_WLAN_ID_LIST = 80, SIM_UST_SERVICE_I_WLAN_EQUIVALENT_HPLMN_IND = 81, SIM_UST_SERVICE_I_WLAN_HPLMN_PRIORITY_IND = 82, SIM_UST_SERVICE_I_WLAN_LAST_PLMN = 83, SIM_UST_SERVICE_EPS_INFO = 84, SIM_UST_SERVICE_CSG_IND = 85, SIM_UST_SERVICE_CALL_CONTROL_EPS_PDN_USIM = 86, SIM_UST_SERVICE_HPLMN_DIRECT_ACCESS = 87, SIM_UST_SERVICE_ECALL_DATA = 88, SIM_UST_SERVICE_OPERATOR_CSG = 89 }; /* 131.102 Section 4.2.47 */ enum sim_est_service { SIM_EST_SERVICE_FDN = 0, SIM_EST_SERVICE_BDN = 1, SIM_EST_SERVICE_ACL = 2 }; /* 51.011 Section 10.3.7 */ enum sim_sst_service { SIM_SST_SERVICE_CHV1_DISABLE = 0, SIM_SST_SERVICE_ADN = 1, SIM_SST_SERVICE_FDN = 2, SIM_SST_SERVICE_SMS = 3, SIM_SST_SERVICE_AOC = 4, SIM_SST_SERVICE_CCP = 5, SIM_SST_SERVICE_PLMN_SELECTOR = 6, SIM_SST_SERVICE_MSISDN = 8, SIM_SST_SERVICE_EXT_1 = 9, SIM_SST_SERVICE_EXT_2 = 10, SIM_SST_SERVICE_SMSP = 11, SIM_SST_SERVICE_LND = 12, SIM_SST_SERVICE_CBS_ID = 13, SIM_SST_SERVICE_GROUP_ID_LEVEL_1 = 14, SIM_SST_SERVICE_GROUP_ID_LEVEL_2 = 15, SIM_SST_SERVICE_PROVIDER_NAME = 16, SIM_SST_SERVICE_SDN = 17, SIM_SST_SERVICE_EXT_3 = 18, SIM_SST_SERVICE_EFVGCS_EFVGCSS = 20, SIM_SST_SERVICE_EFVBS_EFVBSS = 21, SIM_SST_SERVICE_PRECEDENCE_PREEMPTION = 22, SIM_SST_SERVICE_EMLPP = 23, SIM_SST_SERVICE_DATA_DOWNLOAD_SMS_CB = 24, SIM_SST_SERVICE_DATA_DOWNLOAD_SMS_PP = 25, SIM_SST_SERVICE_MENU_SELECTION = 26, SIM_SST_SERVICE_CALL_CONTROL = 27, SIM_SST_SERVICE_PROACTIVE_SIM = 28, SIM_SST_SERVICE_CBS_ID_RANGE = 29, SIM_SST_SERVICE_BDN = 30, SIM_SST_SERVICE_EXT_4 = 31, SIM_SST_SERVICE_DEPERSONALISATION_CTRL_KEY = 32, SIM_SST_SERVICE_NETWORK_LIST = 33, SIM_SST_SERVICE_SMSR = 34, SIM_SST_SERVICE_NIA = 35, SIM_SST_SERVICE_MO_SMS_SIM = 36, SIM_SST_SERVICE_GPRS = 37, SIM_SST_SERVICE_IMG = 38, SIM_SST_SERVICE_SOLSA = 39, SIM_SST_SERVICE_USSD_CALL_CONTROL = 40, SIM_SST_SERVICE_RUN_AT_COMMAND = 41, SIM_SST_SERVICE_USER_PLMN = 42, SIM_SST_SERVICE_OPERATOR_PLMN = 43, SIM_SST_SERVICE_HPLMN = 44, SIM_SST_SERVICE_CPBCCH = 45, SIM_SST_SERVICE_INVESTIGATION_SCAN = 46, SIM_SST_SERVICE_EXT_CCP = 47, SIM_SST_SERVICE_MEXE = 48, SIM_SST_SERVICE_RPLMN = 49, SIM_SST_SERVICE_PLMN_NETWORK_NAME = 50, SIM_SST_SERVICE_OPERATOR_PLMN_LIST = 51, SIM_SST_SERVICE_MAILBOX_DIALLING_NUMBERS = 52, SIM_SST_SERVICE_MWIS = 53, SIM_SST_SERVICE_CFIS = 54, SIM_SST_SERVICE_PROVIDER_DISPLAY_INFO = 55 }; /* CPHS 4.2, Section B.3.1.1 */ enum sim_cphs_service { SIM_CPHS_SERVICE_CSP = 0x0, SIM_CPHS_SERVICE_SST = 0x1, SIM_CPHS_SERVICE_MAILBOX_NUMBERS = 0x2, SIM_CPHS_SERVICE_SHORT_SPN = 0x3, SIM_CPHS_SERVICE_INFO_NUMBERS = 0x4, }; /* CPHS 4.2, Section B4.7 CSP Service Group Codes */ enum sim_csp_entry { SIM_CSP_ENTRY_CALL_OFFERING = 0x01, SIM_CSP_ENTRY_CALL_RESTRICTION = 0x02, SIM_CSP_ENTRY_OTHER_SUPP_SERVICES = 0x03, SIM_CSP_ENTRY_CALL_COMPLETION = 0x04, SIM_CSP_ENTRY_TELESERVICES = 0x05, SIM_CSP_ENTRY_CPHS_TELESERVICES = 0x06, SIM_CSP_ENTRY_CPHS_FEATURES = 0x07, SIM_CSP_ENTRY_NUMBER_IDENTIFICATION = 0x08, SIM_CSP_ENTRY_PHASE_2GPLUS_SERVICES = 0x09, SIM_CSP_ENTRY_VALUE_ADDED_SERVICES = 0xC0, SIM_CSP_ENTRY_INFORMATION_NUMBERS = 0xD5, }; enum ber_tlv_data_type { BER_TLV_DATA_TYPE_UNIVERSAL = 0, BER_TLV_DATA_TYPE_APPLICATION = 1, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC = 2, BER_TLV_DATA_TYPE_PRIVATE = 3, }; enum ber_tlv_data_encoding_type { BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE = 0, BER_TLV_DATA_ENCODING_TYPE_CONSTRUCTED = 1, }; struct sim_eons_operator_info { char *longname; gboolean long_ci; char *shortname; gboolean short_ci; char *info; }; struct sim_ef_info { unsigned short id; unsigned short parent2g; unsigned short parent3g; unsigned char file_type; unsigned char file_structure; unsigned char size; enum sim_file_access perm_read; enum sim_file_access perm_update; }; struct sim_app_record { unsigned char aid[16]; int aid_len; char *label; }; struct simple_tlv_iter { unsigned int max; unsigned int pos; const unsigned char *pdu; unsigned char tag; unsigned short len; const unsigned char *data; }; struct comprehension_tlv_iter { unsigned int max; unsigned int pos; const unsigned char *pdu; unsigned short tag; gboolean cr; unsigned int len; const unsigned char *data; }; struct ber_tlv_iter { unsigned int max; unsigned int pos; const unsigned char *pdu; unsigned int tag; enum ber_tlv_data_type class; enum ber_tlv_data_encoding_type encoding; unsigned int len; const unsigned char *data; }; struct ber_tlv_builder { unsigned int max; unsigned int pos; unsigned char *pdu; struct ber_tlv_builder *parent; unsigned int tag; enum ber_tlv_data_type class; enum ber_tlv_data_encoding_type encoding; unsigned int len; }; struct comprehension_tlv_builder { unsigned int max; unsigned int pos; unsigned char *pdu; unsigned int len; struct ber_tlv_builder *parent; }; void simple_tlv_iter_init(struct simple_tlv_iter *iter, const unsigned char *pdu, unsigned int len); gboolean simple_tlv_iter_next(struct simple_tlv_iter *iter); unsigned char simple_tlv_iter_get_tag(struct simple_tlv_iter *iter); unsigned short simple_tlv_iter_get_length(struct simple_tlv_iter *iter); const unsigned char *simple_tlv_iter_get_data(struct simple_tlv_iter *iter); void comprehension_tlv_iter_init(struct comprehension_tlv_iter *iter, const unsigned char *pdu, unsigned int len); gboolean comprehension_tlv_iter_next(struct comprehension_tlv_iter *iter); unsigned short comprehension_tlv_iter_get_tag(struct comprehension_tlv_iter *i); gboolean comprehension_tlv_get_cr(struct comprehension_tlv_iter *iter); unsigned int comprehension_tlv_iter_get_length( struct comprehension_tlv_iter *iter); const unsigned char *comprehension_tlv_iter_get_data( struct comprehension_tlv_iter *iter); void comprehension_tlv_iter_copy(struct comprehension_tlv_iter *from, struct comprehension_tlv_iter *to); gboolean comprehension_tlv_builder_init( struct comprehension_tlv_builder *builder, unsigned char *pdu, unsigned int size); gboolean comprehension_tlv_builder_next( struct comprehension_tlv_builder *builder, gboolean cr, unsigned short tag); gboolean comprehension_tlv_builder_set_length( struct comprehension_tlv_builder *builder, unsigned int len); unsigned char *comprehension_tlv_builder_get_data( struct comprehension_tlv_builder *builder); void ber_tlv_iter_init(struct ber_tlv_iter *iter, const unsigned char *pdu, unsigned int len); /* * Returns the tag value of the TLV. Note that the tag value can be either * short (0-30) or long */ unsigned int ber_tlv_iter_get_tag(struct ber_tlv_iter *iter); enum ber_tlv_data_type ber_tlv_iter_get_class(struct ber_tlv_iter *iter); enum ber_tlv_data_encoding_type ber_tlv_iter_get_encoding( struct ber_tlv_iter *iter); /* * This will return the short tag along with class and encoding information. * This is more convenient to use for TLV contents of SIM Elementary Files * and SIM toolkit since these elements only use short tags. In case of an * error (e.g. not a short tag) a zero is returned. According to ISO 7816, * a tag value of '00' is invalid. */ unsigned char ber_tlv_iter_get_short_tag(struct ber_tlv_iter *iter); unsigned int ber_tlv_iter_get_length(struct ber_tlv_iter *iter); const unsigned char *ber_tlv_iter_get_data(struct ber_tlv_iter *iter); gboolean ber_tlv_iter_next(struct ber_tlv_iter *iter); void ber_tlv_iter_recurse(struct ber_tlv_iter *iter, struct ber_tlv_iter *recurse); void ber_tlv_iter_recurse_simple(struct ber_tlv_iter *iter, struct simple_tlv_iter *container); void ber_tlv_iter_recurse_comprehension(struct ber_tlv_iter *iter, struct comprehension_tlv_iter *recurse); gboolean ber_tlv_builder_init(struct ber_tlv_builder *builder, unsigned char *pdu, unsigned int size); gboolean ber_tlv_builder_next(struct ber_tlv_builder *builder, enum ber_tlv_data_type class, enum ber_tlv_data_encoding_type encoding, unsigned int new_tag); gboolean ber_tlv_builder_set_length(struct ber_tlv_builder *builder, unsigned int len); unsigned char *ber_tlv_builder_get_data(struct ber_tlv_builder *builder); gboolean ber_tlv_builder_recurse(struct ber_tlv_builder *builder, struct ber_tlv_builder *recurse); gboolean ber_tlv_builder_recurse_comprehension(struct ber_tlv_builder *builder, struct comprehension_tlv_builder *recurse); void ber_tlv_builder_optimize(struct ber_tlv_builder *builder, unsigned char **pdu, unsigned int *len); struct sim_eons *sim_eons_new(int pnn_records); void sim_eons_add_pnn_record(struct sim_eons *eons, int record, const guint8 *tlv, int length); gboolean sim_eons_pnn_is_empty(struct sim_eons *eons); void sim_eons_add_opl_record(struct sim_eons *eons, const guint8 *contents, int length); void sim_eons_optimize(struct sim_eons *eons); const struct sim_eons_operator_info *sim_eons_lookup_with_lac( struct sim_eons *eons, const char *mcc, const char *mnc, guint16 lac); const struct sim_eons_operator_info *sim_eons_lookup(struct sim_eons *eons, const char *mcc, const char *mnc); void sim_eons_free(struct sim_eons *eons); void sim_parse_mcc_mnc(const guint8 *bcd, char *mcc, char *mnc); void sim_encode_mcc_mnc(guint8 *out, const char *mcc, const char *mnc); struct sim_spdi *sim_spdi_new(const guint8 *tlv, int length); gboolean sim_spdi_lookup(struct sim_spdi *spdi, const char *mcc, const char *mnc); void sim_spdi_free(struct sim_spdi *spdi); static inline enum sim_file_access file_access_condition_decode(int bcd) { if (bcd >= 4 && bcd <= 14) return SIM_FILE_ACCESS_ADM; return bcd; } void sim_extract_bcd_number(const unsigned char *buf, int len, char *out); void sim_encode_bcd_number(const char *number, unsigned char *out); gboolean sim_adn_parse(const unsigned char *data, int length, struct ofono_phone_number *ph, char **identifier); void sim_adn_build(unsigned char *data, int length, const struct ofono_phone_number *ph, const char *identifier); struct sim_ef_info *sim_ef_db_lookup(unsigned short efid); unsigned int sim_ef_db_get_path_2g(unsigned short id, unsigned char path[]); unsigned int sim_ef_db_get_path_3g(unsigned short id, unsigned char path[]); gboolean sim_parse_3g_get_response(const unsigned char *data, int len, int *file_len, int *record_len, int *structure, unsigned char *access, unsigned short *efid); gboolean sim_parse_2g_get_response(const unsigned char *response, int len, int *file_len, int *record_len, int *structure, unsigned char *access, unsigned char *file_status); gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len, enum sim_ust_service index); gboolean sim_est_is_active(unsigned char *service_est, unsigned char len, enum sim_est_service index); gboolean sim_sst_is_available(unsigned char *service_sst, unsigned char len, enum sim_sst_service index); gboolean sim_sst_is_active(unsigned char *service_sst, unsigned char len, enum sim_sst_service index); gboolean sim_cphs_is_active(unsigned char *service_cphs, enum sim_cphs_service index); GSList *sim_parse_app_template_entries(const unsigned char *buffer, int len); ofono-1.17.bzr6912+16.04.20160314.3/src/smsutil.c0000644000015600001650000031051012671500024021032 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "util.h" #include "storage.h" #include "smsutil.h" #define uninitialized_var(x) x = x #define SMS_BACKUP_MODE 0600 #define SMS_BACKUP_PATH STORAGEDIR "/%s/sms_assembly" #define SMS_BACKUP_PATH_DIR SMS_BACKUP_PATH "/%s-%i-%i" #define SMS_BACKUP_PATH_FILE SMS_BACKUP_PATH_DIR "/%03i" #define SMS_SR_BACKUP_PATH STORAGEDIR "/%s/sms_sr" #define SMS_SR_BACKUP_PATH_FILE SMS_SR_BACKUP_PATH "/%s-%s" #define SMS_TX_BACKUP_PATH STORAGEDIR "/%s/tx_queue" #define SMS_TX_BACKUP_PATH_DIR SMS_TX_BACKUP_PATH "/%lu-%lu-%s" #define SMS_TX_BACKUP_PATH_FILE SMS_TX_BACKUP_PATH_DIR "/%03i" #define SMS_ADDR_FMT "%24[0-9A-F]" #define SMS_MSGID_FMT "%40[0-9A-F]" /* * Time zone accounts for daylight saving time, and the two extreme time * zones on earth are UTC-12 and UTC+14. */ #define MAX_TIMEZONE 56 #define MIN_TIMEZONE -48 static GSList *sms_assembly_add_fragment_backup(struct sms_assembly *assembly, const struct sms *sms, time_t ts, const struct sms_address *addr, guint16 ref, guint8 max, guint8 seq, gboolean backup); /* * This function uses the meanings of digits 10..15 according to the rules * defined in 23.040 Section 9.1.2.3 and 24.008 Table 10.5.118 */ void extract_bcd_number(const unsigned char *buf, int len, char *out) { static const char digit_lut[] = "0123456789*#abc\0"; unsigned char oct; int i; for (i = 0; i < len; i++) { oct = buf[i]; out[i * 2] = digit_lut[oct & 0x0f]; out[i * 2 + 1] = digit_lut[(oct & 0xf0) >> 4]; } out[i * 2] = '\0'; } static inline int to_semi_oct(char in) { int digit; switch (in) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': digit = in - '0'; break; case '*': digit = 10; break; case '#': digit = 11; break; case 'A': case 'a': digit = 12; break; case 'B': case 'b': digit = 13; break; case 'C': case 'c': digit = 14; break; default: digit = -1; break; } return digit; } void encode_bcd_number(const char *number, unsigned char *out) { while (number[0] != '\0' && number[1] != '\0') { *out = to_semi_oct(*number++); *out++ |= to_semi_oct(*number++) << 4; } if (*number) *out = to_semi_oct(*number) | 0xf0; } /* * Returns whether the DCS could be parsed successfully, e.g. no reserved * values were used */ gboolean sms_dcs_decode(guint8 dcs, enum sms_class *cls, enum sms_charset *charset, gboolean *compressed, gboolean *autodelete) { guint8 upper = (dcs & 0xf0) >> 4; enum sms_charset ch; enum sms_class cl; gboolean comp; gboolean autodel; /* MWI DCS types are handled in sms_mwi_dcs_decode */ if (upper >= 0x8 && upper <= 0xE) return FALSE; upper = (dcs & 0xc0) >> 6; switch (upper) { case 0: case 1: autodel = upper; comp = (dcs & 0x20) ? TRUE : FALSE; if (dcs & 0x10) cl = (enum sms_class) (dcs & 0x03); else cl = SMS_CLASS_UNSPECIFIED; if (((dcs & 0x0c) >> 2) < 3) ch = (enum sms_charset) ((dcs & 0x0c) >> 2); else return FALSE; break; case 3: comp = FALSE; autodel = FALSE; if (dcs & 0x4) ch = SMS_CHARSET_8BIT; else ch = SMS_CHARSET_7BIT; cl = (enum sms_class) (dcs & 0x03); break; default: return FALSE; }; if (compressed) *compressed = comp; if (autodelete) *autodelete = autodel; if (cls) *cls = cl; if (charset) *charset = ch; return TRUE; } gboolean sms_mwi_dcs_decode(guint8 dcs, enum sms_mwi_type *type, enum sms_charset *charset, gboolean *active, gboolean *discard) { guint8 upper = (dcs & 0xf0) >> 4; enum sms_mwi_type t; enum sms_charset ch; gboolean dis; gboolean act; if (upper < 0xC || upper > 0xE) return FALSE; upper = (dcs & 0x30) >> 4; if (upper == 0) dis = TRUE; else dis = FALSE; /* * As per 3GPP TS 23.038 specification, if bits 7..4 set to 1110, * text included in the user data is coded in the uncompresssed * UCS2 character set. */ if (upper == 2) ch = SMS_CHARSET_UCS2; else ch = SMS_CHARSET_7BIT; act = (dcs & 0x8) ? TRUE : FALSE; t = (enum sms_mwi_type) (dcs & 0x3); if (type) *type = t; if (charset) *charset = ch; if (active) *active = act; if (discard) *discard = dis; return TRUE; } int sms_udl_in_bytes(guint8 ud_len, guint8 dcs) { int len_7bit = (ud_len + 1) * 7 / 8; int len_8bit = ud_len; guint8 upper; if (dcs == 0) return len_7bit; upper = (dcs & 0xc0) >> 6; switch (upper) { case 0: case 1: if (dcs & 0x20) /* compressed */ return len_8bit; switch ((dcs & 0x0c) >> 2) { case 0: return len_7bit; case 1: return len_8bit; case 2: return len_8bit; } return 0; case 2: return 0; case 3: switch ((dcs & 0x30) >> 4) { case 0: case 1: return len_7bit; case 2: return len_8bit; case 3: if (dcs & 0x4) return len_8bit; else return len_7bit; } break; default: break; }; return 0; } static inline gboolean next_octet(const unsigned char *pdu, int len, int *offset, unsigned char *oct) { if (len == *offset) return FALSE; *oct = pdu[*offset]; *offset = *offset + 1; return TRUE; } static inline gboolean set_octet(unsigned char *pdu, int *offset, unsigned char oct) { pdu[*offset] = oct; *offset = *offset + 1; return TRUE; } gboolean sms_encode_scts(const struct sms_scts *in, unsigned char *pdu, int *offset) { guint timezone; if (in->year > 99) return FALSE; if (in->month > 12) return FALSE; if (in->day > 31) return FALSE; if (in->hour > 23) return FALSE; if (in->minute > 59) return FALSE; if (in->second > 59) return FALSE; if ((in->timezone > MAX_TIMEZONE || in->timezone < MIN_TIMEZONE) && in->has_timezone == TRUE) return FALSE; pdu = pdu + *offset; pdu[0] = ((in->year / 10) & 0x0f) | (((in->year % 10) & 0x0f) << 4); pdu[1] = ((in->month / 10) & 0x0f) | (((in->month % 10) & 0x0f) << 4); pdu[2] = ((in->day / 10) & 0x0f) | (((in->day % 10) & 0x0f) << 4); pdu[3] = ((in->hour / 10) & 0x0f) | (((in->hour % 10) & 0x0f) << 4); pdu[4] = ((in->minute / 10) & 0x0f) | (((in->minute % 10) & 0x0f) << 4); pdu[5] = ((in->second / 10) & 0x0f) | (((in->second % 10) & 0x0f) << 4); if (in->has_timezone == FALSE) { pdu[6] = 0xff; goto out; } timezone = abs(in->timezone); pdu[6] = ((timezone / 10) & 0x07) | (((timezone % 10) & 0x0f) << 4); if (in->timezone < 0) pdu[6] |= 0x8; out: *offset += 7; return TRUE; } guint8 sms_decode_semi_octet(guint8 in) { return (in & 0x0f) * 10 + (in >> 4); } gboolean sms_decode_scts(const unsigned char *pdu, int len, int *offset, struct sms_scts *out) { unsigned char oct = 0; if ((len - *offset) < 7) return FALSE; next_octet(pdu, len, offset, &oct); out->year = sms_decode_semi_octet(oct); if (out->year > 99) return FALSE; next_octet(pdu, len, offset, &oct); out->month = sms_decode_semi_octet(oct); if (out->month > 12) return FALSE; next_octet(pdu, len, offset, &oct); out->day = sms_decode_semi_octet(oct); if (out->day > 31) return FALSE; next_octet(pdu, len, offset, &oct); out->hour = sms_decode_semi_octet(oct); if (out->hour > 23) return FALSE; next_octet(pdu, len, offset, &oct); out->minute = sms_decode_semi_octet(oct); if (out->minute > 59) return FALSE; next_octet(pdu, len, offset, &oct); out->second = sms_decode_semi_octet(oct); if (out->second > 59) return FALSE; next_octet(pdu, len, offset, &oct); /* * Time Zone indicates the difference, expressed in quarters * of an hour, between the local time and GMT. In the first of the two * semi‑octets, the first bit (bit 3 of the seventh octet of the * TP‑Service‑Centre‑Time‑Stamp field) represents the algebraic * sign of this difference (0: positive, 1: negative). */ out->timezone = (oct & 0x07) * 10 + ((oct & 0xf0) >> 4); if (oct & 0x08) out->timezone = out->timezone * -1; if ((out->timezone > MAX_TIMEZONE) || (out->timezone < MIN_TIMEZONE)) return FALSE; out->has_timezone = TRUE; return TRUE; } static gboolean decode_validity_period(const unsigned char *pdu, int len, int *offset, enum sms_validity_period_format vpf, struct sms_validity_period *vp) { switch (vpf) { case SMS_VALIDITY_PERIOD_FORMAT_ABSENT: return TRUE; case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE: if (!next_octet(pdu, len, offset, &vp->relative)) return FALSE; return TRUE; case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE: if (!sms_decode_scts(pdu, len, offset, &vp->absolute)) return FALSE; return TRUE; case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED: /* * TODO: Parse out enhanced structure properly * 23.040 Section 9.2.3.12.3 */ if ((len - *offset) < 7) return FALSE; memcpy(vp->enhanced, pdu + *offset, 7); *offset = *offset + 7; return TRUE; default: break; } return FALSE; } static gboolean encode_validity_period(const struct sms_validity_period *vp, enum sms_validity_period_format vpf, unsigned char *pdu, int *offset) { switch (vpf) { case SMS_VALIDITY_PERIOD_FORMAT_ABSENT: return TRUE; case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE: set_octet(pdu, offset, vp->relative); return TRUE; case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE: return sms_encode_scts(&vp->absolute, pdu, offset); case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED: /* TODO: Write out proper enhanced VP structure */ memcpy(pdu + *offset, vp->enhanced, 7); *offset = *offset + 7; return TRUE; default: break; } return FALSE; } gboolean sms_encode_address_field(const struct sms_address *in, gboolean sc, unsigned char *pdu, int *offset) { const char *addr = (const char *)&in->address; size_t len = strlen(addr); unsigned char addr_len = 0; unsigned char p[10]; pdu = pdu + *offset; if (len == 0 && sc) { pdu[0] = 0; *offset = *offset + 1; return TRUE; } if (len == 0) goto out; if (in->number_type == SMS_NUMBER_TYPE_ALPHANUMERIC) { long written; long packed; unsigned char *gsm; unsigned char *r; /* TP-OA's 10 octets transport 11 8-bit chars */ if (g_utf8_strlen(addr, strlen(addr)) > 11) return FALSE; gsm = convert_utf8_to_gsm(in->address, len, NULL, &written, 0); if (gsm == NULL) return FALSE; if (written > 11) { g_free(gsm); return FALSE; } r = pack_7bit_own_buf(gsm, written, 0, FALSE, &packed, 0, p); g_free(gsm); if (r == NULL) return FALSE; if (sc) addr_len = packed + 1; else addr_len = (written * 7 + 3) / 4; } else { int j = 0; int i; int c; if (len > 20) return FALSE; for (i = 0; in->address[i]; i++) { c = to_semi_oct(in->address[i]); if (c < 0) return FALSE; if ((i % 2) == 0) { p[j] = c; } else { p[j] |= c << 4; j++; } } if ((i % 2) == 1) { p[j] |= 0xf0; j++; } if (sc) addr_len = j + 1; else addr_len = i; } out: pdu[0] = addr_len; pdu[1] = (in->number_type << 4) | in->numbering_plan | 0x80; memcpy(pdu + 2, p, (sc ? addr_len - 1 : (addr_len + 1) / 2)); *offset = *offset + 2 + (sc ? addr_len - 1 : (addr_len + 1) / 2); return TRUE; } gboolean sms_decode_address_field(const unsigned char *pdu, int len, int *offset, gboolean sc, struct sms_address *out) { unsigned char addr_len; unsigned char addr_type; int byte_len; if (!next_octet(pdu, len, offset, &addr_len)) return FALSE; if (sc && addr_len == 0) { out->address[0] = '\0'; return TRUE; } if (!next_octet(pdu, len, offset, &addr_type)) return FALSE; if (sc) byte_len = addr_len - 1; else byte_len = (addr_len + 1) / 2; if ((len - *offset) < byte_len) return FALSE; out->number_type = bit_field(addr_type, 4, 3); out->numbering_plan = bit_field(addr_type, 0, 4); if (out->number_type != SMS_NUMBER_TYPE_ALPHANUMERIC) { extract_bcd_number(pdu + *offset, byte_len, out->address); *offset += byte_len; } else { int chars; long written; unsigned char *res; char *utf8; if (sc) chars = byte_len * 8 / 7; else chars = addr_len * 4 / 7; /* * This cannot happen according to 24.011, however * nothing is said in 23.040 */ if (chars == 0) { out->address[0] = '\0'; return TRUE; } res = unpack_7bit(pdu + *offset, byte_len, 0, FALSE, chars, &written, 0); *offset = *offset + (addr_len + 1) / 2; if (res == NULL) return FALSE; utf8 = convert_gsm_to_utf8(res, written, NULL, NULL, 0); g_free(res); if (utf8 == NULL) return FALSE; /* * TP-OA's 10 octets transport 11 8-bit chars, * 22 bytes+terminator in UTF-8. */ if (strlen(utf8) > 22) { g_free(utf8); return FALSE; } strcpy(out->address, utf8); g_free(utf8); } return TRUE; } static gboolean encode_deliver(const struct sms_deliver *in, unsigned char *pdu, int *offset) { int ud_oct_len; unsigned char oct; oct = 0; if (!in->mms) oct |= 1 << 2; if (in->sri) oct |= 1 << 5; if (in->rp) oct |= 1 << 7; if (in->udhi) oct |= 1 << 6; set_octet(pdu, offset, oct); if (sms_encode_address_field(&in->oaddr, FALSE, pdu, offset) == FALSE) return FALSE; set_octet(pdu, offset, in->pid); set_octet(pdu, offset, in->dcs); if (sms_encode_scts(&in->scts, pdu, offset) == FALSE) return FALSE; set_octet(pdu, offset, in->udl); ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs); memcpy(pdu + *offset, in->ud, ud_oct_len); *offset = *offset + ud_oct_len; return TRUE; } static gboolean decode_deliver(const unsigned char *pdu, int len, struct sms *out) { int offset = 0; int expected; unsigned char octet; out->type = SMS_TYPE_DELIVER; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; out->deliver.mms = !is_bit_set(octet, 2); out->deliver.sri = is_bit_set(octet, 5); out->deliver.udhi = is_bit_set(octet, 6); out->deliver.rp = is_bit_set(octet, 7); if (!sms_decode_address_field(pdu, len, &offset, FALSE, &out->deliver.oaddr)) return FALSE; if (!next_octet(pdu, len, &offset, &out->deliver.pid)) return FALSE; if (!next_octet(pdu, len, &offset, &out->deliver.dcs)) return FALSE; if (!sms_decode_scts(pdu, len, &offset, &out->deliver.scts)) return FALSE; if (!next_octet(pdu, len, &offset, &out->deliver.udl)) return FALSE; expected = sms_udl_in_bytes(out->deliver.udl, out->deliver.dcs); if ((len - offset) < expected) return FALSE; memcpy(out->deliver.ud, pdu + offset, expected); return TRUE; } static gboolean encode_submit_ack_report(const struct sms_submit_ack_report *in, unsigned char *pdu, int *offset) { unsigned char oct; oct = 1; if (in->udhi) oct |= 1 << 6; set_octet(pdu, offset, oct); set_octet(pdu, offset, in->pi); if (!sms_encode_scts(&in->scts, pdu, offset)) return FALSE; if (in->pi & 0x1) set_octet(pdu, offset, in->pid); if (in->pi & 0x2) set_octet(pdu, offset, in->dcs); if (in->pi & 0x4) { int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs); set_octet(pdu, offset, in->udl); memcpy(pdu + *offset, in->ud, ud_oct_len); *offset = *offset + ud_oct_len; } return TRUE; } static gboolean encode_submit_err_report(const struct sms_submit_err_report *in, unsigned char *pdu, int *offset) { unsigned char oct; oct = 0x1; if (in->udhi) oct |= 1 << 6; set_octet(pdu, offset, oct); set_octet(pdu, offset, in->fcs); set_octet(pdu, offset, in->pi); if (!sms_encode_scts(&in->scts, pdu, offset)) return FALSE; if (in->pi & 0x1) set_octet(pdu, offset, in->pid); if (in->pi & 0x2) set_octet(pdu, offset, in->dcs); if (in->pi & 0x4) { int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs); set_octet(pdu, offset, in->udl); memcpy(pdu + *offset, in->ud, ud_oct_len); *offset = *offset + ud_oct_len; } return TRUE; } static gboolean decode_submit_report(const unsigned char *pdu, int len, struct sms *out) { int offset = 0; unsigned char octet; gboolean udhi; guint8 uninitialized_var(fcs); guint8 pi; struct sms_scts *scts; guint8 pid = 0; guint8 dcs = 0; guint8 udl = 0; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; udhi = is_bit_set(octet, 6); if (!next_octet(pdu, len, &offset, &octet)) return FALSE; /* * At this point we don't know whether this is an ACK or an ERROR. * FCS can only have values 0x80 and above, as 0x00 - 0x7F are reserved * according to 3GPP 23.040. For PI, the values can be only in * bit 0, 1, 2 with the 7th bit reserved as an extension. Since * bits 3-6 are not used, assume no extension is feasible, so if the * value of this octet is >= 0x80, this is an FCS and thus an error * report tpdu. */ if (octet >= 0x80) { out->type = SMS_TYPE_SUBMIT_REPORT_ERROR; fcs = octet; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; scts = &out->submit_err_report.scts; } else { scts = &out->submit_ack_report.scts; out->type = SMS_TYPE_SUBMIT_REPORT_ACK; } pi = octet & 0x07; if (!sms_decode_scts(pdu, len, &offset, scts)) return FALSE; if (pi & 0x01) { if (!next_octet(pdu, len, &offset, &pid)) return FALSE; } if (pi & 0x02) { if (!next_octet(pdu, len, &offset, &dcs)) return FALSE; } if (out->type == SMS_TYPE_SUBMIT_REPORT_ERROR) { out->submit_err_report.udhi = udhi; out->submit_err_report.fcs = fcs; out->submit_err_report.pi = pi; out->submit_err_report.pid = pid; out->submit_err_report.dcs = dcs; } else { out->submit_ack_report.udhi = udhi; out->submit_ack_report.pi = pi; out->submit_ack_report.pid = pid; out->submit_ack_report.dcs = dcs; } if (pi & 0x04) { int expected; if (!next_octet(pdu, len, &offset, &udl)) return FALSE; expected = sms_udl_in_bytes(udl, dcs); if ((len - offset) < expected) return FALSE; if (out->type == SMS_TYPE_SUBMIT_REPORT_ERROR) { out->submit_err_report.udl = udl; memcpy(out->submit_err_report.ud, pdu + offset, expected); } else { out->submit_ack_report.udl = udl; memcpy(out->submit_ack_report.ud, pdu + offset, expected); } } return TRUE; } static gboolean encode_status_report(const struct sms_status_report *in, unsigned char *pdu, int *offset) { unsigned char octet; octet = 0x2; if (!in->mms) octet |= 1 << 2; if (!in->srq) octet |= 1 << 5; if (!in->udhi) octet |= 1 << 6; set_octet(pdu, offset, octet); set_octet(pdu, offset, in->mr); if (!sms_encode_address_field(&in->raddr, FALSE, pdu, offset)) return FALSE; if (!sms_encode_scts(&in->scts, pdu, offset)) return FALSE; if (!sms_encode_scts(&in->dt, pdu, offset)) return FALSE; octet = in->st; set_octet(pdu, offset, octet); if (in->pi == 0) return TRUE; set_octet(pdu, offset, in->pi); if (in->pi & 0x01) set_octet(pdu, offset, in->pid); if (in->pi & 0x02) set_octet(pdu, offset, in->dcs); if (in->pi & 0x4) { int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs); set_octet(pdu, offset, in->udl); memcpy(pdu + *offset, in->ud, ud_oct_len); *offset = *offset + ud_oct_len; } return TRUE; } static gboolean decode_status_report(const unsigned char *pdu, int len, struct sms *out) { int offset = 0; unsigned char octet; out->type = SMS_TYPE_STATUS_REPORT; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; out->status_report.mms = !is_bit_set(octet, 2); out->status_report.srq = is_bit_set(octet, 5); out->status_report.udhi = is_bit_set(octet, 6); if (!next_octet(pdu, len, &offset, &out->status_report.mr)) return FALSE; if (!sms_decode_address_field(pdu, len, &offset, FALSE, &out->status_report.raddr)) return FALSE; if (!sms_decode_scts(pdu, len, &offset, &out->status_report.scts)) return FALSE; if (!sms_decode_scts(pdu, len, &offset, &out->status_report.dt)) return FALSE; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; out->status_report.st = octet; /* * We have to be careful here, PI is labeled as Optional in 23.040 * which is different from RP-ERR & RP-ACK for both Deliver & Submit * reports */ if ((len - offset) == 0) return TRUE; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; out->status_report.pi = octet & 0x07; if (out->status_report.pi & 0x01) { if (!next_octet(pdu, len, &offset, &out->status_report.pid)) return FALSE; } if (out->status_report.pi & 0x02) { if (!next_octet(pdu, len, &offset, &out->status_report.dcs)) return FALSE; } if (out->status_report.pi & 0x04) { int expected; if (!next_octet(pdu, len, &offset, &out->status_report.udl)) return FALSE; expected = sms_udl_in_bytes(out->status_report.udl, out->status_report.dcs); if ((len - offset) < expected) return FALSE; memcpy(out->status_report.ud, pdu + offset, expected); } return TRUE; } static gboolean encode_deliver_ack_report(const struct sms_deliver_ack_report *in, unsigned char *pdu, int *offset) { unsigned char oct; oct = 0; if (in->udhi) oct |= 1 << 6; set_octet(pdu, offset, oct); set_octet(pdu, offset, in->pi); if (in->pi & 0x1) set_octet(pdu, offset, in->pid); if (in->pi & 0x2) set_octet(pdu, offset, in->dcs); if (in->pi & 0x4) { int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs); set_octet(pdu, offset, in->udl); memcpy(pdu + *offset, in->ud, ud_oct_len); *offset = *offset + ud_oct_len; } return TRUE; } static gboolean encode_deliver_err_report(const struct sms_deliver_err_report *in, unsigned char *pdu, int *offset) { unsigned char oct; oct = 0; if (in->udhi) oct |= 1 << 6; set_octet(pdu, offset, oct); set_octet(pdu, offset, in->fcs); set_octet(pdu, offset, in->pi); if (in->pi & 0x1) set_octet(pdu, offset, in->pid); if (in->pi & 0x2) set_octet(pdu, offset, in->dcs); if (in->pi & 0x4) { int ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs); set_octet(pdu, offset, in->udl); memcpy(pdu + *offset, in->ud, ud_oct_len); *offset = *offset + ud_oct_len; } return TRUE; } static gboolean decode_deliver_report(const unsigned char *pdu, int len, struct sms *out) { int offset = 0; unsigned char octet; gboolean udhi; guint8 uninitialized_var(fcs); guint8 pi; guint8 pid = 0; guint8 dcs = 0; guint8 udl = 0; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; udhi = is_bit_set(octet, 6); if (!next_octet(pdu, len, &offset, &octet)) return FALSE; /* * At this point we don't know whether this is an ACK or an ERROR. * FCS can only have values 0x80 and above, as 0x00 - 0x7F are reserved * according to 3GPP 23.040. For PI, the values can be only in * bit 0, 1, 2 with the 7th bit reserved as an extension. Since * bits 3-6 are not used, assume no extension is feasible, so if the * value of this octet is >= 0x80, this is an FCS and thus an error * report tpdu. */ if (octet >= 0x80) { out->type = SMS_TYPE_DELIVER_REPORT_ERROR; fcs = octet; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; } else { out->type = SMS_TYPE_DELIVER_REPORT_ACK; } pi = octet & 0x07; if (pi & 0x01) { if (!next_octet(pdu, len, &offset, &pid)) return FALSE; } if (pi & 0x02) { if (!next_octet(pdu, len, &offset, &dcs)) return FALSE; } if (out->type == SMS_TYPE_DELIVER_REPORT_ERROR) { out->deliver_err_report.udhi = udhi; out->deliver_err_report.fcs = fcs; out->deliver_err_report.pi = pi; out->deliver_err_report.pid = pid; out->deliver_err_report.dcs = dcs; } else { out->deliver_ack_report.udhi = udhi; out->deliver_ack_report.pi = pi; out->deliver_ack_report.pid = pid; out->deliver_ack_report.dcs = dcs; } if (pi & 0x04) { int expected; if (!next_octet(pdu, len, &offset, &udl)) return FALSE; expected = sms_udl_in_bytes(udl, dcs); if ((len - offset) < expected) return FALSE; if (out->type == SMS_TYPE_DELIVER_REPORT_ERROR) { out->deliver_err_report.udl = udl; memcpy(out->deliver_err_report.ud, pdu + offset, expected); } else { out->deliver_ack_report.udl = udl; memcpy(out->deliver_ack_report.ud, pdu + offset, expected); } } return TRUE; } static gboolean encode_submit(const struct sms_submit *in, unsigned char *pdu, int *offset) { unsigned char octet; int ud_oct_len; /* SMS Submit */ octet = 0x1; if (in->rd) octet |= 1 << 2; if (in->rp) octet |= 1 << 7; octet |= in->vpf << 3; if (in->udhi) octet |= 1 << 6; if (in->srr) octet |= 1 << 5; set_octet(pdu, offset, octet); set_octet(pdu, offset, in->mr); if (sms_encode_address_field(&in->daddr, FALSE, pdu, offset) == FALSE) return FALSE; set_octet(pdu, offset, in->pid); set_octet(pdu, offset, in->dcs); if (!encode_validity_period(&in->vp, in->vpf, pdu, offset)) return FALSE; set_octet(pdu, offset, in->udl); ud_oct_len = sms_udl_in_bytes(in->udl, in->dcs); memcpy(pdu + *offset, in->ud, ud_oct_len); *offset = *offset + ud_oct_len; return TRUE; } gboolean sms_decode_unpacked_stk_pdu(const unsigned char *pdu, int len, struct sms *out) { unsigned char octet; int offset = 0; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; if ((octet & 0x3) != 1) return FALSE; out->type = SMS_TYPE_SUBMIT; out->submit.rd = is_bit_set(octet, 2); out->submit.vpf = bit_field(octet, 3, 2); out->submit.rp = is_bit_set(octet, 7); out->submit.udhi = is_bit_set(octet, 6); out->submit.srr = is_bit_set(octet, 5); if (!next_octet(pdu, len, &offset, &out->submit.mr)) return FALSE; if (!sms_decode_address_field(pdu, len, &offset, FALSE, &out->submit.daddr)) return FALSE; if (!next_octet(pdu, len, &offset, &out->submit.pid)) return FALSE; if (!next_octet(pdu, len, &offset, &out->submit.dcs)) return FALSE; /* Now we override the DCS */ out->submit.dcs = 0xF0; if (!decode_validity_period(pdu, len, &offset, out->submit.vpf, &out->submit.vp)) return FALSE; if (!next_octet(pdu, len, &offset, &out->submit.udl)) return FALSE; if ((len - offset) < out->submit.udl) return FALSE; pack_7bit_own_buf(pdu + offset, out->submit.udl, 0, FALSE, NULL, 0, out->submit.ud); return TRUE; } static gboolean decode_submit(const unsigned char *pdu, int len, struct sms *out) { unsigned char octet; int offset = 0; int expected; out->type = SMS_TYPE_SUBMIT; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; out->submit.rd = is_bit_set(octet, 2); out->submit.vpf = bit_field(octet, 3, 2); out->submit.rp = is_bit_set(octet, 7); out->submit.udhi = is_bit_set(octet, 6); out->submit.srr = is_bit_set(octet, 5); if (!next_octet(pdu, len, &offset, &out->submit.mr)) return FALSE; if (!sms_decode_address_field(pdu, len, &offset, FALSE, &out->submit.daddr)) return FALSE; if (!next_octet(pdu, len, &offset, &out->submit.pid)) return FALSE; if (!next_octet(pdu, len, &offset, &out->submit.dcs)) return FALSE; if (!decode_validity_period(pdu, len, &offset, out->submit.vpf, &out->submit.vp)) return FALSE; if (!next_octet(pdu, len, &offset, &out->submit.udl)) return FALSE; expected = sms_udl_in_bytes(out->submit.udl, out->submit.dcs); if ((len - offset) < expected) return FALSE; if (expected > (int) sizeof(out->submit.ud)) return FALSE; memcpy(out->submit.ud, pdu + offset, expected); return TRUE; } static gboolean encode_command(const struct sms_command *in, unsigned char *pdu, int *offset) { unsigned char octet; octet = 0x2; if (in->udhi) octet |= 1 << 6; if (in->srr) octet |= 1 << 5; set_octet(pdu, offset, octet); set_octet(pdu, offset, in->mr); set_octet(pdu, offset, in->pid); octet = in->ct; set_octet(pdu, offset, octet); set_octet(pdu, offset, in->mn); if (!sms_encode_address_field(&in->daddr, FALSE, pdu, offset)) return FALSE; set_octet(pdu, offset, in->cdl); memcpy(pdu + *offset, in->cd, in->cdl); *offset = *offset + in->cdl; return TRUE; } static gboolean decode_command(const unsigned char *pdu, int len, struct sms *out) { unsigned char octet; int offset = 0; out->type = SMS_TYPE_COMMAND; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; out->command.udhi = is_bit_set(octet, 6); out->command.srr = is_bit_set(octet, 5); if (!next_octet(pdu, len, &offset, &out->command.mr)) return FALSE; if (!next_octet(pdu, len, &offset, &out->command.pid)) return FALSE; if (!next_octet(pdu, len, &offset, &octet)) return FALSE; out->command.ct = octet; if (!next_octet(pdu, len, &offset, &out->command.mn)) return FALSE; if (!sms_decode_address_field(pdu, len, &offset, FALSE, &out->command.daddr)) return FALSE; if (!next_octet(pdu, len, &offset, &out->command.cdl)) return FALSE; if ((len - offset) < out->command.cdl) return FALSE; memcpy(out->command.cd, pdu + offset, out->command.cdl); return TRUE; } /* Buffer must be at least 164 (tpud) + 12 (SC address) bytes long */ gboolean sms_encode(const struct sms *in, int *len, int *tpdu_len, unsigned char *pdu) { int offset = 0; int tpdu_start; if (in->type == SMS_TYPE_DELIVER || in->type == SMS_TYPE_SUBMIT || in->type == SMS_TYPE_COMMAND || in->type == SMS_TYPE_STATUS_REPORT) if (!sms_encode_address_field(&in->sc_addr, TRUE, pdu, &offset)) return FALSE; tpdu_start = offset; switch (in->type) { case SMS_TYPE_DELIVER: if (encode_deliver(&in->deliver, pdu, &offset) == FALSE) return FALSE; break; case SMS_TYPE_DELIVER_REPORT_ACK: if (!encode_deliver_ack_report(&in->deliver_ack_report, pdu, &offset)) return FALSE; break; case SMS_TYPE_DELIVER_REPORT_ERROR: if (!encode_deliver_err_report(&in->deliver_err_report, pdu, &offset)) return FALSE; break; case SMS_TYPE_STATUS_REPORT: if (!encode_status_report(&in->status_report, pdu, &offset)) return FALSE; break; case SMS_TYPE_SUBMIT: if (!encode_submit(&in->submit, pdu, &offset)) return FALSE; break; case SMS_TYPE_SUBMIT_REPORT_ACK: if (!encode_submit_ack_report(&in->submit_ack_report, pdu, &offset)) return FALSE; break; case SMS_TYPE_SUBMIT_REPORT_ERROR: if (!encode_submit_err_report(&in->submit_err_report, pdu, &offset)) return FALSE; break; case SMS_TYPE_COMMAND: if (!encode_command(&in->command, pdu, &offset)) return FALSE; break; default: return FALSE; }; if (tpdu_len) *tpdu_len = offset - tpdu_start; if (len) *len = offset; return TRUE; } gboolean sms_decode(const unsigned char *pdu, int len, gboolean outgoing, int tpdu_len, struct sms *out) { unsigned char type; int offset = 0; if (out == NULL) return FALSE; if (len == 0) return FALSE; memset(out, 0, sizeof(*out)); if (tpdu_len < len) { if (!sms_decode_address_field(pdu, len, &offset, TRUE, &out->sc_addr)) return FALSE; } if ((len - offset) < tpdu_len) return FALSE; /* 23.040 9.2.3.1 */ type = pdu[offset] & 0x3; if (outgoing) type |= 0x4; pdu = pdu + offset; switch (type) { case 0: return decode_deliver(pdu, tpdu_len, out); case 1: return decode_submit_report(pdu, tpdu_len, out); case 2: return decode_status_report(pdu, tpdu_len, out); case 3: /* According to 9.2.3.1, Reserved treated as deliver */ return decode_deliver(pdu, tpdu_len, out); case 4: return decode_deliver_report(pdu, tpdu_len, out); case 5: return decode_submit(pdu, tpdu_len, out); case 6: return decode_command(pdu, tpdu_len, out); } return FALSE; } const guint8 *sms_extract_common(const struct sms *sms, gboolean *out_udhi, guint8 *out_dcs, guint8 *out_udl, guint8 *out_max) { const guint8 *ud = NULL; guint8 uninitialized_var(udl); guint8 uninitialized_var(max); gboolean uninitialized_var(udhi); guint8 uninitialized_var(dcs); switch (sms->type) { case SMS_TYPE_DELIVER: udhi = sms->deliver.udhi; ud = sms->deliver.ud; udl = sms->deliver.udl; dcs = sms->deliver.dcs; max = sizeof(sms->deliver.ud); break; case SMS_TYPE_DELIVER_REPORT_ACK: udhi = sms->deliver_ack_report.udhi; ud = sms->deliver_ack_report.ud; udl = sms->deliver_ack_report.udl; dcs = sms->deliver_ack_report.dcs; max = sizeof(sms->deliver_ack_report.ud); break; case SMS_TYPE_DELIVER_REPORT_ERROR: udhi = sms->deliver_err_report.udhi; ud = sms->deliver_err_report.ud; udl = sms->deliver_err_report.udl; dcs = sms->deliver_err_report.dcs; max = sizeof(sms->deliver_err_report.ud); break; case SMS_TYPE_STATUS_REPORT: udhi = sms->status_report.udhi; ud = sms->status_report.ud; udl = sms->status_report.udl; dcs = sms->status_report.dcs; max = sizeof(sms->status_report.ud); break; case SMS_TYPE_SUBMIT: udhi = sms->submit.udhi; ud = sms->submit.ud; udl = sms->submit.udl; dcs = sms->submit.dcs; max = sizeof(sms->submit.ud); break; case SMS_TYPE_SUBMIT_REPORT_ACK: udhi = sms->submit_ack_report.udhi; ud = sms->submit_ack_report.ud; udl = sms->submit_ack_report.udl; dcs = sms->submit_ack_report.dcs; max = sizeof(sms->submit_ack_report.ud); break; case SMS_TYPE_SUBMIT_REPORT_ERROR: udhi = sms->submit_err_report.udhi; ud = sms->submit_err_report.ud; udl = sms->submit_err_report.udl; dcs = sms->submit_err_report.dcs; max = sizeof(sms->submit_err_report.ud); break; case SMS_TYPE_COMMAND: udhi = sms->command.udhi; ud = sms->command.cd; udl = sms->command.cdl; dcs = 0; max = sizeof(sms->command.cd); break; }; if (ud == NULL) return NULL; if (out_udhi) *out_udhi = udhi; if (out_dcs) *out_dcs = dcs; if (out_udl) *out_udl = udl; if (out_max) *out_max = max; return ud; } static gboolean verify_udh(const guint8 *hdr, guint8 max_len) { guint8 max_offset; guint8 offset; /* Must have at least one information-element if udhi is true */ if (hdr[0] < 2) return FALSE; if (hdr[0] >= max_len) return FALSE; /* * According to 23.040: If the length of the User Data Header is * such that there are too few or too many octets in the final * Information Element then the whole User Data Header shall be * ignored. */ max_offset = hdr[0] + 1; offset = 1; do { if ((offset + 2) > max_offset) return FALSE; if ((offset + 2 + hdr[offset + 1]) > max_offset) return FALSE; offset = offset + 2 + hdr[offset + 1]; } while (offset < max_offset); if (offset != max_offset) return FALSE; return TRUE; } gboolean sms_udh_iter_init(const struct sms *sms, struct sms_udh_iter *iter) { gboolean udhi = FALSE; const guint8 *hdr; guint8 udl; guint8 dcs; guint8 max_len; guint8 max_ud_len; hdr = sms_extract_common(sms, &udhi, &dcs, &udl, &max_ud_len); if (hdr == NULL) return FALSE; if (!udhi) return FALSE; if (sms->type == SMS_TYPE_COMMAND) max_len = udl; else max_len = sms_udl_in_bytes(udl, dcs); /* Can't actually store the HDL + IEI / IEL */ if (max_len < 3) return FALSE; if (max_len > max_ud_len) return FALSE; if (!verify_udh(hdr, max_len)) return FALSE; iter->data = hdr; iter->offset = 1; return TRUE; } gboolean sms_udh_iter_init_from_cbs(const struct cbs *cbs, struct sms_udh_iter *iter) { gboolean udhi = FALSE; const guint8 *hdr; guint8 max_ud_len; cbs_dcs_decode(cbs->dcs, &udhi, NULL, NULL, NULL, NULL, NULL); if (!udhi) return FALSE; hdr = cbs->ud; max_ud_len = 82; /* Must have at least one information-element if udhi is true */ if (hdr[0] < 2) return FALSE; if (hdr[0] >= max_ud_len) return FALSE; if (!verify_udh(hdr, max_ud_len)) return FALSE; iter->data = hdr; iter->offset = 1; return TRUE; } guint8 sms_udh_iter_get_udh_length(struct sms_udh_iter *iter) { return iter->data[0]; } const guint8 *sms_udh_iter_get_ud_after_header(struct sms_udh_iter *iter) { return iter->data + iter->data[0] + 1; } enum sms_iei sms_udh_iter_get_ie_type(struct sms_udh_iter *iter) { if (iter->offset > iter->data[0]) return SMS_IEI_INVALID; return (enum sms_iei) iter->data[iter->offset]; } guint8 sms_udh_iter_get_ie_length(struct sms_udh_iter *iter) { guint8 ie_len; ie_len = iter->data[iter->offset + 1]; return ie_len; } void sms_udh_iter_get_ie_data(struct sms_udh_iter *iter, guint8 *data) { guint8 ie_len; ie_len = iter->data[iter->offset + 1]; memcpy(data, &iter->data[iter->offset + 2], ie_len); } gboolean sms_udh_iter_has_next(struct sms_udh_iter *iter) { guint8 total_len = iter->data[0]; guint8 cur_ie_len = iter->data[iter->offset + 1]; if ((iter->offset + 2 + cur_ie_len) > total_len) return FALSE; return TRUE; } gboolean sms_udh_iter_next(struct sms_udh_iter *iter) { if (iter->offset > iter->data[0]) return FALSE; iter->offset = iter->offset + 2 + iter->data[iter->offset + 1]; if (iter->offset > iter->data[0]) return FALSE; return TRUE; } /* * Returns both forms of time. The time_t value returns the time in local * timezone. The struct tm is filled out with the remote time information */ time_t sms_scts_to_time(const struct sms_scts *scts, struct tm *remote) { struct tm t; time_t ret; t.tm_sec = scts->second; t.tm_min = scts->minute; t.tm_hour = scts->hour; t.tm_mday = scts->day; t.tm_mon = scts->month - 1; t.tm_isdst = -1; if (scts->year > 80) t.tm_year = scts->year; else t.tm_year = scts->year + 100; ret = mktime(&t); /* Adjust local time by the local timezone information */ ret += t.tm_gmtoff; /* Set the proper timezone on the remote side */ t.tm_gmtoff = scts->timezone * 15 * 60; /* Now adjust by the remote timezone information */ ret -= t.tm_gmtoff; if (remote) memcpy(remote, &t, sizeof(struct tm)); return ret; } void sms_address_from_string(struct sms_address *addr, const char *str) { addr->numbering_plan = SMS_NUMBERING_PLAN_ISDN; if (str[0] == '+') { addr->number_type = SMS_NUMBER_TYPE_INTERNATIONAL; strcpy(addr->address, str + 1); } else { addr->number_type = SMS_NUMBER_TYPE_UNKNOWN; strcpy(addr->address, str); } } const char *sms_address_to_string(const struct sms_address *addr) { static char buffer[64]; if (addr->number_type == SMS_NUMBER_TYPE_INTERNATIONAL && (strlen(addr->address) > 0) && addr->address[0] != '+') { buffer[0] = '+'; strcpy(buffer + 1, addr->address); } else { strcpy(buffer, addr->address); } return buffer; } static gboolean extract_app_port_common(struct sms_udh_iter *iter, int *dst, int *src, gboolean *is_8bit) { enum sms_iei iei; guint8 addr_hdr[4]; int srcport = -1; int dstport = -1; gboolean uninitialized_var(is_addr_8bit); /* * According to the specification, we have to use the last * useable header. Also, we have to ignore ports that are reserved: * A receiving entity shall ignore (i.e. skip over and commence * processing at the next information element) any information element * where the value of the Information-Element-Data is Reserved or not * supported. */ while ((iei = sms_udh_iter_get_ie_type(iter)) != SMS_IEI_INVALID) { switch (iei) { case SMS_IEI_APPLICATION_ADDRESS_8BIT: if (sms_udh_iter_get_ie_length(iter) != 2) break; sms_udh_iter_get_ie_data(iter, addr_hdr); if (addr_hdr[0] < 240) break; if (addr_hdr[1] < 240) break; dstport = addr_hdr[0]; srcport = addr_hdr[1]; is_addr_8bit = TRUE; break; case SMS_IEI_APPLICATION_ADDRESS_16BIT: if (sms_udh_iter_get_ie_length(iter) != 4) break; sms_udh_iter_get_ie_data(iter, addr_hdr); if (((addr_hdr[0] << 8) | addr_hdr[1]) > 49151) break; dstport = (addr_hdr[0] << 8) | addr_hdr[1]; srcport = (addr_hdr[2] << 8) | addr_hdr[3]; is_addr_8bit = FALSE; break; default: break; } sms_udh_iter_next(iter); } if (dstport == -1 || srcport == -1) return FALSE; if (dst) *dst = dstport; if (src) *src = srcport; if (is_8bit) *is_8bit = is_addr_8bit; return TRUE; } gboolean sms_extract_app_port(const struct sms *sms, int *dst, int *src, gboolean *is_8bit) { struct sms_udh_iter iter; if (!sms_udh_iter_init(sms, &iter)) return FALSE; return extract_app_port_common(&iter, dst, src, is_8bit); } gboolean sms_extract_concatenation(const struct sms *sms, guint16 *ref_num, guint8 *max_msgs, guint8 *seq_num) { struct sms_udh_iter iter; enum sms_iei iei; guint8 concat_hdr[4]; guint16 uninitialized_var(rn); guint8 uninitialized_var(max), uninitialized_var(seq); gboolean concatenated = FALSE; /* * We must ignore the entire user_data header here: * If the length of the User Data Header is such that there * are too few or too many octets in the final Information * Element then the whole User Data Header shall be ignored. */ if (!sms_udh_iter_init(sms, &iter)) return FALSE; /* * According to the specification, we have to use the last * useable header: * In the event that IEs determined as not repeatable are * duplicated, the last occurrence of the IE shall be used. * In the event that two or more IEs occur which have mutually * exclusive meanings (e.g. an 8bit port address and a 16bit * port address), then the last occurring IE shall be used. */ while ((iei = sms_udh_iter_get_ie_type(&iter)) != SMS_IEI_INVALID) { switch (iei) { case SMS_IEI_CONCATENATED_8BIT: if (sms_udh_iter_get_ie_length(&iter) != 3) break; sms_udh_iter_get_ie_data(&iter, concat_hdr); if (concat_hdr[1] == 0) break; if (concat_hdr[2] == 0 || concat_hdr[2] > concat_hdr[1]) break; rn = concat_hdr[0]; max = concat_hdr[1]; seq = concat_hdr[2]; concatenated = TRUE; break; case SMS_IEI_CONCATENATED_16BIT: if (sms_udh_iter_get_ie_length(&iter) != 4) break; sms_udh_iter_get_ie_data(&iter, concat_hdr); if (concat_hdr[2] == 0) break; if (concat_hdr[3] == 0 || concat_hdr[3] > concat_hdr[2]) break; rn = (concat_hdr[0] << 8) | concat_hdr[1]; max = concat_hdr[2]; seq = concat_hdr[3]; concatenated = TRUE; break; default: break; } sms_udh_iter_next(&iter); } if (!concatenated) return FALSE; if (ref_num) *ref_num = rn; if (max_msgs) *max_msgs = max; if (seq_num) *seq_num = seq; return TRUE; } gboolean sms_extract_language_variant(const struct sms *sms, guint8 *locking, guint8 *single) { struct sms_udh_iter iter; enum sms_iei iei; guint8 variant; /* * We must ignore the entire user_data header here: * If the length of the User Data Header is such that there * are too few or too many octets in the final Information * Element then the whole User Data Header shall be ignored. */ if (!sms_udh_iter_init(sms, &iter)) return FALSE; /* * According to the specification, we have to use the last * useable header: * In the event that IEs determined as not repeatable are * duplicated, the last occurrence of the IE shall be used. * In the event that two or more IEs occur which have mutually * exclusive meanings (e.g. an 8bit port address and a 16bit * port address), then the last occurring IE shall be used. */ while ((iei = sms_udh_iter_get_ie_type(&iter)) != SMS_IEI_INVALID) { switch (iei) { case SMS_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT: if (sms_udh_iter_get_ie_length(&iter) != 1) break; sms_udh_iter_get_ie_data(&iter, &variant); if (single) *single = variant; break; case SMS_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT: if (sms_udh_iter_get_ie_length(&iter) != 1) break; sms_udh_iter_get_ie_data(&iter, &variant); if (locking) *locking = variant; break; default: break; } sms_udh_iter_next(&iter); } return TRUE; } /*! * Decodes a list of SMSes that contain a datagram. The list must be * sorted in order of the sequence number. This function assumes that * all fragments are coded using 8-bit character set. * * Returns a pointer to a newly allocated array or NULL if the * conversion could not be performed */ unsigned char *sms_decode_datagram(GSList *sms_list, long *out_len) { GSList *l; const struct sms *sms; unsigned char *buf; long len = 0; for (l = sms_list; l; l = l->next) { guint8 taken = 0; guint8 udl = 0; const guint8 *ud; struct sms_udh_iter iter; sms = l->data; ud = sms_extract_common(sms, NULL, NULL, &udl, NULL); if (ud == NULL) return NULL; /* * Note we do this because we must check whether the UDH * is properly formatted. If not, the entire UDH is ignored */ if (sms_udh_iter_init(sms, &iter)) taken = sms_udh_iter_get_udh_length(&iter) + 1; len += udl - taken; } /* Data is probably in headers we can't understand */ if (len == 0) return NULL; buf = g_try_new(unsigned char, len); if (buf == NULL) return NULL; len = 0; for (l = sms_list; l; l = l->next) { guint8 taken = 0; guint8 udl = 0; const guint8 *ud; struct sms_udh_iter iter; sms = l->data; ud = sms_extract_common(sms, NULL, NULL, &udl, NULL); if (sms_udh_iter_init(sms, &iter)) taken = sms_udh_iter_get_udh_length(&iter) + 1; memcpy(buf + len, ud + taken, udl - taken); len += udl - taken; } if (out_len) *out_len = len; return buf; } static inline int sms_text_capacity_gsm(int max, int offset) { return max - (offset * 8 + 6) / 7; } /*! * Decodes a list of SMSes that contain a text in either 7bit or UCS2 encoding. * The list must be sorted in order of the sequence number. This function * assumes that all fragments have a proper DCS. * * Returns a pointer to a newly allocated string or NULL if the conversion * failed. */ char *sms_decode_text(GSList *sms_list) { GSList *l; GString *str = NULL; GByteArray *utf16 = NULL; const struct sms *sms; int guess_size = g_slist_length(sms_list); char *text = NULL; if (guess_size == 1) guess_size = 160; else guess_size = (guess_size - 1) * 160; for (l = sms_list; l; l = l->next) { guint8 taken = 0; guint8 dcs = 0; guint8 udl = 0; enum sms_charset charset; int udl_in_bytes; const guint8 *ud; struct sms_udh_iter iter; sms = l->data; ud = sms_extract_common(sms, NULL, &dcs, &udl, NULL); if (!sms_mwi_dcs_decode(dcs, NULL, &charset, NULL, NULL) && !sms_dcs_decode(dcs, NULL, &charset, NULL, NULL)) continue; if (charset == SMS_CHARSET_8BIT) continue; if (sms_udh_iter_init(sms, &iter)) taken = sms_udh_iter_get_udh_length(&iter) + 1; udl_in_bytes = sms_udl_in_bytes(udl, dcs); if (udl_in_bytes == taken) continue; if (charset == SMS_CHARSET_7BIT) { unsigned char buf[160]; long written; guint8 locking_shift = 0; guint8 single_shift = 0; int max_chars = sms_text_capacity_gsm(udl, taken); char *converted; if (unpack_7bit_own_buf(ud + taken, udl_in_bytes - taken, taken, FALSE, max_chars, &written, 0, buf) == NULL) continue; /* Take care of improperly split fragments */ if (buf[written-1] == 0x1b) written = written - 1; sms_extract_language_variant(sms, &locking_shift, &single_shift); /* * If language is not defined in 3GPP TS 23.038, * implementations are instructed to ignore it */ if (locking_shift > SMS_ALPHABET_PORTUGUESE) locking_shift = GSM_DIALECT_DEFAULT; if (single_shift > SMS_ALPHABET_PORTUGUESE) single_shift = GSM_DIALECT_DEFAULT; converted = convert_gsm_to_utf8_with_lang(buf, written, NULL, NULL, 0, locking_shift, single_shift); if (converted) { if (str == NULL) str = g_string_sized_new(guess_size); g_string_append(str, converted); g_free(converted); } } else { /* * According to the spec: A UCS2 character shall not be * split in the middle; if the length of the User Data * Header is odd, the maximum length of the whole TP-UD * field is 139 octets */ gssize num_octects = (udl_in_bytes - taken) & ~1u; if (utf16 == NULL) utf16 = g_byte_array_sized_new(guess_size); g_byte_array_append(utf16, ud + taken, num_octects); } } if (str != NULL) { text = g_string_free(str, FALSE); } else if (utf16 != NULL) { text = g_convert((gchar *) utf16->data, utf16->len, "UTF-8//TRANSLIT", "UTF-16BE", NULL, NULL, NULL); g_byte_array_free(utf16, TRUE); } return text; } static int sms_serialize(unsigned char *buf, const struct sms *sms) { int len, tpdu_len; sms_encode(sms, &len, &tpdu_len, buf + 1); buf[0] = tpdu_len; return len + 1; } static gboolean sms_deserialize(const unsigned char *buf, struct sms *sms, int len) { if (len < 1) return FALSE; return sms_decode(buf + 1, len - 1, FALSE, buf[0], sms); } static gboolean sms_deserialize_outgoing(const unsigned char *buf, struct sms *sms, int len) { if (len < 1) return FALSE; return sms_decode(buf + 1, len - 1, TRUE, buf[0], sms); } static gboolean sms_assembly_extract_address(const char *straddr, struct sms_address *out) { unsigned char pdu[12]; long len; int offset = 0; if (decode_hex_own_buf(straddr, -1, &len, 0, pdu) == NULL) return FALSE; return sms_decode_address_field(pdu, len, &offset, FALSE, out); } gboolean sms_address_to_hex_string(const struct sms_address *in, char *straddr) { unsigned char pdu[12]; int offset = 0; if (sms_encode_address_field(in, FALSE, pdu, &offset) == FALSE) return FALSE; if (encode_hex_own_buf(pdu, offset, 0, straddr) == NULL) return FALSE; straddr[offset * 2 + 1] = '\0'; return TRUE; } static void sms_assembly_load(struct sms_assembly *assembly, const struct dirent *dir) { struct sms_address addr; DECLARE_SMS_ADDR_STR(straddr); guint16 ref; guint8 max; guint8 seq; char *path; int len; struct stat segment_stat; struct dirent **segments; char *endp; int r; int i; unsigned char buf[177]; struct sms segment; if (dir->d_type != DT_DIR) return; /* Max of SMS address size is 12 bytes, hex encoded */ if (sscanf(dir->d_name, SMS_ADDR_FMT "-%hi-%hhi", straddr, &ref, &max) < 3) return; if (sms_assembly_extract_address(straddr, &addr) == FALSE) return; path = g_strdup_printf(SMS_BACKUP_PATH "/%s", assembly->imsi, dir->d_name); len = scandir(path, &segments, NULL, versionsort); g_free(path); if (len < 0) return; for (i = 0; i < len; i++) { if (segments[i]->d_type != DT_REG) continue; seq = strtol(segments[i]->d_name, &endp, 10); if (*endp != '\0') continue; r = read_file(buf, sizeof(buf), SMS_BACKUP_PATH "/%s/%s", assembly->imsi, dir->d_name, segments[i]->d_name); if (r < 0) continue; if (!sms_deserialize(buf, &segment, r)) continue; path = g_strdup_printf(SMS_BACKUP_PATH "/%s/%s", assembly->imsi, dir->d_name, segments[i]->d_name); r = stat(path, &segment_stat); g_free(path); if (r != 0) continue; /* Errors cannot occur here */ sms_assembly_add_fragment_backup(assembly, &segment, segment_stat.st_mtime, &addr, ref, max, seq, FALSE); } for (i = 0; i < len; i++) free(segments[i]); free(segments); } static gboolean sms_assembly_store(struct sms_assembly *assembly, struct sms_assembly_node *node, const struct sms *sms, guint8 seq) { unsigned char buf[177]; int len; DECLARE_SMS_ADDR_STR(straddr); if (assembly->imsi == NULL) return FALSE; if (sms_address_to_hex_string(&node->addr, straddr) == FALSE) return FALSE; len = sms_serialize(buf, sms); if (write_file(buf, len, SMS_BACKUP_MODE, SMS_BACKUP_PATH_FILE, assembly->imsi, straddr, node->ref, node->max_fragments, seq) != len) return FALSE; return TRUE; } static void sms_assembly_backup_free(struct sms_assembly *assembly, struct sms_assembly_node *node) { char *path; int seq; DECLARE_SMS_ADDR_STR(straddr); if (assembly->imsi == NULL) return; if (sms_address_to_hex_string(&node->addr, straddr) == FALSE) return; for (seq = 0; seq < node->max_fragments; seq++) { int offset = seq / 32; int bit = 1 << (seq % 32); if (node->bitmap[offset] & bit) { path = g_strdup_printf(SMS_BACKUP_PATH_FILE, assembly->imsi, straddr, node->ref, node->max_fragments, seq); unlink(path); g_free(path); } } path = g_strdup_printf(SMS_BACKUP_PATH_DIR, assembly->imsi, straddr, node->ref, node->max_fragments); rmdir(path); g_free(path); } struct sms_assembly *sms_assembly_new(const char *imsi) { struct sms_assembly *ret = g_new0(struct sms_assembly, 1); char *path; struct dirent **entries; int len; if (imsi) { ret->imsi = imsi; /* Restore state from backup */ path = g_strdup_printf(SMS_BACKUP_PATH, imsi); len = scandir(path, &entries, NULL, alphasort); g_free(path); if (len < 0) return ret; while (len--) { sms_assembly_load(ret, entries[len]); free(entries[len]); } free(entries); } return ret; } void sms_assembly_free(struct sms_assembly *assembly) { GSList *l; for (l = assembly->assembly_list; l; l = l->next) { struct sms_assembly_node *node = l->data; g_slist_foreach(node->fragment_list, (GFunc) g_free, 0); g_slist_free(node->fragment_list); g_free(node); } g_slist_free(assembly->assembly_list); g_free(assembly); } GSList *sms_assembly_add_fragment(struct sms_assembly *assembly, const struct sms *sms, time_t ts, const struct sms_address *addr, guint16 ref, guint8 max, guint8 seq) { return sms_assembly_add_fragment_backup(assembly, sms, ts, addr, ref, max, seq, TRUE); } static GSList *sms_assembly_add_fragment_backup(struct sms_assembly *assembly, const struct sms *sms, time_t ts, const struct sms_address *addr, guint16 ref, guint8 max, guint8 seq, gboolean backup) { unsigned int offset = seq / 32; unsigned int bit = 1 << (seq % 32); GSList *l; GSList *prev; struct sms *newsms; struct sms_assembly_node *node; GSList *completed; unsigned int position; unsigned int i; unsigned int j; prev = NULL; for (l = assembly->assembly_list; l; prev = l, l = l->next) { node = l->data; if (node->addr.number_type != addr->number_type) continue; if (node->addr.numbering_plan != addr->numbering_plan) continue; if (strcmp(node->addr.address, addr->address)) continue; if (ref != node->ref) continue; /* * Message Reference and address the same, but max is not * ignore the SMS completely */ if (max != node->max_fragments) return NULL; /* Now check if we already have this seq number */ if (node->bitmap[offset] & bit) return NULL; /* * Iterate over the bitmap to find in which position * should the fragment be inserted -- basically we * walk each bit in the bitmap until the bit we care * about (offset:bit) and count which are stored -- * that gives us in which position we have to insert. */ position = 0; for (i = 0; i < offset; i++) for (j = 0; j < 32; j++) if (node->bitmap[i] & (1 << j)) position += 1; for (j = 1; j < bit; j = j << 1) if (node->bitmap[offset] & j) position += 1; goto out; } node = g_new0(struct sms_assembly_node, 1); memcpy(&node->addr, addr, sizeof(struct sms_address)); node->ts = ts; node->ref = ref; node->max_fragments = max; assembly->assembly_list = g_slist_prepend(assembly->assembly_list, node); prev = NULL; l = assembly->assembly_list; position = 0; out: newsms = g_new(struct sms, 1); memcpy(newsms, sms, sizeof(struct sms)); node->fragment_list = g_slist_insert(node->fragment_list, newsms, position); node->bitmap[offset] |= bit; node->num_fragments += 1; if (node->num_fragments < node->max_fragments) { if (backup) sms_assembly_store(assembly, node, sms, seq); return NULL; } completed = node->fragment_list; sms_assembly_backup_free(assembly, node); if (prev) prev->next = l->next; else assembly->assembly_list = l->next; g_free(node); g_slist_free_1(l); return completed; } /*! * Expires all incomplete messages that have been received at time prior * to one given by before argument. The fragment list is freed and the * SMSes are vaporized. */ void sms_assembly_expire(struct sms_assembly *assembly, time_t before) { GSList *cur; GSList *prev; GSList *tmp; prev = NULL; cur = assembly->assembly_list; while (cur) { struct sms_assembly_node *node = cur->data; if (node->ts > before) { prev = cur; cur = cur->next; continue; } sms_assembly_backup_free(assembly, node); g_slist_foreach(node->fragment_list, (GFunc) g_free, 0); g_slist_free(node->fragment_list); g_free(node); if (prev) prev->next = cur->next; else assembly->assembly_list = cur->next; tmp = cur; cur = cur->next; g_slist_free_1(tmp); } } static gboolean sha1_equal(gconstpointer v1, gconstpointer v2) { return memcmp(v1, v2, SMS_MSGID_LEN) == 0; } static guint sha1_hash(gconstpointer v) { guint h; memcpy(&h, v, sizeof(h)); return h; } static void sr_assembly_load_backup(GHashTable *assembly_table, const char *imsi, const struct dirent *addr_dir) { struct sms_address addr; DECLARE_SMS_ADDR_STR(straddr); struct id_table_node *node; GHashTable *id_table; int r; char *assembly_table_key; unsigned int *id_table_key; char msgid_str[SMS_MSGID_LEN * 2 + 1]; unsigned char msgid[SMS_MSGID_LEN]; char endc; if (addr_dir->d_type != DT_REG) return; /* * All SMS-messages under the same IMSI-code are * included in the same directory. * So, SMS-address and message ID are included in the same file name * Max of SMS address size is 12 bytes, hex encoded * Max of SMS SHA1 hash is 20 bytes, hex encoded */ if (sscanf(addr_dir->d_name, SMS_ADDR_FMT "-" SMS_MSGID_FMT "%c", straddr, msgid_str, &endc) != 2) return; if (sms_assembly_extract_address(straddr, &addr) == FALSE) return; if (strlen(msgid_str) != 2 * SMS_MSGID_LEN) return; if (decode_hex_own_buf(msgid_str, 2 * SMS_MSGID_LEN, NULL, 0, msgid) == NULL) return; node = g_new0(struct id_table_node, 1); r = read_file((unsigned char *) node, sizeof(struct id_table_node), SMS_SR_BACKUP_PATH "/%s", imsi, addr_dir->d_name); if (r < 0) { g_free(node); return; } id_table = g_hash_table_lookup(assembly_table, sms_address_to_string(&addr)); /* Create hashtable keyed by the to address if required */ if (id_table == NULL) { id_table = g_hash_table_new_full(sha1_hash, sha1_equal, g_free, g_free); assembly_table_key = g_strdup(sms_address_to_string(&addr)); g_hash_table_insert(assembly_table, assembly_table_key, id_table); } /* Node ready, create key and add them to the table */ id_table_key = g_memdup(msgid, SMS_MSGID_LEN); g_hash_table_insert(id_table, id_table_key, node); } struct status_report_assembly *status_report_assembly_new(const char *imsi) { char *path; int len; struct dirent **addresses; struct status_report_assembly *ret = g_new0(struct status_report_assembly, 1); ret->assembly_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy); if (imsi) { ret->imsi = imsi; /* Restore state from backup */ path = g_strdup_printf(SMS_SR_BACKUP_PATH, imsi); len = scandir(path, &addresses, NULL, alphasort); g_free(path); if (len < 0) return ret; /* * Go through different addresses. Each address can relate to * 1-n msg_ids. */ while (len--) { sr_assembly_load_backup(ret->assembly_table, imsi, addresses[len]); g_free(addresses[len]); } g_free(addresses); } return ret; } static gboolean sr_assembly_add_fragment_backup(const char *imsi, const struct id_table_node *node, const struct sms_address *addr, const unsigned char *msgid) { int len = sizeof(struct id_table_node); DECLARE_SMS_ADDR_STR(straddr); char msgid_str[SMS_MSGID_LEN * 2 + 1]; if (imsi == NULL) return FALSE; if (sms_address_to_hex_string(addr, straddr) == FALSE) return FALSE; if (encode_hex_own_buf(msgid, SMS_MSGID_LEN, 0, msgid_str) == NULL) return FALSE; /* storagedir/%s/sms_sr/%s-%s */ if (write_file((unsigned char *) node, len, SMS_BACKUP_MODE, SMS_SR_BACKUP_PATH_FILE, imsi, straddr, msgid_str) != len) return FALSE; return TRUE; } static gboolean sr_assembly_remove_fragment_backup(const char *imsi, const struct sms_address *addr, const unsigned char *sha1) { char *path; DECLARE_SMS_ADDR_STR(straddr); char msgid_str[SMS_MSGID_LEN * 2 + 1]; if (imsi == NULL) return FALSE; if (sms_address_to_hex_string(addr, straddr) == FALSE) return FALSE; if (encode_hex_own_buf(sha1, SMS_MSGID_LEN, 0, msgid_str) == FALSE) return FALSE; path = g_strdup_printf(SMS_SR_BACKUP_PATH_FILE, imsi, straddr, msgid_str); unlink(path); g_free(path); return TRUE; } void status_report_assembly_free(struct status_report_assembly *assembly) { g_hash_table_destroy(assembly->assembly_table); g_free(assembly); } static gboolean sr_st_to_delivered(enum sms_st st, gboolean *delivered) { if (st >= SMS_ST_TEMPFINAL_CONGESTION && st <= SMS_ST_TEMPFINAL_LAST) return FALSE; if (st >= SMS_ST_TEMPORARY_CONGESTION && st <= SMS_ST_TEMPORARY_LAST) return FALSE; if (st <= SMS_ST_COMPLETED_LAST) { *delivered = TRUE; return TRUE; } if (st >= SMS_ST_PERMANENT_RP_ERROR && st <= SMS_ST_PERMANENT_LAST) { *delivered = FALSE; return TRUE; } return FALSE; } static struct id_table_node *find_by_mr_and_mark(GHashTable *id_table, unsigned char mr, GHashTableIter *out_iter, unsigned char **out_id) { unsigned int offset = mr / 32; unsigned int bit = 1 << (mr % 32); gpointer key, value; struct id_table_node *node; g_hash_table_iter_init(out_iter, id_table); while (g_hash_table_iter_next(out_iter, &key, &value)) { node = value; /* Address and MR matched */ if (node->mrs[offset] & bit) { node->mrs[offset] ^= bit; *out_id = key; return node; } } return NULL; } /* * Key (receiver address) does not exist in assembly. Some networks can change * address to international format, although address is sent in the national * format. Handle also change from national to international format. * Notify these special cases by comparing only last six digits of the assembly * addresses and received address. If address contains less than six digits, * compare only existing digits. */ static struct id_table_node *fuzzy_lookup(struct status_report_assembly *assy, const struct sms *sr, const char **out_addr, GHashTableIter *out_iter, unsigned char **out_msgid) { GHashTableIter iter_addr; gpointer key, value; const char *r_addr; r_addr = sms_address_to_string(&sr->status_report.raddr); g_hash_table_iter_init(&iter_addr, assy->assembly_table); while (g_hash_table_iter_next(&iter_addr, &key, &value)) { const char *s_addr = key; GHashTable *id_table = value; unsigned int len, r_len, s_len; unsigned int i; struct id_table_node *node; if (r_addr[0] == '+' && s_addr[0] == '+') continue; if (r_addr[0] != '+' && s_addr[0] != '+') continue; r_len = strlen(r_addr); s_len = strlen(s_addr); len = MIN(6, MIN(r_len, s_len)); for (i = 0; i < len; i++) if (s_addr[s_len - i - 1] != r_addr[r_len - i - 1]) break; /* Not all digits matched. */ if (i < len) continue; /* Address matched. Check message reference. */ node = find_by_mr_and_mark(id_table, sr->status_report.mr, out_iter, out_msgid); if (node != NULL) { *out_addr = s_addr; return node; } } return NULL; } gboolean status_report_assembly_report(struct status_report_assembly *assembly, const struct sms *sr, unsigned char *out_msgid, gboolean *out_delivered) { const char *straddr; GHashTable *id_table; GHashTableIter iter; struct sms_address addr; struct id_table_node *node; gboolean delivered; gboolean pending; unsigned char *msgid = 0; int i; /* We ignore temporary or tempfinal status reports */ if (sr_st_to_delivered(sr->status_report.st, &delivered) == FALSE) return FALSE; straddr = sms_address_to_string(&sr->status_report.raddr); id_table = g_hash_table_lookup(assembly->assembly_table, straddr); if (id_table != NULL) node = find_by_mr_and_mark(id_table, sr->status_report.mr, &iter, &msgid); else node = fuzzy_lookup(assembly, sr, &straddr, &iter, &msgid); /* Unable to find a message reference belonging to this address */ if (node == NULL) return FALSE; node->deliverable = node->deliverable && delivered; /* If we haven't sent the entire message yet, wait until sent */ if (node->sent_mrs < node->total_mrs) return FALSE; /* Figure out if we are expecting more status reports */ for (i = 0, pending = FALSE; i < 8; i++) { /* There are still pending mr(s). */ if (node->mrs[i] != 0) { pending = TRUE; break; } } sms_address_from_string(&addr, straddr); if (pending == TRUE && node->deliverable == TRUE) { /* * More status reports expected, and already received * reports completed. Update backup file. */ sr_assembly_add_fragment_backup(assembly->imsi, node, &addr, msgid); return FALSE; } if (out_delivered) *out_delivered = node->deliverable; if (out_msgid) memcpy(out_msgid, msgid, SMS_MSGID_LEN); sr_assembly_remove_fragment_backup(assembly->imsi, &addr, msgid); id_table = g_hash_table_iter_get_hash_table(&iter); g_hash_table_iter_remove(&iter); if (g_hash_table_size(id_table) == 0) g_hash_table_remove(assembly->assembly_table, straddr); return TRUE; } void status_report_assembly_add_fragment( struct status_report_assembly *assembly, const unsigned char *msgid, const struct sms_address *to, unsigned char mr, time_t expiration, unsigned char total_mrs) { unsigned int offset = mr / 32; unsigned int bit = 1 << (mr % 32); GHashTable *id_table; struct id_table_node *node; unsigned char *id_table_key; id_table = g_hash_table_lookup(assembly->assembly_table, sms_address_to_string(to)); /* Create hashtable keyed by the to address if required */ if (id_table == NULL) { id_table = g_hash_table_new_full(sha1_hash, sha1_equal, g_free, g_free); g_hash_table_insert(assembly->assembly_table, g_strdup(sms_address_to_string(to)), id_table); } node = g_hash_table_lookup(id_table, msgid); /* Create node in the message id hashtable if required */ if (node == NULL) { id_table_key = g_memdup(msgid, SMS_MSGID_LEN); node = g_new0(struct id_table_node, 1); node->total_mrs = total_mrs; node->deliverable = TRUE; g_hash_table_insert(id_table, id_table_key, node); } /* id_table and node both exists */ node->mrs[offset] |= bit; node->expiration = expiration; node->sent_mrs++; sr_assembly_add_fragment_backup(assembly->imsi, node, to, msgid); } void status_report_assembly_expire(struct status_report_assembly *assembly, time_t before) { GHashTable *id_table; GHashTableIter iter_addr, iter_node; struct sms_address addr; char *straddr; gpointer key; struct id_table_node *node; g_hash_table_iter_init(&iter_addr, assembly->assembly_table); /* * Go through different addresses. Each address can relate to * 1-n msg_ids. */ while (g_hash_table_iter_next(&iter_addr, (gpointer) &straddr, (gpointer) &id_table)) { sms_address_from_string(&addr, straddr); g_hash_table_iter_init(&iter_node, id_table); /* Go through different messages. */ while (g_hash_table_iter_next(&iter_node, &key, (gpointer) &node)) { /* * If message is expired, removed it from the * hash-table and remove the backup-file */ if (node->expiration <= before) { g_hash_table_iter_remove(&iter_node); sr_assembly_remove_fragment_backup( assembly->imsi, &addr, key); } } /* * If all messages are removed, remove address * from the hash-table. */ if (g_hash_table_size(id_table) == 0) g_hash_table_iter_remove(&iter_addr); } } static int sms_tx_load_filter(const struct dirent *dent) { char *endp; guint8 seq __attribute__ ((unused)); if (dent->d_type != DT_REG) return 0; seq = strtol(dent->d_name, &endp, 10); if (*endp != '\0') return 0; return 1; } /* * Each directory contains a file per pdu. */ static GSList *sms_tx_load(const char *imsi, const struct dirent *dir) { GSList *list = NULL; struct dirent **pdus; char *path; int len, r; unsigned char buf[177]; struct sms s; if (dir->d_type != DT_DIR) return NULL; path = g_strdup_printf(SMS_TX_BACKUP_PATH "/%s", imsi, dir->d_name); len = scandir(path, &pdus, sms_tx_load_filter, versionsort); g_free(path); if (len < 0) return NULL; while (len--) { r = read_file(buf, sizeof(buf), SMS_TX_BACKUP_PATH "/%s/%s", imsi, dir->d_name, pdus[len]->d_name); if (r < 0) goto free_pdu; if (sms_deserialize_outgoing(buf, &s, r) == FALSE) goto free_pdu; list = g_slist_prepend(list, g_memdup(&s, sizeof(s))); free_pdu: g_free(pdus[len]); } g_free(pdus); return list; } static int sms_tx_queue_filter(const struct dirent *dirent) { if (dirent->d_type != DT_DIR) return 0; if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, "..")) return 0; return 1; } /* * populate the queue with tx_backup_entry from stored backup * data. */ GQueue *sms_tx_queue_load(const char *imsi) { GQueue *retq = 0; char *path; struct dirent **entries; int len; int i; unsigned long id; if (imsi == NULL) return NULL; path = g_strdup_printf(SMS_TX_BACKUP_PATH, imsi); len = scandir(path, &entries, sms_tx_queue_filter, versionsort); if (len < 0) goto nodir_exit; retq = g_queue_new(); for (i = 0, id = 0; i < len; i++) { char uuid[SMS_MSGID_LEN * 2 + 1]; GSList *msg_list; unsigned long oldid; unsigned long flags; char *oldpath, *newpath; struct txq_backup_entry *entry; struct dirent *dir = entries[i]; char endc; if (sscanf(dir->d_name, "%lu-%lu-" SMS_MSGID_FMT "%c", &oldid, &flags, uuid, &endc) != 3) continue; if (strlen(uuid) != 2 * SMS_MSGID_LEN) continue; msg_list = sms_tx_load(imsi, dir); if (msg_list == NULL) continue; entry = g_new0(struct txq_backup_entry, 1); entry->msg_list = msg_list; entry->flags = flags; decode_hex_own_buf(uuid, -1, NULL, 0, entry->uuid); g_queue_push_tail(retq, entry); /* Don't bother re-shuffling the ids if they are the same */ if (oldid == id) { id++; continue; } oldpath = g_strdup_printf("%s/%s", path, dir->d_name); newpath = g_strdup_printf(SMS_TX_BACKUP_PATH_DIR, imsi, id++, flags, uuid); /* rename directory to reflect new position in queue */ rename(oldpath, newpath); g_free(newpath); g_free(oldpath); } for (i = 0; i < len; i++) g_free(entries[i]); g_free(entries); nodir_exit: g_free(path); return retq; } gboolean sms_tx_backup_store(const char *imsi, unsigned long id, unsigned long flags, const char *uuid, guint8 seq, const unsigned char *pdu, int pdu_len, int tpdu_len) { unsigned char buf[177]; int len; if (!imsi) return FALSE; memcpy(buf + 1, pdu, pdu_len); buf[0] = tpdu_len; len = pdu_len + 1; /* * file name is: imsi/tx_queue/order-flags-uuid/pdu */ if (write_file(buf, len, SMS_BACKUP_MODE, SMS_TX_BACKUP_PATH_FILE, imsi, id, flags, uuid, seq) != len) return FALSE; return TRUE; } void sms_tx_backup_free(const char *imsi, unsigned long id, unsigned long flags, const char *uuid) { char *path; struct dirent **entries; int len; path = g_strdup_printf(SMS_TX_BACKUP_PATH_DIR, imsi, id, flags, uuid); len = scandir(path, &entries, NULL, versionsort); if (len < 0) goto nodir_exit; /* skip '..' and '.' entries */ while (len-- > 2) { struct dirent *dir = entries[len]; char *file = g_strdup_printf("%s/%s", path, dir->d_name); unlink(file); g_free(file); g_free(entries[len]); } g_free(entries[1]); g_free(entries[0]); g_free(entries); rmdir(path); nodir_exit: g_free(path); } void sms_tx_backup_remove(const char *imsi, unsigned long id, unsigned long flags, const char *uuid, guint8 seq) { char *path; path = g_strdup_printf(SMS_TX_BACKUP_PATH_FILE, imsi, id, flags, uuid, seq); unlink(path); g_free(path); } static inline GSList *sms_list_append(GSList *l, const struct sms *in) { struct sms *sms; sms = g_new(struct sms, 1); memcpy(sms, in, sizeof(struct sms)); l = g_slist_prepend(l, sms); return l; } /* * Prepares a datagram for transmission. Breaks up into fragments if * necessary using ref as the concatenated message reference number. * Returns a list of sms messages in order. * * @use_delivery_reports: value for the Status-Report-Request field * (23.040 3.2.9, 9.2.2.2) */ GSList *sms_datagram_prepare(const char *to, const unsigned char *data, unsigned int len, guint16 ref, gboolean use_16bit_ref, unsigned short src, unsigned short dst, gboolean use_16bit_port, gboolean use_delivery_reports) { struct sms template; unsigned int offset; unsigned int written; unsigned int left; guint8 seq; GSList *r = NULL; memset(&template, 0, sizeof(struct sms)); template.type = SMS_TYPE_SUBMIT; template.submit.rd = FALSE; template.submit.vpf = SMS_VALIDITY_PERIOD_FORMAT_RELATIVE; template.submit.rp = FALSE; template.submit.srr = use_delivery_reports; template.submit.mr = 0; template.submit.vp.relative = 0xA7; /* 24 Hours */ template.submit.dcs = 0x04; /* Class Unspecified, 8 Bit */ template.submit.udhi = TRUE; sms_address_from_string(&template.submit.daddr, to); offset = 1; if (use_16bit_port) { template.submit.ud[0] += 6; template.submit.ud[offset] = SMS_IEI_APPLICATION_ADDRESS_16BIT; template.submit.ud[offset + 1] = 4; template.submit.ud[offset + 2] = (dst & 0xff00) >> 8; template.submit.ud[offset + 3] = dst & 0xff; template.submit.ud[offset + 4] = (src & 0xff00) >> 8; template.submit.ud[offset + 5] = src & 0xff; offset += 6; } else { template.submit.ud[0] += 4; template.submit.ud[offset] = SMS_IEI_APPLICATION_ADDRESS_8BIT; template.submit.ud[offset + 1] = 2; template.submit.ud[offset + 2] = dst & 0xff; template.submit.ud[offset + 3] = src & 0xff; offset += 4; } if (len <= (140 - offset)) { template.submit.udl = len + offset; memcpy(template.submit.ud + offset, data, len); return sms_list_append(NULL, &template); } if (use_16bit_ref) { template.submit.ud[0] += 6; template.submit.ud[offset] = SMS_IEI_CONCATENATED_16BIT; template.submit.ud[offset + 1] = 4; template.submit.ud[offset + 2] = (ref & 0xff00) >> 8; template.submit.ud[offset + 3] = ref & 0xff; offset += 6; } else { template.submit.ud[0] += 5; template.submit.ud[offset] = SMS_IEI_CONCATENATED_8BIT; template.submit.ud[offset + 1] = 3; template.submit.ud[offset + 2] = ref & 0xff; offset += 5; } seq = 0; left = len; written = 0; while (left > 0) { unsigned int chunk; seq += 1; chunk = 140 - offset; if (left < chunk) chunk = left; template.submit.udl = chunk + offset; memcpy(template.submit.ud + offset, data + written, chunk); written += chunk; left -= chunk; template.submit.ud[offset - 1] = seq; r = sms_list_append(r, &template); if (seq == 255) break; } if (left > 0) { g_slist_foreach(r, (GFunc) g_free, NULL); g_slist_free(r); return NULL; } else { GSList *l; for (l = r; l; l = l->next) { struct sms *sms = l->data; sms->submit.ud[offset - 2] = seq; } } r = g_slist_reverse(r); return r; } /* * Prepares the text for transmission. Breaks up into fragments if * necessary using ref as the concatenated message reference number. * Returns a list of sms messages in order. * * @use_delivery_reports: value for the Status-Report-Request field * (23.040 3.2.9, 9.2.2.2) */ GSList *sms_text_prepare_with_alphabet(const char *to, const char *utf8, guint16 ref, gboolean use_16bit, gboolean use_delivery_reports, enum sms_alphabet alphabet) { struct sms template; int offset = 0; unsigned char *gsm_encoded = NULL; char *ucs2_encoded = NULL; long written; long left; guint8 seq; GSList *r = NULL; enum gsm_dialect used_locking; enum gsm_dialect used_single; memset(&template, 0, sizeof(struct sms)); template.type = SMS_TYPE_SUBMIT; template.submit.rd = FALSE; template.submit.vpf = SMS_VALIDITY_PERIOD_FORMAT_RELATIVE; template.submit.rp = FALSE; template.submit.srr = use_delivery_reports; template.submit.mr = 0; template.submit.vp.relative = 0xA7; /* 24 Hours */ sms_address_from_string(&template.submit.daddr, to); /* * UDHI, UDL, UD and DCS actually depend on the contents of * the text, and also on the GSM dialect we use to encode it. */ gsm_encoded = convert_utf8_to_gsm_best_lang(utf8, -1, NULL, &written, 0, alphabet, &used_locking, &used_single); if (gsm_encoded == NULL) { gsize converted; ucs2_encoded = g_convert(utf8, -1, "UTF-16BE//TRANSLIT", "UTF-8", NULL, &converted, NULL); written = converted; } if (gsm_encoded == NULL && ucs2_encoded == NULL) return NULL; if (gsm_encoded != NULL) template.submit.dcs = 0x00; /* Class Unspecified, 7 Bit */ else template.submit.dcs = 0x08; /* Class Unspecified, UCS2 */ if (gsm_encoded != NULL && used_single != GSM_DIALECT_DEFAULT) { if (!offset) offset = 1; template.submit.ud[0] += 3; template.submit.ud[offset] = SMS_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT; template.submit.ud[offset + 1] = 1; template.submit.ud[offset + 2] = used_single; offset += 3; } if (gsm_encoded != NULL && used_locking != GSM_DIALECT_DEFAULT) { if (!offset) offset = 1; template.submit.ud[0] += 3; template.submit.ud[offset] = SMS_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT; template.submit.ud[offset + 1] = 1; template.submit.ud[offset + 2] = used_locking; offset += 3; } if (offset != 0) template.submit.udhi = TRUE; if (gsm_encoded && (written <= sms_text_capacity_gsm(160, offset))) { template.submit.udl = written + (offset * 8 + 6) / 7; pack_7bit_own_buf(gsm_encoded, written, offset, FALSE, NULL, 0, template.submit.ud + offset); g_free(gsm_encoded); return sms_list_append(NULL, &template); } if (ucs2_encoded && (written <= (140 - offset))) { template.submit.udl = written + offset; memcpy(template.submit.ud + offset, ucs2_encoded, written); g_free(ucs2_encoded); return sms_list_append(NULL, &template); } template.submit.udhi = TRUE; if (!offset) offset = 1; if (use_16bit) { template.submit.ud[0] += 6; template.submit.ud[offset] = SMS_IEI_CONCATENATED_16BIT; template.submit.ud[offset + 1] = 4; template.submit.ud[offset + 2] = (ref & 0xff00) >> 8; template.submit.ud[offset + 3] = ref & 0xff; offset += 6; } else { template.submit.ud[0] += 5; template.submit.ud[offset] = SMS_IEI_CONCATENATED_8BIT; template.submit.ud[offset + 1] = 3; template.submit.ud[offset + 2] = ref & 0xff; offset += 5; } seq = 0; left = written; written = 0; while (left > 0) { long chunk; seq += 1; if (gsm_encoded) { chunk = sms_text_capacity_gsm(160, offset); if (left < chunk) chunk = left; if (gsm_encoded[written + chunk - 1] == 0x1b) chunk -= 1; template.submit.udl = chunk + (offset * 8 + 6) / 7; pack_7bit_own_buf(gsm_encoded + written, chunk, offset, FALSE, NULL, 0, template.submit.ud + offset); } else { chunk = 140 - offset; chunk &= ~0x1; if (left < chunk) chunk = left; template.submit.udl = chunk + offset; memcpy(template.submit.ud + offset, ucs2_encoded + written, chunk); } written += chunk; left -= chunk; template.submit.ud[offset - 1] = seq; r = sms_list_append(r, &template); if (seq == 255) break; } if (gsm_encoded) g_free(gsm_encoded); if (ucs2_encoded) g_free(ucs2_encoded); if (left > 0) { g_slist_foreach(r, (GFunc) g_free, NULL); g_slist_free(r); return NULL; } else { GSList *l; for (l = r; l; l = l->next) { struct sms *sms = l->data; sms->submit.ud[offset - 2] = seq; } } r = g_slist_reverse(r); return r; } GSList *sms_text_prepare(const char *to, const char *utf8, guint16 ref, gboolean use_16bit, gboolean use_delivery_reports) { return sms_text_prepare_with_alphabet(to, utf8, ref, use_16bit, use_delivery_reports, SMS_ALPHABET_DEFAULT); } gboolean cbs_dcs_decode(guint8 dcs, gboolean *udhi, enum sms_class *cls, enum sms_charset *charset, gboolean *compressed, enum cbs_language *language, gboolean *iso639) { guint8 upper = (dcs & 0xf0) >> 4; guint8 lower = dcs & 0xf; enum sms_charset ch; enum sms_class cl; enum cbs_language lang = CBS_LANGUAGE_UNSPECIFIED; gboolean iso = FALSE; gboolean comp = FALSE; gboolean udh = FALSE; if (upper == 0x3 || upper == 0x8 || (upper >= 0xA && upper <= 0xE)) return FALSE; switch (upper) { case 0: ch = SMS_CHARSET_7BIT; cl = SMS_CLASS_UNSPECIFIED; lang = (enum cbs_language) lower; break; case 1: if (lower > 1) return FALSE; if (lower == 0) ch = SMS_CHARSET_7BIT; else ch = SMS_CHARSET_UCS2; cl = SMS_CLASS_UNSPECIFIED; iso = TRUE; break; case 2: if (lower > 4) return FALSE; ch = SMS_CHARSET_7BIT; cl = SMS_CLASS_UNSPECIFIED; lang = (enum cbs_language) dcs; break; case 4: case 5: case 6: case 7: comp = (dcs & 0x20) ? TRUE : FALSE; if (dcs & 0x10) cl = (enum sms_class) (dcs & 0x03); else cl = SMS_CLASS_UNSPECIFIED; if (((dcs & 0x0c) >> 2) < 3) ch = (enum sms_charset) ((dcs & 0x0c) >> 2); else return FALSE; break; case 9: udh = TRUE; cl = (enum sms_class) (dcs & 0x03); if (((dcs & 0x0c) >> 2) < 3) ch = (enum sms_charset) ((dcs & 0x0c) >> 2); else return FALSE; break; case 15: if (lower & 0x8) return FALSE; if (lower & 0x4) ch = SMS_CHARSET_8BIT; else ch = SMS_CHARSET_7BIT; if (lower & 0x3) cl = (enum sms_class) (lower & 0x3); else cl = SMS_CLASS_UNSPECIFIED; break; default: return FALSE; }; if (udhi) *udhi = udh; if (cls) *cls = cl; if (charset) *charset = ch; if (compressed) *compressed = comp; if (language) *language = lang; if (iso639) *iso639 = iso; return TRUE; } gboolean cbs_decode(const unsigned char *pdu, int len, struct cbs *out) { /* CBS is always a fixed length of 88 bytes */ if (len != 88) return FALSE; out->gs = (enum cbs_geo_scope) ((pdu[0] >> 6) & 0x03); out->message_code = ((pdu[0] & 0x3f) << 4) | ((pdu[1] >> 4) & 0xf); out->update_number = (pdu[1] & 0xf); out->message_identifier = (pdu[2] << 8) | pdu[3]; out->dcs = pdu[4]; out->max_pages = pdu[5] & 0xf; out->page = (pdu[5] >> 4) & 0xf; /* * If a mobile receives the code 0000 in either the first field or * the second field then it shall treat the CBS message exactly the * same as a CBS message with page parameter 0001 0001 (i.e. a single * page message). */ if (out->max_pages == 0 || out->page == 0) { out->max_pages = 1; out->page = 1; } memcpy(out->ud, pdu + 6, 82); return TRUE; } gboolean cbs_encode(const struct cbs *cbs, int *len, unsigned char *pdu) { pdu[0] = (cbs->gs << 6) | ((cbs->message_code >> 4) & 0x3f); pdu[1] = ((cbs->message_code & 0xf) << 4) | cbs->update_number; pdu[2] = cbs->message_identifier >> 8; pdu[3] = cbs->message_identifier & 0xff; pdu[4] = cbs->dcs; pdu[5] = cbs->max_pages | (cbs->page << 4); memcpy(pdu + 6, cbs->ud, 82); if (len) *len = 88; return TRUE; } gboolean cbs_extract_app_port(const struct cbs *cbs, int *dst, int *src, gboolean *is_8bit) { struct sms_udh_iter iter; if (!sms_udh_iter_init_from_cbs(cbs, &iter)) return FALSE; return extract_app_port_common(&iter, dst, src, is_8bit); } gboolean iso639_2_from_language(enum cbs_language lang, char *iso639) { switch (lang) { case CBS_LANGUAGE_GERMAN: iso639[0] = 'd'; iso639[1] = 'e'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_ENGLISH: iso639[0] = 'e'; iso639[1] = 'n'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_ITALIAN: iso639[0] = 'i'; iso639[1] = 't'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_FRENCH: iso639[0] = 'f'; iso639[1] = 'r'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_SPANISH: iso639[0] = 'e'; iso639[1] = 's'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_DUTCH: iso639[0] = 'n'; iso639[1] = 'l'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_SWEDISH: iso639[0] = 's'; iso639[1] = 'v'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_DANISH: iso639[0] = 'd'; iso639[1] = 'a'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_PORTUGESE: iso639[0] = 'p'; iso639[1] = 't'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_FINNISH: iso639[0] = 'f'; iso639[1] = 'i'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_NORWEGIAN: iso639[0] = 'n'; iso639[1] = 'o'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_GREEK: iso639[0] = 'e'; iso639[1] = 'l'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_TURKISH: iso639[0] = 't'; iso639[1] = 'r'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_HUNGARIAN: iso639[0] = 'h'; iso639[1] = 'u'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_POLISH: iso639[0] = 'p'; iso639[1] = 'l'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_CZECH: iso639[0] = 'c'; iso639[1] = 's'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_HEBREW: iso639[0] = 'h'; iso639[1] = 'e'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_ARABIC: iso639[0] = 'a'; iso639[1] = 'r'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_RUSSIAN: iso639[0] = 'r'; iso639[1] = 'u'; iso639[2] = '\0'; return TRUE; case CBS_LANGUAGE_ICELANDIC: iso639[0] = 'i'; iso639[1] = 's'; iso639[2] = '\0'; return TRUE; default: iso639[0] = '\0'; break; } return FALSE; } char *cbs_decode_text(GSList *cbs_list, char *iso639_lang) { GSList *l; const struct cbs *cbs; enum sms_charset uninitialized_var(charset); enum cbs_language lang; gboolean uninitialized_var(iso639); int bufsize = 0; unsigned char *buf; char *utf8; if (cbs_list == NULL) return NULL; /* * CBS can only come from the network, so we're much less lenient * on what we support. Namely we require the same charset to be * used across all pages. */ for (l = cbs_list; l; l = l->next) { enum sms_charset curch; gboolean curiso; cbs = l->data; if (!cbs_dcs_decode(cbs->dcs, NULL, NULL, &curch, NULL, &lang, &curiso)) return NULL; if (l == cbs_list) { iso639 = curiso; charset = curch; } if (curch != charset) return NULL; if (curiso != iso639) return NULL; if (curch == SMS_CHARSET_8BIT) return NULL; if (curch == SMS_CHARSET_7BIT) { bufsize += CBS_MAX_GSM_CHARS; if (iso639) bufsize -= 3; } else { bufsize += 82; if (iso639) bufsize -= 2; } } if (lang) { cbs = cbs_list->data; if (iso639) { struct sms_udh_iter iter; int taken = 0; if (sms_udh_iter_init_from_cbs(cbs, &iter)) taken = sms_udh_iter_get_udh_length(&iter) + 1; unpack_7bit_own_buf(cbs->ud + taken, 82 - taken, taken, FALSE, 2, NULL, 0, (unsigned char *)iso639_lang); iso639_lang[2] = '\0'; } else { iso639_2_from_language(lang, iso639_lang); } } buf = g_new(unsigned char, bufsize); bufsize = 0; for (l = cbs_list; l; l = l->next) { const guint8 *ud; struct sms_udh_iter iter; int taken = 0; cbs = l->data; ud = cbs->ud; if (sms_udh_iter_init_from_cbs(cbs, &iter)) taken = sms_udh_iter_get_udh_length(&iter) + 1; if (charset == SMS_CHARSET_7BIT) { unsigned char unpacked[CBS_MAX_GSM_CHARS]; long written; int max_chars; int i; max_chars = sms_text_capacity_gsm(CBS_MAX_GSM_CHARS, taken); unpack_7bit_own_buf(ud + taken, 82 - taken, taken, FALSE, max_chars, &written, 0, unpacked); i = iso639 ? 3 : 0; /* * CR is a padding character, which means we can * safely discard everything afterwards if there are * only trailing CR characters. */ for (; i < written; i++, bufsize++) { if (unpacked[i] == '\r') { int t; t = strspn((const char *) unpacked + i, "\r"); if (t + i == written) break; } buf[bufsize] = unpacked[i]; } /* * It isn't clear whether extension sequences * (2 septets) must be wholly present in the page * and not broken over multiple pages. The behavior * is probably the same as SMS, but we don't make * the check here since the specification isn't clear */ } else { int num_ucs2_chars = (82 - taken) >> 1; int i = taken; int max_offset = taken + num_ucs2_chars * 2; /* * It is completely unclear how UCS2 chars are handled * especially across pages or when the UDH is present. * For now do the best we can. */ if (iso639) { i += 2; num_ucs2_chars -= 1; } while (i < max_offset) { if (ud[i] == 0x00 && ud[i + 1] == '\r') { int j = i + 2; for (; j < max_offset; j = j + 2) if (ud[j + 1] != '\r' || ud[j] != 0x00) break; if (j == max_offset) break; } buf[bufsize] = ud[i]; buf[bufsize + 1] = ud[i + 1]; bufsize += 2; i += 2; } } } if (charset == SMS_CHARSET_7BIT) utf8 = convert_gsm_to_utf8(buf, bufsize, NULL, NULL, 0); else utf8 = g_convert((char *) buf, bufsize, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); g_free(buf); return utf8; } static inline gboolean cbs_is_update_newer(unsigned int n, unsigned int o) { unsigned int old_update = o & 0xf; unsigned int new_update = n & 0xf; if (new_update == old_update) return FALSE; /* * Any Update Number eight or less higher (modulo 16) than the last * received Update Number will be considered more recent, and shall be * treated as a new CBS message, provided the mobile has not been * switched off. */ if (new_update <= ((old_update + 8) % 16)) return TRUE; return FALSE; } struct cbs_assembly *cbs_assembly_new(void) { return g_new0(struct cbs_assembly, 1); } void cbs_assembly_free(struct cbs_assembly *assembly) { GSList *l; for (l = assembly->assembly_list; l; l = l->next) { struct cbs_assembly_node *node = l->data; g_slist_foreach(node->pages, (GFunc) g_free, 0); g_slist_free(node->pages); g_free(node); } g_slist_free(assembly->assembly_list); g_slist_free(assembly->recv_plmn); g_slist_free(assembly->recv_loc); g_slist_free(assembly->recv_cell); g_free(assembly); } static gint cbs_compare_node_by_gs(gconstpointer a, gconstpointer b) { const struct cbs_assembly_node *node = a; unsigned int gs = GPOINTER_TO_UINT(b); if (((node->serial >> 14) & 0x3) == gs) return 0; return 1; } static gint cbs_compare_node_by_update(gconstpointer a, gconstpointer b) { const struct cbs_assembly_node *node = a; unsigned int serial = GPOINTER_TO_UINT(b); if ((serial & (~0xf)) != (node->serial & (~0xf))) return 1; if (cbs_is_update_newer(node->serial, serial)) return 1; return 0; } static gint cbs_compare_recv_by_serial(gconstpointer a, gconstpointer b) { unsigned int old_serial = GPOINTER_TO_UINT(a); unsigned int new_serial = GPOINTER_TO_UINT(b); if ((old_serial & (~0xf)) == (new_serial & (~0xf))) return 0; return 1; } static void cbs_assembly_expire(struct cbs_assembly *assembly, GCompareFunc func, gconstpointer *userdata) { GSList *l; GSList *prev; GSList *tmp; /* * Take care of the case where several updates are being * reassembled at the same time. If the newer one is assembled * first, then the subsequent old update is discarded, make * sure that we're also discarding the assembly node for the * partially assembled ones */ prev = NULL; l = assembly->assembly_list; while (l) { struct cbs_assembly_node *node = l->data; if (func(node, userdata) != 0) { prev = l; l = l->next; continue; } if (prev) prev->next = l->next; else assembly->assembly_list = l->next; g_slist_foreach(node->pages, (GFunc) g_free, NULL); g_slist_free(node->pages); g_free(node->pages); tmp = l; l = l->next; g_slist_free_1(tmp); } } void cbs_assembly_location_changed(struct cbs_assembly *assembly, gboolean plmn, gboolean lac, gboolean ci) { /* * Location Area wide (in GSM) (which means that a CBS message with the * same Message Code and Update Number may or may not be "new" in the * next cell according to whether the next cell is in the same Location * Area as the current cell), or * * Service Area Wide (in UMTS) (which means that a CBS message with the * same Message Code and Update Number may or may not be "new" in the * next cell according to whether the next cell is in the same Service * Area as the current cell) * * NOTE 4: According to 3GPP TS 23.003 [2] a Service Area consists of * one cell only. */ if (plmn) { lac = TRUE; g_slist_free(assembly->recv_plmn); assembly->recv_plmn = NULL; cbs_assembly_expire(assembly, cbs_compare_node_by_gs, GUINT_TO_POINTER(CBS_GEO_SCOPE_PLMN)); } if (lac) { /* If LAC changed, then cell id has changed */ ci = TRUE; g_slist_free(assembly->recv_loc); assembly->recv_loc = NULL; cbs_assembly_expire(assembly, cbs_compare_node_by_gs, GUINT_TO_POINTER(CBS_GEO_SCOPE_SERVICE_AREA)); } if (ci) { g_slist_free(assembly->recv_cell); assembly->recv_cell = NULL; cbs_assembly_expire(assembly, cbs_compare_node_by_gs, GUINT_TO_POINTER(CBS_GEO_SCOPE_CELL_IMMEDIATE)); cbs_assembly_expire(assembly, cbs_compare_node_by_gs, GUINT_TO_POINTER(CBS_GEO_SCOPE_CELL_NORMAL)); } } GSList *cbs_assembly_add_page(struct cbs_assembly *assembly, const struct cbs *cbs) { struct cbs *newcbs; struct cbs_assembly_node *node; GSList *completed; unsigned int new_serial; GSList **recv; GSList *l; GSList *prev; int position; new_serial = cbs->gs << 14; new_serial |= cbs->message_code << 4; new_serial |= cbs->update_number; new_serial |= cbs->message_identifier << 16; if (cbs->gs == CBS_GEO_SCOPE_PLMN) recv = &assembly->recv_plmn; else if (cbs->gs == CBS_GEO_SCOPE_SERVICE_AREA) recv = &assembly->recv_loc; else recv = &assembly->recv_cell; /* Have we seen this message before? */ l = g_slist_find_custom(*recv, GUINT_TO_POINTER(new_serial), cbs_compare_recv_by_serial); /* If we have, is the message newer? */ if (l && !cbs_is_update_newer(new_serial, GPOINTER_TO_UINT(l->data))) return NULL; /* Easy case first, page 1 of 1 */ if (cbs->max_pages == 1 && cbs->page == 1) { if (l) l->data = GUINT_TO_POINTER(new_serial); else *recv = g_slist_prepend(*recv, GUINT_TO_POINTER(new_serial)); newcbs = g_new(struct cbs, 1); memcpy(newcbs, cbs, sizeof(struct cbs)); completed = g_slist_append(NULL, newcbs); return completed; } prev = NULL; position = 0; for (l = assembly->assembly_list; l; prev = l, l = l->next) { int j; node = l->data; if (new_serial != node->serial) continue; if (node->bitmap & (1 << cbs->page)) return NULL; for (j = 1; j < cbs->page; j++) if (node->bitmap & (1 << j)) position += 1; goto out; } node = g_new0(struct cbs_assembly_node, 1); node->serial = new_serial; assembly->assembly_list = g_slist_prepend(assembly->assembly_list, node); prev = NULL; l = assembly->assembly_list; position = 0; out: newcbs = g_new(struct cbs, 1); memcpy(newcbs, cbs, sizeof(struct cbs)); node->pages = g_slist_insert(node->pages, newcbs, position); node->bitmap |= 1 << cbs->page; if (g_slist_length(node->pages) < cbs->max_pages) return NULL; completed = node->pages; if (prev) prev->next = l->next; else assembly->assembly_list = l->next; g_free(node); g_slist_free_1(l); cbs_assembly_expire(assembly, cbs_compare_node_by_update, GUINT_TO_POINTER(new_serial)); *recv = g_slist_prepend(*recv, GUINT_TO_POINTER(new_serial)); return completed; } static inline int skip_to_next_field(const char *str, int pos, int len) { if (pos < len && str[pos] == ',') pos += 1; while (pos < len && str[pos] == ' ') pos += 1; return pos; } static gboolean next_range(const char *str, int *offset, gint *min, gint *max) { int pos; int end; int len; int low = 0; int high = 0; len = strlen(str); pos = *offset; while (pos < len && str[pos] == ' ') pos += 1; end = pos; while (str[end] >= '0' && str[end] <= '9') { low = low * 10 + (int)(str[end] - '0'); end += 1; } if (pos == end) return FALSE; if (str[end] != '-') { high = low; goto out; } pos = end = end + 1; while (str[end] >= '0' && str[end] <= '9') { high = high * 10 + (int)(str[end] - '0'); end += 1; } if (pos == end) return FALSE; out: *offset = skip_to_next_field(str, end, len); if (min) *min = low; if (max) *max = high; return TRUE; } GSList *cbs_optimize_ranges(GSList *ranges) { struct cbs_topic_range *range; unsigned char bitmap[125]; GSList *l; unsigned short i; GSList *ret = NULL; memset(bitmap, 0, sizeof(bitmap)); for (l = ranges; l; l = l->next) { range = l->data; for (i = range->min; i <= range->max; i++) { int byte_offset = i / 8; int bit = i % 8; bitmap[byte_offset] |= 1 << bit; } } range = NULL; for (i = 0; i <= 999; i++) { int byte_offset = i / 8; int bit = i % 8; if (is_bit_set(bitmap[byte_offset], bit) == FALSE) { if (range) { ret = g_slist_prepend(ret, range); range = NULL; } continue; } if (range) { range->max = i; continue; } range = g_new0(struct cbs_topic_range, 1); range->min = i; range->max = i; } if (range != NULL) ret = g_slist_prepend(ret, range); ret = g_slist_reverse(ret); return ret; } GSList *cbs_extract_topic_ranges(const char *ranges) { int min; int max; int offset = 0; GSList *ret = NULL; GSList *tmp; while (next_range(ranges, &offset, &min, &max) == TRUE) { if (min < 0 || min > 999) return NULL; if (max < 0 || max > 999) return NULL; if (max < min) return NULL; } if (ranges[offset] != '\0') return NULL; offset = 0; while (next_range(ranges, &offset, &min, &max) == TRUE) { struct cbs_topic_range *range = g_new0(struct cbs_topic_range, 1); range->min = min; range->max = max; ret = g_slist_prepend(ret, range); } tmp = cbs_optimize_ranges(ret); g_slist_foreach(ret, (GFunc) g_free, NULL); g_slist_free(ret); return tmp; } static inline int element_length(unsigned short element) { if (element <= 9) return 1; if (element <= 99) return 2; if (element <= 999) return 3; if (element <= 9999) return 4; return 5; } static inline int range_length(struct cbs_topic_range *range) { if (range->min == range->max) return element_length(range->min); return element_length(range->min) + element_length(range->max) + 1; } char *cbs_topic_ranges_to_string(GSList *ranges) { int len = 0; int nelem = 0; struct cbs_topic_range *range; GSList *l; char *ret; if (ranges == NULL) return g_new0(char, 1); for (l = ranges; l; l = l->next) { range = l->data; len += range_length(range); nelem += 1; } /* Space for ranges, commas and terminator null */ ret = g_new(char, len + nelem); len = 0; for (l = ranges; l; l = l->next) { range = l->data; if (range->min != range->max) len += sprintf(ret + len, "%hu-%hu", range->min, range->max); else len += sprintf(ret + len, "%hu", range->min); if (l->next != NULL) ret[len++] = ','; } return ret; } static gint cbs_topic_compare(gconstpointer a, gconstpointer b) { const struct cbs_topic_range *range = a; unsigned short topic = GPOINTER_TO_UINT(b); if (topic >= range->min && topic <= range->max) return 0; return 1; } gboolean cbs_topic_in_range(unsigned int topic, GSList *ranges) { if (ranges == NULL) return FALSE; return g_slist_find_custom(ranges, GUINT_TO_POINTER(topic), cbs_topic_compare) != NULL; } char *ussd_decode(int dcs, int len, const unsigned char *data) { gboolean udhi; enum sms_charset charset; gboolean compressed; gboolean iso639; char *utf8; if (!cbs_dcs_decode(dcs, &udhi, NULL, &charset, &compressed, NULL, &iso639)) return NULL; if (udhi || compressed || iso639) return NULL; switch (charset) { case SMS_CHARSET_7BIT: { long written; unsigned char *unpacked = unpack_7bit(data, len, 0, TRUE, 0, &written, 0); if (unpacked == NULL) return NULL; utf8 = convert_gsm_to_utf8(unpacked, written, NULL, NULL, 0); g_free(unpacked); break; } case SMS_CHARSET_8BIT: utf8 = convert_gsm_to_utf8(data, len, NULL, NULL, 0); break; case SMS_CHARSET_UCS2: utf8 = g_convert((const gchar *) data, len, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); break; default: utf8 = NULL; } return utf8; } gboolean ussd_encode(const char *str, long *items_written, unsigned char *pdu) { unsigned char *converted = NULL; long written; long num_packed; if (pdu == NULL) return FALSE; converted = convert_utf8_to_gsm(str, -1, NULL, &written, 0); if (converted == NULL || written > 182) { g_free(converted); return FALSE; } pack_7bit_own_buf(converted, written, 0, TRUE, &num_packed, 0, pdu); g_free(converted); if (num_packed < 1) return FALSE; if (items_written) *items_written = num_packed; return TRUE; } gboolean ussd_dcs_encode(const char *str, int *dcs, long *items_written, unsigned char *pdu) { gsize written; char *ucs2; /* For the DCS coding of USSD strings, see 3gpp 23.038, sect. 5 */ if (ussd_encode(str, items_written, pdu)) { /* DCS 0x0F: GSM 7 bits, language unspecified */ *dcs = 0x0F; return TRUE; } /* Trying with UCS-2 */ ucs2 = g_convert(str, -1, "UCS-2BE//TRANSLIT", "UTF-8", NULL, &written, NULL); if (ucs2 == NULL || written > 160) { g_free(ucs2); return FALSE; } memcpy(pdu, ucs2, written); g_free(ucs2); if (items_written) *items_written = written; /* DCS 0x48: UCS-2 string, uncompressed, unspecified message class */ *dcs = 0x48; return TRUE; } ofono-1.17.bzr6912+16.04.20160314.3/src/location-reporting.c0000644000015600001650000002321412671500024023153 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 ProFUSION embedded systems. * * 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 #endif #include #include #include #include #include #include "ofono.h" #include "common.h" #ifndef DBUS_TYPE_UNIX_FD #define DBUS_TYPE_UNIX_FD -1 #endif static GSList *g_drivers = NULL; struct ofono_location_reporting { DBusMessage *pending; const struct ofono_location_reporting_driver *driver; void *driver_data; struct ofono_atom *atom; ofono_bool_t enabled; char *client_owner; guint disconnect_watch; }; static const char *location_reporting_type_to_string( enum ofono_location_reporting_type type) { switch (type) { case OFONO_LOCATION_REPORTING_TYPE_NMEA: return "nmea"; }; return NULL; } static DBusMessage *location_reporting_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_location_reporting *lr = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; const char *type; dbus_bool_t value; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); value = lr->enabled; ofono_dbus_dict_append(&dict, "Enabled", DBUS_TYPE_BOOLEAN, &value); type = location_reporting_type_to_string(lr->driver->type); ofono_dbus_dict_append(&dict, "Type", DBUS_TYPE_STRING, &type); dbus_message_iter_close_container(&iter, &dict); return reply; } static void client_remove(struct ofono_location_reporting *lr) { DBusConnection *conn = ofono_dbus_get_connection(); if (lr->disconnect_watch) { g_dbus_remove_watch(conn, lr->disconnect_watch); lr->disconnect_watch = 0; } g_free(lr->client_owner); } static void signal_enabled(const struct ofono_location_reporting *lr) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = __ofono_atom_get_path(lr->atom); dbus_bool_t value = lr->enabled; ofono_dbus_signal_property_changed(conn, path, OFONO_LOCATION_REPORTING_INTERFACE, "Enabled", DBUS_TYPE_BOOLEAN, &value); } static void client_exited_disable_cb(const struct ofono_error *error, void *data) { struct ofono_location_reporting *lr = data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Disabling location-reporting failed"); return; } client_remove(lr); lr->enabled = FALSE; signal_enabled(lr); } static void client_exited(DBusConnection *conn, void *data) { struct ofono_location_reporting *lr = data; lr->disconnect_watch = 0; lr->driver->disable(lr, client_exited_disable_cb, lr); } static void location_reporting_disable_cb(const struct ofono_error *error, void *data) { struct ofono_location_reporting *lr = data; DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Disabling location-reporting failed"); reply = __ofono_error_failed(lr->pending); __ofono_dbus_pending_reply(&lr->pending, reply); return; } client_remove(lr); lr->enabled = FALSE; reply = dbus_message_new_method_return(lr->pending); __ofono_dbus_pending_reply(&lr->pending, reply); signal_enabled(lr); } static void location_reporting_enable_cb(const struct ofono_error *error, int fd, void *data) { struct ofono_location_reporting *lr = data; DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *reply; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("Enabling location-reporting failed"); reply = __ofono_error_failed(lr->pending); __ofono_dbus_pending_reply(&lr->pending, reply); return; } lr->enabled = TRUE; lr->client_owner = g_strdup(dbus_message_get_sender(lr->pending)); lr->disconnect_watch = g_dbus_add_disconnect_watch(conn, lr->client_owner, client_exited, lr, NULL); reply = dbus_message_new_method_return(lr->pending); dbus_message_append_args(reply, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID); __ofono_dbus_pending_reply(&lr->pending, reply); signal_enabled(lr); } static DBusMessage *location_reporting_request(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_location_reporting *lr = data; if (lr->pending != NULL) return __ofono_error_busy(msg); if (lr->enabled) return __ofono_error_in_use(msg); lr->pending = dbus_message_ref(msg); lr->driver->enable(lr, location_reporting_enable_cb, lr); return NULL; } static DBusMessage *location_reporting_release(DBusConnection *conn, DBusMessage *msg, void *data) { struct ofono_location_reporting *lr = data; const char *caller = dbus_message_get_sender(msg); /* * Avoid a race by not trying to release the device if there is a * pending message or client already signaled it's exiting. In the * later case, the device will eventually be released in * client_exited_disable_cb(). */ if (lr->pending != NULL || (lr->enabled && !lr->disconnect_watch)) return __ofono_error_busy(msg); if (lr->enabled == FALSE) return __ofono_error_not_available(msg); if (g_strcmp0(caller, lr->client_owner)) return __ofono_error_access_denied(msg); lr->pending = dbus_message_ref(msg); lr->driver->disable(lr, location_reporting_disable_cb, lr); return NULL; } static const GDBusMethodTable location_reporting_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), location_reporting_get_properties) }, { GDBUS_ASYNC_METHOD("Request", NULL, GDBUS_ARGS({ "fd", "h" }), location_reporting_request) }, { GDBUS_ASYNC_METHOD("Release", NULL, NULL, location_reporting_release) }, { } }; static const GDBusSignalTable location_reporting_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; int ofono_location_reporting_driver_register( const struct ofono_location_reporting_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d == NULL || d->probe == NULL) return -EINVAL; g_drivers = g_slist_prepend(g_drivers, (void *) d); return 0; } void ofono_location_reporting_driver_unregister( const struct ofono_location_reporting_driver *d) { DBG("driver: %p, name: %s", d, d->name); if (d == NULL) return; g_drivers = g_slist_remove(g_drivers, (void *) d); } struct ofono_modem *ofono_location_reporting_get_modem( struct ofono_location_reporting *lr) { return __ofono_atom_get_modem(lr->atom); } static void location_reporting_unregister(struct ofono_atom *atom) { struct ofono_location_reporting *lr = __ofono_atom_get_data(atom); const char *path = __ofono_atom_get_path(lr->atom); DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(lr->atom); ofono_modem_remove_interface(modem, OFONO_LOCATION_REPORTING_INTERFACE); g_dbus_unregister_interface(conn, path, OFONO_LOCATION_REPORTING_INTERFACE); } static void location_reporting_remove(struct ofono_atom *atom) { struct ofono_location_reporting *lr = __ofono_atom_get_data(atom); DBG("atom: %p", atom); if (lr == NULL) return; if (lr->driver && lr->driver->remove) lr->driver->remove(lr); g_free(lr); } struct ofono_location_reporting *ofono_location_reporting_create( struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct ofono_location_reporting *lr; GSList *l; if (driver == NULL) return NULL; /* Only D-Bus >= 1.3 supports fd-passing */ if (DBUS_TYPE_UNIX_FD == -1) return NULL; lr = g_try_new0(struct ofono_location_reporting, 1); if (lr == NULL) return NULL; lr->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_LOCATION_REPORTING, location_reporting_remove, lr); for (l = g_drivers; l; l = l->next) { const struct ofono_location_reporting_driver *drv = l->data; if (g_strcmp0(drv->name, driver) != 0) continue; if (drv->probe(lr, vendor, data) < 0) continue; lr->driver = drv; break; } return lr; } void ofono_location_reporting_register(struct ofono_location_reporting *lr) { DBusConnection *conn = ofono_dbus_get_connection(); struct ofono_modem *modem = __ofono_atom_get_modem(lr->atom); const char *path = __ofono_atom_get_path(lr->atom); if (!g_dbus_register_interface(conn, path, OFONO_LOCATION_REPORTING_INTERFACE, location_reporting_methods, location_reporting_signals, NULL, lr, NULL)) { ofono_error("Could not create %s interface", OFONO_LOCATION_REPORTING_INTERFACE); return; } ofono_modem_add_interface(modem, OFONO_LOCATION_REPORTING_INTERFACE); __ofono_atom_register(lr->atom, location_reporting_unregister); } void ofono_location_reporting_remove(struct ofono_location_reporting *lr) { __ofono_atom_free(lr->atom); } void ofono_location_reporting_set_data(struct ofono_location_reporting *lr, void *data) { lr->driver_data = data; } void *ofono_location_reporting_get_data(struct ofono_location_reporting *lr) { return lr->driver_data; } ofono-1.17.bzr6912+16.04.20160314.3/src/stkagent.h0000644000015600001650000001301112671500024021153 0ustar pbuserpbgroup00000000000000/* * * 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 * */ struct stk_agent; enum stk_agent_result { STK_AGENT_RESULT_OK, STK_AGENT_RESULT_BACK, STK_AGENT_RESULT_TERMINATE, STK_AGENT_RESULT_TIMEOUT, STK_AGENT_RESULT_BUSY, }; struct stk_menu_item { char *text; uint8_t icon_id; uint8_t item_id; }; struct stk_menu { char *title; struct stk_icon_id icon; struct stk_menu_item *items; int default_item; gboolean soft_key; gboolean has_help; }; typedef void (*stk_agent_display_text_cb)(enum stk_agent_result result, void *user_data); typedef void (*stk_agent_selection_cb)(enum stk_agent_result result, uint8_t id, void *user_data); typedef void (*stk_agent_confirmation_cb)(enum stk_agent_result result, ofono_bool_t confirm, void *user_data); typedef void (*stk_agent_string_cb)(enum stk_agent_result result, char *string, void *user_data); typedef void (*stk_agent_tone_cb)(enum stk_agent_result result, void *user_data); typedef void (*stk_agent_display_action_cb)(enum stk_agent_result result, void *user_data); struct stk_agent *stk_agent_new(const char *path, const char *sender, ofono_bool_t remove_on_terminate); void stk_agent_free(struct stk_agent *agent); void stk_agent_set_removed_notify(struct stk_agent *agent, ofono_destroy_func removed_cb, void *user_data); ofono_bool_t stk_agent_matches(struct stk_agent *agent, const char *path, const char *sender); void stk_agent_request_cancel(struct stk_agent *agent); int stk_agent_request_selection(struct stk_agent *agent, const struct stk_menu *menu, stk_agent_selection_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_display_text(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, ofono_bool_t urgent, stk_agent_display_text_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_request_confirmation(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_confirmation_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_request_digit(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_string_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_request_quick_digit(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_string_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_request_key(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, ofono_bool_t unicode_charset, stk_agent_string_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_request_digits(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, const char *default_text, int min, int max, ofono_bool_t hidden, stk_agent_string_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_request_input(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, const char *default_text, ofono_bool_t unicode_charset, int min, int max, ofono_bool_t hidden, stk_agent_string_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_confirm_call(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_confirmation_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_play_tone(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, ofono_bool_t vibrate, const char *tone, stk_agent_tone_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_loop_tone(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, ofono_bool_t vibrate, const char *tone, stk_agent_tone_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); void append_menu_items_variant(DBusMessageIter *iter, const struct stk_menu_item *items); int stk_agent_display_action_info(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon); int stk_agent_confirm_launch_browser(struct stk_agent *agent, const char *text, unsigned char icon_id, const char *url, stk_agent_confirmation_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); int stk_agent_display_action(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_display_action_cb cb, void *user_data, ofono_destroy_func destroy); int stk_agent_confirm_open_channel(struct stk_agent *agent, const char *text, const struct stk_icon_id *icon, stk_agent_confirmation_cb cb, void *user_data, ofono_destroy_func destroy, int timeout); ofono-1.17.bzr6912+16.04.20160314.3/src/idmap.h0000644000015600001650000000237412671500024020437 0ustar pbuserpbgroup00000000000000/* * * 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 * */ struct idmap; struct idmap *idmap_new(unsigned int size); void idmap_free(struct idmap *idmap); void idmap_put(struct idmap *idmap, unsigned int id); void idmap_take(struct idmap *idmap, unsigned int id); unsigned int idmap_alloc(struct idmap *idmap); unsigned int idmap_alloc_next(struct idmap *idmap, unsigned int last); struct idmap *idmap_new_from_range(unsigned int min, unsigned int max); unsigned int idmap_get_min(struct idmap *idmap); unsigned int idmap_get_max(struct idmap *idmap); ofono-1.17.bzr6912+16.04.20160314.3/src/storage.c0000644000015600001650000001061612671500024021002 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "storage.h" int create_dirs(const char *filename, const mode_t mode) { struct stat st; char *dir; const char *prev, *next; int err; if (filename[0] != '/') return -1; err = stat(filename, &st); if (!err && S_ISREG(st.st_mode)) return 0; dir = g_try_malloc(strlen(filename) + 1); if (dir == NULL) return -1; strcpy(dir, "/"); for (prev = filename; (next = strchr(prev + 1, '/')); prev = next) { /* Skip consecutive '/' characters */ if (next - prev == 1) continue; strncat(dir, prev + 1, next - prev); if (mkdir(dir, mode) == -1 && errno != EEXIST) { g_free(dir); return -1; } } g_free(dir); return 0; } ssize_t read_file(unsigned char *buffer, size_t len, const char *path_fmt, ...) { va_list ap; char *path; ssize_t r; int fd; va_start(ap, path_fmt); path = g_strdup_vprintf(path_fmt, ap); va_end(ap); fd = TFR(open(path, O_RDONLY)); g_free(path); if (fd == -1) return -1; r = TFR(read(fd, buffer, len)); TFR(close(fd)); return r; } /* * Write a buffer to a file in a transactionally safe form * * Given a buffer, write it to a file named after * @path_fmt+args. However, to make sure the file contents are * consistent (ie: a crash right after opening or during write() * doesn't leave a file half baked), the contents are written to a * file with a temporary name and when closed, it is renamed to the * specified name (@path_fmt+args). */ ssize_t write_file(const unsigned char *buffer, size_t len, mode_t mode, const char *path_fmt, ...) { va_list ap; char *tmp_path, *path; ssize_t r; int fd; va_start(ap, path_fmt); path = g_strdup_vprintf(path_fmt, ap); va_end(ap); tmp_path = g_strdup_printf("%s.XXXXXX.tmp", path); r = -1; if (create_dirs(path, mode | S_IXUSR) != 0) goto error_create_dirs; fd = TFR(g_mkstemp_full(tmp_path, O_WRONLY | O_CREAT | O_TRUNC, mode)); if (fd == -1) goto error_mkstemp_full; r = TFR(write(fd, buffer, len)); TFR(close(fd)); if (r != (ssize_t) len) { r = -1; goto error_write; } /* * Now that the file contents are written, rename to the real * file name; this way we are uniquely sure that the whole * thing is there. */ unlink(path); /* conserve @r's value from 'write' */ if (link(tmp_path, path) == -1) r = -1; error_write: unlink(tmp_path); error_mkstemp_full: error_create_dirs: g_free(tmp_path); g_free(path); return r; } GKeyFile *storage_open(const char *imsi, const char *store) { GKeyFile *keyfile; char *path; if (store == NULL) return NULL; if (imsi) path = g_strdup_printf(STORAGEDIR "/%s/%s", imsi, store); else path = g_strdup_printf(STORAGEDIR "/%s", store); keyfile = g_key_file_new(); if (path) { g_key_file_load_from_file(keyfile, path, 0, NULL); g_free(path); } return keyfile; } void storage_sync(const char *imsi, const char *store, GKeyFile *keyfile) { char *path; char *data; gsize length = 0; if (imsi) path = g_strdup_printf(STORAGEDIR "/%s/%s", imsi, store); else path = g_strdup_printf(STORAGEDIR "/%s", store); if (path == NULL) return; if (create_dirs(path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) { g_free(path); return; } data = g_key_file_to_data(keyfile, &length, NULL); g_file_set_contents(path, data, length, NULL); g_free(data); g_free(path); } void storage_close(const char *imsi, const char *store, GKeyFile *keyfile, gboolean save) { if (save == TRUE) storage_sync(imsi, store, keyfile); g_key_file_free(keyfile); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/0000755000015600001650000000000012671500304020056 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/0000755000015600001650000000000012671500304021666 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/sms.c0000644000015600001650000001754112671500073022647 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2013 Canonical Ltd. * Copyright (C) 2013 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "smsutil.h" #include "util.h" #include "rilmodem.h" #include "grilrequest.h" #include "grilreply.h" #include "grilunsol.h" struct sms_data { GRil *ril; unsigned int vendor; }; static void ril_csca_set_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sms_sca_set_cb_t cb = cbd->cb; struct sms_data *sd = cbd->user; if (message->error == RIL_E_SUCCESS) { CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { ofono_error("%s RILD reply failure: %s", g_ril_request_id_to_string(sd->ril, message->req), ril_error_to_string(message->error)); CALLBACK_WITH_FAILURE(cb, cbd->data); } } static void ril_csca_set(struct ofono_sms *sms, const struct ofono_phone_number *sca, ofono_sms_sca_set_cb_t cb, void *user_data) { struct sms_data *sd = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data, sd); struct parcel rilp; g_ril_request_set_smsc_address(sd->ril, sca, &rilp); /* Send request to RIL */ if (g_ril_send(sd->ril, RIL_REQUEST_SET_SMSC_ADDRESS, &rilp, ril_csca_set_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } } static void ril_csca_query_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sms_sca_query_cb_t cb = cbd->cb; struct sms_data *sd = cbd->user; struct ofono_phone_number *sca; if (message->error != RIL_E_SUCCESS) { ofono_error("%s RILD reply failure: %s", g_ril_request_id_to_string(sd->ril, message->req), ril_error_to_string(message->error)); CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); return; } sca = g_ril_reply_parse_get_smsc_address(sd->ril, message); if (sca == NULL) { CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } else { CALLBACK_WITH_SUCCESS(cb, sca, cbd->data); g_free(sca); } } static void ril_csca_query(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb, void *user_data) { struct sms_data *sd = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data, sd); DBG("Sending csca_query"); if (g_ril_send(sd->ril, RIL_REQUEST_GET_SMSC_ADDRESS, NULL, ril_csca_query_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, user_data); } } static void ril_submit_sms_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sms_submit_cb_t cb = cbd->cb; struct sms_data *sd = cbd->user; int mr; if (message->error != RIL_E_SUCCESS) { CALLBACK_WITH_FAILURE(cb, 0, cbd->data); return; } mr = g_ril_reply_parse_sms_response(sd->ril, message); CALLBACK_WITH_SUCCESS(cb, mr, cbd->data); } static void ril_cmgs(struct ofono_sms *sms, const unsigned char *pdu, int pdu_len, int tpdu_len, int mms, ofono_sms_submit_cb_t cb, void *user_data) { struct sms_data *sd = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data, sd); struct parcel rilp; struct req_sms_cmgs req; DBG("pdu_len: %d, tpdu_len: %d mms: %d", pdu_len, tpdu_len, mms); /* TODO: if (mms) { ... } */ req.pdu = pdu; req.pdu_len = pdu_len; req.tpdu_len = tpdu_len; g_ril_request_sms_cmgs(sd->ril, &req, &rilp); if (g_ril_send(sd->ril, RIL_REQUEST_SEND_SMS, &rilp, ril_submit_sms_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, user_data); } } static void ril_ack_delivery_cb(struct ril_msg *message, gpointer user_data) { if (message->error != RIL_E_SUCCESS) ofono_error("SMS acknowledgement failed: " "Further SMS reception is not guaranteed"); } static void ril_ack_delivery(struct ofono_sms *sms) { struct sms_data *sd = ofono_sms_get_data(sms); struct parcel rilp; g_ril_request_sms_acknowledge(sd->ril, &rilp); /* TODO: should ACK be sent for either of the error cases? */ /* ACK the incoming NEW_SMS */ g_ril_send(sd->ril, RIL_REQUEST_SMS_ACKNOWLEDGE, &rilp, ril_ack_delivery_cb, NULL, NULL); } static void ril_sms_notify(struct ril_msg *message, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *sd = ofono_sms_get_data(sms); unsigned int smsc_len; long ril_buf_len; struct unsol_sms_data *pdu_data; DBG("req: %d; data_len: %d", message->req, (int) message->buf_len); pdu_data = g_ril_unsol_parse_new_sms(sd->ril, message); if (pdu_data == NULL) goto error; /* * The first octect in the pdu contains the SMSC address length * which is the X following octects it reads. We add 1 octet to * the read length to take into account this read octet in order * to calculate the proper tpdu length. */ smsc_len = pdu_data->data[0] + 1; ril_buf_len = pdu_data->length; DBG("smsc_len is %d", smsc_len); if (message->req == RIL_UNSOL_RESPONSE_NEW_SMS) /* Last parameter is 'tpdu_len' ( substract SMSC length ) */ ofono_sms_deliver_notify(sms, pdu_data->data, ril_buf_len, ril_buf_len - smsc_len); else if (message->req == RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT) ofono_sms_status_notify(sms, pdu_data->data, ril_buf_len, ril_buf_len - smsc_len); /* ACK the incoming NEW_SMS */ ril_ack_delivery(sms); g_ril_unsol_free_sms_data(pdu_data); error: ; } static gboolean ril_delayed_register(gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); DBG(""); ofono_sms_register(sms); /* register to receive INCOMING_SMS and SMS status reports */ g_ril_register(data->ril, RIL_UNSOL_RESPONSE_NEW_SMS, ril_sms_notify, sms); g_ril_register(data->ril, RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT, ril_sms_notify, sms); /* This makes the delayed call a single-shot */ return FALSE; } static int ril_sms_probe(struct ofono_sms *sms, unsigned int vendor, void *user) { GRil *ril = user; struct sms_data *data; data = g_new0(struct sms_data, 1); data->ril = g_ril_clone(ril); data->vendor = vendor; ofono_sms_set_data(sms, data); /* * ofono_sms_register() needs to be called after * the driver has been set in ofono_sms_create(), which * calls this function. Most other drivers make some * kind of capabilities query to the modem, and then * call register in the callback; we use an idle add instead. */ g_idle_add(ril_delayed_register, sms); return 0; } static void ril_sms_remove(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); DBG(""); g_ril_unref(data->ril); g_free(data); ofono_sms_set_data(sms, NULL); } static struct ofono_sms_driver driver = { .name = RILMODEM, .probe = ril_sms_probe, .sca_query = ril_csca_query, .sca_set = ril_csca_set, .remove = ril_sms_remove, .submit = ril_cmgs, /* * TODO: investigate/implement: * .bearer_query = NULL, * .bearer_set = NULL, */ }; void ril_sms_init(void) { DBG(""); if (ofono_sms_driver_register(&driver)) DBG("ofono_sms_driver_register failed!"); } void ril_sms_exit(void) { DBG(""); ofono_sms_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/voicecall.c0000644000015600001650000005066712671500024024010 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2013 Canonical Ltd. * Copyright (C) 2013 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gril.h" #include "grilrequest.h" #include "grilreply.h" #include "grilunsol.h" #include "common.h" #include "rilmodem.h" #include "voicecall.h" /* Amount of ms we wait between CLCC calls */ #define POLL_CLCC_INTERVAL 300 #define FLAG_NEED_CLIP 1 #define MAX_DTMF_BUFFER 32 /* To use with change_state_req::affected_types */ #define AFFECTED_STATES_ALL 0x3F /* Auto-answer delay in seconds */ #define AUTO_ANSWER_DELAY_S 3 struct release_id_req { struct ofono_voicecall *vc; ofono_voicecall_cb_t cb; void *data; int id; }; struct change_state_req { struct ofono_voicecall *vc; ofono_voicecall_cb_t cb; void *data; /* Call states affected by a local release (1 << enum call_status) */ int affected_types; }; struct lastcause_req { struct ofono_voicecall *vc; int id; }; /* Data for dial after swap */ struct hold_before_dial_req { struct ofono_voicecall *vc; struct ofono_phone_number dial_ph; enum ofono_clir_option dial_clir; }; static void send_one_dtmf(struct ril_voicecall_data *vd); static void clear_dtmf_queue(struct ril_voicecall_data *vd); static void lastcause_cb(struct ril_msg *message, gpointer user_data) { struct lastcause_req *reqdata = user_data; struct ofono_voicecall *vc = reqdata->vc; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); enum ofono_disconnect_reason reason; reason = g_ril_reply_parse_call_fail_cause(vd->ril, message); DBG("Call %d ended with reason %d", reqdata->id, reason); ofono_voicecall_disconnected(vc, reqdata->id, reason, NULL); } static gboolean auto_answer_call(gpointer user_data) { struct ofono_voicecall *vc = user_data; DBG(""); ril_answer(vc, NULL, NULL); return FALSE; } static gboolean is_auto_answer(struct ril_voicecall_data *vd, struct ofono_call *call) { static const char test_mcc_mnc_1[] = "00101"; static const char test_mcc_mnc_2[] = "001001"; const char *imsi; struct ofono_sim *sim; if (call->status != CALL_STATUS_INCOMING) return FALSE; sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, vd->modem); if (sim == NULL) return FALSE; imsi = ofono_sim_get_imsi(sim); if (imsi == NULL) return FALSE; if (strncmp(imsi, test_mcc_mnc_1, sizeof(test_mcc_mnc_1) - 1) == 0 || strncmp(imsi, test_mcc_mnc_2, sizeof(test_mcc_mnc_2) - 1) == 0) { ofono_info("Auto answering incoming call, imsi is %s", imsi); return TRUE; } return FALSE; } static void clcc_poll_cb(struct ril_msg *message, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); int reqid = RIL_REQUEST_LAST_CALL_FAIL_CAUSE; GSList *calls; GSList *n, *o; struct ofono_call *nc, *oc; /* * We consider all calls have been dropped if there is no radio, which * happens, for instance, when flight mode is set whilst in a call. */ if (message->error != RIL_E_SUCCESS && message->error != RIL_E_RADIO_NOT_AVAILABLE) { ofono_error("We are polling CLCC and received an error"); ofono_error("All bets are off for call management"); return; } calls = g_ril_reply_parse_get_calls(vd->ril, message); n = calls; o = vd->calls; while (n || o) { nc = n ? n->data : NULL; oc = o ? o->data : NULL; /* TODO: Add comments explaining call id handling */ if (oc && (nc == NULL || (nc->id > oc->id))) { if (vd->local_release & (1 << oc->id)) { ofono_voicecall_disconnected(vc, oc->id, OFONO_DISCONNECT_REASON_LOCAL_HANGUP, NULL); } else if (message->error == RIL_E_RADIO_NOT_AVAILABLE) { ofono_voicecall_disconnected(vc, oc->id, OFONO_DISCONNECT_REASON_ERROR, NULL); } else { /* Get disconnect cause before calling core */ struct lastcause_req *reqdata = g_try_new0(struct lastcause_req, 1); if (reqdata != NULL) { reqdata->vc = user_data; reqdata->id = oc->id; g_ril_send(vd->ril, reqid, NULL, lastcause_cb, reqdata, g_free); } } clear_dtmf_queue(vd); o = o->next; } else if (nc && (oc == NULL || (nc->id < oc->id))) { /* new call, signal it */ if (nc->type) { ofono_voicecall_notify(vc, nc); if (vd->cb) { struct ofono_error error; ofono_voicecall_cb_t cb = vd->cb; decode_ril_error(&error, "OK"); cb(&error, vd->data); vd->cb = NULL; vd->data = NULL; } if (is_auto_answer(vd, nc)) g_timeout_add_seconds( AUTO_ANSWER_DELAY_S, auto_answer_call, vc); } n = n->next; } else { /* * Always use the clip_validity from old call * the only place this is truly told to us is * in the CLIP notify, the rest are fudged * anyway. Useful when RING, CLIP is used, * and we're forced to use CLCC and clip_validity * is 1 */ if (oc->clip_validity == 1) nc->clip_validity = oc->clip_validity; nc->cnap_validity = oc->cnap_validity; /* * CDIP doesn't arrive as part of CLCC, always * re-use from the old call */ memcpy(&nc->called_number, &oc->called_number, sizeof(oc->called_number)); /* * If the CLIP is not provided and the CLIP never * arrives, or RING is used, then signal the call * here */ if (nc->status == CALL_STATUS_INCOMING && (vd->flags & FLAG_NEED_CLIP)) { if (nc->type) ofono_voicecall_notify(vc, nc); vd->flags &= ~FLAG_NEED_CLIP; } else if (memcmp(nc, oc, sizeof(*nc)) && nc->type) ofono_voicecall_notify(vc, nc); n = n->next; o = o->next; } } g_slist_foreach(vd->calls, (GFunc) g_free, NULL); g_slist_free(vd->calls); vd->calls = calls; vd->local_release = 0; } gboolean ril_poll_clcc(gpointer user_data) { struct ofono_voicecall *vc = user_data; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); g_ril_send(vd->ril, RIL_REQUEST_GET_CURRENT_CALLS, NULL, clcc_poll_cb, vc, NULL); vd->clcc_source = 0; return FALSE; } static void generic_cb(struct ril_msg *message, gpointer user_data) { struct change_state_req *req = user_data; struct ril_voicecall_data *vd = ofono_voicecall_get_data(req->vc); struct ofono_error error; if (message->error == RIL_E_SUCCESS) { decode_ril_error(&error, "OK"); } else { decode_ril_error(&error, "FAIL"); goto out; } g_ril_print_response_no_args(vd->ril, message); if (req->affected_types) { GSList *l; struct ofono_call *call; for (l = vd->calls; l; l = l->next) { call = l->data; if (req->affected_types & (1 << call->status)) vd->local_release |= (1 << call->id); } } out: g_ril_send(vd->ril, RIL_REQUEST_GET_CURRENT_CALLS, NULL, clcc_poll_cb, req->vc, NULL); /* We have to callback after we schedule a poll if required */ if (req->cb) req->cb(&error, req->data); } static int ril_template(const guint rreq, struct ofono_voicecall *vc, GRilResponseFunc func, unsigned int affected_types, gpointer pdata, ofono_voicecall_cb_t cb, void *data) { struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); struct change_state_req *req = g_try_new0(struct change_state_req, 1); int ret; if (req == NULL) goto error; req->vc = vc; req->cb = cb; req->data = data; req->affected_types = affected_types; ret = g_ril_send(vd->ril, rreq, pdata, func, req, g_free); if (ret > 0) return ret; error: g_free(req); if (cb) CALLBACK_WITH_FAILURE(cb, data); return 0; } static void rild_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_voicecall *vc = cbd->user; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); ofono_voicecall_cb_t cb = cbd->cb; struct ofono_error error; /* * DIAL_MODIFIED_TO_DIAL means redirection. The call we will see when * polling will have a different called number. */ if (message->error == RIL_E_SUCCESS || (g_ril_vendor(vd->ril) == OFONO_RIL_VENDOR_AOSP && message->error == RIL_E_DIAL_MODIFIED_TO_DIAL)) { decode_ril_error(&error, "OK"); } else { decode_ril_error(&error, "FAIL"); goto out; } g_ril_print_response_no_args(vd->ril, message); /* CLCC will update the oFono call list with proper ids */ if (!vd->clcc_source) vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL, ril_poll_clcc, vc); /* we cannot answer just yet since we don't know the call id */ vd->cb = cb; vd->data = cbd->data; return; out: cb(&error, cbd->data); } static void dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, enum ofono_clir_option clir, ofono_voicecall_cb_t cb, void *data) { struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); struct cb_data *cbd = cb_data_new(cb, data, vc); struct parcel rilp; g_ril_request_dial(vd->ril, ph, clir, &rilp); /* Send request to RIL */ if (g_ril_send(vd->ril, RIL_REQUEST_DIAL, &rilp, rild_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void hold_before_dial_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct hold_before_dial_req *req = cbd->user; struct ril_voicecall_data *vd = ofono_voicecall_get_data(req->vc); ofono_voicecall_cb_t cb = cbd->cb; if (message->error != RIL_E_SUCCESS) { g_free(req); CALLBACK_WITH_FAILURE(cb, cbd->data); return; } g_ril_print_response_no_args(vd->ril, message); /* Current calls held: we can dial now */ dial(req->vc, &req->dial_ph, req->dial_clir, cb, cbd->data); g_free(req); } void ril_dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, enum ofono_clir_option clir, ofono_voicecall_cb_t cb, void *data) { struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); int current_active = 0; struct ofono_call *call; GSList *l; /* Check for current active calls */ for (l = vd->calls; l; l = l->next) { call = l->data; if (call->status == CALL_STATUS_ACTIVE) { current_active = 1; break; } } /* * The network will put current active calls on hold. In some cases * (mako), the modem also updates properly the state. In others * (maguro), we need to explicitly set the state to held. In both cases * we send a request for holding the active call, as it is not harmful * when it is not really needed, and is what Android does. */ if (current_active) { struct hold_before_dial_req *req; struct cb_data *cbd; req = g_malloc0(sizeof(*req)); req->vc = vc; req->dial_ph = *ph; req->dial_clir = clir; cbd = cb_data_new(cb, data, req); if (g_ril_send(vd->ril, RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE, NULL, hold_before_dial_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } else { dial(vc, ph, clir, cb, data); } } void ril_hangup_all(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); struct parcel rilp; struct ofono_error error; struct ofono_call *call; GSList *l; for (l = vd->calls; l; l = l->next) { call = l->data; if (call->status == CALL_STATUS_INCOMING) { /* * Need to use this request so that declined * calls in this state, are properly forwarded * to voicemail. REQUEST_HANGUP doesn't do the * right thing for some operators, causing the * caller to hear a fast busy signal. */ ril_template(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, vc, generic_cb, AFFECTED_STATES_ALL, NULL, NULL, NULL); } else { /* TODO: Hangup just the active ones once we have call * state tracking (otherwise it can't handle ringing) */ g_ril_request_hangup(vd->ril, call->id, &rilp); /* Send request to RIL */ ril_template(RIL_REQUEST_HANGUP, vc, generic_cb, AFFECTED_STATES_ALL, &rilp, NULL, NULL); } } /* TODO: Deal in case of an error at hungup */ decode_ril_error(&error, "OK"); cb(&error, data); } void ril_hangup_specific(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); struct parcel rilp; DBG("Hanging up call with id %d", id); g_ril_request_hangup(vd->ril, id, &rilp); /* Send request to RIL */ ril_template(RIL_REQUEST_HANGUP, vc, generic_cb, AFFECTED_STATES_ALL, &rilp, cb, data); } void ril_call_state_notify(struct ril_msg *message, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); g_ril_print_unsol_no_args(vd->ril, message); /* Just need to request the call list again */ ril_poll_clcc(vc); return; } static void ril_ss_notify(struct ril_msg *message, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); struct unsol_supp_svc_notif *unsol; unsol = g_ril_unsol_parse_supp_svc_notif(vd->ril, message); if (unsol == NULL) { ofono_error("%s: Parsing error", __func__); return; } DBG("RIL data: MT/MO: %i, code: %i, index: %i", unsol->notif_type, unsol->code, unsol->index); /* 0 stands for MO intermediate, 1 for MT unsolicited */ /* TODO How do we know the affected call? Refresh call list? */ if (unsol->notif_type == 1) ofono_voicecall_ssn_mt_notify( vc, 0, unsol->code, unsol->index, &unsol->number); else ofono_voicecall_ssn_mo_notify(vc, 0, unsol->code, unsol->index); g_ril_unsol_free_supp_svc_notif(unsol); } void ril_answer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { DBG("Answering current call"); /* Send request to RIL */ ril_template(RIL_REQUEST_ANSWER, vc, generic_cb, 0, NULL, cb, data); } static void ril_send_dtmf_cb(struct ril_msg *message, gpointer user_data) { struct ril_voicecall_data *vd = user_data; if (message->error == RIL_E_SUCCESS) { /* Remove sent DTMF character from queue */ gchar *tmp_tone_queue = g_strdup(vd->tone_queue + 1); int remaining = strlen(tmp_tone_queue); memcpy(vd->tone_queue, tmp_tone_queue, remaining); vd->tone_queue[remaining] = '\0'; g_free(tmp_tone_queue); vd->tone_pending = FALSE; if (remaining > 0) send_one_dtmf(vd); } else { DBG("error=%d", message->error); clear_dtmf_queue(vd); } } static void send_one_dtmf(struct ril_voicecall_data *vd) { struct parcel rilp; if (vd->tone_pending == TRUE) return; /* RIL request pending */ if (strlen(vd->tone_queue) == 0) return; /* nothing to send */ g_ril_request_dtmf(vd->ril, vd->tone_queue[0], &rilp); g_ril_send(vd->ril, RIL_REQUEST_DTMF, &rilp, ril_send_dtmf_cb, vd, NULL); vd->tone_pending = TRUE; } void ril_send_dtmf(struct ofono_voicecall *vc, const char *dtmf, ofono_voicecall_cb_t cb, void *data) { struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); struct ofono_error error; DBG("Queue '%s'", dtmf); /* * Queue any incoming DTMF (up to MAX_DTMF_BUFFER characters), * send them to RIL one-by-one, immediately call back * core with no error */ g_strlcat(vd->tone_queue, dtmf, MAX_DTMF_BUFFER); send_one_dtmf(vd); /* We don't really care about errors here */ decode_ril_error(&error, "OK"); cb(&error, data); } static void clear_dtmf_queue(struct ril_voicecall_data *vd) { g_free(vd->tone_queue); vd->tone_queue = g_strnfill(MAX_DTMF_BUFFER + 1, '\0'); vd->tone_pending = FALSE; } void ril_create_multiparty(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ril_template(RIL_REQUEST_CONFERENCE, vc, generic_cb, 0, NULL, cb, data); } void ril_private_chat(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); struct parcel rilp; g_ril_request_separate_conn(vd->ril, id, &rilp); /* Send request to RIL */ ril_template(RIL_REQUEST_SEPARATE_CONNECTION, vc, generic_cb, 0, &rilp, cb, data); } void ril_swap_without_accept(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ril_template(RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE, vc, generic_cb, 0, NULL, cb, data); } void ril_hold_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ril_template(RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE, vc, generic_cb, 0, NULL, cb, data); } void ril_release_all_held(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ril_template(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, vc, generic_cb, 0, NULL, cb, data); } void ril_release_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ril_template(RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, vc, generic_cb, 0, NULL, cb, data); } void ril_set_udub(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ril_template(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, vc, generic_cb, 0, NULL, cb, data); } static gboolean enable_supp_svc(gpointer user_data) { struct ofono_voicecall *vc = user_data; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); struct parcel rilp; g_ril_request_set_supp_svc_notif(vd->ril, &rilp); g_ril_send(vd->ril, RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, &rilp, NULL, vc, NULL); /* Makes this a single shot */ return FALSE; } static gboolean ril_delayed_register(gpointer user_data) { struct ofono_voicecall *vc = user_data; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); ofono_voicecall_register(vc); /* Initialize call list */ ril_poll_clcc(vc); /* Unsol when call state changes */ g_ril_register(vd->ril, RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, ril_call_state_notify, vc); /* Unsol when call set on hold */ g_ril_register(vd->ril, RIL_UNSOL_SUPP_SVC_NOTIFICATION, ril_ss_notify, vc); /* request supplementary service notifications*/ enable_supp_svc(vc); /* This makes the timeout a single-shot */ return FALSE; } void ril_voicecall_start(struct ril_voicecall_driver_data *driver_data, struct ofono_voicecall *vc, unsigned int vendor, struct ril_voicecall_data *vd) { vd->ril = g_ril_clone(driver_data->gril); vd->modem = driver_data->modem; vd->vendor = vendor; vd->cb = NULL; vd->data = NULL; clear_dtmf_queue(vd); ofono_voicecall_set_data(vc, vd); /* * ofono_voicecall_register() needs to be called after * the driver has been set in ofono_voicecall_create(), * which calls this function. Most other drivers make * some kind of capabilities query to the modem, and then * call register in the callback; we use an idle event instead. */ g_idle_add(ril_delayed_register, vc); } int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, void *data) { struct ril_voicecall_driver_data *driver_data = data; struct ril_voicecall_data *vd; vd = g_try_new0(struct ril_voicecall_data, 1); if (vd == NULL) return -ENOMEM; ril_voicecall_start(driver_data, vc, vendor, vd); return 0; } void ril_voicecall_remove(struct ofono_voicecall *vc) { struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); if (vd->clcc_source) g_source_remove(vd->clcc_source); g_slist_foreach(vd->calls, (GFunc) g_free, NULL); g_slist_free(vd->calls); ofono_voicecall_set_data(vc, NULL); g_ril_unref(vd->ril); g_free(vd->tone_queue); g_free(vd); } static struct ofono_voicecall_driver driver = { .name = RILMODEM, .probe = ril_voicecall_probe, .remove = ril_voicecall_remove, .dial = ril_dial, .answer = ril_answer, .hangup_all = ril_hangup_all, .release_specific = ril_hangup_specific, .send_tones = ril_send_dtmf, .create_multiparty = ril_create_multiparty, .private_chat = ril_private_chat, .swap_without_accept = ril_swap_without_accept, .hold_all_active = ril_hold_all_active, .release_all_held = ril_release_all_held, .set_udub = ril_set_udub, .release_all_active = ril_release_all_active, }; void ril_voicecall_init(void) { ofono_voicecall_driver_register(&driver); } void ril_voicecall_exit(void) { ofono_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/call-forwarding.c0000644000015600001650000002026112671500024025105 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2013 Jolla Ltd * Contact: Jussi Kangas * Copyright (C) 2014 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gril.h" #include "grilrequest.h" #include "grilreply.h" #include "grilunsol.h" #include "rilmodem.h" #include "common.h" enum cf_action { CF_ACTION_DISABLE, CF_ACTION_ENABLE, CF_ACTION_INTERROGATE, CF_ACTION_REGISTRATION, CF_ACTION_ERASURE, }; struct forw_data { GRil *ril; enum cf_action last_action; int last_cls; }; static const char *cf_action_to_string(enum cf_action action) { switch (action) { case CF_ACTION_DISABLE: return "DISABLE"; case CF_ACTION_ENABLE: return "ENABLE"; case CF_ACTION_INTERROGATE: return "INTERROGATE"; case CF_ACTION_REGISTRATION: return "REGISTRATION"; case CF_ACTION_ERASURE: return "ERASURE"; } return NULL; } static void ril_query_call_fwd_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct forw_data *fd = ofono_call_forwarding_get_data(cbd->user); ofono_call_forwarding_query_cb_t cb = cbd->cb; struct ofono_call_forwarding_condition *list; unsigned int list_size; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: rild error: %s", __func__, ril_error_to_string(message->error)); goto error; } list = g_ril_reply_parse_query_call_fwd(fd->ril, message, &list_size); /* * From atmodem: * * Specification is really unclear about this * generate status=0 for all classes just in case */ if (list_size == 0) { list = g_new0(struct ofono_call_forwarding_condition, 1); list_size = 1; list->status = 0; list->cls = fd->last_cls; } else if (list == NULL) { goto error; } CALLBACK_WITH_SUCCESS(cb, (int) list_size, list, cbd->data); g_free(list); return; error: CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data); } static void ril_set_forward_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_forwarding_set_cb_t cb = cbd->cb; struct forw_data *fd = ofono_call_forwarding_get_data(cbd->user); if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(fd->ril, message); CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { ofono_error("%s: CF %s failed; rild error: %s", __func__, cf_action_to_string(fd->last_action), ril_error_to_string(message->error)); CALLBACK_WITH_FAILURE(cb, cbd->data); } } static int ril_send_forward_cmd(int type, int cls, const struct ofono_phone_number *number, int time, struct cb_data *cbd, enum cf_action action) { struct ofono_call_forwarding *cf = cbd->user; struct forw_data *fd = ofono_call_forwarding_get_data(cf); struct parcel rilp; struct req_call_fwd fwd_req; int ret = 0, request; GRilResponseFunc response_func; if (action == CF_ACTION_INTERROGATE) { request = RIL_REQUEST_QUERY_CALL_FORWARD_STATUS; response_func = ril_query_call_fwd_cb; } else { request = RIL_REQUEST_SET_CALL_FORWARD; response_func = ril_set_forward_cb; } DBG("%s - %s", ril_request_id_to_string(request), cf_action_to_string(action)); /* * Modem seems to respond with error to all queries * or settings made with bearer class * BEARER_CLASS_DEFAULT. Design decision: If given * class is BEARER_CLASS_DEFAULT let's map it to * SERVICE_CLASS_NONE as with it e.g. ./send-ussd '*21*#' * returns cls:53 i.e. 1+4+16+32 as service class. */ if (cls == BEARER_CLASS_DEFAULT) cls = SERVICE_CLASS_NONE; fd->last_action = action; fd->last_cls = cls; fwd_req.action = (int) action; fwd_req.type = type; fwd_req.cls = cls; fwd_req.number = number; /* * time has no real meaing for action commands other * then registration, so if not needed, set arbitrary * 60s time so rild doesn't return an error. */ if (time == -1) fwd_req.time = 60; else fwd_req.time = time; g_ril_request_call_fwd(fd->ril, &fwd_req, &rilp); ret = g_ril_send(fd->ril, request, &rilp, response_func, cbd, g_free); if (ret == 0) ofono_error("%s: CF action %s failed", __func__, cf_action_to_string(action)); return ret; } static void ril_activate(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data, cf); if (ril_send_forward_cmd(type, cls, NULL, -1, cbd, CF_ACTION_ENABLE) == 0) { CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } } static void ril_erasure(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data, cf); if (ril_send_forward_cmd(type, cls, NULL, -1, cbd, CF_ACTION_ERASURE) == 0) { CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } } static void ril_deactivate(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data, cf); if (ril_send_forward_cmd(type, cls, NULL, -1, cbd, CF_ACTION_DISABLE) == 0) { CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } } static void ril_registration(struct ofono_call_forwarding *cf, int type, int cls, const struct ofono_phone_number *number, int time, ofono_call_forwarding_set_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data, cf); if (ril_send_forward_cmd(type, cls, number, time, cbd, CF_ACTION_REGISTRATION) == 0) { CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } } static void ril_query(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_query_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data, cf); if (ril_send_forward_cmd(type, cls, NULL, -1, cbd, CF_ACTION_INTERROGATE) == 0) { CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data); g_free(cbd); } } static gboolean ril_delayed_register(gpointer user_data) { struct ofono_call_forwarding *cf = user_data; ofono_call_forwarding_register(cf); return FALSE; } static int ril_call_forwarding_probe(struct ofono_call_forwarding *cf, unsigned int vendor, void *user) { GRil *ril = user; struct forw_data *fd; fd = g_try_new0(struct forw_data, 1); if (fd == NULL) return -ENOMEM; fd->ril = g_ril_clone(ril); ofono_call_forwarding_set_data(cf, fd); /* * ofono_call_forwarding_register() needs to be called after * the driver has been set in ofono_call_forwarding_create(), * which calls this function. Most other drivers make * some kind of capabilities query to the modem, and then * call register in the callback; we use an idle event instead. */ g_idle_add(ril_delayed_register, cf); return 0; } static void ril_call_forwarding_remove(struct ofono_call_forwarding *cf) { struct forw_data *data = ofono_call_forwarding_get_data(cf); ofono_call_forwarding_set_data(cf, NULL); g_ril_unref(data->ril); g_free(data); } static struct ofono_call_forwarding_driver driver = { .name = RILMODEM, .probe = ril_call_forwarding_probe, .remove = ril_call_forwarding_remove, .erasure = ril_erasure, .deactivation = ril_deactivate, .query = ril_query, .registration = ril_registration, .activation = ril_activate }; void ril_call_forwarding_init(void) { ofono_call_forwarding_driver_register(&driver); } void ril_call_forwarding_exit(void) { ofono_call_forwarding_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/phonebook.c0000644000015600001650000006162312671500024024025 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) ST-Ericsson SA 2010. * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2013 Jolla Ltd * Contact: Jussi Kangas * Copyright (C) 2014 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gril.h" #include "simutil.h" #include "common.h" #include "rilmodem.h" #define UNUSED 0xFF #define EXT1_CP_SUBADDRESS 1 #define EXT1_ADDITIONAL_DATA 2 /* TON (Type Of Number) See TS 24.008 */ #define TON_MASK 0x70 #define TON_INTERNATIONAL 0x10 enum constructed_tag { TYPE_1_TAG = 0xA8, TYPE_2_TAG = 0xA9, TYPE_3_TAG = 0xAA }; enum file_type_tag { TYPE_ADN = 0xC0, TYPE_IAP = 0xC1, TYPE_EXT1 = 0xC2, TYPE_SNE = 0xC3, TYPE_ANR = 0xC4, TYPE_PBC = 0xC5, TYPE_GPR = 0xC6, TYPE_AAS = 0xC7, TYPE_GAS = 0xC8, TYPE_UID = 0xC9, TYPE_EMAIL = 0xCA, TYPE_CCP1 = 0xCB }; struct pb_file_info { enum constructed_tag pbr_type; int file_id; enum file_type_tag file_type; int file_length; int record_length; }; struct record_to_read { int file_id; enum file_type_tag type_tag; int record_length; int record; int adn_idx; gboolean anr_ext; /* Is it an EXT1 record for ANR? */ gboolean set_by_iap; /* Type 2 file? */ }; struct phonebook_entry { int entry; char *name; char *number; char *email; char *anr; char *sne; }; unsigned char sim_path[] = { 0x3F, 0x00, 0x7F, 0x10 }; unsigned char usim_path[] = { 0x3F, 0x00, 0x7F, 0x10, 0x5F, 0x3A }; /* * Table for BCD to utf8 conversion. See table 4.4 in TS 31.102. * BCD 0x0C indicates pause before sending following digits as DTMF tones. * BCD 0x0D is a wildcard that means "any digit". These values are mapped to * ',' and '?', following the Android/iPhone convention for the first and Nokia * convention for the second (only OEM that I have seen that supports this * feature). BCD 0x0E is reserved, we convert it to 'r'. */ static const char digit_to_utf8[] = "0123456789*#,?r\0"; /* One of these for each record in EF_PBR */ struct pb_ref_rec { GSList *pb_files; /* File ids to read (pb_file_info nodes) */ GSList *pb_next; /* Next file info to read */ GSList *pending_records; /* List of record_to_read */ GSList *next_record; /* Next record_to_read to process */ GTree *phonebook; /* Container of phonebook_entry structures */ }; struct pb_data { GSList *pb_refs; GSList *pb_ref_next; struct ofono_sim *sim; struct ofono_sim_context *sim_context; const unsigned char *df_path; size_t df_size; }; static void read_info_cb(int ok, unsigned char file_status, int total_length, int record_length, void *userdata); static gint comp_int(gconstpointer a, gconstpointer b) { int a_val = GPOINTER_TO_INT(a); int b_val = GPOINTER_TO_INT(b); return a_val - b_val; } static const struct pb_file_info * ext1_info(const GSList *pb_files) { const GSList *l; for (l = pb_files; l; l = l->next) { const struct pb_file_info *f_info = l->data; if (f_info->file_type == TYPE_EXT1) return f_info; } return NULL; } static struct phonebook_entry *handle_adn(size_t len, const unsigned char *msg, struct pb_ref_rec *ref, int adn_idx) { unsigned name_length = len - 14; unsigned number_start = name_length; unsigned number_length; unsigned extension_record = UNUSED; unsigned i, prefix; char *number = NULL; char *name = sim_string_to_utf8(msg, name_length); struct phonebook_entry *new_entry; /* Length contains also TON & NPI */ number_length = msg[number_start]; if (number_length != UNUSED && number_length != 0) { number_length--; /* '+' + number + terminator */ number = g_try_malloc0(2 * number_length + 2); if (number) { prefix = 0; if ((msg[number_start + 1] & TON_MASK) == TON_INTERNATIONAL) { number[0] = '+'; prefix = 1; } for (i = 0; i < number_length; i++) { number[2 * i + prefix] = digit_to_utf8[msg[number_start + 2 + i] & 0x0f]; number[2 * i + 1 + prefix] = digit_to_utf8[msg[number_start + 2 + i] >> 4]; } extension_record = msg[len - 1]; } } DBG("ADN name %s, number %s ", name, number); DBG("number length %d extension_record %d", 2 * number_length, extension_record); if ((name == NULL || *name == '\0') && number == NULL) goto end; new_entry = g_try_malloc0(sizeof(*new_entry)); if (new_entry == NULL) { ofono_error("%s: out of memory", __func__); goto end; } new_entry->name = name; new_entry->number = number; DBG("Creating PB entry %d with", adn_idx); DBG("name %s and number %s", new_entry->name, new_entry->number); g_tree_insert(ref->phonebook, GINT_TO_POINTER(adn_idx), new_entry); if (extension_record != UNUSED) { struct record_to_read *ext_rec = g_try_malloc0(sizeof(*ext_rec)); const struct pb_file_info *f_info = ext1_info(ref->pb_files); if (ext_rec && f_info) { ext_rec->file_id = f_info->file_id; ext_rec->type_tag = TYPE_EXT1; ext_rec->record_length = f_info->record_length; ext_rec->record = extension_record; ext_rec->adn_idx = adn_idx; ref->pending_records = g_slist_prepend(ref->pending_records, ext_rec); } } return new_entry; end: g_free(name); g_free(number); return NULL; } static void handle_iap(size_t len, const unsigned char *msg, struct pb_ref_rec *ref, const struct record_to_read *rec_data) { GSList *l; size_t i = 0; for (l = ref->pb_files; l; l = l->next) { struct pb_file_info *f_info = l->data; if (f_info->pbr_type == TYPE_2_TAG) { if (i >= len) { ofono_error("%s: EF_IAP record too small", __func__); return; } if (msg[i] != UNUSED) { struct record_to_read *new_rec = g_try_malloc0(sizeof(*new_rec)); if (new_rec == NULL) { ofono_error("%s: OOM", __func__); return; } DBG("type 0x%X record %d", f_info->file_type, msg[i]); new_rec->file_id = f_info->file_id; new_rec->type_tag = f_info->file_type; new_rec->record_length = f_info->record_length; new_rec->record = msg[i]; new_rec->adn_idx = rec_data->adn_idx; new_rec->anr_ext = FALSE; new_rec->set_by_iap = TRUE; ref->pending_records = g_slist_prepend(ref->pending_records, new_rec); } ++i; } } } static void handle_sne(size_t len, const unsigned char *msg, struct pb_ref_rec *ref, const struct record_to_read *rec_data) { char *sne; /* There are additional fields for type 2 files */ if (rec_data->set_by_iap) len -= 2; sne = sim_string_to_utf8(msg, len); if (sne && *sne != '\0') { struct phonebook_entry *entry; entry = g_tree_lookup(ref->phonebook, GINT_TO_POINTER(rec_data->adn_idx)); if (entry) { /* If one already exists, delete it */ if (entry->sne) g_free(entry->sne); DBG("Adding SNE %s to %d", sne, rec_data->adn_idx); DBG("name %s", entry->name); entry->sne = sne; } else { g_free(sne); } } else { g_free(sne); } } static void handle_anr(size_t len, const unsigned char *msg, struct pb_ref_rec *ref, const struct record_to_read *rec_data) { unsigned number_length; unsigned extension_record; unsigned aas_record; unsigned i, prefix; char *anr; struct phonebook_entry *entry; if (len < 15) { ofono_error("%s: bad EF_ANR record size", __func__); return; } aas_record = msg[0]; if (aas_record == UNUSED) return; DBG("ANR %d", aas_record); /* Length contains also TON & NPI */ number_length = msg[1]; if (number_length < 2) return; number_length--; /* '+' + number + terminator */ anr = g_try_malloc0(2 * number_length + 2); if (anr == NULL) return; prefix = 0; if ((msg[2] & TON_MASK) == TON_INTERNATIONAL) { anr[0] = '+'; prefix = 1; } for (i = 0; i < number_length; i++) { anr[2 * i + prefix] = digit_to_utf8[msg[3 + i] & 0x0f]; anr[2 * i + 1 + prefix] = digit_to_utf8[msg[3 + i] >> 4]; } entry = g_tree_lookup(ref->phonebook, GINT_TO_POINTER(rec_data->adn_idx)); if (entry == NULL) { g_free(anr); return; } /* If one already exists, delete it */ if (entry->anr) g_free(entry->anr); DBG("Adding ANR %s to %d", anr, rec_data->adn_idx); DBG("name %s", entry->name); entry->anr = anr; extension_record = msg[14]; DBG("ANR to entry %d number %s number length %d", rec_data->adn_idx, anr, number_length); DBG("extension_record %d aas %d", extension_record, aas_record); if (extension_record != UNUSED) { struct record_to_read *ext_rec = g_try_malloc0(sizeof(*ext_rec)); const struct pb_file_info *f_info = ext1_info(ref->pb_files); if (ext_rec && f_info) { ext_rec->file_id = f_info->file_id; ext_rec->type_tag = TYPE_EXT1; ext_rec->record_length = f_info->record_length; ext_rec->record = extension_record; ext_rec->adn_idx = rec_data->adn_idx; ext_rec->anr_ext = TRUE; ref->pending_records = g_slist_prepend(ref->pending_records, ext_rec); } } } static void handle_email(size_t len, const unsigned char *msg, struct pb_ref_rec *ref, const struct record_to_read *rec_data) { char *email; struct phonebook_entry *entry; /* There are additional fields for type 2 files */ if (rec_data->set_by_iap) len -= 2; email = sim_string_to_utf8(msg, len); if (email == NULL || *email == '\0') { g_free(email); return; } entry = g_tree_lookup(ref->phonebook, GINT_TO_POINTER(rec_data->adn_idx)); if (entry == NULL) { g_free(email); return; } /* if one already exists, delete it */ if (entry->email) g_free(entry->email); DBG("Adding email to entry %d", rec_data->adn_idx); DBG("name %s", entry->name); entry->email = email; } static void handle_ext1(size_t len, const unsigned char *msg, struct pb_ref_rec *ref, const struct record_to_read *rec_data) { unsigned number_length, i, next_extension_record; struct phonebook_entry *entry; char *ext_number; if (len < 13) { ofono_error("%s: bad EF_EXT1 record size", __func__); return; } /* Check if there is more extension data */ next_extension_record = msg[12]; if (next_extension_record != UNUSED) { struct record_to_read *ext_rec = g_try_malloc0(sizeof(*ext_rec)); const struct pb_file_info *f_info = ext1_info(ref->pb_files); if (ext_rec && f_info) { DBG("next_extension_record %d", next_extension_record); ext_rec->file_id = f_info->file_id; ext_rec->record_length = f_info->record_length; ext_rec->type_tag = TYPE_EXT1; ext_rec->record = next_extension_record; ext_rec->adn_idx = rec_data->adn_idx; ext_rec->anr_ext = rec_data->anr_ext; ref->pending_records = g_slist_prepend(ref->pending_records, ext_rec); } } if (msg[0] != EXT1_ADDITIONAL_DATA) { DBG("EXT1 record with subaddress ignored"); return; } number_length = msg[1]; ext_number = g_try_malloc0(2 * number_length + 1); if (ext_number == NULL) return; for (i = 0; i < number_length; i++) { ext_number[2 * i] = digit_to_utf8[msg[2 + i] & 0x0f]; ext_number[2 * i + 1] = digit_to_utf8[msg[2 + i] >> 4]; } DBG("Number extension %s", ext_number); DBG("number length %d", number_length); DBG("Looking for ADN entry %d", rec_data->adn_idx); entry = g_tree_lookup(ref->phonebook, GINT_TO_POINTER(rec_data->adn_idx)); if (entry == NULL) { g_free(ext_number); return; } if (rec_data->anr_ext) { char *anr = entry->anr; entry->anr = g_strconcat(anr, ext_number, NULL); g_free(anr); } else { char *number = entry->number; entry->number = g_strconcat(number, ext_number, NULL); g_free(number); } g_free(ext_number); } static const char *file_tag_to_string(enum file_type_tag tag) { switch (tag) { case TYPE_ADN: return "ADN"; case TYPE_IAP: return "IAP"; case TYPE_EXT1: return "EXT1"; case TYPE_SNE: return "SNE"; case TYPE_ANR: return "ANR"; case TYPE_PBC: return "PBC"; case TYPE_GPR: return "GPR"; case TYPE_AAS: return "AAS"; case TYPE_GAS: return "GAS"; case TYPE_UID: return "UID"; case TYPE_EMAIL: return "EMAIL"; case TYPE_CCP1: return "CCP1"; default: return ""; } } static void decode_read_response(const struct record_to_read *rec_data, const unsigned char *msg, size_t len, struct pb_ref_rec *ref) { DBG("Decoding %s type record", file_tag_to_string(rec_data->type_tag)); switch (rec_data->type_tag) { case TYPE_IAP: handle_iap(len, msg, ref, rec_data); break; case TYPE_SNE: handle_sne(len, msg, ref, rec_data); break; case TYPE_ANR: handle_anr(len, msg, ref, rec_data); break; case TYPE_EMAIL: handle_email(len, msg, ref, rec_data); break; case TYPE_EXT1: handle_ext1(len, msg, ref, rec_data); break; default: DBG("Skipping type"); break; } } static gboolean export_entry(gpointer key, gpointer value, gpointer data) { struct ofono_phonebook *pb = data; struct phonebook_entry *entry = value; ofono_phonebook_entry(pb, -1, entry->number, -1, entry->name, -1, NULL, entry->anr, -1, entry->sne, entry->email, NULL, NULL); g_free(entry->name); g_free(entry->number); g_free(entry->email); g_free(entry->anr); g_free(entry->sne); g_free(entry); return FALSE; } static void export_and_return(gboolean ok, struct cb_data *cbd) { struct ofono_phonebook *pb = cbd->user; ofono_phonebook_cb_t cb = cbd->cb; struct pb_data *pbd = ofono_phonebook_get_data(pb); GSList *l; DBG("phonebook fully read"); for (l = pbd->pb_refs; l != NULL; l = l->next) { struct pb_ref_rec *ref = l->data; g_tree_foreach(ref->phonebook, export_entry, pb); g_tree_destroy(ref->phonebook); g_slist_free_full(ref->pending_records, g_free); g_slist_free_full(ref->pb_files, g_free); } g_slist_free_full(pbd->pb_refs, g_free); pbd->pb_refs = NULL; if (ok) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void read_record_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct cb_data *cbd = userdata; struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); struct pb_ref_rec *ref = pbd->pb_ref_next->data; struct record_to_read *rec; if (!ok) { ofono_error("%s: error %d", __func__, ok); export_and_return(FALSE, cbd); return; } DBG("ok %d; total_length %d; record %d; record_length %d", ok, total_length, record, record_length); rec = ref->next_record->data; /* This call might add elements to pending_records */ decode_read_response(rec, data, record_length, ref); ref->pending_records = g_slist_remove(ref->pending_records, rec); g_free(rec); if (ref->pending_records) { struct record_to_read *rec; ref->next_record = ref->pending_records; rec = ref->next_record->data; ofono_sim_read_record(pbd->sim_context, rec->file_id, OFONO_SIM_FILE_STRUCTURE_FIXED, rec->record, rec->record_length, pbd->df_path, pbd->df_size, read_record_cb, cbd); } else { /* Read files from next EF_PBR record, if any */ pbd->pb_ref_next = pbd->pb_ref_next->next; if (pbd->pb_ref_next == NULL) { export_and_return(TRUE, cbd); } else { struct pb_ref_rec *ref; DBG("Next EFpbr record"); ref = pbd->pb_ref_next->data; if (!ref->pb_files) { export_and_return(TRUE, cbd); } else { struct pb_file_info *file_info; ref->pb_next = ref->pb_files; file_info = ref->pb_files->data; ofono_sim_read_info(pbd->sim_context, file_info->file_id, OFONO_SIM_FILE_STRUCTURE_FIXED, pbd->df_path, pbd->df_size, read_info_cb, cbd); } } } } static void pb_adn_cb(int ok, int total_length, int record, const unsigned char *data, int record_length, void *userdata) { struct cb_data *cbd = userdata; struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); struct pb_ref_rec *ref = pbd->pb_ref_next->data; GSList *l; if (!ok) { ofono_error("%s: error %d", __func__, ok); export_and_return(FALSE, cbd); return; } DBG("ok %d; total_length %d; record %d; record_length %d", ok, total_length, record, record_length); if (handle_adn(record_length, data, ref, record) != NULL) { /* Add type 1 records */ for (l = ref->pb_files; l; l = l->next) { const struct pb_file_info *f_info = l->data; struct record_to_read *ext_rec; if (f_info->pbr_type == TYPE_1_TAG && f_info->file_type != TYPE_ADN) { ext_rec = g_try_malloc0(sizeof(*ext_rec)); if (ext_rec == NULL) break; ext_rec->file_id = f_info->file_id; ext_rec->type_tag = f_info->file_type; ext_rec->record_length = f_info->record_length; ext_rec->record = record; ext_rec->adn_idx = record; ref->pending_records = g_slist_prepend(ref->pending_records, ext_rec); } } } if (record*record_length >= total_length) { DBG("All ADN records read: reading additional files"); if (ref->pending_records) { struct record_to_read *rec; ref->next_record = ref->pending_records; rec = ref->next_record->data; ofono_sim_read_record(pbd->sim_context, rec->file_id, OFONO_SIM_FILE_STRUCTURE_FIXED, rec->record, rec->record_length, pbd->df_path, pbd->df_size, read_record_cb, cbd); } else { export_and_return(TRUE, cbd); } } } static void read_info_cb(int ok, unsigned char file_status, int total_length, int record_length, void *userdata) { struct cb_data *cbd = userdata; struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); struct pb_file_info *file_info; struct pb_ref_rec *ref = pbd->pb_ref_next->data; file_info = ref->pb_next->data; ref->pb_next = ref->pb_next->next; if (ok) { file_info->record_length = record_length; file_info->file_length = total_length; DBG("file id %x record length %d total_length %d", file_info->file_id, record_length, total_length); } else { ofono_warn("%s: %x not found", __func__, file_info->file_id); ref->pb_files = g_slist_remove(ref->pb_files, file_info); g_free(file_info); } if (ref->pb_next == NULL) { if (ref->pb_files == NULL) { ofono_warn("%s: no phonebook on SIM", __func__); export_and_return(FALSE, cbd); return; } /* Read full contents of the master file */ file_info = ref->pb_files->data; ofono_sim_read_path(pbd->sim_context, file_info->file_id, OFONO_SIM_FILE_STRUCTURE_FIXED, pbd->df_path, pbd->df_size, pb_adn_cb, cbd); } else { file_info = ref->pb_next->data; ofono_sim_read_info(pbd->sim_context, file_info->file_id, OFONO_SIM_FILE_STRUCTURE_FIXED, pbd->df_path, pbd->df_size, read_info_cb, cbd); } } static void start_sim_app_read(struct cb_data *cbd) { struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); struct pb_ref_rec *ref_rec; struct pb_file_info *f_info; struct pb_file_info *f_ext1; pbd->df_path = sim_path; pbd->df_size = sizeof(sim_path); ref_rec = g_try_malloc0(sizeof(*ref_rec)); if (ref_rec == NULL) { ofono_error("%s: OOM", __func__); export_and_return(FALSE, cbd); return; } ref_rec->phonebook = g_tree_new(comp_int); /* Only EF_ADN and EF_EXT1 read for SIM */ f_info = g_try_malloc0(sizeof(*f_info)); if (f_info == NULL) { ofono_error("%s: OOM", __func__); export_and_return(FALSE, cbd); return; } f_info->file_id = SIM_EFADN_FILEID; f_info->pbr_type = TYPE_1_TAG; f_info->file_type = TYPE_ADN; ref_rec->pb_files = g_slist_append(ref_rec->pb_files, f_info); f_ext1 = g_try_malloc0(sizeof(*f_ext1)); if (f_ext1 == NULL) { ofono_error("%s: OOM", __func__); export_and_return(FALSE, cbd); return; } f_ext1->file_id = SIM_EFEXT1_FILEID; f_ext1->pbr_type = TYPE_3_TAG; f_ext1->file_type = TYPE_EXT1; ref_rec->pb_files = g_slist_append(ref_rec->pb_files, f_ext1); pbd->pb_refs = g_slist_append(pbd->pb_refs, ref_rec); pbd->pb_ref_next = pbd->pb_refs; ref_rec->pb_next = ref_rec->pb_files; /* Start reading process for MF */ ofono_sim_read_info(pbd->sim_context, f_info->file_id, OFONO_SIM_FILE_STRUCTURE_FIXED, pbd->df_path, pbd->df_size, read_info_cb, cbd); } static void pb_reference_data_cb(int ok, int total_length, int record, const unsigned char *sdata, int record_length, void *userdata) { struct cb_data *cbd = userdata; struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); const unsigned char *ptr = sdata; gboolean finished = FALSE; struct pb_ref_rec *ref_rec; DBG("total_length %d record %d record_length %d", total_length, record, record_length); if (!ok) { /* We migh have a SIM instead of USIM application: try that */ DBG("%s: error %d, trying SIM files", __func__, ok); start_sim_app_read(cbd); return; } ref_rec = g_try_malloc0(sizeof(*ref_rec)); if (ref_rec == NULL) { ofono_error("%s: OOM", __func__); export_and_return(FALSE, cbd); return; } ref_rec->phonebook = g_tree_new(comp_int); while (ptr < sdata + record_length && finished == FALSE) { int typelen, file_id, i; enum constructed_tag pbr_type = *ptr; switch (pbr_type) { case TYPE_1_TAG: case TYPE_2_TAG: case TYPE_3_TAG: typelen = *(ptr + 1); DBG("File type=%02X, len=%d", *ptr, typelen); ptr += 2; i = 0; while (i < typelen) { struct pb_file_info *file_info = g_try_new0(struct pb_file_info, 1); if (!file_info) { ofono_error("%s: OOM", __func__); export_and_return(FALSE, cbd); return; } file_id = (ptr[i + 2] << 8) + ptr[i + 3]; DBG("creating file info for File type=%02X", ptr[i]); DBG("File ID=%04X", file_id); file_info->pbr_type = pbr_type; file_info->file_type = ptr[i]; file_info->file_id = file_id; /* Keep order, important for type 2 files */ ref_rec->pb_files = g_slist_append(ref_rec->pb_files, file_info); i += ptr[i + 1] + 2; } ptr += typelen; break; default: DBG("All handled %02x", *ptr); finished = TRUE; break; } } pbd->pb_refs = g_slist_append(pbd->pb_refs, ref_rec); if (record*record_length >= total_length) { struct pb_ref_rec *ref; struct pb_file_info *file_info; DBG("All EFpbr records read"); pbd->pb_ref_next = pbd->pb_refs; ref = pbd->pb_ref_next->data; if (ref->pb_files == NULL) { ofono_error("%s: no files to read", __func__); export_and_return(FALSE, cbd); return; } ref->pb_next = ref->pb_files; file_info = ref->pb_files->data; /* Start reading process for first EF_PBR entry */ ofono_sim_read_info(pbd->sim_context, file_info->file_id, OFONO_SIM_FILE_STRUCTURE_FIXED, pbd->df_path, pbd->df_size, read_info_cb, cbd); } } static void ril_export_entries(struct ofono_phonebook *pb, const char *storage, ofono_phonebook_cb_t cb, void *data) { struct pb_data *pbd = ofono_phonebook_get_data(pb); struct cb_data *cbd; DBG("Storage %s", storage); /* Only for SIM memory */ if (strcmp(storage, "SM") != 0) { CALLBACK_WITH_FAILURE(cb, data); return; } cbd = cb_data_new(cb, data, pb); /* Assume USIM, change in case EF_PBR is not present */ pbd->df_path = usim_path; pbd->df_size = sizeof(usim_path); ofono_sim_read(pbd->sim_context, SIM_EFPBR_FILEID, OFONO_SIM_FILE_STRUCTURE_FIXED, pb_reference_data_cb, cbd); } static gboolean ril_delayed_register(gpointer user_data) { struct ofono_phonebook *pb = user_data; ofono_phonebook_register(pb); return FALSE; } static int ril_phonebook_probe(struct ofono_phonebook *pb, unsigned int vendor, void *user) { struct ofono_modem *modem = user; struct pb_data *pd = g_try_new0(struct pb_data, 1); if (pd == NULL) return -ENOMEM; pd->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem); if (pd->sim == NULL) return -ENOENT; pd->sim_context = ofono_sim_context_create(pd->sim); if (pd->sim_context == NULL) return -ENOENT; ofono_phonebook_set_data(pb, pd); g_idle_add(ril_delayed_register, pb); return 0; } static void ril_phonebook_remove(struct ofono_phonebook *pb) { struct pb_data *pbd = ofono_phonebook_get_data(pb); ofono_phonebook_set_data(pb, NULL); ofono_sim_context_free(pbd->sim_context); g_free(pbd); } static struct ofono_phonebook_driver driver = { .name = RILMODEM, .probe = ril_phonebook_probe, .remove = ril_phonebook_remove, .export_entries = ril_export_entries }; void ril_phonebook_init(void) { ofono_phonebook_driver_register(&driver); } void ril_phonebook_exit(void) { ofono_phonebook_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/call-barring.c0000644000015600001650000001457712671500073024410 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Jolla Ltd * Contact: Miia Leinonen * Copyright (C) 2014 Canonical 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. * * 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 #include #include #include #include "common.h" #include "grilrequest.h" #include "grilreply.h" #include "call-barring.h" #include "rilmodem.h" #include "ril_constants.h" struct barring_data { GRil *ril; }; static void ril_call_barring_query_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_barring_query_cb_t cb = cbd->cb; struct barring_data *bd = cbd->user; int bearer_class; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: query failed, err: %s", __func__, ril_error_to_string(message->error)); goto error; } bearer_class = g_ril_reply_parse_query_facility_lock(bd->ril, message); if (bearer_class < 0) goto error; CALLBACK_WITH_SUCCESS(cb, bearer_class, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void ril_call_barring_query(struct ofono_call_barring *cb, const char *lock, int cls, ofono_call_barring_query_cb_t callback, void *data) { struct barring_data *bd = ofono_call_barring_get_data(cb); struct cb_data *cbd = cb_data_new(callback, data, bd); struct parcel rilp; DBG("lock: %s, services to query: %d", lock, cls); /* * RIL modems do not support 7 as default bearer class. According to * TS 22.030 Annex C: When service code is not given it corresponds to * "All tele and bearer services" */ if (cls == BEARER_CLASS_DEFAULT) cls = SERVICE_CLASS_NONE; /* ril.h: password should be empty string "" when not needed */ g_ril_request_query_facility_lock(bd->ril, lock, "", cls, &rilp); if (g_ril_send(bd->ril, RIL_REQUEST_QUERY_FACILITY_LOCK, &rilp, ril_call_barring_query_cb, cbd, g_free) <= 0) { ofono_error("%s: sending failed", __func__); g_free(cbd); CALLBACK_WITH_FAILURE(callback, -1, data); } } static void ril_call_barring_set_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_barring_set_cb_t cb = cbd->cb; struct barring_data *bd = cbd->user; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: set failed, err: %s", __func__, ril_error_to_string(message->error)); goto error; } if (g_ril_reply_parse_set_facility_lock(bd->ril, message) < 0) goto error; CALLBACK_WITH_SUCCESS(cb, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, cbd->data); } static void ril_call_barring_set(struct ofono_call_barring *cb, const char *lock, int enable, const char *passwd, int cls, ofono_call_barring_set_cb_t callback, void *data) { struct barring_data *bd = ofono_call_barring_get_data(cb); struct cb_data *cbd = cb_data_new(callback, data, bd); struct parcel rilp; DBG("lock: %s, enable: %d, bearer class: %d", lock, enable, cls); /* * RIL modem does not support 7 as default bearer class. According to * the 22.030 Annex C: When service code is not given it corresponds to * "All tele and bearer services" */ if (cls == BEARER_CLASS_DEFAULT) cls = SERVICE_CLASS_NONE; g_ril_request_set_facility_lock(bd->ril, lock, enable, passwd, cls, &rilp); if (g_ril_send(bd->ril, RIL_REQUEST_SET_FACILITY_LOCK, &rilp, ril_call_barring_set_cb, cbd, g_free) <= 0) { ofono_error("%s: sending failed", __func__); g_free(cbd); CALLBACK_WITH_FAILURE(callback, data); } } static void ril_call_barring_set_passwd_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_barring_set_cb_t cb = cbd->cb; struct barring_data *bd = cbd->user; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: set password failed, err: %s", __func__, ril_error_to_string(message->error)); goto error; } g_ril_print_response_no_args(bd->ril, message); CALLBACK_WITH_SUCCESS(cb, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, cbd->data); } static void ril_call_barring_set_passwd(struct ofono_call_barring *barr, const char *lock, const char *old_passwd, const char *new_passwd, ofono_call_barring_set_cb_t cb, void *data) { struct barring_data *bd = ofono_call_barring_get_data(barr); struct cb_data *cbd = cb_data_new(cb, data, bd); struct parcel rilp; DBG("lock %s old %s new %s", lock, old_passwd, new_passwd); g_ril_request_change_barring_password(bd->ril, lock, old_passwd, new_passwd, &rilp); if (g_ril_send(bd->ril, RIL_REQUEST_CHANGE_BARRING_PASSWORD, &rilp, ril_call_barring_set_passwd_cb, cbd, g_free) <= 0) { ofono_error("%s: sending failed", __func__); g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static gboolean ril_delayed_register(gpointer user_data) { struct ofono_call_barring *cb = user_data; ofono_call_barring_register(cb); return FALSE; } static int ril_call_barring_probe(struct ofono_call_barring *cb, unsigned int vendor, void *user) { GRil *ril = user; struct barring_data *bd = g_try_new0(struct barring_data, 1); if (bd == NULL) return -ENOMEM; bd->ril = g_ril_clone(ril); ofono_call_barring_set_data(cb, bd); g_idle_add(ril_delayed_register, cb); return 0; } static void ril_call_barring_remove(struct ofono_call_barring *cb) { struct barring_data *data = ofono_call_barring_get_data(cb); ofono_call_barring_set_data(cb, NULL); g_ril_unref(data->ril); g_free(data); } static struct ofono_call_barring_driver driver = { .name = "rilmodem", .probe = ril_call_barring_probe, .remove = ril_call_barring_remove, .query = ril_call_barring_query, .set = ril_call_barring_set, .set_passwd = ril_call_barring_set_passwd }; void ril_call_barring_init(void) { ofono_call_barring_driver_register(&driver); } void ril_call_barring_exit(void) { ofono_call_barring_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/radio-settings.h0000644000015600001650000000324312671500024024774 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical 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. * * 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 radio_data { GRil *ril; struct ofono_modem *modem; gboolean fast_dormancy; gboolean pending_fd; }; void ril_delayed_register(const struct ofono_error *error, void *user_data); void ril_radio_settings_remove(struct ofono_radio_settings *rs); void ril_query_rat_mode(struct ofono_radio_settings *rs, ofono_radio_settings_rat_mode_query_cb_t cb, void *data); void ril_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); void ril_query_fast_dormancy(struct ofono_radio_settings *rs, ofono_radio_settings_fast_dormancy_query_cb_t cb, void *data); void ril_set_fast_dormancy(struct ofono_radio_settings *rs, ofono_bool_t enable, ofono_radio_settings_fast_dormancy_set_cb_t cb, void *data); void ril_query_available_rats(struct ofono_radio_settings *rs, ofono_radio_settings_available_rats_query_cb_t cb, void *data); ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/voicecall.h0000644000015600001650000000524112671500024024001 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical 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. * * 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 ril_voicecall_data { GSList *calls; /* Call local hangup indicator, one bit per call (1 << call_id) */ unsigned int local_release; unsigned int clcc_source; GRil *ril; struct ofono_modem *modem; unsigned int vendor; unsigned char flags; ofono_voicecall_cb_t cb; void *data; gchar *tone_queue; gboolean tone_pending; }; int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, void *data); void ril_voicecall_remove(struct ofono_voicecall *vc); void ril_dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, enum ofono_clir_option clir, ofono_voicecall_cb_t cb, void *data); void ril_answer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); void ril_hangup_all(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); void ril_hangup_specific(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data); void ril_send_dtmf(struct ofono_voicecall *vc, const char *dtmf, ofono_voicecall_cb_t cb, void *data); void ril_create_multiparty(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); void ril_private_chat(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data); void ril_swap_without_accept(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); void ril_hold_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); void ril_release_all_held(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); void ril_set_udub(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); void ril_release_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data); void ril_voicecall_start(struct ril_voicecall_driver_data *driver_data, struct ofono_voicecall *vc, unsigned int vendor, struct ril_voicecall_data *vd); void ril_call_state_notify(struct ril_msg *message, gpointer user_data); gboolean ril_poll_clcc(gpointer user_data); ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/radio-settings.c0000644000015600001650000002011112671500024024760 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2013 Jolla Ltd * Contact: Jussi Kangas * Copyright (C) 2014 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gril.h" #include "rilmodem.h" #include "grilrequest.h" #include "grilreply.h" #include "radio-settings.h" static void ril_set_rat_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_radio_settings *rs = cbd->user; struct radio_data *rd = ofono_radio_settings_get_data(rs); ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb; if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(rd->ril, message); CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { ofono_error("%s: rat mode setting failed", __func__); CALLBACK_WITH_FAILURE(cb, cbd->data); } } void ril_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_data *rd = ofono_radio_settings_get_data(rs); struct cb_data *cbd = cb_data_new(cb, data, rs); struct parcel rilp; int pref = PREF_NET_TYPE_GSM_WCDMA; switch (mode) { case OFONO_RADIO_ACCESS_MODE_ANY: pref = PREF_NET_TYPE_LTE_GSM_WCDMA; break; case OFONO_RADIO_ACCESS_MODE_GSM: pref = PREF_NET_TYPE_GSM_ONLY; break; case OFONO_RADIO_ACCESS_MODE_UMTS: pref = PREF_NET_TYPE_GSM_WCDMA; break; case OFONO_RADIO_ACCESS_MODE_LTE: pref = PREF_NET_TYPE_LTE_GSM_WCDMA; break; } g_ril_request_set_preferred_network_type(rd->ril, pref, &rilp); if (g_ril_send(rd->ril, RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, &rilp, ril_set_rat_cb, cbd, g_free) == 0) { ofono_error("%s: unable to set rat mode", __func__); g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void ril_rat_mode_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb; struct ofono_radio_settings *rs = cbd->user; struct radio_data *rd = ofono_radio_settings_get_data(rs); int mode, pref; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: error %s", __func__, ril_error_to_string(message->error)); CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } pref = g_ril_reply_parse_get_preferred_network_type(rd->ril, message); if (pref < 0) { ofono_error("%s: parse error", __func__); CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } /* * GSM_WCDMA_AUTO -> ril.h: GSM/WCDMA (auto mode, according to PRL) * PRL: preferred roaming list. * This value is returned when selecting the slot as having 3G * capabilities, so it is sort of the default for MTK modems. */ switch (pref) { case PREF_NET_TYPE_GSM_WCDMA: case PREF_NET_TYPE_GSM_WCDMA_AUTO: mode = OFONO_RADIO_ACCESS_MODE_UMTS; break; case PREF_NET_TYPE_GSM_ONLY: mode = OFONO_RADIO_ACCESS_MODE_GSM; break; case PREF_NET_TYPE_LTE_GSM_WCDMA: mode = OFONO_RADIO_ACCESS_MODE_LTE; break; default: ofono_error("%s: Unexpected preferred network type (%d)", __func__, pref); mode = OFONO_RADIO_ACCESS_MODE_ANY; break; } CALLBACK_WITH_SUCCESS(cb, mode, cbd->data); } void ril_query_rat_mode(struct ofono_radio_settings *rs, ofono_radio_settings_rat_mode_query_cb_t cb, void *data) { struct radio_data *rd = ofono_radio_settings_get_data(rs); struct cb_data *cbd = cb_data_new(cb, data, rs); if (g_ril_send(rd->ril, RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, NULL, ril_rat_mode_cb, cbd, g_free) == 0) { ofono_error("%s: unable to send rat mode query", __func__); g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } } void ril_query_fast_dormancy(struct ofono_radio_settings *rs, ofono_radio_settings_fast_dormancy_query_cb_t cb, void *data) { struct radio_data *rd = ofono_radio_settings_get_data(rs); CALLBACK_WITH_SUCCESS(cb, rd->fast_dormancy, data); } static void ril_display_state_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_radio_settings *rs = cbd->user; struct radio_data *rd = ofono_radio_settings_get_data(rs); ofono_radio_settings_fast_dormancy_set_cb_t cb = cbd->cb; if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(rd->ril, message); rd->fast_dormancy = rd->pending_fd; CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { CALLBACK_WITH_FAILURE(cb, cbd->data); } } void ril_set_fast_dormancy(struct ofono_radio_settings *rs, ofono_bool_t enable, ofono_radio_settings_fast_dormancy_set_cb_t cb, void *data) { struct radio_data *rd = ofono_radio_settings_get_data(rs); struct cb_data *cbd = cb_data_new(cb, data, rs); struct parcel rilp; g_ril_request_screen_state(rd->ril, enable ? 0 : 1, &rilp); rd->pending_fd = enable; if (g_ril_send(rd->ril, RIL_REQUEST_SCREEN_STATE, &rilp, ril_display_state_cb, cbd, g_free) <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static ofono_bool_t query_available_rats_cb(gpointer user_data) { unsigned int available_rats; struct cb_data *cbd = user_data; ofono_radio_settings_available_rats_query_cb_t cb = cbd->cb; struct ofono_radio_settings *rs = cbd->user; struct radio_data *rd = ofono_radio_settings_get_data(rs); available_rats = OFONO_RADIO_ACCESS_MODE_GSM | OFONO_RADIO_ACCESS_MODE_UMTS; if (ofono_modem_get_boolean(rd->modem, MODEM_PROP_LTE_CAPABLE)) available_rats |= OFONO_RADIO_ACCESS_MODE_LTE; CALLBACK_WITH_SUCCESS(cb, available_rats, cbd->data); g_free(cbd); return FALSE; } void ril_query_available_rats(struct ofono_radio_settings *rs, ofono_radio_settings_available_rats_query_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data, rs); g_idle_add(query_available_rats_cb, cbd); } void ril_delayed_register(const struct ofono_error *error, void *user_data) { struct ofono_radio_settings *rs = user_data; if (error->type == OFONO_ERROR_TYPE_NO_ERROR) ofono_radio_settings_register(rs); else ofono_error("%s: cannot set default fast dormancy", __func__); } static int ril_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *user) { struct ril_radio_settings_driver_data *rs_init_data = user; struct radio_data *rsd = g_try_new0(struct radio_data, 1); if (rsd == NULL) { ofono_error("%s: cannot allocate memory", __func__); return -ENOMEM; } rsd->ril = g_ril_clone(rs_init_data->gril); rsd->modem = rs_init_data->modem; ofono_radio_settings_set_data(rs, rsd); ril_set_fast_dormancy(rs, FALSE, ril_delayed_register, rs); return 0; } void ril_radio_settings_remove(struct ofono_radio_settings *rs) { struct radio_data *rd = ofono_radio_settings_get_data(rs); ofono_radio_settings_set_data(rs, NULL); g_ril_unref(rd->ril); g_free(rd); } static struct ofono_radio_settings_driver driver = { .name = RILMODEM, .probe = ril_radio_settings_probe, .remove = ril_radio_settings_remove, .query_rat_mode = ril_query_rat_mode, .set_rat_mode = ril_set_rat_mode, .query_fast_dormancy = ril_query_fast_dormancy, .set_fast_dormancy = ril_set_fast_dormancy, .query_available_rats = ril_query_available_rats }; void ril_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void ril_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/call-volume.c0000644000015600001650000001042312671500024024251 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2013 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "gril.h" #include "grilutil.h" #include "common.h" #include "rilmodem.h" #include "parcel.h" #include "grilrequest.h" #include "grilreply.h" struct cv_data { GRil *ril; unsigned int vendor; }; static void volume_mute_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_volume_cb_t cb = cbd->cb; struct cv_data *cvd = cbd->user; struct ofono_error error; if (message->error == RIL_E_SUCCESS) { decode_ril_error(&error, "OK"); g_ril_print_response_no_args(cvd->ril, message); } else { ofono_error("Could not set the ril mute state"); decode_ril_error(&error, "FAIL"); } cb(&error, cbd->data); } static void ril_call_volume_mute(struct ofono_call_volume *cv, int muted, ofono_call_volume_cb_t cb, void *data) { struct cv_data *cvd = ofono_call_volume_get_data(cv); struct cb_data *cbd = cb_data_new(cb, data, cvd); struct parcel rilp; DBG("Initial ril muted state: %d", muted); g_ril_request_set_mute(cvd->ril, muted, &rilp); if (g_ril_send(cvd->ril, RIL_REQUEST_SET_MUTE, &rilp, volume_mute_cb, cbd, g_free) == 0) { ofono_error("Send RIL_REQUEST_SET_MUTE failed."); g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void probe_mute_cb(struct ril_msg *message, gpointer user_data) { struct ofono_call_volume *cv = user_data; struct cv_data *cvd = ofono_call_volume_get_data(cv); int muted; if (message->error != RIL_E_SUCCESS) { ofono_error("Could not retrieve the ril mute state"); return; } muted = g_ril_reply_parse_get_mute(cvd->ril, message); ofono_call_volume_set_muted(cv, muted); } static void call_probe_mute(gpointer user_data) { struct ofono_call_volume *cv = user_data; struct cv_data *cvd = ofono_call_volume_get_data(cv); g_ril_send(cvd->ril, RIL_REQUEST_GET_MUTE, NULL, probe_mute_cb, cv, NULL); } static gboolean ril_delayed_register(gpointer user_data) { struct ofono_call_volume *cv = user_data; DBG(""); ofono_call_volume_register(cv); /* Probe the mute state */ call_probe_mute(user_data); /* This makes the timeout a single-shot */ return FALSE; } static int ril_call_volume_probe(struct ofono_call_volume *cv, unsigned int vendor, void *data) { GRil *ril = data; struct cv_data *cvd; cvd = g_new0(struct cv_data, 1); if (cvd == NULL) return -ENOMEM; cvd->ril = g_ril_clone(ril); cvd->vendor = vendor; ofono_call_volume_set_data(cv, cvd); /* * ofono_call_volume_register() needs to be called after * the driver has been set in ofono_call_volume_create(), * which calls this function. Most other drivers make * some kind of capabilities query to the modem, and then * call register in the callback; we use an idle event instead. */ g_idle_add(ril_delayed_register, cv); return 0; } static void ril_call_volume_remove(struct ofono_call_volume *cv) { struct cv_data *cvd = ofono_call_volume_get_data(cv); ofono_call_volume_set_data(cv, NULL); g_ril_unref(cvd->ril); g_free(cvd); } static struct ofono_call_volume_driver driver = { .name = RILMODEM, .probe = ril_call_volume_probe, .remove = ril_call_volume_remove, .mute = ril_call_volume_mute, }; void ril_call_volume_init(void) { ofono_call_volume_driver_register(&driver); } void ril_call_volume_exit(void) { ofono_call_volume_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/sim.c0000644000015600001650000010155712671500073022636 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2013 Canonical, Ltd. All rights reserved. * Copyright (C) 2015 Ratchanan Srirattanamet. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "ofono.h" #include "simutil.h" #include "util.h" #include "gril.h" #include "grilutil.h" #include "parcel.h" #include "ril_constants.h" #include "rilmodem.h" #include "grilreply.h" #include "grilrequest.h" #include "grilunsol.h" #include "drivers/infineonmodem/infineon_constants.h" #include "drivers/qcommsimmodem/qcom_msim_constants.h" /* Number of passwords in EPINC response */ #define MTK_EPINC_NUM_PASSWD 4 /* * Based on ../drivers/atmodem/sim.c. * * TODO: * 1. Defines constants for hex literals * 2. Document P1-P3 usage (+CSRM) */ /* * TODO: CDMA/IMS * * This code currently only grabs the AID/application ID from * the gsm_umts application on the SIM card. This code will * need to be modified for CDMA support, and possibly IMS-based * applications. In this case, app_id should be changed to an * array or HashTable of app_status structures. * * The same applies to the app_type. */ static void ril_pin_change_state(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); struct sim_data { GRil *ril; enum ofono_ril_vendor vendor; gchar *aid_str; guint app_type; gchar *app_str; guint app_index; enum ofono_sim_password_type passwd_type; int retries[OFONO_SIM_PASSWORD_INVALID]; enum ofono_sim_password_type passwd_state; struct ofono_modem *modem; ofono_sim_state_event_cb_t ril_state_watch; ofono_bool_t unlock_pending; }; struct change_state_cbd { 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; }; static void send_get_sim_status(struct ofono_sim *sim); static void ril_file_info_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_file_info_cb_t cb = cbd->cb; struct sim_data *sd = cbd->user; struct ofono_error error; gboolean ok = FALSE; int sw1, sw2; int flen = 0, rlen = 0, str = 0; guchar access[3] = { 0x00, 0x00, 0x00 }; guchar file_status = EF_STATUS_VALID; struct reply_sim_io *reply = NULL; /* Error, and no data */ if (message->error != RIL_E_SUCCESS && message->buf_len == 0) { ofono_error("%s: Reply failure: %s", __func__, ril_error_to_string(message->error)); decode_ril_error(&error, "FAIL"); goto error; } /* * The reply can have event data even when message->error is not zero * in mako. */ reply = g_ril_reply_parse_sim_io(sd->ril, message); if (reply == NULL) { decode_ril_error(&error, "FAIL"); goto error; } sw1 = reply->sw1; sw2 = reply->sw2; /* * SIM app file not found || USIM app file not found * See 3gpp TS 51.011, 9.4.4, and ETSI TS 102 221, 10.2.1.5.3 * This can happen with result SUCCESS (maguro) or GENERIC_FAILURE * (mako) */ if ((sw1 == 0x94 && sw2 == 0x04) || (sw1 == 0x6A && sw2 == 0x82)) { DBG("File not found. Error %s", ril_error_to_string(message->error)); decode_ril_error(&error, "FAIL"); goto error; } if (message->error == RIL_E_SUCCESS) { decode_ril_error(&error, "OK"); } else { ofono_error("%s: Reply failure: %s, %02x, %02x", __func__, ril_error_to_string(message->error), sw1, sw2); decode_ril_error(&error, "FAIL"); goto error; } if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) || (sw1 == 0x90 && sw2 != 0x00)) { ofono_error("Error reply, invalid values: sw1: %02x sw2: %02x", sw1, sw2); /* TODO: fix decode_ril_error to take type & error */ error.type = OFONO_ERROR_TYPE_SIM; error.error = (sw1 << 8) | sw2; goto error; } if (reply->hex_len) { if (reply->hex_response[0] == 0x62) { ok = sim_parse_3g_get_response(reply->hex_response, reply->hex_len, &flen, &rlen, &str, access, NULL); } else { ok = sim_parse_2g_get_response(reply->hex_response, reply->hex_len, &flen, &rlen, &str, access, &file_status); } } if (!ok) { ofono_error("%s: parse response failed", __func__); decode_ril_error(&error, "FAIL"); goto error; } cb(&error, flen, str, rlen, access, file_status, cbd->data); g_ril_reply_free_sim_io(reply); return; error: g_ril_reply_free_sim_io(reply); cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data); } static void ril_sim_read_info(struct ofono_sim *sim, 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, sd); struct parcel rilp; struct req_sim_read_info req; guint ret = 0; DBG("file %04x", fileid); req.app_type = sd->app_type; req.aid_str = sd->aid_str; req.fileid = fileid; req.path = path; req.path_len = path_len; if (!g_ril_request_sim_read_info(sd->ril, &req, &rilp)) { ofono_error("Couldn't build SIM read info request"); goto error; } g_ril_append_print_buf(sd->ril, "%s0,0,15,(null),pin2=(null),aid=%s)", print_buf, sd->aid_str); ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp, ril_file_info_cb, cbd, g_free); error: if (ret == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, data); } } static void ril_file_io_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_read_cb_t cb = cbd->cb; struct sim_data *sd = cbd->user; struct ofono_error error; struct reply_sim_io *reply; if (message->error == RIL_E_SUCCESS) { decode_ril_error(&error, "OK"); } else { ofono_error("RILD reply failure: %s", ril_error_to_string(message->error)); goto error; } reply = g_ril_reply_parse_sim_io(sd->ril, message); if (reply == NULL) { ofono_error("Can't parse SIM IO response from RILD"); goto error; } if (reply->hex_len == 0) { ofono_error("Null SIM IO response from RILD"); g_ril_reply_free_sim_io(reply); goto error; } cb(&error, reply->hex_response, reply->hex_len, cbd->data); g_ril_reply_free_sim_io(reply); return; error: decode_ril_error(&error, "FAIL"); cb(&error, NULL, 0, cbd->data); } static void ril_file_write_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_write_cb_t cb = cbd->cb; struct sim_data *sd = cbd->user; struct reply_sim_io *reply; int sw1, sw2; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: RILD reply failure: %s", __func__, ril_error_to_string(message->error)); goto error; } reply = g_ril_reply_parse_sim_io(sd->ril, message); if (reply == NULL) { ofono_error("%s: Can't parse SIM IO response", __func__); goto error; } sw1 = reply->sw1; sw2 = reply->sw2; g_ril_reply_free_sim_io(reply); if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) || (sw1 == 0x90 && sw2 != 0x00)) { struct ofono_error error; 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, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, cbd->data); } static void ril_sim_read_binary(struct ofono_sim *sim, 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, sd); struct parcel rilp; struct req_sim_read_binary req; gint ret = 0; DBG("file %04x", fileid); req.app_type = sd->app_type; req.aid_str = sd->aid_str; req.fileid = fileid; req.path = path; req.path_len = path_len; req.start = start; req.length = length; if (!g_ril_request_sim_read_binary(sd->ril, &req, &rilp)) { ofono_error("Couldn't build SIM read binary request"); goto error; } g_ril_append_print_buf(sd->ril, "%s%d,%d,%d,(null),pin2=(null),aid=%s)", print_buf, (start >> 8), (start & 0xff), length, sd->aid_str); ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp, ril_file_io_cb, cbd, g_free); error: if (ret == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } } static void ril_sim_read_record(struct ofono_sim *sim, 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, sd); struct parcel rilp; struct req_sim_read_record req; guint ret = 0; DBG("file %04x", fileid); req.app_type = sd->app_type; req.aid_str = sd->aid_str; req.fileid = fileid; req.path = path; req.path_len = path_len; req.record = record; req.length = length; if (!g_ril_request_sim_read_record(sd->ril, &req, &rilp)) { ofono_error("Couldn't build SIM read record request"); goto error; } g_ril_append_print_buf(sd->ril, "%s%d,%d,%d,(null),pin2=(null),aid=%s)", print_buf, record, 4, length, sd->aid_str); ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp, ril_file_io_cb, cbd, g_free); error: if (ret == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } } static void ril_sim_update_binary(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 *data) { struct sim_data *sd = ofono_sim_get_data(sim); struct cb_data *cbd = cb_data_new(cb, data, sd); struct parcel rilp; struct req_sim_write_binary req; guint ret = 0; DBG("file 0x%04x", fileid); req.app_type = sd->app_type; req.aid_str = sd->aid_str; req.fileid = fileid; req.path = path; req.path_len = path_len; req.start = start; req.length = length; req.data = value; if (!g_ril_request_sim_write_binary(sd->ril, &req, &rilp)) { ofono_error("%s: Couldn't build SIM write request", __func__); goto error; } ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp, ril_file_write_cb, cbd, g_free); error: if (ret == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void update_record(struct ofono_sim *sim, int fileid, enum req_record_access_mode mode, int record, int length, const unsigned char *value, const unsigned char *path, unsigned int path_len, ofono_sim_write_cb_t cb, void *data) { struct sim_data *sd = ofono_sim_get_data(sim); struct cb_data *cbd = cb_data_new(cb, data, sd); struct parcel rilp; struct req_sim_write_record req; guint ret = 0; DBG("file 0x%04x", fileid); req.app_type = sd->app_type; req.aid_str = sd->aid_str; req.fileid = fileid; req.path = path; req.path_len = path_len; req.mode = mode; req.record = record; req.length = length; req.data = value; if (!g_ril_request_sim_write_record(sd->ril, &req, &rilp)) { ofono_error("%s: Couldn't build SIM write request", __func__); goto error; } ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp, ril_file_write_cb, cbd, g_free); error: if (ret == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void ril_sim_update_record(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 *data) { update_record(sim, fileid, GRIL_REC_ACCESS_MODE_ABSOLUTE, record, length, value, path, path_len, cb, data); } static void ril_sim_update_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 *data) { /* Only mode valid for cyclic files is PREVIOUS */ update_record(sim, fileid, GRIL_REC_ACCESS_MODE_PREVIOUS, 0, length, value, path, path_len, cb, data); } static void ril_imsi_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_imsi_cb_t cb = cbd->cb; struct sim_data *sd = cbd->user; struct ofono_error error; gchar *imsi; if (message->error == RIL_E_SUCCESS) { DBG("GET IMSI reply - OK"); decode_ril_error(&error, "OK"); } else { ofono_error("Reply failure: %s", ril_error_to_string(message->error)); goto error; } imsi = g_ril_reply_parse_imsi(sd->ril, message); if (imsi == NULL) { ofono_error("Error empty IMSI"); goto error; } cb(&error, imsi, cbd->data); g_free(imsi); return; error: decode_ril_error(&error, "FAIL"); cb(&error, NULL, cbd->data); } static void ril_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb, void *data) { struct sim_data *sd = ofono_sim_get_data(sim); struct cb_data *cbd = cb_data_new(cb, data, sd); struct parcel rilp; g_ril_request_read_imsi(sd->ril, sd->aid_str, &rilp); if (g_ril_send(sd->ril, RIL_REQUEST_GET_IMSI, &rilp, ril_imsi_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } } static void configure_active_app(struct sim_data *sd, struct reply_sim_app *app, guint index) { g_free(sd->aid_str); g_free(sd->app_str); sd->app_type = app->app_type; sd->aid_str = g_strdup(app->aid_str); sd->app_str = g_strdup(app->app_str); sd->app_index = index; DBG("setting aid_str (AID) to: %s", sd->aid_str); switch (app->app_state) { case RIL_APPSTATE_PIN: sd->passwd_state = OFONO_SIM_PASSWORD_SIM_PIN; break; case RIL_APPSTATE_PUK: sd->passwd_state = OFONO_SIM_PASSWORD_SIM_PUK; break; case RIL_APPSTATE_SUBSCRIPTION_PERSO: switch (app->perso_substate) { case RIL_PERSOSUBSTATE_SIM_NETWORK: sd->passwd_state = OFONO_SIM_PASSWORD_PHNET_PIN; break; case RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET: sd->passwd_state = OFONO_SIM_PASSWORD_PHNETSUB_PIN; break; case RIL_PERSOSUBSTATE_SIM_CORPORATE: sd->passwd_state = OFONO_SIM_PASSWORD_PHCORP_PIN; break; case RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER: sd->passwd_state = OFONO_SIM_PASSWORD_PHSP_PIN; break; case RIL_PERSOSUBSTATE_SIM_SIM: sd->passwd_state = OFONO_SIM_PASSWORD_PHSIM_PIN; break; case RIL_PERSOSUBSTATE_SIM_NETWORK_PUK: sd->passwd_state = OFONO_SIM_PASSWORD_PHNET_PUK; break; case RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK: sd->passwd_state = OFONO_SIM_PASSWORD_PHNETSUB_PUK; break; case RIL_PERSOSUBSTATE_SIM_CORPORATE_PUK: sd->passwd_state = OFONO_SIM_PASSWORD_PHCORP_PUK; break; case RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK: sd->passwd_state = OFONO_SIM_PASSWORD_PHSP_PUK; break; case RIL_PERSOSUBSTATE_SIM_SIM_PUK: sd->passwd_state = OFONO_SIM_PASSWORD_PHFSIM_PUK; break; default: sd->passwd_state = OFONO_SIM_PASSWORD_NONE; break; }; break; case RIL_APPSTATE_READY: sd->passwd_state = OFONO_SIM_PASSWORD_NONE; break; case RIL_APPSTATE_UNKNOWN: case RIL_APPSTATE_DETECTED: default: sd->passwd_state = OFONO_SIM_PASSWORD_INVALID; break; } } static void sim_send_set_uicc_subscription_cb(struct ril_msg *message, gpointer user_data) { struct ofono_sim *sim = user_data; struct sim_data *sd = ofono_sim_get_data(sim); if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(sd->ril, message); } else { ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); /* * Send RIL_REQUEST_GET_SIM_STATUS again. The reply will run * the app selection algorithm again, causing the request to * be re-sent. */ send_get_sim_status(sim); } } static void sim_send_set_uicc_subscription(struct ofono_sim *sim, int slot_id, int app_index, int sub_id, int sub_status) { struct sim_data *sd = ofono_sim_get_data(sim); struct parcel rilp; DBG(""); g_ril_request_set_uicc_subscription(sd->ril, slot_id, app_index, sub_id, sub_status, &rilp); g_ril_send(sd->ril, QCOM_MSIM_RIL_REQUEST_SET_UICC_SUBSCRIPTION, &rilp, sim_send_set_uicc_subscription_cb, sim, NULL); } static int sim_select_uicc_subscription(struct ofono_sim *sim, struct reply_sim_status *status) { struct sim_data *sd = ofono_sim_get_data(sim); int slot_id = ofono_modem_get_integer(sd->modem, "Slot"); int selected_app = -1; unsigned int i; for (i = 0; i < status->num_apps; i++) { switch (status->apps[i]->app_type) { case RIL_APPTYPE_UNKNOWN: continue; case RIL_APPTYPE_USIM: case RIL_APPTYPE_RUIM: if (selected_app != -1) { switch (status->apps[selected_app]->app_type) { case RIL_APPTYPE_USIM: case RIL_APPTYPE_RUIM: break; default: selected_app = i; } } else { selected_app = i; } break; default: if (selected_app == -1) selected_app = i; } } DBG("Select app %d for subscription.", selected_app); if (selected_app != -1) /* Number 1 means activates that app */ sim_send_set_uicc_subscription(sim, slot_id, selected_app, slot_id, 1); return selected_app; } static void sim_status_cb(struct ril_msg *message, gpointer user_data) { struct ofono_sim *sim = user_data; struct sim_data *sd = ofono_sim_get_data(sim); struct reply_sim_status *status; guint search_index; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); return; } status = g_ril_reply_parse_sim_status(sd->ril, message); if (status == NULL) { ofono_error("%s: Cannot parse SIM status reply", __func__); return; } DBG("SIM status is %u", status->card_state); if (status->card_state == RIL_CARDSTATE_PRESENT) ofono_sim_inserted_notify(sim, TRUE); else if (status && status->card_state == RIL_CARDSTATE_ABSENT) ofono_sim_inserted_notify(sim, FALSE); else ofono_error("%s: bad SIM state (%u)", __func__, status->card_state); if (status->card_state == RIL_CARDSTATE_PRESENT) { /* * TODO(CDMA): need some kind of logic * to set the correct app_index */ search_index = status->gsm_umts_index; if (search_index > status->num_apps && sd->vendor == OFONO_RIL_VENDOR_QCOM_MSIM) { /* * On QCOM's multi SIM device, all index will be -1 * (but will wrap up to UINT_MAX because index is uint) * until we send RIL_REQUEST_SET_UICC_SUBSCRIPTION. * So, we need to figure out which app to use * and send that request out. */ search_index = sim_select_uicc_subscription(sim, status); } if (search_index < status->num_apps) { struct reply_sim_app *app = status->apps[search_index]; if (app->app_type != RIL_APPTYPE_UNKNOWN) { /* * We cache the current password state. Ideally * this should be done by issuing a * GET_SIM_STATUS request from * ril_query_passwd_state, which is called by * the core after sending a password, but * unfortunately the response to GET_SIM_STATUS * is not reliable in mako when sent just after * sending the password. Some time is needed * before the modem refreshes its internal * state, and when it does it sends a * SIM_STATUS_CHANGED event. In that moment we * retrieve the status and this function is * executed. We call __ofono_sim_recheck_pin as * it is the only way to indicate the core to * call query_passwd_state again. An option * that can be explored in the future is wait * before invoking core callback for send_passwd * until we know the real password state. */ configure_active_app(sd, app, search_index); DBG("passwd_state: %d", sd->passwd_state); /* * Note: There doesn't seem to be any other way * to force the core SIM code to recheck the * PIN. This call causes the core to call this * atom's query_passwd() function. */ __ofono_sim_recheck_pin(sim); } } } g_ril_reply_free_sim_status(status); } static void send_get_sim_status(struct ofono_sim *sim) { struct sim_data *sd = ofono_sim_get_data(sim); g_ril_send(sd->ril, RIL_REQUEST_GET_SIM_STATUS, NULL, sim_status_cb, sim, NULL); } static void ril_sim_status_changed(struct ril_msg *message, gpointer user_data) { struct ofono_sim *sim = (struct ofono_sim *) user_data; struct sim_data *sd = ofono_sim_get_data(sim); DBG(""); g_ril_print_unsol_no_args(sd->ril, message); send_get_sim_status(sim); } static void inf_pin_retries_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_pin_retries_cb_t cb = cbd->cb; struct sim_data *sd = cbd->user; struct reply_oem_hook *reply = NULL; int32_t *ret_data; if (message->error != RIL_E_SUCCESS) { ofono_error("Reply failure: %s", ril_error_to_string(message->error)); goto error; } reply = g_ril_reply_oem_hook_raw(sd->ril, message); if (reply == NULL) { ofono_error("%s: parse error", __func__); goto error; } if (reply->length < 5 * (int) sizeof(int32_t)) { ofono_error("%s: reply too small", __func__); goto error; } /* First integer is INF_RIL_REQUEST_OEM_GET_REMAIN_SIM_PIN_ATTEMPTS */ ret_data = reply->data; sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] = *(++ret_data); sd->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = *(++ret_data); sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = *(++ret_data); sd->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = *(++ret_data); g_ril_reply_free_oem_hook(reply); CALLBACK_WITH_SUCCESS(cb, sd->retries, cbd->data); return; error: g_ril_reply_free_oem_hook(reply); CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void mtk_pin_retries_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_pin_retries_cb_t cb = cbd->cb; struct sim_data *sd = cbd->user; struct parcel_str_array *str_arr = NULL; int pin[MTK_EPINC_NUM_PASSWD]; int num_pin; if (message->error != RIL_E_SUCCESS) { ofono_error("Reply failure: %s", ril_error_to_string(message->error)); goto error; } str_arr = g_ril_reply_oem_hook_strings(sd->ril, message); if (str_arr == NULL || str_arr->num_str < 1) { ofono_error("%s: parse error", __func__); goto error; } num_pin = sscanf(str_arr->str[0], "+EPINC:%d,%d,%d,%d", &pin[0], &pin[1], &pin[2], &pin[3]); if (num_pin != MTK_EPINC_NUM_PASSWD) { ofono_error("%s: failed parsing %s", __func__, str_arr->str[0]); goto error; } sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] = pin[0]; sd->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = pin[1]; sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = pin[2]; sd->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = pin[3]; parcel_free_str_array(str_arr); CALLBACK_WITH_SUCCESS(cb, sd->retries, cbd->data); return; error: parcel_free_str_array(str_arr); CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void ril_query_pin_retries(struct ofono_sim *sim, ofono_sim_pin_retries_cb_t cb, void *data) { struct sim_data *sd = ofono_sim_get_data(sim); DBG(""); if (sd->vendor == OFONO_RIL_VENDOR_INFINEON) { struct cb_data *cbd = cb_data_new(cb, data, sd); struct parcel rilp; int32_t oem_req = INF_RIL_REQUEST_OEM_GET_REMAIN_SIM_PIN_ATTEMPTS; g_ril_request_oem_hook_raw(sd->ril, &oem_req, sizeof(oem_req), &rilp); /* Send request to RIL */ if (g_ril_send(sd->ril, RIL_REQUEST_OEM_HOOK_RAW, &rilp, inf_pin_retries_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } } else if (sd->vendor == OFONO_RIL_VENDOR_MTK) { struct cb_data *cbd = cb_data_new(cb, data, sd); struct parcel rilp; const char *at_epinc[] = { "AT+EPINC", "+EPINC:" }; g_ril_request_oem_hook_strings(sd->ril, at_epinc, G_N_ELEMENTS(at_epinc), &rilp); if (g_ril_send(sd->ril, RIL_REQUEST_OEM_HOOK_STRINGS, &rilp, mtk_pin_retries_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } } else { CALLBACK_WITH_SUCCESS(cb, sd->retries, data); } } static void ril_query_passwd_state(struct ofono_sim *sim, ofono_sim_passwd_cb_t cb, void *data) { struct sim_data *sd = ofono_sim_get_data(sim); DBG("passwd_state %u", sd->passwd_state); if (sd->passwd_state == OFONO_SIM_PASSWORD_INVALID) CALLBACK_WITH_FAILURE(cb, -1, data); else CALLBACK_WITH_SUCCESS(cb, sd->passwd_state, data); } static void ril_pin_change_state_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_lock_unlock_cb_t cb = cbd->cb; struct ofono_sim *sim = cbd->user; struct sim_data *sd = ofono_sim_get_data(sim); int *retries; /* * There is no reason to ask SIM status until * unsolicited sim status change indication * Looks like state does not change before that. */ DBG("Enter password: type %d, result %d", sd->passwd_type, message->error); retries = g_ril_reply_parse_retries(sd->ril, message, sd->passwd_type); if (retries != NULL) { memcpy(sd->retries, retries, sizeof(sd->retries)); g_free(retries); } /* TODO: re-factor to not use macro for FAILURE; doesn't return error! */ if (message->error == RIL_E_SUCCESS) { CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { CALLBACK_WITH_FAILURE(cb, cbd->data); /* * Refresh passwd_state (not needed if the unlock is * successful, as an event will refresh the state in that case) */ send_get_sim_status(sim); } } static void ril_pin_send(struct ofono_sim *sim, const char *passwd, ofono_sim_lock_unlock_cb_t cb, void *data) { /* * TODO: This function is supposed to enter the pending password, which * might be also PIN2. So we must check the pending PIN in the future. */ struct sim_data *sd = ofono_sim_get_data(sim); struct cb_data *cbd = cb_data_new(cb, data, sim); struct parcel rilp; sd->passwd_type = OFONO_SIM_PASSWORD_SIM_PIN; g_ril_request_pin_send(sd->ril, passwd, sd->aid_str, &rilp); if (g_ril_send(sd->ril, RIL_REQUEST_ENTER_SIM_PIN, &rilp, ril_pin_change_state_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void enter_pin_done(const struct ofono_error *error, void *data) { struct change_state_cbd *csd = data; struct sim_data *sd = ofono_sim_get_data(csd->sim); if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_error("%s: wrong password", __func__); sd->unlock_pending = FALSE; CALLBACK_WITH_FAILURE(csd->cb, csd->data); } else { ril_pin_change_state(csd->sim, csd->passwd_type, csd->enable, csd->passwd, csd->cb, csd->data); } g_free(csd); } static void ril_pin_change_state(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) { struct sim_data *sd = ofono_sim_get_data(sim); struct cb_data *cbd; struct parcel rilp; struct req_pin_change_state req; int ret = 0; /* * If we want to unlock a password that has not been entered yet, * we enter it before trying to unlock. We need sd->unlock_pending as * the password still has not yet been refreshed when this function is * called from enter_pin_done(). */ if (ofono_sim_get_password_type(sim) == passwd_type && enable == FALSE && sd->unlock_pending == FALSE) { struct change_state_cbd *csd = g_malloc0(sizeof(*csd)); csd->sim = sim; csd->passwd_type = passwd_type; csd->enable = enable; csd->passwd = passwd; csd->cb = cb; csd->data = data; sd->unlock_pending = TRUE; ril_pin_send(sim, passwd, enter_pin_done, csd); return; } sd->unlock_pending = FALSE; cbd = cb_data_new(cb, data, sim); sd->passwd_type = passwd_type; req.aid_str = sd->aid_str; req.passwd_type = passwd_type; req.enable = enable; req.passwd = passwd; if (!g_ril_request_pin_change_state(sd->ril, &req, &rilp)) { ofono_error("Couldn't build pin change state request"); goto error; } ret = g_ril_send(sd->ril, RIL_REQUEST_SET_FACILITY_LOCK, &rilp, ril_pin_change_state_cb, cbd, g_free); error: if (ret == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void ril_pin_send_puk(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); struct cb_data *cbd = cb_data_new(cb, data, sim); struct parcel rilp; sd->passwd_type = OFONO_SIM_PASSWORD_SIM_PUK; g_ril_request_pin_send_puk(sd->ril, puk, passwd, sd->aid_str, &rilp); if (g_ril_send(sd->ril, RIL_REQUEST_ENTER_SIM_PUK, &rilp, ril_pin_change_state_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void ril_change_passwd(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) { struct sim_data *sd = ofono_sim_get_data(sim); struct cb_data *cbd = cb_data_new(cb, data, sim); struct parcel rilp; int request = RIL_REQUEST_CHANGE_SIM_PIN; sd->passwd_type = passwd_type; g_ril_request_change_passwd(sd->ril, old_passwd, new_passwd, sd->aid_str, &rilp); if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) request = RIL_REQUEST_CHANGE_SIM_PIN2; if (g_ril_send(sd->ril, request, &rilp, ril_pin_change_state_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static gboolean listen_and_get_sim_status(gpointer user) { struct ofono_sim *sim = user; struct sim_data *sd = ofono_sim_get_data(sim); send_get_sim_status(sim); g_ril_register(sd->ril, RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, (GRilNotifyFunc) ril_sim_status_changed, sim); /* TODO: should we also register for RIL_UNSOL_SIM_REFRESH? */ return FALSE; } static gboolean ril_sim_register(gpointer user) { struct ofono_sim *sim = user; struct sim_data *sd = ofono_sim_get_data(sim); DBG(""); ofono_sim_register(sim); if (sd->ril_state_watch != NULL && !ofono_sim_add_state_watch(sim, sd->ril_state_watch, sd->modem, NULL)) ofono_error("Error registering ril sim watch"); /* * We use g_idle_add here to make sure that the presence of the SIM * interface is signalled before signalling anything else from the said * interface, as ofono_sim_register also uses g_idle_add. */ g_idle_add(listen_and_get_sim_status, sim); return FALSE; } static int ril_sim_probe(struct ofono_sim *sim, unsigned int vendor, void *data) { struct ril_sim_data *ril_data = data; GRil *ril = ril_data->gril; struct sim_data *sd; int i; sd = g_new0(struct sim_data, 1); sd->ril = g_ril_clone(ril); sd->vendor = vendor; sd->aid_str = NULL; sd->app_str = NULL; sd->app_type = RIL_APPTYPE_UNKNOWN; sd->passwd_state = OFONO_SIM_PASSWORD_NONE; sd->passwd_type = OFONO_SIM_PASSWORD_NONE; sd->modem = ril_data->modem; sd->ril_state_watch = ril_data->ril_state_watch; for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) sd->retries[i] = -1; ofono_sim_set_data(sim, sd); /* * TODO: analyze if capability check is needed * and/or timer should be adjusted. * * ofono_sim_register() needs to be called after the * driver has been set in ofono_sim_create(), which * calls this function. Most other drivers make some * kind of capabilities query to the modem, and then * call register in the callback; we use an idle event * instead. */ g_idle_add(ril_sim_register, sim); return 0; } static void ril_sim_remove(struct ofono_sim *sim) { struct sim_data *sd = ofono_sim_get_data(sim); ofono_sim_set_data(sim, NULL); g_ril_unref(sd->ril); g_free(sd->aid_str); g_free(sd->app_str); g_free(sd); } static struct ofono_sim_driver driver = { .name = RILMODEM, .probe = ril_sim_probe, .remove = ril_sim_remove, .read_file_info = ril_sim_read_info, .read_file_transparent = ril_sim_read_binary, .read_file_linear = ril_sim_read_record, .read_file_cyclic = ril_sim_read_record, .write_file_transparent = ril_sim_update_binary, .write_file_linear = ril_sim_update_record, .write_file_cyclic = ril_sim_update_cyclic, .read_imsi = ril_read_imsi, .query_passwd_state = ril_query_passwd_state, .send_passwd = ril_pin_send, .query_pin_retries = ril_query_pin_retries, .reset_passwd = ril_pin_send_puk, .change_passwd = ril_change_passwd, .lock = ril_pin_change_state, /* * TODO: Implmenting PIN/PUK support requires defining * the following driver methods. * * In the meanwhile, as long as the SIM card is present, * and unlocked, the core SIM code will check for the * presence of query_passwd_state, and if null, then the * function sim_initialize_after_pin() is called. * * .query_locked = ril_pin_query_enabled, */ }; void ril_sim_init(void) { DBG(""); ofono_sim_driver_register(&driver); } void ril_sim_exit(void) { ofono_sim_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/rilmodem.c0000644000015600001650000000363312671500024023646 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical, Ltd. 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include "rilmodem.h" static int rilmodem_init(void) { DBG(""); ril_devinfo_init(); ril_sim_init(); ril_voicecall_init(); ril_sms_init(); ril_netreg_init(); ril_call_volume_init(); ril_gprs_init(); ril_gprs_context_init(); ril_ussd_init(); ril_call_settings_init(); ril_call_forwarding_init(); ril_radio_settings_init(); ril_call_barring_init(); ril_phonebook_init(); return 0; } static void rilmodem_exit(void) { DBG(""); ril_devinfo_exit(); ril_sim_exit(); ril_voicecall_exit(); ril_sms_exit(); ril_netreg_exit(); ril_call_volume_exit(); ril_gprs_exit(); ril_gprs_context_exit(); ril_ussd_exit(); ril_call_settings_exit(); ril_call_forwarding_exit(); ril_radio_settings_exit(); ril_call_barring_exit(); ril_phonebook_exit(); } OFONO_PLUGIN_DEFINE(rilmodem, "RIL modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, rilmodem_init, rilmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/devinfo.c0000644000015600001650000001226212671500024023466 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2013 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gril.h" #include "rilmodem.h" #include "grilreply.h" /* * TODO: The functions in this file are stubbed out, and * will need to be re-worked to talk to the /gril layer * in order to get real values from RILD. */ static void ril_query_manufacturer(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { const char *attr = "Fake Manufacturer"; struct cb_data *cbd = cb_data_new(cb, data, NULL); struct ofono_error error; decode_ril_error(&error, "OK"); cb(&error, attr, cbd->data); /* Note: this will need to change if cbd passed to gril layer */ g_free(cbd); } static void ril_query_model(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { const char *attr = "Fake Modem Model"; struct cb_data *cbd = cb_data_new(cb, data, NULL); struct ofono_error error; decode_ril_error(&error, "OK"); cb(&error, attr, cbd->data); /* Note: this will need to change if cbd passed to gril layer */ g_free(cbd); } static void query_revision_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_devinfo_query_cb_t cb = cbd->cb; GRil *ril = cbd->user; struct ofono_error error; char *revision; if (message->error == RIL_E_SUCCESS) { decode_ril_error(&error, "OK"); } else { decode_ril_error(&error, "FAIL"); cb(&error, NULL, cbd->data); return; } revision = g_ril_reply_parse_baseband_version(ril, message); cb(&error, revision, cbd->data); g_free(revision); } static void ril_query_revision(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { GRil *ril = ofono_devinfo_get_data(info); struct cb_data *cbd = cb_data_new(cb, data, ril); if (g_ril_send(ril, RIL_REQUEST_BASEBAND_VERSION, NULL, query_revision_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } } static void query_serial_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_devinfo_query_cb_t cb = cbd->cb; GRil *ril = cbd->user; struct ofono_error error; gchar *imei; if (message->error == RIL_E_SUCCESS) { decode_ril_error(&error, "OK"); } else { decode_ril_error(&error, "FAIL"); cb(&error, NULL, cbd->data); return; } imei = g_ril_reply_parse_baseband_version(ril, message); cb(&error, imei, cbd->data); g_free(imei); } static void ril_query_serial(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { GRil *ril = ofono_devinfo_get_data(info); struct cb_data *cbd = cb_data_new(cb, data, ril); /* * TODO: make it support both RIL_REQUEST_GET_IMEI (deprecated) and * RIL_REQUEST_DEVICE_IDENTITY depending on the rild version used */ if (g_ril_send(ril, RIL_REQUEST_GET_IMEI, NULL, query_serial_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } } static gboolean ril_delayed_register(gpointer user_data) { struct ofono_devinfo *info = user_data; DBG(""); ofono_devinfo_register(info); /* This makes the timeout a single-shot */ return FALSE; } static int ril_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor, void *data) { GRil *ril = NULL; if (data != NULL) ril = g_ril_clone(data); ofono_devinfo_set_data(info, ril); /* * ofono_devinfo_register() needs to be called after * the driver has been set in ofono_devinfo_create(), * which calls this function. Most other drivers make * some kind of capabilities query to the modem, and then * call register in the callback; we use an idle event instead. */ g_idle_add(ril_delayed_register, info); return 0; } static void ril_devinfo_remove(struct ofono_devinfo *info) { GRil *ril = ofono_devinfo_get_data(info); ofono_devinfo_set_data(info, NULL); g_ril_unref(ril); } static struct ofono_devinfo_driver driver = { .name = RILMODEM, .probe = ril_devinfo_probe, .remove = ril_devinfo_remove, .query_manufacturer = ril_query_manufacturer, .query_model = ril_query_model, .query_revision = ril_query_revision, .query_serial = ril_query_serial }; void ril_devinfo_init(void) { ofono_devinfo_driver_register(&driver); } void ril_devinfo_exit(void) { ofono_devinfo_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/rilutil.c0000644000015600001650000001045612671500024023523 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical 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. * * 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 #endif #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "common.h" #include "rilutil.h" #include "simutil.h" #include "util.h" #include "ril_constants.h" struct ril_util_sim_state_query { GRil *ril; guint cpin_poll_source; guint cpin_poll_count; guint interval; guint num_times; ril_util_sim_inserted_cb_t cb; void *userdata; GDestroyNotify destroy; }; static gboolean cpin_check(gpointer userdata); void decode_ril_error(struct ofono_error *error, const char *final) { if (!strcmp(final, "OK")) { error->type = OFONO_ERROR_TYPE_NO_ERROR; error->error = 0; } else { error->type = OFONO_ERROR_TYPE_FAILURE; error->error = 0; } } gint ril_util_call_compare_by_status(gconstpointer a, gconstpointer b) { const struct ofono_call *call = a; int status = GPOINTER_TO_INT(b); if (status != call->status) return 1; return 0; } gint ril_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b) { const struct ofono_call *call = a; const struct ofono_phone_number *pb = b; return memcmp(&call->phone_number, pb, sizeof(struct ofono_phone_number)); } gint ril_util_call_compare_by_id(gconstpointer a, gconstpointer b) { const struct ofono_call *call = a; unsigned int id = GPOINTER_TO_UINT(b); if (id < call->id) return -1; if (id > call->id) return 1; return 0; } gint ril_util_call_compare(gconstpointer a, gconstpointer b) { const struct ofono_call *ca = a; const struct ofono_call *cb = b; if (ca->id < cb->id) return -1; if (ca->id > cb->id) return 1; return 0; } static gboolean cpin_check(gpointer userdata) { struct ril_util_sim_state_query *req = userdata; req->cpin_poll_source = 0; return FALSE; } gchar *ril_util_get_netmask(const gchar *address) { char *result; if (g_str_has_suffix(address, "/30")) { result = PREFIX_30_NETMASK; } else if (g_str_has_suffix(address, "/29")) { result = PREFIX_29_NETMASK; } else if (g_str_has_suffix(address, "/28")) { result = PREFIX_28_NETMASK; } else if (g_str_has_suffix(address, "/27")) { result = PREFIX_27_NETMASK; } else if (g_str_has_suffix(address, "/26")) { result = PREFIX_26_NETMASK; } else if (g_str_has_suffix(address, "/25")) { result = PREFIX_25_NETMASK; } else if (g_str_has_suffix(address, "/24")) { result = PREFIX_24_NETMASK; } else { /* * This handles the case where the * Samsung RILD returns an address without * a prefix, however it explicitly sets a * /24 netmask ( which isn't returned as * an attribute of the DATA_CALL. * * TODO/OEM: this might need to be quirked * for specific devices. */ result = PREFIX_24_NETMASK; } DBG("address: %s netmask: %s", address, result); return result; } struct ril_util_sim_state_query *ril_util_sim_state_query_new(GRil *ril, guint interval, guint num_times, ril_util_sim_inserted_cb_t cb, void *userdata, GDestroyNotify destroy) { struct ril_util_sim_state_query *req; req = g_new0(struct ril_util_sim_state_query, 1); req->ril = ril; req->interval = interval; req->num_times = num_times; req->cb = cb; req->userdata = userdata; req->destroy = destroy; cpin_check(req); return req; } void ril_util_sim_state_query_free(struct ril_util_sim_state_query *req) { if (req == NULL) return; if (req->cpin_poll_source > 0) g_source_remove(req->cpin_poll_source); if (req->destroy) req->destroy(req->userdata); g_free(req); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/network-registration.c0000644000015600001650000003533212671500024026240 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * Copyright (C) 2012-2013 Canonical Ltd. * Copyright (C) 2013 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "common.h" #include "gril.h" #include "rilmodem.h" #include "grilreply.h" #include "grilrequest.h" #include "grilunsol.h" struct netreg_data { GRil *ril; char mcc[OFONO_MAX_MCC_LENGTH + 1]; char mnc[OFONO_MAX_MNC_LENGTH + 1]; int signal_index; /* If strength is reported via CIND */ int signal_min; /* min strength reported via CIND */ int signal_max; /* max strength reported via CIND */ int signal_invalid; /* invalid strength reported via CIND */ int tech; struct ofono_network_time time; guint nitz_timeout; unsigned int vendor; }; static void ril_registration_status(struct ofono_netreg *netreg, ofono_netreg_status_cb_t cb, void *data); static int ril_tech_to_access_tech(int ril_tech) { /* * This code handles the mapping between the RIL_RadioTechnology * and ofono's access technology values ( see values - 27.007 * Section 7.3 ). */ switch (ril_tech) { case RADIO_TECH_UNKNOWN: return -1; case RADIO_TECH_GSM: case RADIO_TECH_GPRS: return ACCESS_TECHNOLOGY_GSM; case RADIO_TECH_EDGE: return ACCESS_TECHNOLOGY_GSM_EGPRS; case RADIO_TECH_UMTS: return ACCESS_TECHNOLOGY_UTRAN; case RADIO_TECH_HSDPA: return ACCESS_TECHNOLOGY_UTRAN_HSDPA; case RADIO_TECH_HSUPA: return ACCESS_TECHNOLOGY_UTRAN_HSUPA; case RADIO_TECH_HSPAP: case RADIO_TECH_HSPA: /* HSPAP is HSPA+; which ofono doesn't define; * so, if differentiating HSPA and HSPA+ is * important, then ofono needs to be patched, * and we probably also need to introduce a * new indicator icon. */ return ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA; case RADIO_TECH_LTE: return ACCESS_TECHNOLOGY_EUTRAN; default: return -1; } } static void extract_mcc_mnc(const char *str, char *mcc, char *mnc) { /* Three digit country code */ strncpy(mcc, str, OFONO_MAX_MCC_LENGTH); mcc[OFONO_MAX_MCC_LENGTH] = '\0'; /* Usually a 2 but sometimes 3 digit network code */ strncpy(mnc, str + OFONO_MAX_MCC_LENGTH, OFONO_MAX_MNC_LENGTH); mnc[OFONO_MAX_MNC_LENGTH] = '\0'; } static void ril_creg_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_status_cb_t cb = cbd->cb; struct netreg_data *nd = cbd->user; struct reply_reg_state *reply; DBG(""); if (message->error != RIL_E_SUCCESS) { ofono_error("%s: failed to pull registration state", __func__); goto error; } reply = g_ril_reply_parse_voice_reg_state(nd->ril, message); if (reply == NULL) goto error; nd->tech = reply->tech; CALLBACK_WITH_SUCCESS(cb, reply->status, reply->lac, reply->ci, ril_tech_to_access_tech(reply->tech), cbd->data); g_free(reply); return; error: CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data); } static void ril_creg_notify(struct ofono_error *error, int status, int lac, int ci, int tech, gpointer user_data) { struct ofono_netreg *netreg = user_data; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Error during status notification"); return; } ofono_netreg_status_notify(netreg, status, lac, ci, tech); } static void ril_network_state_change(struct ril_msg *message, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); g_ril_print_unsol_no_args(nd->ril, message); ril_registration_status(netreg, NULL, NULL); } static void ril_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; /* * If no cb specified, setup internal callback to * handle unsolicited VOICE_NET_STATE_CHANGE events. */ if (cb == NULL) cbd = cb_data_new(ril_creg_notify, netreg, nd); else cbd = cb_data_new(cb, data, nd); if (g_ril_send(nd->ril, RIL_REQUEST_VOICE_REGISTRATION_STATE, NULL, ril_creg_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data); } } static void set_oper_name(const struct reply_operator *reply, struct ofono_network_operator *op) { const char *spn = NULL; /* Use SPN list if we do not have name */ if (strcmp(reply->numeric, reply->lalpha) == 0) { spn = __ofono_spn_table_get_spn(reply->numeric); if (spn != NULL) { DBG("using spn override %s", spn); strncpy(op->name, spn, OFONO_MAX_OPERATOR_NAME_LENGTH); } } if (spn == NULL) { /* Try to use long by default */ if (reply->lalpha) strncpy(op->name, reply->lalpha, OFONO_MAX_OPERATOR_NAME_LENGTH); else if (reply->salpha) strncpy(op->name, reply->salpha, OFONO_MAX_OPERATOR_NAME_LENGTH); } } static void ril_cops_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_operator_cb_t cb = cbd->cb; struct netreg_data *nd = cbd->user; struct reply_operator *reply; struct ofono_network_operator op; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: failed to retrive the current operator", __func__); goto error; } reply = g_ril_reply_parse_operator(nd->ril, message); if (reply == NULL) goto error; set_oper_name(reply, &op); extract_mcc_mnc(reply->numeric, op.mcc, op.mnc); /* Set to current */ op.status = OPERATOR_STATUS_CURRENT; op.tech = ril_tech_to_access_tech(nd->tech); CALLBACK_WITH_SUCCESS(cb, &op, cbd->data); g_ril_reply_free_operator(reply); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void ril_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, nd); if (g_ril_send(nd->ril, RIL_REQUEST_OPERATOR, NULL, ril_cops_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } } static void ril_cops_list_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_operator_list_cb_t cb = cbd->cb; struct netreg_data *nd = cbd->user; struct reply_avail_ops *reply = NULL; struct ofono_network_operator *ops; struct reply_operator *operator; GSList *l; unsigned int i = 0; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: failed to retrive the list of operators", __func__); goto error; } reply = g_ril_reply_parse_avail_ops(nd->ril, message); if (reply == NULL) goto error; ops = g_try_new0(struct ofono_network_operator, reply->num_ops); if (ops == NULL) { ofono_error("%s: can't allocate ofono_network_operator", __func__); goto error; } for (l = reply->list; l; l = l->next) { operator = l->data; set_oper_name(operator, &ops[i]); extract_mcc_mnc(operator->numeric, ops[i].mcc, ops[i].mnc); ops[i].tech = ril_tech_to_access_tech(operator->tech); /* Set the proper status */ if (!strcmp(operator->status, "unknown")) ops[i].status = OPERATOR_STATUS_UNKNOWN; else if (!strcmp(operator->status, "available")) ops[i].status = OPERATOR_STATUS_AVAILABLE; else if (!strcmp(operator->status, "current")) ops[i].status = OPERATOR_STATUS_CURRENT; else if (!strcmp(operator->status, "forbidden")) ops[i].status = OPERATOR_STATUS_FORBIDDEN; i++; } CALLBACK_WITH_SUCCESS(cb, reply->num_ops, ops, cbd->data); g_ril_reply_free_avail_ops(reply); return; error: CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data); g_ril_reply_free_avail_ops(reply); } static void ril_list_operators(struct ofono_netreg *netreg, ofono_netreg_operator_list_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data, nd); if (g_ril_send(nd->ril, RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, NULL, ril_cops_list_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, 0, NULL, data); } } static void ril_register_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_register_cb_t cb = cbd->cb; struct netreg_data *nd = cbd->user; struct ofono_error error; if (message->error == RIL_E_SUCCESS) { decode_ril_error(&error, "OK"); g_ril_print_response_no_args(nd->ril, message); } else { decode_ril_error(&error, "FAIL"); } cb(&error, cbd->data); } static void ril_register_auto(struct ofono_netreg *netreg, ofono_netreg_register_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data, nd); if (g_ril_send(nd->ril, RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, NULL, ril_register_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void ril_register_manual(struct ofono_netreg *netreg, const char *mcc, const char *mnc, ofono_netreg_register_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data, nd); char buf[OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1]; struct parcel rilp; /* RIL expects a char * specifying MCCMNC of network to select */ snprintf(buf, sizeof(buf), "%s%s", mcc, mnc); g_ril_request_set_net_select_manual(nd->ril, buf, &rilp); /* In case of error free cbd and return the cb with failure */ if (g_ril_send(nd->ril, RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, &rilp, ril_register_cb, cbd, g_free) == 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void ril_strength_notify(struct ril_msg *message, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); int strength = g_ril_unsol_parse_signal_strength(nd->ril, message, nd->tech); ofono_netreg_strength_notify(netreg, strength); } static void ril_strength_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_strength_cb_t cb = cbd->cb; struct netreg_data *nd = cbd->user; struct ofono_error error; int strength; if (message->error == RIL_E_SUCCESS) { decode_ril_error(&error, "OK"); } else { ofono_error("Failed to retrive the signal strength"); goto error; } /* The g_ril_unsol* function handles both reply & unsolicited */ strength = g_ril_unsol_parse_signal_strength(nd->ril, message, nd->tech); cb(&error, strength, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void ril_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, nd); if (g_ril_send(nd->ril, RIL_REQUEST_SIGNAL_STRENGTH, NULL, ril_strength_cb, cbd, g_free) == 0) { ofono_error("Send RIL_REQUEST_SIGNAL_STRENGTH failed."); g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } } static void ril_nitz_notify(struct ril_msg *message, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); int year, mon, mday, hour, min, sec, dst, tzi, n_match; char tzs, tz[4]; gchar *nitz; nitz = g_ril_unsol_parse_nitz(nd->ril, message); if (nitz == NULL) goto error; n_match = sscanf(nitz, "%u/%u/%u,%u:%u:%u%c%u,%u", &year, &mon, &mday, &hour, &min, &sec, &tzs, &tzi, &dst); if (n_match != 9) goto error; sprintf(tz, "%c%d", tzs, tzi); nd->time.utcoff = atoi(tz) * 15 * 60; nd->time.dst = dst; nd->time.sec = sec; nd->time.min = min; nd->time.hour = hour; nd->time.mday = mday; nd->time.mon = mon; nd->time.year = 2000 + year; ofono_netreg_time_notify(netreg, &nd->time); g_free(nitz); return; error: ofono_error("%s: unable to notify ofono about NITZ (%s)", __func__, nitz ? nitz : "null"); g_free(nitz); } static gboolean ril_delayed_register(gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); ofono_netreg_register(netreg); /* Register for network state changes */ g_ril_register(nd->ril, RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, ril_network_state_change, netreg); /* Register for network time update reports */ g_ril_register(nd->ril, RIL_UNSOL_NITZ_TIME_RECEIVED, ril_nitz_notify, netreg); /* Register for signal strength changes */ g_ril_register(nd->ril, RIL_UNSOL_SIGNAL_STRENGTH, ril_strength_notify, netreg); /* This makes the timeout a single-shot */ return FALSE; } static int ril_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor, void *data) { GRil *ril = data; struct netreg_data *nd; nd = g_new0(struct netreg_data, 1); nd->ril = g_ril_clone(ril); nd->vendor = vendor; nd->tech = RADIO_TECH_UNKNOWN; nd->time.sec = -1; nd->time.min = -1; nd->time.hour = -1; nd->time.mday = -1; nd->time.mon = -1; nd->time.year = -1; nd->time.dst = 0; nd->time.utcoff = 0; ofono_netreg_set_data(netreg, nd); /* * ofono_netreg_register() needs to be called after * the driver has been set in ofono_netreg_create(), * which calls this function. Most other drivers make * some kind of capabilities query to the modem, and then * call register in the callback; we use the idle loop here. */ g_idle_add(ril_delayed_register, netreg); return 0; } static void ril_netreg_remove(struct ofono_netreg *netreg) { struct netreg_data *nd = ofono_netreg_get_data(netreg); if (nd->nitz_timeout) g_source_remove(nd->nitz_timeout); ofono_netreg_set_data(netreg, NULL); g_ril_unref(nd->ril); g_free(nd); } static struct ofono_netreg_driver driver = { .name = RILMODEM, .probe = ril_netreg_probe, .remove = ril_netreg_remove, .registration_status = ril_registration_status, .current_operator = ril_current_operator, .list_operators = ril_list_operators, .register_auto = ril_register_auto, .register_manual = ril_register_manual, .strength = ril_signal_strength, }; void ril_netreg_init(void) { ofono_netreg_driver_register(&driver); } void ril_netreg_exit(void) { ofono_netreg_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/ussd.c0000644000015600001650000001461612671500024023017 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2013 Jolla Ltd * Copyright (C) 2013 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "gril.h" #include "grilutil.h" #include "grilrequest.h" #include "grilunsol.h" #include "rilmodem.h" #include "ril_constants.h" struct ussd_data { GRil *ril; }; static gboolean request_success(gpointer data) { struct cb_data *cbd = data; ofono_ussd_cb_t cb = cbd->cb; CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); return FALSE; } static void ril_ussd_cb(struct ril_msg *message, gpointer user_data) { struct ofono_ussd *ussd = user_data; struct ussd_data *ud = ofono_ussd_get_data(ussd); /* * We fake an ON_USSD event if there was an error sending the request, * as core will be waiting for one to respond to the Initiate() call. * Note that we already made the callback (see ril_ussd_request()). */ if (message->error == RIL_E_SUCCESS) g_ril_print_response_no_args(ud->ril, message); else ofono_ussd_notify(ussd, OFONO_USSD_STATUS_NOT_SUPPORTED, 0, NULL, 0); } static void ril_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, ussd); enum sms_charset charset; char *text = NULL; int ret = 0; if (cbs_dcs_decode(dcs, NULL, NULL, &charset, NULL, NULL, NULL)) { if (charset == SMS_CHARSET_7BIT) { long written; text = (char *) unpack_7bit(pdu, len, 0, TRUE, 0, &written, 1); if (text != NULL) *(text + written) = '\0'; } else if (charset == SMS_CHARSET_UCS2) { text = g_convert((char *) pdu, len, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); } else { ofono_error("%s: No support for charset %d", __func__, charset); } } if (text) { struct parcel rilp; g_ril_request_send_ussd(ud->ril, text, &rilp); ret = g_ril_send(ud->ril, RIL_REQUEST_SEND_USSD, &rilp, ril_ussd_cb, ussd, NULL); g_free(text); } /* * We do not wait for the SEND_USSD reply to do the callback, as some * networks send it after sending one or more ON_USSD events. From the * ofono core perspective, Initiate() does not return until one ON_USSD * event is received: making here a successful callback just makes the * core wait for that event. */ if (ret <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } else { g_idle_add(request_success, cbd); } } static void ril_ussd_cancel_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_ussd *ussd = cbd->user; struct ussd_data *ud = ofono_ussd_get_data(ussd); ofono_ussd_cb_t cb = cbd->cb; if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(ud->ril, message); CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { CALLBACK_WITH_FAILURE(cb, cbd->data); } } static void ril_ussd_cancel(struct ofono_ussd *ussd, ofono_ussd_cb_t cb, void *user_data) { struct ussd_data *ud = ofono_ussd_get_data(ussd); struct cb_data *cbd = cb_data_new(cb, user_data, ussd); int ret; ret = g_ril_send(ud->ril, RIL_REQUEST_CANCEL_USSD, NULL, ril_ussd_cancel_cb, cbd, g_free); if (ret <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } } static void ril_ussd_notify(struct ril_msg *message, gpointer user_data) { struct ofono_ussd *ussd = user_data; struct ussd_data *ud = ofono_ussd_get_data(ussd); struct unsol_ussd *unsol; unsol = g_ril_unsol_parse_ussd(ud->ril, message); if (unsol == NULL) { ofono_error("%s: Parsing error", __func__); return; } /* To fix bug in MTK: USSD-Notify arrive with type 2 instead of 0 */ if (g_ril_vendor(ud->ril) == OFONO_RIL_VENDOR_MTK && unsol->message != NULL && unsol->type == 2) unsol->type = 0; /* * With data coding scheme 0x48, we are saying that the ussd string is a * UCS-2 string, uncompressed, and with unspecified message class. For * the DCS coding, see 3gpp 23.038, sect. 5. */ if (unsol->message != NULL) { gsize written; char *ucs2; ucs2 = g_convert(unsol->message, -1, "UCS-2BE//TRANSLIT", "UTF-8", NULL, &written, NULL); if (ucs2 != NULL) { ofono_ussd_notify(ussd, unsol->type, 0x48, (unsigned char *) ucs2, written); g_free(ucs2); } else { ofono_error("%s: Error transcoding", __func__); } } else { ofono_ussd_notify(ussd, unsol->type, 0, NULL, 0); } g_ril_unsol_free_ussd(unsol); } static gboolean ril_delayed_register(gpointer user_data) { struct ofono_ussd *ussd = user_data; struct ussd_data *ud = ofono_ussd_get_data(ussd); DBG(""); ofono_ussd_register(ussd); /* Register for USSD responses */ g_ril_register(ud->ril, RIL_UNSOL_ON_USSD, ril_ussd_notify, ussd); return FALSE; } static int ril_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor, void *user) { GRil *ril = user; struct ussd_data *ud = g_new0(struct ussd_data, 1); ud->ril = g_ril_clone(ril); ofono_ussd_set_data(ussd, ud); g_idle_add(ril_delayed_register, ussd); return 0; } static void ril_ussd_remove(struct ofono_ussd *ussd) { struct ussd_data *ud = ofono_ussd_get_data(ussd); ofono_ussd_set_data(ussd, NULL); g_ril_unref(ud->ril); g_free(ud); } static struct ofono_ussd_driver driver = { .name = RILMODEM, .probe = ril_ussd_probe, .remove = ril_ussd_remove, .request = ril_ussd_request, .cancel = ril_ussd_cancel }; void ril_ussd_init(void) { ofono_ussd_driver_register(&driver); } void ril_ussd_exit(void) { ofono_ussd_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/call-settings.c0000644000015600001650000001612312671500024024605 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2013 Jolla Ltd * Copyright (C) 2013 Canonical Ltd * Contact: Jussi Kangas * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gril.h" #include "grilutil.h" #include "grilrequest.h" #include "grilreply.h" #include "rilmodem.h" #include "ril_constants.h" #include "common.h" struct settings_data { GRil *ril; }; static void ril_set_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_call_settings *cs = cbd->user; struct settings_data *sd = ofono_call_settings_get_data(cs); ofono_call_settings_set_cb_t cb = cbd->cb; if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(sd->ril, message); CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { CALLBACK_WITH_FAILURE(cb, cbd->data); } } static void ril_cw_set(struct ofono_call_settings *cs, int mode, int cls, ofono_call_settings_set_cb_t cb, void *data) { struct settings_data *sd = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data, cs); int ret; struct parcel rilp; g_ril_request_set_call_waiting(sd->ril, mode, cls, &rilp); ret = g_ril_send(sd->ril, RIL_REQUEST_SET_CALL_WAITING, &rilp, ril_set_cb, cbd, g_free); /* In case of error free cbd and return the cb with failure */ if (ret <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void ril_cw_query_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_call_settings *cs = cbd->user; struct settings_data *sd = ofono_call_settings_get_data(cs); ofono_call_settings_status_cb_t cb = cbd->cb; if (message->error == RIL_E_SUCCESS) { int res; res = g_ril_reply_parse_query_call_waiting(sd->ril, message); CALLBACK_WITH_SUCCESS(cb, res, cbd->data); } else { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } } static void ril_cw_query(struct ofono_call_settings *cs, int cls, ofono_call_settings_status_cb_t cb, void *data) { struct settings_data *sd = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data, cs); int ret; struct parcel rilp; g_ril_request_query_call_waiting(sd->ril, cls, &rilp); ret = g_ril_send(sd->ril, RIL_REQUEST_QUERY_CALL_WAITING, &rilp, ril_cw_query_cb, cbd, g_free); /* In case of error free cbd and return the cb with failure */ if (ret <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } } static void ril_clip_query_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_call_settings *cs = cbd->user; struct settings_data *sd = ofono_call_settings_get_data(cs); ofono_call_settings_status_cb_t cb = cbd->cb; if (message->error == RIL_E_SUCCESS) { int res; res = g_ril_reply_parse_query_clip(sd->ril, message); CALLBACK_WITH_SUCCESS(cb, res, cbd->data); } else { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } } static void ril_clip_query(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data) { struct settings_data *sd = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data, cs); int ret; ret = g_ril_send(sd->ril, RIL_REQUEST_QUERY_CLIP, NULL, ril_clip_query_cb, cbd, g_free); /* In case of error free cbd and return the cb with failure */ if (ret <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } } static void ril_clir_query_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_call_settings *cs = cbd->user; struct settings_data *sd = ofono_call_settings_get_data(cs); ofono_call_settings_clir_cb_t cb = cbd->cb; struct reply_clir *rclir; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: Reply failure: %s", __func__, ril_error_to_string(message->error)); goto error; } rclir = g_ril_reply_parse_get_clir(sd->ril, message); if (rclir == NULL) { ofono_error("%s: parse error", __func__); goto error; } CALLBACK_WITH_SUCCESS(cb, rclir->status, rclir->provisioned, cbd->data); g_ril_reply_free_get_clir(rclir); return; error: CALLBACK_WITH_FAILURE(cb, -1, -1, cbd->data); } static void ril_clir_query(struct ofono_call_settings *cs, ofono_call_settings_clir_cb_t cb, void *data) { struct settings_data *sd = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data, cs); int ret; ret = g_ril_send(sd->ril, RIL_REQUEST_GET_CLIR, NULL, ril_clir_query_cb, cbd, g_free); if (ret <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, -1, data); } } static void ril_clir_set(struct ofono_call_settings *cs, int mode, ofono_call_settings_set_cb_t cb, void *data) { struct settings_data *sd = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data, cs); struct parcel rilp; int ret; g_ril_request_set_clir(sd->ril, mode, &rilp); ret = g_ril_send(sd->ril, RIL_REQUEST_SET_CLIR, &rilp, ril_set_cb, cbd, g_free); if (ret <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static gboolean ril_delayed_register(gpointer user_data) { struct ofono_call_settings *cs = user_data; ofono_call_settings_register(cs); return FALSE; } static int ril_call_settings_probe(struct ofono_call_settings *cs, unsigned int vendor, void *user) { GRil *ril = user; struct settings_data *sd = g_new0(struct settings_data, 1); sd->ril = g_ril_clone(ril); ofono_call_settings_set_data(cs, sd); g_idle_add(ril_delayed_register, cs); return 0; } static void ril_call_settings_remove(struct ofono_call_settings *cs) { struct settings_data *sd = ofono_call_settings_get_data(cs); ofono_call_settings_set_data(cs, NULL); g_ril_unref(sd->ril); g_free(sd); } static struct ofono_call_settings_driver driver = { .name = RILMODEM, .probe = ril_call_settings_probe, .remove = ril_call_settings_remove, .clip_query = ril_clip_query, .cw_query = ril_cw_query, .cw_set = ril_cw_set, .clir_query = ril_clir_query, .clir_set = ril_clir_set /* * Not supported in RIL API * .colp_query = ril_colp_query, * .colr_query = ril_colr_query */ }; void ril_call_settings_init(void) { ofono_call_settings_driver_register(&driver); } void ril_call_settings_exit(void) { ofono_call_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/gprs.h0000644000015600001650000000311612671500024023012 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical 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. * * 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 "drivers/rilmodem/rilutil.h" struct ril_gprs_data { GRil *ril; struct ofono_modem *modem; gboolean ofono_attached; unsigned int max_cids; int rild_status; int tech; int state_changed_unsol; int pending_deact_req; guint status_retry_cb_id; }; int ril_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor, void *data); void ril_gprs_remove(struct ofono_gprs *gprs); void ril_gprs_start(struct ril_gprs_driver_data *driver_data, struct ofono_gprs *gprs, struct ril_gprs_data *gd); gboolean ril_gprs_set_attached_cb(gpointer user_data); void ril_gprs_registration_status(struct ofono_gprs *gprs, ofono_gprs_status_cb_t cb, void *data); void ril_gprs_set_ia_apn(struct ofono_gprs *gprs, const char *apn, enum ofono_gprs_proto proto, const char *user, const char *passwd, const char *mccmnc, ofono_gprs_cb_t cb, void *data); ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/rilmodem.h0000644000015600001650000000377212671500024023657 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical 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. * * 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 "rilutil.h" #define RILMODEM "rilmodem" /* Shared constants */ #define EF_STATUS_INVALIDATED 0 #define EF_STATUS_VALID 1 extern void ril_devinfo_init(void); extern void ril_devinfo_exit(void); extern void ril_call_volume_init(void); extern void ril_call_volume_exit(void); extern void ril_voicecall_init(void); extern void ril_voicecall_exit(void); extern void ril_sim_init(void); extern void ril_sim_exit(void); extern void ril_sms_init(void); extern void ril_sms_exit(void); extern void ril_netreg_init(void); extern void ril_netreg_exit(void); extern void ril_gprs_init(void); extern void ril_gprs_exit(void); extern void ril_gprs_context_init(void); extern void ril_gprs_context_exit(void); extern void ril_ussd_init(void); extern void ril_ussd_exit(void); extern void ril_call_settings_init(void); extern void ril_call_settings_exit(void); extern void ril_call_forwarding_init(void); extern void ril_call_forwarding_exit(void); extern void ril_radio_settings_init(void); extern void ril_radio_settings_exit(void); extern void ril_call_barring_init(void); extern void ril_call_barring_exit(void); extern void ril_phonebook_init(void); extern void ril_phonebook_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/gprs.c0000644000015600001650000003350012671500024023005 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * Copyright (C) 2013 Canonical Ltd. * Copyright (C) 2013 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gril.h" #include "grilutil.h" #include "common.h" #include "rilmodem.h" #include "grilreply.h" #include "grilrequest.h" #include "grilunsol.h" #include "gprs.h" /* Time between get data status retries */ #define GET_STATUS_TIMER_MS 5000 /* * This module is the ofono_gprs_driver implementation for rilmodem. * * Notes: * * 1. ofono_gprs_suspend/resume() are not used by this module, as * the concept of suspended GPRS is not exposed by RILD. */ static int ril_tech_to_bearer_tech(int ril_tech) { /* * This code handles the mapping between the RIL_RadioTechnology * and packet bearer values ( see values - 27.007 * Section 7.29 ). */ switch (ril_tech) { case RADIO_TECH_GSM: case RADIO_TECH_UNKNOWN: return PACKET_BEARER_NONE; case RADIO_TECH_GPRS: return PACKET_BEARER_GPRS; case RADIO_TECH_EDGE: return PACKET_BEARER_EGPRS; case RADIO_TECH_UMTS: return PACKET_BEARER_UMTS; case RADIO_TECH_HSDPA: return PACKET_BEARER_HSDPA; case RADIO_TECH_HSUPA: return PACKET_BEARER_HSUPA; case RADIO_TECH_HSPAP: case RADIO_TECH_HSPA: /* * HSPAP is HSPA+; which ofono doesn't define; * so, if differentiating HSPA and HSPA+ is * important, then ofono needs to be patched, * and we probably also need to introduce a * new indicator icon. */ return PACKET_BEARER_HSUPA_HSDPA; case RADIO_TECH_LTE: return PACKET_BEARER_EPS; default: return PACKET_BEARER_NONE; } } static void set_ia_apn_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_cb_t cb = cbd->cb; struct ofono_gprs *gprs = cbd->user; struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); if (message->error != RIL_E_SUCCESS) { ofono_error("%s: reply failure: %s", __func__, ril_error_to_string(message->error)); CALLBACK_WITH_FAILURE(cb, cbd->data); return; } g_ril_print_response_no_args(gd->ril, message); CALLBACK_WITH_SUCCESS(cb, cbd->data); } void ril_gprs_set_ia_apn(struct ofono_gprs *gprs, const char *apn, enum ofono_gprs_proto proto, const char *user, const char *passwd, const char *mccmnc, ofono_gprs_cb_t cb, void *data) { struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); struct cb_data *cbd; struct parcel rilp; if (!ofono_modem_get_boolean(gd->modem, MODEM_PROP_LTE_CAPABLE)) { CALLBACK_WITH_SUCCESS(cb, data); return; } cbd = cb_data_new(cb, data, gprs); g_ril_request_set_initial_attach_apn(gd->ril, apn, proto, user, passwd, mccmnc, &rilp); if (g_ril_send(gd->ril, RIL_REQUEST_SET_INITIAL_ATTACH_APN, &rilp, set_ia_apn_cb, cbd, g_free) == 0) { ofono_error("%s: failure sending request", __func__); g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void ril_gprs_state_change(struct ril_msg *message, gpointer user_data) { struct ofono_gprs *gprs = user_data; struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); g_ril_print_unsol_no_args(gd->ril, message); /* * We just want to track network data status if ofono * itself is attached, so we avoid unnecessary data state requests. */ if (gd->ofono_attached == TRUE) ril_gprs_registration_status(gprs, NULL, NULL); } gboolean ril_gprs_set_attached_cb(gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_cb_t cb = cbd->cb; DBG(""); CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); /* Run once per g_idle_add() call */ return FALSE; } static void ril_gprs_set_attached(struct ofono_gprs *gprs, int attached, ofono_gprs_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data, NULL); struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); DBG("attached: %d", attached); /* * As RIL offers no actual control over the GPRS 'attached' * state, we save the desired state, and use it to override * the actual modem's state in the 'attached_status' function. * This is similar to the way the core ofono gprs code handles * data roaming ( see src/gprs.c gprs_netreg_update(). * * The core gprs code calls driver->set_attached() when a netreg * notificaiton is received and any configured roaming conditions * are met. */ gd->ofono_attached = attached; /* * Call from idle loop, so core can set driver_attached before * the callback is invoked. */ g_idle_add(ril_gprs_set_attached_cb, cbd); } static gboolean ril_get_status_retry(gpointer user_data) { struct ofono_gprs *gprs = user_data; struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); gd->status_retry_cb_id = 0; ril_gprs_registration_status(gprs, NULL, NULL); return FALSE; } static void ril_data_reg_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_status_cb_t cb = cbd->cb; struct ofono_gprs *gprs = cbd->user; struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); struct reply_data_reg_state *reply; gboolean attached = FALSE; gboolean notify_status = FALSE; int old_status; old_status = gd->rild_status; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: DATA_REGISTRATION_STATE reply failure: %s", __func__, ril_error_to_string(message->error)); goto error; } reply = g_ril_reply_parse_data_reg_state(gd->ril, message); if (reply == NULL) goto error; /* * There are three cases that can result in this callback * running: * * 1) The driver's probe() method was called, and thus an * internal call to ril_gprs_registration_status() is * generated. No ofono cb exists. * * 2) ril_gprs_state_change() is called due to an unsolicited * event from RILD. No ofono cb exists. * * 3) The ofono code code calls the driver's attached_status() * function. A valid ofono cb exists. */ if (gd->rild_status != reply->reg_state.status) { gd->rild_status = reply->reg_state.status; if (cb == NULL) notify_status = TRUE; } /* * Override the actual status based upon the desired * attached status set by the core GPRS code ( controlled * by the ConnnectionManager's 'Powered' property ). */ attached = (reply->reg_state.status == NETWORK_REGISTRATION_STATUS_REGISTERED || reply->reg_state.status == NETWORK_REGISTRATION_STATUS_ROAMING); if (attached && gd->ofono_attached == FALSE) { DBG("attached=true; ofono_attached=false; return !REGISTERED"); reply->reg_state.status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED; /* * Further optimization so that if ril_status == * NOT_REGISTERED, ofono_attached == false, and status == * ROAMING | REGISTERED, then notify gets cleared... * * As is, this results in unecessary status notify calls * when nothing has changed. */ if (notify_status && reply->reg_state.status == old_status) notify_status = FALSE; } if (old_status == -1) { ofono_gprs_register(gprs); /* Different rild implementations use different events here */ g_ril_register(gd->ril, gd->state_changed_unsol, ril_gprs_state_change, gprs); if (reply->max_cids == 0) gd->max_cids = RIL_MAX_NUM_ACTIVE_DATA_CALLS; else if (reply->max_cids < RIL_MAX_NUM_ACTIVE_DATA_CALLS) gd->max_cids = reply->max_cids; else gd->max_cids = RIL_MAX_NUM_ACTIVE_DATA_CALLS; DBG("Setting max cids to %d", gd->max_cids); ofono_gprs_set_cid_range(gprs, 1, gd->max_cids); /* * This callback is a result of the inital call * to probe(), so should return after registration. */ g_free(reply); return; } /* Just need to notify ofono if it's already attached */ if (notify_status) { /* * If network disconnect has occurred, call detached_notify() * instead of status_notify(). */ if (!attached && (old_status == NETWORK_REGISTRATION_STATUS_REGISTERED || old_status == NETWORK_REGISTRATION_STATUS_ROAMING)) { DBG("calling ofono_gprs_detached_notify()"); ofono_gprs_detached_notify(gprs); reply->reg_state.tech = RADIO_TECH_UNKNOWN; } else { DBG("calling ofono_gprs_status_notify()"); ofono_gprs_status_notify(gprs, reply->reg_state.status); } } if (gd->tech != reply->reg_state.tech) { gd->tech = reply->reg_state.tech; ofono_gprs_bearer_notify(gprs, ril_tech_to_bearer_tech(reply->reg_state.tech)); } if (cb) CALLBACK_WITH_SUCCESS(cb, reply->reg_state.status, cbd->data); g_free(reply); return; error: /* * For some modems DATA_REGISTRATION_STATE will return an error until we * are registered in the voice network. */ if (old_status == -1 && message->error == RIL_E_GENERIC_FAILURE) gd->status_retry_cb_id = g_timeout_add(GET_STATUS_TIMER_MS, ril_get_status_retry, gprs); if (cb) CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } void ril_gprs_registration_status(struct ofono_gprs *gprs, ofono_gprs_status_cb_t cb, void *data) { struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); struct cb_data *cbd = cb_data_new(cb, data, gprs); DBG(""); if (g_ril_send(gd->ril, RIL_REQUEST_DATA_REGISTRATION_STATE, NULL, ril_data_reg_cb, cbd, g_free) == 0) { ofono_error("%s: send " "RIL_REQUEST_DATA_REGISTRATION_STATE failed", __func__); g_free(cbd); if (cb != NULL) CALLBACK_WITH_FAILURE(cb, -1, data); } } static void drop_data_call_cb(struct ril_msg *message, gpointer user_data) { struct ofono_gprs *gprs = user_data; struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); if (message->error == RIL_E_SUCCESS) g_ril_print_response_no_args(gd->ril, message); else ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); if (--(gd->pending_deact_req) == 0) ril_gprs_registration_status(gprs, NULL, NULL); } static int drop_data_call(struct ofono_gprs *gprs, int cid) { struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); struct req_deactivate_data_call request; struct parcel rilp; struct ofono_error error; request.cid = cid; request.reason = RIL_DEACTIVATE_DATA_CALL_NO_REASON; g_ril_request_deactivate_data_call(gd->ril, &request, &rilp, &error); if (g_ril_send(gd->ril, RIL_REQUEST_DEACTIVATE_DATA_CALL, &rilp, drop_data_call_cb, gprs, NULL) == 0) { ofono_error("%s: send failed", __func__); return -1; } return 0; } static void get_active_data_calls_cb(struct ril_msg *message, gpointer user_data) { struct ofono_gprs *gprs = user_data; struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); struct ril_data_call_list *call_list = NULL; GSList *iterator; struct ril_data_call *call; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); goto end; } /* reply can be NULL when there are no existing data calls */ call_list = g_ril_unsol_parse_data_call_list(gd->ril, message); if (call_list == NULL) goto end; /* * We disconnect from previous calls here, which might be needed * because of a previous ofono abort, as some rild implementations do * not disconnect the calls even after the ril socket is closed. */ for (iterator = call_list->calls; iterator; iterator = iterator->next) { call = iterator->data; DBG("Standing data call with cid %d", call->cid); if (drop_data_call(gprs, call->cid) == 0) ++(gd->pending_deact_req); } g_ril_unsol_free_data_call_list(call_list); end: if (gd->pending_deact_req == 0) ril_gprs_registration_status(gprs, NULL, NULL); } static void get_active_data_calls(struct ofono_gprs *gprs) { struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); if (g_ril_send(gd->ril, RIL_REQUEST_DATA_CALL_LIST, NULL, get_active_data_calls_cb, gprs, NULL) == 0) ofono_error("%s: send failed", __func__); } void ril_gprs_start(struct ril_gprs_driver_data *driver_data, struct ofono_gprs *gprs, struct ril_gprs_data *gd) { gd->ril = g_ril_clone(driver_data->gril); gd->modem = driver_data->modem; gd->ofono_attached = FALSE; gd->max_cids = 0; gd->rild_status = -1; gd->tech = RADIO_TECH_UNKNOWN; /* AOSP RILD tracks data network state together with voice */ gd->state_changed_unsol = RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED; ofono_gprs_set_data(gprs, gd); get_active_data_calls(gprs); } int ril_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor, void *data) { struct ril_gprs_driver_data *driver_data = data; struct ril_gprs_data *gd; gd = g_try_new0(struct ril_gprs_data, 1); if (gd == NULL) return -ENOMEM; ril_gprs_start(driver_data, gprs, gd); return 0; } void ril_gprs_remove(struct ofono_gprs *gprs) { struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); DBG(""); if (gd->status_retry_cb_id != 0) g_source_remove(gd->status_retry_cb_id); ofono_gprs_set_data(gprs, NULL); g_ril_unref(gd->ril); g_free(gd); } static struct ofono_gprs_driver driver = { .name = RILMODEM, .probe = ril_gprs_probe, .remove = ril_gprs_remove, .set_attached = ril_gprs_set_attached, .attached_status = ril_gprs_registration_status, .set_ia_apn = ril_gprs_set_ia_apn, }; void ril_gprs_init(void) { ofono_gprs_driver_register(&driver); } void ril_gprs_exit(void) { ofono_gprs_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/rilutil.h0000644000015600001650000001033512671500024023524 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical 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. * * 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 * */ #ifndef RILUTIL_H #define RILUTIL_H #include #include #include #include /* TODO: create a table lookup*/ #define PREFIX_30_NETMASK "255.255.255.252" #define PREFIX_29_NETMASK "255.255.255.248" #define PREFIX_28_NETMASK "255.255.255.240" #define PREFIX_27_NETMASK "255.255.255.224" #define PREFIX_26_NETMASK "255.255.255.192" #define PREFIX_25_NETMASK "255.255.255.128" #define PREFIX_24_NETMASK "255.255.255.0" #define MODEM_PROP_LTE_CAPABLE "lte-capable" enum ril_util_sms_store { RIL_UTIL_SMS_STORE_SM = 0, RIL_UTIL_SMS_STORE_ME = 1, RIL_UTIL_SMS_STORE_MT = 2, RIL_UTIL_SMS_STORE_SR = 3, RIL_UTIL_SMS_STORE_BM = 4, }; /* 3GPP TS 27.007 Release 8 Section 5.5 */ enum at_util_charset { RIL_UTIL_CHARSET_GSM = 0x1, RIL_UTIL_CHARSET_HEX = 0x2, RIL_UTIL_CHARSET_IRA = 0x4, RIL_UTIL_CHARSET_PCCP437 = 0x8, RIL_UTIL_CHARSET_PCDN = 0x10, RIL_UTIL_CHARSET_UCS2 = 0x20, RIL_UTIL_CHARSET_UTF8 = 0x40, RIL_UTIL_CHARSET_8859_1 = 0x80, RIL_UTIL_CHARSET_8859_2 = 0x100, RIL_UTIL_CHARSET_8859_3 = 0x200, RIL_UTIL_CHARSET_8859_4 = 0x400, RIL_UTIL_CHARSET_8859_5 = 0x800, RIL_UTIL_CHARSET_8859_6 = 0x1000, RIL_UTIL_CHARSET_8859_C = 0x2000, RIL_UTIL_CHARSET_8859_A = 0x4000, RIL_UTIL_CHARSET_8859_G = 0x8000, RIL_UTIL_CHARSET_8859_H = 0x10000, }; struct ril_sim_data { struct ofono_modem *modem; GRil *gril; ofono_sim_state_event_cb_t ril_state_watch; }; struct ril_gprs_context_data { GRil *gril; struct ofono_modem *modem; enum ofono_gprs_context_type type; }; struct ril_voicecall_driver_data { GRil *gril; struct ofono_modem *modem; }; struct ril_gprs_driver_data { GRil *gril; struct ofono_modem *modem; }; struct ril_radio_settings_driver_data { GRil *gril; struct ofono_modem *modem; }; typedef void (*ril_util_sim_inserted_cb_t)(gboolean present, void *userdata); void decode_ril_error(struct ofono_error *error, const char *final); gint ril_util_call_compare_by_status(gconstpointer a, gconstpointer b); gint ril_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b); gint ril_util_call_compare_by_id(gconstpointer a, gconstpointer b); gint ril_util_call_compare(gconstpointer a, gconstpointer b); gchar *ril_util_get_netmask(const char *address); struct ril_util_sim_state_query *ril_util_sim_state_query_new(GRil *ril, guint interval, guint num_times, ril_util_sim_inserted_cb_t cb, void *userdata, GDestroyNotify destroy); void ril_util_sim_state_query_free(struct ril_util_sim_state_query *req); struct cb_data { void *cb; void *data; void *user; }; static inline struct cb_data *cb_data_new(void *cb, void *data, void *user) { struct cb_data *ret; ret = g_new0(struct cb_data, 1); ret->cb = cb; ret->data = data; ret->user = user; return ret; } static inline int ril_util_convert_signal_strength(int strength) { int result; if (strength == 99) result = -1; else result = (strength * 100) / 31; return result; } #define DECLARE_FAILURE(e) \ struct ofono_error e; \ e.type = OFONO_ERROR_TYPE_FAILURE; \ e.error = 0 \ #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) #endif /* RILUTIL_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/gprs-context.c0000644000015600001650000003560412671500024024476 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2013 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "ofono.h" #include "grilreply.h" #include "grilrequest.h" #include "grilunsol.h" #include "gprs.h" #include "rilmodem.h" #include "drivers/mtkmodem/mtkutil.h" #define NUM_DEACTIVATION_RETRIES 4 #define TIME_BETWEEN_DEACT_RETRIES_S 2 enum state { STATE_IDLE, STATE_ENABLING, STATE_DISABLING, STATE_ACTIVE, }; struct gprs_context_data { GRil *ril; struct ofono_modem *modem; unsigned vendor; gint active_ctx_cid; gint active_rild_cid; enum state state; guint call_list_id; char *apn; enum ofono_gprs_context_type type; int deact_retries; guint retry_ev_id; struct cb_data *retry_cbd; guint reset_ev_id; }; static void ril_gprs_context_deactivate_primary(struct ofono_gprs_context *gc, unsigned int id, ofono_gprs_context_cb_t cb, void *data); static void ril_deactivate_data_call_cb(struct ril_msg *message, gpointer user_data); static void set_context_disconnected(struct gprs_context_data *gcd) { DBG(""); gcd->active_ctx_cid = -1; gcd->active_rild_cid = -1; gcd->state = STATE_IDLE; g_free(gcd->apn); gcd->apn = NULL; } static void disconnect_context(struct ofono_gprs_context *gc) { ril_gprs_context_deactivate_primary(gc, 0, NULL, NULL); } static void ril_gprs_context_call_list_changed(struct ril_msg *message, gpointer user_data) { struct ofono_gprs_context *gc = user_data; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ril_data_call *call = NULL; struct ril_data_call_list *call_list; gboolean active_cid_found = FALSE; gboolean disconnect = FALSE; GSList *iterator = NULL; call_list = g_ril_unsol_parse_data_call_list(gcd->ril, message); if (call_list == NULL) return; DBG("*gc: %p num calls: %d", gc, g_slist_length(call_list->calls)); for (iterator = call_list->calls; iterator; iterator = iterator->next) { call = (struct ril_data_call *) iterator->data; if (call->cid == gcd->active_rild_cid) { active_cid_found = TRUE; DBG("found call - cid: %d", call->cid); if (call->active == 0) { DBG("call !active; notify disconnect: %d", call->cid); disconnect = TRUE; } break; } } if ((disconnect == TRUE || active_cid_found == FALSE) && gcd->state != STATE_IDLE) { ofono_info("Clearing active context; disconnect: %d" " active_cid_found: %d active_ctx_cid: %d", disconnect, active_cid_found, gcd->active_ctx_cid); ofono_gprs_context_deactivated(gc, gcd->active_ctx_cid); set_context_disconnected(gcd); } g_ril_unsol_free_data_call_list(call_list); } static void ril_setup_data_call_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ril_data_call *call = NULL; struct ril_data_call_list *call_list = NULL; DBG("*gc: %p", gc); if (message->error != RIL_E_SUCCESS) { ofono_error("%s: setup data call failed for apn: %s - %s", __func__, gcd->apn, ril_error_to_string(message->error)); set_context_disconnected(gcd); goto error; } call_list = g_ril_unsol_parse_data_call_list(gcd->ril, message); if (call_list == NULL) { /* parsing failed, need to actually disconnect */ disconnect_context(gc); goto error; } if (g_slist_length(call_list->calls) != 1) { ofono_error("%s: setup_data_call reply for apn: %s," " includes %d calls", __func__, gcd->apn, g_slist_length(call_list->calls)); disconnect_context(gc); goto error; } call = (struct ril_data_call *) call_list->calls->data; /* Check for valid DNS settings, except for MMS contexts */ if (gcd->type != OFONO_GPRS_CONTEXT_TYPE_MMS && (call->dns_addrs == NULL || g_strv_length(call->dns_addrs) == 0)) { ofono_error("%s: no DNS in context of type %d", __func__, gcd->type); disconnect_context(gc); goto error; } if (call->status != PDP_FAIL_NONE) { ofono_error("%s: reply->status for apn: %s, is non-zero: %s", __func__, gcd->apn, ril_pdp_fail_to_string(call->status)); set_context_disconnected(gcd); goto error; } gcd->active_rild_cid = call->cid; gcd->state = STATE_ACTIVE; ofono_gprs_context_set_interface(gc, call->ifname); ofono_gprs_context_set_ipv4_netmask(gc, ril_util_get_netmask(call->ip_addr)); ofono_gprs_context_set_ipv4_address(gc, call->ip_addr, TRUE); ofono_gprs_context_set_ipv4_gateway(gc, call->gateways[0]); ofono_gprs_context_set_ipv4_dns_servers(gc, (const char **) call->dns_addrs); g_ril_unsol_free_data_call_list(call_list); /* activate listener for data call changed events.... */ gcd->call_list_id = g_ril_register(gcd->ril, RIL_UNSOL_DATA_CALL_LIST_CHANGED, ril_gprs_context_call_list_changed, gc); CALLBACK_WITH_SUCCESS(cb, cbd->data); return; error: g_ril_unsol_free_data_call_list(call_list); CALLBACK_WITH_FAILURE(cb, cbd->data); } static void ril_gprs_context_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 ofono_modem *modem = ofono_gprs_context_get_modem(gc); struct ofono_atom *gprs_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_GPRS); struct ofono_gprs *gprs = NULL; struct ril_gprs_data *gd = NULL; struct cb_data *cbd = cb_data_new(cb, data, gc); struct req_setup_data_call request; struct parcel rilp; struct ofono_error error; int ret = 0; g_assert(gprs_atom != NULL); gprs = __ofono_atom_get_data(gprs_atom); g_assert(gprs != NULL); gd = ofono_gprs_get_data(gprs); g_assert(gd != NULL); /* * 0: CDMA 1: GSM/UMTS, 2... * anything 2+ is a RadioTechnology value +2 */ DBG("*gc: %p activating cid: %d; curr_tech: %d", gc, ctx->cid, gd->tech); if (gd->tech == RADIO_TECH_UNKNOWN) { ofono_error("%s: radio tech for apn: %s UNKNOWN!", __func__, gcd->apn); request.tech = 1; } else { request.tech = gd->tech + 2; } /* * TODO: add comments about tethering, other non-public * profiles... */ if (g_ril_vendor(gcd->ril) == OFONO_RIL_VENDOR_MTK && gcd->type == OFONO_GPRS_CONTEXT_TYPE_MMS) request.data_profile = RIL_DATA_PROFILE_MTK_MMS; else request.data_profile = RIL_DATA_PROFILE_DEFAULT; request.apn = g_strdup(ctx->apn); request.username = g_strdup(ctx->username); request.password = g_strdup(ctx->password); /* * We do the same as in $AOSP/frameworks/opt/telephony/src/java/com/ * android/internal/telephony/dataconnection/DataConnection.java, * onConnect(), and use authentication or not depending on whether * the user field is empty or not. */ if (request.username != NULL && request.username[0] != '\0') request.auth_type = RIL_AUTH_BOTH; else request.auth_type = RIL_AUTH_NONE; request.protocol = ctx->proto; request.req_cid = ctx->cid; if (g_ril_request_setup_data_call(gcd->ril, &request, &rilp, &error) == FALSE) { ofono_error("%s: couldn't build SETUP_DATA_CALL" " request for apn: %s.", __func__, request.apn); goto error; } gcd->active_ctx_cid = ctx->cid; gcd->state = STATE_ENABLING; gcd->apn = g_strdup(ctx->apn); ret = g_ril_send(gcd->ril, RIL_REQUEST_SETUP_DATA_CALL, &rilp, ril_setup_data_call_cb, cbd, g_free); error: g_free(request.apn); g_free(request.username); g_free(request.password); if (ret == 0) { ofono_error("%s: send SETUP_DATA_CALL failed for apn: %s.", __func__, gcd->apn); set_context_disconnected(gcd); g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static gboolean reset_modem(gpointer data) { struct gprs_context_data *gcd = data; struct ofono_modem *modem = gcd->modem; gcd->reset_ev_id = 0; mtk_reset_modem(modem); return FALSE; } static gboolean retry_deactivate(gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct req_deactivate_data_call request; struct parcel rilp; struct ofono_error error; gcd->retry_ev_id = 0; /* We might have received a call list update while waiting */ if (gcd->state == STATE_IDLE) { if (cb) CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); return FALSE; } request.cid = gcd->active_rild_cid; request.reason = RIL_DEACTIVATE_DATA_CALL_NO_REASON; g_ril_request_deactivate_data_call(gcd->ril, &request, &rilp, &error); if (g_ril_send(gcd->ril, RIL_REQUEST_DEACTIVATE_DATA_CALL, &rilp, ril_deactivate_data_call_cb, cbd, g_free) == 0) { ofono_error("%s: send DEACTIVATE_DATA_CALL failed for apn: %s", __func__, gcd->apn); if (cb) CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } return FALSE; } static void ril_deactivate_data_call_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); gint active_ctx_cid; DBG("*gc: %p", gc); if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(gcd->ril, message); active_ctx_cid = gcd->active_ctx_cid; set_context_disconnected(gcd); /* * If the deactivate was a result of a data network detach or of * an error in data call establishment, there won't be call * back, so _deactivated() needs to be called directly. */ if (cb) CALLBACK_WITH_SUCCESS(cb, cbd->data); else ofono_gprs_context_deactivated(gc, active_ctx_cid); } else { ofono_error("%s: reply failure for apn: %s - %s", __func__, gcd->apn, ril_error_to_string(message->error)); /* * It has been detected that some modems fail the deactivation * temporarily. We do retries to handle that case. */ if (--(gcd->deact_retries) > 0) { gcd->retry_cbd = cb_data_new(cb, cbd->data, gc); gcd->retry_ev_id = g_timeout_add_seconds( TIME_BETWEEN_DEACT_RETRIES_S, retry_deactivate, gcd->retry_cbd); } else { ofono_error("%s: retry limit hit", __func__); if (cb) CALLBACK_WITH_FAILURE(cb, cbd->data); /* * Reset modem if MTK. TODO Failures deactivating a * context have not been reported for other modems, but * it would be good to have a generic method to force an * internal reset nonetheless. */ if (gcd->vendor == OFONO_RIL_VENDOR_MTK) gcd->reset_ev_id = g_idle_add(reset_modem, gcd); } } } static void ril_gprs_context_deactivate_primary(struct ofono_gprs_context *gc, unsigned int id, ofono_gprs_context_cb_t cb, void *data) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *cbd = NULL; struct parcel rilp; struct req_deactivate_data_call request; struct ofono_error error; int ret = 0; DBG("*gc: %p cid: %d active_rild_cid: %d", gc, id, gcd->active_rild_cid); if (gcd->state == STATE_IDLE || gcd->state == STATE_DISABLING) { /* nothing to do */ if (cb) { CALLBACK_WITH_SUCCESS(cb, data); g_free(cbd); } return; } cbd = cb_data_new(cb, data, gc); gcd->state = STATE_DISABLING; if (g_ril_unregister(gcd->ril, gcd->call_list_id) == FALSE) { ofono_warn("%s: couldn't remove call_list listener" " for apn: %s.", __func__, gcd->apn); } request.cid = gcd->active_rild_cid; request.reason = RIL_DEACTIVATE_DATA_CALL_NO_REASON; if (g_ril_request_deactivate_data_call(gcd->ril, &request, &rilp, &error) == FALSE) { ofono_error("%s: couldn't build DEACTIVATE_DATA_CALL" " request for apn: %s.", __func__, gcd->apn); goto error; } gcd->deact_retries = NUM_DEACTIVATION_RETRIES; ret = g_ril_send(gcd->ril, RIL_REQUEST_DEACTIVATE_DATA_CALL, &rilp, ril_deactivate_data_call_cb, cbd, g_free); error: if (ret == 0) { /* TODO: should we force state to disconnected here? */ ofono_error("%s: send DEACTIVATE_DATA_CALL failed for apn: %s", __func__, gcd->apn); g_free(cbd); if (cb) CALLBACK_WITH_FAILURE(cb, data); } } static void ril_gprs_context_detach_shutdown(struct ofono_gprs_context *gc, unsigned int id) { DBG("*gc: %p cid: %d", gc, id); ril_gprs_context_deactivate_primary(gc, 0, NULL, NULL); } static int ril_gprs_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *data) { struct ril_gprs_context_data *ril_data = data; struct gprs_context_data *gcd; DBG("*gc: %p", gc); gcd = g_try_new0(struct gprs_context_data, 1); if (gcd == NULL) return -ENOMEM; gcd->ril = g_ril_clone(ril_data->gril); gcd->modem = ril_data->modem; gcd->vendor = vendor; set_context_disconnected(gcd); gcd->call_list_id = -1; gcd->type = ril_data->type; ofono_gprs_context_set_data(gc, gcd); return 0; } static void ril_gprs_context_remove(struct ofono_gprs_context *gc) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); DBG("*gc: %p", gc); if (gcd->state != STATE_IDLE && gcd->state != STATE_DISABLING) { struct req_deactivate_data_call request; struct parcel rilp; struct ofono_error error; request.cid = gcd->active_rild_cid; request.reason = RIL_DEACTIVATE_DATA_CALL_NO_REASON; g_ril_request_deactivate_data_call(gcd->ril, &request, &rilp, &error); g_ril_send(gcd->ril, RIL_REQUEST_DEACTIVATE_DATA_CALL, &rilp, NULL, NULL, NULL); } if (gcd->retry_ev_id > 0) { g_source_remove(gcd->retry_ev_id); g_free(gcd->retry_cbd); } if (gcd->reset_ev_id > 0) g_source_remove(gcd->reset_ev_id); ofono_gprs_context_set_data(gc, NULL); g_ril_unref(gcd->ril); g_free(gcd); } static struct ofono_gprs_context_driver driver = { .name = RILMODEM, .probe = ril_gprs_context_probe, .remove = ril_gprs_context_remove, .activate_primary = ril_gprs_context_activate_primary, .deactivate_primary = ril_gprs_context_deactivate_primary, .detach_shutdown = ril_gprs_context_detach_shutdown, }; void ril_gprs_context_init(void) { ofono_gprs_context_driver_register(&driver); } void ril_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/rilmodem/vendor.h0000644000015600001650000000174012671500024023335 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical Ltd. 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 * */ #ifndef RILMODEM_VENDOR_H #define RILMODEM_VENDOR_H enum ofono_ril_vendor { OFONO_RIL_VENDOR_AOSP = 0, OFONO_RIL_VENDOR_MTK, OFONO_RIL_VENDOR_INFINEON, OFONO_RIL_VENDOR_QCOM_MSIM }; #endif /* RILMODEM_VENDOR_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/mbmmodem/0000755000015600001650000000000012671500304021653 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/mbmmodem/mbmmodem.h0000644000015600001650000000203512671500024023620 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void mbm_gprs_context_init(void); extern void mbm_gprs_context_exit(void); extern void mbm_stk_init(void); extern void mbm_stk_exit(void); extern void mbm_location_reporting_init(); extern void mbm_location_reporting_exit(); ofono-1.17.bzr6912+16.04.20160314.3/drivers/mbmmodem/mbmmodem.c0000644000015600001650000000247712671500024023625 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "mbmmodem.h" static int mbmmodem_init(void) { mbm_gprs_context_init(); mbm_stk_init(); mbm_location_reporting_init(); return 0; } static void mbmmodem_exit(void) { mbm_location_reporting_exit(); mbm_stk_exit(); mbm_gprs_context_exit(); } OFONO_PLUGIN_DEFINE(mbmmodem, "MBM modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, mbmmodem_init, mbmmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/mbmmodem/stk.c0000644000015600001650000001353012671500024022621 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "mbmmodem.h" struct stk_data { GAtChat *chat; }; static const char *none_prefix[] = { NULL }; static const char *stke_prefix[] = { "*STKE:", NULL }; static void stke_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_stk_envelope_cb_t cb = cbd->cb; GAtResultIter iter; struct ofono_error error; const guint8 *pdu = NULL; gint len = 0; DBG(""); decode_at_error(&error, g_at_result_final_response(result)); if (!ok) goto done; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "*STKE:") == FALSE) goto done; /* Response data is optional */ g_at_result_iter_next_hexstring(&iter, &pdu, &len); DBG("len %d", len); done: cb(&error, pdu, len, cbd->data); } static void mbm_stk_envelope(struct ofono_stk *stk, int length, const unsigned char *command, ofono_stk_envelope_cb_t cb, void *data) { struct stk_data *sd = ofono_stk_get_data(stk); struct cb_data *cbd = cb_data_new(cb, data); char *buf = g_try_new(char, 64 + length * 2); int len; DBG(""); if (buf == NULL) goto error; len = sprintf(buf, "AT*STKE=\""); for (; length; length--) len += sprintf(buf + len, "%02hhX", *command++); len += sprintf(buf + len, "\""); DBG("%s", buf); if (g_at_chat_send(sd->chat, buf, stke_prefix, stke_cb, cbd, g_free) > 0) { g_free(buf); return; } error: g_free(buf); g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } static void stkr_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_stk_generic_cb_t cb = cbd->cb; struct ofono_error error; DBG(""); decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void mbm_stk_terminal_response(struct ofono_stk *stk, int length, const unsigned char *command, ofono_stk_generic_cb_t cb, void *data) { struct stk_data *sd = ofono_stk_get_data(stk); struct cb_data *cbd = cb_data_new(cb, data); char *buf = g_try_new(char, 64 + length * 2); int len; DBG(""); if (buf == NULL) goto error; len = sprintf(buf, "AT*STKR=\""); for (; length; length--) len += sprintf(buf + len, "%02hhX", *command++); len += sprintf(buf + len, "\""); DBG("%s", buf); if (g_at_chat_send(sd->chat, buf, none_prefix, stkr_cb, cbd, g_free) > 0) { g_free(buf); return; } error: g_free(buf); g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void stki_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; GAtResultIter iter; const guint8 *pdu; gint len; DBG(""); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "*STKI:")) return; if (!g_at_result_iter_next_hexstring(&iter, &pdu, &len)) return; ofono_stk_proactive_command_notify(stk, len, pdu); } static void stkn_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; GAtResultIter iter; const guint8 *pdu; gint len; DBG(""); /* Proactive command has been handled by the modem. */ g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "*STKN:") == FALSE) return; if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE) return; if (len == 0) return; ofono_stk_proactive_command_handled_notify(stk, len, pdu); } static void stkend_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; DBG(""); ofono_stk_proactive_session_end_notify(stk); } static void mbm_stkc_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; struct stk_data *sd = ofono_stk_get_data(stk); DBG(""); if (!ok) return; g_at_chat_register(sd->chat, "*STKI:", stki_notify, FALSE, stk, NULL); g_at_chat_register(sd->chat, "*STKN:", stkn_notify, FALSE, stk, NULL); g_at_chat_register(sd->chat, "*STKEND", stkend_notify, FALSE, stk, NULL); ofono_stk_register(stk); } static int mbm_stk_probe(struct ofono_stk *stk, unsigned int vendor, void *data) { GAtChat *chat = data; struct stk_data *sd; DBG(""); sd = g_try_new0(struct stk_data, 1); if (sd == NULL) return -ENOMEM; sd->chat = g_at_chat_clone(chat); ofono_stk_set_data(stk, sd); /* Perform PROFILE DOWNLOAD and enable *STKI / *STKN */ g_at_chat_send(sd->chat, "AT*STKC=1,\"19E1FFFF0000FF7FFF03FEFF\"", none_prefix, mbm_stkc_cb, stk, NULL); return 0; } static void mbm_stk_remove(struct ofono_stk *stk) { struct stk_data *sd = ofono_stk_get_data(stk); DBG(""); ofono_stk_set_data(stk, NULL); g_at_chat_unref(sd->chat); g_free(sd); } static struct ofono_stk_driver driver = { .name = "mbmmodem", .probe = mbm_stk_probe, .remove = mbm_stk_remove, .envelope = mbm_stk_envelope, .terminal_response = mbm_stk_terminal_response, }; void mbm_stk_init(void) { ofono_stk_driver_register(&driver); } void mbm_stk_exit(void) { ofono_stk_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mbmmodem/location-reporting.c0000644000015600001650000001314412671500024025640 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ProFUSION embedded systems. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "gattty.h" #include "mbmmodem.h" static const char *none_prefix[] = { NULL }; static const char *e2gpsctl_prefix[] = { "*E2GPSCTL:", NULL }; struct gps_data { GAtChat *chat; GAtChat *data_chat; }; static void mbm_e2gpsctl_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; struct gps_data *gd = ofono_location_reporting_get_data(lr); 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; } g_at_chat_unref(gd->data_chat); CALLBACK_WITH_SUCCESS(cb, cbd->data); } static void mbm_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*E2GPSCTL=0,5,1", none_prefix, mbm_e2gpsctl_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; GIOStatus status; gsize written; int fd; modem = ofono_location_reporting_get_modem(lr); gps_dev = ofono_modem_get_string(modem, "GPSDevice"); 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); status = g_io_channel_write_chars(channel, "AT*E2GPSNPD\r\n", -1, &written, NULL); g_io_channel_set_close_on_unref(channel, FALSE); g_io_channel_unref(channel); if (status != G_IO_STATUS_NORMAL || written != 13) { close(fd); return -1; } return fd; } static void mbm_e2gpsctl_enable_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 mbm_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*E2GPSCTL=1,5,1", none_prefix, mbm_e2gpsctl_enable_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } static void mbm_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 mbm_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*E2GPSCTL=?", e2gpsctl_prefix, mbm_location_reporting_support_cb, lr, NULL); return 0; } static void mbm_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 = "mbmmodem", .type = OFONO_LOCATION_REPORTING_TYPE_NMEA, .probe = mbm_location_reporting_probe, .remove = mbm_location_reporting_remove, .enable = mbm_location_reporting_enable, .disable = mbm_location_reporting_disable, }; void mbm_location_reporting_init() { ofono_location_reporting_driver_register(&driver); } void mbm_location_reporting_exit() { ofono_location_reporting_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mbmmodem/gprs-context.c0000644000015600001650000003006112671500024024453 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "mbmmodem.h" #define MBM_E2NAP_DISCONNECTED 0 #define MBM_E2NAP_CONNECTED 1 #define MBM_E2NAP_CONNECTING 2 #define AUTH_BUF_LENGTH OFONO_GPRS_MAX_USERNAME_LENGTH + \ OFONO_GPRS_MAX_PASSWORD_LENGTH + 128 #define MAX_DNS 5 #define STATIC_IP_NETMASK "255.255.255.248" static const char *none_prefix[] = { NULL }; static const char *e2ipcfg_prefix[] = { "*E2IPCFG:", NULL }; static const char *enap_prefix[] = { "*ENAP:", NULL }; static gboolean mbm_enap_poll(gpointer user_data); enum mbm_state { MBM_NONE = 0, MBM_ENABLING = 1, MBM_DISABLING = 2, }; struct gprs_context_data { GAtChat *chat; unsigned int active_context; gboolean have_e2nap; gboolean have_e2ipcfg; unsigned int enap_source; enum mbm_state mbm_state; ofono_gprs_context_cb_t cb; void *cb_data; /* Callback data */ int enap; /* State of the call */ }; static void mbm_e2ipcfg_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; int numdns = 0; int type; const char *str; const char *ip = NULL; const char *gateway = NULL; const char *dns[MAX_DNS + 1]; struct ofono_modem *modem; const char *interface; gboolean success = FALSE; DBG("ok %d", ok); if (!ok) goto out; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "*E2IPCFG:") == FALSE) return; while (g_at_result_iter_open_list(&iter)) { if (g_at_result_iter_next_number(&iter, &type) == FALSE) break; if (g_at_result_iter_next_string(&iter, &str) == FALSE) break; switch (type) { case 1: ip = str; break; case 2: gateway = str; break; case 3: if (numdns < MAX_DNS) dns[numdns++] = str; break; default: break; } if (g_at_result_iter_close_list(&iter) == FALSE) break; } dns[numdns] = NULL; if (ip && gateway && numdns) success = TRUE; out: modem = ofono_gprs_context_get_modem(gc); interface = ofono_modem_get_string(modem, "NetworkInterface"); ofono_info("IP: %s Gateway: %s", ip, gateway); ofono_info("DNS: %s, %s", dns[0], dns[1]); ofono_gprs_context_set_interface(gc, interface); if (success) { ofono_gprs_context_set_ipv4_address(gc, ip, TRUE); ofono_gprs_context_set_ipv4_netmask(gc, STATIC_IP_NETMASK); ofono_gprs_context_set_ipv4_dns_servers(gc, dns); ofono_gprs_context_set_ipv4_gateway(gc, gateway); } else ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE); CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); gcd->mbm_state = MBM_NONE; gcd->cb = NULL; gcd->cb_data = NULL; } static void mbm_get_ip_details(struct ofono_gprs_context *gc) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_modem *modem; const char *interface; char buf[64]; DBG(""); if (gcd->have_e2ipcfg) { g_at_chat_send(gcd->chat, "AT*E2IPCFG?", e2ipcfg_prefix, mbm_e2ipcfg_cb, gc, NULL); return; } snprintf(buf, sizeof(buf), "AT+CGPADDR=%u", gcd->active_context); g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL); modem = ofono_gprs_context_get_modem(gc); interface = ofono_modem_get_string(modem, "NetworkInterface"); ofono_gprs_context_set_interface(gc, interface); ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE); CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); gcd->mbm_state = MBM_NONE; gcd->cb = NULL; gcd->cb_data = NULL; } static void mbm_state_changed(struct ofono_gprs_context *gc, int state) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); DBG("state %d", state); if (gcd->active_context == 0) return; switch (state) { case MBM_E2NAP_DISCONNECTED: DBG("disconnected"); if (gcd->mbm_state == MBM_DISABLING) { CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); gcd->cb = NULL; } else if (gcd->mbm_state == MBM_ENABLING) { CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data); gcd->cb = NULL; } else { ofono_gprs_context_deactivated(gc, gcd->active_context); } gcd->mbm_state = MBM_NONE; gcd->cb_data = NULL; gcd->active_context = 0; break; case MBM_E2NAP_CONNECTED: DBG("connected"); if (gcd->mbm_state == MBM_ENABLING) mbm_get_ip_details(gc); break; case MBM_E2NAP_CONNECTING: DBG("connecting"); break; default: break; }; gcd->enap = state; } static void mbm_enap_poll_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; int state; DBG("ok %d", ok); g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "*ENAP:") == FALSE) return; g_at_result_iter_next_number(&iter, &state); mbm_state_changed(gc, state); if ((state == MBM_E2NAP_CONNECTED && gcd->mbm_state == MBM_DISABLING) || state == MBM_E2NAP_CONNECTING) gcd->enap_source = g_timeout_add_seconds(1, mbm_enap_poll, gc); } static gboolean mbm_enap_poll(gpointer user_data) { struct ofono_gprs_context *gc = user_data; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); g_at_chat_send(gcd->chat, "AT*ENAP?", enap_prefix, mbm_enap_poll_cb, gc, NULL); gcd->enap_source = 0; return FALSE; } static void at_enap_down_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; DBG("ok %d", ok); /* Now we have to wait for the unsolicited notification to arrive */ if (ok && gcd->enap != 0) { gcd->mbm_state = MBM_DISABLING; gcd->cb = cb; gcd->cb_data = cbd->data; if (gcd->have_e2nap == FALSE) g_at_chat_send(gcd->chat, "AT*ENAP?", enap_prefix, mbm_enap_poll_cb, gc, NULL); return; } decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void mbm_enap_up_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; DBG("ok %d", ok); if (ok) { gcd->mbm_state = MBM_ENABLING; gcd->cb = cb; gcd->cb_data = cbd->data; if (gcd->have_e2nap == FALSE) g_at_chat_send(gcd->chat, "AT*ENAP?", enap_prefix, mbm_enap_poll_cb, gc, NULL); return; } gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void mbm_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *ncbd; char buf[64]; DBG("ok %d", ok); if (!ok) { struct ofono_error error; gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); return; } ncbd = g_memdup(cbd, sizeof(struct cb_data)); snprintf(buf, sizeof(buf), "AT*ENAP=1,%u", gcd->active_context); if (g_at_chat_send(gcd->chat, buf, none_prefix, mbm_enap_up_cb, ncbd, g_free) > 0) return; g_free(ncbd); gcd->active_context = 0; CALLBACK_WITH_FAILURE(cb, cbd->data); } static void mbm_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 cb_data *cbd = cb_data_new(cb, data); char buf[AUTH_BUF_LENGTH]; int len; /* IPv6 support not implemented */ if (ctx->proto != OFONO_GPRS_PROTO_IP) goto error; DBG("cid %u", ctx->cid); gcd->active_context = ctx->cid; cbd->user = gc; len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); if (ctx->apn) snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, mbm_cgdcont_cb, cbd, g_free) == 0) goto error; /* * Set username and password, this should be done after CGDCONT * or an error can occur. We don't bother with error checking * here * */ snprintf(buf, sizeof(buf), "AT*EIAAUW=%d,1,\"%s\",\"%s\"", ctx->cid, ctx->username, ctx->password); g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL); return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void mbm_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 cb_data *cbd = cb_data_new(cb, data); DBG("cid %u", cid); cbd->user = gc; if (g_at_chat_send(gcd->chat, "AT*ENAP=0", none_prefix, at_enap_down_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void e2nap_notifier(GAtResult *result, gpointer user_data) { struct ofono_gprs_context *gc = user_data; GAtResultIter iter; int state; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "*E2NAP:") == FALSE) return; g_at_result_iter_next_number(&iter, &state); mbm_state_changed(gc, state); } static void mbm_e2nap_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); DBG("ok %d", ok); gcd->have_e2nap = ok; if (ok) g_at_chat_register(gcd->chat, "*E2NAP:", e2nap_notifier, FALSE, gc, NULL); } static void mbm_e2ipcfg_query_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); gcd->have_e2ipcfg = ok; } static int mbm_gprs_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *data) { GAtChat *chat = data; struct gprs_context_data *gcd; DBG(""); gcd = g_try_new0(struct gprs_context_data, 1); if (gcd == NULL) return -ENOMEM; gcd->chat = g_at_chat_clone(chat); ofono_gprs_context_set_data(gc, gcd); g_at_chat_send(gcd->chat, "AT*ENAPDBG=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(gcd->chat, "AT*E2NAP=1", none_prefix, mbm_e2nap_cb, gc, NULL); g_at_chat_send(gcd->chat, "AT*E2IPCFG=?", e2ipcfg_prefix, mbm_e2ipcfg_query_cb, gc, NULL); return 0; } static void mbm_gprs_context_remove(struct ofono_gprs_context *gc) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); DBG(""); if (gcd->enap_source) { g_source_remove(gcd->enap_source); gcd->enap_source = 0; } ofono_gprs_context_set_data(gc, NULL); g_at_chat_unref(gcd->chat); g_free(gcd); } static struct ofono_gprs_context_driver driver = { .name = "mbmmodem", .probe = mbm_gprs_context_probe, .remove = mbm_gprs_context_remove, .activate_primary = mbm_gprs_activate_primary, .deactivate_primary = mbm_gprs_deactivate_primary, }; void mbm_gprs_context_init(void) { ofono_gprs_context_driver_register(&driver); } void mbm_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/0000755000015600001650000000000012671500304021673 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtkunsol.c0000644000015600001650000001051112671500024023710 0ustar pbuserpbgroup00000000000000/* * * MTK driver for ofono/rilmodem * * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #include "mtkunsol.h" #include "util.h" #include "common.h" void g_mtk_unsol_free_call_indication(struct unsol_call_indication *unsol) { g_free(unsol); } struct unsol_call_indication *g_mtk_unsol_parse_incoming_call_indication( GRil *gril, struct ril_msg *message) { struct parcel rilp; int numstr; struct unsol_call_indication *unsol; char *call_id, *phone, *mystery, *call_mode, *seq_num; char *endp; unsol = g_try_malloc0(sizeof(*unsol)); if (unsol == NULL) { ofono_error("%s: out of memory", __func__); goto error; } g_ril_init_parcel(message, &rilp); numstr = parcel_r_int32(&rilp); if (numstr != 5) { ofono_error("%s: wrong array size (%d)", __func__, numstr); goto error; } call_id = parcel_r_string(&rilp); phone = parcel_r_string(&rilp); mystery = parcel_r_string(&rilp); call_mode = parcel_r_string(&rilp); seq_num = parcel_r_string(&rilp); g_ril_append_print_buf(gril, "{%s,%s,%s,%s,%s}", call_id, phone, mystery, call_mode, seq_num); g_ril_print_unsol(gril, message); if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); goto err_conv; } if (call_id != NULL && *call_id != '\0') { unsol->call_id = (int) strtol(call_id, &endp, 10); if (*endp != '\0') { ofono_error("%s: cannot parse call id", __func__); goto err_conv; } } else { ofono_error("%s: no call id", __func__); goto err_conv; } if (call_mode != NULL && *call_mode != '\0') { unsol->call_mode = (int) strtol(call_mode, &endp, 10); if (*endp != '\0') { ofono_error("%s: cannot parse call mode", __func__); goto err_conv; } } else { ofono_error("%s: no call mode", __func__); goto err_conv; } if (seq_num != NULL && *seq_num != '\0') { unsol->seq_number = (int) strtol(seq_num, &endp, 10); if (*endp != '\0') { ofono_error("%s: cannot parse seq num", __func__); goto err_conv; } } else { ofono_error("%s: no seq num", __func__); goto err_conv; } g_free(call_id); g_free(phone); g_free(mystery); g_free(call_mode); g_free(seq_num); return unsol; err_conv: g_free(call_id); g_free(phone); g_free(mystery); g_free(call_mode); g_free(seq_num); error: g_free(unsol); return NULL; } int g_mtk_unsol_parse_registration_suspended(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int numint, session_id; g_ril_init_parcel(message, &rilp); numint = parcel_r_int32(&rilp); if (numint != 1) { ofono_error("%s Wrong format", __func__); goto error; } session_id = parcel_r_int32(&rilp); if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); goto error; } g_ril_append_print_buf(gril, "{%d}", session_id); g_ril_print_unsol(gril, message); return session_id; error: return -1; } struct parcel_str_array *g_mtk_unsol_parse_plmn_changed(GRil *gril, const struct ril_msg *message) { struct parcel rilp; struct parcel_str_array *str_arr; int i; g_ril_init_parcel(message, &rilp); str_arr = parcel_r_str_array(&rilp); if (str_arr == NULL || str_arr->num_str == 0) { ofono_error("%s: parse error for %s", __func__, ril_request_id_to_string(message->req)); parcel_free_str_array(str_arr); str_arr = NULL; goto out; } g_ril_append_print_buf(gril, "{"); for (i = 0; i < str_arr->num_str; ++i) { if (i + 1 == str_arr->num_str) g_ril_append_print_buf(gril, "%s%s}", print_buf, str_arr->str[i]); else g_ril_append_print_buf(gril, "%s%s, ", print_buf, str_arr->str[i]); } g_ril_print_unsol(gril, message); out: return str_arr; } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtkrequest.c0000644000015600001650000000746112671500024024252 0ustar pbuserpbgroup00000000000000/* * * MTK driver for ofono/rilmodem * * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include "mtkrequest.h" #include "simutil.h" #include "util.h" #include "common.h" void g_mtk_request_dual_sim_mode_switch(GRil *gril, int mode, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); parcel_w_int32(rilp, mode); g_ril_append_print_buf(gril, "(%d)", mode); } void g_mtk_request_set_gprs_connect_type(GRil *gril, int always, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); parcel_w_int32(rilp, always); g_ril_append_print_buf(gril, "(%d)", always); }; void g_mtk_request_set_gprs_transfer_type(GRil *gril, int callprefer, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); parcel_w_int32(rilp, callprefer); g_ril_append_print_buf(gril, "(%d)", callprefer); } void g_mtk_request_set_call_indication(GRil *gril, int mode, int call_id, int seq_number, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 3); /* What the parameters set is unknown */ parcel_w_int32(rilp, mode); parcel_w_int32(rilp, call_id); parcel_w_int32(rilp, seq_number); g_ril_append_print_buf(gril, "(%d,%d,%d)", mode, call_id, seq_number); } void g_mtk_request_set_fd_mode(GRil *gril, int mode, int param1, int param2, struct parcel *rilp) { int num_args; switch (mode) { case MTK_FD_MODE_DISABLE: case MTK_FD_MODE_ENABLE: num_args = 1; break; case MTK_FD_MODE_SET_TIMER: num_args = 3; break; case MTK_FD_MODE_SCREEN_STATUS: num_args = 2; break; default: ofono_error("%s: mode %d is wrong", __func__, mode); return; } parcel_init(rilp); parcel_w_int32(rilp, num_args); parcel_w_int32(rilp, mode); g_ril_append_print_buf(gril, "(%d,%d", num_args, mode); if (mode == MTK_FD_MODE_SCREEN_STATUS) { parcel_w_int32(rilp, param1); g_ril_append_print_buf(gril, "%s,%d)", print_buf, param1); } else if (mode == MTK_FD_MODE_SET_TIMER) { parcel_w_int32(rilp, param1); parcel_w_int32(rilp, param2); g_ril_append_print_buf(gril, "%s,%d,%d)", print_buf, param1, param2); } else { g_ril_append_print_buf(gril, "%s)", print_buf); } } void g_mtk_request_set_3g_capability(GRil *gril, struct parcel *rilp) { int mode = g_ril_get_slot(gril) + 1; parcel_init(rilp); parcel_w_int32(rilp, 1); parcel_w_int32(rilp, mode); g_ril_append_print_buf(gril, "(%d)", mode); } void g_mtk_request_store_modem_type(GRil *gril, int mode, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); parcel_w_int32(rilp, mode); g_ril_append_print_buf(gril, "(%d)", mode); } void g_mtk_request_set_trm(GRil *gril, int trm, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); parcel_w_int32(rilp, trm); g_ril_append_print_buf(gril, "(%d)", trm); } void g_mtk_request_resume_registration(GRil *gril, int session_id, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); parcel_w_int32(rilp, session_id); g_ril_append_print_buf(gril, "(%d)", session_id); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/voicecall.c0000644000015600001650000001052312671500024024000 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2014 Canonical Ltd. * Copyright (C) 2013 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "mtk_constants.h" #include "mtkunsol.h" #include "mtkrequest.h" #include "common.h" #include "mtkmodem.h" #include "drivers/rilmodem/rilutil.h" #include "drivers/rilmodem/voicecall.h" /* * This is the voicecall atom implementation for mtkmodem. Most of the * implementation can be found in the rilmodem atom. The main reason for * creating a new atom is the need to handle specific MTK requests and * unsolicited events. */ static void mtk_set_indication_cb(struct ril_msg *message, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); if (message->error == RIL_E_SUCCESS) g_ril_print_response_no_args(vd->ril, message); else ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); } static void mtk_incoming_notify(struct ril_msg *message, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); struct parcel rilp; struct unsol_call_indication *call_ind; call_ind = g_mtk_unsol_parse_incoming_call_indication(vd->ril, message); if (call_ind == NULL) { ofono_error("%s: error parsing event", __func__); return; } g_mtk_request_set_call_indication(vd->ril, MTK_CALL_INDIC_MODE_AVAIL, call_ind->call_id, call_ind->seq_number, &rilp); if (g_ril_send(vd->ril, MTK_RIL_REQUEST_SET_CALL_INDICATION, &rilp, mtk_set_indication_cb, vc, NULL) == 0) ofono_error("%s: cannot send indication", __func__); g_mtk_unsol_free_call_indication(call_ind); } static gboolean mtk_delayed_register(gpointer user_data) { struct ofono_voicecall *vc = user_data; struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc); /* MTK generates this event instead of CALL_STATE_CHANGED */ g_ril_register(vd->ril, MTK_RIL_UNSOL_CALL_PROGRESS_INFO, ril_call_state_notify, vc); /* Indicates incoming call, before telling the network our state */ g_ril_register(vd->ril, MTK_RIL_UNSOL_INCOMING_CALL_INDICATION, mtk_incoming_notify, vc); /* This makes the timeout a single-shot */ return FALSE; } static int mtk_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, void *data) { struct ril_voicecall_driver_data *driver_data = data; struct ril_voicecall_data *vd; vd = g_try_new0(struct ril_voicecall_data, 1); if (vd == NULL) return -ENOMEM; ril_voicecall_start(driver_data, vc, vendor, vd); /* * Register events after ofono_voicecall_register() is called from * ril_delayed_register(). */ g_idle_add(mtk_delayed_register, vc); return 0; } static struct ofono_voicecall_driver driver = { .name = MTKMODEM, .probe = mtk_voicecall_probe, .remove = ril_voicecall_remove, .dial = ril_dial, .answer = ril_answer, .hangup_all = ril_hangup_all, .release_specific = ril_hangup_specific, .send_tones = ril_send_dtmf, .create_multiparty = ril_create_multiparty, .private_chat = ril_private_chat, .swap_without_accept = ril_swap_without_accept, .hold_all_active = ril_hold_all_active, .release_all_held = ril_release_all_held, .set_udub = ril_set_udub, .release_all_active = ril_release_all_active, }; void mtk_voicecall_init(void) { ofono_voicecall_driver_register(&driver); } void mtk_voicecall_exit(void) { ofono_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtkrequest.h0000644000015600001650000001006512671500024024251 0ustar pbuserpbgroup00000000000000/* * * MTK driver for ofono/rilmodem * * Copyright (C) 2014 Canonical 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. * * 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 * */ #ifndef MTKREQUEST_H #define MTKREQUEST_H #include #include #include "gril.h" #ifdef __cplusplus extern "C" { #endif #define MTK_SWITCH_MODE_SIM_1_ACTIVE 1 #define MTK_SWITCH_MODE_SIM_2_ACTIVE 2 #define MTK_SWITCH_MODE_ALL_INACTIVE -1 #define MTK_GPRS_CONNECT_WHEN_NEEDED 0 #define MTK_GPRS_CONNECT_ALWAYS 1 #define MTK_GPRS_TRANSFER_DATA_PREFER 0 #define MTK_GPRS_TRANSFER_CALL_PREFER 1 #define MTK_CALL_INDIC_MODE_AVAIL 0 #define MTK_CALL_INDIC_MODE_NOT_AVAIL 1 #define MTK_FD_MODE_DISABLE 0 #define MTK_FD_MODE_ENABLE 1 #define MTK_FD_MODE_SET_TIMER 2 #define MTK_FD_MODE_SCREEN_STATUS 3 #define MTK_FD_PAR1_SCREEN_OFF 0 #define MTK_FD_PAR1_SCREEN_ON 1 #define MTK_MD_TYPE_INVALID 0 #define MTK_MD_TYPE_2G 1 #define MTK_MD_TYPE_3G 2 #define MTK_MD_TYPE_WG 3 #define MTK_MD_TYPE_TG 4 /* FDD CSFB modem: LTE FDD - WCDMA - GSM */ #define MTK_MD_TYPE_LWG 5 /* TDD CSFB modem: LTE TDD - TD-SCDMA - GSM */ #define MTK_MD_TYPE_LTG 6 /* SGLTE modem */ #define MTK_MD_TYPE_LTNG 7 /* * The meaning of mode seems to be: * -1 -> Both SIMs inactive * 1 -> SIM 1 active * 2 -> SIM 2 active * 3 -> Both SIMs active */ void g_mtk_request_dual_sim_mode_switch(GRil *gril, int mode, struct parcel *rilp); /* 0:WHEN_NEEDED , 1: ALWAYS */ void g_mtk_request_set_gprs_connect_type(GRil *gril, int always, struct parcel *rilp); /* 0:data prefer , 1: call prefer */ void g_mtk_request_set_gprs_transfer_type(GRil *gril, int callprefer, struct parcel *rilp); /* * mode. MTK_CALL_INDIC_MODE_AVAIL: indicates that the specified call can be * answered. * MTK_CALL_INDIC_MODE_NOT_AVAIL: indicates we are busy, and that the * specified call cannot be handled * "mode" seems useful for full dual SIM. An example would be that we have an * active call and an incoming call from the other SIM is signalled. * * call_id and seq_number are used to identify a specific call in the modem and * are taken from a previous RIL_UNSOL_INCOMING_CALL_INDICATION. */ void g_mtk_request_set_call_indication(GRil *gril, int mode, int call_id, int seq_number, struct parcel *rilp); /* * mode . MTK_FD_MODE_DISABLE: Disable fast dormancy * MTK_FD_MODE_ENABLE: Enable fast formancy * MTK_FD_MODE_SET_TIMER: Set fast dormancy timer * MTK_FD_MODE_SCREEN_STATUS: Inform modem of screen status, to select * timers to use for FD. * param1 Id of timer to set or screen status * param2 Value of timer, in units of 0.1 seconds * * Setting timers and enabling FD is done by rild initialization, so we do not * need to do it again in ofono. */ void g_mtk_request_set_fd_mode(GRil *gril, int mode, int param1, int param2, struct parcel *rilp); /* * Sets the slot to which the GRil object is connected as the one with 3G * capabilities. This makes the other slot just 2G. */ void g_mtk_request_set_3g_capability(GRil *gril, struct parcel *rilp); /* * Set the MTK modem mode. See MTK_MD_TYPE_*. */ void g_mtk_request_store_modem_type(GRil *gril, int mode, struct parcel *rilp); /* * Set TRM */ void g_mtk_request_set_trm(GRil *gril, int trm, struct parcel *rilp); /* * Request to resume registration. */ void g_mtk_request_resume_registration(GRil *gril, int session_id, struct parcel *rilp); #ifdef __cplusplus } #endif #endif /* MTKREQUEST_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtkutil.h0000644000015600001650000000235212671500024023536 0ustar pbuserpbgroup00000000000000/* * * MTK driver for ofono/rilmodem * * Copyright (C) 2014 Canonical 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. * * 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 * */ #ifndef MTKUTIL_H #define MTKUTIL_H #include #include #ifdef __cplusplus extern "C" { #endif struct ofono_modem; void mtk_set_attach_state(struct ofono_modem *modem, ofono_bool_t attached); void mtk_detach_received(struct ofono_modem *modem); void mtk_reset_all_modems(void); void mtk_reset_modem(struct ofono_modem *modem); const char *mtk_request_id_to_string(int req); const char *mtk_unsol_request_to_string(int req); #ifdef __cplusplus } #endif #endif /* MTKUTIL_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtksettings.c0000644000015600001650000001440612671500024024417 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "drivers/mtkmodem/mtk_constants.h" #include "drivers/mtkmodem/mtkrequest.h" #include "drivers/mtkmodem/mtkutil.h" #include "mtksettings.h" #define MTK_SETTINGS_INTERFACE "org.ofono.MtkSettings" struct mtk_settings_data { struct ofono_modem *modem; GRil *ril; ofono_bool_t has_3g; ofono_bool_t has_3g_pending; DBusMessage *pending; }; static void set_3g(struct mtk_settings_data *msd, ofono_bool_t has_3g) { DBusConnection *conn = ofono_dbus_get_connection(); const char *path = ofono_modem_get_path(msd->modem); dbus_bool_t value = has_3g; if (msd->has_3g == has_3g) return; ofono_dbus_signal_property_changed(conn, path, MTK_SETTINGS_INTERFACE, "Has3G", DBUS_TYPE_BOOLEAN, &value); msd->has_3g = has_3g; } static void set_3g_cb(struct ril_msg *message, gpointer user_data) { struct mtk_settings_data *msd = user_data; DBusMessage *reply; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: Error setting 3G", __func__); msd->has_3g_pending = msd->has_3g; reply = __ofono_error_failed(msd->pending); __ofono_dbus_pending_reply(&msd->pending, reply); return; } g_ril_print_response_no_args(msd->ril, message); reply = dbus_message_new_method_return(msd->pending); __ofono_dbus_pending_reply(&msd->pending, reply); set_3g(msd, msd->has_3g_pending); mtk_reset_all_modems(); } static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct mtk_settings_data *msd = data; DBusMessageIter iter; DBusMessageIter var; const char *property; if (msd->pending) return __ofono_error_busy(msg); if (!dbus_message_iter_init(msg, &iter)) return __ofono_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __ofono_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (g_strcmp0(property, "Has3G") == 0) { dbus_bool_t value; struct parcel rilp; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return __ofono_error_invalid_args(msg); dbus_message_iter_get_basic(&var, &value); if (msd->has_3g_pending == (ofono_bool_t) value) return dbus_message_new_method_return(msg); /* * We can only set to true, as setting to false could be * confusing in a multi-sim environment (>2 SIM) */ if (value == FALSE) return __ofono_error_invalid_args(msg); g_mtk_request_set_3g_capability(msd->ril, &rilp); if (g_ril_send(msd->ril, MTK_RIL_REQUEST_SET_3G_CAPABILITY, &rilp, set_3g_cb, msd, NULL) == 0) { ofono_error("%s: unable to set 3G for slot", __func__); return __ofono_error_failed(msg); } msd->pending = dbus_message_ref(msg); msd->has_3g_pending = value; return NULL; } return __ofono_error_invalid_args(msg); } static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct mtk_settings_data *msd = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); ofono_dbus_dict_append(&dict, "Has3G", DBUS_TYPE_BOOLEAN, &msd->has_3g); dbus_message_iter_close_container(&iter, &dict); return reply; } static const GDBusMethodTable mtk_settings_methods[] = { { GDBUS_ASYNC_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, set_property) }, { } }; static const GDBusSignalTable mtk_settings_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static void register_interface(struct mtk_settings_data *msd) { DBusConnection *conn = ofono_dbus_get_connection(); if (!g_dbus_register_interface(conn, ofono_modem_get_path(msd->modem), MTK_SETTINGS_INTERFACE, mtk_settings_methods, mtk_settings_signals, NULL, msd, NULL)) { ofono_error("Could not create %s interface", MTK_SETTINGS_INTERFACE); return; } ofono_modem_add_interface(msd->modem, MTK_SETTINGS_INTERFACE); } static void unregister_interface(struct mtk_settings_data *msd) { DBusConnection *conn = ofono_dbus_get_connection(); ofono_modem_remove_interface(msd->modem, MTK_SETTINGS_INTERFACE); g_dbus_unregister_interface(conn, ofono_modem_get_path(msd->modem), MTK_SETTINGS_INTERFACE); } struct mtk_settings_data *mtk_settings_create(struct ofono_modem *modem, GRil *ril, ofono_bool_t has_3g) { struct mtk_settings_data *msd = g_try_malloc0(sizeof(*msd)); DBG(""); if (msd == NULL) { ofono_error("%s: Cannot allocate mtk_settings_data", __func__); return NULL; } msd->modem = modem; msd->ril = g_ril_clone(ril); msd->has_3g = has_3g; msd->has_3g_pending = has_3g; register_interface(msd); return msd; } void mtk_settings_remove(struct mtk_settings_data *msd) { DBG(""); if (msd == NULL) return; unregister_interface(msd); g_ril_unref(msd->ril); g_free(msd); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtkutil.c0000644000015600001650000001220712671500024023531 0ustar pbuserpbgroup00000000000000/* * * MTK driver for ofono/rilmodem * * Copyright (C) 2014 Canonical 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. * * 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 #include "mtkutil.h" #include "mtk_constants.h" const char *mtk_request_id_to_string(int req) { switch (req) { case MTK_RIL_REQUEST_RADIO_POWEROFF: return "MTK_RIL_REQUEST_RADIO_POWEROFF"; case MTK_RIL_REQUEST_DUAL_SIM_MODE_SWITCH: return "MTK_RIL_REQUEST_DUAL_SIM_MODE_SWITCH"; case MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE: return "MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE"; case MTK_RIL_REQUEST_SET_GPRS_TRANSFER_TYPE: return "MTK_RIL_REQUEST_SET_GPRS_TRANSFER_TYPE"; case MTK_RIL_REQUEST_RADIO_POWERON: return "MTK_RIL_REQUEST_RADIO_POWERON"; case MTK_RIL_REQUEST_SET_CALL_INDICATION: return "MTK_RIL_REQUEST_SET_CALL_INDICATION"; case MTK_RIL_REQUEST_GET_3G_CAPABILITY: return "MTK_RIL_REQUEST_GET_3G_CAPABILITY"; case MTK_RIL_REQUEST_SET_3G_CAPABILITY: return "MTK_RIL_REQUEST_SET_3G_CAPABILITY"; case MTK_RIL_REQUEST_SET_TRM: return "MTK_RIL_REQUEST_SET_TRM"; case MTK_RIL_REQUEST_SET_FD_MODE: return "MTK_RIL_REQUEST_SET_FD_MODE"; case MTK_RIL_REQUEST_STORE_MODEM_TYPE: return "MTK_RIL_REQUEST_STORE_MODEM_TYPE"; case MTK_RIL_REQUEST_RESUME_REGISTRATION: return "MTK_RIL_REQUEST_RESUME_REGISTRATION"; case MTK_RIL_REQUEST_QUERY_MODEM_TYPE: return "MTK_RIL_REQUEST_QUERY_MODEM_TYPE"; default: return NULL; } } const char *mtk_unsol_request_to_string(int req) { switch (req) { case MTK_RIL_UNSOL_NEIGHBORING_CELL_INFO: return "MTK_RIL_UNSOL_NEIGHBORING_CELL_INFO"; case MTK_RIL_UNSOL_NETWORK_INFO: return "MTK_RIL_UNSOL_NETWORK_INFO"; case MTK_RIL_UNSOL_CALL_FORWARDING: return "MTK_RIL_UNSOL_CALL_FORWARDING"; case MTK_RIL_UNSOL_CRSS_NOTIFICATION: return "MTK_RIL_UNSOL_CRSS_NOTIFICATION"; case MTK_RIL_UNSOL_CALL_PROGRESS_INFO: return "MTK_RIL_UNSOL_CALL_PROGRESS_INFO"; case MTK_RIL_UNSOL_PHB_READY_NOTIFICATION: return "MTK_RIL_UNSOL_PHB_READY_NOTIFICATION"; case MTK_RIL_UNSOL_SPEECH_INFO: return "MTK_RIL_UNSOL_SPEECH_INFO"; case MTK_RIL_UNSOL_SIM_INSERTED_STATUS: return "MTK_RIL_UNSOL_SIM_INSERTED_STATUS"; case MTK_RIL_UNSOL_RADIO_TEMPORARILY_UNAVAILABLE: return "MTK_RIL_UNSOL_RADIO_TEMPORARILY_UNAVAILABLE"; case MTK_RIL_UNSOL_ME_SMS_STORAGE_FULL: return "MTK_RIL_UNSOL_ME_SMS_STORAGE_FULL"; case MTK_RIL_UNSOL_SMS_READY_NOTIFICATION: return "MTK_RIL_UNSOL_SMS_READY_NOTIFICATION"; case MTK_RIL_UNSOL_SCRI_RESULT: return "MTK_RIL_UNSOL_SCRI_RESULT"; case MTK_RIL_UNSOL_VT_STATUS_INFO: return "MTK_RIL_UNSOL_VT_STATUS_INFO"; case MTK_RIL_UNSOL_VT_RING_INFO: return "MTK_RIL_UNSOL_VT_RING_INFO"; case MTK_RIL_UNSOL_INCOMING_CALL_INDICATION: return "MTK_RIL_UNSOL_INCOMING_CALL_INDICATION"; case MTK_RIL_UNSOL_SIM_MISSING: return "MTK_RIL_UNSOL_SIM_MISSING"; case MTK_RIL_UNSOL_GPRS_DETACH: return "MTK_RIL_UNSOL_GPRS_DETACH"; case MTK_RIL_UNSOL_ATCI_RESPONSE: return "MTK_RIL_UNSOL_ATCI_RESPONSE"; case MTK_RIL_UNSOL_SIM_RECOVERY: return "MTK_RIL_UNSOL_SIM_RECOVERY"; case MTK_RIL_UNSOL_VIRTUAL_SIM_ON: return "MTK_RIL_UNSOL_VIRTUAL_SIM_ON"; case MTK_RIL_UNSOL_VIRTUAL_SIM_OFF: return "MTK_RIL_UNSOL_VIRTUAL_SIM_OFF"; case MTK_RIL_UNSOL_INVALID_SIM: return "MTK_RIL_UNSOL_INVALID_SIM"; case MTK_RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED: return "MTK_RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED"; case MTK_RIL_UNSOL_RESPONSE_ACMT: return "MTK_RIL_UNSOL_RESPONSE_ACMT"; case MTK_RIL_UNSOL_EF_CSP_PLMN_MODE_BIT: return "MTK_RIL_UNSOL_EF_CSP_PLMN_MODE_BIT"; case MTK_RIL_UNSOL_IMEI_LOCK: return "MTK_RIL_UNSOL_IMEI_LOCK"; case MTK_RIL_UNSOL_RESPONSE_MMRR_STATUS_CHANGED: return "MTK_RIL_UNSOL_RESPONSE_MMRR_STATUS_CHANGED"; case MTK_RIL_UNSOL_SIM_PLUG_OUT: return "MTK_RIL_UNSOL_SIM_PLUG_OUT"; case MTK_RIL_UNSOL_SIM_PLUG_IN: return "MTK_RIL_UNSOL_SIM_PLUG_IN"; case MTK_RIL_UNSOL_RESPONSE_ETWS_NOTIFICATION: return "MTK_RIL_UNSOL_RESPONSE_ETWS_NOTIFICATION"; case MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED: return "MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED"; case MTK_RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED: return "MTK_RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED"; case MTK_RIL_UNSOL_STK_EVDL_CALL: return "MTK_RIL_UNSOL_STK_EVDL_CALL"; case MTK_RIL_UNSOL_DATA_PACKETS_FLUSH: return "MTK_RIL_UNSOL_DATA_PACKETS_FLUSH"; case MTK_RIL_UNSOL_CIPHER_INDICATION: return "MTK_RIL_UNSOL_CIPHER_INDICATION"; case MTK_RIL_UNSOL_FEMTOCELL_INFO: return "MTK_RIL_UNSOL_FEMTOCELL_INFO"; case MTK_RIL_UNSOL_CNAP: return "MTK_RIL_UNSOL_CNAP"; case MTK_RIL_UNSOL_RAC_UPDATE: return "MTK_RIL_UNSOL_RAC_UPDATE"; default: return NULL; } } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtkunsol.h0000644000015600001650000000310512671500024023716 0ustar pbuserpbgroup00000000000000/* * * MTK driver for ofono/rilmodem * * Copyright (C) 2014 Canonical 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. * * 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 * */ #ifndef MTKUNSOL_H #define MTKUNSOL_H #include #include "gril.h" #ifdef __cplusplus extern "C" { #endif struct unsol_call_indication { int call_id; int call_mode; int seq_number; }; void g_mtk_unsol_free_call_indication(struct unsol_call_indication *unsol); struct unsol_call_indication *g_mtk_unsol_parse_incoming_call_indication( GRil *gril, struct ril_msg *message); /* Returns a session id that must be used in resume registration request */ int g_mtk_unsol_parse_registration_suspended(GRil *gril, const struct ril_msg *message); /* * Returns a minimum of one PLMN or NULL. parcel_free_str_array must be invoked * to free the returned pointer. */ struct parcel_str_array *g_mtk_unsol_parse_plmn_changed(GRil *gril, const struct ril_msg *message); #ifdef __cplusplus } #endif #endif /* MTKUNSOL_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/radio-settings.c0000644000015600001650000001252512671500024024777 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "mtk_constants.h" #include "mtkunsol.h" #include "mtkreply.h" #include "mtkrequest.h" #include "common.h" #include "mtkmodem.h" #include "drivers/rilmodem/radio-settings.h" #include "drivers/rilmodem/rilutil.h" /* * This is the radio settings atom implementation for mtkmodem. Most of the * implementation can be found in the rilmodem atom. The main reason for * creating a new atom is the need to handle specific MTK requests. */ struct set_fd_mode { struct ofono_radio_settings *rst; gboolean on; }; static void mtk_set_fd_mode_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct set_fd_mode *user = cbd->user; struct ofono_radio_settings *rs = user->rst; struct radio_data *rsd = ofono_radio_settings_get_data(rs); ofono_radio_settings_fast_dormancy_set_cb_t cb = cbd->cb; if (message->error == RIL_E_SUCCESS || message->error == RIL_E_RADIO_NOT_AVAILABLE) { if (message->error == RIL_E_RADIO_NOT_AVAILABLE) ofono_warn("Could not set fast dormancy " "as radio is not available"); g_ril_print_response_no_args(rsd->ril, message); ril_set_fast_dormancy(rs, user->on, cb, cbd->data); } else { CALLBACK_WITH_FAILURE(cb, cbd->data); } g_free(user); } static void mtk_set_fast_dormancy(struct ofono_radio_settings *rs, ofono_bool_t enable, ofono_radio_settings_fast_dormancy_set_cb_t cb, void *data) { struct radio_data *rsd = ofono_radio_settings_get_data(rs); struct set_fd_mode *user = g_malloc0(sizeof(*user)); struct cb_data *cbd; struct parcel rilp; user->rst = rs; user->on = enable; cbd = cb_data_new(cb, data, user); g_mtk_request_set_fd_mode(rsd->ril, MTK_FD_MODE_SCREEN_STATUS, enable ? FALSE : TRUE, 0, &rilp); if (g_ril_send(rsd->ril, MTK_RIL_REQUEST_SET_FD_MODE, &rilp, mtk_set_fd_mode_cb, cbd, g_free) <= 0) { g_free(cbd); g_free(user); CALLBACK_WITH_FAILURE(cb, data); } } static int mtk_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *user) { struct ril_radio_settings_driver_data *rs_init_data = user; struct radio_data *rsd = g_try_new0(struct radio_data, 1); if (rsd == NULL) { ofono_error("%s: cannot allocate memory", __func__); return -ENOMEM; } rsd->ril = g_ril_clone(rs_init_data->gril); rsd->modem = rs_init_data->modem; ofono_radio_settings_set_data(rs, rsd); mtk_set_fast_dormancy(rs, FALSE, ril_delayed_register, rs); return 0; } static void mtk_query_available_rats_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_radio_settings *rs = cbd->user; struct radio_data *rd = ofono_radio_settings_get_data(rs); ofono_radio_settings_available_rats_query_cb_t cb = cbd->cb; unsigned int available_rats = OFONO_RADIO_ACCESS_MODE_GSM; int slot_3g, is_3g; if (message->error != RIL_E_SUCCESS) { ofono_error("%s: error %s", __func__, ril_error_to_string(message->error)); CALLBACK_WITH_FAILURE(cb, 0, cbd->data); return; } slot_3g = g_mtk_reply_parse_get_3g_capability(rd->ril, message); if (slot_3g < 0) { ofono_error("%s: parse error", __func__); CALLBACK_WITH_FAILURE(cb, 0, cbd->data); return; } is_3g = (g_ril_get_slot(rd->ril) == slot_3g); if (is_3g) { available_rats |= OFONO_RADIO_ACCESS_MODE_UMTS; if (ofono_modem_get_boolean(rd->modem, MODEM_PROP_LTE_CAPABLE)) available_rats |= OFONO_RADIO_ACCESS_MODE_LTE; } CALLBACK_WITH_SUCCESS(cb, available_rats, cbd->data); } static void mtk_query_available_rats(struct ofono_radio_settings *rs, ofono_radio_settings_available_rats_query_cb_t cb, void *data) { struct radio_data *rd = ofono_radio_settings_get_data(rs); struct cb_data *cbd = cb_data_new(cb, data, rs); if (g_ril_send(rd->ril, MTK_RIL_REQUEST_GET_3G_CAPABILITY, NULL, mtk_query_available_rats_cb, cbd, g_free) <= 0) { g_free(cbd); CALLBACK_WITH_FAILURE(cb, 0, data); } } static struct ofono_radio_settings_driver driver = { .name = MTKMODEM, .probe = mtk_radio_settings_probe, .remove = ril_radio_settings_remove, .query_rat_mode = ril_query_rat_mode, .set_rat_mode = ril_set_rat_mode, .query_fast_dormancy = ril_query_fast_dormancy, .set_fast_dormancy = mtk_set_fast_dormancy, .query_available_rats = mtk_query_available_rats }; void mtk_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void mtk_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtk_constants.h0000644000015600001650000001012312671500024024727 0ustar pbuserpbgroup00000000000000/* * * RIL constants for MTK modem * * Copyright (C) 2014 Canonical 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. * * 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 * */ #ifndef MTK_CONSTANTS_H #define MTK_CONSTANTS_H /* RIL Request Messages */ #define MTK_RIL_REQUEST_RADIO_POWEROFF 2011 #define MTK_RIL_REQUEST_DUAL_SIM_MODE_SWITCH 2012 #define MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE 2016 #define MTK_RIL_REQUEST_SET_GPRS_TRANSFER_TYPE 2017 #define MTK_RIL_REQUEST_RADIO_POWERON 2033 #define MTK_RIL_REQUEST_SET_CALL_INDICATION 2036 #define MTK_RIL_REQUEST_GET_3G_CAPABILITY 2038 #define MTK_RIL_REQUEST_SET_3G_CAPABILITY 2039 #define MTK_RIL_REQUEST_SET_TRM 2053 #define MTK_RIL_REQUEST_SET_FD_MODE 2073 #define MTK_RIL_REQUEST_RESUME_REGISTRATION 2077 #define MTK_RIL_REQUEST_STORE_MODEM_TYPE 2078 #define MTK_RIL_REQUEST_QUERY_MODEM_TYPE 2079 /* RIL Unsolicited Messages */ #define MTK_RIL_UNSOL_MTK_BASE 3000 #define MTK_RIL_UNSOL_NEIGHBORING_CELL_INFO (MTK_RIL_UNSOL_MTK_BASE + 0) #define MTK_RIL_UNSOL_NETWORK_INFO (MTK_RIL_UNSOL_MTK_BASE + 1) #define MTK_RIL_UNSOL_CALL_FORWARDING (MTK_RIL_UNSOL_MTK_BASE + 2) #define MTK_RIL_UNSOL_CRSS_NOTIFICATION (MTK_RIL_UNSOL_MTK_BASE + 3) #define MTK_RIL_UNSOL_CALL_PROGRESS_INFO (MTK_RIL_UNSOL_MTK_BASE + 4) #define MTK_RIL_UNSOL_PHB_READY_NOTIFICATION (MTK_RIL_UNSOL_MTK_BASE + 5) #define MTK_RIL_UNSOL_SPEECH_INFO (MTK_RIL_UNSOL_MTK_BASE + 6) #define MTK_RIL_UNSOL_SIM_INSERTED_STATUS (MTK_RIL_UNSOL_MTK_BASE + 7) #define MTK_RIL_UNSOL_RADIO_TEMPORARILY_UNAVAILABLE (MTK_RIL_UNSOL_MTK_BASE + 8) #define MTK_RIL_UNSOL_ME_SMS_STORAGE_FULL (MTK_RIL_UNSOL_MTK_BASE + 9) #define MTK_RIL_UNSOL_SMS_READY_NOTIFICATION (MTK_RIL_UNSOL_MTK_BASE + 10) #define MTK_RIL_UNSOL_SCRI_RESULT (MTK_RIL_UNSOL_MTK_BASE + 11) #define MTK_RIL_UNSOL_VT_STATUS_INFO (MTK_RIL_UNSOL_MTK_BASE + 12) #define MTK_RIL_UNSOL_VT_RING_INFO (MTK_RIL_UNSOL_MTK_BASE + 13) #define MTK_RIL_UNSOL_INCOMING_CALL_INDICATION (MTK_RIL_UNSOL_MTK_BASE + 14) #define MTK_RIL_UNSOL_SIM_MISSING (MTK_RIL_UNSOL_MTK_BASE + 15) #define MTK_RIL_UNSOL_GPRS_DETACH (MTK_RIL_UNSOL_MTK_BASE + 16) #define MTK_RIL_UNSOL_ATCI_RESPONSE (MTK_RIL_UNSOL_MTK_BASE + 17) #define MTK_RIL_UNSOL_SIM_RECOVERY (MTK_RIL_UNSOL_MTK_BASE + 18) #define MTK_RIL_UNSOL_VIRTUAL_SIM_ON (MTK_RIL_UNSOL_MTK_BASE + 19) #define MTK_RIL_UNSOL_VIRTUAL_SIM_OFF (MTK_RIL_UNSOL_MTK_BASE + 20) #define MTK_RIL_UNSOL_INVALID_SIM (MTK_RIL_UNSOL_MTK_BASE + 21) #define MTK_RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED \ (MTK_RIL_UNSOL_MTK_BASE + 22) #define MTK_RIL_UNSOL_RESPONSE_ACMT (MTK_RIL_UNSOL_MTK_BASE + 23) #define MTK_RIL_UNSOL_EF_CSP_PLMN_MODE_BIT (MTK_RIL_UNSOL_MTK_BASE + 24) #define MTK_RIL_UNSOL_IMEI_LOCK (MTK_RIL_UNSOL_MTK_BASE + 25) #define MTK_RIL_UNSOL_RESPONSE_MMRR_STATUS_CHANGED (MTK_RIL_UNSOL_MTK_BASE + 26) #define MTK_RIL_UNSOL_SIM_PLUG_OUT (MTK_RIL_UNSOL_MTK_BASE + 27) #define MTK_RIL_UNSOL_SIM_PLUG_IN (MTK_RIL_UNSOL_MTK_BASE + 28) #define MTK_RIL_UNSOL_RESPONSE_ETWS_NOTIFICATION (MTK_RIL_UNSOL_MTK_BASE + 29) #define MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED (MTK_RIL_UNSOL_MTK_BASE + 30) #define MTK_RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED \ (MTK_RIL_UNSOL_MTK_BASE + 31) #define MTK_RIL_UNSOL_STK_EVDL_CALL (MTK_RIL_UNSOL_MTK_BASE + 32) #define MTK_RIL_UNSOL_DATA_PACKETS_FLUSH (MTK_RIL_UNSOL_MTK_BASE + 33) #define MTK_RIL_UNSOL_CIPHER_INDICATION (MTK_RIL_UNSOL_MTK_BASE + 34) #define MTK_RIL_UNSOL_FEMTOCELL_INFO (MTK_RIL_UNSOL_MTK_BASE + 35) #define MTK_RIL_UNSOL_CNAP (MTK_RIL_UNSOL_MTK_BASE + 36) #define MTK_RIL_UNSOL_RAC_UPDATE (MTK_RIL_UNSOL_MTK_BASE + 37) #endif /* MTK_CONSTANTS_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtkmodem.c0000644000015600001650000000265212671500024023660 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2014 Canonical, Ltd. 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include "mtkmodem.h" static int mtkmodem_init(void) { DBG(""); mtk_voicecall_init(); mtk_gprs_init(); mtk_radio_settings_init(); return 0; } static void mtkmodem_exit(void) { DBG(""); mtk_voicecall_exit(); mtk_gprs_exit(); mtk_radio_settings_exit(); } OFONO_PLUGIN_DEFINE(mtkmodem, "MTK modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, mtkmodem_init, mtkmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtksettings.h0000644000015600001650000000215712671500024024424 0ustar pbuserpbgroup00000000000000/* * * MTK driver for ofono/rilmodem * * Copyright (C) 2014 Canonical 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. * * 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 * */ #ifndef MTKSETTINGS_H #define MTKSETTINGS_H #include #include "gril.h" #ifdef __cplusplus extern "C" { #endif struct mtk_settings_data; struct mtk_settings_data *mtk_settings_create(struct ofono_modem *modem, GRil *ril, ofono_bool_t has_3g); void mtk_settings_remove(struct mtk_settings_data *msd); #ifdef __cplusplus } #endif #endif /* MTKSETTINGS_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtkreply.h0000644000015600001650000000212412671500024023711 0ustar pbuserpbgroup00000000000000/* * * MTK driver for ofono/rilmodem * * Copyright (C) 2014 Canonical 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. * * 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 * */ #ifndef MTKREPLY_H #define MTKREPLY_H #include #include "gril.h" #ifdef __cplusplus extern "C" { #endif int g_mtk_reply_parse_get_3g_capability(GRil *gril, const struct ril_msg *message); int g_mtk_reply_parse_query_modem_type(GRil *gril, const struct ril_msg *message); #ifdef __cplusplus } #endif #endif /* MTKREPLY_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/gprs.c0000644000015600001650000001151012671500024023007 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * Copyright (C) 2013-2014 Canonical Ltd. * Copyright (C) 2013 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "common.h" #include "mtkutil.h" #include "mtkmodem.h" #include "mtk_constants.h" #include "mtkrequest.h" #include "drivers/rilmodem/rilutil.h" #include "drivers/rilmodem/gprs.h" /* Time between get data status retries */ #define GET_STATUS_TIMER_MS 5000 /* * This module is the ofono_gprs_driver implementation for mtkmodem. Most of the * implementation can be found in the rilmodem gprs atom. The main reason for * creating a new atom is the need to handle specific MTK requests that are * needed to set-up the data call. * * Notes: * * 1. ofono_gprs_suspend/resume() are not used by this module, as * the concept of suspended GPRS is not exposed by RILD. */ struct gprs_attach_data { struct ril_gprs_data *gd; gboolean set_attached; }; static void mtk_gprs_set_connect_type_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_cb_t cb = cbd->cb; struct gprs_attach_data *attach_data = cbd->user; struct ril_gprs_data *gd = attach_data->gd; if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(gd->ril, message); gd->ofono_attached = attach_data->set_attached; mtk_set_attach_state(gd->modem, gd->ofono_attached); CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { ofono_error("%s: RIL error %s", __func__, ril_error_to_string(message->error)); CALLBACK_WITH_FAILURE(cb, cbd->data); } g_free(attach_data); } static void mtk_gprs_set_attached(struct ofono_gprs *gprs, int attached, ofono_gprs_cb_t cb, void *data) { struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); struct cb_data *cbd; struct parcel rilp; struct gprs_attach_data *attach_data = g_try_new0(struct gprs_attach_data, 1); if (attach_data == NULL) { ofono_error("%s: out of memory", __func__); return; } DBG("attached: %d", attached); attach_data->gd = gd; attach_data->set_attached = attached; cbd = cb_data_new(cb, data, attach_data); /* MTK controls attachment with this request, as opposed to rilmodem */ g_mtk_request_set_gprs_connect_type(gd->ril, attached, &rilp); if (g_ril_send(gd->ril, MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE, &rilp, mtk_gprs_set_connect_type_cb, cbd, g_free) == 0) { ofono_error("%s: send failed", __func__); g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } } static void detach_event(struct ril_msg *message, gpointer user_data) { struct ofono_gprs *gprs = user_data; struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); g_ril_print_unsol_no_args(gd->ril, message); mtk_detach_received(gd->modem); } static int mtk_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor, void *data) { struct ril_gprs_driver_data *driver_data = data; struct ril_gprs_data *gd; gd = g_try_new0(struct ril_gprs_data, 1); if (gd == NULL) return -ENOMEM; ril_gprs_start(driver_data, gprs, gd); /* * In MTK the event emitted when the gprs state changes is different * from the one in AOSP ril. Overwrite the one set in parent. */ gd->state_changed_unsol = MTK_RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED; g_ril_register(gd->ril, MTK_RIL_UNSOL_GPRS_DETACH, detach_event, gprs); return 0; } static void mtk_gprs_remove(struct ofono_gprs *gprs) { struct ril_gprs_data *gd = ofono_gprs_get_data(gprs); gd->ofono_attached = FALSE; mtk_set_attach_state(gd->modem, gd->ofono_attached); ril_gprs_remove(gprs); } static struct ofono_gprs_driver driver = { .name = MTKMODEM, .probe = mtk_gprs_probe, .remove = mtk_gprs_remove, .set_attached = mtk_gprs_set_attached, .attached_status = ril_gprs_registration_status, .set_ia_apn = ril_gprs_set_ia_apn, }; void mtk_gprs_init(void) { ofono_gprs_driver_register(&driver); } void mtk_gprs_exit(void) { ofono_gprs_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtkreply.c0000644000015600001650000000406412671500024023711 0ustar pbuserpbgroup00000000000000/* * * MTK driver for ofono/rilmodem * * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include "mtkreply.h" int g_mtk_reply_parse_get_3g_capability(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int slot_3g, numint; g_ril_init_parcel(message, &rilp); numint = parcel_r_int32(&rilp); if (numint < 1) { ofono_error("%s: wrong format", __func__); goto error; } /* * Bitmap with 3g capability per slot. Reply is the same regardless of * the socket used to sent the request. */ slot_3g = parcel_r_int32(&rilp); if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); goto error; } g_ril_append_print_buf(gril, "{%d}", slot_3g); g_ril_print_response(gril, message); /* Do it zero-based */ return slot_3g - 1; error: return -1; } int g_mtk_reply_parse_query_modem_type(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int numint, type; g_ril_init_parcel(message, &rilp); numint = parcel_r_int32(&rilp); if (numint != 1) { ofono_error("%s Wrong format", __func__); goto error; } type = parcel_r_int32(&rilp); if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); goto error; } g_ril_append_print_buf(gril, "{%d}", type); g_ril_print_response(gril, message); return type; error: return -1; } ofono-1.17.bzr6912+16.04.20160314.3/drivers/mtkmodem/mtkmodem.h0000644000015600001650000000211412671500024023656 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2014 Canonical 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. * * 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 MTKMODEM "mtkmodem" extern void mtk_voicecall_init(void); extern void mtk_voicecall_exit(void); extern void mtk_gprs_init(void); extern void mtk_gprs_exit(void); extern void mtk_radio_settings_init(void); extern void mtk_radio_settings_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/ifxmodem/0000755000015600001650000000000012671500304021666 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/ifxmodem/ctm.c0000644000015600001650000001010212671500024022606 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "ifxmodem.h" static const char *none_prefix[] = { NULL }; static const char *xctms_prefix[] = { "+XCTMS:", NULL }; struct ctm_data { GAtChat *chat; }; static void xctms_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_ctm_query_cb_t cb = cbd->cb; struct ofono_error error; GAtResultIter iter; int value; ofono_bool_t enable; 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, "+XCTMS:") == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &value) == FALSE) goto error; /* FULL TTY mode status only sent to oFono */ enable = (value == 1) ? TRUE : FALSE; cb(&error, enable, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void ifx_query_tty(struct ofono_ctm *ctm, ofono_ctm_query_cb_t cb, void *data) { struct ctm_data *ctmd = ofono_ctm_get_data(ctm); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(ctmd->chat, "AT+XCTMS?", xctms_prefix, xctms_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void xctms_modify_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_ctm_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); /* TODO: Audio path configuration */ cb(&error, cbd->data); } static void ifx_set_tty(struct ofono_ctm *ctm, ofono_bool_t enable, ofono_ctm_set_cb_t cb, void *data) { struct ctm_data *ctmd = ofono_ctm_get_data(ctm); struct cb_data *cbd = cb_data_new(cb, data); char buf[20]; /* Only FULL TTY mode enabled/disabled */ snprintf(buf, sizeof(buf), "AT+XCTMS=%i", enable ? 1 : 0); if (g_at_chat_send(ctmd->chat, buf, none_prefix, xctms_modify_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void xctms_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_ctm *ctm = user_data; if (ok) ofono_ctm_register(ctm); } static int ifx_ctm_probe(struct ofono_ctm *ctm, unsigned int vendor, void *data) { GAtChat *chat = data; struct ctm_data *ctmd; ctmd = g_try_new0(struct ctm_data, 1); if (ctmd == NULL) return -ENOMEM; ctmd->chat = g_at_chat_clone(chat); ofono_ctm_set_data(ctm, ctmd); g_at_chat_send(ctmd->chat, "AT+XCTMS=?", xctms_prefix, xctms_support_cb, ctm, NULL); return 0; } static void ifx_ctm_remove(struct ofono_ctm *ctm) { struct ctm_data *ctmd = ofono_ctm_get_data(ctm); ofono_ctm_set_data(ctm, NULL); g_at_chat_unref(ctmd->chat); g_free(ctmd); } static struct ofono_ctm_driver driver = { .name = "ifxmodem", .probe = ifx_ctm_probe, .remove = ifx_ctm_remove, .query_tty = ifx_query_tty, .set_tty = ifx_set_tty, }; void ifx_ctm_init(void) { ofono_ctm_driver_register(&driver); } void ifx_ctm_exit(void) { ofono_ctm_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/ifxmodem/voicecall.c0000644000015600001650000006202612671500024024000 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "common.h" #include "ifxmodem.h" static const char *none_prefix[] = { NULL }; static const char *xlema_prefix[] = { "+XLEMA:", NULL }; struct voicecall_data { GSList *calls; unsigned int local_release; GAtChat *chat; char **en_list; }; struct release_id_req { struct ofono_voicecall *vc; ofono_voicecall_cb_t cb; void *data; int id; }; struct change_state_req { struct ofono_voicecall *vc; ofono_voicecall_cb_t cb; void *data; int affected_types; }; static int class_to_call_type(int cls) { switch (cls) { case 1: return 0; case 4: return 2; case 8: return 9; default: return 1; } } static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, int direction, int status, const char *num, int num_type, int clip, int id) { struct voicecall_data *d = ofono_voicecall_get_data(vc); struct ofono_call *call; /* Generate a call structure for the waiting call */ call = g_try_new(struct ofono_call, 1); if (call == NULL) return NULL; ofono_call_init(call); call->id = id; call->type = type; call->direction = direction; call->status = status; if (clip != 2) { strncpy(call->phone_number.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH); call->phone_number.type = num_type; } call->clip_validity = clip; d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare); return call; } static void xcallstat_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; int id; int status; GSList *l; struct ofono_call *new_call; struct ofono_call *existing_call = NULL; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "+XCALLSTAT:") == FALSE) return; if (g_at_result_iter_next_number(&iter, &id) == FALSE) return; if (g_at_result_iter_next_number(&iter, &status) == FALSE) return; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(id), at_util_call_compare_by_id); if (l == NULL && status != CALL_STATUS_DIALING && status != CALL_STATUS_INCOMING && status != CALL_STATUS_WAITING) { ofono_error("Received XCALLSTAT for an untracked" " call, this indicates a bug!"); return; } if (l) existing_call = l->data; switch (status) { case CALL_STATUS_DISCONNECTED: { enum ofono_disconnect_reason reason; existing_call->status = status; if (vd->local_release & (1 << existing_call->id)) reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP; else reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP; ofono_voicecall_disconnected(vc, existing_call->id, reason, NULL); vd->local_release &= ~(1 << existing_call->id); vd->calls = g_slist_remove(vd->calls, l->data); g_free(existing_call); break; } case CALL_STATUS_DIALING: new_call = create_call(vc, 0, CALL_DIRECTION_MOBILE_ORIGINATED, status, NULL, 128, CLIP_VALIDITY_NOT_AVAILABLE, id); if (new_call == NULL) { ofono_error("Unable to malloc. " "Call management is fubar"); return; } ofono_voicecall_notify(vc, new_call); break; case CALL_STATUS_WAITING: case CALL_STATUS_INCOMING: /* Handle the following situation: * Active Call + Waiting Call. Active Call is Released. * The Waiting call becomes Incoming. In this case, no * need to create a new call. Call status change will be * triggered from clip_notify. */ if (existing_call) { existing_call->status = status; return; } new_call = create_call(vc, 0, CALL_DIRECTION_MOBILE_TERMINATED, status, NULL, 128, CLIP_VALIDITY_NOT_AVAILABLE, id); if (new_call == NULL) { ofono_error("Unable to malloc. " "Call management is fubar"); return; } break; case CALL_STATUS_ALERTING: case CALL_STATUS_ACTIVE: case CALL_STATUS_HELD: default: /* For connected status, simply reset back to active */ if (status == 7) status = CALL_STATUS_ACTIVE; existing_call->status = status; ofono_voicecall_notify(vc, existing_call); break; } } static void xem_notify(GAtResult *result, gpointer user_data) { //struct ofono_voicecall *vc = user_data; //struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; int state; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "+XEM:") == FALSE) return; if (g_at_result_iter_next_number(&iter, &state) == FALSE) return; DBG("state %d", state); switch (state) { case 0: ofono_info("Emergency call is finished"); break; case 1: ofono_info("Emergency call is entered"); break; } } static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct change_state_req *req = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (ok && req->affected_types) { GSList *l; struct ofono_call *call; for (l = vd->calls; l; l = l->next) { call = l->data; if (req->affected_types & (1 << call->status)) vd->local_release |= (1 << call->id); } } req->cb(&error, req->data); } static void release_id_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct release_id_req *req = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (ok) vd->local_release |= 1 << req->id; req->cb(&error, req->data); } static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_voicecall_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); /* Let oFono core will generate a call with the dialed number * inside its dial callback. */ cb(&error, cbd->data); } static void ifx_dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, enum ofono_clir_option clir, 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; if (ph->type == 145) snprintf(buf, sizeof(buf), "ATD+%s", ph->number); else snprintf(buf, sizeof(buf), "ATD%s", ph->number); switch (clir) { case OFONO_CLIR_OPTION_INVOCATION: strcat(buf, "I"); break; case OFONO_CLIR_OPTION_SUPPRESSION: strcat(buf, "i"); break; default: break; } strcat(buf, ";"); if (g_at_chat_send(vd->chat, buf, none_prefix, atd_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void ifx_template(const char *cmd, struct ofono_voicecall *vc, GAtResultFunc result_cb, unsigned int affected_types, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct change_state_req *req = g_try_new0(struct change_state_req, 1); if (req == NULL) goto error; req->vc = vc; req->cb = cb; req->data = data; req->affected_types = affected_types; if (g_at_chat_send(vd->chat, cmd, none_prefix, result_cb, req, g_free) > 0) return; error: g_free(req); CALLBACK_WITH_FAILURE(cb, data); } static void ifx_answer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ifx_template("ATA", vc, generic_cb, 0, cb, data); } static void ifx_ath(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { /* Hangup active + held call, but not waiting */ ifx_template("ATH", vc, generic_cb, 0x1f, cb, data); } static void ifx_chup(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { /* Hangup active + but not held or waiting */ ifx_template("AT+CHUP", vc, generic_cb, 0x1d, cb, data); } static void ifx_hold_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ifx_template("AT+CHLD=2", vc, generic_cb, 0, cb, data); } static void ifx_release_all_held(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { unsigned int held_status = 1 << CALL_STATUS_HELD; ifx_template("AT+CHLD=0", vc, generic_cb, held_status, cb, data); } static void ifx_set_udub(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { unsigned int incoming_or_waiting = (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING); ifx_template("AT+CHLD=0", vc, generic_cb, incoming_or_waiting, cb, data); } static void ifx_release_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ifx_template("AT+CHLD=1", vc, generic_cb, 0x1, cb, data); } static void ifx_release_specific(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct release_id_req *req = g_try_new0(struct release_id_req, 1); char buf[32]; if (req == NULL) goto error; req->vc = vc; req->cb = cb; req->data = data; req->id = id; snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id); if (g_at_chat_send(vd->chat, buf, none_prefix, release_id_cb, req, g_free) > 0) return; error: g_free(req); CALLBACK_WITH_FAILURE(cb, data); } static void ifx_private_chat(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { char buf[32]; snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id); ifx_template(buf, vc, generic_cb, 0, cb, data); } static void ifx_create_multiparty(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ifx_template("AT+CHLD=3", vc, generic_cb, 0, cb, data); } static void ifx_transfer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { /* Held & Active */ unsigned int transfer = 0x1 | 0x2; /* Transfer can puts held & active calls together and disconnects * from both. However, some networks support transferring of * dialing/ringing calls as well. */ transfer |= 0x4 | 0x8; ifx_template("AT+CHLD=4", vc, generic_cb, transfer, cb, data); } static void ifx_deflect(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, ofono_voicecall_cb_t cb, void *data) { char buf[128]; unsigned int incoming_or_waiting = (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING); snprintf(buf, sizeof(buf), "AT+CTFR=%s,%d", ph->number, ph->type); ifx_template(buf, vc, generic_cb, incoming_or_waiting, cb, data); } static void ifx_swap_without_accept(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ifx_template("AT+CHLD=6", vc, generic_cb, 0, cb, data); } static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_voicecall_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void ifx_send_dtmf(struct ofono_voicecall *vc, const char *dtmf, 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); int len = strlen(dtmf); int s; int i; char *buf; /* strlen("+VTS=T\;") = 7 + initial AT + null */ buf = g_try_new(char, len * 7 + 3); if (buf == NULL) goto error; s = sprintf(buf, "AT+VTS=%c", dtmf[0]); for (i = 1; i < len; i++) s += sprintf(buf + s, ";+VTS=%c", dtmf[i]); s = g_at_chat_send(vd->chat, buf, none_prefix, vts_cb, cbd, g_free); g_free(buf); if (s > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void cring_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *line; GSList *l; struct ofono_call *call; /* Handle the following situation: * Active Call + Waiting Call. Active Call is Released. The Waiting * call becomes Incoming and CRING indications are signaled. * Sometimes these arrive before the actual state change notification. * If this happens, simply ignore the CRING when a waiting call * exists (cannot have waiting + incoming in GSM) */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), at_util_call_compare_by_status)) return; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status); if (l == NULL) { ofono_error("CRING received before XCALLSTAT!!!"); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CRING:")) return; line = g_at_result_iter_raw_line(&iter); if (line == NULL) return; call = l->data; /* Ignore everything that is not voice for now */ if (!strcasecmp(line, "VOICE")) call->type = 0; else call->type = 9; /* Assume the CLIP always arrives, and we signal the call there */ DBG("cring_notify"); } static void clip_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *num; int type, validity; GSList *l; struct ofono_call *call; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status); if (l == NULL) { ofono_error("CLIP for unknown call"); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CLIP:")) return; if (!g_at_result_iter_next_string(&iter, &num)) return; if (!g_at_result_iter_next_number(&iter, &type)) return; if (strlen(num) > 0) validity = 0; else validity = 2; /* Skip subaddr, satype and alpha */ g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); /* If we have CLI validity field, override our guessed value */ g_at_result_iter_next_number(&iter, &validity); DBG("clip_notify: %s %d %d", num, type, validity); call = l->data; 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 = type; call->clip_validity = validity; if (call->type == 0) ofono_voicecall_notify(vc, call); } static void cnap_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *name; int validity; GSList *l; struct ofono_call *call; /* * Currently, its not clear which URC will contain the * calling party name for the waiting call. */ l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status); if (l == NULL) { ofono_error("CNAP for unknown call"); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CNAP:")) return; if (!g_at_result_iter_next_string(&iter, &name)) return; if (strlen(name) > 0) validity = CNAP_VALIDITY_VALID; else validity = CNAP_VALIDITY_NOT_AVAILABLE; /* If we have CNI validity field, override our guessed value */ g_at_result_iter_next_number(&iter, &validity); DBG("%s %d", name, validity); call = l->data; strncpy(call->name, name, OFONO_MAX_CALLER_NAME_LENGTH); call->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0'; call->cnap_validity = validity; if (call->type == 0) ofono_voicecall_notify(vc, call); } static void ccwa_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *num; int num_type, validity, cls; GSList *l; struct ofono_call *call; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), at_util_call_compare_by_status); if (l == NULL) { ofono_error("CCWA received before XCALLSTAT!!!"); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CCWA:")) return; if (!g_at_result_iter_next_string(&iter, &num)) return; if (!g_at_result_iter_next_number(&iter, &num_type)) return; if (!g_at_result_iter_next_number(&iter, &cls)) return; /* Skip alpha field */ g_at_result_iter_skip_next(&iter); if (strlen(num) > 0) validity = 0; else validity = 2; /* If we have CLI validity field, override our guessed value */ g_at_result_iter_next_number(&iter, &validity); DBG("ccwa_notify: %s %d %d %d", num, num_type, cls, validity); call = l->data; call->type = class_to_call_type(cls); 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; call->clip_validity = validity; if (call->type == 0) /* Only notify voice calls */ ofono_voicecall_notify(vc, call); } static void xcolp_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *num; int type, call_id; GSList *l; struct ofono_call *call; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+XCOLP:")) return; if (!g_at_result_iter_next_number(&iter, &call_id)) return; if (!g_at_result_iter_next_string(&iter, &num)) return; if (!g_at_result_iter_next_number(&iter, &type)) return; if (strlen(num) == 0) { ofono_error("XCOLP received with invalid number!!!"); return; } DBG("xcolp_notify: %d %s %d", call_id, num, type); l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id), at_util_call_compare_by_id); if (l == NULL) { ofono_error("XCOLP for unknown call"); return; } call = l->data; 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 = type; call->clip_validity = CLIP_VALIDITY_VALID; ofono_voicecall_notify(vc, call); } static void xlema_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; int index, total_cnt; const char *number; int len; int count = (vd->en_list == NULL) ? 0 : g_strv_length(vd->en_list); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+XLEMA:")) return; if (!g_at_result_iter_next_number(&iter, &index)) return; if (!g_at_result_iter_next_number(&iter, &total_cnt)) return; if (!g_at_result_iter_next_string(&iter, &number)) return; /* Skip category */ if (g_at_result_iter_skip_next(&iter) == FALSE) goto done; /* Skip presence */ if (g_at_result_iter_skip_next(&iter) == FALSE) goto done; /* If we succeed here, then the number is from NVM or NITZ */ if (g_at_result_iter_skip_next(&iter) == FALSE) goto done; if (vd->en_list == NULL) vd->en_list = g_new0(char *, total_cnt + 1); len = strspn(number, "0123456789"); vd->en_list[count] = g_strndup(number, len); if (number[len] != '\0') ofono_warn("Malformed emergency number: %.*s", len, number); done: if (index != total_cnt) return; if (vd->en_list) { ofono_voicecall_en_list_notify(vc, vd->en_list); g_strfreev(vd->en_list); vd->en_list = NULL; } } static void xlema_read(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; int num = 0; int index, total_cnt; const char *number; int len; if (!ok) { DBG("Emergency number list read failed"); return; } g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+XLEMA:")) num += 1; vd->en_list = g_new0(char *, num + 1); num = 0; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+XLEMA:")) { if (!g_at_result_iter_next_number(&iter, &index)) continue; if (!g_at_result_iter_next_number(&iter, &total_cnt)) continue; if (!g_at_result_iter_next_string(&iter, &number)) continue; len = strspn(number, "0123456789"); vd->en_list[num++] = g_strndup(number, len); if (number[len] != '\0') ofono_warn("Malformed emergency number: %.*s", len, number); } ofono_voicecall_en_list_notify(vc, vd->en_list); g_strfreev(vd->en_list); vd->en_list = NULL; } static void cssi_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; GAtResultIter iter; int code, index; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSSI:")) return; if (!g_at_result_iter_next_number(&iter, &code)) return; if (!g_at_result_iter_next_number(&iter, &index)) index = 0; ofono_voicecall_ssn_mo_notify(vc, 0, code, index); } static void cssu_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; GAtResultIter iter; int code; int index; const char *num; struct ofono_phone_number ph; ph.number[0] = '\0'; ph.type = 129; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSSU:")) return; if (!g_at_result_iter_next_number(&iter, &code)) return; if (!g_at_result_iter_next_number_default(&iter, -1, &index)) goto out; if (!g_at_result_iter_next_string(&iter, &num)) goto out; strncpy(ph.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH); if (!g_at_result_iter_next_number(&iter, &ph.type)) return; out: ofono_voicecall_ssn_mt_notify(vc, 0, code, index, &ph); } static void ifx_voicecall_initialized(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); DBG("voicecall_init: registering to notifications"); g_at_chat_register(vd->chat, "+CRING:", cring_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CNAP:", cnap_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+XEM:", xem_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+XCALLSTAT:", xcallstat_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+XCOLP:", xcolp_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+XLEMA:", xlema_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CSSI:", cssi_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CSSU:", cssu_notify, FALSE, vc, NULL); /* Enable emergency number list notification */ g_at_chat_send(vd->chat, "AT+XLEMA=1", xlema_prefix, xlema_read, vc, NULL); ofono_voicecall_register(vc); } static int ifx_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, void *data) { GAtChat *chat = data; struct voicecall_data *vd; vd = g_try_new0(struct voicecall_data, 1); if (vd == NULL) return -ENOMEM; vd->chat = g_at_chat_clone(chat); ofono_voicecall_set_data(vc, vd); g_at_chat_send(vd->chat, "AT+XCALLSTAT=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+XEMC=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+XCOLP=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CRC=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CLIP=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CNAP=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CSSN=1,1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CCWA=1", none_prefix, ifx_voicecall_initialized, vc, NULL); return 0; } static void ifx_voicecall_remove(struct ofono_voicecall *vc) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); g_slist_foreach(vd->calls, (GFunc) g_free, NULL); g_slist_free(vd->calls); g_strfreev(vd->en_list); ofono_voicecall_set_data(vc, NULL); g_at_chat_unref(vd->chat); g_free(vd); } static struct ofono_voicecall_driver driver = { .name = "ifxmodem", .probe = ifx_voicecall_probe, .remove = ifx_voicecall_remove, .dial = ifx_dial, .answer = ifx_answer, .hangup_all = ifx_ath, .hangup_active = ifx_chup, .hold_all_active = ifx_hold_all_active, .release_all_held = ifx_release_all_held, .set_udub = ifx_set_udub, .release_all_active = ifx_release_all_active, .release_specific = ifx_release_specific, .private_chat = ifx_private_chat, .create_multiparty = ifx_create_multiparty, .transfer = ifx_transfer, .deflect = ifx_deflect, .swap_without_accept = ifx_swap_without_accept, .send_tones = ifx_send_dtmf }; void ifx_voicecall_init(void) { ofono_voicecall_driver_register(&driver); } void ifx_voicecall_exit(void) { ofono_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/ifxmodem/audio-settings.c0000644000015600001650000002533612671500024025001 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "ifxmodem.h" static const char *none_prefix[] = { NULL }; static const char *xprogress_prefix[] = { "+XPROGRESS:", NULL }; static const char *xdrv_prefix[] = { "+XDRV:", NULL }; enum xdrv_destination { XDRV_DESTINATION_SPEECH_TX = 0, XDRV_DESTINATION_ANALOG_OUT = 1, XDRV_DESTINATION_I2SX_TX = 2, XDRV_DESTINATION_I2SY_TX = 3, XDRV_DESTINATION_PCM_GENERAL = 4, }; enum xdrv_source { XDRV_SOURCE_SPEECH_RX = 0, XDRV_SOURCE_SPEECH_ANALOG_IN = 1, XDRV_SOURCE_DIGITAL_MIC_IN = 2, XDRV_SOURCE_I2SX_RX = 3, XDRV_SOURCE_I2SY_RX = 4, XDRV_SOURCE_SIMPLE_TONES = 5, }; enum xdrv_sampling_rate { XDRV_SAMPLING_RATE_8KHZ = 0, XDRV_SAMPLING_RATE_11KHZ = 1, XDRV_SAMPLING_RATE_12KHZ = 2, XDRV_SAMPLING_RATE_16KHZ = 3, XDRV_SAMPLING_RATE_22KHZ = 4, XDRV_SAMPLING_RATE_24KHZ = 5, XDRV_SAMPLING_RATE_32KHZ = 6, XDRV_SAMPLING_RATE_44KHZ = 7, XDRV_SAMPLING_RATE_48KHZ = 8, XDRV_SAMPLING_RATE_96KHZ = 9, XDRV_SAMPLING_RATE_192KHZ = 10, }; enum xdrv_sampling_width { XDRV_SAMPLING_WIDTH_16 = 0, XDRV_SAMPLING_WIDTH_18 = 1, XDRV_SAMPLING_WIDTH_20 = 2, XDRV_SAMPLING_WIDTH_24 = 3, XDRV_SAMPLING_WIDTH_32 = 4, XDRV_SAMPLING_WIDTH_48 = 5, XDRV_SAMPLING_WIDTH_64 = 6, }; enum xdrv_i2s_mode { XDRV_I2S_MODE_MASTER = 0, XDRV_I2S_MODE_SLAVE = 1, }; enum xdrv_i2s_clock { XDRV_I2S_CLOCK_0 = 0, XDRV_I2S_CLOCK_1 = 1, }; enum xdrv_i2s_configuration_mode { XDRV_I2S_CONFIGURATION_MODE_UPDATE_ALL = 0, XDRV_I2S_CONFIGURATION_MODE_UPDATE_HW = 1, XDRV_I2S_CONFIGURATION_MODE_UPDATE_TRANSDUCER = 2, }; enum xdrv_i2s_settings { XDRV_I2S_SETTINGS_NORMAL = 0, XDRV_I2S_SETTINGS_SPECIAL1 = 1, XDRV_I2S_SETTINGS_SPECIAL2 = 2, }; enum xdrv_i2s_transmission_mode { XDRV_I2S_TRANSMISSION_MODE_PCM = 0, XDRV_I2S_TRANSMISSION_MODE_NORMAL = 1, XDRV_IS2_TRANSMISSION_MODE_PCM_BURST = 2, }; enum xdrv_source_transducer { XDRV_SOURCE_TRANSDUCER_DEFAULT = 0, XDRV_SOURCE_TRANSDUCER_HANDSET = 1, XDRV_SOURCE_TRANSDUCER_HEADSET = 2, XDRV_SOURCE_TRANSDUCER_HF = 3, XDRV_SOURCE_TRANSDUCER_AUX = 4, XDRV_SOURCE_TRANSDUCER_TTY = 5, XDRV_SOURCE_TRANSDUCER_BLUETOOTH = 6, XDRV_SOURCE_TRANSDUCER_USER_DEFINED_15 = 21, }; enum xdrv_dest_transducer { XDRV_DEST_TRANSDUCER_DEFAULT = 0, XDRV_DEST_TRANSDUCER_HANDSET = 1, XDRV_DEST_TRANSDUCER_HEADSET = 2, XDRV_DEST_TRANSDUCER_BACKSPEAKER = 3, XDRV_DEST_TRANSDUCER_TTY = 6, XDRV_DEST_TRANSDUCER_BLUETOOTH = 7, XDRV_DEST_TRANSDUCER_USER_DEFINED_15 = 22, }; enum xdrv_audio_mode { XDRV_AUDIO_MODE_MONO = 0, XDRV_AUDIO_MODE_DUAL_MONO = 1, XDRV_AUDIO_MODE_STEREO = 2, XDRV_AUDIO_MODE_DUAL_MONO_R = 3, XDRV_AUDIO_MODE_DUAL_MONO_L = 4, }; struct audio_settings_data { GAtChat *chat; }; static inline void xdrv_enable_source(GAtChat *chat, enum xdrv_source src) { char buf[256]; sprintf(buf, "AT+XDRV=40,2,%i", src); g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL); } static inline void xdrv_disable_source(GAtChat *chat, enum xdrv_source src) { char buf[256]; sprintf(buf, "AT+XDRV=40,3,%i", src); g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL); } static inline void xdrv_configure_source(GAtChat *chat, enum xdrv_source src, enum xdrv_i2s_clock clock, enum xdrv_i2s_mode master_slave, enum xdrv_sampling_rate sample_rate, enum xdrv_sampling_width bits, enum xdrv_i2s_transmission_mode tx_mode, enum xdrv_i2s_settings settings, enum xdrv_audio_mode mode, enum xdrv_i2s_configuration_mode config_mode, enum xdrv_source_transducer transducer_mode) { char buf[256]; int ctx = 0; /* This is always 0 for now */ sprintf(buf, "AT+XDRV=40,4,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i", src, ctx, clock, master_slave, sample_rate, bits, tx_mode, settings, mode, config_mode, transducer_mode); g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL); } static inline void xdrv_configure_destination(GAtChat *chat, enum xdrv_destination dest, enum xdrv_i2s_clock clock, enum xdrv_i2s_mode master_slave, enum xdrv_sampling_rate sample_rate, enum xdrv_sampling_width bits, enum xdrv_i2s_transmission_mode tx_mode, enum xdrv_i2s_settings settings, enum xdrv_audio_mode mode, enum xdrv_i2s_configuration_mode config_mode, enum xdrv_dest_transducer transducer_mode) { char buf[256]; int ctx = 0; /* This is always 0 for now */ sprintf(buf, "AT+XDRV=40,5,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i", dest, ctx, clock, master_slave, sample_rate, bits, tx_mode, settings, mode, config_mode, transducer_mode); g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL); } static inline void xdrv_set_destination_for_source(GAtChat *chat, enum xdrv_source src, enum xdrv_destination dest) { char buf[256]; sprintf(buf, "AT+XDRV=40,6,%i,%i", src, dest); g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL); } static inline void xdrv_set_destination_volume(GAtChat *chat, enum xdrv_destination dest, int volume) { char buf[256]; sprintf(buf, "AT+XDRV=40,8,%i,%i", dest, volume); g_at_chat_send(chat, buf, xdrv_prefix, NULL, NULL, NULL); } static void send_xdrv_setup_sequence(struct ofono_audio_settings *as) { struct audio_settings_data *asd = ofono_audio_settings_get_data(as); /* Mute */ xdrv_set_destination_volume(asd->chat, XDRV_DESTINATION_I2SX_TX, 0); xdrv_set_destination_volume(asd->chat, XDRV_DESTINATION_SPEECH_TX, 0); xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_SPEECH_RX, XDRV_DESTINATION_PCM_GENERAL); xdrv_disable_source(asd->chat, XDRV_SOURCE_I2SX_RX); xdrv_disable_source(asd->chat, XDRV_SOURCE_I2SY_RX); xdrv_configure_source(asd->chat, XDRV_SOURCE_I2SX_RX, XDRV_I2S_CLOCK_1, XDRV_I2S_MODE_MASTER, XDRV_SAMPLING_RATE_48KHZ, XDRV_SAMPLING_WIDTH_16, XDRV_I2S_TRANSMISSION_MODE_NORMAL, XDRV_I2S_SETTINGS_NORMAL, XDRV_AUDIO_MODE_STEREO, XDRV_I2S_CONFIGURATION_MODE_UPDATE_ALL, XDRV_SOURCE_TRANSDUCER_USER_DEFINED_15); xdrv_configure_destination(asd->chat, XDRV_DESTINATION_I2SX_TX, XDRV_I2S_CLOCK_1, XDRV_I2S_MODE_MASTER, XDRV_SAMPLING_RATE_48KHZ, XDRV_SAMPLING_WIDTH_16, XDRV_I2S_TRANSMISSION_MODE_NORMAL, XDRV_I2S_SETTINGS_NORMAL, XDRV_AUDIO_MODE_STEREO, XDRV_I2S_CONFIGURATION_MODE_UPDATE_ALL, XDRV_DEST_TRANSDUCER_USER_DEFINED_15); xdrv_configure_source(asd->chat, XDRV_SOURCE_I2SY_RX, XDRV_I2S_CLOCK_0, XDRV_I2S_MODE_MASTER, XDRV_SAMPLING_RATE_48KHZ, XDRV_SAMPLING_WIDTH_16, XDRV_I2S_TRANSMISSION_MODE_NORMAL, XDRV_I2S_SETTINGS_NORMAL, XDRV_AUDIO_MODE_STEREO, XDRV_I2S_CONFIGURATION_MODE_UPDATE_ALL, XDRV_SOURCE_TRANSDUCER_USER_DEFINED_15); xdrv_configure_destination(asd->chat, XDRV_DESTINATION_I2SY_TX, XDRV_I2S_CLOCK_0, XDRV_I2S_MODE_MASTER, XDRV_SAMPLING_RATE_48KHZ, XDRV_SAMPLING_WIDTH_16, XDRV_I2S_TRANSMISSION_MODE_NORMAL, XDRV_I2S_SETTINGS_NORMAL, XDRV_AUDIO_MODE_STEREO, XDRV_I2S_CONFIGURATION_MODE_UPDATE_ALL, XDRV_DEST_TRANSDUCER_USER_DEFINED_15); /* Seems unnecessary xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_SPEECH_RX, XDRV_DESTINATION_PCM_GENERAL); */ xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_I2SX_RX, XDRV_DESTINATION_SPEECH_TX); xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_I2SY_RX, XDRV_DESTINATION_I2SX_TX); xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_SIMPLE_TONES, XDRV_DESTINATION_I2SX_TX); xdrv_enable_source(asd->chat, XDRV_SOURCE_I2SX_RX); xdrv_enable_source(asd->chat, XDRV_SOURCE_I2SY_RX); xdrv_set_destination_for_source(asd->chat, XDRV_SOURCE_SPEECH_RX, XDRV_DESTINATION_I2SX_TX); /* Unmute */ xdrv_set_destination_volume(asd->chat, XDRV_DESTINATION_I2SX_TX, 66); xdrv_set_destination_volume(asd->chat, XDRV_DESTINATION_SPEECH_TX, 100); } static void xprogress_notify(GAtResult *result, gpointer user_data) { struct ofono_audio_settings *as = user_data; GAtResultIter iter; int id, status; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "+XPROGRESS:") == FALSE) return; if (g_at_result_iter_next_number(&iter, &id) == FALSE) return; if (g_at_result_iter_next_number(&iter, &status) == FALSE) return; switch (status) { case 0: case 1: case 4: case 9: case 10: case 11: ofono_audio_settings_active_notify(as, FALSE); break; case 2: case 3: case 5: case 6: case 7: case 8: ofono_audio_settings_active_notify(as, TRUE); break; } } static void xprogress_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_audio_settings *as = user_data; struct audio_settings_data *asd = ofono_audio_settings_get_data(as); if (!ok) return; g_at_chat_register(asd->chat, "+XPROGRESS:", xprogress_notify, FALSE, as, NULL); g_at_chat_send(asd->chat, "AT+XPROGRESS=1", none_prefix, NULL, NULL, NULL); ofono_audio_settings_register(as); send_xdrv_setup_sequence(as); } static int ifx_audio_settings_probe(struct ofono_audio_settings *as, unsigned int vendor, void *data) { GAtChat *chat = data; struct audio_settings_data *asd; asd = g_try_new0(struct audio_settings_data, 1); if (asd == NULL) return -ENOMEM; asd->chat = g_at_chat_clone(chat); ofono_audio_settings_set_data(as, asd); g_at_chat_send(asd->chat, "AT+XPROGRESS=?", xprogress_prefix, xprogress_support_cb, as, NULL); return 0; } static void ifx_audio_settings_remove(struct ofono_audio_settings *as) { struct audio_settings_data *asd = ofono_audio_settings_get_data(as); ofono_audio_settings_set_data(as, NULL); g_at_chat_unref(asd->chat); g_free(asd); } static struct ofono_audio_settings_driver driver = { .name = "ifxmodem", .probe = ifx_audio_settings_probe, .remove = ifx_audio_settings_remove, }; void ifx_audio_settings_init(void) { ofono_audio_settings_driver_register(&driver); } void ifx_audio_settings_exit(void) { ofono_audio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/ifxmodem/radio-settings.c0000644000015600001650000001200112671500024024757 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "ifxmodem.h" static const char *none_prefix[] = { NULL }; static const char *xrat_prefix[] = { "+XRAT:", NULL }; struct radio_settings_data { GAtChat *chat; }; static void xrat_query_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; int value, preferred; 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, "+XRAT:") == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &value) == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &preferred) == FALSE) goto error; switch (value) { case 0: mode = OFONO_RADIO_ACCESS_MODE_GSM; break; case 1: mode = OFONO_RADIO_ACCESS_MODE_ANY; break; case 2: mode = OFONO_RADIO_ACCESS_MODE_UMTS; break; default: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } cb(&error, mode, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void ifx_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+XRAT?", xrat_prefix, xrat_query_cb, cbd, g_free) == 0) { CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } } static void xrat_modify_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void ifx_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); struct cb_data *cbd = cb_data_new(cb, data); char buf[20]; int value = 1, preferred = 2; switch (mode) { case OFONO_RADIO_ACCESS_MODE_ANY: value = 1; break; case OFONO_RADIO_ACCESS_MODE_GSM: value = 0; break; case OFONO_RADIO_ACCESS_MODE_UMTS: value = 2; break; case OFONO_RADIO_ACCESS_MODE_LTE: goto error; } if (value == 1) snprintf(buf, sizeof(buf), "AT+XRAT=%u,%u", value, preferred); else snprintf(buf, sizeof(buf), "AT+XRAT=%u", value); if (g_at_chat_send(rsd->chat, buf, none_prefix, xrat_modify_cb, cbd, g_free) > 0) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void xrat_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_radio_settings *rs = user_data; if (!ok) { ofono_radio_settings_remove(rs); return; } ofono_radio_settings_register(rs); } static int ifx_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *data) { GAtChat *chat = data; struct radio_settings_data *rsd; rsd = g_try_new0(struct radio_settings_data, 1); if (rsd == NULL) return -ENOMEM; rsd->chat = g_at_chat_clone(chat); ofono_radio_settings_set_data(rs, rsd); g_at_chat_send(rsd->chat, "AT+XRAT=?", xrat_prefix, xrat_support_cb, rs, NULL); return 0; } static void ifx_radio_settings_remove(struct ofono_radio_settings *rs) { struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs); ofono_radio_settings_set_data(rs, NULL); g_at_chat_unref(rsd->chat); g_free(rsd); } static struct ofono_radio_settings_driver driver = { .name = "ifxmodem", .probe = ifx_radio_settings_probe, .remove = ifx_radio_settings_remove, .query_rat_mode = ifx_query_rat_mode, .set_rat_mode = ifx_set_rat_mode }; void ifx_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void ifx_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/ifxmodem/ifxmodem.h0000644000015600001650000000240112671500024023643 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void ifx_voicecall_init(void); extern void ifx_voicecall_exit(void); extern void ifx_audio_settings_init(void); extern void ifx_audio_settings_exit(void); extern void ifx_radio_settings_init(void); extern void ifx_radio_settings_exit(void); extern void ifx_gprs_context_init(void); extern void ifx_gprs_context_exit(void); extern void ifx_stk_init(void); extern void ifx_stk_exit(void); extern void ifx_ctm_init(void); extern void ifx_ctm_exit(void);ofono-1.17.bzr6912+16.04.20160314.3/drivers/ifxmodem/stk.c0000644000015600001650000001573112671500024022641 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "ifxmodem.h" struct stk_data { GAtChat *chat; }; static const char *none_prefix[] = { NULL }; static const char *sate_prefix[] = { "+SATE:", NULL }; static const char *xsatk_prefix[] = { "+XSATK:", NULL }; static void sate_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_stk_envelope_cb_t cb = cbd->cb; GAtResultIter iter; struct ofono_error error; int sw1, sw2, envelope, event; const guint8 *pdu = NULL; gint len = 0; DBG(""); decode_at_error(&error, g_at_result_final_response(result)); if (!ok) goto done; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "+SATE:") == FALSE) goto done; if (g_at_result_iter_next_number(&iter, &sw1) == FALSE) goto done; if (g_at_result_iter_next_number(&iter, &sw2) == FALSE) goto done; if (g_at_result_iter_next_number(&iter, &envelope) == FALSE) goto done; if (g_at_result_iter_next_number(&iter, &event) == FALSE) goto done; DBG("sw1 %d sw2 %d envelope %d event %d", sw1, sw2, envelope, event); /* Response data is optional */ g_at_result_iter_next_hexstring(&iter, &pdu, &len); DBG("len %d", len); done: cb(&error, pdu, len, cbd->data); } static void ifx_stk_envelope(struct ofono_stk *stk, int length, const unsigned char *command, ofono_stk_envelope_cb_t cb, void *data) { struct stk_data *sd = ofono_stk_get_data(stk); struct cb_data *cbd = cb_data_new(cb, data); char *buf = g_try_new(char, 64 + length * 2); int len; DBG(""); if (buf == NULL) goto error; len = sprintf(buf, "AT+SATE=\""); for (; length; length--) len += sprintf(buf + len, "%02hhX", *command++); len += sprintf(buf + len, "\""); DBG("%s", buf); if (g_at_chat_send(sd->chat, buf, sate_prefix, sate_cb, cbd, g_free) > 0) { g_free(buf); return; } error: g_free(buf); g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } static void satr_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_stk_generic_cb_t cb = cbd->cb; struct ofono_error error; DBG(""); decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void ifx_stk_terminal_response(struct ofono_stk *stk, int length, const unsigned char *command, ofono_stk_generic_cb_t cb, void *data) { struct stk_data *sd = ofono_stk_get_data(stk); struct cb_data *cbd = cb_data_new(cb, data); char *buf = g_try_new(char, 64 + length * 2); int len; DBG(""); if (buf == NULL) goto error; len = sprintf(buf, "AT+SATR=\""); for (; length; length--) len += sprintf(buf + len, "%02hhX", *command++); len += sprintf(buf + len, "\""); DBG("%s", buf); if (g_at_chat_send(sd->chat, buf, none_prefix, satr_cb, cbd, g_free) > 0) { g_free(buf); return; } error: g_free(buf); g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void ifx_stk_user_confirmation(struct ofono_stk *stk, gboolean confirm) { struct stk_data *sd = ofono_stk_get_data(stk); char buf[20]; snprintf(buf, sizeof(buf), "AT+SATD=%i", confirm ? 1 : 0); g_at_chat_send(sd->chat, buf, none_prefix, NULL, NULL, NULL); } static void sati_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; GAtResultIter iter; const guint8 *pdu; gint len; DBG(""); g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "+SATI:") == FALSE) return; if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE) return; DBG("len %d", len); ofono_stk_proactive_command_notify(stk, len, pdu); } static void satn_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; GAtResultIter iter; const guint8 *pdu; gint len; DBG(""); /* Proactive command has been handled by the modem. */ g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "+SATN:") == FALSE) return; if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE) return; if (len == 0) return; ofono_stk_proactive_command_handled_notify(stk, len, pdu); } static void satf_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; GAtResultIter iter; int sw1, sw2; DBG(""); g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "+SATF:") == FALSE) return; if (g_at_result_iter_next_number(&iter, &sw1) == FALSE) return; if (g_at_result_iter_next_number(&iter, &sw2) == FALSE) return; DBG("sw1 %d sw2 %d", sw1, sw2); if (sw1 == 0x90 && sw2 == 0x00) ofono_stk_proactive_session_end_notify(stk); } static void xsatk_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; struct stk_data *sd = ofono_stk_get_data(stk); DBG(""); if (!ok) return; g_at_chat_register(sd->chat, "+SATI:", sati_notify, FALSE, stk, NULL); g_at_chat_register(sd->chat, "+SATN:", satn_notify, FALSE, stk, NULL); g_at_chat_register(sd->chat, "+SATF:", satf_notify, FALSE, stk, NULL); g_at_chat_send(sd->chat, "AT+XSATK=1,1", none_prefix, NULL, NULL, NULL); ofono_stk_register(stk); } static int ifx_stk_probe(struct ofono_stk *stk, unsigned int vendor, void *data) { GAtChat *chat = data; struct stk_data *sd; DBG(""); sd = g_try_new0(struct stk_data, 1); if (sd == NULL) return -ENOMEM; sd->chat = g_at_chat_clone(chat); ofono_stk_set_data(stk, sd); g_at_chat_send(sd->chat, "AT+XSATK=?", xsatk_prefix, xsatk_support_cb, stk, NULL); return 0; } static void ifx_stk_remove(struct ofono_stk *stk) { struct stk_data *sd = ofono_stk_get_data(stk); DBG(""); ofono_stk_set_data(stk, NULL); g_at_chat_unref(sd->chat); g_free(sd); } static struct ofono_stk_driver driver = { .name = "ifxmodem", .probe = ifx_stk_probe, .remove = ifx_stk_remove, .envelope = ifx_stk_envelope, .terminal_response = ifx_stk_terminal_response, .user_confirmation = ifx_stk_user_confirmation, }; void ifx_stk_init(void) { ofono_stk_driver_register(&driver); } void ifx_stk_exit(void) { ofono_stk_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/ifxmodem/gprs-context.c0000644000015600001650000003001012671500024024460 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "gatrawip.h" #include "ifxmodem.h" #define TUN_SYSFS_DIR "/sys/devices/virtual/misc/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 }; enum state { STATE_IDLE, STATE_ENABLING, STATE_DISABLING, STATE_ACTIVE, }; struct gprs_context_data { GAtChat *chat; 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]; ofono_gprs_context_cb_t cb; void *cb_data; /* Callback data */ }; static void rawip_debug(const char *str, void *data) { ofono_info("%s: %s", (const char *) data, str); } static const char *setup_rawip(struct ofono_gprs_context *gc) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); GAtIO *io; DBG(""); io = g_at_chat_get_io(gcd->chat); g_at_chat_suspend(gcd->chat); gcd->rawip = g_at_rawip_new_from_io(io); if (gcd->rawip == NULL) { g_at_chat_resume(gcd->chat); return NULL; } if (getenv("OFONO_IP_DEBUG")) g_at_rawip_set_debug(gcd->rawip, rawip_debug, "IP"); g_at_rawip_open(gcd->rawip); return g_at_rawip_get_interface(gcd->rawip); } static void failed_setup(struct ofono_gprs_context *gc, GAtResult *result, gboolean deactivate) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; char buf[64]; DBG("deactivate %d", deactivate); if (deactivate == TRUE) { sprintf(buf, "AT+CGACT=0,%u", gcd->active_context); g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL); } gcd->active_context = 0; gcd->state = STATE_IDLE; if (result == NULL) { CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data); return; } decode_at_error(&error, g_at_result_final_response(result)); gcd->cb(&error, gcd->cb_data); } static void session_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); const char *interface; const char *dns[3]; DBG("ok %d", ok); if (!ok) { ofono_error("Failed to establish session"); failed_setup(gc, result, TRUE); return; } gcd->state = STATE_ACTIVE; dns[0] = gcd->dns1; dns[1] = gcd->dns2; dns[2] = 0; interface = setup_rawip(gc); if (interface == NULL) interface = "invalid"; ofono_gprs_context_set_interface(gc, interface); ofono_gprs_context_set_ipv4_address(gc, gcd->address, TRUE); ofono_gprs_context_set_ipv4_netmask(gc, STATIC_IP_NETMASK); ofono_gprs_context_set_ipv4_dns_servers(gc, dns); CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); gcd->cb = NULL; gcd->cb_data = NULL; } static void dns_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]; int cid; const char *dns1, *dns2; GAtResultIter iter; gboolean found = FALSE; DBG("ok %d", ok); if (!ok) { ofono_error("Unable to get DNS details"); failed_setup(gc, result, TRUE); return; } g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+XDNS:")) { if (!g_at_result_iter_next_number(&iter, &cid)) goto error; if (!g_at_result_iter_next_string(&iter, &dns1)) goto error; if (!g_at_result_iter_next_string(&iter, &dns2)) goto error; if ((unsigned int) cid == gcd->active_context) { found = TRUE; strncpy(gcd->dns1, dns1, sizeof(gcd->dns1)); strncpy(gcd->dns2, dns2, sizeof(gcd->dns2)); } } if (found == FALSE) goto error; ofono_info("IP: %s", gcd->address); ofono_info("DNS: %s, %s", gcd->dns1, gcd->dns2); sprintf(buf, "AT+CGDATA=\"M-RAW_IP\",%d", gcd->active_context); if (g_at_chat_send(gcd->chat, buf, none_prefix, session_cb, gc, NULL) > 0) return; error: failed_setup(gc, NULL, TRUE); } static void address_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); int cid; const char *address; GAtResultIter iter; DBG("ok %d", ok); if (!ok) { ofono_error("Unable to get context address"); failed_setup(gc, result, TRUE); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CGPADDR:")) goto error; if (!g_at_result_iter_next_number(&iter, &cid)) goto error; if ((unsigned int) cid != gcd->active_context) goto error; if (!g_at_result_iter_next_string(&iter, &address)) goto error; strncpy(gcd->address, address, sizeof(gcd->address)); if (g_at_chat_send(gcd->chat, "AT+XDNS?", xdns_prefix, dns_cb, gc, NULL) > 0) return; error: failed_setup(gc, NULL, TRUE); } 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); if (!ok) { ofono_error("Unable to activate context"); failed_setup(gc, result, FALSE); 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); } 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]; DBG("ok %d", ok); if (!ok) { ofono_error("Failed to setup context"); failed_setup(gc, result, FALSE); return; } if (gcd->username[0] && gcd->password[0]) sprintf(buf, "AT+XGAUTH=%u,1,\"%s\",\"%s\"", gcd->active_context, gcd->username, gcd->password); else sprintf(buf, "AT+XGAUTH=%u,0,\"\",\"\"", gcd->active_context); if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0) goto error; g_at_chat_send(gcd->chat, "AT+XDNS=?", none_prefix, NULL, NULL, NULL); switch (gcd->proto) { case OFONO_GPRS_PROTO_IP: sprintf(buf, "AT+XDNS=%u,1", gcd->active_context); break; case OFONO_GPRS_PROTO_IPV6: sprintf(buf, "AT+XDNS=%u,2", gcd->active_context); break; case OFONO_GPRS_PROTO_IPV4V6: sprintf(buf, "AT+XDNS=%u,3", gcd->active_context); break; } if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0) goto error; sprintf(buf, "AT+CGACT=1,%u", gcd->active_context); if (g_at_chat_send(gcd->chat, buf, none_prefix, activate_cb, gc, NULL) > 0) return; error: failed_setup(gc, NULL, FALSE); } static void ifx_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); char buf[OFONO_GPRS_MAX_APN_LENGTH + 128]; int len = 0; DBG("cid %u", ctx->cid); gcd->active_context = ctx->cid; gcd->cb = cb; gcd->cb_data = data; memcpy(gcd->username, ctx->username, sizeof(ctx->username)); memcpy(gcd->password, ctx->password, sizeof(ctx->password)); gcd->state = STATE_ENABLING; gcd->proto = ctx->proto; switch (ctx->proto) { case OFONO_GPRS_PROTO_IP: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); break; case OFONO_GPRS_PROTO_IPV6: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"", ctx->cid); break; case OFONO_GPRS_PROTO_IPV4V6: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"", ctx->cid); break; } if (ctx->apn) snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, setup_cb, gc, NULL) > 0) return; CALLBACK_WITH_FAILURE(cb, data); } static void deactivate_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); DBG("ok %d", ok); g_at_rawip_unref(gcd->rawip); gcd->rawip = NULL; gcd->active_context = 0; gcd->state = STATE_IDLE; g_at_chat_resume(gcd->chat); CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); } static void ifx_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); GAtChat *chat = g_at_chat_get_slave(gcd->chat); char buf[64]; DBG("cid %u", cid); gcd->state = STATE_DISABLING; gcd->cb = cb; gcd->cb_data = data; 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); } static void cgev_notify(GAtResult *result, gpointer user_data) { struct ofono_gprs_context *gc = user_data; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); const char *event; int cid; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CGEV:")) return; if (!g_at_result_iter_next_unquoted_string(&iter, &event)) return; if (g_str_has_prefix(event, "NW DEACT") == FALSE) return; if (!g_at_result_iter_skip_next(&iter)) return; if (!g_at_result_iter_next_number(&iter, &cid)) return; DBG("cid %d", cid); if ((unsigned int) cid != gcd->active_context) return; if (gcd->state != STATE_IDLE && gcd->rawip) { g_at_rawip_shutdown(gcd->rawip); g_at_rawip_unref(gcd->rawip); gcd->rawip = NULL; } 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, unsigned int vendor, void *data) { GAtChat *chat = data; struct gprs_context_data *gcd; struct stat st; DBG(""); if (stat(TUN_SYSFS_DIR, &st) < 0) { ofono_error("Missing support for TUN/TAP devices"); return -ENODEV; } 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->chat = g_at_chat_clone(chat); ofono_gprs_context_set_data(gc, gcd); chat = g_at_chat_get_slave(gcd->chat); g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL); return 0; } static void ifx_gprs_context_remove(struct ofono_gprs_context *gc) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); DBG(""); if (gcd->state != STATE_IDLE && gcd->rawip) { g_at_rawip_unref(gcd->rawip); g_at_chat_resume(gcd->chat); } ofono_gprs_context_set_data(gc, NULL); g_at_chat_unref(gcd->chat); g_free(gcd); } static struct ofono_gprs_context_driver driver = { .name = "ifxmodem", .probe = ifx_gprs_context_probe, .remove = ifx_gprs_context_remove, .activate_primary = ifx_gprs_activate_primary, .deactivate_primary = ifx_gprs_deactivate_primary, }; void ifx_gprs_context_init(void) { ofono_gprs_context_driver_register(&driver); } void ifx_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/ifxmodem/ifxmodem.c0000644000015600001650000000270412671500024023644 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "ifxmodem.h" static int ifxmodem_init(void) { ifx_voicecall_init(); ifx_audio_settings_init(); ifx_radio_settings_init(); ifx_gprs_context_init(); ifx_stk_init(); ifx_ctm_init(); return 0; } static void ifxmodem_exit(void) { ifx_stk_exit(); ifx_gprs_context_exit(); ifx_radio_settings_exit(); ifx_audio_settings_exit(); ifx_voicecall_exit(); ifx_ctm_exit(); } OFONO_PLUGIN_DEFINE(ifxmodem, "Infineon modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, ifxmodem_init, ifxmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/swmodem/0000755000015600001650000000000012671500304021531 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/swmodem/swmodem.c0000644000015600001650000000233012671500024023345 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "swmodem.h" static int swmodem_init(void) { sw_gprs_context_init(); return 0; } static void swmodem_exit(void) { sw_gprs_context_exit(); } OFONO_PLUGIN_DEFINE(swmodem, "Sierra modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, swmodem_init, swmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/swmodem/swmodem.h0000644000015600001650000000160312671500024023354 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void sw_gprs_context_init(void); extern void sw_gprs_context_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/swmodem/gprs-context.c0000644000015600001650000001410112671500024024326 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "gattty.h" #include "swmodem.h" static const char *none_prefix[] = { NULL }; struct gprs_context_data { GAtChat *chat; unsigned int active_context; ofono_gprs_context_cb_t cb; void *cb_data; }; static void at_scact_down_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; DBG("ok %d", ok); if (ok) { gcd->cb = cb; gcd->cb_data = cbd->data; } decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_scact_up_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_modem *modem; const char *interface; char buf[64]; DBG("ok %d", ok); if (!ok) { struct ofono_error error; gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); return; } gcd->cb = cb; gcd->cb_data = cbd->data; snprintf(buf, sizeof(buf), "AT!SCPADDR=%u", gcd->active_context); g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL); snprintf(buf, sizeof(buf), "AT+CGCONTRDP=%u", gcd->active_context); g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL); modem = ofono_gprs_context_get_modem(gc); interface = ofono_modem_get_string(modem, "NetworkInterface"); ofono_gprs_context_set_interface(gc, interface); ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE); CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); } static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *ncbd; char buf[64]; DBG("ok %d", ok); if (!ok) { struct ofono_error error; gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); return; } ncbd = g_memdup(cbd, sizeof(struct cb_data)); snprintf(buf, sizeof(buf), "AT!SCACT=1,%u", gcd->active_context); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_scact_up_cb, ncbd, g_free) > 0) return; g_free(ncbd); gcd->active_context = 0; CALLBACK_WITH_FAILURE(cb, cbd->data); } static void sw_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 cb_data *cbd = cb_data_new(cb, data); char buf[OFONO_GPRS_MAX_APN_LENGTH + 128]; int len = 0; DBG("cid %u", ctx->cid); gcd->active_context = ctx->cid; cbd->user = gc; switch (ctx->proto) { case OFONO_GPRS_PROTO_IP: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); break; case OFONO_GPRS_PROTO_IPV6: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"", ctx->cid); break; case OFONO_GPRS_PROTO_IPV4V6: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"", ctx->cid); break; } if (ctx->apn) snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_cgdcont_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void sw_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 cb_data *cbd = cb_data_new(cb, data); char buf[128]; DBG("cid %u", cid); cbd->user = gc; snprintf(buf, sizeof(buf), "AT!SCACT=0,%u", cid); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_scact_down_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static int sw_gprs_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *data) { GAtChat *chat = data; struct gprs_context_data *gcd; DBG(""); gcd = g_try_new0(struct gprs_context_data, 1); if (gcd == NULL) return -ENOMEM; gcd->chat = g_at_chat_clone(chat); ofono_gprs_context_set_data(gc, gcd); return 0; } static void sw_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); g_at_chat_unref(gcd->chat); g_free(gcd); } static struct ofono_gprs_context_driver driver = { .name = "swmodem", .probe = sw_gprs_context_probe, .remove = sw_gprs_context_remove, .activate_primary = sw_gprs_activate_primary, .deactivate_primary = sw_gprs_deactivate_primary, }; void sw_gprs_context_init(void) { ofono_gprs_context_driver_register(&driver); } void sw_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/0000755000015600001650000000000012671500304021664 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/gpds.h0000644000015600001650000002073612671500024023001 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_GPDS_H #define __ISIMODEM_GPDS_H #ifdef __cplusplus extern "C" { #endif #define GPDS_MAX_CONTEXT_COUNT 11 #define GPDS_TIMEOUT 3 #define GPDS_CTX_ACTIVATE_TIMEOUT (6 * 30) /* 6 * T3380 */ #define GPDS_CTX_DEACTIVATE_TIMEOUT (6 * 8) /* 6 * T3390 */ #define GPDS_ATTACH_TIMEOUT (6 * 15) /* 6 * T3310 */ #define GPDS_DETACH_TIMEOUT (6 * 15) /* 6 * T3321 */ #define GPDS_MAX_APN_STRING_LENGTH 100 #define GPDS_MAX_USERNAME_LENGTH 53 #define GPDS_MAX_PASSWORD_LENGTH 53 #define PN_GPDS 0x31 #define PN_PEP_TYPE_GPRS 0x04 #define PN_WRAN 0xB4 enum gpds_message_id { GPDS_LL_CONFIGURE_REQ = 0x00, GPDS_LL_CONFIGURE_RESP = 0x01, GPDS_CONTEXT_ID_CREATE_REQ = 0x02, GPDS_CONTEXT_ID_CREATE_RESP = 0x03, GPDS_CONTEXT_ID_CREATE_IND = 0x04, GPDS_CONTEXT_ID_DELETE_IND = 0x05, GPDS_CONTEXT_CONFIGURE_REQ = 0x06, GPDS_CONTEXT_CONFIGURE_RESP = 0x07, GPDS_CONTEXT_ACTIVATE_REQ = 0x08, GPDS_CONTEXT_ACTIVATE_RESP = 0x09, GPDS_CONTEXT_ACTIVATE_IND = 0x0A, GPDS_CONTEXT_DEACTIVATE_REQ = 0x0B, GPDS_CONTEXT_DEACTIVATE_RESP = 0x0C, GPDS_CONTEXT_DEACTIVATE_IND = 0x0D, GPDS_CONTEXT_MWI_ACT_REQUEST_IND = 0x0E, GPDS_CONTEXT_NWI_ACT_REJECT_REQ = 0x0F, GPDS_CONTEXT_NWI_ACT_REJECT_RESP = 0x10, GPDS_CONFIGURE_REQ = 0x11, GPDS_CONFIGURE_RESP = 0x12, GPDS_ATTACH_REQ = 0x13, GPDS_ATTACH_RESP = 0x14, GPDS_ATTACH_IND = 0x15, GPDS_DETACH_REQ = 0x16, GPDS_DETACH_RESP = 0x17, GPDS_DETACH_IND = 0x18, GPDS_STATUS_REQ = 0x19, GPDS_STATUS_RESP = 0x1A, GPDS_SMS_PDU_SEND_REQ = 0x1B, GPDS_SMS_PDU_SEND_RESP = 0x1C, GPDS_SMS_PDU_RECEIVE_IND = 0x1D, GPDS_TRANSFER_STATUS_IND = 0x1E, GPDS_CONTEXT_ACTIVATE_FAIL_IND = 0x1F, GPDS_LL_BIND_REQ = 0x20, GPDS_LL_BIND_RESP = 0x21, GPDS_CONTEXT_STATUS_REQ = 0x22, GPDS_CONTEXT_STATUS_RESP = 0x23, GPDS_CONTEXT_STATUS_IND = 0x24, GPDS_CONTEXT_ACTIVATING_IND = 0x25, GPDS_CONTEXT_MODIFY_REQ = 0x2A, GPDS_CONTEXT_MODIFY_RESP = 0x2B, GPDS_CONTEXT_MODIFY_IND = 0x2C, GPDS_ATTACH_FAIL_IND = 0x2D, GPDS_CONTEXT_DEACTIVATING_IND = 0x2F, GPDS_CONFIGURATION_INFO_REQ = 0x30, GPDS_CONFIGURATION_INFO_RESP = 0x31, GPDS_CONFIGURATION_INFO_IND = 0x32, GPDS_CONTEXT_AUTH_REQ = 0x33, GPDS_CONTEXT_AUTH_RESP = 0x34, GPDS_TEST_MODE_REQ = 0x35, GPDS_TEST_MODE_RESP = 0x36, GPDS_RADIO_ACTIVITY_IND = 0x37, GPDS_FORCED_READY_STATE_REQ = 0x38, GPDS_FORCED_READY_STATE_RESP = 0x39, GPDS_CONTEXTS_CLEAR_REQ = 0x3A, GPDS_CONTEXTS_CLEAR_RESP = 0x3B, GPDS_MBMS_SERVICE_SELECTION_REQ = 0x3C, GPDS_MBMS_SERVICE_SELECTION_RESP = 0x3D, GPDS_MBMS_STATUS_IND = 0x3E, GPDS_MBMS_CONTEXT_CREATE_REQ = 0x3F, GPDS_MBMS_CONTEXT_CREATE_RESP = 0x40, GPDS_MBMS_CONTEXT_ACTIVATE_REQ = 0x41, GPDS_MBMS_CONTEXT_ACTIVATE_RESP = 0x42, GPDS_MBMS_CONTEXT_DELETE_REQ = 0x43, GPDS_MBMS_CONTEXT_DELETE_RESP = 0x44, GPDS_MBMS_CONTEXT_DELETE_IND = 0x45, GPDS_MBMS_SERVICE_SELECTION_IND = 0x46, GPDS_MBMS_SERVICE_AVAILABLE_IND = 0x47, GPDS_TEST_REQ = 0x48, GPDS_TEST_RESP = 0x49 }; enum gpds_subblock { GPDS_COMP_INFO = 0x00, GPDS_QOS_REQ_INFO = 0x01, GPDS_QOS_MIN_INFO = 0x02, GPDS_QOS_NEG_INFO = 0x03, GPDS_PDP_ADDRESS_INFO = 0x04, GPDS_APN_INFO = 0x05, GPDS_QOS99_REQ_INFO = 0x06, GPDS_QOS99_MIN_INFO = 0x07, GPDS_QOS99_NEG_INFO = 0x08, GPDS_TFT_INFO = 0x09, GPDS_TFT_FILTER_INFO = 0x0A, GPDS_USER_NAME_INFO = 0x0B, GPDS_PASSWORD_INFO = 0x0C, GPDS_PDNS_ADDRESS_INFO = 0x0D, GPDS_SDNS_ADDRESS_INFO = 0x0E, GPDS_CHALLENGE_INFO = 0x0F, GPDS_DNS_ADDRESS_REQ_INFO = 0x90, }; enum gpds_status { GPDS_ERROR = 0x00, GPDS_OK = 0x01, GPDS_FAIL = 0x02 }; enum gpds_isi_cause { GPDS_CAUSE_UNKNOWN = 0x00, GPDS_CAUSE_IMSI = 0x02, GPDS_CAUSE_MS_ILLEGAL = 0x03, GPDS_CAUSE_ME_ILLEGAL = 0x06, GPDS_CAUSE_GPRS_NOT_ALLOWED = 0x07, GPDS_NOT_ALLOWED = 0x08, GPDS_CAUSE_MS_IDENTITY = 0x09, GPDS_CAUSE_DETACH = 0x0A, GPDS_PLMN_NOT_ALLOWED = 0x0B, GPDS_LA_NOT_ALLOWED = 0x0C, GPDS_ROAMING_NOT_ALLOWED = 0x0D, GPDS_CAUSE_GPRS_NOT_ALLOWED_IN_PLMN = 0x0E, GPDS_CAUSE_MSC_NOT_REACH = 0x10, GPDS_CAUSE_PLMN_FAIL = 0x11, GPDS_CAUSE_NETWORK_CONGESTION = 0x16, GPDS_CAUSE_MBMS_BEARER_CAPABILITY_INSUFFICIENT = 0x18, GPDS_CAUSE_LLC_SNDCP_FAILURE = 0x19, GPDS_CAUSE_RESOURCE_INSUFF = 0x1A, GPDS_CAUSE_APN = 0x1B, GPDS_CAUSE_PDP_UNKNOWN = 0x1C, GPDS_CAUSE_AUTHENTICATION = 0x1D, GPDS_CAUSE_ACT_REJECT_GGSN = 0x1E, GPDS_CAUSE_ACT_REJECT = 0x1F, GPDS_CAUSE_SERV_OPT_NOT_SUPPORTED = 0x20, GPDS_CAUSE_SERV_OPT_NOT_SUBSCRIBED = 0x21, GPDS_CAUSE_SERV_OPT_OUT_OF_ORDER = 0x22, GPDS_CAUSE_NSAPI_ALREADY_USED = 0x23, GPDS_CAUSE_DEACT_REGULAR = 0x24, GPDS_CAUSE_QOS = 0x25, GPDS_CAUSE_NETWORK_FAIL = 0x26, GPDS_CAUSE_REACTIVATION_REQ = 0x27, GPDS_CAUSE_FEAT_NOT_SUPPORTED = 0x28, GPDS_CAUSE_TFT_SEMANTIC_ERROR = 0x29, GPDS_CAUSE_TFT_SYNTAX_ERROR = 0x2A, GPDS_CAUSE_CONTEXT_UNKNOWN = 0x2B, GPDS_CAUSE_FILTER_SEMANTIC_ERROR = 0x2C, GPDS_CAUSE_FILTER_SYNTAX_ERROR = 0x2D, GPDS_CAUSE_CONT_WITHOUT_TFT = 0x2E, GPDS_CAUSE_MULTICAST_MEMBERSHIP_TIMEOUT = 0x2F, GPDS_CAUSE_INVALID_MANDATORY_INFO = 0x60, GPDS_CAUSE_MSG_TYPE_NON_EXISTENTOR_NOT_IMPLTD = 0x61, GPDS_CAUSE_MSG_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 0x62, GPDS_CAUSE_IE_NON_EXISTENT_OR_NOT_IMPLEMENTED = 0x63, GPDS_CAUSE_CONDITIONAL_IE_ERROR = 0x64, GPDS_CUASEMSG_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 0x65, GPDS_CAUSE_UNSPECIFIED = 0x6F, GPDS_CAUSE_APN_INCOMPATIBLE_WITH_CURR_CTXT = 0x70, GPDS_CAUSE_FDN = 0xA0, GPDS_CAUSE_USER_ABORT = 0xA1, GPDS_CAUSE_CS_INACTIVE = 0xA2, GPDS_CAUSE_CSD_OVERRIDE = 0xA3, GPDS_CAUSE_APN_CONTROL = 0xA4, GPDS_CAUSE_CALL_CONTROL = 0xA5, GPDS_CAUSE_TEMPERATURE_LIMIT = 0xA6, GPDS_CAUSE_RETRY_COUNTER_EXPIRED = 0xC8, GPDS_CAUSE_NO_CONNECTION = 0xC9, GPDS_CAUSE_DETACHED = 0xF5, GPDS_CAUSE_NO_SERVICE_POWER_SAVE = 0xF7, GPDS_CAUSE_SIM_REMOVED = 0xF9, GPDS_CAUSE_POWER_OFF = 0xFA, GPDS_CAUSE_LAI_FORBIDDEN_NATIONAL_ROAM_LIST = 0xFB, GPDS_CAUSE_LAI_FORBIDDEN_REG_PROVISION_LIST = 0xFC, GPDS_CAUSE_ACCESS_BARRED = 0xFD, GPDS_CAUSE_FATAL_FAILURE = 0xFE, GPDS_CAUSE_AUT_FAILURE = 0xFF, }; enum gpds_transfer_status { GPDS_TRANSFER_NOT_AVAIL = 0x00, GPDS_TRANSFER_AVAIL = 0x01, }; enum gpds_transfer_cause { GPDS_TRANSFER_CAUSE_ATTACHED = 0x02, GPDS_TRANSFER_CAUSE_DETACHED = 0x03, GPDS_TRANSFER_CAUSE_RESUMED = 0x04, GPDS_TRANSFER_CAUSE_SUSPENDED_NO_COVERAGE = 0x05, GPDS_TRANSFER_CAUSE_SUSPENDED_CALL_SMS = 0x07, GPDS_TRANSFER_CAUSE_SUSPENDED_CALL = 0x08, GPDS_TRANSFER_CAUSE_SUSPENDED_RAU = 0x09, GPDS_TRANSFER_CAUSE_SUSPENDED_LU = 0x0A, GPDS_TRANSFER_CAUSE_DSAC_RESTRICTION = 0x0B, }; enum gpds_context_type { GPDS_CONT_TYPE_NORMAL = 0x00, GPDS_CONT_TYPE_NWI = 0x01, GPDS_CONT_TYPE_SEC = 0x02 }; enum gpds_ppp_mode { GPDS_LL_FRAMED_PPP = 0x00, GPDS_LL_NONFRAMED_PPP = 0x01, GPDS_LL_PLAIN = 0x02 }; enum gpds_pdp_type { GPDS_PDP_TYPE_PPP = 0x01, GPDS_PDP_TYPE_IPV4 = 0x21, GPDS_PDP_TYPE_IPV6 = 0x57, GPDS_PDP_TYPE_DEFAULT = 0xFF }; enum gpds_request_mode { GPDS_FOLLOW_OFF = 0x00, GPDS_FOLLOW_ON = 0x01 }; enum gpds_attach_status { GPDS_DETACHED = 0x00, GPDS_ATTACHED = 0x01 }; enum gpds_attach_mode { GPDS_ATTACH_MODE_MANUAL = 0x00, GPDS_ATTACH_MODE_AUTOMATIC = 0x01, GPDS_ATTACH_MODE_DEFAULT = 0xFF }; enum gpds_mt_act_mode { GPDS_MT_ACT_MODE_REJECT = 0x00, GPDS_MT_ACT_MODE_ACCEPT = 0x01, GPDS_MT_ACT_MODE_DEFAULT = 0xFF }; enum gpds_classc_mode { GPDS_CLASSC_MODE_GPRS = 0x00, GPDS_CLASSC_MODE_GSM = 0x01, GPDS_CLASSC_MODE_DEFAULT = 0xFF }; enum gpds_aol_context { GPDS_AOL_CTX_NOT_ACTIVE = 0x00, GPDS_AOL_CTX_HPLMN_ACTIVE = 0x01, GPDS_AOL_CTX_VPLMN_ACTIVE = 0x02, GPDS_AOL_CTX_ACTIVE = 0x03, GPDS_AOL_CTX_DEFAULT = 0xFF }; #ifdef __cplusplus }; #endif #endif /* !__ISIMODEM_GPDS_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/network.h0000644000015600001650000001553612671500024023537 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_NETWORK_H #define __ISIMODEM_NETWORK_H #ifdef __cplusplus extern "C" { #endif #define PN_NETWORK 0x0A #define PN_MODEM_NETWORK 0xC8 #define NETWORK_SCAN_TIMEOUT 180 #define NETWORK_SET_TIMEOUT 240 #define NET_INVALID_TIME 0x64 enum net_message_id { NET_MODEM_REG_STATUS_GET_REQ = 0x00, NET_MODEM_REG_STATUS_GET_RESP = 0x01, NET_MODEM_REG_STATUS_IND = 0x02, NET_MODEM_AVAILABLE_GET_REQ = 0x03, NET_MODEM_AVAILABLE_GET_RESP = 0x04, NET_SET_REQ = 0x07, NET_SET_RESP = 0x08, NET_RSSI_GET_REQ = 0x0B, NET_RSSI_GET_RESP = 0x0C, NET_CS_STATE_IND = 0x19, NET_RSSI_IND = 0x1E, NET_CIPHERING_IND = 0x20, NET_TIME_IND = 0x27, NET_OLD_OPER_NAME_READ_REQ = 0x28, NET_OLD_OPER_NAME_READ_RESP = 0x29, NET_CHANNEL_INFO_IND = 0x2C, NET_RAT_IND = 0x35, NET_RAT_REQ = 0x36, NET_RAT_RESP = 0x37, NET_CS_STATE_REQ = 0x3A, NET_CS_STATE_RESP = 0x3B, NET_CELL_INFO_GET_REQ = 0x40, NET_CELL_INFO_GET_RESP = 0x41, NET_CELL_INFO_IND = 0x42, NET_NITZ_NAME_IND = 0x43, NET_NW_ACCESS_CONF_REQ = 0x48, NET_NW_ACCESS_CONF_RESP = 0x49, NET_REG_STATUS_GET_REQ = 0xE0, NET_REG_STATUS_GET_RESP = 0xE1, NET_REG_STATUS_IND = 0xE2, NET_AVAILABLE_GET_REQ = 0xE3, NET_AVAILABLE_GET_RESP = 0xE4, NET_OPER_NAME_READ_REQ = 0xE5, NET_OPER_NAME_READ_RESP = 0xE6, }; enum net_subblock { NET_REG_INFO_COMMON = 0x00, NET_MODEM_AVAIL_NETWORK_INFO_COMMON = 0x01, NET_OPERATOR_INFO_COMMON = 0x02, NET_RSSI_CURRENT = 0x04, NET_GSM_REG_INFO = 0x09, NET_DETAILED_NETWORK_INFO = 0x0B, NET_MODEM_DETAILED_NETWORK_INFO = 0x0B, NET_GSM_OPERATOR_INFO = 0x0C, NET_TIME_INFO = 0x10, NET_GSM_BAND_INFO = 0x11, NET_RAT_INFO = 0x2C, NET_GSM_CELL_INFO = 0x46, NET_WCDMA_CELL_INFO = 0x47, NET_FULL_NITZ_NAME = 0x48, NET_SHORT_NITZ_NAME = 0x49, NET_REGISTRATION_CONF_INFO = 0x55, NET_ROAMING_CONF_INFO = 0x56, NET_REGISTRATION_CONF1_INFO = 0x59, NET_ROAMING_CONF1_INFO = 0x5A, NET_AVAIL_NETWORK_INFO_COMMON = 0xE1, NET_OPER_NAME_INFO = 0xE7, }; enum net_reg_status { NET_REG_STATUS_HOME = 0x00, NET_REG_STATUS_ROAM = 0x01, NET_REG_STATUS_ROAM_BLINK = 0x02, NET_REG_STATUS_NOSERV = 0x03, NET_REG_STATUS_NOSERV_SEARCHING = 0x04, NET_REG_STATUS_NOSERV_NOTSEARCHING = 0x05, NET_REG_STATUS_NOSERV_NOSIM = 0x06, NET_REG_STATUS_POWER_OFF = 0x08, NET_REG_STATUS_NSPS = 0x09, NET_REG_STATUS_NSPS_NO_COVERAGE = 0x0A, NET_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW = 0x0B, }; enum net_network_status { NET_OPER_STATUS_UNKNOWN = 0x00, NET_OPER_STATUS_AVAILABLE = 0x01, NET_OPER_STATUS_CURRENT = 0x02, NET_OPER_STATUS_FORBIDDEN = 0x03, }; enum net_network_pref { NET_GSM_HOME_PLMN = 0x00, NET_GSM_PREFERRED_PLMN = 0x01, NET_GSM_FORBIDDEN_PLMN = 0x02, NET_GSM_OTHER_PLMN = 0x03, NET_GSM_NO_PLMN_AVAIL = 0x04, }; enum net_umts_available { NET_UMTS_NOT_AVAILABLE = 0x00, NET_UMTS_AVAILABLE = 0x01, }; enum net_band_info { NET_GSM_BAND_900_1800 = 0x00, NET_GSM_BAND_850_1900 = 0x01, NET_GSM_BAND_INFO_NOT_AVAIL = 0x02, NET_GSM_BAND_ALL_SUPPORTED_BANDS = 0x03, NET_GSM_BAND_850_LOCKED = 0xB0, NET_GSM_BAND_900_LOCKED = 0xA0, NET_GSM_BAND_1800_LOCKED = 0xA1, NET_GSM_BAND_1900_LOCKED = 0xB1, }; enum net_gsm_cause { NET_GSM_IMSI_UNKNOWN_IN_HLR = 0x02, NET_GSM_ILLEGAL_MS = 0x03, NET_GSM_IMSI_UNKNOWN_IN_VLR = 0x04, NET_GSM_IMEI_NOT_ACCEPTED = 0x05, NET_GSM_ILLEGAL_ME = 0x06, NET_GSM_GPRS_SERVICES_NOT_ALLOWED = 0x07, NET_GSM_GPRS_AND_NON_GPRS_NA = 0x08, NET_GSM_MS_ID_CANNOT_BE_DERIVED = 0x09, NET_GSM_IMPLICITLY_DETACHED = 0x0A, NET_GSM_PLMN_NOT_ALLOWED = 0x0B, NET_GSM_LA_NOT_ALLOWED = 0x0C, NET_GSM_ROAMING_NOT_IN_THIS_LA = 0x0D, NET_GSM_GPRS_SERV_NA_IN_THIS_PLMN = 0x0E, NET_GSM_NO_SUITABLE_CELLS_IN_LA = 0x0F, NET_GSM_MSC_TEMP_NOT_REACHABLE = 0x10, NET_GSM_NETWORK_FAILURE = 0x11, NET_GSM_MAC_FAILURE = 0x14, NET_GSM_SYNCH_FAILURE = 0x15, NET_GSM_CONGESTION = 0x16, NET_GSM_AUTH_UNACCEPTABLE = 0x17, NET_GSM_SERV_OPT_NOT_SUPPORTED = 0x20, NET_GSM_SERV_OPT_NOT_SUBSCRIBED = 0x21, NET_GSM_SERV_TEMP_OUT_OF_ORDER = 0x22, NET_GSM_RETRY_ENTRY_NEW_CELL_LOW = 0x30, NET_GSM_RETRY_ENTRY_NEW_CELL_HIGH = 0x3F, NET_GSM_SEMANTICALLY_INCORRECT = 0x5F, NET_GSM_INVALID_MANDATORY_INFO = 0x60, NET_GSM_MSG_TYPE_NONEXISTENT = 0x61, NET_GSM_CONDITIONAL_IE_ERROR = 0x64, NET_GSM_MSG_TYPE_WRONG_STATE = 0x65, NET_GSM_PROTOCOL_ERROR_UNSPECIFIED = 0x6F, }; enum net_cs_type { NET_CS_GSM = 0x00, }; enum net_rat_name { NET_GSM_RAT = 0x01, NET_UMTS_RAT = 0x02 }; enum net_rat_type { NET_CURRENT_RAT = 0x00, NET_SUPPORTED_RATS = 0x01, }; enum net_measurement_type { NET_CURRENT_CELL_RSSI = 0x02, }; enum net_search_mode { NET_MANUAL_SEARCH = 0x00, }; enum net_oper_name_type { NET_HARDCODED_LATIN_OPER_NAME = 0x00, }; enum net_select_mode { NET_SELECT_MODE_UNKNOWN = 0x00, NET_SELECT_MODE_MANUAL = 0x01, NET_SELECT_MODE_AUTOMATIC = 0x02, NET_SELECT_MODE_USER_RESELECTION = 0x03, NET_SELECT_MODE_NO_SELECTION = 0x04, }; enum net_cs_states { NET_CS_INACTIVE = 0x00, NET_CS_ACTIVE = 0x01, }; enum net_isi_cause { NET_CAUSE_OK = 0x00, NET_CAUSE_COMMUNICATION_ERROR = 0x01, NET_CAUSE_INVALID_PARAMETER = 0x02, NET_CAUSE_NO_SIM = 0x03, NET_CAUSE_SIM_NOT_YET_READY = 0x04, NET_CAUSE_NET_NOT_FOUND = 0x05, NET_CAUSE_REQUEST_NOT_ALLOWED = 0x06, NET_CAUSE_CALL_ACTIVE = 0x07, NET_CAUSE_SERVER_BUSY = 0x08, NET_CAUSE_SECURITY_CODE_REQUIRED = 0x09, NET_CAUSE_NOTHING_TO_CANCEL = 0x0A, NET_CAUSE_UNABLE_TO_CANCEL = 0x0B, NET_CAUSE_NETWORK_FORBIDDEN = 0x0C, NET_CAUSE_REQUEST_REJECTED = 0x0D, NET_CAUSE_CS_NOT_SUPPORTED = 0x0E, NET_CAUSE_PAR_INFO_NOT_AVAILABLE = 0x0F, NET_CAUSE_NOT_DONE = 0x10, NET_CAUSE_NO_SELECTED_NETWORK = 0x11, NET_CAUSE_REQUEST_INTERRUPTED = 0x12, NET_CAUSE_TOO_BIG_INDEX = 0x14, NET_CAUSE_MEMORY_FULL = 0x15, NET_CAUSE_SERVICE_NOT_ALLOWED = 0x16, NET_CAUSE_NOT_SUPPORTED_IN_TECH = 0x17, }; #ifdef __cplusplus }; #endif #endif /* !__ISIMODEM_NETWORK_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/mtc.h0000644000015600001650000000602012671500024022615 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_MTC_H #define __ISIMODEM_MTC_H #ifdef __cplusplus extern "C" { #endif #define PN_MTC 0x15 #define PN_MODEM_MCE 0xC2 #define MTC_TIMEOUT 5 #define MTC_STATE_REQ_TIMEOUT (6 + 5) enum mce_message_id { MCE_MODEM_STATE_IND = 0x00, MCE_MODEM_STATE_QUERY_REQ = 0x01, MCE_MODEM_STATE_QUERY_RESP = 0x02, MCE_RF_STATE_REQ = 0x03, MCE_RF_STATE_RESP = 0x04, MCE_RF_STATE_IND = 0x05, MCE_RF_STATE_QUERY_REQ = 0x06, MCE_RF_STATE_QUERY_RESP = 0x07, MCE_POWER_OFF_REQ = 0x08, MCE_POWER_OFF_RESP = 0x09 }; enum mce_rf_state { MCE_RF_OFF = 0x00, MCE_RF_ON = 0x01 }; enum mce_status_info { MCE_OK = 0x00, MCE_FAIL = 0x01, MCE_ALREADY_ACTIVE = 0x06, MCE_TRANSITION_ONGOING = 0x16 }; enum mce_modem_state { MCE_NORMAL = 0x00, MCE_LOCAL = 0x01, MCE_SW_RESET = 0x80, MCE_POWER_OFF = 0x81 }; enum mce_isi_action { MCE_START = 0x03, MCE_READY = 0x04 }; enum mtc_isi_cause { MTC_OK = 0x00, MTC_FAIL = 0x01, MTC_NOT_ALLOWED = 0x02, MTC_STATE_TRANSITION_GOING_ON = 0x05, MTC_ALREADY_ACTIVE = 0x06, MTC_SERVICE_DISABLED = 0x10, MTC_NOT_READY_YET = 0x13, MTC_NOT_SUPPORTED = 0x14, MTC_TRANSITION_ONGOING = 0x16, MTC_RESET_REQUIRED = 0x17, }; enum mtc_isi_action { MTC_START = 0x03, MTC_READY = 0x04, MTC_NOS_READY = 0x0C, MTC_SOS_START = 0x11, MTC_SOS_READY = 0x12, }; enum mtc_message_id { MTC_STATE_REQ = 0x01, MTC_STATE_QUERY_REQ = 0x02, MTC_POWER_OFF_REQ = 0x03, MTC_POWER_ON_REQ = 0x04, MTC_STARTUP_SYNQ_REQ = 0x0B, MTC_SHUTDOWN_SYNC_REQ = 0x12, MTC_STATE_RESP = 0x64, MTC_STATE_QUERY_RESP = 0x65, MTC_POWER_OFF_RESP = 0x66, MTC_POWER_ON_RESP = 0x67, MTC_STARTUP_SYNQ_RESP = 0x6E, MTC_SHUTDOWN_SYNC_RESP = 0x75, MTC_STATE_INFO_IND = 0xC0, }; enum mtc_modem_state { MTC_STATE_NONE = -1, /* Used only internally */ MTC_POWER_OFF = 0x00, MTC_NORMAL = 0x01, MTC_CHARGING = 0x02, MTC_ALARM = 0x03, MTC_TEST = 0x04, MTC_LOCAL = 0x05, MTC_WARRANTY = 0x06, MTC_RELIABILITY = 0x07, MTC_SELFTEST_FAIL = 0x08, MTC_SWDL = 0x09, MTC_RF_INACTIVE = 0x0A, MTC_ID_WRITE = 0x0B, MTC_DISCHARGING = 0x0C, MTC_DISK_WIPE = 0x0D, MTC_SW_RESET = 0x0E, MTC_CMT_ONLY_MODE = 0xFF, }; #ifdef __cplusplus }; #endif #endif /* __ISIMODEM_MTC_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/isimodem.h0000644000015600001650000000403312671500024023642 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ extern void isi_phonebook_init(void); extern void isi_phonebook_exit(void); extern void isi_devinfo_init(void); extern void isi_devinfo_exit(void); extern void isi_netreg_init(void); extern void isi_netreg_exit(void); extern void isi_voicecall_init(void); extern void isi_voicecall_exit(void); extern void isi_sms_init(void); extern void isi_sms_exit(void); extern void isi_cbs_init(void); extern void isi_cbs_exit(void); extern void isi_sim_init(void); extern void isi_sim_exit(void); extern void isi_ussd_init(void); extern void isi_ussd_exit(void); extern void isi_call_forwarding_init(void); extern void isi_call_forwarding_exit(void); extern void isi_call_settings_init(void); extern void isi_call_settings_exit(void); extern void isi_call_barring_init(void); extern void isi_call_barring_exit(void); extern void isi_call_meter_init(void); extern void isi_call_meter_exit(void); extern void isi_radio_settings_init(void); extern void isi_radio_settings_exit(void); extern void isi_gprs_init(void); extern void isi_gprs_exit(void); extern void isi_gprs_context_init(void); extern void isi_gprs_context_exit(void); extern void isi_audio_settings_init(void); extern void isi_audio_settings_exit(void); extern void isi_uicc_init(void); extern void isi_uicc_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/sim.h0000644000015600001650000001371112671500024022627 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_SIM_H #define __ISIMODEM_SIM_H #ifdef __cplusplus extern "C" { #endif #define PN_SIM 0x09 #define SIM_TIMEOUT 5 #define PN_SECURITY 0x08 #define SIM_MAX_IMSI_LENGTH 15 enum sim_isi_cause { SIM_SERV_NOT_AVAIL = 0x00, SIM_SERV_OK = 0x01, SIM_SERV_PIN_VERIFY_REQUIRED = 0x02, SIM_SERV_PIN_REQUIRED = 0x03, SIM_SERV_SIM_BLOCKED = 0x04, SIM_SERV_SIM_PERMANENTLY_BLOCKED = 0x05, SIM_SERV_SIM_DISCONNECTED = 0x06, SIM_SERV_SIM_REJECTED = 0x07, SIM_SERV_LOCK_ACTIVE = 0x08, SIM_SERV_AUTOLOCK_CLOSED = 0x09, SIM_SERV_AUTOLOCK_ERROR = 0x0A, SIM_SERV_INIT_OK = 0x0B, SIM_SERV_INIT_NOT_OK = 0x0C, SIM_SERV_WRONG_OLD_PIN = 0x0D, SIM_SERV_PIN_DISABLED = 0x0E, SIM_SERV_COMMUNICATION_ERROR = 0x0F, SIM_SERV_UPDATE_IMPOSSIBLE = 0x10, SIM_SERV_NO_SECRET_CODE_IN_SIM = 0x11, SIM_SERV_PIN_ENABLE_OK = 0x12, SIM_SERV_PIN_DISABLE_OK = 0x13, SIM_SERV_WRONG_UNBLOCKING_KEY = 0x15, SIM_SERV_ILLEGAL_NUMBER = 0x2E, SIM_SERV_NOT_OK = 0x1C, SIM_SERV_PN_LIST_ENABLE_OK = 0x1E, SIM_SERV_PN_LIST_DISABLE_OK = 0x1F, SIM_SERV_NO_PIN = 0x20, SIM_SERV_PIN_VERIFY_OK = 0x21, SIM_SERV_PIN_BLOCKED = 0x22, SIM_SERV_PIN_PERM_BLOCKED = 0x23, SIM_SERV_DATA_NOT_AVAIL = 0x24, SIM_SERV_IN_HOME_ZONE = 0x25, SIM_SERV_STATE_CHANGED = 0x27, SIM_SERV_INF_NBR_READ_OK = 0x28, SIM_SERV_INF_NBR_READ_NOT_OK = 0x29, SIM_SERV_IMSI_EQUAL = 0x2A, SIM_SERV_IMSI_NOT_EQUAL = 0x2B, SIM_SERV_INVALID_LOCATION = 0x2C, SIM_SERV_STA_SIM_REMOVED = 0x35, SIM_SERV_SECOND_SIM_REMOVED_CS = 0x36, SIM_SERV_CONNECTED_INDICATION_CS = 0x37, SIM_SERV_SECOND_SIM_CONNECTED_CS = 0x38, SIM_SERV_PIN_RIGHTS_LOST_IND_CS = 0x39, SIM_SERV_PIN_RIGHTS_GRANTED_IND_CS = 0x3A, SIM_SERV_INIT_OK_CS = 0x3B, SIM_SERV_INIT_NOT_OK_CS = 0x3C, SIM_FDN_ENABLED = 0x19, SIM_FDN_DISABLED = 0x1A, SIM_SERV_INVALID_FILE = 0x45, SIM_SERV_DATA_AVAIL = 0x4F, SIM_SERV_ICC_EQUAL = 0x49, SIM_SERV_ICC_NOT_EQUAL = 0x4A, SIM_SERV_SIM_NOT_INITIALISED = 0x4B, SIM_SERV_SERVICE_NOT_AVAIL = 0x50, SIM_SERV_FDN_STATUS_ERROR = 0x57, SIM_SERV_FDN_CHECK_PASSED = 0x58, SIM_SERV_FDN_CHECK_FAILED = 0x59, SIM_SERV_FDN_CHECK_DISABLED = 0x5A, SIM_SERV_FDN_CHECK_NO_FDN_SIM = 0x5B, SIM_STA_ISIM_AVAILEBLE_PIN_REQUIRED = 0x5C, SIM_STA_ISIM_AVAILEBLE = 0x5D, SIM_STA_USIM_AVAILEBLE = 0x5E, SIM_STA_SIM_AVAILEBLE = 0x5F, SIM_STA_ISIM_NOT_INITIALIZED = 0x60, SIM_STA_IMS_READY = 0x61, SIM_STA_APP_DATA_READ_OK = 0x96, SIM_STA_APP_ACTIVATE_OK = 0x97, SIM_STA_APP_ACTIVATE_NOT_OK = 0x98, SIM_SERV_NOT_DEFINED = 0xF9, SIM_SERV_NOSERVICE = 0xFA, SIM_SERV_NOTREADY = 0xFB, SIM_SERV_ERROR = 0xFC, SIM_SERV_CIPHERING_INDICATOR_DISPLAY_REQUIRED = 0x30, SIM_SERV_CIPHERING_INDICATOR_DISPLAY_NOT_REQUIRED = 0x31, SIM_SERV_FILE_NOT_AVAILABLE = 0x4D }; enum sim_subblock { SIM_PB_INFO_REQUEST = 0xE4, SIM_PB_STATUS = 0xFB, SIM_PB_LOCATION = 0xFE, SIM_PB_LOCATION_SEARCH = 0xFF, }; enum sim_pb_type { SIM_PB_ADN = 0xC8, }; enum sim_pb_tag { SIM_PB_ANR = 0xCA, SIM_PB_EMAIL = 0xDD, SIM_PB_SNE = 0xF7, }; enum sim_message_id { SIM_NETWORK_INFO_REQ = 0x19, SIM_NETWORK_INFO_RESP = 0x1A, SIM_IMSI_REQ_READ_IMSI = 0x1D, SIM_IMSI_RESP_READ_IMSI = 0x1E, SIM_SERV_PROV_NAME_REQ = 0x21, SIM_SERV_PROV_NAME_RESP = 0x22, SIM_DYNAMIC_FLAGS_REQ = 0x29, SIM_DYNAMIC_FLAGS_RESP = 0x2A, SIM_READ_FIELD_REQ = 0xBA, SIM_READ_FIELD_RESP = 0xBB, SIM_SMS_REQ = 0xBC, SIM_SMS_RESP = 0xBD, SIM_STATUS_REQ = 0xC0, SIM_STATUS_RESP = 0xC1, SIM_PB_REQ_SIM_PB_READ = 0xDC, SIM_PB_RESP_SIM_PB_READ = 0xDD, SIM_SERVER_READY_IND = 0xED, SIM_IND = 0xEF, }; enum sim_service_type { SIM_ST_CARD_STATUS = 0x00, SIM_ST_PIN = 0x01, SIM_ST_ALL_SERVICES = 0x05, SIM_ST_INFO = 0x0D, SIM_PB_READ = 0x0F, SIM_ST_CAT_SUPPORT_ENABLE = 0x15, SIM_ST_CAT_SUPPORT_DISABLE = 0x16, SIM_ST_READ_SERV_PROV_NAME = 0x2C, READ_IMSI = 0x2D, READ_HPLMN = 0x2F, READ_DYN_FLAGS = 0x35, READ_PARAMETER = 0x52, UPDATE_PARAMETER = 0x53, ICC = 0x66, }; #define SEC_CODE_MAX_LENGTH 0x0A enum sec_message_id { SEC_CODE_STATE_REQ = 0x01, SEC_CODE_STATE_OK_RESP = 0x02, SEC_CODE_STATE_FAIL_RESP = 0x03, SEC_CODE_CHANGE_REQ = 0x04, SEC_CODE_CHANGE_OK_RESP = 0x05, SEC_CODE_CHANGE_FAIL_RESP = 0x06, SEC_CODE_VERIFY_REQ = 0x07, SEC_CODE_VERIFY_OK_RESP = 0x08, SEC_CODE_VERIFY_FAIL_RESP = 0x09, SEC_STATE_REQ = 0x11, SEC_STATE_RESP = 0x12, }; enum sec_code_id_info { SEC_CODE_PIN = 0x02, SEC_CODE_PUK = 0x03, SEC_CODE_PIN2 = 0x04, SEC_CODE_PUK2 = 0x05, }; enum sec_code_state_info { SEC_CODE_DISABLE = 0x00, SEC_CODE_ENABLE = 0x01, SEC_CODE_STATE_QUERY = 0x04, }; enum sec_state_cause_info { SEC_CAUSE_PIN_REQUIRED = 0x02, SEC_CAUSE_PUK_REQUIRED = 0x03, SEC_STARTUP_OK = 0x05, SEC_STARTUP_ONGOING = 0x07, SEC_CAUSE_CODE_BLOCKED = 0x08, SEC_CAUSE_NO_SIM = 0x16, SEC_CAUSE_SIM_REJECTED = 0x1A, SEC_CAUSE_INVALID_SIM = 0x1E, }; #ifdef __cplusplus }; #endif #endif /* __ISIMODEM_SIM_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/sms.c0000644000015600001650000006224412671500024022641 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 ST-Ericsson AB. * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smsutil.h" #include "isimodem.h" #include "isiutil.h" #include "sms.h" #include "sim.h" #include "debug.h" /* This is a straightforward copy of the EF_smsp structure */ struct sim_efsmsp{ uint8_t absent; uint8_t tp_pid; uint8_t tp_dcs; uint8_t tp_vp; uint8_t dst[12]; uint8_t sca[12]; uint8_t alphalen; uint8_t filler[3]; uint16_t alpha[17]; }; /* Sub-block used by PN_SMS */ struct sms_params { uint8_t location; uint8_t absent; uint8_t tp_pid; uint8_t tp_dcs; uint8_t dst[12]; uint8_t sca[12]; uint8_t tp_vp; uint8_t alphalen; uint8_t filler[2]; uint16_t alpha[17]; }; struct sms_report { uint8_t type; uint8_t cause; uint8_t ref; }; struct sms_status { uint8_t status; uint8_t ref; uint8_t route; uint8_t cseg; /* Current segment */ uint8_t tseg; /* Total segments */ }; struct sms_addr { uint8_t type; uint8_t len; uint8_t *data; }; struct sms_common { uint8_t len; uint8_t *data; }; struct sms_data { GIsiClient *client; GIsiClient *sim; GIsiVersion version; struct sim_efsmsp params; }; static uint8_t bearer_to_cs_pref(int bearer) { switch (bearer) { case 0: return SMS_ROUTE_NOT_AVAILABLE; case 1: return SMS_ROUTE_PRIORITY_1; case 2: return SMS_ROUTE_PRIORITY_2; case 3: return SMS_ROUTE_PRIORITY_1; } return SMS_ROUTE_NOT_AVAILABLE; } static uint8_t bearer_to_ps_pref(int bearer) { switch (bearer) { case 0: return SMS_ROUTE_PRIORITY_1; case 1: return SMS_ROUTE_NOT_AVAILABLE; case 2: return SMS_ROUTE_PRIORITY_1; case 3: return SMS_ROUTE_PRIORITY_2; } return SMS_ROUTE_NOT_AVAILABLE; } static int cs_ps_pref_to_bearer(uint8_t cs, uint8_t ps) { if (cs == SMS_ROUTE_NOT_AVAILABLE && ps == SMS_ROUTE_PRIORITY_1) return 0; if (cs == SMS_ROUTE_PRIORITY_1 && ps == SMS_ROUTE_NOT_AVAILABLE) return 1; if (cs == SMS_ROUTE_PRIORITY_2 && ps == SMS_ROUTE_PRIORITY_1) return 2; if (cs == SMS_ROUTE_PRIORITY_1 && ps == SMS_ROUTE_PRIORITY_2) return 3; return 0; } static gboolean check_sim(const GIsiMessage *msg, uint8_t msgid, uint8_t service) { uint8_t type; uint8_t cause; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", g_isi_msg_strerror(msg)); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", sms_message_id_name(g_isi_msg_id(msg))); return FALSE; } if (!g_isi_msg_data_get_byte(msg, 0, &type)) return FALSE; if (type != service) { DBG("Unexpected service type: 0x%02X", type); return FALSE; } if (!g_isi_msg_data_get_byte(msg, 1, &cause)) return FALSE; if (cause != SIM_SERV_OK) { DBG("Request failed: %s", sim_isi_cause_name(cause)); return FALSE; } return TRUE; } static gboolean check_sms(const GIsiMessage *msg, uint8_t msgid, int expect) { uint8_t cause; int pos; /* * Quirk for the cause code position in the response. More * recent versions of the API use 16bit subblock IDs, causing * the cause to be bumped forward by one byte. */ if (ISI_VERSION_AT_LEAST(msg->version, 9, 1)) pos = 1; else pos = 0; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", g_isi_msg_strerror(msg)); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", sms_message_id_name(g_isi_msg_id(msg))); return FALSE; } if (expect == -1) return TRUE; if (!g_isi_msg_data_get_byte(msg, pos, &cause)) { DBG("Unable to parse cause"); return FALSE; } if (cause == expect) return TRUE; if (cause == SMS_ERR_PP_RESERVED) { DBG("Request failed: 0x%02"PRIx8" (%s).\n\n Unable to " "bootstrap SMS routing.\n It appears some other " "component is already\n registered as the SMS " "routing endpoint.\n As a consequence, " "only sending SMSs is going to work.\n\n", cause, sms_isi_cause_name(cause)); return TRUE; } DBG("Request failed: %s", sms_isi_cause_name(cause)); return FALSE; } static void sca_sim_query_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; struct ofono_sms *sms = cbd->user; struct sms_data *sd = ofono_sms_get_data(sms); ofono_sms_sca_query_cb_t cb = cbd->cb; struct ofono_phone_number sca; struct sms_params *info; size_t len = sizeof(struct sms_params); uint8_t bcd_len; if (!check_sim(msg, SIM_SMS_RESP, READ_PARAMETER)) goto error; if (!g_isi_msg_data_get_struct(msg, 2, (const void **) &info, len)) goto error; if (info->alphalen > 17) info->alphalen = 17; else if (info->alphalen < 1) info->alphalen = 1; info->alpha[info->alphalen - 1] = '\0'; sd->params.absent = info->absent; sd->params.tp_pid = info->tp_pid; sd->params.tp_dcs = info->tp_dcs; sd->params.tp_vp = info->tp_vp; memcpy(sd->params.dst, info->dst, sizeof(sd->params.dst)); memcpy(sd->params.sca, info->sca, sizeof(sd->params.sca)); sd->params.alphalen = info->alphalen; memcpy(sd->params.alpha, info->alpha, sizeof(sd->params.alpha)); /* * Bitmask indicating absence of parameters -- * If second bit is set it indicates that the SCA is absent */ if (info->absent & 0x2) goto error; bcd_len = info->sca[0]; if (bcd_len == 0 || bcd_len > 12) goto error; extract_bcd_number(info->sca + 2, bcd_len - 1, sca.number); sca.type = 0x80 | info->sca[1]; CALLBACK_WITH_SUCCESS(cb, &sca, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static gboolean sca_sim_query(GIsiClient *client, void *data, GDestroyNotify notify) { const uint8_t msg[] = { SIM_SMS_REQ, READ_PARAMETER, 1, /* Location, default is 1 */ }; return g_isi_client_send(client, msg, sizeof(msg), sca_sim_query_resp_cb, data, notify); } static void isi_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 isi_cb_data *cbd = isi_cb_data_new(sms, cb, data); if (cbd == NULL || sd->sim == NULL) goto error; if (sca_sim_query(sd->sim, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, NULL, data); g_free(cbd); } static void sca_sim_set_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_sms_sca_set_cb_t cb = cbd->cb; if (!check_sim(msg, SIM_SMS_RESP, UPDATE_PARAMETER)) { CALLBACK_WITH_FAILURE(cb, cbd->data); return; } CALLBACK_WITH_SUCCESS(cb, cbd->data); } static gboolean sca_sim_set(GIsiClient *client, struct sim_efsmsp *params, const struct ofono_phone_number *sca, void *data, GDestroyNotify notify) { uint8_t msg[] = { SIM_SMS_REQ, UPDATE_PARAMETER, 1, /* Location, default is 1 */ }; struct iovec iov[2] = { { msg, sizeof(msg) }, { params, sizeof(struct sim_efsmsp) }, }; uint8_t *bcd; bcd = params->sca; params->absent &= ~SMS_PI_SERVICE_CENTER_ADDRESS; encode_bcd_number(sca->number, bcd + 2); bcd[0] = 1 + (strlen(sca->number) + 1) / 2; bcd[1] = sca->type & 0xFF; return g_isi_client_vsend(client, iov, 2, sca_sim_set_resp_cb, data, notify); } static void isi_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 isi_cb_data *cbd = isi_cb_data_new(sms, cb, data); if (cbd == NULL || sd->sim == NULL) goto error; if (sca_sim_set(sd->sim, &sd->params, sca, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void submit_failure_debug(struct sms_report *report) { const char *cause; if (report->type == SMS_CAUSE_TYPE_COMMON) cause = sms_isi_cause_name(report->cause); else cause = sms_gsm_cause_name(report->cause); DBG("Message 0x%02"PRIx8" failed: %s", report->ref, cause); } static void submit_tpdu_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_sms_submit_cb_t cb = cbd->cb; struct sms_report *report; size_t len = sizeof(struct sms_report); if (!check_sms(msg, SMS_MESSAGE_SEND_RESP, -1)) goto error; if (g_isi_msg_data_len(msg) < len) goto error; if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &report, len)) goto error; if (report->type == SMS_CAUSE_TYPE_COMMON && report->cause == SMS_OK) { CALLBACK_WITH_SUCCESS(cb, report->ref, cbd->data); return; } submit_failure_debug(report); error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void submit_gsm_tpdu_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_sms_submit_cb_t cb = cbd->cb; struct sms_report *report; size_t len = sizeof(struct sms_report); GIsiSubBlockIter iter; if (!check_sms(msg, SMS_MESSAGE_SEND_RESP, -1)) goto error; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != SMS_GSM_REPORT) continue; if (!g_isi_sb_iter_get_struct(&iter, (void **) &report, len, 2)) goto error; if (report->type == SMS_CAUSE_TYPE_COMMON && report->cause == SMS_OK) { CALLBACK_WITH_SUCCESS(cb, report->ref, cbd->data); return; } submit_failure_debug(report); } error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static gboolean submit_tpdu(GIsiClient *client, const unsigned char *pdu, int pdu_len, int tpdu_len, int mms, void *data, GDestroyNotify notify) { uint8_t use_sca = (pdu_len - tpdu_len) > 1; size_t sca_sb_len = use_sca ? 18 : 0; size_t tpdu_sb_len = ALIGN4(6 + tpdu_len); size_t tpdu_pad_len = tpdu_sb_len - (6 + tpdu_len); uint8_t msg[] = { SMS_MESSAGE_SEND_REQ, mms, /* More messages to send */ SMS_ROUTE_ANY, /* Use any (default) route */ 0, /* Repeated message */ 0, 0, /* Filler */ use_sca ? 3 : 2, /* Subblock count */ ISI_16BIT(SMS_SB_SMS_PARAMETERS), ISI_16BIT(8), /* Subblock length */ SMS_PARAMETER_LOCATION_DEFAULT, SMS_PI_SERVICE_CENTER_ADDRESS, 0, 0, /* Filler */ ISI_16BIT(SMS_SB_TPDU), ISI_16BIT(tpdu_sb_len), tpdu_len, 0, /* Filler */ /* Databytes aligned to next 32bit boundary */ }; uint8_t sca_sb[18] = { ISI_16BIT(SMS_SB_ADDRESS), ISI_16BIT(18), SMS_SMSC_ADDRESS, 0, /* Filled in later */ }; uint8_t padding[4] = { 0 }; struct iovec iov[4] = { { msg, sizeof(msg) }, { (void *) (pdu + pdu_len - tpdu_len), tpdu_len }, { padding, tpdu_pad_len }, { sca_sb, sca_sb_len }, }; if (use_sca) { sca_sb[5] = pdu_len - tpdu_len; memcpy(sca_sb + 6, pdu, pdu_len - tpdu_len); } return g_isi_client_vsend_with_timeout(client, iov, 4, SMS_TIMEOUT, submit_tpdu_resp_cb, data, notify); } static gboolean submit_gsm_tpdu(GIsiClient *client, const unsigned char *pdu, int pdu_len, int tpdu_len, int mms, void *data, GDestroyNotify notify) { uint8_t use_sca = (pdu_len - tpdu_len) > 1; size_t sca_sb_len = use_sca ? 16 : 0; size_t tpdu_sb_len = ALIGN4(4 + tpdu_len); size_t tpdu_pad_len = tpdu_sb_len - (4 + tpdu_len); uint8_t msg[] = { SMS_MESSAGE_SEND_REQ, mms, /* More messages to send */ SMS_ROUTE_CS_PREF, 0, /* Repeated message */ SMS_SENDER_ANY, SMS_TYPE_TEXT_MESSAGE, 1, /* Subblock count */ SMS_GSM_TPDU, tpdu_sb_len + sca_sb_len, 0, /* Filler */ use_sca ? 2 : 1, /* Sub-sub blocks */ SMS_COMMON_DATA, tpdu_sb_len, tpdu_len, 0, /* Packing required? */ /* Databytes aligned to next 32bit boundary */ }; uint8_t sca_sb[16] = { SMS_ADDRESS, 16, /* Subblock length */ SMS_GSM_0411_ADDRESS, 0, /* Filled in later */ }; uint8_t padding[4] = { 0 }; struct iovec iov[4] = { { msg, sizeof(msg) }, { (void *) (pdu + pdu_len - tpdu_len), tpdu_len }, { padding, tpdu_pad_len }, { sca_sb, sca_sb_len }, }; if (use_sca) { sca_sb[3] = pdu_len - tpdu_len; memcpy(sca_sb + 4, pdu, pdu_len - tpdu_len); } /* * Modem seems to time out SMS_MESSAGE_SEND_REQ in 5 seconds. * Wait normal timeout plus the modem timeout. */ return g_isi_client_vsend_with_timeout(client, iov, 4, SMS_TIMEOUT + 5, submit_gsm_tpdu_resp_cb, data, notify); } static void isi_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 isi_cb_data *cbd = isi_cb_data_new(sms, cb, data); if (cbd == NULL) goto error; if (ISI_VERSION_AT_LEAST(&sd->version, 9, 1)) { if (submit_tpdu(sd->client, pdu, pdu_len, tpdu_len, mms, cbd, g_free)) return; } else { if (submit_gsm_tpdu(sd->client, pdu, pdu_len, tpdu_len, mms, cbd, g_free)) return; } error: CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } static void bearer_query_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_sms_bearer_query_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint8_t sb, cs, ps; if (!check_sms(msg, SMS_SETTINGS_READ_RESP, SMS_OK)) goto error; if (!g_isi_msg_data_get_byte(msg, 1, &sb)) goto error; for (g_isi_sb_iter_init_full(&iter, msg, 2, TRUE, sb); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != SMS_SB_ROUTE_INFO) continue; if (!g_isi_msg_data_get_byte(msg, 5, &cs)) goto error; if (!g_isi_msg_data_get_byte(msg, 6, &ps)) goto error; CALLBACK_WITH_SUCCESS(cb, cs_ps_pref_to_bearer(cs, ps), cbd->data); return; } error: CALLBACK_WITH_FAILURE(cb, 0, cbd->data); } static void isi_bearer_query(struct ofono_sms *sms, ofono_sms_bearer_query_cb_t cb, void *data) { struct sms_data *sd = ofono_sms_get_data(sms); struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data); const uint8_t msg[] = { SMS_SETTINGS_READ_REQ, SMS_SETTING_TYPE_ROUTE, 0, }; DBG(""); if (cbd == NULL) goto error; if (g_isi_client_send(sd->client, msg, sizeof(msg), bearer_query_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, 0, data); g_free(cbd); } static void bearer_set_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_sms_bearer_set_cb_t cb = cbd->cb; if (check_sms(msg, SMS_SETTINGS_UPDATE_RESP, SMS_OK)) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); } static void isi_bearer_set(struct ofono_sms *sms, int bearer, ofono_sms_bearer_set_cb_t cb, void *data) { struct sms_data *sd = ofono_sms_get_data(sms); struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data); const uint8_t msg[] = { SMS_SETTINGS_UPDATE_REQ, SMS_SETTING_TYPE_ROUTE, 1, /* Subblock count */ ISI_16BIT(SMS_SB_ROUTE_INFO), ISI_16BIT(8), /* Subblock length */ bearer_to_cs_pref(bearer), /* CS priority */ bearer_to_ps_pref(bearer), /* PS priority */ 0, 0, }; if (cbd == NULL) goto error; if (g_isi_client_send(sd->client, msg, sizeof(msg), bearer_set_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void send_status_ind_cb(const GIsiMessage *msg, void *data) { struct sms_status *info; size_t len = sizeof(struct sms_status); DBG(""); if (g_isi_msg_id(msg) != SMS_MESSAGE_SEND_STATUS_IND) return; if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &info, len)) return; DBG("status=0x%"PRIx8", ref=0x%"PRIx8", route=0x%"PRIx8 ", cseg=0x%"PRIx8", tseg=0x%"PRIx8, info->status, info->ref, info->route, info->cseg, info->tseg); DBG("TODO: Status notification"); } static void gsm_report_resp_cb(const GIsiMessage *msg, void *data) { if (!check_sms(msg, SMS_GSM_RECEIVED_PP_REPORT_RESP, SMS_OK)) DBG("Sending report failed"); } static void report_resp_cb(const GIsiMessage *msg, void *data) { if (!check_sms(msg, SMS_RECEIVED_MSG_REPORT_RESP, SMS_OK)) DBG("Sending report failed"); } static gboolean send_gsm_deliver_report(GIsiClient *client, gboolean success, void *data, GDestroyNotify destroy) { const uint8_t msg[] = { SMS_GSM_RECEIVED_PP_REPORT_REQ, success ? 0 : SMS_CAUSE_TYPE_GSM, success ? SMS_OK : SMS_GSM_ERR_MEMORY_CAPACITY_EXC, 0, 0, 0, /* Filler */ 1, /* Sub blocks */ SMS_GSM_DELIVER_REPORT, 8, /* Subblock length */ 0, /* Message parameters */ 0, /* Cause type */ 0, 0, 0, /* Filler */ 0, /* Sub blocks */ }; return g_isi_client_send(client, msg, sizeof(msg), gsm_report_resp_cb, data, destroy); } static gboolean send_deliver_report(GIsiClient *client, gboolean success, void *data, GDestroyNotify destroy) { const uint8_t msg[] = { SMS_RECEIVED_MSG_REPORT_REQ, success ? 0 : SMS_CAUSE_TYPE_GSM, success ? SMS_OK : SMS_GSM_ERR_MEMORY_CAPACITY_EXC, 0, 0, 0, /* Filler */ 0, /* Subblocks */ }; return g_isi_client_send(client, msg, sizeof(msg), report_resp_cb, data, destroy); } static gboolean parse_sms_address(GIsiSubBlockIter *iter, unsigned offset, struct sms_addr *add) { add->data = NULL; if (!g_isi_sb_iter_get_byte(iter, &add->type, offset)) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &add->len, offset + 1)) return FALSE; if (add->len == 0) return FALSE; if (!g_isi_sb_iter_get_struct(iter, (void **) &add->data, add->len, offset + 2)) return FALSE; return TRUE; } static gboolean parse_sms_tpdu(GIsiSubBlockIter *iter, unsigned offset, struct sms_common *com) { com->data = NULL; if (!g_isi_sb_iter_get_byte(iter, &com->len, offset)) return FALSE; if (com->len == 0) return FALSE; if (!g_isi_sb_iter_get_struct(iter, (void **) &com->data, com->len, offset + 2)) return FALSE; return TRUE; } static gboolean parse_gsm_tpdu(GIsiSubBlockIter *parent, struct sms_addr *add, struct sms_common *com) { GIsiSubBlockIter iter; for (g_isi_sb_subiter_init(parent, &iter, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case SMS_ADDRESS: if (!parse_sms_address(&iter, 2, add)) return FALSE; if (add->type != SMS_GSM_0411_ADDRESS) return FALSE; break; case SMS_COMMON_DATA: if (!parse_sms_tpdu(&iter, 2, com)) return FALSE; break; } } return TRUE; } static void routing_ntf_cb(const GIsiMessage *msg, void *data) { struct ofono_sms *sms = data; struct sms_data *sd = ofono_sms_get_data(sms); struct sms_common tpdu; struct sms_addr addr; GIsiSubBlockIter iter; uint8_t pdu[176]; if (g_isi_msg_id(msg) != SMS_PP_ROUTING_NTF) return; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != SMS_GSM_TPDU) continue; if (!parse_gsm_tpdu(&iter, &addr, &tpdu)) return; } if (tpdu.data == NULL || addr.data == NULL || tpdu.len + addr.len > sizeof(pdu)) return; memcpy(pdu, addr.data, addr.len); memcpy(pdu + addr.len, tpdu.data, tpdu.len); /* 23.040 9.2.3.1 */ if ((tpdu.data[0] & 0x03) == 0x02) ofono_sms_status_notify(sms, pdu, tpdu.len + addr.len, tpdu.len); else ofono_sms_deliver_notify(sms, pdu, tpdu.len + addr.len, tpdu.len); send_gsm_deliver_report(sd->client, TRUE, NULL, NULL); } static void received_msg_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_sms *sms = data; struct sms_data *sd = ofono_sms_get_data(sms); struct sms_common tpdu; struct sms_addr addr; GIsiSubBlockIter iter; uint8_t pdu[176]; uint8_t sbcount; DBG(""); if (g_isi_msg_id(msg) != SMS_RECEIVED_MSG_IND) return; if (!g_isi_msg_data_get_byte(msg, 1, &sbcount)) return; for (g_isi_sb_iter_init_full(&iter, msg, 2, TRUE, sbcount); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case SMS_ADDRESS: if (!parse_sms_address(&iter, 4, &addr)) return; if (addr.type != SMS_SMSC_ADDRESS) return; break; case SMS_SB_TPDU: if (!parse_sms_tpdu(&iter, 4, &tpdu)) return; break; } } if (tpdu.data == NULL || addr.data == NULL || tpdu.len + addr.len > sizeof(pdu)) return; memcpy(pdu, addr.data, addr.len); memcpy(pdu + addr.len, tpdu.data, tpdu.len); /* 23.040 9.2.3.1 */ if ((tpdu.data[0] & 0x03) == 0x02) ofono_sms_status_notify(sms, pdu, tpdu.len + addr.len, tpdu.len); else ofono_sms_deliver_notify(sms, pdu, tpdu.len + addr.len, tpdu.len); send_deliver_report(sd->client, TRUE, NULL, NULL); } static void reception_resp_cb(const GIsiMessage *msg, void *data) { struct ofono_sms *sms = data; if (sms == NULL) return; if (!check_sms(msg, SMS_RECEIVE_MESSAGE_RESP, SMS_RECEPTION_ACTIVE)) return; ofono_sms_register(sms); } static void routing_resp_cb(const GIsiMessage *msg, void *data) { struct ofono_sms *sms = data; if (sms == NULL) return; if (!check_sms(msg, SMS_PP_ROUTING_RESP, SMS_OK)) return; ofono_sms_register(sms); } static void sim_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_sms *sms = data; struct sms_data *sd = ofono_sms_get_data(sms); if (sd == NULL) return; if (g_isi_msg_error(msg) < 0) { DBG("Unable to bootstrap SIM service"); g_isi_client_destroy(sd->sim); sd->sim = NULL; return; } ISI_RESOURCE_DBG(msg); } static gboolean set_routing(GIsiClient *client, void *data, GDestroyNotify destroy) { const uint8_t msg[] = { SMS_PP_ROUTING_REQ, SMS_ROUTING_SET, 1, /* Sub-block count */ SMS_GSM_ROUTING, 8, /* Sub-block length */ SMS_GSM_TPDU_ROUTING, SMS_GSM_MT_ALL_TYPE, 0, 0, 0, /* Filler */ 0, /* Sub-sub-block count */ }; return g_isi_client_send(client, msg, sizeof(msg), routing_resp_cb, data, destroy); } static gboolean unset_routing(GIsiClient *client) { const uint8_t msg[] = { SMS_PP_ROUTING_REQ, SMS_ROUTING_RELEASE, 0x01, /* Sub-block count */ SMS_GSM_ROUTING, 0x08, /* Sub-block length */ SMS_GSM_TPDU_ROUTING, SMS_GSM_MT_ALL_TYPE, 0, 0, 0, /* Filler */ 0, /* Sub-sub-block count */ }; return g_isi_client_send(client, msg, sizeof(msg), NULL, NULL, NULL); } static gboolean activate_reception(GIsiClient *client, void *data, GDestroyNotify destroy) { const uint8_t msg[] = { SMS_RECEIVE_MESSAGE_REQ, SMS_RECEPTION_ACTIVATE, 0, }; return g_isi_client_send(client, msg, sizeof(msg), reception_resp_cb, data, destroy); } static gboolean deactivate_reception(GIsiClient *client) { const uint8_t msg[] = { SMS_RECEIVE_MESSAGE_REQ, SMS_RECEPTION_DEACTIVATE, 0, }; return g_isi_client_send(client, msg, sizeof(msg), NULL, NULL, NULL); } static void sms_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_sms *sms = data; struct sms_data *sd = ofono_sms_get_data(sms); if (g_isi_msg_error(msg) < 0) { DBG("unable to find SMS resource"); ofono_sms_remove(sms); return; } if (sd == NULL) return; ISI_RESOURCE_DBG(msg); sd->version.major = g_isi_msg_version_major(msg); sd->version.minor = g_isi_msg_version_minor(msg); if (ISI_VERSION_AT_LEAST(&sd->version, 9, 1)) activate_reception(sd->client, sms, NULL); else set_routing(sd->client, sms, NULL); g_isi_client_verify(sd->sim, sim_reachable_cb, sms, NULL); } static int isi_sms_probe(struct ofono_sms *sms, unsigned int vendor, void *user) { GIsiModem *modem = user; struct sms_data *sd = g_try_new0(struct sms_data, 1); if (sd == NULL) return -ENOMEM; sd->params.absent = 0xFF; sd->params.alphalen = 1; /* Includes final UCS2-coded NUL */ sd->client = g_isi_client_create(modem, PN_SMS); if (sd->client == NULL) goto nomem; sd->sim = g_isi_client_create(modem, PN_SIM); if (sd->sim == NULL) goto nomem; ofono_sms_set_data(sms, sd); g_isi_client_ind_subscribe(sd->client, SMS_MESSAGE_SEND_STATUS_IND, send_status_ind_cb, sms); g_isi_client_ind_subscribe(sd->client, SMS_RECEIVED_MSG_IND, received_msg_ind_cb, sms); g_isi_client_ntf_subscribe(sd->client, SMS_PP_ROUTING_NTF, routing_ntf_cb, sms); g_isi_client_verify(sd->client, sms_reachable_cb, sms, NULL); return 0; nomem: g_isi_client_destroy(sd->client); g_free(sd); return -ENOMEM; } static void isi_sms_remove(struct ofono_sms *sms) { struct sms_data *sd = ofono_sms_get_data(sms); if (sd == NULL) return; ofono_sms_set_data(sms, NULL); /* * Send a promiscuous routing release, so as not to * hog resources unnecessarily after being removed */ if (ISI_VERSION_AT_LEAST(&sd->version, 9, 1)) deactivate_reception(sd->client); else unset_routing(sd->client); g_isi_client_destroy(sd->client); g_isi_client_destroy(sd->sim); g_free(sd); } static struct ofono_sms_driver driver = { .name = "isimodem", .probe = isi_sms_probe, .remove = isi_sms_remove, .sca_query = isi_sca_query, .sca_set = isi_sca_set, .submit = isi_submit, .bearer_query = isi_bearer_query, .bearer_set = isi_bearer_set, }; void isi_sms_init(void) { ofono_sms_driver_register(&driver); } void isi_sms_exit(void) { ofono_sms_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/call.h0000644000015600001650000003333312671500024022754 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_CALL_H #define __ISIMODEM_CALL_H #ifdef __cplusplus extern "C" { #endif #define PN_CALL 0x01 #define PN_MODEM_CALL 0xC9 #define CALL_MODEM_PROP_PRESENT_DEFAULT 0x00 enum call_message_id { CALL_CREATE_REQ = 0x01, CALL_CREATE_RESP = 0x02, CALL_COMING_IND = 0x03, CALL_MO_ALERT_IND = 0x04, CALL_MT_ALERT_IND = 0x05, CALL_WAITING_IND = 0x06, CALL_ANSWER_REQ = 0x07, CALL_ANSWER_RESP = 0x08, CALL_RELEASE_REQ = 0x09, CALL_RELEASE_RESP = 0x0A, CALL_RELEASE_IND = 0x0B, CALL_TERMINATED_IND = 0x0C, CALL_STATUS_REQ = 0x0D, CALL_STATUS_RESP = 0x0E, CALL_STATUS_IND = 0x0F, CALL_SERVER_STATUS_IND = 0x10, CALL_CONTROL_REQ = 0x11, CALL_CONTROL_RESP = 0x12, CALL_CONTROL_IND = 0x13, CALL_MODE_SWITCH_REQ = 0x14, CALL_MODE_SWITCH_RESP = 0x15, CALL_MODE_SWITCH_IND = 0x16, CALL_DTMF_SEND_REQ = 0x17, CALL_DTMF_SEND_RESP = 0x18, CALL_DTMF_STOP_REQ = 0x19, CALL_DTMF_STOP_RESP = 0x1A, CALL_DTMF_STATUS_IND = 0x1B, CALL_DTMF_TONE_IND = 0x1C, CALL_RECONNECT_IND = 0x1E, CALL_PROPERTY_GET_REQ = 0x1F, CALL_PROPERTY_GET_RESP = 0x20, CALL_PROPERTY_SET_REQ = 0x21, CALL_PROPERTY_SET_RESP = 0x22, CALL_PROPERTY_SET_IND = 0x23, CALL_EMERGENCY_NBR_CHECK_REQ = 0x28, CALL_EMERGENCY_NBR_CHECK_RESP = 0x29, CALL_EMERGENCY_NBR_GET_REQ = 0x26, CALL_EMERGENCY_NBR_GET_RESP = 0x27, CALL_EMERGENCY_NBR_MODIFY_REQ = 0x24, CALL_EMERGENCY_NBR_MODIFY_RESP = 0x25, CALL_GSM_NOTIFICATION_IND = 0xA0, CALL_GSM_USER_TO_USER_REQ = 0xA1, CALL_GSM_USER_TO_USER_RESP = 0xA2, CALL_GSM_USER_TO_USER_IND = 0xA3, CALL_GSM_BLACKLIST_CLEAR_REQ = 0xA4, CALL_GSM_BLACKLIST_CLEAR_RESP = 0xA5, CALL_GSM_BLACKLIST_TIMER_IND = 0xA6, CALL_GSM_DATA_CH_INFO_IND = 0xA7, CALL_GSM_CCP_GET_REQ = 0xAA, CALL_GSM_CCP_GET_RESP = 0xAB, CALL_GSM_CCP_CHECK_REQ = 0xAC, CALL_GSM_CCP_CHECK_RESP = 0xAD, CALL_GSM_COMING_REJ_IND = 0xA9, CALL_GSM_RAB_IND = 0xA8, CALL_GSM_IMMEDIATE_MODIFY_IND = 0xAE, CALL_CREATE_NO_SIMATK_REQ = 0x2A, CALL_GSM_SS_DATA_IND = 0xAF, CALL_TIMER_REQ = 0x2B, CALL_TIMER_RESP = 0x2C, CALL_TIMER_NTF = 0x2D, CALL_TIMER_IND = 0x2E, CALL_TIMER_RESET_REQ = 0x2F, CALL_TIMER_RESET_RESP = 0x30, CALL_EMERGENCY_NBR_IND = 0x31, CALL_SERVICE_DENIED_IND = 0x32, CALL_RELEASE_END_REQ = 0x34, CALL_RELEASE_END_RESP = 0x35, CALL_USER_CONNECT_IND = 0x33, CALL_AUDIO_CONNECT_IND = 0x40, CALL_KODIAK_ALLOW_CTRL_REQ = 0x36, CALL_KODIAK_ALLOW_CTRL_RESP = 0x37, CALL_SERVICE_ACTIVATE_IND = 0x38, CALL_SERVICE_ACTIVATE_REQ = 0x39, CALL_SERVICE_ACTIVATE_RESP = 0x3A, CALL_SIM_ATK_IND = 0x3B, CALL_CONTROL_OPER_IND = 0x3C, CALL_TEST_CALL_STATUS_IND = 0x3E, CALL_SIM_ATK_INFO_IND = 0x3F, CALL_SECURITY_IND = 0x41, CALL_MEDIA_HANDLE_REQ = 0x42, CALL_MEDIA_HANDLE_RESP = 0x43, }; enum call_status { CALL_STATUS_IDLE = 0x00, CALL_STATUS_CREATE = 0x01, CALL_STATUS_COMING = 0x02, CALL_STATUS_PROCEEDING = 0x03, CALL_STATUS_MO_ALERTING = 0x04, CALL_STATUS_MT_ALERTING = 0x05, CALL_STATUS_WAITING = 0x06, CALL_STATUS_ANSWERED = 0x07, CALL_STATUS_ACTIVE = 0x08, CALL_STATUS_MO_RELEASE = 0x09, CALL_STATUS_MT_RELEASE = 0x0A, CALL_STATUS_HOLD_INITIATED = 0x0B, CALL_STATUS_HOLD = 0x0C, CALL_STATUS_RETRIEVE_INITIATED = 0x0D, CALL_STATUS_RECONNECT_PENDING = 0x0E, CALL_STATUS_TERMINATED = 0x0F, CALL_STATUS_SWAP_INITIATED = 0x10, }; enum call_isi_cause { CALL_CAUSE_NO_CAUSE = 0x00, CALL_CAUSE_NO_CALL = 0x01, CALL_CAUSE_TIMEOUT = 0x02, CALL_CAUSE_RELEASE_BY_USER = 0x03, CALL_CAUSE_BUSY_USER_REQUEST = 0x04, CALL_CAUSE_ERROR_REQUEST = 0x05, CALL_CAUSE_COST_LIMIT_REACHED = 0x06, CALL_CAUSE_CALL_ACTIVE = 0x07, CALL_CAUSE_NO_CALL_ACTIVE = 0x08, CALL_CAUSE_INVALID_CALL_MODE = 0x09, CALL_CAUSE_SIGNALLING_FAILURE = 0x0A, CALL_CAUSE_TOO_LONG_ADDRESS = 0x0B, CALL_CAUSE_INVALID_ADDRESS = 0x0C, CALL_CAUSE_EMERGENCY = 0x0D, CALL_CAUSE_NO_TRAFFIC_CHANNEL = 0x0E, CALL_CAUSE_NO_COVERAGE = 0x0F, CALL_CAUSE_CODE_REQUIRED = 0x10, CALL_CAUSE_NOT_ALLOWED = 0x11, CALL_CAUSE_NO_DTMF = 0x12, CALL_CAUSE_CHANNEL_LOSS = 0x13, CALL_CAUSE_FDN_NOT_OK = 0x14, CALL_CAUSE_USER_TERMINATED = 0x15, CALL_CAUSE_BLACKLIST_BLOCKED = 0x16, CALL_CAUSE_BLACKLIST_DELAYED = 0x17, CALL_CAUSE_NUMBER_NOT_FOUND = 0x18, CALL_CAUSE_NUMBER_CANNOT_REMOVE = 0x19, CALL_CAUSE_EMERGENCY_FAILURE = 0x1A, CALL_CAUSE_CS_SUSPENDED = 0x1B, CALL_CAUSE_DCM_DRIVE_MODE = 0x1C, CALL_CAUSE_MULTIMEDIA_NOT_ALLOWED = 0x1D, CALL_CAUSE_SIM_REJECTED = 0x1E, CALL_CAUSE_NO_SIM = 0x1F, CALL_CAUSE_SIM_LOCK_OPERATIVE = 0x20, CALL_CAUSE_SIMATKCC_REJECTED = 0x21, CALL_CAUSE_SIMATKCC_MODIFIED = 0x22, CALL_CAUSE_DTMF_INVALID_DIGIT = 0x23, CALL_CAUSE_DTMF_SEND_ONGOING = 0x24, CALL_CAUSE_CS_INACTIVE = 0x25, CALL_CAUSE_SECURITY_MODE = 0x26, CALL_CAUSE_TRACFONE_FAILED = 0x27, CALL_CAUSE_TRACFONE_WAIT_FAILED = 0x28, CALL_CAUSE_TRACFONE_CONF_FAILED = 0x29, CALL_CAUSE_TEMPERATURE_LIMIT = 0x2A, CALL_CAUSE_KODIAK_POC_FAILED = 0x2B, CALL_CAUSE_NOT_REGISTERED = 0x2C, CALL_CAUSE_CS_CALLS_ONLY = 0x2D, CALL_CAUSE_VOIP_CALLS_ONLY = 0x2E, CALL_CAUSE_LIMITED_CALL_ACTIVE = 0x2F, CALL_CAUSE_LIMITED_CALL_NOT_ALLOWED = 0x30, CALL_CAUSE_SECURE_CALL_NOT_POSSIBLE = 0x31, CALL_CAUSE_INTERCEPT = 0x32, }; enum call_gsm_cause { CALL_GSM_CAUSE_UNASSIGNED_NUMBER = 0x01, CALL_GSM_CAUSE_NO_ROUTE = 0x03, CALL_GSM_CAUSE_CH_UNACCEPTABLE = 0x06, CALL_GSM_CAUSE_OPER_BARRING = 0x08, CALL_GSM_CAUSE_NORMAL = 0x10, CALL_GSM_CAUSE_USER_BUSY = 0x11, CALL_GSM_CAUSE_NO_USER_RESPONSE = 0x12, CALL_GSM_CAUSE_ALERT_NO_ANSWER = 0x13, CALL_GSM_CAUSE_CALL_REJECTED = 0x15, CALL_GSM_CAUSE_NUMBER_CHANGED = 0x16, CALL_GSM_CAUSE_NON_SELECT_CLEAR = 0x1A, CALL_GSM_CAUSE_DEST_OUT_OF_ORDER = 0x1B, CALL_GSM_CAUSE_INVALID_NUMBER = 0x1C, CALL_GSM_CAUSE_FACILITY_REJECTED = 0x1D, CALL_GSM_CAUSE_RESP_TO_STATUS = 0x1E, CALL_GSM_CAUSE_NORMAL_UNSPECIFIED = 0x1F, CALL_GSM_CAUSE_NO_CHANNEL = 0x22, CALL_GSM_CAUSE_NETW_OUT_OF_ORDER = 0x26, CALL_GSM_CAUSE_TEMPORARY_FAILURE = 0x29, CALL_GSM_CAUSE_CONGESTION = 0x2A, CALL_GSM_CAUSE_ACCESS_INFO_DISC = 0x2B, CALL_GSM_CAUSE_CHANNEL_NA = 0x2C, CALL_GSM_CAUSE_RESOURCES_NA = 0x2F, CALL_GSM_CAUSE_QOS_NA = 0x31, CALL_GSM_CAUSE_FACILITY_UNSUBS = 0x32, CALL_GSM_CAUSE_COMING_BARRED_CUG = 0x37, CALL_GSM_CAUSE_BC_UNAUTHORIZED = 0x39, CALL_GSM_CAUSE_BC_NA = 0x3A, CALL_GSM_CAUSE_SERVICE_NA = 0x3F, CALL_GSM_CAUSE_BEARER_NOT_IMPL = 0x41, CALL_GSM_CAUSE_ACM_MAX = 0x44, CALL_GSM_CAUSE_FACILITY_NOT_IMPL = 0x45, CALL_GSM_CAUSE_ONLY_RDI_BC = 0x46, CALL_GSM_CAUSE_SERVICE_NOT_IMPL = 0x4F, CALL_GSM_CAUSE_INVALID_TI = 0x51, CALL_GSM_CAUSE_NOT_IN_CUG = 0x57, CALL_GSM_CAUSE_INCOMPATIBLE_DEST = 0x58, CALL_GSM_CAUSE_INV_TRANS_NET_SEL = 0x5B, CALL_GSM_CAUSE_SEMANTICAL_ERR = 0x5F, CALL_GSM_CAUSE_INVALID_MANDATORY = 0x60, CALL_GSM_CAUSE_MSG_TYPE_INEXIST = 0x61, CALL_GSM_CAUSE_MSG_TYPE_INCOMPAT = 0x62, CALL_GSM_CAUSE_IE_NON_EXISTENT = 0x63, CALL_GSM_CAUSE_COND_IE_ERROR = 0x64, CALL_GSM_CAUSE_MSG_INCOMPATIBLE = 0x65, CALL_GSM_CAUSE_TIMER_EXPIRY = 0x66, CALL_GSM_CAUSE_PROTOCOL_ERROR = 0x6F, CALL_GSM_CAUSE_INTERWORKING = 0x7F, }; enum call_cause_type { CALL_CAUSE_TYPE_DEFAULT = 0x00, CALL_CAUSE_TYPE_CLIENT = 0x01, CALL_CAUSE_TYPE_SERVER = 0x02, CALL_CAUSE_TYPE_NETWORK = 0x03, }; enum call_subblock { CALL_ORIGIN_ADDRESS = 0x01, CALL_ORIGIN_SUBADDRESS = 0x02, CALL_DESTINATION_ADDRESS = 0x03, CALL_DESTINATION_SUBADDRESS = 0x04, CALL_DESTINATION_PRE_ADDRESS = 0x05, CALL_DESTINATION_POST_ADDRESS = 0x06, CALL_MODE = 0x07, CALL_CAUSE = 0x08, CALL_OPERATION = 0x09, CALL_STATUS = 0x0A, CALL_STATUS_INFO = 0x0B, CALL_ALERTING_INFO = 0x0C, CALL_RELEASE_INFO = 0x0D, CALL_ORIGIN_INFO = 0x0E, CALL_DTMF_DIGIT = 0x0F, CALL_DTMF_STRING = 0x10, CALL_DTMF_BCD_STRING = 0x19, CALL_DTMF_INFO = 0x1A, CALL_PROPERTY_INFO = 0x13, CALL_EMERGENCY_NUMBER = 0x14, CALL_DTMF_STATUS = 0x11, CALL_DTMF_TONE = 0x12, CALL_GSM_CUG_INFO = 0xA0, CALL_GSM_ALERTING_PATTERN = 0xA1, CALL_GSM_DEFLECTION_ADDRESS = 0xA2, CALL_GSM_DEFLECTION_SUBADDRESS = 0xA3, CALL_GSM_REDIRECTING_ADDRESS = 0xA4, CALL_GSM_REDIRECTING_SUBADDRESS = 0xA5, CALL_GSM_REMOTE_ADDRESS = 0xA6, CALL_GSM_REMOTE_SUBADDRESS = 0xA7, CALL_GSM_USER_TO_USER_INFO = 0xA8, CALL_GSM_DIAGNOSTICS = 0xA9, CALL_GSM_SS_DIAGNOSTICS = 0xAA, CALL_GSM_NEW_DESTINATION = 0xAB, CALL_GSM_CCBS_INFO = 0xAC, CALL_GSM_ADDRESS_OF_B = 0xAD, CALL_GSM_SUBADDRESS_OF_B = 0xB0, CALL_GSM_NOTIFY = 0xB1, CALL_GSM_SS_NOTIFY = 0xB2, CALL_GSM_SS_CODE = 0xB3, CALL_GSM_SS_STATUS = 0xB4, CALL_GSM_SS_NOTIFY_INDICATOR = 0xB5, CALL_GSM_SS_HOLD_INDICATOR = 0xB6, CALL_GSM_SS_ECT_INDICATOR = 0xB7, CALL_GSM_DATA_CH_INFO = 0xB8, CALL_DESTINATION_CS_ADDRESS = 0x16, CALL_GSM_CCP = 0xBA, CALL_GSM_RAB_INFO = 0xB9, CALL_GSM_FNUR_INFO = 0xBB, CALL_GSM_CAUSE_OF_NO_CLI = 0xBC, CALL_GSM_MM_CAUSE = 0xBD, CALL_GSM_EVENT_INFO = 0xBE, CALL_GSM_DETAILED_CAUSE = 0xBF, CALL_GSM_SS_DATA = 0xC0, CALL_TIMER = 0x17, CALL_GSM_ALS_INFO = 0xC1, CALL_STATE_AUTO_CHANGE = 0x18, CALL_EMERGENCY_NUMBER_INFO = 0x1B, CALL_STATUS_MODE = 0x1C, CALL_ADDR_AND_STATUS_INFO = 0x1D, CALL_DTMF_TIMERS = 0x1E, CALL_NAS_SYNC_INDICATOR = 0x1F, CALL_NW_CAUSE = 0x20, CALL_TRACFONE_RESULT = 0x21, CALL_KODIAK_POC = 0x22, CALL_DISPLAY_NUMBER = 0x23, CALL_DESTINATION_URI = 0x24, CALL_ORIGIN_URI = 0x25, CALL_URI = 0x26, CALL_SYSTEM_INFO = 0x27, CALL_SYSTEMS = 0x28, CALL_VOIP_TIMER = 0x29, CALL_REDIRECTING_URI = 0x2A, CALL_REMOTE_URI = 0x2B, CALL_DEFLECTION_URI = 0x2C, CALL_TRANSFER_INFO = 0x2D, CALL_FORWARDING_INFO = 0x2E, CALL_ID_INFO = 0x2F, CALL_TEST_CALL = 0x30, CALL_AUDIO_CONF_INFO = 0x31, CALL_SECURITY_INFO = 0x33, CALL_SINGLE_TIMERS = 0x32, CALL_MEDIA_INFO = 0x35, CALL_MEDIA_HANDLE = 0x34, CALL_MODE_CHANGE_INFO = 0x36, CALL_ADDITIONAL_PARAMS = 0x37, CALL_DSAC_INFO = 0x38, CALL_LINE_ID = 0x47, }; enum call_id { CALL_ID_NONE = 0x00, CALL_ID_1 = 0x01, CALL_ID_2 = 0x02, CALL_ID_3 = 0x03, CALL_ID_4 = 0x04, CALL_ID_5 = 0x05, CALL_ID_6 = 0x06, CALL_ID_7 = 0x07, CALL_ID_CONFERENCE = 0x10, CALL_ID_WAITING = 0x20, CALL_ID_HOLD = 0x40, CALL_ID_ACTIVE = 0x80, CALL_ID_ALL = 0xF0, }; enum call_dtmf_pause_values { CALL_DTMF_PAUSE_1S = 0x01 }; enum call_mode { CALL_MODE_EMERGENCY = 0x00, CALL_MODE_SPEECH = 0x01, CALL_GSM_MODE_ALS_LINE_1 = 0xA5, CALL_GSM_MODE_ALS_LINE_2 = 0xA2, }; enum { CALL_MODE_INFO_NONE = 0x00, CALL_MODE_ORIGINATOR = 0x01, }; enum { CALL_PRESENTATION_ALLOWED = 0x00, CALL_PRESENTATION_RESTRICTED = 0x01, CALL_GSM_PRESENTATION_DEFAULT = 0x07, }; enum call_modem_line_id { CALL_MODEM_PRESENT_DEFAULT = 0x00, CALL_MODEM_PRESENT_ALLOWED = 0x01, CALL_MODEM_PRESENT_RESTRICTED = 0x02 }; enum call_operation { CALL_OP_HOLD = 0x01, CALL_OP_RETRIEVE = 0x02, CALL_OP_SWAP = 0x03, CALL_OP_CONFERENCE_BUILD = 0x04, CALL_OP_CONFERENCE_SPLIT = 0x05, CALL_OP_DATA_RATE_CHANGE = 0x06, CALL_GSM_OP_CUG = 0xA0, CALL_GSM_OP_TRANSFER = 0xA1, CALL_GSM_OP_DEFLECT = 0xA2, CALL_GSM_OP_CCBS = 0xA3, CALL_GSM_OP_UUS1 = 0xA4, CALL_GSM_OP_UUS2 = 0xA5, CALL_GSM_OP_UUS3 = 0xA6, }; enum { CALL_GSM_OP_UUS_REQUIRED = 0x01, }; enum call_status_mode { CALL_STATUS_MODE_DEFAULT = 0x00, CALL_STATUS_MODE_ADDR = 0x01, CALL_STATUS_MODE_ADDR_AND_ORIGIN = 0x02, CALL_STATUS_MODE_POC = 0x03, CALL_STATUS_MODE_VOIP_ADDR = 0x04, }; enum { CALL_DTMF_ENABLE_TONE_IND_SEND = 0x01, CALL_DTMF_DISABLE_TONE_IND_SEND = 0x02, }; enum call_notification_indicator { CALL_NOTIFY_USER_SUSPENDED = 0x00, CALL_NOTIFY_USER_RESUMED = 0x01, CALL_NOTIFY_BEARER_CHANGE = 0x02 }; enum call_mmi_ss_codes { CALL_SSC_ALL_FWDS = 0x0002, CALL_SSC_ALL_COND_FWD = 0x0004, CALL_SSC_CFU = 0x0015, CALL_SSC_CFB = 0x0043, CALL_SSC_CFNRY = 0x003D, CALL_SSC_CFGNC = 0x003E, CALL_SSC_OUTGOING_BARR_SERV = 0x014D, CALL_SSC_INCOMING_BARR_SERV = 0x0161, CALL_SSC_CALL_WAITING = 0x002B, CALL_SSC_CLIR = 0x001F, CALL_SSC_ETC = 0x0060, CALL_SSC_MPTY = 0xFFFE, CALL_SSC_CALL_HOLD = 0xFFFF }; enum call_ss_status { CALL_SS_STATUS_ACTIVE = 0x01, CALL_SS_STATUS_REGISTERED = 0x02, CALL_SS_STATUS_PROVISIONED = 0x04, CALL_SS_STATUS_QUIESCENT = 0x08 }; enum call_ss_notification { CALL_SSN_INCOMING_IS_FWD = 0x01, CALL_SSN_INCOMING_FWD = 0x02, CALL_SSN_OUTGOING_FWD = 0x04 }; enum call_ss_indicator { CALL_SSI_CALL_IS_WAITING = 0x01, CALL_SSI_MPTY = 0x02, CALL_SSI_CLIR_SUPPR_REJ = 0x04 }; enum call_ss_hold_indicator { CALL_HOLD_IND_RETRIEVED = 0x00, CALL_HOLD_IND_ON_HOLD = 0x01 }; enum call_ss_ect_indicator { CALL_ECT_CALL_STATE_ALERT = 0x00, CALL_ECT_CALL_STATE_ACTIVE = 0x01 }; /* 27.007 Section 7.7 */ enum clir_status { CLIR_STATUS_NOT_PROVISIONED = 0, CLIR_STATUS_PROVISIONED_PERMANENT, CLIR_STATUS_UNKNOWN, CLIR_STATUS_TEMPORARY_RESTRICTED, CLIR_STATUS_TEMPORARY_ALLOWED }; #ifdef __cplusplus }; #endif #endif /* !__ISIMODEM_CALL_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/call-meter.c0000644000015600001650000000631112671500024024055 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "isimodem.h" #include "isiutil.h" #include "ss.h" struct call_meter_data { GIsiClient *client; }; static void isi_call_meter_query(struct ofono_call_meter *cm, ofono_call_meter_query_cb_t cb, void *data) { } static void isi_acm_query(struct ofono_call_meter *cm, ofono_call_meter_query_cb_t cb, void *data) { } static void isi_acm_reset(struct ofono_call_meter *cm, const char *sim_pin2, ofono_call_meter_set_cb_t cb, void *data) { } static void isi_acm_max_query(struct ofono_call_meter *cm, ofono_call_meter_query_cb_t cb, void *data) { } static void isi_acm_max_set(struct ofono_call_meter *cm, int new_value, const char *sim_pin2, ofono_call_meter_set_cb_t cb, void *data) { } static void isi_puct_query(struct ofono_call_meter *cm, ofono_call_meter_puct_query_cb_t cb, void *data) { } static void isi_puct_set(struct ofono_call_meter *cm, const char *currency, double ppu, const char *sim_pin2, ofono_call_meter_set_cb_t cb, void *data) { } static int isi_call_meter_probe(struct ofono_call_meter *cm, unsigned int vendor, void *user) { GIsiModem *modem = user; struct call_meter_data *cmd; cmd = g_try_new0(struct call_meter_data, 1); if (cmd == NULL) return -ENOMEM; cmd->client = g_isi_client_create(modem, PN_SS); if (cmd->client == NULL) { g_free(cmd); return -ENOMEM; } ofono_call_meter_set_data(cm, cmd); return 0; } static void isi_call_meter_remove(struct ofono_call_meter *cm) { struct call_meter_data *data = ofono_call_meter_get_data(cm); ofono_call_meter_set_data(cm, NULL); if (data == NULL) return; g_isi_client_destroy(data->client); g_free(data); } static struct ofono_call_meter_driver driver = { .name = "isimodem", .probe = isi_call_meter_probe, .remove = isi_call_meter_remove, .call_meter_query = isi_call_meter_query, .acm_query = isi_acm_query, .acm_reset = isi_acm_reset, .acm_max_query = isi_acm_max_query, .acm_max_set = isi_acm_max_set, .puct_query = isi_puct_query, .puct_set = isi_puct_set }; void isi_call_meter_init(void) { ofono_call_meter_driver_register(&driver); } void isi_call_meter_exit(void) { ofono_call_meter_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/voicecall.c0000644000015600001650000012506512671500024024001 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "isimodem.h" #include "isiutil.h" #include "call.h" #include "debug.h" #define ISI_CALL_TIMEOUT 1000 struct isi_call { uint8_t id; uint8_t call_id; uint8_t status; uint8_t prev_status; uint8_t mode; uint8_t mode_info; uint8_t cause_type; uint8_t cause; uint8_t addr_type; uint8_t presentation; uint8_t name_presentation; uint8_t reason; char address[20]; char name[20]; char addr_pad[4]; }; struct call_addr_info { uint8_t call_id; uint8_t mode; uint8_t mode_info; uint8_t status; uint8_t filler[2]; uint8_t addr_type; uint8_t presentation; uint8_t filler2; uint8_t addr_len; }; struct call_info { uint8_t call_id; uint8_t mode; uint8_t mode_info; uint8_t status; }; struct isi_voicecall { GIsiClient *client; GIsiClient *pn_call; GIsiClient *pn_modem_call; struct isi_call_req_ctx *queue; struct isi_call calls[8]; void *control_req_irc; }; typedef void isi_call_req_step(struct isi_call_req_ctx *ctx, int reason); struct isi_call_req_ctx { struct isi_call_req_ctx *next; struct isi_call_req_ctx **prev; isi_call_req_step *step; struct ofono_voicecall *ovc; ofono_voicecall_cb_t cb; void *data; }; static struct isi_call_req_ctx *isi_call_req(struct ofono_voicecall *ovc, const void *__restrict req, size_t len, GIsiNotifyFunc handler, ofono_voicecall_cb_t cb, void *data) { struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); struct isi_call_req_ctx *irc; irc = g_try_new0(struct isi_call_req_ctx, 1); if (irc == NULL) { CALLBACK_WITH_FAILURE(cb, data); return NULL; } irc->ovc = ovc; irc->cb = cb; irc->data = data; if (g_isi_client_send(ivc->client, req, len, handler, irc, NULL)) return irc; g_free(irc); return NULL; } static void isi_ctx_queue(struct isi_call_req_ctx *irc, isi_call_req_step *next) { struct isi_voicecall *ivc; if (irc->prev != NULL) { irc->step = next; return; } ivc = ofono_voicecall_get_data(irc->ovc); if (ivc->queue) { irc->next = ivc->queue; irc->next->prev = &irc->next; } irc->prev = &ivc->queue; ivc->queue = irc; } static void isi_ctx_remove(struct isi_call_req_ctx *irc) { if (irc->prev == NULL) return; *irc->prev = irc->next; if (irc->next) { irc->next->prev = irc->prev; irc->next = NULL; } irc->prev = NULL; } static void isi_ctx_free(struct isi_call_req_ctx *irc) { if (irc == NULL) return; isi_ctx_remove(irc); g_free(irc); } static gboolean isi_ctx_return(struct isi_call_req_ctx *irc, enum ofono_error_type type, int error) { if (irc == NULL) return TRUE; if (irc->cb) { struct ofono_error e = { .type = type, .error = error }; irc->cb(&e, irc->data); } isi_ctx_free(irc); return TRUE; } static gboolean isi_ctx_return_failure(struct isi_call_req_ctx *irc) { return isi_ctx_return(irc, OFONO_ERROR_TYPE_FAILURE, 0); } static gboolean isi_ctx_return_success(struct isi_call_req_ctx *irc) { if (irc == NULL || irc->step == NULL) return isi_ctx_return(irc, OFONO_ERROR_TYPE_NO_ERROR, 0); irc->step(irc, 0); return TRUE; } /* Decoding subblocks */ static void isi_call_any_address_sb_proc(struct isi_voicecall *ivc, struct isi_call *call, GIsiSubBlockIter *sb) { uint8_t type; uint8_t pres; uint8_t len; char *addr; if (!g_isi_sb_iter_get_byte(sb, &type, 2)) return; if (!g_isi_sb_iter_get_byte(sb, &pres, 3)) return; if (!g_isi_sb_iter_get_byte(sb, &len, 5)) return; if (!g_isi_sb_iter_get_alpha_tag(sb, &addr, 2 * len, 6)) return; call->addr_type = type | 0x80; call->presentation = pres; strncpy(call->address, addr, sizeof(call->address)); g_free(addr); } static void isi_call_origin_address_sb_proc(struct isi_voicecall *ivc, struct isi_call *call, GIsiSubBlockIter *sb) { if (call->address[0] == '\0') isi_call_any_address_sb_proc(ivc, call, sb); } static void isi_call_destination_address_sb_proc(struct isi_voicecall *ivc, struct isi_call *call, GIsiSubBlockIter *sb) { if (call->address[0] == '\0') isi_call_any_address_sb_proc(ivc, call, sb); } static void isi_call_origin_info_sb_proc(struct isi_voicecall *ivc, struct isi_call *call, GIsiSubBlockIter *sb) { uint8_t pres; uint8_t id; uint8_t len; char *name; if (!g_isi_sb_iter_get_byte(sb, &pres, 2)) return; if (!g_isi_sb_iter_get_byte(sb, &id, 6)) return; if (!g_isi_sb_iter_get_byte(sb, &len, 7)) return; if (!g_isi_sb_iter_get_alpha_tag(sb, &name, 2 * len, 8)) return; DBG("Got name %s", name); call->name_presentation = pres; strncpy(call->name, name, sizeof(call->name)); g_free(name); } static void isi_call_mode_sb_proc(struct isi_voicecall *ivc, struct isi_call *call, GIsiSubBlockIter *sb) { uint8_t mode; uint8_t info; if (!g_isi_sb_iter_get_byte(sb, &mode, 2) || !g_isi_sb_iter_get_byte(sb, &info, 3)) return; call->mode = mode; call->mode_info = info; } static void isi_call_cause_sb_proc(struct isi_voicecall *ivc, struct isi_call *call, GIsiSubBlockIter *sb) { uint8_t type; uint8_t cause; if (!g_isi_sb_iter_get_byte(sb, &type, 2) || !g_isi_sb_iter_get_byte(sb, &cause, 3)) return; call->cause_type = type; call->cause = cause; } static void isi_call_status_sb_proc(struct isi_voicecall *ivc, struct isi_call *call, GIsiSubBlockIter *sb) { uint8_t status; if (!g_isi_sb_iter_get_byte(sb, &status, 2)) return; call->prev_status = call->status; call->status = status; } static struct isi_call *isi_call_status_info_sb_proc(struct isi_voicecall *ivc, GIsiSubBlockIter *sb) { struct isi_call *call = NULL; int i; struct call_info *ci; size_t len = sizeof(struct call_info); if (!g_isi_sb_iter_get_struct(sb, (void *) &ci, len, 2)) return NULL; i = ci->call_id & 7; if (1 <= i && i <= 7) { call = &ivc->calls[i]; call->call_id = ci->call_id; call->status = ci->status; call->mode = ci->mode; call->mode_info = ci->mode_info; } return call; } static struct isi_call *isi_call_addr_and_status_info_sb_proc( struct isi_voicecall *ivc, GIsiSubBlockIter *sb) { struct isi_call *call = NULL; int i; struct call_addr_info *ci; size_t len = sizeof(struct call_addr_info); char *addr; if (!g_isi_sb_iter_get_struct(sb, (void *) &ci, len, 2)) return NULL; if (!g_isi_sb_iter_get_alpha_tag(sb, &addr, 2 * ci->addr_len, 12)) return NULL; i = ci->call_id & 7; if (1 <= i && i <= 7) { call = &ivc->calls[i]; call->call_id = ci->call_id; call->status = ci->status; call->mode = ci->mode; call->mode_info = ci->mode_info; call->addr_type = ci->addr_type | 0x80; call->presentation = ci->presentation; strncpy(call->address, addr, sizeof call->address); } g_free(addr); return call; } static int isi_call_status_to_clcc(const struct isi_call *call) { switch (call->status) { case CALL_STATUS_CREATE: return 2; case CALL_STATUS_COMING: return 4; case CALL_STATUS_PROCEEDING: if ((call->mode_info & CALL_MODE_ORIGINATOR)) return 4; /* MT */ else return 2; /* MO */ case CALL_STATUS_MO_ALERTING: return 3; case CALL_STATUS_MT_ALERTING: return 4; case CALL_STATUS_WAITING: return 5; case CALL_STATUS_MO_RELEASE: return 6; case CALL_STATUS_MT_RELEASE: if ((call->prev_status == CALL_STATUS_MT_ALERTING) || (call->prev_status == CALL_STATUS_COMING) || (call->prev_status == CALL_STATUS_WAITING)) return 4; else return 6; case CALL_STATUS_ACTIVE: case CALL_STATUS_HOLD_INITIATED: return 0; case CALL_STATUS_HOLD: case CALL_STATUS_RETRIEVE_INITIATED: return 1; case CALL_STATUS_RECONNECT_PENDING: case CALL_STATUS_SWAP_INITIATED: default: return 0; } } static struct ofono_call isi_call_as_ofono_call(const struct isi_call *call) { struct ofono_call ocall; struct ofono_phone_number *number = &ocall.phone_number; ofono_call_init(&ocall); ocall.id = call->id; ocall.type = 0; /* Voice call */ ocall.direction = call->mode_info & CALL_MODE_ORIGINATOR; ocall.status = isi_call_status_to_clcc(call); memcpy(number->number, call->address, sizeof(number->number)); memcpy(ocall.name, call->name, sizeof(ocall.name)); number->type = 0x80 | call->addr_type; ocall.clip_validity = call->presentation & 3; ocall.cnap_validity = call->name_presentation & 3; if (ocall.clip_validity == 0 && strlen(number->number) == 0) ocall.clip_validity = 2; if (ocall.cnap_validity == 0 && strlen(call->name) == 0) ocall.cnap_validity = 2; return ocall; } static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid) { if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", strerror(-g_isi_msg_error(msg))); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", net_message_id_name(g_isi_msg_id(msg))); return FALSE; } return TRUE; } static struct isi_call *isi_call_set_idle(struct isi_call *call) { uint8_t id; if (call == NULL) return NULL; id = call->id; memset(call, 0, sizeof(struct isi_call)); call->id = id; return call; } static void isi_call_disconnected(struct ofono_voicecall *ovc, struct isi_call *call) { struct ofono_error error = { OFONO_ERROR_TYPE_NO_ERROR, 0 }; DBG("disconnected id=%u reason=%u", call->id, call->reason); ofono_voicecall_disconnected(ovc, call->id, call->reason, &error); isi_call_set_idle(call); } static void isi_call_set_disconnect_reason(struct isi_call *call) { enum ofono_disconnect_reason reason; if (call->reason != OFONO_DISCONNECT_REASON_UNKNOWN) return; switch (call->status) { case CALL_STATUS_IDLE: reason = OFONO_DISCONNECT_REASON_UNKNOWN; break; case CALL_STATUS_MO_RELEASE: reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP; break; case CALL_STATUS_MT_RELEASE: reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP; break; case CALL_STATUS_TERMINATED: default: reason = OFONO_DISCONNECT_REASON_ERROR; } call->reason = reason; } static void isi_call_notify(struct ofono_voicecall *ovc, struct isi_call *call) { struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); struct isi_call_req_ctx *irc, **queue; struct ofono_call ocall; DBG("called with status=%s (0x%02X)", call_status_name(call->status), call->status); for (queue = &ivc->queue; (irc = *queue);) { irc->step(irc, call->status); if (*queue == irc) queue = &irc->next; } switch (call->status) { case CALL_STATUS_IDLE: isi_call_disconnected(ovc, call); return; case CALL_STATUS_COMING: case CALL_STATUS_PROCEEDING: if ((call->mode_info & CALL_MODE_ORIGINATOR)) /* Do not notify early MT calls */ return; break; case CALL_STATUS_MO_RELEASE: case CALL_STATUS_MT_RELEASE: /* * Core requires the call status to be either incoming * or waiting to identify the disconnected call as missed. * The MT RELEASE is not mapped to any state in +CLCC, but * we need the disconnect reason. */ isi_call_set_disconnect_reason(call); break; case CALL_STATUS_TERMINATED: DBG("State( CALL_STATUS_TERMINATED ) need not be reported to Core"); /* * The call terminated is not reported to core as * these intermediate states are not processed in * the core. We report the call status when it becomes * idle and TERMINATED is not mapped to +CLCC. The disconnect * reason is set, so that the call termination cause * in case of error is available to the core. */ isi_call_set_disconnect_reason(call); return; case CALL_STATUS_ANSWERED: DBG("State need not be reported to Core"); return; } ocall = isi_call_as_ofono_call(call); DBG("id=%u,%s,%u,\"%s\",\"%s\",%u,%u", ocall.id, ocall.direction ? "terminated" : "originated", ocall.status, ocall.phone_number.number, ocall.name, ocall.phone_number.type, ocall.clip_validity); ofono_voicecall_notify(ovc, &ocall); } static void isi_call_create_resp(const GIsiMessage *msg, void *data) { struct isi_call_req_ctx *irc = data; uint8_t call_id; uint8_t subblocks; if (!check_response_status(msg, CALL_CREATE_RESP)) goto failure; if (!g_isi_msg_data_get_byte(msg, 0, &call_id) || call_id == CALL_ID_NONE) goto failure; if (!g_isi_msg_data_get_byte(msg, 1, &subblocks)) goto failure; if (subblocks != 0) { GIsiSubBlockIter iter; struct isi_call call = { 0 }; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case CALL_CAUSE: isi_call_cause_sb_proc(NULL, &call, &iter); DBG("CALL_CREATE_RESP " "cause_type=0x%02x cause=0x%02x", call.cause_type, call.cause); goto failure; } } } isi_ctx_return_success(irc); return; failure: isi_ctx_return_failure(irc); } static struct isi_call_req_ctx *isi_modem_call_create_req( struct ofono_voicecall *ovc, uint8_t presentation, uint8_t addr_type, char const address[21], ofono_voicecall_cb_t cb, void *data) { size_t addr_len = strlen(address); size_t sub_len = ALIGN4(6 + 2 * addr_len); size_t offset = 3 + 4 + 4 + 6; uint8_t req[3 + 4 + 4 + 6 + 40] = { CALL_CREATE_REQ, 0, /* No id */ 3, /* Mode, Clir, Number */ CALL_MODE, 4, CALL_MODE_SPEECH, 0, CALL_LINE_ID, 4, presentation, 0, CALL_DESTINATION_ADDRESS, sub_len, addr_type & 0x7F, 0, 0, addr_len, /* uint16_t addr[20] */ }; size_t rlen = 3 + 4 + 4 + sub_len; size_t i; if (addr_len > 20) { CALLBACK_WITH_FAILURE(cb, data); return NULL; } for (i = 0; i < addr_len; i++) req[offset + 2 * i + 1] = address[i]; return isi_call_req(ovc, req, rlen, isi_call_create_resp, cb, data); } static struct isi_call_req_ctx *isi_call_create_req(struct ofono_voicecall *ovc, uint8_t presentation, uint8_t addr_type, char const address[21], ofono_voicecall_cb_t cb, void *data) { size_t addr_len = strlen(address); size_t sub_len = ALIGN4(6 + 2 * addr_len); size_t offset = 3 + 4 + 8 + 6; uint8_t req[3 + 4 + 8 + 6 + 40] = { CALL_CREATE_REQ, 0, /* No id */ 3, /* Mode, Clir, Number */ CALL_MODE, 4, CALL_MODE_SPEECH, CALL_MODE_INFO_NONE, CALL_ORIGIN_INFO, 8, presentation, 0, 0, 0, 0, 0, CALL_DESTINATION_ADDRESS, sub_len, addr_type & 0x7F, 0, 0, addr_len, /* uint16_t addr[20] */ }; size_t rlen = 3 + 4 + 8 + sub_len; size_t i; if (addr_len > 20) { CALLBACK_WITH_FAILURE(cb, data); return NULL; } for (i = 0; i < addr_len; i++) req[offset + 2 * i + 1] = address[i]; return isi_call_req(ovc, req, rlen, isi_call_create_resp, cb, data); } static void isi_call_status_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_voicecall *ovc = data; struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); struct isi_call *call; GIsiSubBlockIter iter; uint8_t call_id; uint8_t old_status; if (ivc == NULL || g_isi_msg_id(msg) != CALL_STATUS_IND || !g_isi_msg_data_get_byte(msg, 0, &call_id) || (call_id & 7) == 0) return; call = &ivc->calls[call_id & 7]; old_status = call->status; call->call_id = call_id; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case CALL_STATUS: isi_call_status_sb_proc(ivc, call, &iter); break; case CALL_MODE: isi_call_mode_sb_proc(ivc, call, &iter); break; case CALL_CAUSE: isi_call_cause_sb_proc(ivc, call, &iter); break; case CALL_DESTINATION_ADDRESS: isi_call_destination_address_sb_proc(ivc, call, &iter); break; case CALL_ORIGIN_ADDRESS: isi_call_origin_address_sb_proc(ivc, call, &iter); break; case CALL_ORIGIN_INFO: isi_call_origin_info_sb_proc(ivc, call, &iter); break; case CALL_GSM_DETAILED_CAUSE: case CALL_DESTINATION_PRE_ADDRESS: case CALL_DESTINATION_POST_ADDRESS: case CALL_DESTINATION_SUBADDRESS: case CALL_GSM_EVENT_INFO: case CALL_NW_CAUSE: break; } } if (old_status == call->status) return; isi_call_notify(ovc, call); } static void isi_call_terminated_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_voicecall *ovc = data; struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); struct isi_call *call; uint8_t call_id; uint8_t old_status; if (ivc == NULL || g_isi_msg_id(msg) != CALL_TERMINATED_IND || !g_isi_msg_data_get_byte(msg, 0, &call_id) || (call_id & 7) == 0) return; call = &ivc->calls[call_id & 7]; old_status = call->status; if (old_status == CALL_STATUS_IDLE) return; call->status = CALL_STATUS_TERMINATED; isi_call_notify(ovc, call); } static gboolean decode_notify(GIsiSubBlockIter *iter) { uint8_t byte; if (!g_isi_sb_iter_get_byte(iter, &byte, 2)) return FALSE; switch (byte) { case CALL_NOTIFY_USER_SUSPENDED: DBG("CALL_NOTIFY_USER_SUSPENDED"); break; case CALL_NOTIFY_USER_RESUMED: DBG("CALL_NOTIFY_USER_RESUMED"); break; case CALL_NOTIFY_BEARER_CHANGE: DBG("CALL_NOTIFY_BEARER_CHANGE"); break; default: DBG("Unknown notification: 0x%02X", byte); } return TRUE; } static gboolean decode_ss_code(GIsiSubBlockIter *iter, int *cssi, int *cssu) { uint16_t word; if (!g_isi_sb_iter_get_word(iter, &word, 2)) return FALSE; switch (word) { case CALL_SSC_ALL_FWDS: DBG("Call forwarding is active"); break; case CALL_SSC_ALL_COND_FWD: *cssi = SS_MO_CONDITIONAL_FORWARDING; DBG("Some of conditional call forwardings active"); break; case CALL_SSC_CFU: *cssi = SS_MO_UNCONDITIONAL_FORWARDING; DBG("Unconditional call forwarding is active"); break; case CALL_SSC_OUTGOING_BARR_SERV: *cssi = SS_MO_OUTGOING_BARRING; DBG("Outgoing calls are barred"); break; case CALL_SSC_INCOMING_BARR_SERV: *cssi = SS_MO_INCOMING_BARRING; DBG("Incoming calls are barred"); break; case CALL_SSC_CALL_WAITING: DBG("Incoming calls are barred"); break; case CALL_SSC_CLIR: DBG("CLIR connected unknown indication."); break; case CALL_SSC_MPTY: *cssu = SS_MT_MULTIPARTY_VOICECALL; DBG("Multiparty call entered."); break; case CALL_SSC_CALL_HOLD: *cssu = SS_MT_VOICECALL_HOLD_RELEASED; DBG("Call on hold has been released."); break; default: DBG("Unknown/unhandled notification: 0x%02X", word); break; } return TRUE; } static gboolean decode_ss_status(GIsiSubBlockIter *iter) { uint8_t byte; if (!g_isi_sb_iter_get_byte(iter, &byte, 2)) return FALSE; if (byte & CALL_SS_STATUS_ACTIVE) DBG("CALL_SS_STATUS_ACTIVE"); if (byte & CALL_SS_STATUS_REGISTERED) DBG("CALL_SS_STATUS_REGISTERED"); if (byte & CALL_SS_STATUS_PROVISIONED) DBG("CALL_SS_STATUS_PROVISIONED"); if (byte & CALL_SS_STATUS_QUIESCENT) DBG("CALL_SS_STATUS_QUIESCENT"); return TRUE; } static gboolean decode_ss_notify(GIsiSubBlockIter *iter, int *cssi, int *cssu) { uint8_t byte; if (!g_isi_sb_iter_get_byte(iter, &byte, 2)) return FALSE; if (byte & CALL_SSN_INCOMING_IS_FWD) { *cssu = SS_MT_CALL_FORWARDED; DBG("This is a forwarded call #1."); } if (byte & CALL_SSN_INCOMING_FWD) DBG("This is a forwarded call #2."); if (byte & CALL_SSN_OUTGOING_FWD) { *cssi = SS_MO_CALL_FORWARDED; DBG("Call has been forwarded."); } return TRUE; } static gboolean decode_ss_notify_indicator(GIsiSubBlockIter *iter, int *cssi) { uint8_t byte; if (!g_isi_sb_iter_get_byte(iter, &byte, 2)) return FALSE; if (byte & CALL_SSI_CALL_IS_WAITING) { *cssi = SS_MO_CALL_WAITING; DBG("Call is waiting."); } if (byte & CALL_SSI_MPTY) DBG("Multiparty call"); if (byte & CALL_SSI_CLIR_SUPPR_REJ) { *cssi = SS_MO_CLIR_SUPPRESSION_REJECTED; DBG("CLIR suppression rejected"); } return TRUE; } static gboolean decode_ss_hold_indicator(GIsiSubBlockIter *iter, int *cssu) { uint8_t byte; if (!g_isi_sb_iter_get_byte(iter, &byte, 2)) return FALSE; if (byte == CALL_HOLD_IND_RETRIEVED) { *cssu = SS_MT_VOICECALL_RETRIEVED; DBG("Call has been retrieved"); } else if (byte & CALL_HOLD_IND_ON_HOLD) { *cssu = SS_MT_VOICECALL_ON_HOLD; DBG("Call has been put on hold"); } else { return FALSE; } return TRUE; } static gboolean decode_ss_ect_indicator(GIsiSubBlockIter *iter, int *cssu) { uint8_t byte; if (!g_isi_sb_iter_get_byte(iter, &byte, 2)) return FALSE; if (byte & CALL_ECT_CALL_STATE_ALERT) { *cssu = SS_MT_VOICECALL_IN_TRANSFER; DBG("Call is being connected with the remote party in " "alerting state"); } if (byte & CALL_ECT_CALL_STATE_ACTIVE) { *cssu = SS_MT_VOICECALL_TRANSFERRED; DBG("Call has been connected with the other remote " "party in explicit call transfer operation."); } return TRUE; } static gboolean decode_remote_address(GIsiSubBlockIter *iter, struct ofono_phone_number *number, int *index) { uint8_t type, len; char *addr; if (!g_isi_sb_iter_get_byte(iter, &type, 2)) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &len, 5)) return FALSE; if (len > OFONO_MAX_PHONE_NUMBER_LENGTH) return FALSE; if (!g_isi_sb_iter_get_alpha_tag(iter, &addr, 2 * len, 6)) return FALSE; strncpy(number->number, addr, len); number->number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0'; number->type = type; g_free(addr); return TRUE; } static gboolean decode_cug_info(GIsiSubBlockIter *iter, int *index, int *cssu) { uint8_t pref; uint8_t access; uint16_t word; if (!g_isi_sb_iter_get_byte(iter, &pref, 2)) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &access, 3)) return FALSE; if (!g_isi_sb_iter_get_word(iter, &word, 4)) return FALSE; DBG("Preferential CUG: 0x%02X", pref); DBG("CUG output access: 0x%02X", access); *index = word; *cssu = SS_MO_CUG_CALL; return TRUE; } static void notification_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_voicecall *ovc = data; GIsiSubBlockIter iter; struct ofono_phone_number number; int index = 0; int cssi = -1; int cssu = -1; uint8_t call_id; if (ovc == NULL || g_isi_msg_id(msg) != CALL_GSM_NOTIFICATION_IND || !g_isi_msg_data_get_byte(msg, 0, &call_id) || (call_id & 7) == 0) return; DBG("Received CallServer notification for call: 0x%02X", call_id); for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case CALL_GSM_NOTIFY: if (!decode_notify(&iter)) return; break; case CALL_GSM_SS_CODE: if (!decode_ss_code(&iter, &cssi, &cssu)) return; break; case CALL_GSM_SS_STATUS: if (!decode_ss_status(&iter)) return; break; case CALL_GSM_SS_NOTIFY: if (!decode_ss_notify(&iter, &cssi, &cssu)) return; break; case CALL_GSM_SS_NOTIFY_INDICATOR: if (!decode_ss_notify_indicator(&iter, &cssi)) return; break; case CALL_GSM_SS_HOLD_INDICATOR: if (!decode_ss_hold_indicator(&iter, &cssu)) return; break; case CALL_GSM_SS_ECT_INDICATOR: if (!decode_ss_ect_indicator(&iter, &cssu)) return; break; case CALL_GSM_REMOTE_ADDRESS: if (!decode_remote_address(&iter, &number, &index)) return; break; case CALL_GSM_REMOTE_SUBADDRESS: break; case CALL_GSM_CUG_INFO: if (!decode_cug_info(&iter, &index, &cssu)) return; break; case CALL_ORIGIN_INFO: break; case CALL_GSM_ALERTING_PATTERN: break; case CALL_ALERTING_INFO: break; } } if (cssi != -1) ofono_voicecall_ssn_mo_notify(ovc, call_id & 7, cssi, index); if (cssu != -1) ofono_voicecall_ssn_mt_notify(ovc, call_id & 7, cssu, index, &number); } static void isi_call_answer_resp(const GIsiMessage *msg, void *data) { struct isi_call_req_ctx *irc = data; uint8_t call_id; if (!check_response_status(msg, CALL_ANSWER_RESP) || !g_isi_msg_data_get_byte(msg, 0, &call_id) || call_id == CALL_ID_NONE) { isi_ctx_return_failure(irc); return; } isi_ctx_return_success(irc); } static struct isi_call_req_ctx *isi_call_answer_req(struct ofono_voicecall *ovc, uint8_t call_id, ofono_voicecall_cb_t cb, void *data) { const uint8_t req[] = { CALL_ANSWER_REQ, call_id, 0 }; return isi_call_req(ovc, req, sizeof(req), isi_call_answer_resp, cb, data); } static void isi_call_release_resp(const GIsiMessage *msg, void *data) { struct isi_call_req_ctx *irc = data; GIsiSubBlockIter iter; uint8_t cause_type; uint8_t cause; if (!check_response_status(msg, CALL_RELEASE_RESP)) goto error; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE) continue; if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) || !g_isi_sb_iter_get_byte(&iter, &cause, 3)) goto error; } if ((cause_type == CALL_CAUSE_TYPE_SERVER || cause_type == CALL_CAUSE_TYPE_CLIENT) && (cause == CALL_CAUSE_RELEASE_BY_USER || cause == CALL_CAUSE_BUSY_USER_REQUEST)) { isi_ctx_return_success(irc); return; } error: isi_ctx_return_failure(irc); } static struct isi_call_req_ctx *isi_call_release_req( struct ofono_voicecall *ovc, uint8_t call_id, enum call_cause_type cause_type, uint8_t cause, ofono_voicecall_cb_t cb, void *data) { const uint8_t req[] = { CALL_RELEASE_REQ, call_id, 1, /* Sub-block count */ CALL_CAUSE, 4, /* Sub-block length */ cause_type, cause, }; return isi_call_req(ovc, req, sizeof(req), isi_call_release_resp, cb, data); } static void isi_call_status_resp(const GIsiMessage *msg, void *data) { struct isi_call_req_ctx *irc = data; struct ofono_voicecall *ovc = irc->ovc; struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); GIsiSubBlockIter iter; struct isi_call *call = NULL; if (!check_response_status(msg, CALL_STATUS_RESP)) { isi_ctx_return_failure(irc); return; } for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case CALL_STATUS_INFO: call = isi_call_status_info_sb_proc(ivc, &iter); break; case CALL_ADDR_AND_STATUS_INFO: call = isi_call_addr_and_status_info_sb_proc(ivc, &iter); if (call) isi_call_notify(ovc, call); break; case CALL_CAUSE: if (call) isi_call_cause_sb_proc(ivc, call, &iter); break; } } isi_ctx_return_success(irc); } static struct isi_call_req_ctx *isi_call_status_req(struct ofono_voicecall *ovc, uint8_t call_id, uint8_t mode, ofono_voicecall_cb_t cb, void *data) { const uint8_t req[] = { CALL_STATUS_REQ, call_id, 1, /* Sub-block count */ CALL_STATUS_MODE, 4, /* Sub-block length */ mode, 0, }; return isi_call_req(ovc, req, sizeof(req), isi_call_status_resp, cb, data); } static void isi_call_control_resp(const GIsiMessage *msg, void *data) { struct isi_call_req_ctx *irc = data; GIsiSubBlockIter iter; uint8_t cause = CALL_CAUSE_NO_CAUSE; uint8_t cause_type = 0; if (!check_response_status(msg, CALL_CONTROL_RESP)) goto error; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE) continue; if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) || !g_isi_sb_iter_get_byte(&iter, &cause, 3)) goto error; } if (cause == CALL_CAUSE_NO_CAUSE) { isi_ctx_return_success(irc); return; } error: isi_ctx_return_failure(irc); } static struct isi_call_req_ctx *isi_call_control_req( struct ofono_voicecall *ovc, uint8_t call_id, enum call_operation op, uint8_t info, ofono_voicecall_cb_t cb, void *data) { const uint8_t req[] = { CALL_CONTROL_REQ, call_id, 1, /* Sub-block count */ CALL_OPERATION, 4, /* Sub-block length */ op, info, }; return isi_call_req(ovc, req, sizeof(req), isi_call_control_resp, cb, data); } static struct isi_call_req_ctx *isi_call_deflect_req( struct ofono_voicecall *ovc, uint8_t call_id, uint8_t address_type, const char address[21], ofono_voicecall_cb_t cb, void *data) { size_t addr_len = strlen(address); size_t sub_len = (6 + 2 * addr_len + 3) & ~3; size_t i, offset = 3 + 4 + 6; size_t rlen = 3 + 4 + sub_len; uint8_t req[3 + 4 + 6 + 40] = { CALL_CONTROL_REQ, call_id, 2, /* Sub-block count */ CALL_OPERATION, 4, /* Sub-block length */ CALL_GSM_OP_DEFLECT, 0, CALL_GSM_DEFLECTION_ADDRESS, sub_len, /* Sub-block length */ address_type & 0x7F, 0x7, /* Default presentation */ 0, /* Filler */ addr_len, }; if (addr_len > 20) { CALLBACK_WITH_FAILURE(cb, data); return NULL; } for (i = 0; i < addr_len; i++) req[offset + 2 * i + 1] = address[i]; return isi_call_req(ovc, req, rlen, isi_call_control_resp, cb, data); } static void isi_call_control_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_voicecall *ovc = data; struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); GIsiSubBlockIter iter; uint8_t cause_type = 0, cause = 0; if (ivc == NULL || g_isi_msg_id(msg) != CALL_CONTROL_IND) return; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE) continue; if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) || !g_isi_sb_iter_get_byte(&iter, &cause, 3)) return; } if (ivc->control_req_irc) { if (!cause) isi_ctx_return_success(ivc->control_req_irc); else isi_ctx_return_failure(ivc->control_req_irc); ivc->control_req_irc = NULL; } } static void isi_call_dtmf_send_resp(const GIsiMessage *msg, void *data) { struct isi_call_req_ctx *irc = data; GIsiSubBlockIter iter; uint8_t cause_type; uint8_t cause = CALL_CAUSE_NO_CAUSE; if (!check_response_status(msg, CALL_DTMF_SEND_RESP)) goto error; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE) continue; if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) || !g_isi_sb_iter_get_byte(&iter, &cause, 3)) goto error; } if (cause == CALL_CAUSE_NO_CAUSE) { isi_ctx_return_success(irc); return; } error: isi_ctx_return_failure(irc); } static struct isi_call_req_ctx *isi_call_dtmf_send_req( struct ofono_voicecall *ovc, uint8_t call_id, const char *string, ofono_voicecall_cb_t cb, void *data) { size_t str_len = strlen(string); size_t sub_len = 4 + ((2 * str_len + 3) & ~3); size_t i, offset = 3 + 4 + 8 + 4; size_t rlen = 3 + 4 + 8 + sub_len; uint8_t req[3 + 4 + 8 + (255 & ~3)] = { CALL_DTMF_SEND_REQ, call_id, 3, CALL_DTMF_INFO, 4, CALL_DTMF_ENABLE_TONE_IND_SEND, 0, CALL_DTMF_TIMERS, 8, 0, 200, /* duration in ms */ 0, 100, /* gap in ms */ 0, 0, /* filler */ CALL_DTMF_STRING, sub_len, 100, /* pause length */ str_len, /* string */ }; if (sub_len >= 256) { CALLBACK_WITH_FAILURE(cb, data); return FALSE; } for (i = 0; i < str_len; i++) req[offset + 2 * i + 1] = string[i]; return isi_call_req(ovc, req, rlen, isi_call_dtmf_send_resp, cb, data); } static void isi_dial(struct ofono_voicecall *ovc, const struct ofono_phone_number *number, enum ofono_clir_option clir, ofono_voicecall_cb_t cb, void *data) { struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); gboolean have_pn_call = g_isi_client_resource(ivc->client) == PN_CALL; unsigned char presentation; switch (clir) { case OFONO_CLIR_OPTION_INVOCATION: presentation = CALL_PRESENTATION_RESTRICTED; break; case OFONO_CLIR_OPTION_SUPPRESSION: presentation = CALL_PRESENTATION_ALLOWED; break; case OFONO_CLIR_OPTION_DEFAULT: default: presentation = have_pn_call ? CALL_GSM_PRESENTATION_DEFAULT : CALL_MODEM_PROP_PRESENT_DEFAULT; } if (have_pn_call) isi_call_create_req(ovc, presentation, number->type, number->number, cb, data); else isi_modem_call_create_req(ovc, presentation, number->type, number->number, cb, data); } static void isi_answer(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); int id; for (id = 1; id <= 7; id++) if (ivc->calls[id].status == CALL_STATUS_MT_ALERTING) goto answer_by_id; id = CALL_ID_ALL; answer_by_id: isi_call_answer_req(ovc, id, cb, data); } static void isi_hangup_current(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { /* * Hangup call(s) that are not held or waiting: * active calls or calls in progress. */ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); int id = 0; uint8_t cause = CALL_CAUSE_RELEASE_BY_USER; for (id = 1; id <= 7; id++) { if (ivc->calls[id].call_id & CALL_ID_WAITING) continue; if (ivc->calls[id].call_id & CALL_ID_HOLD) continue; switch (ivc->calls[id].status) { case CALL_STATUS_CREATE: case CALL_STATUS_COMING: case CALL_STATUS_MO_ALERTING: case CALL_STATUS_ANSWERED: case CALL_STATUS_HOLD_INITIATED: goto release_by_id; case CALL_STATUS_MT_ALERTING: cause = CALL_CAUSE_BUSY_USER_REQUEST; goto release_by_id; case CALL_STATUS_PROCEEDING: if (ivc->calls[id].mode_info & CALL_MODE_ORIGINATOR) cause = CALL_CAUSE_BUSY_USER_REQUEST; goto release_by_id; } } id = CALL_ID_ACTIVE; release_by_id: isi_call_release_req(ovc, id, CALL_CAUSE_TYPE_CLIENT, cause, cb, data); } static void isi_release_all_held(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { isi_call_release_req(ovc, CALL_ID_HOLD, CALL_CAUSE_TYPE_CLIENT, CALL_CAUSE_RELEASE_BY_USER, cb, data); } static void isi_set_udub(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { /* Release waiting calls */ isi_call_release_req(ovc, CALL_ID_WAITING, CALL_CAUSE_TYPE_CLIENT, CALL_CAUSE_BUSY_USER_REQUEST, cb, data); } static void isi_retrieve(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { isi_call_control_req(ovc, CALL_ID_HOLD, CALL_OP_RETRIEVE, 0, cb, data); } static void isi_wait_and_answer(struct isi_call_req_ctx *irc, int event) { DBG("irc=%p event=%u", (void *) irc, event); if (event != CALL_STATUS_TERMINATED) return; isi_answer(irc->ovc, irc->cb, irc->data); isi_ctx_free(irc); } static void isi_wait_and_retrieve(struct isi_call_req_ctx *irc, int event) { DBG("irc=%p event=%u", (void *) irc, event); if (event != CALL_STATUS_TERMINATED) return; isi_retrieve(irc->ovc, irc->cb, irc->data); isi_ctx_free(irc); } static void isi_release_all_active(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); struct isi_call_req_ctx *irc; int id = 0; int waiting = 0; int active = 0; int hold = 0; for (id = 1; id <= 7; id++) { if (ivc->calls[id].call_id & CALL_ID_WAITING) waiting++; if (ivc->calls[id].call_id & CALL_ID_HOLD) hold++; if (ivc->calls[id].call_id & CALL_ID_ACTIVE) active++; } if (!active) { CALLBACK_WITH_FAILURE(cb, data); return; } irc = isi_call_release_req(ovc, CALL_ID_ACTIVE, CALL_CAUSE_TYPE_CLIENT, CALL_CAUSE_RELEASE_BY_USER, cb, data); if (irc == NULL) return; if (waiting) isi_ctx_queue(irc, isi_wait_and_answer); else if (hold) isi_ctx_queue(irc, isi_wait_and_retrieve); } static void isi_hold_all_active(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); int id = 0; int op = 0; int waiting = 0; int active = 0; int hold = 0; for (id = 1; id <= 7; id++) { if (ivc->calls[id].call_id & CALL_ID_WAITING) waiting++; if (ivc->calls[id].call_id & CALL_ID_HOLD) hold++; if (ivc->calls[id].call_id & CALL_ID_ACTIVE) active++; } if (!waiting && !hold && !active) { CALLBACK_WITH_FAILURE(cb, data); return; } if (waiting) { isi_call_answer_req(ovc, CALL_ID_WAITING, cb, data); } else if (hold) { if (active) { op = CALL_OP_SWAP; id = CALL_ID_ACTIVE; } else { op = CALL_OP_RETRIEVE; id = CALL_ID_HOLD; } isi_call_control_req(ovc, id, op, 0, cb, data); } else if (active) { id = CALL_ID_ACTIVE; op = CALL_OP_HOLD; isi_call_control_req(ovc, id, op, 0, cb, data); } } static void isi_release_specific(struct ofono_voicecall *ovc, int id, ofono_voicecall_cb_t cb, void *data) { struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); const struct isi_call *status; uint8_t cause; if (id < 1 || id > 7) { CALLBACK_WITH_FAILURE(cb, data); return; } status = &ivc->calls[id]; cause = CALL_CAUSE_RELEASE_BY_USER; switch (status->status) { case CALL_STATUS_MT_ALERTING: case CALL_STATUS_WAITING: cause = CALL_CAUSE_BUSY_USER_REQUEST; break; case CALL_STATUS_PROCEEDING: if ((status->mode_info & CALL_MODE_ORIGINATOR)) cause = CALL_CAUSE_BUSY_USER_REQUEST; break; } isi_call_release_req(ovc, id, CALL_CAUSE_TYPE_CLIENT, cause, cb, data); } static void isi_private_chat(struct ofono_voicecall *ovc, int id, ofono_voicecall_cb_t cb, void *data) { if (id < 1 || id > 7) { CALLBACK_WITH_FAILURE(cb, data); return; } isi_call_control_req(ovc, id, CALL_OP_CONFERENCE_SPLIT, 0, cb, data); } static void isi_create_multiparty(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { isi_call_control_req(ovc, CALL_ID_ALL, CALL_OP_CONFERENCE_BUILD, 0, cb, data); } static void isi_transfer(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); uint8_t id; for (id = 1; id <= 7; id++) { if (ivc->calls[id].status == CALL_STATUS_MO_ALERTING) break; } if (id > 7) id = CALL_ID_ACTIVE; isi_call_control_req(ovc, id, CALL_GSM_OP_TRANSFER, 0, cb, data); } static void isi_deflect(struct ofono_voicecall *ovc, const struct ofono_phone_number *ph, ofono_voicecall_cb_t cb, void *data) { isi_call_deflect_req(ovc, CALL_ID_WAITING, ph->type, ph->number, cb, data); } static void isi_swap_without_accept(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); int id = 0; int op = 0; int active = 0; int hold = 0; for (id = 1; id <= 7; id++) { if (ivc->calls[id].call_id & CALL_ID_HOLD) hold++; if (ivc->calls[id].call_id & CALL_ID_ACTIVE) active++; } if (hold && active) { id = CALL_ID_ACTIVE; op = CALL_OP_SWAP; } else if (active) { id = CALL_ID_ACTIVE; op = CALL_OP_HOLD; } else if (hold) { id = CALL_ID_HOLD; op = CALL_OP_RETRIEVE; } else { CALLBACK_WITH_FAILURE(cb, data); return; } isi_call_control_req(ovc, id, op, 0, cb, data); } static void isi_send_tones(struct ofono_voicecall *ovc, const char *tones, ofono_voicecall_cb_t cb, void *data) { isi_call_dtmf_send_req(ovc, CALL_ID_ALL, tones, cb, data); } static void subscribe_indications(GIsiClient *cl, void *data) { g_isi_client_ind_subscribe(cl, CALL_STATUS_IND, isi_call_status_ind_cb, data); g_isi_client_ind_subscribe(cl, CALL_CONTROL_IND, isi_call_control_ind_cb, data); g_isi_client_ind_subscribe(cl, CALL_TERMINATED_IND, isi_call_terminated_ind_cb, data); g_isi_client_ind_subscribe(cl, CALL_GSM_NOTIFICATION_IND, notification_ind_cb, data); } static void pn_call_verify_cb(const GIsiMessage *msg, void *data) { struct ofono_voicecall *ovc = data; struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); if (g_isi_msg_error(msg) < 0) { DBG("PN_CALL not reachable, removing client"); g_isi_client_destroy(ivc->pn_call); ivc->pn_call = NULL; if (ivc->pn_modem_call == NULL) ofono_voicecall_remove(ovc); return; } ISI_RESOURCE_DBG(msg); if (ivc == NULL || ivc->client != NULL) return; ivc->client = ivc->pn_call; subscribe_indications(ivc->client, ovc); if (!isi_call_status_req(ovc, CALL_ID_ALL, CALL_STATUS_MODE_ADDR_AND_ORIGIN, NULL, NULL)) DBG("Failed to request call status"); ofono_voicecall_register(ovc); } static void pn_modem_call_verify_cb(const GIsiMessage *msg, void *data) { struct ofono_voicecall *ovc = data; struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); if (g_isi_msg_error(msg) < 0) { DBG("PN_MODEM_CALL not reachable, removing client"); g_isi_client_destroy(ivc->pn_modem_call); ivc->pn_modem_call = NULL; if (ivc->pn_call == NULL) ofono_voicecall_remove(ovc); return; } ISI_RESOURCE_DBG(msg); if (ivc == NULL || ivc->client != NULL) return; ivc->client = ivc->pn_modem_call; subscribe_indications(ivc->client, ovc); if (!isi_call_status_req(ovc, CALL_ID_ALL, CALL_STATUS_MODE_ADDR_AND_ORIGIN, NULL, NULL)) DBG("Failed to request call status"); ofono_voicecall_register(ovc); } static int isi_probe(struct ofono_voicecall *ovc, unsigned int vendor, void *user) { GIsiModem *modem = user; struct isi_voicecall *ivc; int id; ivc = g_try_new0(struct isi_voicecall, 1); if (ivc == NULL) return -ENOMEM; for (id = 0; id <= 7; id++) ivc->calls[id].id = id; ivc->pn_call = g_isi_client_create(modem, PN_CALL); if (ivc->pn_call == NULL) { g_free(ivc); return -ENOMEM; } ivc->pn_modem_call = g_isi_client_create(modem, PN_MODEM_CALL); if (ivc->pn_call == NULL) { g_isi_client_destroy(ivc->pn_call); g_free(ivc); return -ENOMEM; } ofono_voicecall_set_data(ovc, ivc); g_isi_client_verify(ivc->pn_call, pn_call_verify_cb, ovc, NULL); g_isi_client_verify(ivc->pn_modem_call, pn_modem_call_verify_cb, ovc, NULL); return 0; } static void isi_remove(struct ofono_voicecall *call) { struct isi_voicecall *data = ofono_voicecall_get_data(call); ofono_voicecall_set_data(call, NULL); if (data == NULL) return; g_isi_client_destroy(data->pn_call); g_isi_client_destroy(data->pn_modem_call); g_free(data); } static struct ofono_voicecall_driver driver = { .name = "isimodem", .probe = isi_probe, .remove = isi_remove, .dial = isi_dial, .answer = isi_answer, .hangup_active = isi_hangup_current, .hold_all_active = isi_hold_all_active, .release_all_held = isi_release_all_held, .set_udub = isi_set_udub, .release_all_active = isi_release_all_active, .release_specific = isi_release_specific, .private_chat = isi_private_chat, .create_multiparty = isi_create_multiparty, .transfer = isi_transfer, .deflect = isi_deflect, .swap_without_accept = isi_swap_without_accept, .send_tones = isi_send_tones, }; void isi_voicecall_init(void) { ofono_voicecall_driver_register(&driver); } void isi_voicecall_exit(void) { ofono_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/audio-settings.c0000644000015600001650000000604512671500024024773 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "isiutil.h" #include "isimodem.h" #include "call.h" #include "debug.h" struct audio_settings_data { GIsiClient *client; }; static void isi_call_server_status_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_audio_settings *oas = data; uint8_t status; if (g_isi_msg_id(msg) != CALL_SERVER_STATUS_IND) return; if (!g_isi_msg_data_get_byte(msg, 0, &status)) return; ofono_audio_settings_active_notify(oas, status ? TRUE : FALSE); } static void isi_call_verify_cb(const GIsiMessage *msg, void *data) { struct ofono_audio_settings *as = data; struct audio_settings_data *asd = ofono_audio_settings_get_data(as); if (g_isi_msg_error(msg) < 0) { ofono_audio_settings_remove(as); return; } ISI_RESOURCE_DBG(msg); g_isi_client_ind_subscribe(asd->client, CALL_SERVER_STATUS_IND, isi_call_server_status_ind_cb, as); ofono_audio_settings_register(as); } static int isi_audio_settings_probe(struct ofono_audio_settings *as, unsigned int vendor, void *data) { GIsiModem *modem = data; struct audio_settings_data *asd; asd = g_try_new0(struct audio_settings_data, 1); if (asd == NULL) return -ENOMEM; asd->client = g_isi_client_create(modem, PN_CALL); if (asd->client == NULL) { g_free(asd); return -ENOMEM; } ofono_audio_settings_set_data(as, asd); g_isi_client_verify(asd->client, isi_call_verify_cb, as, NULL); return 0; } static void isi_audio_settings_remove(struct ofono_audio_settings *as) { struct audio_settings_data *asd = ofono_audio_settings_get_data(as); ofono_audio_settings_set_data(as, NULL); if (asd == NULL) return; g_isi_client_destroy(asd->client); g_free(asd); } static struct ofono_audio_settings_driver driver = { .name = "isimodem", .probe = isi_audio_settings_probe, .remove = isi_audio_settings_remove, }; void isi_audio_settings_init(void) { ofono_audio_settings_driver_register(&driver); } void isi_audio_settings_exit(void) { ofono_audio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/call-forwarding.c0000644000015600001650000002505012671500024025104 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "isimodem.h" #include "isiutil.h" #include "ss.h" #include "debug.h" struct forw_data { GIsiClient *client; }; struct forw_info { uint8_t bsc; /* Basic service code */ uint8_t status; /* SS status */ uint8_t ton; /* Type of number */ uint8_t noreply; /* No reply timeout */ uint8_t forw_opt; /* Forwarding option */ uint8_t numlen; /* Number length */ uint8_t sublen; /* Sub-address length */ uint8_t filler; }; static int forw_type_to_isi_code(int type) { int ss_code; switch (type) { case 0: ss_code = SS_GSM_FORW_UNCONDITIONAL; break; case 1: ss_code = SS_GSM_FORW_BUSY; break; case 2: ss_code = SS_GSM_FORW_NO_REPLY; break; case 3: ss_code = SS_GSM_FORW_NO_REACH; break; case 4: ss_code = SS_GSM_ALL_FORWARDINGS; break; case 5: ss_code = SS_GSM_ALL_COND_FORWARDINGS; break; default: DBG("Unknown forwarding type %d", type); ss_code = -1; break; } return ss_code; } static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid, uint8_t type) { uint8_t service; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", g_isi_msg_strerror(msg)); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", ss_message_id_name(g_isi_msg_id(msg))); return FALSE; } if (!g_isi_msg_data_get_byte(msg, 0, &service) || service != type) { DBG("Unexpected service type: 0x%02X", service); return FALSE; } return TRUE; } static gboolean decode_gsm_forwarding_info(GIsiSubBlockIter *parent, uint8_t *status, uint8_t *ton, uint8_t *noreply, char **number) { GIsiSubBlockIter iter; struct forw_info *info; size_t len = sizeof(struct forw_info); char *tag = NULL; for (g_isi_sb_subiter_init(parent, &iter, 4); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_FEATURE) continue; if (!g_isi_sb_iter_get_struct(&iter, (void *) &info, len, 2)) return FALSE; if (info->numlen != 0) { if (!g_isi_sb_iter_get_alpha_tag(&iter, &tag, info->numlen * 2, 2 + len)) return FALSE; if (number) *number = tag; else g_free(tag); } else { if (number) *number = g_strdup(""); } if (status) *status = info->status; if (ton) *ton = info->ton; if (noreply) *noreply = info->noreply; return TRUE; } return FALSE; } static void registration_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_call_forwarding_set_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint8_t status; if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_REGISTRATION)) goto error; for (g_isi_sb_iter_init(&iter, msg, 6); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_INFO) continue; if (!decode_gsm_forwarding_info(&iter, &status, NULL, NULL, NULL)) goto error; if (status & (SS_GSM_ACTIVE | SS_GSM_REGISTERED)) { CALLBACK_WITH_SUCCESS(cb, cbd->data); return; } } error: CALLBACK_WITH_FAILURE(cb, cbd->data); } static void isi_registration(struct ofono_call_forwarding *cf, int type, int cls, const struct ofono_phone_number *number, int time, ofono_call_forwarding_set_cb_t cb, void *data) { struct forw_data *fd = ofono_call_forwarding_get_data(cf); struct isi_cb_data *cbd = isi_cb_data_new(cf, cb, data); int ss_code = forw_type_to_isi_code(type); char *ucs2 = NULL; size_t numlen = strlen(number->number); size_t sb_len = ALIGN4(6 + 2 * numlen); size_t pad_len = sb_len - (6 + 2 * numlen); uint8_t msg[7 + 6 + 28 * 2 + 3] = { SS_SERVICE_REQ, SS_REGISTRATION, SS_GSM_TELEPHONY, ss_code >> 8, ss_code & 0xFF, SS_SEND_ADDITIONAL_INFO, 1, /* Subblock count */ SS_FORWARDING, sb_len, number->type, ss_code == SS_GSM_FORW_NO_REPLY ? time : SS_UNDEFINED_TIME, numlen, 0, /* Sub address length */ /* * Followed by number in UCS-2 (no NULL termination), * zero sub address bytes, and 0 to 3 bytes of filler */ }; size_t msg_len = 7 + 6 + numlen * 2 + pad_len; if (cbd == NULL || fd == NULL || numlen > 28) goto error; DBG("forwarding type %d class %d number %s", type, cls, number->number); if (ss_code < 0) goto error; ucs2 = g_convert(number->number, numlen, "UCS-2BE", "UTF-8//TRANSLIT", NULL, NULL, NULL); if (ucs2 == NULL) goto error; memcpy(msg + 13, ucs2, numlen * 2); g_free(ucs2); if (g_isi_client_send(fd->client, msg, msg_len, registration_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void erasure_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_call_forwarding_set_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint8_t status; if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_ERASURE)) goto error; for (g_isi_sb_iter_init(&iter, msg, 6); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_INFO) continue; if (!decode_gsm_forwarding_info(&iter, &status, NULL, NULL, NULL)) goto error; if (status & (SS_GSM_ACTIVE | SS_GSM_REGISTERED)) goto error; } CALLBACK_WITH_SUCCESS(cb, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, cbd->data); } static void isi_erasure(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data) { struct forw_data *fd = ofono_call_forwarding_get_data(cf); struct isi_cb_data *cbd = isi_cb_data_new(cf, cb, data); int ss_code = forw_type_to_isi_code(type); const uint8_t msg[] = { SS_SERVICE_REQ, SS_ERASURE, SS_GSM_TELEPHONY, ss_code >> 8, ss_code & 0xFF, SS_SEND_ADDITIONAL_INFO, 0, /* Subblock count */ }; DBG("forwarding type %d class %d", type, cls); if (cbd == NULL || fd == NULL || ss_code < 0) goto error; if (g_isi_client_send(fd->client, msg, sizeof(msg), erasure_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void query_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_call_forwarding_query_cb_t cb = cbd->cb; GIsiSubBlockIter iter; struct ofono_call_forwarding_condition list = { .status = 0, .cls = 7, .time = 0, .phone_number = { .number[0] = '\0', .type = 0, }, }; uint8_t status; uint8_t ton; uint8_t noreply; char *number = NULL; if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_INTERROGATION)) goto error; for (g_isi_sb_iter_init(&iter, msg, 6); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { DBG("Got %s", ss_subblock_name(g_isi_sb_iter_get_id(&iter))); if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_INFO) continue; if (!decode_gsm_forwarding_info(&iter, &status, &ton, &noreply, &number)) goto error; list.status = status & (SS_GSM_ACTIVE | SS_GSM_REGISTERED | SS_GSM_PROVISIONED); list.time = noreply; list.phone_number.type = ton | 0x80; DBG("Number <%s>", number); strncpy(list.phone_number.number, number, OFONO_MAX_PHONE_NUMBER_LENGTH); list.phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0'; g_free(number); DBG("forwarding query: %d, %d, %s(%d) - %d sec", list.status, list.cls, list.phone_number.number, list.phone_number.type, list.time); } CALLBACK_WITH_SUCCESS(cb, 1, &list, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data); } static void isi_query(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_query_cb_t cb, void *data) { struct forw_data *fd = ofono_call_forwarding_get_data(cf); struct isi_cb_data *cbd = isi_cb_data_new(cf, cb, data); int ss_code = forw_type_to_isi_code(type); const uint8_t msg[] = { SS_SERVICE_REQ, SS_INTERROGATION, SS_GSM_TELEPHONY, ss_code >> 8, ss_code & 0xFF, SS_SEND_ADDITIONAL_INFO, 0, /* Subblock count */ }; DBG("forwarding type %d class %d", type, cls); if (cbd == NULL || fd == NULL || cls != 7 || ss_code < 0) goto error; if (g_isi_client_send(fd->client, msg, sizeof(msg), query_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, 0, NULL, data); g_free(cbd); } static void reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_call_forwarding *cf = data; if (g_isi_msg_error(msg) < 0) { ofono_call_forwarding_remove(cf); return; } ISI_RESOURCE_DBG(msg); ofono_call_forwarding_register(cf); } static int isi_call_forwarding_probe(struct ofono_call_forwarding *cf, unsigned int vendor, void *user) { GIsiModem *modem = user; struct forw_data *fd; fd = g_try_new0(struct forw_data, 1); if (fd == NULL) return -ENOMEM; fd->client = g_isi_client_create(modem, PN_SS); if (fd->client == NULL) { g_free(fd); return -ENOMEM; } ofono_call_forwarding_set_data(cf, fd); g_isi_client_verify(fd->client, reachable_cb, cf, NULL); return 0; } static void isi_call_forwarding_remove(struct ofono_call_forwarding *cf) { struct forw_data *data = ofono_call_forwarding_get_data(cf); ofono_call_forwarding_set_data(cf, NULL); if (data == NULL) return; g_isi_client_destroy(data->client); g_free(data); } static struct ofono_call_forwarding_driver driver = { .name = "isimodem", .probe = isi_call_forwarding_probe, .remove = isi_call_forwarding_remove, .activation = NULL, .registration = isi_registration, .deactivation = NULL, .erasure = isi_erasure, .query = isi_query }; void isi_call_forwarding_init(void) { ofono_call_forwarding_driver_register(&driver); } void isi_call_forwarding_exit(void) { ofono_call_forwarding_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/cbs.c0000644000015600001650000001267012671500024022604 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "isimodem.h" #include "isiutil.h" #include "sms.h" #include "debug.h" struct cbs_data { GIsiClient *client; }; struct cbs_info { uint8_t pdu[88]; }; static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid) { uint8_t cause; uint8_t reason; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", strerror(-g_isi_msg_error(msg))); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", sms_message_id_name(g_isi_msg_id(msg))); return FALSE; } if (!g_isi_msg_data_get_byte(msg, 0, &cause)) return FALSE; if (cause == SMS_OK) return TRUE; if (!g_isi_msg_data_get_byte(msg, 1, &reason)) return FALSE; if (reason == SMS_ERR_PP_RESERVED) { DBG("Request failed: 0x%02"PRIx8" (%s).\n\n Unable to " "bootstrap CBS routing.\n It appears some other " "component is already\n registered as the CBS " "routing endpoint.\n As a consequence, " "receiving CBSs is not going to work.\n\n", reason, sms_isi_cause_name(reason)); } return FALSE; } static void isi_set_topics(struct ofono_cbs *cbs, const char *topics, ofono_cbs_set_cb_t cb, void *data) { DBG("Not implemented (topics=%s), all topics accepted", topics); CALLBACK_WITH_SUCCESS(cb, data); } static void isi_clear_topics(struct ofono_cbs *cbs, ofono_cbs_set_cb_t cb, void *data) { DBG("Not implemented"); CALLBACK_WITH_SUCCESS(cb, data); } static void routing_ntf_cb(const GIsiMessage *msg, void *data) { struct ofono_cbs *cbs = data; struct cbs_info *info; size_t len = sizeof(struct cbs_info); GIsiSubBlockIter iter; if (!check_resp(msg, SMS_GSM_CB_ROUTING_NTF)) return; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != SMS_GSM_CB_MESSAGE) continue; if (!g_isi_sb_iter_get_struct(&iter, (void *) &info, len, 2)) return; ofono_cbs_notify(cbs, info->pdu, len); return; } } static void routing_resp_cb(const GIsiMessage *msg, void *data) { struct ofono_cbs *cbs = data; struct cbs_data *cd = ofono_cbs_get_data(cbs); if (!check_resp(msg, SMS_GSM_CB_ROUTING_RESP)) { ofono_cbs_remove(cbs); return; } g_isi_client_ntf_subscribe(cd->client, SMS_GSM_CB_ROUTING_NTF, routing_ntf_cb, cbs); ofono_cbs_register(cbs); } static void cbs_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_cbs *cbs = data; struct cbs_data *cd = ofono_cbs_get_data(cbs); const uint8_t req[] = { SMS_GSM_CB_ROUTING_REQ, SMS_ROUTING_SET, SMS_GSM_ROUTING_MODE_ALL, SMS_CB_NOT_ALLOWED_IDS_LIST, 0x00, /* Subject count */ 0x00, /* Language count */ 0x00, /* CB range */ 0x00, /* Subject list MSBS */ 0x00, /* Subject list LSBS */ 0x00 /* Languages */ }; if (g_isi_msg_error(msg) < 0) { DBG("Unable to find CBS resource"); ofono_cbs_remove(cbs); return; } ISI_RESOURCE_DBG(msg); g_isi_client_send(cd->client, req, sizeof(req), routing_resp_cb, cbs, NULL); } static int isi_cbs_probe(struct ofono_cbs *cbs, unsigned int vendor, void *user) { GIsiModem *modem = user; struct cbs_data *cd = g_try_new0(struct cbs_data, 1); if (cd == NULL) return -ENOMEM; cd->client = g_isi_client_create(modem, PN_SMS); if (cd->client == NULL) { g_free(cd); return -ENOMEM; } ofono_cbs_set_data(cbs, cd); g_isi_client_verify(cd->client, cbs_reachable_cb, cbs, NULL); return 0; } static void isi_cbs_remove(struct ofono_cbs *cbs) { struct cbs_data *cd = ofono_cbs_get_data(cbs); const uint8_t msg[] = { SMS_GSM_CB_ROUTING_REQ, SMS_ROUTING_RELEASE, SMS_GSM_ROUTING_MODE_ALL, SMS_CB_NOT_ALLOWED_IDS_LIST, 0x00, /* Subject count */ 0x00, /* Language count */ 0x00, /* CB range */ 0x00, /* Subject list MSBS */ 0x00, /* Subject list LSBS */ 0x00 /* Languages */ }; ofono_cbs_set_data(cbs, NULL); if (cd == NULL) return; /* * Send a promiscuous routing release, so as not to hog * resources unnecessarily after being removed. */ g_isi_client_send(cd->client, msg, sizeof(msg), NULL, NULL, NULL); g_isi_client_destroy(cd->client); g_free(cd); } static struct ofono_cbs_driver driver = { .name = "isimodem", .probe = isi_cbs_probe, .remove = isi_cbs_remove, .set_topics = isi_set_topics, .clear_topics = isi_clear_topics }; void isi_cbs_init(void) { ofono_cbs_driver_register(&driver); } void isi_cbs_exit(void) { ofono_cbs_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/debug.h0000644000015600001650000000730712671500024023131 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_DEBUG_H #define __ISIMODEM_DEBUG_H #include #include "ss.h" #include "mtc.h" #include "sms.h" #include "uicc.h" #include "sim.h" #include "info.h" #include "call.h" #include "network.h" #include "gss.h" #include "gpds.h" const char *ss_message_id_name(enum ss_message_id value); const char *ss_subblock_name(enum ss_subblock value); const char *ss_ussd_type_name(enum ss_ussd_type value); const char *mtc_isi_cause_name(enum mtc_isi_cause value); const char *mtc_message_id_name(enum mtc_message_id value); const char *mtc_modem_state_name(enum mtc_modem_state value); const char *mce_rf_state_name(enum mce_rf_state value); const char *mce_message_id_name(enum mce_message_id value); const char *mce_modem_state_name(enum mce_modem_state value); const char *mce_status_info(enum mce_status_info value); const char *uicc_message_id_name(enum uicc_message_id value); const char *uicc_subblock_name(uint8_t value); const char *uicc_service_type_name(uint8_t value); const char *uicc_status_name(uint8_t value); const char *uicc_details_name(uint8_t value); const char *sms_gsm_cause_name(enum sms_gsm_cause value); const char *sms_isi_cause_name(enum sms_isi_cause value); const char *sms_message_id_name(enum sms_message_id value); const char *sms_subblock_name(enum sms_subblock value); const char *sim_isi_cause_name(enum sim_isi_cause value); const char *sim_message_id_name(enum sim_message_id value); const char *sim_subblock_name(enum sim_subblock value); enum ofono_sim_password_type; const char *sim_password_name(enum ofono_sim_password_type value); const char *sec_message_id_name(enum sec_message_id value); const char *info_isi_cause_name(enum info_isi_cause value); const char *info_message_id_name(enum info_message_id value); const char *info_subblock_name(enum info_subblock value); const char *call_gsm_cause_name(enum call_gsm_cause value); const char *call_isi_cause_name(enum call_isi_cause value); const char *call_status_name(enum call_status value); const char *call_message_id_name(enum call_message_id value); const char *net_gsm_cause_name(enum net_gsm_cause value); const char *net_isi_cause_name(enum net_isi_cause value); const char *net_status_name(enum net_reg_status value); const char *net_message_id_name(enum net_message_id value); const char *net_subblock_name(enum net_subblock value); const char *gss_message_id_name(enum gss_message_id value); const char *gss_subblock_name(enum gss_subblock value); const char *gpds_message_id_name(enum gpds_message_id value); const char *gpds_subblock_name(enum gpds_subblock value); const char *gpds_status_name(enum gpds_status value); const char *gpds_isi_cause_name(enum gpds_isi_cause value); const char *gpds_transfer_status_name(enum gpds_transfer_status value); const char *gpds_transfer_cause_name(enum gpds_transfer_cause value); void isi_trace(const GIsiMessage *msg, void *data); const char *pn_resource_name(int value); #endif /* __ISIMODEM_DEBUG_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/phonebook.c0000644000015600001650000001712012671500024024014 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "isimodem.h" #include "isiutil.h" #include "sim.h" #include "debug.h" struct pb_data { GIsiClient *client; }; struct read_resp { uint8_t service_type; uint8_t sb_count; uint8_t data[]; }; static gboolean parse_adn(GIsiSubBlockIter *iter, uint16_t *location, char **name, char **number) { uint8_t namelen; uint8_t numlen; if (!g_isi_sb_iter_get_word(iter, location, 4) || !g_isi_sb_iter_get_byte(iter, &namelen, 6) || !g_isi_sb_iter_get_byte(iter, &numlen, 7)) return FALSE; if (!g_isi_sb_iter_get_alpha_tag(iter, name, namelen * 2, 8)) return FALSE; if (!g_isi_sb_iter_get_alpha_tag(iter, number, numlen * 2, 8 + namelen * 2)) { g_free(*name); return FALSE; } return TRUE; } static gboolean parse_sne(GIsiSubBlockIter *iter, char **sne) { uint8_t len; if (!g_isi_sb_iter_get_byte(iter, &len, 6)) return FALSE; if (!g_isi_sb_iter_get_alpha_tag(iter, sne, len * 2, 8)) return FALSE; return TRUE; } static gboolean parse_anr(GIsiSubBlockIter *iter, char **anr) { uint8_t len; if (!g_isi_sb_iter_get_byte(iter, &len, 6)) return FALSE; if (!g_isi_sb_iter_get_alpha_tag(iter, anr, len * 2, 8)) return FALSE; return TRUE; } static gboolean parse_email(GIsiSubBlockIter *iter, char **email) { uint8_t len; if (!g_isi_sb_iter_get_byte(iter, &len, 6)) return FALSE; if (!g_isi_sb_iter_get_alpha_tag(iter, email, len * 2, 8)) return FALSE; return TRUE; } static gboolean decode_response(const GIsiMessage *msg, uint16_t *location, void *data) { struct ofono_phonebook *pb = data; const struct read_resp *resp = g_isi_msg_data(msg); size_t len = g_isi_msg_data_len(msg); GIsiSubBlockIter iter; char *name = NULL; char *number = NULL; char *sne = NULL; char *anr = NULL; char *email = NULL; uint8_t status = 0; gboolean success = FALSE; if (g_isi_msg_id(msg) != SIM_PB_RESP_SIM_PB_READ || resp == NULL || len < sizeof(struct read_resp) || resp->service_type != SIM_PB_READ) return FALSE; for (g_isi_sb_iter_init_full(&iter, msg, 2, TRUE, resp->sb_count); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case SIM_PB_ADN: if (!parse_adn(&iter, location, &name, &number)) goto error; success = TRUE; break; case SIM_PB_SNE: if (!parse_sne(&iter, &sne)) goto error; break; case SIM_PB_ANR: if (!parse_anr(&iter, &anr)) goto error; break; case SIM_PB_EMAIL: if (!parse_email(&iter, &email)) goto error; break; case SIM_PB_STATUS: if (!g_isi_sb_iter_get_byte(&iter, &status, 4)) goto error; break; default: DBG("Skipping sub-block: %s (%zd bytes)", sim_subblock_name(g_isi_sb_iter_get_id(&iter)), g_isi_sb_iter_get_len(&iter)); break; } } if (status == SIM_SERV_OK) ofono_phonebook_entry(pb, -1, number, -1, name, -1, NULL, anr, -1, sne, email, NULL, NULL); error: g_free(name); g_free(number); g_free(sne); g_free(anr); g_free(email); return success; } static void read_next_entry(GIsiClient *client, uint16_t location, GIsiNotifyFunc notify, void *data) { struct isi_cb_data *cbd = data; ofono_phonebook_cb_t cb = cbd->cb; const uint8_t msg[] = { SIM_PB_REQ_SIM_PB_READ, SIM_PB_READ, 2, /* number of subblocks */ 0, SIM_PB_LOCATION_SEARCH, /* subblock id */ 0, 8, /* subblock size */ 0, SIM_PB_ADN, location >> 8, location & 0xFF, /* read next entry after * specified by location */ 0, SIM_PB_INFO_REQUEST, /* subblock id */ 0, 16, /* subblock size */ 4, /* number of tags */ 0, /* filler */ 0, SIM_PB_ADN, /* tags */ 0, SIM_PB_SNE, 0, SIM_PB_ANR, 0, SIM_PB_EMAIL, 0, 0 /* filler */ }; if (cbd == NULL) goto error; if (g_isi_client_send(client, msg, sizeof(msg), notify, cbd, NULL)) return; error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void read_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); ofono_phonebook_cb_t cb = cbd->cb; uint16_t location; if (g_isi_msg_error(msg) < 0) { CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); return; } if (decode_response(msg, &location, cbd->user)) { read_next_entry(pbd->client, location, read_resp_cb, cbd); return; } CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); } static void isi_export_entries(struct ofono_phonebook *pb, const char *storage, ofono_phonebook_cb_t cb, void *data) { struct pb_data *pbd = ofono_phonebook_get_data(pb); struct isi_cb_data *cbd = isi_cb_data_new(pb, cb, data); const uint8_t msg[] = { SIM_PB_REQ_SIM_PB_READ, SIM_PB_READ, 2, /* number of subblocks */ 0, SIM_PB_LOCATION, /* subblock id */ 0, 8, /* subblock size */ 0, SIM_PB_ADN, 0xFF, 0xFF, /* read first entry in pb */ 0, SIM_PB_INFO_REQUEST, /* subblock id */ 0, 16, /* subblock size */ 4, /* number of tags */ 0, /* filler */ 0, SIM_PB_ADN, /* tags */ 0, SIM_PB_SNE, 0, SIM_PB_ANR, 0, SIM_PB_EMAIL, 0, 0 /* filler */ }; size_t len = sizeof(msg); if (cbd == NULL || pbd == NULL || strcmp(storage, "SM") != 0) goto error; if (g_isi_client_send(pbd->client, msg, len, read_resp_cb, cbd, NULL)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_phonebook *pb = data; if (g_isi_msg_error(msg) < 0) { ofono_phonebook_remove(pb); return; } ISI_RESOURCE_DBG(msg); ofono_phonebook_register(pb); } static int isi_phonebook_probe(struct ofono_phonebook *pb, unsigned int vendor, void *user) { GIsiModem *modem = user; struct pb_data *data; data = g_try_new0(struct pb_data, 1); if (data == NULL) return -ENOMEM; data->client = g_isi_client_create(modem, PN_SIM); if (data->client == NULL) { g_free(data); return -ENOMEM; } ofono_phonebook_set_data(pb, data); g_isi_client_verify(data->client, reachable_cb, pb, NULL); return 0; } static void isi_phonebook_remove(struct ofono_phonebook *pb) { struct pb_data *data = ofono_phonebook_get_data(pb); ofono_phonebook_set_data(pb, NULL); if (data == NULL) return; g_isi_client_destroy(data->client); g_free(data); } static struct ofono_phonebook_driver driver = { .name = "isimodem", .probe = isi_phonebook_probe, .remove = isi_phonebook_remove, .export_entries = isi_export_entries }; void isi_phonebook_init(void) { ofono_phonebook_driver_register(&driver); } void isi_phonebook_exit(void) { ofono_phonebook_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/uicc-util.h0000644000015600001650000000332312671500024023733 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 ST-Ericsson AB. * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_UICC_UTIL_H #define __ISIMODEM_UICC_UTIL_H #ifdef __cplusplus extern "C" { #endif #include struct uicc_sim_data; struct uicc_sim_application { int id; uint8_t type; uint8_t status; uint8_t length; struct uicc_sim_data *sim; }; struct uicc_sim_data { GIsiClient *client; unsigned flags; int app_id; int app_type; uint8_t client_id; GIsiVersion version; gboolean server_running; gboolean pin_state_received; gboolean passwd_required; /* Application state */ gboolean uicc_app_started; uint8_t trying_app_id; uint8_t trying_app_type; GHashTable *app_table; uint8_t pin1_id; uint8_t pin2_id; }; gboolean uicc_get_fileid_path(struct uicc_sim_data *sd, int *mf_path, int *df1_path, int *df2_path, unsigned char *df_len, int fileid); uint8_t uicc_get_sfi(const int fileid); #ifdef __cplusplus }; #endif #endif /* __ISIMODEM_UICC_UTIL_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/call-barring.c0000644000015600001650000002356212671500024024374 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "isimodem.h" #include "isiutil.h" #include "ss.h" #include "debug.h" struct barr_data { GIsiClient *client; }; static int lock_code_to_mmi(const char *lock) { if (strcmp(lock, "AO") == 0) return SS_GSM_BARR_ALL_OUT; else if (strcmp(lock, "OI") == 0) return SS_GSM_BARR_OUT_INTER; else if (strcmp(lock, "OX") == 0) return SS_GSM_BARR_OUT_INTER_EXC_HOME; else if (strcmp(lock, "AI") == 0) return SS_GSM_BARR_ALL_IN; else if (strcmp(lock, "IR") == 0) return SS_GSM_BARR_ALL_IN_ROAM; else if (strcmp(lock, "AB") == 0) return SS_GSM_ALL_BARRINGS; else if (strcmp(lock, "AG") == 0) return SS_GSM_OUTGOING_BARR_SERV; else if (strcmp(lock, "AC") == 0) return SS_GSM_INCOMING_BARR_SERV; else return 0; } static void update_status_mask(uint32_t *mask, uint8_t bsc) { switch (bsc) { case SS_GSM_TELEPHONY: *mask |= 1; break; case SS_GSM_ALL_DATA_TELE: *mask |= 1 << 1; break; case SS_GSM_FACSIMILE: *mask |= 1 << 2; break; case SS_GSM_SMS: *mask |= 1 << 3; break; case SS_GSM_ALL_DATA_CIRCUIT_SYNC: *mask |= 1 << 4; break; case SS_GSM_ALL_DATA_CIRCUIT_ASYNC: *mask |= 1 << 5; break; case SS_GSM_ALL_DATA_PACKET_SYNC: *mask |= 1 << 6; break; case SS_GSM_ALL_PAD_ACCESS: *mask |= 1 << 7; break; default: DBG("Unknown BSC value %d, please report", bsc); break; } } static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid, uint8_t type) { uint8_t service; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", g_isi_msg_strerror(msg)); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", ss_message_id_name(g_isi_msg_id(msg))); return FALSE; } if (!g_isi_msg_data_get_byte(msg, 0, &service) || service != type) { DBG("Unexpected service type: 0x%02X", service); return FALSE; } return TRUE; } static gboolean decode_gsm_bsc_info(GIsiSubBlockIter *iter, uint32_t *mask) { uint8_t *bsc; uint8_t num, i; if (!g_isi_sb_iter_get_byte(iter, &num, 2)) return FALSE; if (!g_isi_sb_iter_get_struct(iter, (void **) &bsc, num, 3)) return FALSE; for (i = 0; i < num; i++) update_status_mask(mask, bsc[i]); return TRUE; } static gboolean decode_gsm_barring_info(GIsiSubBlockIter *outer, uint32_t *mask) { GIsiSubBlockIter iter; uint8_t status; uint8_t bsc; for (g_isi_sb_subiter_init(outer, &iter, 4); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != SS_GSM_BARRING_FEATURE) continue; if (!g_isi_sb_iter_get_byte(&iter, &bsc, 2)) return FALSE; if (!g_isi_sb_iter_get_byte(&iter, &status, 3)) return FALSE; if (status & SS_GSM_ACTIVE) update_status_mask(mask, bsc); return TRUE; } return FALSE; } static void unset_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_call_barring_set_cb_t cb = cbd->cb; if (check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_DEACTIVATION)) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); } static void set_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_call_barring_set_cb_t cb = cbd->cb; if (check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_ACTIVATION)) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); } static void isi_set(struct ofono_call_barring *barr, const char *lock, int enable, const char *passwd, int cls, ofono_call_barring_set_cb_t cb, void *data) { struct barr_data *bd = ofono_call_barring_get_data(barr); struct isi_cb_data *cbd = isi_cb_data_new(barr, cb, data); int ss_code = lock_code_to_mmi(lock); const uint8_t msg[] = { SS_SERVICE_REQ, enable ? SS_ACTIVATION : SS_DEACTIVATION, SS_ALL_TELE_AND_BEARER, ss_code >> 8, ss_code & 0xFF, /* Service code */ SS_SEND_ADDITIONAL_INFO, 1, /* Subblock count */ SS_GSM_PASSWORD, 28, /* Subblock length */ 0, passwd[0], 0, passwd[1], 0, passwd[2], 0, passwd[3], 0, 0, 0, 0, 0, 0, 0, 0, /* Filler */ 0, 0, 0, 0, 0, 0, 0, 0, /* Filler */ 0, 0, /* Filler */ }; DBG("lock code %s enable %d class %d password %s", lock, enable, cls, passwd); if (cbd == NULL || bd == NULL) goto error; if (g_isi_client_send(bd->client, msg, sizeof(msg), enable ? set_resp_cb : unset_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void query_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_call_barring_query_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint32_t mask = 0; uint8_t status; if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_INTERROGATION)) goto error; for (g_isi_sb_iter_init(&iter, msg, 6); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case SS_STATUS_RESULT: if (!g_isi_sb_iter_get_byte(&iter, &status, 2)) goto error; if (status & SS_GSM_ACTIVE) mask = 1; break; case SS_GSM_BARRING_INFO: if (!decode_gsm_barring_info(&iter, &mask)) goto error; break; case SS_GSM_BSC_INFO: if (!decode_gsm_bsc_info(&iter, &mask)) goto error; break; case SS_GSM_ADDITIONAL_INFO: break; } } DBG("mask=0x%04X", mask); CALLBACK_WITH_SUCCESS(cb, mask, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, 0, cbd->data); } static void isi_query(struct ofono_call_barring *barr, const char *lock, int cls, ofono_call_barring_query_cb_t cb, void *data) { struct barr_data *bd = ofono_call_barring_get_data(barr); struct isi_cb_data *cbd = isi_cb_data_new(barr, cb, data); int ss_code = lock_code_to_mmi(lock); unsigned char msg[] = { SS_SERVICE_REQ, SS_INTERROGATION, SS_ALL_TELE_AND_BEARER, ss_code >> 8, ss_code & 0xFF, /* services code */ SS_SEND_ADDITIONAL_INFO, /* Get BER-encoded result */ 0 /* Subblock count */ }; DBG("barring query lock code %s", lock); if (cbd == NULL || bd == NULL) goto error; if (g_isi_client_send(bd->client, msg, sizeof(msg), query_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, 0, data); g_free(cbd); } static void set_passwd_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_call_barring_set_cb_t cb = cbd->cb; if (check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_GSM_PASSWORD_REGISTRATION)) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); } static void isi_set_passwd(struct ofono_call_barring *barr, const char *lock, const char *old_passwd, const char *new_passwd, ofono_call_barring_set_cb_t cb, void *data) { struct barr_data *bd = ofono_call_barring_get_data(barr); struct isi_cb_data *cbd = isi_cb_data_new(barr, cb, data); int ss_code = lock_code_to_mmi(lock); const uint8_t msg[] = { SS_SERVICE_REQ, SS_GSM_PASSWORD_REGISTRATION, SS_ALL_TELE_AND_BEARER, ss_code >> 8, ss_code & 0xFF, /* Service code */ SS_SEND_ADDITIONAL_INFO, 1, /* Subblock count */ SS_GSM_PASSWORD, 28, /* Subblock length */ 0, old_passwd[0], 0, old_passwd[1], 0, old_passwd[2], 0, old_passwd[3], 0, new_passwd[0], 0, new_passwd[1], 0, new_passwd[2], 0, new_passwd[3], 0, new_passwd[0], 0, new_passwd[1], 0, new_passwd[2], 0, new_passwd[3], 0, 0, /* Filler */ }; DBG("lock code %s (%u) old password %s new password %s", lock, ss_code, old_passwd, new_passwd); if (cbd == NULL || bd == NULL) goto error; if (g_isi_client_send(bd->client, msg, sizeof(msg), set_passwd_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_call_barring *barr = data; if (g_isi_msg_error(msg) < 0) { ofono_call_barring_remove(barr); return; } ISI_RESOURCE_DBG(msg); ofono_call_barring_register(barr); } static int isi_call_barring_probe(struct ofono_call_barring *barr, unsigned int vendor, void *user) { GIsiModem *modem = user; struct barr_data *bd; bd = g_try_new0(struct barr_data, 1); if (bd == NULL) return -ENOMEM; bd->client = g_isi_client_create(modem, PN_SS); if (bd->client == NULL) { g_free(bd); return -ENOMEM; } ofono_call_barring_set_data(barr, bd); g_isi_client_verify(bd->client, reachable_cb, barr, NULL); return 0; } static void isi_call_barring_remove(struct ofono_call_barring *barr) { struct barr_data *data = ofono_call_barring_get_data(barr); ofono_call_barring_set_data(barr, NULL); if (data == NULL) return; g_isi_client_destroy(data->client); g_free(data); } static struct ofono_call_barring_driver driver = { .name = "isimodem", .probe = isi_call_barring_probe, .remove = isi_call_barring_remove, .set = isi_set, .query = isi_query, .set_passwd = isi_set_passwd }; void isi_call_barring_init(void) { ofono_call_barring_driver_register(&driver); } void isi_call_barring_exit(void) { ofono_call_barring_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/uicc.h0000644000015600001650000002053012671500024022757 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 ST-Ericsson AB. * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_UICC_H #define __ISIMODEM_UICC_H #ifdef __cplusplus extern "C" { #endif #include #include #define PN_UICC 0x8C #define UICC_APPL_ID_UNKNOWN 0x00 #define UICC_SFI_NOT_PRESENT 0x00 #define UICC_SESSION_ID_NOT_USED 0x00 enum uicc_status { UICC_STATUS_OK = 0x00, UICC_STATUS_FAIL = 0x01, UICC_STATUS_UNKNOWN = 0x02, UICC_STATUS_NOT_READY = 0x10, UICC_STATUS_START_UP_COMPLETED = 0x11, UICC_STATUS_SHUTTING_DOWN = 0x12, UICC_STATUS_CARD_NOT_READY = 0x20, UICC_STATUS_CARD_READY = 0x21, UICC_STATUS_CARD_DISCONNECTED = 0x22, UICC_STATUS_CARD_NOT_PRESENT = 0x23, UICC_STATUS_CARD_REJECTED = 0x24, UICC_STATUS_APPL_ACTIVE = 0x30, UICC_STATUS_APPL_NOT_ACTIVE = 0x31, UICC_STATUS_PIN_ENABLED = 0x40, UICC_STATUS_PIN_DISABLED = 0x41, }; enum uicc_subblock { UICC_SB_SHUT_DOWN_CONFIG = 0x0023, UICC_SB_CARD_STATUS = 0x0001, UICC_SB_CARD_INFO = 0x0024, UICC_SB_CARD_REJECT_CAUSE = 0x0025, UICC_SB_CLIENT = 0x001F, UICC_SB_APPL_DATA_OBJECT = 0x0002, UICC_SB_APPLICATION = 0x0003, UICC_SB_APPL_INFO = 0x0004, UICC_SB_APPL_STATUS = 0x0005, UICC_SB_FCP = 0x0007, UICC_SB_FCI = 0x001C, UICC_SB_CHV = 0x001B, UICC_SB_PIN = 0x0008, UICC_SB_PIN_REF = 0x0009, UICC_SB_PUK = 0x000A, UICC_SB_PIN_SUBST = 0x000B, UICC_SB_PIN_INFO = 0x000C, UICC_SB_APPL_PATH = 0x000D, UICC_SB_SESSION = 0x000E, UICC_SB_FILE_DATA = 0x000F, UICC_SB_APDU = 0x0014, UICC_SB_TRANSPARENT_READ = 0x0010, UICC_SB_TRANSPARENT_UPDATE = 0x0011, UICC_SB_TRANSPARENT = 0x0012, UICC_SB_LINEAR_FIXED = 0x0013, UICC_SB_CYCLIC = 0x0026, UICC_SB_TERMINAL_PROFILE = 0x0015, UICC_SB_TERMINAL_RESPONSE = 0x001D, UICC_SB_ENVELOPE = 0x0021, UICC_SB_POLLING_SET = 0x0016, UICC_SB_REFRESH = 0x0017, UICC_SB_AID = 0x0006, UICC_SB_REFRESH_RESULT = 0x0018, UICC_SB_APDU_ACTIONS = 0x0019, UICC_SB_OBJECT_ID = 0x001A, UICC_SB_STATUS_WORD = 0x0020, UICC_SB_APDU_SAP_INFO = 0x0022, UICC_SB_ACCESS_MODE = 0x0027, UICC_SB_RESP_INFO = 0x0028, UICC_SB_APDU_SAP_CONFIG = 0x0029, }; enum uicc_message_id { UICC_REQ = 0x00, UICC_RESP = 0x01, UICC_IND = 0x02, UICC_CARD_REQ = 0x03, UICC_CARD_RESP = 0x04, UICC_CARD_IND = 0x05, UICC_APPLICATION_REQ = 0x06, UICC_APPLICATION_RESP = 0x07, UICC_APPLICATION_IND = 0x08, UICC_PIN_REQ = 0x09, UICC_PIN_RESP = 0x0A, UICC_PIN_IND = 0x0B, UICC_APPL_CMD_REQ = 0x0C, UICC_APPL_CMD_RESP = 0x0D, UICC_APPL_CMD_IND = 0x0E, UICC_CONNECTOR_REQ = 0x0F, UICC_CONNECTOR_RESP = 0x10, UICC_CAT_REQ = 0x12, UICC_CAT_RESP = 0x13, UICC_CAT_IND = 0x14, UICC_APDU_REQ = 0x15, UICC_APDU_RESP = 0x16, UICC_APDU_RESET_IND = 0x17, UICC_REFRESH_REQ = 0x18, UICC_REFRESH_RESP = 0x19, UICC_REFRESH_IND = 0x1A, UICC_SIMLOCK_REQ = 0x1B, UICC_SIMLOCK_RESP = 0x1C, UICC_APDU_SAP_REQ = 0x1E, UICC_APDU_SAP_RESP = 0x1F, UICC_APDU_SAP_IND = 0x20, UICC_PWR_CTRL_REQ = 0x21, UICC_PWR_CTRL_RESP = 0x22, UICC_PWR_CTRL_IND = 0x23, UICC_CARD_READER_IND = 0x26, }; enum uicc_service_type { UICC_APPL_LIST = 0x01, UICC_APPL_HOST_ACTIVATE = 0x03, UICC_APPL_START_UP_COMPLETE = 0x05, UICC_APPL_SHUT_DOWN_INITIATED = 0x06, UICC_APPL_STATUS_GET = 0x07, UICC_APPL_HOST_DEACTIVATE = 0x09, UICC_PIN_VERIFY = 0x11, UICC_PIN_UNBLOCK = 0x12, UICC_PIN_DISABLE = 0x13, UICC_PIN_ENABLE = 0x14, UICC_PIN_CHANGE = 0x15, UICC_PIN_SUBSTITUTE = 0x16, UICC_PIN_INFO = 0x17, UICC_PIN_PROMPT_VERIFY = 0x18, UICC_APPL_READ_TRANSPARENT = 0x21, UICC_APPL_UPDATE_TRANSPARENT = 0x22, UICC_APPL_READ_LINEAR_FIXED = 0x23, UICC_APPL_UPDATE_LINEAR_FIXED = 0x24, UICC_APPL_FILE_INFO = 0x25, UICC_APPL_APDU_SEND = 0x26, UICC_APPL_CLEAR_CACHE = 0x27, UICC_APPL_SESSION_START = 0x28, UICC_APPL_SESSION_END = 0x29, UICC_APPL_READ_CYCLIC = 0x2A, UICC_APPL_UPDATE_CYCLIC = 0x2B, UICC_CONNECT = 0x31, UICC_DISCONNECT = 0x32, UICC_RECONNECT = 0x33, UICC_CAT_ENABLE = 0x41, UICC_CAT_DISABLE = 0x42, UICC_CAT_TERMINAL_PROFILE = 0x43, UICC_CAT_TERMINAL_RESPONSE = 0x44, UICC_CAT_ENVELOPE = 0x45, UICC_CAT_POLLING_SET = 0x46, UICC_CAT_REFRESH = 0x47, UICC_CAT_POLL = 0x48, UICC_APDU_SEND = 0x51, UICC_APDU_ATR_GET = 0x52, UICC_APDU_CONTROL = 0x53, UICC_REFRESH_STATUS = 0x61, UICC_APPL_TERMINATED = 0x71, UICC_APPL_RECOVERED = 0x72, UICC_APPL_ACTIVATED = 0x75, UICC_PIN_VERIFY_NEEDED = 0x81, UICC_PIN_UNBLOCK_NEEDED = 0x82, UICC_PIN_PERMANENTLY_BLOCKED = 0x83, UICC_PIN_VERIFIED = 0x84, UICC_CAT_FETCHED_CMD = 0x91, UICC_CAT_NOT_SUPPORTED = 0x92, UICC_CAT_REG_FAILED = 0x93, UICC_CAT_REG_OK = 0x94, UICC_REFRESH_PERMISSION = 0xA1, UICC_REFRESH_STARTING = 0xA2, UICC_REFRESH_CANCELLED = 0xA3, UICC_REFRESH_NOW = 0xA4, UICC_START_UP_COMPLETE = 0xB0, UICC_STATUS_GET = 0xB1, UICC_READY = 0xB2, UICC_READY_FOR_ACTIVATION = 0xB3, UICC_INITIALIZED = 0xB4, UICC_SHUTTING_DOWN = 0xB5, UICC_SHUT_DOWN_CONFIG = 0xB6, UICC_ERROR = 0xB7, UICC_CARD_DISCONNECTED = 0xC0, UICC_CARD_REMOVED = 0xC1, UICC_CARD_NOT_PRESENT = 0xC2, UICC_CARD_READY = 0xC4, UICC_CARD_STATUS_GET = 0xC5, UICC_CARD_REJECTED = 0xC8, UICC_CARD_INFO_GET = 0xC9, UICC_SIMLOCK_ACTIVE = 0xD0, UICC_APDU_SAP_ACTIVATE = 0xE1, UICC_APDU_SAP_DEACTIVATE = 0xE2, UICC_APDU_SAP_ATR_GET = 0xE3, UICC_APDU_SAP_COLD_RESET = 0xE4, UICC_APDU_SAP_WARM_RESET = 0xE5, UICC_APDU_SAP_APDU_SEND = 0xE6, UICC_APDU_SAP_RECOVERY = 0xE7, UICC_APDU_SAP_CONFIG_GET = 0xE8, UICC_PWR_CTRL_ENABLE = 0xF1, UICC_PWR_CTRL_DISABLE = 0xF2, UICC_PWR_CTRL_WAIT = 0xF3, UICC_PWR_CTRL_PROCEED = 0xF4, UICC_PWR_CTRL_PERMISSION = 0xFA, }; enum uicc_appl_type_table { UICC_APPL_TYPE_UNKNOWN = 0x00, UICC_APPL_TYPE_ICC_SIM = 0x01, UICC_APPL_TYPE_UICC_USIM = 0x02, }; enum uicc_pin_qualifier { UICC_PIN_NEW = 0x01, UICC_PIN_OLD = 0x02, }; enum uicc_appl_start_up_type { UICC_APPL_START_UP_NO_INIT_PROC = 0x00, UICC_APPL_START_UP_INIT_PROC = 0x01, }; enum uicc_card_type { UICC_CARD_TYPE_ICC = 0x01, UICC_CARD_TYPE_UICC = 0x02, }; enum uicc_details { UICC_NO_DETAILS = 0x00, UICC_INVALID_PARAMETERS = 0x01, UICC_FILE_NOT_FOUND = 0x02, UICC_SECURITY_CONDITIONS_NOT_SATISFIED = 0x03, UICC_APPL_CONFLICT = 0x04, UICC_CARD_ERROR = 0x05, UICC_SERVICE_NOT_SUPPORTED = 0x06, UICC_SESSION_EXPIRED = 0x07, }; enum uicc_simlock_status { UICC_SIMLOCK_STATUS_ACTIVE = 0x01, UICC_SIMLOCK_STATUS_INACTIVE = 0x02, }; enum uicc_apdu_status_word { UICC_PIN_STATUS_AUTH_RETRIES = 0x63c0, UICC_PIN_STATUS_AUTH_BLOCKED = 0x6983, UICC_PIN_STATUS_AUTH_FAILED = 0x9840, }; enum uicc_template { UICC_TEMPLATE_APPLICATION = 0x61, UICC_TEMPLATE_FCP = 0x62, UICC_TEMPLATE_SECURITY_ENVIRONMENT = 0x7B, }; enum uicc_fcp_param { UICC_FCP_PARAM_FILE_SIZE_DATA = 0x80, UICC_FCP_PARAM_FILE_SIZE_TOTAL = 0x81, UICC_FCP_PARAM_FILE_DESC = 0x82, UICC_FCP_PARAM_FILE_ID = 0x83, UICC_FCP_PARAM_AID = 0x84, UICC_FCP_PARAM_LIFECYCLE = 0x8A, UICC_FCP_PARAM_SECURITY_REFERENCE = 0x8B, UICC_FCP_PARAM_SECURITY_COMPACT = 0x8C, UICC_FCP_PARAM_SECURITY_EXPANDED = 0xAB, UICC_FCP_PARAM_PIN_STATUS = 0xC6, }; enum uicc_app_param { UICC_APP_PARAM_ID = 0x4F, UICC_APP_PARAM_LABEL = 0x50, UICC_APP_PARAM_PATH = 0x51, UICC_APP_PARAM_COMMAND = 0x52, UICC_APP_PARAM_DISC_DATA = 0x53, UICC_APP_PARAM_DISC_TEMPLATE = 0x73, UICC_APP_PARAM_URL = 0x5F50, }; gboolean isi_uicc_properties(GIsiModem *modem, int *app_id, int *app_type, int *client_id); #ifdef __cplusplus }; #endif #endif /* __ISIMODEM_UICC_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/gss.h0000644000015600001650000000250212671500024022627 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_GSS_H #define __ISIMODEM_GSS_H #ifdef __cplusplus extern "C" { #endif #define PN_GSS 0x32 #define GSS_TIMEOUT 5 enum gss_message_id { GSS_CS_SERVICE_REQ = 0x00, GSS_CS_SERVICE_RESP = 0x01, GSS_CS_SERVICE_FAIL_RESP = 0x02, }; enum gss_subblock { GSS_RAT_INFO = 0x0B, }; enum gss_selection_mode { GSS_DUAL_RAT = 0x00, GSS_GSM_RAT = 0x01, GSS_UMTS_RAT = 0x02, }; enum gss_operation { GSS_SELECTED_RAT_WRITE = 0x0E, GSS_SELECTED_RAT_READ = 0x9C, }; #ifdef __cplusplus }; #endif #endif /* !__ISIMODEM_GSS_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/radio-settings.c0000644000015600001650000002077512671500024024776 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "isimodem.h" #include "isiutil.h" #include "debug.h" #include "gpds.h" #include "gss.h" #include "network.h" struct radio_data { GIsiClient *gss_client; GIsiClient *gpds_client; GIsiClient *wran_client; uint16_t wran_object; uint16_t quick_release:1; }; static enum ofono_radio_access_mode isi_mode_to_ofono_mode(guint8 mode) { switch (mode) { case GSS_DUAL_RAT: return OFONO_RADIO_ACCESS_MODE_ANY; case GSS_GSM_RAT: return OFONO_RADIO_ACCESS_MODE_GSM; case GSS_UMTS_RAT: return OFONO_RADIO_ACCESS_MODE_UMTS; default: return -1; } } static int ofono_mode_to_isi_mode(enum ofono_radio_access_mode mode) { switch (mode) { case OFONO_RADIO_ACCESS_MODE_ANY: return GSS_DUAL_RAT; case OFONO_RADIO_ACCESS_MODE_GSM: return GSS_GSM_RAT; case OFONO_RADIO_ACCESS_MODE_UMTS: return GSS_UMTS_RAT; default: return -1; } } static void rat_mode_read_resp_cb(const GIsiMessage *msg, void *opaque) { struct isi_cb_data *cbd = opaque; ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb; int mode = -1; GIsiSubBlockIter iter; if (g_isi_msg_error(msg) < 0) { DBG("message error"); goto error; } if (g_isi_msg_id(msg) == GSS_CS_SERVICE_FAIL_RESP) goto error; if (g_isi_msg_id(msg) != GSS_CS_SERVICE_RESP) return; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case GSS_RAT_INFO: { guint8 info; if (!g_isi_sb_iter_get_byte(&iter, &info, 2)) goto error; mode = isi_mode_to_ofono_mode(info); break; } default: DBG("Skipping sub-block: %s (%zu bytes)", gss_subblock_name( g_isi_sb_iter_get_id(&iter)), g_isi_sb_iter_get_len(&iter)); break; } } CALLBACK_WITH_SUCCESS(cb, mode, cbd->data); g_free(cbd); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); return; } static void isi_query_rat_mode(struct ofono_radio_settings *rs, ofono_radio_settings_rat_mode_query_cb_t cb, void *data) { struct radio_data *rd = ofono_radio_settings_get_data(rs); struct isi_cb_data *cbd = isi_cb_data_new(rd, cb, data); const unsigned char msg[] = { GSS_CS_SERVICE_REQ, GSS_SELECTED_RAT_READ, 0x00 /* subblock count */ }; if (cbd == NULL || rd == NULL) goto error; if (g_isi_client_send(rd->gss_client, msg, sizeof(msg), rat_mode_read_resp_cb, cbd, NULL)) return; error: CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } static void mode_write_resp_cb(const GIsiMessage *msg, void *opaque) { struct isi_cb_data *cbd = opaque; ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb; if (g_isi_msg_error(msg) < 0) { DBG("message error"); goto error; } if (g_isi_msg_id(msg) == GSS_CS_SERVICE_FAIL_RESP) goto error; if (g_isi_msg_id(msg) != GSS_CS_SERVICE_RESP) return; CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); return; error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); return; } static void isi_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_data *rd = ofono_radio_settings_get_data(rs); struct isi_cb_data *cbd = isi_cb_data_new(rd, cb, data); int isi_mode = ofono_mode_to_isi_mode(mode); const unsigned char msg[] = { GSS_CS_SERVICE_REQ, GSS_SELECTED_RAT_WRITE, 0x01, /* subblock count */ GSS_RAT_INFO, 0x04, /* subblock length */ isi_mode, 0x00 /* filler */ }; if (cbd == NULL || rd == NULL) goto error; if (isi_mode == -1) goto error; if (g_isi_client_send(rd->gss_client, msg, sizeof(msg), mode_write_resp_cb, cbd, NULL)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void update_fast_dormancy(struct radio_data *rd) { GIsiModem *modem; struct sockaddr_pn dst = { .spn_family = AF_PHONET, .spn_resource = 0x3a, .spn_dev = rd->wran_object >> 8, .spn_obj = rd->wran_object & 0xff, }; if (!rd->wran_object) return; modem = g_isi_client_modem(rd->wran_client); if (rd->quick_release) { const unsigned char msg[] = { 0x00, 0x1f, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }; g_isi_modem_sendto(modem, &dst, msg, sizeof(msg)); } else { const unsigned char msg[] = { 0x00, 0x1f, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00 }; g_isi_modem_sendto(modem, &dst, msg, sizeof(msg)); } DBG("3G PS quick release %s", rd->quick_release ? "enabled" : "disabled"); } static void gpds_context_activating_ind_cb(const GIsiMessage *msg, void *opaque) { struct radio_data *rd = opaque; update_fast_dormancy(rd); } static void isi_query_fast_dormancy(struct ofono_radio_settings *rs, ofono_radio_settings_fast_dormancy_query_cb_t cb, void *data) { struct radio_data *rd = ofono_radio_settings_get_data(rs); CALLBACK_WITH_SUCCESS(cb, rd->quick_release, data); } static void isi_set_fast_dormancy(struct ofono_radio_settings *rs, ofono_bool_t enable, ofono_radio_settings_fast_dormancy_set_cb_t cb, void *data) { struct radio_data *rd = ofono_radio_settings_get_data(rs); rd->quick_release = enable; update_fast_dormancy(rd); CALLBACK_WITH_SUCCESS(cb, data); } static void wran_reachable_cb(const GIsiMessage *msg, void *opaque) { struct radio_data *rd = opaque; if (g_isi_msg_error(msg) < 0) return; ISI_RESOURCE_DBG(msg); rd->wran_object = g_isi_msg_object(msg); DBG("PN_WRAN object = 0x%04x", rd->wran_object); update_fast_dormancy(rd); g_isi_client_ind_subscribe(rd->gpds_client, GPDS_CONTEXT_ACTIVATING_IND, gpds_context_activating_ind_cb, rd); } static void gss_reachable_cb(const GIsiMessage *msg, void *opaque) { struct ofono_radio_settings *rs = opaque; if (g_isi_msg_error(msg) < 0) { ofono_radio_settings_remove(rs); return; } ISI_RESOURCE_DBG(msg); ofono_radio_settings_register(rs); } static int isi_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *user) { GIsiModem *modem = user; struct radio_data *rd = g_try_new0(struct radio_data, 1); if (rd == NULL) return -ENOMEM; rd->gss_client = g_isi_client_create(modem, PN_GSS); if (rd->gss_client == NULL) goto nomem; rd->gpds_client = g_isi_client_create(modem, PN_GPDS); if (rd->gpds_client == NULL) goto nomem; rd->wran_client = g_isi_client_create(modem, PN_WRAN); if (rd->wran_client == NULL) goto nomem; ofono_radio_settings_set_data(rs, rd); g_isi_client_verify(rd->gss_client, gss_reachable_cb, rs, NULL); g_isi_client_verify(rd->wran_client, wran_reachable_cb, rd, NULL); return 0; nomem: g_isi_client_destroy(rd->gss_client); g_isi_client_destroy(rd->wran_client); g_isi_client_destroy(rd->gpds_client); g_free(rd); return -ENOMEM; } static void isi_radio_settings_remove(struct ofono_radio_settings *rs) { struct radio_data *rd = ofono_radio_settings_get_data(rs); ofono_radio_settings_set_data(rs, NULL); if (rd == NULL) return; g_isi_client_destroy(rd->gss_client); g_isi_client_destroy(rd->wran_client); g_isi_client_destroy(rd->gpds_client); g_free(rd); } static struct ofono_radio_settings_driver driver = { .name = "isimodem", .probe = isi_radio_settings_probe, .remove = isi_radio_settings_remove, .query_rat_mode = isi_query_rat_mode, .set_rat_mode = isi_set_rat_mode, .query_fast_dormancy = isi_query_fast_dormancy, .set_fast_dormancy = isi_set_fast_dormancy, }; void isi_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void isi_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/ss.h0000644000015600001650000001120012671500024022453 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_SS_H #define __ISIMODEM_SS_H #define PN_SS 0x06 #define SS_TIMEOUT 15 #define SS_MAX_USSD_LENGTH 160 enum ss_message_id { SS_SERVICE_REQ = 0x00, SS_SERVICE_COMPLETED_RESP = 0x01, SS_SERVICE_FAILED_RESP = 0x02, SS_SERVICE_NOT_SUPPORTED_RESP = 0x03, SS_GSM_USSD_SEND_REQ = 0x04, SS_GSM_USSD_SEND_RESP = 0x05, SS_GSM_USSD_RECEIVE_IND = 0x06, SS_STATUS_IND = 0x09, SS_SERVICE_COMPLETED_IND = 0x10, }; enum ss_ussd_type { SS_GSM_USSD_MT_REPLY = 0x01, SS_GSM_USSD_COMMAND = 0x02, SS_GSM_USSD_REQUEST = 0x03, SS_GSM_USSD_NOTIFY = 0x04, SS_GSM_USSD_END = 0x05 }; enum ss_ussd_status { SS_GSM_STATUS_REQUEST_USSD_START = 0x02, SS_GSM_STATUS_REQUEST_USSD_STOP = 0x03, SS_GSM_STATUS_REQUEST_USSD_FAILED = 0x04 }; enum ss_operations { SS_ACTIVATION = 0x01, SS_DEACTIVATION = 0x02, SS_REGISTRATION = 0x03, SS_ERASURE = 0x04, SS_INTERROGATION = 0x05, SS_GSM_PASSWORD_REGISTRATION = 0x06 }; enum ss_basic_service_codes { SS_ALL_TELE_AND_BEARER = 0, SS_GSM_ALL_TELE = 10, SS_GSM_TELEPHONY = 11, SS_GSM_ALL_DATA_TELE = 12, SS_GSM_FACSIMILE = 13, SS_GSM_SMS = 16, SS_GSM_VOICE_GROUP = 17, SS_GSM_ALL_TELE_EXC_SMS = 19, SS_GSM_ALL_BEARER = 20, SS_GSM_ALL_ASYNC = 21, SS_GSM_ALL_SYNC = 22, SS_GSM_ALL_DATA_CIRCUIT_SYNC = 24, SS_GSM_ALL_DATA_CIRCUIT_ASYNC = 25, SS_GSM_ALL_DATA_PACKET_SYNC = 26, SS_GSM_ALL_PAD_ACCESS = 27 }; enum ss_codes { SS_GSM_ALL_FORWARDINGS = 002, SS_GSM_ALL_COND_FORWARDINGS = 004, SS_GSM_FORW_UNCONDITIONAL = 21, SS_GSM_BARR_ALL_OUT = 33, SS_GSM_OUTGOING_BARR_SERV = 333, SS_GSM_INCOMING_BARR_SERV = 353, SS_GSM_BARR_ALL_IN = 35, SS_GSM_CALL_WAITING = 43, SS_GSM_FORW_NO_REPLY = 61, SS_GSM_FORW_NO_REACH = 62, SS_GSM_FORW_BUSY = 67, SS_GSM_ALL_BARRINGS = 330, SS_GSM_BARR_OUT_INTER = 331, SS_GSM_BARR_OUT_INTER_EXC_HOME = 332, SS_GSM_BARR_ALL_IN_ROAM = 351, SS_GSM_CLIP = 0x001E, SS_GSM_CLIR = 0x001F, SS_GSM_COLP = 0x004C, SS_GSM_COLR = 0x004D, SS_GSM_CNAP = 0x012C, SS_GSM_ECT = 0x0060 }; enum ss_response_data { SS_SEND_ADDITIONAL_INFO = 0x01, }; enum ss_subblock { SS_FORWARDING = 0x00, SS_STATUS_RESULT = 0x01, SS_GSM_PASSWORD = 0x03, SS_GSM_FORWARDING_INFO = 0x04, SS_GSM_FORWARDING_FEATURE = 0x05, SS_GSM_BARRING_INFO = 0x06, SS_GSM_BARRING_FEATURE = 0x07, SS_GSM_DATA = 0x08, SS_GSM_BSC_INFO = 0x09, SS_GSM_GENERIC_SERVICE_INFO = 0x0A, SS_GSM_PASSWORD_INFO = 0x0B, SS_GSM_CLIR_INFO = 0x0C, SS_GSM_INDICATE_PASSWORD_ERROR = 0x0D, SS_GSM_INDICATE_ERROR = 0x0E, SS_GSM_ADDITIONAL_INFO = 0x2F, SS_GSM_USSD_STRING = 0x32 }; enum ss_isi_cause { SS_GSM_ACTIVE = 0x01, SS_GSM_REGISTERED = 0x02, SS_GSM_PROVISIONED = 0x04, SS_GSM_QUIESCENT = 0x08, }; enum ss_gsm_cli_restriction_option { SS_GSM_CLI_PERMANENT = 0x00, SS_GSM_DEFAULT_RESTRICTED = 0x01, SS_GSM_CLI_DEFAULT_ALLOWED = 0x02, SS_GSM_OVERRIDE_ENABLED = 0x03, SS_GSM_OVERRIDE_DISABLED = 0x04 }; enum ss_constants { SS_UNDEFINED_TIME = 0x00, }; /* TS 27.007 Supplementary service notifications +CSSN */ enum ss_cssi { SS_MO_UNCONDITIONAL_FORWARDING = 0, SS_MO_CONDITIONAL_FORWARDING = 1, SS_MO_CALL_FORWARDED = 2, SS_MO_CALL_WAITING = 3, SS_MO_CUG_CALL = 4, SS_MO_OUTGOING_BARRING = 5, SS_MO_INCOMING_BARRING = 6, SS_MO_CLIR_SUPPRESSION_REJECTED = 7, SS_MO_CALL_DEFLECTED = 8, }; enum ss_cssu { SS_MT_CALL_FORWARDED = 0, SS_MT_CUG_CALL = 1, SS_MT_VOICECALL_ON_HOLD = 2, SS_MT_VOICECALL_RETRIEVED = 3, SS_MT_MULTIPARTY_VOICECALL = 4, SS_MT_VOICECALL_HOLD_RELEASED = 5, SS_MT_FORWARD_CHECK_SS_MESSAGE = 6, SS_MT_VOICECALL_IN_TRANSFER = 7, SS_MT_VOICECALL_TRANSFERRED = 8, SS_MT_CALL_DEFLECTED = 9, }; #endif /* __ISIMODEM_SS_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/info.h0000644000015600001650000000375312671500024022777 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_INFO_H #define __ISIMODEM_INFO_H #ifdef __cplusplus extern "C" { #endif #define PN_PHONE_INFO 0x1B #define PN_MODEM_INFO 0xC5 #define PN_EPOC_INFO 0x62 #define INFO_TIMEOUT 5 enum info_isi_cause { INFO_OK = 0x00, INFO_FAIL = 0x01, INFO_NO_NUMBER = 0x02, INFO_NOT_SUPPORTED = 0x03, }; enum info_message_id { INFO_SERIAL_NUMBER_READ_REQ = 0x00, INFO_SERIAL_NUMBER_READ_RESP = 0x01, INFO_PP_READ_REQ = 0x02, INFO_PP_READ_RESP = 0x03, INFO_VERSION_READ_REQ = 0x07, INFO_VERSION_READ_RESP = 0x08, INFO_PRODUCT_INFO_READ_REQ = 0x15, INFO_PRODUCT_INFO_READ_RESP = 0x16, }; enum info_subblock { INFO_SB_MODEMSW_VERSION = 0x00, INFO_SB_PRODUCT_INFO_NAME = 0x01, INFO_SB_PRODUCT_INFO_MANUFACTURER = 0x07, INFO_SB_SN_IMEI_PLAIN = 0x41, INFO_SB_SN_IMEI_SV_TO_NET = 0x43, INFO_SB_PP = 0x47, INFO_SB_MCUSW_VERSION = 0x48, }; enum info_product_info_type { INFO_PRODUCT_NAME = 0x01, INFO_PRODUCT_MANUFACTURER = 0x07, }; enum info_serial_number_type { INFO_SN_IMEI_PLAIN = 0x41, }; enum info_version_type { INFO_MCUSW = 0x01, }; enum info_pp_feature { INFO_PP_MAX_PDP_CONTEXTS = 0xCA }; #ifdef __cplusplus }; #endif #endif /* !__ISIMODEM_INFO_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/uicc-util.c0000644000015600001650000000654012671500024023732 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 ST-Ericsson AB. * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "simutil.h" #include "sim.h" #include "uicc-util.h" #include "uicc.h" #include "debug.h" #define USIM_APP_DEDICATED_FILE 0x7FFF gboolean uicc_get_fileid_path(struct uicc_sim_data *sd, int *mf_path, int *df1_path, int *df2_path, unsigned char *df_len, int fileid) { switch (fileid) { case SIM_EFPL_FILEID: case SIM_EF_ICCID_FILEID: *mf_path = SIM_MF_FILEID; *df1_path = 0x0000; *df2_path = 0x0000; *df_len = 2; break; case SIM_EFSMSP_FILEID: case SIM_EFSDN_FILEID: case SIM_EFMSISDN_FILEID: *mf_path = SIM_MF_FILEID; if (sd->app_type == UICC_APPL_TYPE_ICC_SIM) *df1_path = SIM_DFTELECOM_FILEID; else *df1_path = USIM_APP_DEDICATED_FILE; *df2_path = 0x0000; *df_len = 4; break; case SIM_EFLI_FILEID: case SIM_EFSPN_FILEID: case SIM_EFAD_FILEID: case SIM_EFPNN_FILEID: case SIM_EFOPL_FILEID: case SIM_EFMBDN_FILEID: case SIM_EFMBI_FILEID: case SIM_EFMWIS_FILEID: case SIM_EFSPDI_FILEID: case SIM_EFECC_FILEID: case SIM_EFCBMI_FILEID: case SIM_EFCBMIR_FILEID: case SIM_EFCBMID_FILEID: case SIM_EFIMSI_FILEID: case SIM_EFPHASE_FILEID: /*Did not find in TS 31.102 v6.21.0*/ case SIM_EFARR_FILEID: case SIM_EF_CPHS_INFORMATION_FILEID: /*Found from unofficial source*/ *mf_path = SIM_MF_FILEID; if (sd->app_type == UICC_APPL_TYPE_ICC_SIM) *df1_path = SIM_DFGSM_FILEID; else *df1_path = USIM_APP_DEDICATED_FILE; *df2_path = 0x0000; *df_len = 4; break; /* No info */ case SIM_EF_CPHS_MBDN_FILEID: case SIM_EF_CPHS_MWIS_FILEID: DBG("======== No path info for %04X", fileid); return FALSE; case SIM_EFADN_FILEID: /* Only for SIM */ case SIM_EFEXT1_FILEID: /* Only for SIM */ *mf_path = SIM_MF_FILEID; *df1_path = SIM_DFTELECOM_FILEID; *df2_path = 0x0000; *df_len = 4; break; default: *mf_path = SIM_MF_FILEID; *df1_path = SIM_DFTELECOM_FILEID; *df2_path = SIM_DFPHONEBOOK_FILEID; *df_len = 6; break; } return TRUE; } uint8_t uicc_get_sfi(const int fileid) { /* SFI list from 3GPP TS 31.102 Annex H */ switch (fileid) { case SIM_EFECC_FILEID: return 01; case SIM_EFLI_FILEID: return 02; case SIM_EFAD_FILEID: return 03; case SIM_EFIMSI_FILEID: return 07; case SIM_EFCBMID_FILEID: return 0x0E; case SIM_EFPNN_FILEID: return 0x19; case SIM_EFOPL_FILEID: return 0x1A; default: return UICC_SFI_NOT_PRESENT; } } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/sim.c0000644000015600001650000005446612671500024022636 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "ofono.h" #include "simutil.h" #include "isimodem.h" #include "isiutil.h" #include "sim.h" #include "debug.h" #define SIM_MAX_SPN_LENGTH 16 struct sim_data { GIsiClient *client; GIsiClient *sec_client; enum ofono_sim_password_type passwd_state; ofono_bool_t ready; ofono_bool_t notify_ready; }; struct sim_imsi { uint8_t length; uint8_t imsi[8]; }; struct sim_iccid { uint8_t id[10]; }; struct sim_spn { uint16_t name[SIM_MAX_SPN_LENGTH + 1]; uint8_t disp_home; uint8_t disp_roam; }; struct file_info { int fileid; int length; int structure; int record_length; uint8_t access[3]; uint8_t file_status; }; static int sim_resp_status(const GIsiMessage *msg, uint8_t msgid, uint8_t service) { uint8_t type = 0; uint8_t status; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", strerror(-g_isi_msg_error(msg))); return -1; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", sim_message_id_name(g_isi_msg_id(msg))); return -1; } if (!g_isi_msg_data_get_byte(msg, 1, &status) || !g_isi_msg_data_get_byte(msg, 0, &type)) { DBG("Runt msg: %s", sim_message_id_name(msgid)); return -1; } if (status != SIM_SERV_OK) DBG("Request failed: %s", sim_isi_cause_name(status)); if (type != service) { DBG("Unexpected service: 0x%02X", type); return -1; } return status; } /* Returns file info */ static gboolean fake_file_info(gpointer user) { struct isi_cb_data *cbd = user; ofono_sim_file_info_cb_t cb = cbd->cb; struct file_info const *fi = cbd->user; DBG("Returning static file info for %04X", fi->fileid); CALLBACK_WITH_SUCCESS(cb, fi->length, fi->structure, fi->record_length, fi->access, fi->file_status, cbd->data); g_free(cbd); return FALSE; } static void isi_read_file_info(struct ofono_sim *sim, int fileid, const unsigned char *path, unsigned int path_len, ofono_sim_file_info_cb_t cb, void *data) { int i; static struct file_info const info[] = { { SIM_EFSPN_FILEID, 17, 0, 0, { 0x0f, 0xff, 0xff }, 1 }, { SIM_EF_ICCID_FILEID, 10, 0, 0, { 0x0f, 0xff, 0xff }, 1 }, }; int N = sizeof(info) / sizeof(info[0]); struct isi_cb_data *cbd; for (i = 0; i < N; i++) { if (fileid == info[i].fileid) { cbd = isi_cb_data_new((void *) &info[i], cb, data); g_idle_add(fake_file_info, cbd); return; } } DBG("Fileid %04X not implemented", fileid); CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, data); } static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid, uint8_t service) { return sim_resp_status(msg, msgid, service) == SIM_SERV_OK; } static void spn_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_sim_read_cb_t cb = cbd->cb; const struct sim_spn *resp = NULL; size_t len = sizeof(struct sim_spn); uint8_t spn[SIM_MAX_SPN_LENGTH + 1]; int i; if (!check_response_status(msg, SIM_SERV_PROV_NAME_RESP, SIM_ST_READ_SERV_PROV_NAME)) goto error; if (!g_isi_msg_data_get_struct(msg, 2, (const void **) &resp, len)) goto error; /* Set display condition bits */ spn[0] = (resp->disp_home & 0x01) | ((resp->disp_roam & 0x01) << 1); /* Convert from a NULL-terminated UCS-2 string to ASCII */ for (i = 0; i < SIM_MAX_SPN_LENGTH; i++) { uint16_t c = resp->name[i] >> 8 | resp->name[i] << 8; if (c == 0) c = 0xFF; else if (!g_ascii_isprint(c)) c = '?'; spn[i + 1] = c; } CALLBACK_WITH_SUCCESS(cb, spn, sizeof(spn), cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); } static gboolean isi_read_spn(struct ofono_sim *sim, struct isi_cb_data *cbd) { struct sim_data *sd = ofono_sim_get_data(sim); const uint8_t msg[] = { SIM_SERV_PROV_NAME_REQ, SIM_ST_READ_SERV_PROV_NAME, 0 }; return g_isi_client_send(sd->client, msg, sizeof(msg), spn_resp_cb, cbd, g_free); } static void read_iccid_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_sim_read_cb_t cb = cbd->cb; struct sim_iccid *icc; size_t len = sizeof(struct sim_iccid); if (!check_response_status(msg, SIM_READ_FIELD_RESP, ICC)) goto error; if (!g_isi_msg_data_get_struct(msg, 2, (const void **) &icc, len)) goto error; CALLBACK_WITH_SUCCESS(cb, icc->id, 10, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); } static gboolean isi_read_iccid(struct ofono_sim *sim, struct isi_cb_data *cbd) { struct sim_data *sd = ofono_sim_get_data(sim); const uint8_t req[] = { SIM_READ_FIELD_REQ, ICC, }; return g_isi_client_send(sd->client, req, sizeof(req), read_iccid_resp_cb, cbd, g_free); } static void isi_read_file_transparent(struct ofono_sim *sim, int fileid, int start, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_cb_t cb, void *data) { struct isi_cb_data *cbd; gboolean done; cbd = isi_cb_data_new(sim, cb, data); if (cbd == NULL) goto error; switch (fileid) { case SIM_EFSPN_FILEID: done = isi_read_spn(sim, cbd); break; case SIM_EF_ICCID_FILEID: done = isi_read_iccid(sim, cbd); break; default: done = FALSE; } if (done) return; DBG("Fileid %04X not implemented", fileid); error: CALLBACK_WITH_FAILURE(cb, NULL, 0, data); g_free(cbd); } static void isi_read_file_linear(struct ofono_sim *sim, int fileid, int record, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_cb_t cb, void *data) { DBG("Fileid %04X not implemented", fileid); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } static void isi_read_file_cyclic(struct ofono_sim *sim, int fileid, int record, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_cb_t cb, void *data) { DBG("Fileid %04X not implemented", fileid); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } static void isi_write_file_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 *data) { DBG("Fileid %04X not implemented", fileid); CALLBACK_WITH_FAILURE(cb, data); } static void isi_write_file_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 *data) { DBG("Fileid %04X not implemented", fileid); CALLBACK_WITH_FAILURE(cb, data); } static void isi_write_file_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 *data) { DBG("Fileid %04X not implemented", fileid); CALLBACK_WITH_FAILURE(cb, data); } static void imsi_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_sim_imsi_cb_t cb = cbd->cb; const struct sim_imsi *resp; size_t len = sizeof(struct sim_imsi); char imsi[SIM_MAX_IMSI_LENGTH + 1]; size_t i, j; if (!check_response_status(msg, SIM_IMSI_RESP_READ_IMSI, READ_IMSI)) goto error; if (!g_isi_msg_data_get_struct(msg, 2, (const void **) &resp, len)) goto error; /* Ignore the low-order semi-octet of the first byte */ imsi[0] = ((resp->imsi[0] & 0xF0) >> 4) + '0'; for (i = 1, j = 1; i < resp->length && j < SIM_MAX_IMSI_LENGTH; i++) { char nibble; imsi[j++] = (resp->imsi[i] & 0x0F) + '0'; nibble = (resp->imsi[i] & 0xF0) >> 4; if (nibble != 0x0F) imsi[j++] = nibble + '0'; } imsi[j] = '\0'; CALLBACK_WITH_SUCCESS(cb, imsi, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void isi_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb, void *data) { struct sim_data *sd = ofono_sim_get_data(sim); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); const uint8_t msg[] = { SIM_IMSI_REQ_READ_IMSI, READ_IMSI }; size_t len = sizeof(msg); if (cbd == NULL || sd == NULL) goto error; if (g_isi_client_send(sd->client, msg, len, imsi_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, NULL, data); g_free(cbd); } static void isi_query_passwd_state(struct ofono_sim *sim, ofono_sim_passwd_cb_t cb, void *data) { struct sim_data *sd = ofono_sim_get_data(sim); DBG("passwd_state %u", sd->passwd_state); sd->notify_ready = TRUE; switch (sd->passwd_state) { case OFONO_SIM_PASSWORD_NONE: if (sd->ready) CALLBACK_WITH_SUCCESS(cb, sd->passwd_state, data); else CALLBACK_WITH_FAILURE(cb, -1, data); break; case OFONO_SIM_PASSWORD_INVALID: CALLBACK_WITH_FAILURE(cb, -1, data); break; default: CALLBACK_WITH_SUCCESS(cb, sd->passwd_state, data); } } static void sim_set_passwd_state(struct ofono_sim *sim, enum ofono_sim_password_type pin_type) { struct sim_data *sd = ofono_sim_get_data(sim); int inserted; int previous; if (pin_type == sd->passwd_state) return; DBG("new state \"%s\"", sim_password_name(pin_type)); inserted = pin_type != OFONO_SIM_PASSWORD_INVALID; previous = sd->passwd_state != OFONO_SIM_PASSWORD_INVALID; sd->passwd_state = pin_type; if (pin_type != OFONO_SIM_PASSWORD_NONE) { sd->ready = FALSE; sd->notify_ready = FALSE; } if (inserted != previous) ofono_sim_inserted_notify(sim, inserted); } static void check_sec_response(const GIsiMessage *msg, void *opaque, uint8_t success, uint8_t failure) { struct isi_cb_data *cbd = opaque; ofono_sim_lock_unlock_cb_t cb = cbd->cb; struct ofono_sim *sim = cbd->user; uint8_t id; uint8_t cause; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", strerror(-g_isi_msg_error(msg))); goto failure; } id = g_isi_msg_id(msg); if (id == success) { DBG("%s", sec_message_id_name(id)); sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE); CALLBACK_WITH_SUCCESS(cb, cbd->data); return; } if (id == failure && g_isi_msg_data_get_byte(msg, 0, &cause)) { DBG("%s(cause=%02x)", sec_message_id_name(id), cause); if (cause == SEC_CAUSE_CODE_BLOCKED) sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PUK); } else DBG("Error msg: %s", sec_message_id_name(id)); failure: CALLBACK_WITH_FAILURE(cb, cbd->data); } static void sec_code_verify_resp(const GIsiMessage *msg, void *opaque) { check_sec_response(msg, opaque, SEC_CODE_VERIFY_OK_RESP, SEC_CODE_VERIFY_FAIL_RESP); } static void isi_send_passwd(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); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); unsigned char msg[2 + SEC_CODE_MAX_LENGTH + 1] = { SEC_CODE_VERIFY_REQ, SEC_CODE_PIN, }; int len = 2 + strlen(passwd) + 1; DBG(""); if (!cbd) goto error; strcpy((char *) msg + 2, passwd); if (g_isi_client_send(sd->sec_client, msg, len, sec_code_verify_resp, cbd, g_free)) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void isi_reset_passwd(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); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); enum ofono_sim_password_type passwd_type = OFONO_SIM_PASSWORD_SIM_PIN; unsigned char msg[2 + 2 * (SEC_CODE_MAX_LENGTH + 1)] = { SEC_CODE_VERIFY_REQ, }; size_t len = sizeof(msg); DBG(""); if (!cbd) goto error; if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN) msg[1] = SEC_CODE_PUK; else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) msg[1] = SEC_CODE_PUK2; else goto error; strcpy((char *) &msg[2], puk); strcpy((char *) &msg[2 + SEC_CODE_MAX_LENGTH + 1], passwd); if (g_isi_client_send(sd->sec_client, msg, len, sec_code_verify_resp, cbd, g_free)) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } /* ISI callback: Enable/disable PIN */ static void pin_enable_resp_cb(const GIsiMessage *msg, void *opaque) { check_sec_response(msg, opaque, SEC_CODE_STATE_OK_RESP, SEC_CODE_STATE_FAIL_RESP); } static void isi_lock(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) { struct sim_data *sd = ofono_sim_get_data(sim); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); unsigned char req[3 + SEC_CODE_MAX_LENGTH + 1] = { SEC_CODE_STATE_REQ, }; if (!cbd) goto error; DBG("enable %d pintype %d pass %s", enable, passwd_type, passwd); if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN) req[1] = SEC_CODE_PIN; else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) req[1] = SEC_CODE_PIN2; else goto error; if (enable) req[2] = SEC_CODE_ENABLE; else req[2] = SEC_CODE_DISABLE; strcpy((char *) &req[3], passwd); if (g_isi_client_send(sd->sec_client, req, sizeof(req), pin_enable_resp_cb, cbd, g_free)) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } /* ISI callback: PIN state (enabled/disabled) query */ static void sec_code_change_resp(const GIsiMessage *msg, void *opaque) { check_sec_response(msg, opaque, SEC_CODE_CHANGE_OK_RESP, SEC_CODE_CHANGE_FAIL_RESP); } static void isi_change_passwd(struct ofono_sim *sim, enum ofono_sim_password_type passwd_type, const char *old, const char *new, ofono_sim_lock_unlock_cb_t cb, void *data) { struct sim_data *sd = ofono_sim_get_data(sim); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); unsigned char msg[2 + 2 * (SEC_CODE_MAX_LENGTH + 1)] = { SEC_CODE_CHANGE_REQ, }; DBG("passwd_type %d", passwd_type); if (!cbd) goto error; if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN) msg[1] = SEC_CODE_PIN; else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) msg[1] = SEC_CODE_PIN2; else goto error; strcpy((char *) &msg[2], old); strcpy((char *) &msg[2 + SEC_CODE_MAX_LENGTH + 1], new); if (g_isi_client_send(sd->sec_client, msg, sizeof(msg), sec_code_change_resp, cbd, g_free)) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } /* 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); } static void isi_query_locked(struct ofono_sim *sim, enum ofono_sim_password_type passwd_type, ofono_sim_locked_cb_t cb, void *data) { struct sim_data *sd = ofono_sim_get_data(sim); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); unsigned char msg[] = { SEC_CODE_STATE_REQ, 0, SEC_CODE_STATE_QUERY }; DBG(""); if (!cbd) goto error; if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN) msg[1] = SEC_CODE_PIN; else if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) msg[1] = SEC_CODE_PIN2; else goto error; if (g_isi_client_send(sd->sec_client, msg, sizeof(msg), sec_code_state_resp_cb, cbd, g_free)) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void sim_ind_cb(const GIsiMessage *msg, void *opaque) { struct ofono_sim *sim = opaque; uint8_t service; uint8_t status; DBG(""); if (g_isi_msg_id(msg) != SIM_IND || !g_isi_msg_data_get_byte(msg, 0, &service) || !g_isi_msg_data_get_byte(msg, 1, &status)) return; if (status == SIM_SERV_PIN_VERIFY_REQUIRED && service == SIM_ST_PIN) sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PIN); else if (status == SIM_SERV_SIM_BLOCKED) sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PUK); else if (status == SIM_SERV_INIT_OK && service == SIM_ST_INFO) sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE); else if (status == SIM_SERV_SIM_DISCONNECTED) sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_INVALID); } static void sim_server_ready_ind_cb(const GIsiMessage *msg, void *opaque) { struct ofono_sim *sim = opaque; struct sim_data *sd = ofono_sim_get_data(sim); DBG(""); if (sd == NULL || g_isi_msg_id(msg) != SIM_SERVER_READY_IND) return; sd->ready = TRUE; if (sd->notify_ready) __ofono_sim_recheck_pin(sim); } static void read_dyn_flags_cb(const GIsiMessage *msg, void *opaque) { struct ofono_sim *sim = opaque; struct sim_data *sd = ofono_sim_get_data(sim); int status; status = sim_resp_status(msg, SIM_DYNAMIC_FLAGS_RESP, READ_DYN_FLAGS); if (status < 0 || status == SIM_SERV_NOTREADY) return; sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE); sd->ready = TRUE; if (sd->notify_ready) __ofono_sim_recheck_pin(sim); } static void read_dyn_flags_req(struct ofono_sim *sim) { struct sim_data *sd = ofono_sim_get_data(sim); unsigned char req[] = { SIM_DYNAMIC_FLAGS_REQ, READ_DYN_FLAGS, 0 }; g_isi_client_send(sd->client, req, sizeof(req), read_dyn_flags_cb, sim, NULL); } static void sec_state_resp_cb(const GIsiMessage *msg, void *opaque) { struct ofono_sim *sim = opaque; uint8_t msgid; uint8_t cause; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", strerror(-g_isi_msg_error(msg))); return; } msgid = g_isi_msg_id(msg); if (msgid != SEC_STATE_RESP) { DBG("Unexpected msg: %s", sec_message_id_name(msgid)); return; } if (!g_isi_msg_data_get_byte(msg, 0, &cause)) { DBG("Runt msg: %s", sec_message_id_name(msgid)); return; } DBG("%s(cause=0x%0x)", sec_message_id_name(msgid), cause); switch (cause) { case SEC_STARTUP_OK: DBG("SEC_STARTUP_OK"); sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_NONE); /* Check if SIM server is already ready */ read_dyn_flags_req(sim); break; case SEC_CAUSE_PIN_REQUIRED: DBG("SEC_CAUSE_PIN_REQUIRED"); sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PIN); break; case SEC_CAUSE_PUK_REQUIRED: DBG("SEC_CAUSE_PUK_REQUIRED"); sim_set_passwd_state(sim, OFONO_SIM_PASSWORD_SIM_PIN); break; case SEC_CAUSE_NO_SIM: DBG("SEC_CAUSE_NO_SIM"); break; case SEC_CAUSE_INVALID_SIM: DBG("SEC_CAUSE_INVALID_SIM"); break; case SEC_CAUSE_SIM_REJECTED: DBG("SEC_CAUSE_SIM_REJECTED"); break; default: break; } } static void isi_sec_state_req(struct ofono_sim *sim) { struct sim_data *sd = ofono_sim_get_data(sim); unsigned char req[] = { SEC_STATE_REQ, 0, 0 }; g_isi_client_send(sd->sec_client, req, sizeof(req), sec_state_resp_cb, sim, NULL); } static void sim_status_resp_cb(const GIsiMessage *msg, void *opaque) { struct ofono_sim *sim = opaque; struct sim_data *sd = ofono_sim_get_data(sim); int status = sim_resp_status(msg, SIM_STATUS_RESP, SIM_ST_CARD_STATUS); if (status < 0 || status == SIM_SERV_SIM_DISCONNECTED) return; /* We probably have a SIM. */ if (sd->sec_client) isi_sec_state_req(sim); else read_dyn_flags_req(sim); } static void isi_sim_status_req(struct ofono_sim *sim) { struct sim_data *sd = ofono_sim_get_data(sim); const unsigned char req[] = { SIM_STATUS_REQ, SIM_ST_CARD_STATUS }; g_isi_client_send(sd->client, req, sizeof(req), sim_status_resp_cb, sim, NULL); } static void sec_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_sim *sim = data; struct sim_data *sd = ofono_sim_get_data(sim); if (g_isi_msg_error(msg) < 0) { DBG("PN_SECURITY: %s", strerror(-g_isi_msg_error(msg))); DBG("PIN code handling not available"); g_isi_client_destroy(sd->sec_client); sd->sec_client = NULL; } g_isi_client_ind_subscribe(sd->client, SIM_IND, sim_ind_cb, sim); g_isi_client_ind_subscribe(sd->client, SIM_SERVER_READY_IND, sim_server_ready_ind_cb, sim); /* Check if we have a SIM */ isi_sim_status_req(sim); ofono_sim_register(sim); } static void sim_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_sim *sim = data; struct sim_data *sd = ofono_sim_get_data(sim); if (g_isi_msg_error(msg) < 0) { DBG("PN_SIM: %s", strerror(-g_isi_msg_error(msg))); ofono_sim_remove(sim); return; } ISI_RESOURCE_DBG(msg); g_isi_client_verify(sd->sec_client, sec_reachable_cb, sim, NULL); } static int isi_sim_probe(struct ofono_sim *sim, unsigned int vendor, void *user) { GIsiModem *modem = user; struct sim_data *sd; sd = g_try_new0(struct sim_data, 1); if (sd == NULL) return -ENOMEM; sd->passwd_state = OFONO_SIM_PASSWORD_INVALID; sd->client = g_isi_client_create(modem, PN_SIM); if (sd->client == NULL) goto error; sd->sec_client = g_isi_client_create(modem, PN_SECURITY); if (sd->sec_client == NULL) goto error; g_isi_client_set_timeout(sd->client, SIM_TIMEOUT); g_isi_client_set_timeout(sd->sec_client, SIM_TIMEOUT); ofono_sim_set_data(sim, sd); g_isi_client_ind_subscribe(sd->client, SIM_IND, sim_ind_cb, sim); g_isi_client_verify(sd->client, sim_reachable_cb, sim, NULL); return 0; error: g_isi_client_destroy(sd->client); g_isi_client_destroy(sd->sec_client); return -ENOMEM; } static void isi_sim_remove(struct ofono_sim *sim) { struct sim_data *data = ofono_sim_get_data(sim); ofono_sim_set_data(sim, NULL); if (data == NULL) return; g_isi_client_destroy(data->client); g_isi_client_destroy(data->sec_client); g_free(data); } static struct ofono_sim_driver driver = { .name = "isimodem", .probe = isi_sim_probe, .remove = isi_sim_remove, .read_file_info = isi_read_file_info, .read_file_transparent = isi_read_file_transparent, .read_file_linear = isi_read_file_linear, .read_file_cyclic = isi_read_file_cyclic, .write_file_transparent = isi_write_file_transparent, .write_file_linear = isi_write_file_linear, .write_file_cyclic = isi_write_file_cyclic, .read_imsi = isi_read_imsi, .query_passwd_state = isi_query_passwd_state, .send_passwd = isi_send_passwd, .reset_passwd = isi_reset_passwd, .lock = isi_lock, .change_passwd = isi_change_passwd, .query_locked = isi_query_locked, }; void isi_sim_init(void) { ofono_sim_driver_register(&driver); } void isi_sim_exit(void) { ofono_sim_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/infoserver.c0000644000015600001650000000636212671500024024220 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "info.h" #include "infoserver.h" struct isi_infoserver { GIsiServer *server; unsigned sv; /* Software version in 0..98 */ }; static GIsiVersion isiversion = { .major = 0, .minor = 0, }; static void send_error(GIsiServer *server, const GIsiMessage *req, uint8_t code) { const uint8_t error[] = { INFO_SERIAL_NUMBER_READ_RESP, code, 0 }; g_isi_server_send(server, req, error, sizeof(error)); } static void send_response(GIsiServer *server, const GIsiMessage *req, unsigned sv) { const uint8_t resp[] = { INFO_SERIAL_NUMBER_READ_RESP, INFO_OK, 1, INFO_SB_SN_IMEI_SV_TO_NET, 16, /* Mobile Identity IE, TS 24.008 section 10.5.1.4 */ 0, 9, /* F in place of IMEI digits and filler */ 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f | ((sv / 10) << 4), 0xf0 | ((sv % 10) & 0x0f), /* Subblock filler */ 0, 0, 0, }; g_isi_server_send(server, req, resp, sizeof(resp)); } static void serial_number_read_req(const GIsiMessage *msg, void *data) { struct isi_infoserver *self = data; uint8_t target; if (g_isi_msg_id(msg) != INFO_SERIAL_NUMBER_READ_REQ) return; if (!g_isi_msg_data_get_byte(msg, 0, &target)) { send_error(self->server, msg, INFO_FAIL); return; } if (target == INFO_SB_SN_IMEI_SV_TO_NET) { /* IMEISV defined in 3GPP TS 23.003 section 6.2.2 */ send_response(self->server, msg, self->sv); return; } DBG("Unknown query target 0x%02X", target); send_error(self->server, msg, INFO_NOT_SUPPORTED); } struct isi_infoserver *isi_infoserver_create(struct ofono_modem *modem, void *data) { struct isi_infoserver *self; GIsiModem *isimodem = data; if (isimodem == NULL) { errno = EINVAL; return NULL; } self = g_try_new0(struct isi_infoserver, 1); if (self == NULL) { errno = ENOMEM; return NULL; } self->server = g_isi_server_create(isimodem, PN_EPOC_INFO, &isiversion); if (self->server == NULL) { g_free(self); errno = ENOMEM; return NULL; } g_isi_server_handle(self->server, INFO_SERIAL_NUMBER_READ_REQ, serial_number_read_req, self); return self; } void isi_infoserver_destroy(struct isi_infoserver *self) { if (self == NULL) return; g_isi_server_destroy(self->server); g_free(self); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/isiutil.h0000644000015600001650000000416612671500024023525 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_UTIL_H #define __ISIMODEM_UTIL_H struct isi_cb_data { void *cb; void *data; void *user; }; static inline struct isi_cb_data *isi_cb_data_new(void *user, void *cb, void *data) { struct isi_cb_data *ret; ret = g_try_new0(struct isi_cb_data, 1); if (ret) { ret->cb = cb; ret->data = data; ret->user = user; } return ret; } #define CALLBACK_WITH_FAILURE(f, args...) \ do { \ struct ofono_error e; \ e.type = OFONO_ERROR_TYPE_FAILURE; \ e.error = 0; \ f(&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) #define ISI_RESOURCE_DBG(msg) \ DBG("QSO: %s [0x%02X] v%03d.%03d", \ pn_resource_name(g_isi_msg_resource((msg))), \ g_isi_msg_resource((msg)), \ g_isi_msg_version_major((msg)), \ g_isi_msg_version_minor((msg))); #define ISI_VERSION_AT_LEAST(ver,maj,min) \ ((ver) != NULL && ((ver)->major > (maj) || \ ((ver)->major == (maj) && \ (ver)->minor >= (min)))) #define ALIGN4(val) (((val) + 3) & ~3) #define ISI_16BIT(val) \ (((val) >> 8) & 0xFF), ((val) & 0xFF) #define ISI_32BIT(val) \ (((val) >> 24) & 0xFF), (((val) >> 16) & 0xFF), \ (((val) >> 8) & 0xFF), ((val) & 0xFF) #endif /* !__ISIMODEM_UTIL_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/devinfo.c0000644000015600001650000001361112671500024023463 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "isimodem.h" #include "isiutil.h" #include "debug.h" #include "info.h" struct devinfo_data { GIsiClient *client; }; static void info_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_devinfo_query_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint8_t msgid; uint8_t status; msgid = g_isi_msg_id(msg); if (msgid != INFO_PRODUCT_INFO_READ_RESP && msgid != INFO_VERSION_READ_RESP && msgid != INFO_SERIAL_NUMBER_READ_RESP) goto error; if (g_isi_msg_error(msg) < 0) goto error; if (!g_isi_msg_data_get_byte(msg, 0, &status)) goto error; if (status != INFO_OK) goto error; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { uint8_t id = g_isi_sb_iter_get_id(&iter); uint8_t chars; char *info = NULL; if (id != INFO_SB_PRODUCT_INFO_MANUFACTURER && id != INFO_SB_PRODUCT_INFO_NAME && id != INFO_SB_MCUSW_VERSION && id != INFO_SB_SN_IMEI_PLAIN) continue; if (g_isi_sb_iter_get_len(&iter) < 5) goto error; if (!g_isi_sb_iter_get_byte(&iter, &chars, 3)) goto error; if (!g_isi_sb_iter_get_latin_tag(&iter, &info, chars, 4)) goto error; CALLBACK_WITH_SUCCESS(cb, info, cbd->data); g_free(info); return; } error: CALLBACK_WITH_FAILURE(cb, "", cbd->data); } static void isi_query_manufacturer(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct devinfo_data *dev = ofono_devinfo_get_data(info); struct isi_cb_data *cbd = isi_cb_data_new(dev, cb, data); const uint8_t msg[] = { INFO_PRODUCT_INFO_READ_REQ, INFO_PRODUCT_MANUFACTURER }; size_t len = sizeof(msg); if (cbd == NULL || dev == NULL) goto error; if (g_isi_client_send(dev->client, msg, len, info_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, "", data); g_free(cbd); } static void isi_query_model(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct devinfo_data *dev = ofono_devinfo_get_data(info); struct isi_cb_data *cbd = isi_cb_data_new(dev, cb, data); const uint8_t msg[] = { INFO_PRODUCT_INFO_READ_REQ, INFO_PRODUCT_NAME }; size_t len = sizeof(msg); if (cbd == NULL || dev == NULL) goto error; if (g_isi_client_send(dev->client, msg, len, info_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, "", data); g_free(cbd); } static void isi_query_revision(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct devinfo_data *dev = ofono_devinfo_get_data(info); struct isi_cb_data *cbd = isi_cb_data_new(dev, cb, data); const uint8_t msg[] = { INFO_VERSION_READ_REQ, 0x00, INFO_MCUSW, 0x00, 0x00, 0x00, 0x00 }; size_t len = sizeof(msg); if (cbd == NULL || dev == NULL) goto error; if (g_isi_client_send(dev->client, msg, len, info_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, "", data); g_free(cbd); } static void isi_query_serial(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct devinfo_data *dev = ofono_devinfo_get_data(info); struct isi_cb_data *cbd = isi_cb_data_new(dev, cb, data); const uint8_t msg[] = { INFO_SERIAL_NUMBER_READ_REQ, INFO_SN_IMEI_PLAIN }; size_t len = sizeof(msg); if (cbd == NULL || dev == NULL) goto error; if (g_isi_client_send(dev->client, msg, len, info_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, "", data); g_free(cbd); } static void reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_devinfo *info = data; if (g_isi_msg_error(msg) < 0) { ofono_devinfo_remove(info); return; } ISI_RESOURCE_DBG(msg); ofono_devinfo_register(info); } static int isi_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor, void *user) { GIsiModem *idx = user; struct devinfo_data *data = g_try_new0(struct devinfo_data, 1); if (data == NULL) return -ENOMEM; data->client = g_isi_client_create(idx, PN_PHONE_INFO); if (data->client == NULL) { g_free(data); return -ENOMEM; } ofono_devinfo_set_data(info, data); g_isi_client_set_timeout(data->client, INFO_TIMEOUT); g_isi_client_verify(data->client, reachable_cb, info, NULL); return 0; } static void isi_devinfo_remove(struct ofono_devinfo *info) { struct devinfo_data *data = ofono_devinfo_get_data(info); ofono_devinfo_set_data(info, NULL); if (data == NULL) return; g_isi_client_destroy(data->client); g_free(data); } static struct ofono_devinfo_driver driver = { .name = "isimodem", .probe = isi_devinfo_probe, .remove = isi_devinfo_remove, .query_manufacturer = isi_query_manufacturer, .query_model = isi_query_model, .query_revision = isi_query_revision, .query_serial = isi_query_serial }; void isi_devinfo_init(void) { ofono_devinfo_driver_register(&driver); } void isi_devinfo_exit(void) { ofono_devinfo_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/sms.h0000644000015600001650000001537712671500024022653 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __ISIMODEM_SMS_H #define __ISIMODEM_SMS_H #ifdef __cplusplus extern "C" { #endif #define PN_SMS 0x02 #define CBS_TIMEOUT 5 #define SMS_TIMEOUT 5 enum sms_isi_cause { SMS_OK = 0x00, SMS_ERR_ROUTING_RELEASED = 0x01, SMS_ERR_INVALID_PARAMETER = 0x02, SMS_ERR_DEVICE_FAILURE = 0x03, SMS_ERR_PP_RESERVED = 0x04, SMS_ERR_ROUTE_NOT_AVAILABLE = 0x05, SMS_ERR_ROUTE_NOT_ALLOWED = 0x06, SMS_ERR_SERVICE_RESERVED = 0x07, SMS_ERR_INVALID_LOCATION = 0x08, SMS_ERR_NO_SIM = 0x09, SMS_ERR_SIM_NOT_READY = 0x0A, SMS_ERR_NO_NETW_RESPONSE = 0x0B, SMS_ERR_DEST_ADDR_FDN_RESTRICTED = 0x0C, SMS_ERR_SMSC_ADDR_FDN_RESTRICTED = 0x0D, SMS_ERR_RESEND_ALREADY_DONE = 0x0E, SMS_ERR_SMSC_ADDR_NOT_AVAILABLE = 0x0F, SMS_ERR_ROUTING_FAILED = 0x10, SMS_ERR_CS_INACTIVE = 0x11, SMS_ERR_SAT_MO_CONTROL_MODIFIED = 0x12, SMS_ERR_SAT_MO_CONTROL_REJECT = 0x13, SMS_ERR_TRACFONE_FAILED = 0x14, }; enum sms_isi_cause_type { SMS_CAUSE_TYPE_COMMON = 0x00, SMS_CAUSE_TYPE_GSM = 0x01, }; enum sms_gsm_cause { SMS_GSM_ERR_UNASSIGNED_NUMBER = 0x01, SMS_GSM_ERR_OPER_DETERMINED_BARR = 0x08, SMS_GSM_ERR_CALL_BARRED = 0x0A, SMS_GSM_ERR_RESERVED = 0x0B, SMS_GSM_ERR_MSG_TRANSFER_REJ = 0x15, SMS_GSM_ERR_MEMORY_CAPACITY_EXC = 0x16, SMS_GSM_ERR_DEST_OUT_OF_ORDER = 0x1B, SMS_GSM_ERR_UNDEFINED_SUBSCRIBER = 0x1C, SMS_GSM_ERR_FACILITY_REJECTED = 0x1D, SMS_GSM_ERR_UNKNOWN_SUBSCRIBER = 0x1E, SMS_GSM_ERR_NETW_OUT_OF_ORDER = 0x26, SMS_GSM_ERR_TEMPORARY_FAILURE = 0x29, SMS_GSM_ERR_CONGESTION = 0x2A, SMS_GSM_ERR_RESOURCE_UNAVAILABLE = 0x2F, SMS_GSM_ERR_REQ_FACILITY_NOT_SUB = 0x32, SMS_GSM_ERR_REQ_FACILITY_NOT_IMP = 0x45, SMS_GSM_ERR_INVALID_REFERENCE = 0x51, SMS_GSM_ERR_INCORRECT_MESSAGE = 0x5F, SMS_GSM_ERR_INVALID_MAND_INFO = 0x60, SMS_GSM_ERR_INVALID_MSG_TYPE = 0x61, SMS_GSM_ERR_MSG_NOT_COMP_WITH_ST = 0x62, SMS_GSM_ERR_INVALID_INFO_ELEMENT = 0x63, SMS_GSM_ERR_PROTOCOL_ERROR = 0x6F, SMS_GSM_ERR_INTERWORKING = 0x7F, SMS_GSM_ERR_NO_CAUSE = 0x80, SMS_GSM_ERR_IMSI_UNKNOWN_HLR = 0x82, SMS_GSM_ERR_ILLEGAL_MS = 0x83, SMS_GSM_ERR_IMSI_UNKNOWN_VLR = 0x84, SMS_GSM_ERR_IMEI_NOT_ACCEPTED = 0x85, SMS_GSM_ERR_ILLEGAL_ME = 0x86, SMS_GSM_ERR_PLMN_NOT_ALLOWED = 0x8B, SMS_GSM_ERR_LA_NOT_ALLOWED = 0x8C, SMS_GSM_ERR_ROAM_NOT_ALLOWED_LA = 0x8D, SMS_GSM_ERR_NO_SUITABLE_CELLS_LA = 0x8F, SMS_GSM_ERR_NETWORK_FAILURE = 0x91, SMS_GSM_ERR_MAC_FAILURE = 0x94, SMS_GSM_ERR_SYNC_FAILURE = 0x95, SMS_GSM_ERR_LOW_LAYER_CONGESTION = 0x96, SMS_GSM_ERR_AUTH_UNACCEPTABLE = 0x97, SMS_GSM_ERR_SERV_OPT_NOT_SUPPORTED = 0xA0, SMS_GSM_ERR_SERV_OPT_NOT_SUBSCRIBED = 0xA1, SMS_GSM_ERR_SERV_OPT_TEMP_OUT_OF_ORDER = 0xA2, SMS_GSM_ERR_CALL_CANNOT_BE_IDENTIFIED = 0xA6, SMS_GSM_ERR_SEMANTICALLY_INCORR_MSG = 0xDF, SMS_GSM_ERR_LOW_LAYER_INVALID_MAND_INFO = 0xE0, SMS_GSM_ERR_LOW_LAYER_INVALID_MSG_TYPE = 0xE1, SMS_GSM_ERR_LOW_LAYER_MSG_TYPE_NOT_COMP_WITH_ST = 0xE2, SMS_GSM_ERR_LOW_LAYER_INVALID_INFO_ELEMENT = 0xE3, SMS_GSM_ERR_CONDITIONAL_IE_ERROR = 0xE4, SMS_GSM_ERR_LOW_LAYER_MSG_NOT_COMP_WITH_ST = 0xE5, SMS_GSM_ERR_CS_BARRED = 0xE8, SMS_GSM_ERR_LOW_LAYER_PROTOCOL_ERROR = 0xEF, }; enum sms_message_id { SMS_MESSAGE_SEND_REQ = 0x02, SMS_MESSAGE_SEND_RESP = 0x03, SMS_PP_ROUTING_REQ = 0x06, SMS_PP_ROUTING_RESP = 0x07, SMS_PP_ROUTING_NTF = 0x08, SMS_GSM_RECEIVED_PP_REPORT_REQ = 0x09, SMS_GSM_RECEIVED_PP_REPORT_RESP = 0x0A, SMS_GSM_CB_ROUTING_REQ = 0x0B, SMS_GSM_CB_ROUTING_RESP = 0x0C, SMS_GSM_CB_ROUTING_NTF = 0x0D, SMS_MESSAGE_SEND_STATUS_IND = 0x22, SMS_SETTINGS_UPDATE_REQ = 0x30, SMS_SETTINGS_UPDATE_RESP = 0x31, SMS_SETTINGS_READ_REQ = 0x32, SMS_SETTINGS_READ_RESP = 0x33, SMS_RECEIVED_MSG_REPORT_REQ = 0x3B, SMS_RECEIVED_MSG_REPORT_RESP = 0x3C, SMS_RECEIVE_MESSAGE_REQ = 0x41, SMS_RECEIVE_MESSAGE_RESP = 0x42, SMS_RECEIVED_MSG_IND = 0x43, }; enum sms_subblock { SMS_GSM_DELIVER = 0x00, SMS_GSM_STATUS_REPORT = 0x01, SMS_GSM_SUBMIT = 0x02, SMS_GSM_COMMAND = 0x03, SMS_GSM_DELIVER_REPORT = 0x06, SMS_GSM_REPORT = 0x0C, SMS_GSM_ROUTING = 0x0D, SMS_GSM_CB_MESSAGE = 0x0E, SMS_GSM_TPDU = 0x11, SMS_SB_TPDU = 0x001C, SMS_SB_ROUTE_INFO = 0x0023, SMS_SB_SMS_PARAMETERS = 0x0031, SMS_COMMON_DATA = 0x80, SMS_ADDRESS = 0x82, SMS_SB_ADDRESS = 0x0082, }; enum sms_routing_command { SMS_ROUTING_RELEASE = 0x00, SMS_ROUTING_SET = 0x01, SMS_ROUTING_SUSPEND = 0x02, SMS_ROUTING_RESUME = 0x03, SMS_ROUTING_UPDATE = 0x04, }; enum sms_route_preference { SMS_ROUTE_ANY = 0x00, SMS_ROUTE_GPRS_PREF = 0x00, SMS_ROUTE_CS = 0x01, SMS_ROUTE_GPRS = 0x02, SMS_ROUTE_CS_PREF = 0x03, SMS_ROUTE_DEFAULT = 0x04, }; enum sms_routing_mode { SMS_GSM_ROUTING_MODE_ALL = 0x0B, SMS_GSM_ROUTING_MODE_CB_DDL = 0x0C, }; enum sms_routing_type { SMS_GSM_TPDU_ROUTING = 0x06, }; enum sms_message_type { SMS_GSM_MT_ALL_TYPE = 0x06, }; enum sms_address_type { SMS_UNICODE_ADDRESS = 0x00, SMS_GSM_0340_ADDRESS = 0x01, SMS_GSM_0411_ADDRESS = 0x02, SMS_SMSC_ADDRESS = 0x02, }; enum sms_sender_type { SMS_SENDER_ANY = 0x00, SMS_SENDER_SIM_ATK = 0x01, }; enum sms_content_type { SMS_TYPE_DEFAULT = 0x00, SMS_TYPE_TEXT_MESSAGE = 0x01, }; enum sms_subject_list_type { SMS_CB_ALLOWED_IDS_LIST = 0x00, SMS_CB_NOT_ALLOWED_IDS_LIST = 0x01, }; enum sms_reception_command { SMS_RECEPTION_ACTIVATE = 0x01, SMS_RECEPTION_DEACTIVATE = 0x02, }; enum sms_reception_status { SMS_RECEPTION_ACTIVE = 0x01, SMS_RECEPTION_INACTIVE = 0x02, }; enum sms_setting_type { SMS_SETTING_TYPE_ROUTE = 0x02, }; enum sms_route_priority { SMS_ROUTE_NOT_AVAILABLE = 0x00, SMS_ROUTE_PRIORITY_1 = 0x01, SMS_ROUTE_PRIORITY_2 = 0x02, }; enum sms_parameter_indicator { SMS_PI_DESTINATION_ADDRESS = 0x01, SMS_PI_SERVICE_CENTER_ADDRESS = 0x02, SMS_PI_PROTOCOL_ID = 0x04, SMS_PI_DATA_CODING_SCHEME = 0x08, SMS_PI_VALIDITY_PERIOD = 0x10, }; enum sms_parameter_location { SMS_PARAMETER_LOCATION_DEFAULT = 0x00, }; #ifdef __cplusplus }; #endif #endif /* __ISIMODEM_SMS_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/infoserver.h0000644000015600001650000000221412671500024024215 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __OFONO_ISI_INFOSERVER_H #define __OFONO_ISI_INFOSERVER_H #ifdef __cplusplus extern "C" { #endif #include struct isi_infoserver; struct isi_infoserver *isi_infoserver_create(struct ofono_modem *modem, void *data); void isi_infoserver_destroy(struct isi_infoserver *self); #ifdef __cplusplus } #endif #endif /* __OFONO_ISI_INFOSERVER_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/network-registration.c0000644000015600001650000006523612671500024026244 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "isimodem.h" #include "isiutil.h" #include "network.h" #include "debug.h" struct reg_info { uint8_t status; uint8_t mode; }; struct gsm_info { uint16_t lac; uint32_t ci; uint8_t egprs; uint8_t hsdpa; uint8_t hsupa; }; struct rat_info { uint8_t rat; uint8_t compact; }; struct network_time { uint8_t year; uint8_t mon; uint8_t mday; uint8_t hour; uint8_t min; uint8_t sec; uint8_t utc; uint8_t dst; }; struct netreg_data { GIsiClient *client; GIsiClient *pn_network; GIsiClient *pn_modem_network; struct reg_info reg; struct gsm_info gsm; struct rat_info rat; GIsiVersion version; char nitz_name[OFONO_MAX_OPERATOR_NAME_LENGTH + 1]; }; static inline guint8 *mccmnc_to_bcd(const char *mcc, const char *mnc, guint8 *bcd) { bcd[0] = (mcc[0] - '0') | (mcc[1] - '0') << 4; bcd[1] = (mcc[2] - '0'); bcd[1] |= (mnc[2] == '\0' ? 0x0f : (mnc[2] - '0')) << 4; bcd[2] = (mnc[0] - '0') | (mnc[1] - '0') << 4; return bcd; } static inline int isi_status_to_at_status(struct reg_info *reg) { switch (reg->status) { case NET_REG_STATUS_NOSERV: case NET_REG_STATUS_NOSERV_NOTSEARCHING: case NET_REG_STATUS_NOSERV_NOSIM: case NET_REG_STATUS_POWER_OFF: case NET_REG_STATUS_NSPS: case NET_REG_STATUS_NSPS_NO_COVERAGE: return 0; case NET_REG_STATUS_HOME: return 1; case NET_REG_STATUS_NOSERV_SEARCHING: return 2; case NET_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW: return 3; case NET_REG_STATUS_ROAM_BLINK: case NET_REG_STATUS_ROAM: return 5; } return 4; } static inline int isi_to_at_tech(struct rat_info *rat, struct gsm_info *gsm) { int tech = -1; if (rat == NULL || gsm == NULL) return -1; if (rat->rat == NET_GSM_RAT) tech = 0; if (rat->compact) tech = 1; if (rat->rat == NET_UMTS_RAT) tech = 2; if (gsm->egprs) tech = 3; if (gsm->hsdpa) tech = 4; if (gsm->hsupa) tech = 5; if (gsm->hsdpa && gsm->hsupa) tech = 6; return tech; } static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid) { uint8_t cause; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", g_isi_msg_strerror(msg)); return FALSE; } if (g_isi_msg_id(msg) != msgid) return FALSE; if (!g_isi_msg_data_get_byte(msg, 0, &cause)) return FALSE; if (cause != NET_CAUSE_OK) { DBG("Request failed: %s", net_isi_cause_name(cause)); return FALSE; } return TRUE; } static gboolean parse_common_info(GIsiSubBlockIter *iter, struct reg_info *reg) { return reg && g_isi_sb_iter_get_byte(iter, ®->status, 2) && g_isi_sb_iter_get_byte(iter, ®->mode, 3); } static gboolean parse_gsm_info(GIsiSubBlockIter *iter, struct gsm_info *gsm) { return gsm && g_isi_sb_iter_get_word(iter, &gsm->lac, 2) && g_isi_sb_iter_get_dword(iter, &gsm->ci, 4) && g_isi_sb_iter_get_byte(iter, &gsm->egprs, 17) && g_isi_sb_iter_get_byte(iter, &gsm->hsdpa, 20) && g_isi_sb_iter_get_byte(iter, &gsm->hsupa, 21); } static void reg_status_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_netreg *netreg = data; struct netreg_data *nd = ofono_netreg_get_data(netreg); GIsiSubBlockIter iter; if (netreg == NULL || nd == NULL) return; if (g_isi_msg_id(msg) != NET_REG_STATUS_IND && g_isi_msg_id(msg) != NET_MODEM_REG_STATUS_IND) return; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case NET_REG_INFO_COMMON: if (!parse_common_info(&iter, &nd->reg)) return; break; case NET_GSM_REG_INFO: if (!parse_gsm_info(&iter, &nd->gsm)) return; break; } } ofono_netreg_status_notify(netreg, isi_status_to_at_status(&nd->reg), nd->gsm.lac, nd->gsm.ci, isi_to_at_tech(&nd->rat, &nd->gsm)); } static gboolean parse_rat_info(GIsiSubBlockIter *iter, struct rat_info *rat) { uint8_t len; if (!g_isi_sb_iter_get_byte(iter, &rat->rat, 2)) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &len, 3)) return FALSE; if (len != 0 && !g_isi_sb_iter_get_byte(iter, &rat->compact, 4)) return FALSE; return TRUE; } static void rat_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_netreg *netreg = data; struct netreg_data *nd = ofono_netreg_get_data(netreg); GIsiSubBlockIter iter; if (nd == NULL || g_isi_msg_id(msg) != NET_RAT_IND) return; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != NET_RAT_INFO) continue; if (!parse_rat_info(&iter, &nd->rat)) return; } ofono_netreg_status_notify(netreg, isi_status_to_at_status(&nd->reg), nd->gsm.lac, nd->gsm.ci, isi_to_at_tech(&nd->rat, &nd->gsm)); } static void reg_status_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; struct ofono_netreg *netreg = cbd->user; struct netreg_data *nd = ofono_netreg_get_data(netreg); ofono_netreg_status_cb_t cb = cbd->cb; GIsiSubBlockIter iter; if (!check_response_status(msg, NET_MODEM_REG_STATUS_GET_RESP) && !check_response_status(msg, NET_REG_STATUS_GET_RESP)) goto error; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case NET_REG_INFO_COMMON: if (!parse_common_info(&iter, &nd->reg)) goto error; break; case NET_GSM_REG_INFO: if (!parse_gsm_info(&iter, &nd->gsm)) goto error; break; } } CALLBACK_WITH_SUCCESS(cb, isi_status_to_at_status(&nd->reg), nd->gsm.lac, nd->gsm.ci, isi_to_at_tech(&nd->rat, &nd->gsm), cbd->data); g_free(cbd); return; error: CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data); g_free(cbd); } static void rat_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; struct ofono_netreg *netreg = cbd->user; struct netreg_data *nd = ofono_netreg_get_data(netreg); ofono_netreg_status_cb_t cb = cbd->cb; uint8_t req[] = { NET_REG_STATUS_GET_REQ, }; GIsiSubBlockIter iter; if (cbd == NULL || nd == NULL) goto error; if (!check_response_status(msg, NET_RAT_RESP)) goto error; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != NET_RAT_INFO) continue; if (!parse_rat_info(&iter, &nd->rat)) goto error; } if (g_isi_client_resource(nd->client) == PN_MODEM_NETWORK || nd->version.major < 14) req[0] = NET_MODEM_REG_STATUS_GET_REQ; if (g_isi_client_send(nd->client, req, sizeof(req), reg_status_resp_cb, cbd, NULL)) return; error: CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data); g_free(cbd); } static void isi_registration_status(struct ofono_netreg *netreg, ofono_netreg_status_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data); /* * Current technology depends on the current RAT as well as * the services reported by the current cell. Therefore we * need a pair of queries to deduce the full registration * status: first query for the RAT then the actual * registration status. */ const uint8_t rat[] = { NET_RAT_REQ, NET_CURRENT_RAT }; if (nd == NULL || cbd == NULL) goto error; if (g_isi_client_send(nd->client, rat, sizeof(rat), rat_resp_cb, cbd, NULL)) return; error: CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data); g_free(cbd); } static void cell_info_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_netreg_operator_cb_t cb = cbd->cb; struct ofono_netreg *netreg = cbd->user; struct netreg_data *nd = ofono_netreg_get_data(netreg); struct ofono_network_operator op; GIsiSubBlockIter iter; memset(&op, 0, sizeof(struct ofono_network_operator)); if (!check_response_status(msg, NET_CELL_INFO_GET_RESP)) goto error; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case NET_GSM_CELL_INFO: if (!g_isi_sb_iter_get_oper_code(&iter, op.mcc, op.mnc, 12)) goto error; op.tech = 0; break; case NET_WCDMA_CELL_INFO: if (!g_isi_sb_iter_get_oper_code(&iter, op.mcc, op.mnc, 12)) goto error; op.tech = 2; break; } } if (nd->nitz_name[0] != '\0') strcpy(op.name, nd->nitz_name); CALLBACK_WITH_SUCCESS(cb, &op, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void create_cell_info_get_req(struct ofono_netreg *netreg, ofono_netreg_operator_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data); const uint8_t msg[] = { NET_CELL_INFO_GET_REQ, }; if (cbd == NULL || nd == NULL) goto error; if (g_isi_client_send(nd->client, msg, sizeof(msg), cell_info_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, NULL, data); g_free(cbd); } static void name_get_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_netreg_operator_cb_t cb = cbd->cb; struct ofono_network_operator op; GIsiSubBlockIter iter; uint8_t len = 0; char *tag = NULL; memset(&op, 0, sizeof(struct ofono_network_operator)); if (!check_response_status(msg, NET_OLD_OPER_NAME_READ_RESP) && !check_response_status(msg, NET_OPER_NAME_READ_RESP)) goto error; for (g_isi_sb_iter_init(&iter, msg, 6); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case NET_GSM_OPERATOR_INFO: if (!g_isi_sb_iter_get_oper_code(&iter, op.mcc, op.mnc, 2)) goto error; break; case NET_OPER_NAME_INFO: if (!g_isi_sb_iter_get_byte(&iter, &len, 3)) goto error; /* Name is UCS-2 encoded */ len *= 2; if (!g_isi_sb_iter_get_alpha_tag(&iter, &tag, len, 4)) goto error; strncpy(op.name, tag, OFONO_MAX_OPERATOR_NAME_LENGTH); op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0'; g_free(tag); break; } } CALLBACK_WITH_SUCCESS(cb, &op, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void create_name_get_req(struct ofono_netreg *netreg, ofono_netreg_operator_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data); uint8_t msg[] = { NET_OPER_NAME_READ_REQ, NET_HARDCODED_LATIN_OPER_NAME, OFONO_MAX_OPERATOR_NAME_LENGTH, 0x00, 0x00, /* Index not used */ 0x00, /* Filler */ 0x00, /* No sub-blocks */ }; if (cbd == NULL || nd == NULL) goto error; if (nd->version.major < 14) msg[0] = NET_OLD_OPER_NAME_READ_REQ; if (g_isi_client_send(nd->client, msg, sizeof(msg), name_get_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, NULL, data); g_free(cbd); } static void isi_current_operator(struct ofono_netreg *netreg, ofono_netreg_operator_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); if (g_isi_client_resource(nd->client) == PN_MODEM_NETWORK) create_cell_info_get_req(netreg, cb, data); else create_name_get_req(netreg, cb, data); } static void available_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_netreg_operator_list_cb_t cb = cbd->cb; struct ofono_network_operator *list = NULL; GIsiSubBlockIter iter; uint8_t sb_count; int total = 0; int common = 0; int detail = 0; if (!check_response_status(msg, NET_MODEM_AVAILABLE_GET_RESP) && !check_response_status(msg, NET_AVAILABLE_GET_RESP)) goto error; if (!g_isi_msg_data_get_byte(msg, 1, &sb_count)) goto error; /* Each description of an operator has a pair of sub-blocks */ total = sb_count / 2; list = alloca(total * sizeof(struct ofono_network_operator)); for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { struct ofono_network_operator *op; char *tag = NULL; uint8_t taglen = 0; uint8_t status = 0; uint8_t umts = 0; switch (g_isi_sb_iter_get_id(&iter)) { case NET_MODEM_AVAIL_NETWORK_INFO_COMMON: if (!g_isi_sb_iter_get_byte(&iter, &status, 2)) goto error; op = list + common++; op->status = status; /* * FIXME: PN_MODEM_NETWORK provides no name * tags. We need access to the GSMA name list * here, or alternatively, core has to fill in * the blanks. */ op->name[0] = '\0'; break; case NET_AVAIL_NETWORK_INFO_COMMON: if (!g_isi_sb_iter_get_byte(&iter, &status, 2)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &taglen, 5)) goto error; if (!g_isi_sb_iter_get_alpha_tag(&iter, &tag, taglen * 2, 6)) goto error; op = list + common++; op->status = status; strncpy(op->name, tag, OFONO_MAX_OPERATOR_NAME_LENGTH); op->name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0'; g_free(tag); break; /* case NET_MODEM_DETAILED_NETWORK_INFO: */ case NET_DETAILED_NETWORK_INFO: op = list + detail++; if (!g_isi_sb_iter_get_oper_code(&iter, op->mcc, op->mnc, 2)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &umts, 7)) goto error; op->tech = umts ? 2 : 3; break; } } if (common == detail && detail == total) { CALLBACK_WITH_SUCCESS(cb, total, list, cbd->data); return; } error: CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data); } static void isi_list_operators(struct ofono_netreg *netreg, ofono_netreg_operator_list_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data); uint8_t msg[] = { NET_AVAILABLE_GET_REQ, NET_MANUAL_SEARCH, 0x01, /* Sub-block count */ NET_GSM_BAND_INFO, 0x04, /* Sub-block length */ NET_GSM_BAND_ALL_SUPPORTED_BANDS, 0x00 }; if (cbd == NULL || nd == NULL) goto error; if (g_isi_client_resource(nd->client) == PN_MODEM_NETWORK || nd->version.major < 14) msg[0] = NET_MODEM_AVAILABLE_GET_REQ; if (g_isi_client_send_with_timeout(nd->client, msg, sizeof(msg), NETWORK_SCAN_TIMEOUT, available_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, 0, NULL, data); g_free(cbd); } static void set_auto_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; struct netreg_data *nd = cbd->user; ofono_netreg_register_cb_t cb = cbd->cb; if (!check_response_status(msg, NET_SET_RESP)) { CALLBACK_WITH_FAILURE(cb, cbd->data); return; } nd->reg.mode = NET_SELECT_MODE_AUTOMATIC; CALLBACK_WITH_SUCCESS(cb, cbd->data); } static void isi_register_auto(struct ofono_netreg *netreg, ofono_netreg_register_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data); const unsigned char msg[] = { NET_SET_REQ, 0x00, /* Registered in another protocol? */ 0x01, /* Sub-block count */ NET_OPERATOR_INFO_COMMON, 0x04, /* Sub-block length */ nd->reg.mode == NET_SELECT_MODE_AUTOMATIC ? NET_SELECT_MODE_USER_RESELECTION : NET_SELECT_MODE_AUTOMATIC, 0x00 /* Index not used */ }; if (nd == NULL || cbd == NULL) goto error; if (g_isi_client_send_with_timeout(nd->client, msg, sizeof(msg), NETWORK_SET_TIMEOUT, set_auto_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void set_manual_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; struct ofono_netreg *netreg = cbd->user; struct netreg_data *nd = ofono_netreg_get_data(netreg); ofono_netreg_register_cb_t cb = cbd->cb; if (!check_response_status(msg, NET_SET_RESP)) { CALLBACK_WITH_FAILURE(cb, cbd->data); return; } nd->reg.mode = NET_SELECT_MODE_MANUAL; CALLBACK_WITH_SUCCESS(cb, cbd->data); } static void isi_register_manual(struct ofono_netreg *netreg, const char *mcc, const char *mnc, ofono_netreg_register_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data); guint8 buffer[3] = { 0 }; guint8 *bcd = mccmnc_to_bcd(mcc, mnc, buffer); const unsigned char msg[] = { NET_SET_REQ, 0x00, /* Registered in another protocol? */ 0x02, /* Sub-block count */ NET_OPERATOR_INFO_COMMON, 0x04, /* Sub-block length */ NET_SELECT_MODE_MANUAL, 0x00, /* Index not used */ NET_GSM_OPERATOR_INFO, 0x08, /* Sub-block length */ bcd[0], bcd[1], bcd[2], NET_GSM_BAND_INFO_NOT_AVAIL, /* Pick any supported band */ 0x00, 0x00 /* Filler */ }; if (cbd == NULL || nd == NULL) goto error; if (g_isi_client_send_with_timeout(nd->client, msg, sizeof(msg), NETWORK_SET_TIMEOUT, set_manual_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void rssi_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_netreg *netreg = data; uint8_t rssi; if (g_isi_msg_id(msg) != NET_RSSI_IND) return; if (!g_isi_msg_data_get_byte(msg, 0, &rssi)) return; ofono_netreg_strength_notify(netreg, rssi ? rssi : -1); } static gboolean parse_nettime(GIsiSubBlockIter *iter, struct ofono_network_time *info) { struct network_time *time; size_t len = sizeof(struct network_time); if (!g_isi_sb_iter_get_struct(iter, (void **) &time, len, 2)) return FALSE; /* Value is years since last turn of century */ info->year = time->year != NET_INVALID_TIME ? time->year : -1; info->year += 2000; info->mon = time->mon != NET_INVALID_TIME ? time->mon : -1; info->mday = time->mday != NET_INVALID_TIME ? time->mday : -1; info->hour = time->hour != NET_INVALID_TIME ? time->hour : -1; info->min = time->min != NET_INVALID_TIME ? time->min : -1; info->sec = time->sec != NET_INVALID_TIME ? time->sec : -1; /* * Most significant bit set indicates negative offset. The * second most significant bit is 'reserved'. The value is the * offset from UTCin a count of 15min intervals, possibly * including the current DST adjustment. */ info->utcoff = (time->utc & 0x3F) * 15 * 60; if (time->utc & 0x80) info->utcoff *= -1; info->dst = time->dst != NET_INVALID_TIME ? time->dst : -1; return TRUE; } static void time_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_netreg *netreg = data; struct ofono_network_time info; GIsiSubBlockIter iter; if (g_isi_msg_id(msg) != NET_TIME_IND) return; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != NET_TIME_INFO) continue; if (!parse_nettime(&iter, &info)) return; ofono_netreg_time_notify(netreg, &info); return; } } static void name_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_netreg *netreg = data; struct netreg_data *nd = ofono_netreg_get_data(netreg); GIsiSubBlockIter iter; char *tag; uint8_t taglen; if (g_isi_msg_id(msg) != NET_NITZ_NAME_IND) return; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { uint8_t id; id = g_isi_sb_iter_get_id(&iter); if (id != NET_FULL_NITZ_NAME && id != NET_SHORT_NITZ_NAME) continue; if (!g_isi_sb_iter_get_byte(&iter, &taglen, 5)) return; if (!g_isi_sb_iter_get_alpha_tag(&iter, &tag, taglen * 2, 7)) return; strncpy(nd->nitz_name, tag, OFONO_MAX_OPERATOR_NAME_LENGTH); nd->nitz_name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0'; g_free(tag); } } static void rssi_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_netreg_strength_cb_t cb = cbd->cb; uint8_t rssi; GIsiSubBlockIter iter; if (!check_response_status(msg, NET_RSSI_GET_RESP)) goto error; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != NET_RSSI_CURRENT) continue; if (!g_isi_sb_iter_get_byte(&iter, &rssi, 2)) break; CALLBACK_WITH_SUCCESS(cb, rssi ? rssi : -1, cbd->data); return; } error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void isi_strength(struct ofono_netreg *netreg, ofono_netreg_strength_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct isi_cb_data *cbd = isi_cb_data_new(netreg, cb, data); const uint8_t msg[] = { NET_RSSI_GET_REQ, NET_CS_GSM, NET_CURRENT_CELL_RSSI, 0, 0, 0, 0, }; size_t len = sizeof(msg); if (nd == NULL || cbd == NULL) goto error; /* Filler is only required by PN_MODEM_NETWORK */ if (g_isi_client_resource(nd->client) != PN_MODEM_NETWORK) len -= 4; if (g_isi_client_send(nd->client, msg, len, rssi_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } static void cs_access_config_resp_cb(const GIsiMessage *msg, void *data) { GIsiSubBlockIter iter; DBG(""); if (g_isi_msg_id(msg) != NET_NW_ACCESS_CONF_RESP) return; /* * TODO: Check that roaming and registration * are now enabled. */ for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { uint8_t id = g_isi_sb_iter_get_id(&iter); uint8_t mode; DBG("SB=%02X", id); switch (id) { case NET_REGISTRATION_CONF_INFO: case NET_REGISTRATION_CONF1_INFO: g_isi_sb_iter_get_byte(&iter, &mode, 2); DBG("Reg %X", mode); break; case NET_ROAMING_CONF_INFO: case NET_ROAMING_CONF1_INFO: g_isi_sb_iter_get_byte(&iter, &mode, 2); DBG("Roam %X", mode); break; default: DBG("Unknown subblock"); } } } static void enable_registration(struct ofono_netreg *netreg) { struct netreg_data *nd = ofono_netreg_get_data(netreg); const uint8_t req[] = { NET_NW_ACCESS_CONF_REQ, 0, 2, /* Subblock 1 */ 0x59, 4, 1, 0, /* Subblock 2 */ 0x5A, 4, 1, 0, }; DBG(""); g_isi_client_send(nd->client, req, sizeof(req), cs_access_config_resp_cb, netreg, NULL); } static void activate_cs_and_enable_registration(struct ofono_netreg *netreg) { DBG("not implemented"); } static void cs_state_resp_cb(const GIsiMessage *msg, void *data) { struct ofono_netreg *netreg = data; uint8_t code; DBG(""); if (g_isi_msg_id(msg) != NET_CS_STATE_RESP) return; if (!g_isi_msg_data_get_byte(msg, 0, &code)) return; if (code != NET_CAUSE_OK) { DBG("Failed with cause=%X", code); return; } if (!g_isi_msg_data_get_byte(msg, 1, &code)) return; DBG("CS STATE=%X", code); if (code == NET_CS_INACTIVE) activate_cs_and_enable_registration(netreg); else enable_registration(netreg); } static void subscribe_indications(GIsiClient *cl, void *data) { g_isi_client_ind_subscribe(cl, NET_RSSI_IND, rssi_ind_cb, data); g_isi_client_ind_subscribe(cl, NET_NITZ_NAME_IND, name_ind_cb, data); g_isi_client_ind_subscribe(cl, NET_RAT_IND, rat_ind_cb, data); g_isi_client_ind_subscribe(cl, NET_TIME_IND, time_ind_cb, data); g_isi_client_ind_subscribe(cl, NET_REG_STATUS_IND, reg_status_ind_cb, data); g_isi_client_ind_subscribe(cl, NET_MODEM_REG_STATUS_IND,reg_status_ind_cb, data); } static void pn_network_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_netreg *netreg = data; struct netreg_data *nd = ofono_netreg_get_data(netreg); if (g_isi_msg_error(msg) < 0) { DBG("PN_NETWORK not reachable, removing client"); g_isi_client_destroy(nd->pn_network); nd->pn_network = NULL; if (nd->pn_modem_network == NULL) ofono_netreg_remove(netreg); return; } ISI_RESOURCE_DBG(msg); if (nd == NULL || nd->client != NULL) return; nd->client = nd->pn_network; nd->version.major = g_isi_msg_version_major(msg); nd->version.minor = g_isi_msg_version_minor(msg); subscribe_indications(nd->client, netreg); ofono_netreg_register(netreg); } static void pn_modem_network_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_netreg *netreg = data; struct netreg_data *nd = ofono_netreg_get_data(netreg); const uint8_t req[] = { NET_CS_STATE_REQ, }; if (g_isi_msg_error(msg) < 0) { DBG("PN_MODEM_NETWORK not reachable, removing client"); g_isi_client_destroy(nd->pn_modem_network); nd->pn_modem_network = NULL; if (nd->pn_network == NULL) ofono_netreg_remove(netreg); return; } ISI_RESOURCE_DBG(msg); if (nd == NULL || nd->client != NULL) return; nd->client = nd->pn_modem_network; nd->version.major = g_isi_msg_version_major(msg); nd->version.minor = g_isi_msg_version_minor(msg); subscribe_indications(nd->client, netreg); ofono_netreg_register(netreg); g_isi_client_send(nd->client, req, sizeof(req), cs_state_resp_cb, netreg, NULL); } static int isi_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor, void *user) { GIsiModem *modem = user; struct netreg_data *nd; nd = g_try_new0(struct netreg_data, 1); if (nd == NULL) return -ENOMEM; nd->pn_network = g_isi_client_create(modem, PN_NETWORK); if (nd->pn_network == NULL) { g_free(nd); return -ENOMEM; } nd->pn_modem_network = g_isi_client_create(modem, PN_MODEM_NETWORK); if (nd->pn_modem_network == NULL) { g_isi_client_destroy(nd->pn_network); g_free(nd); return -ENOMEM; } ofono_netreg_set_data(netreg, nd); g_isi_client_verify(nd->pn_network, pn_network_reachable_cb, netreg, NULL); g_isi_client_verify(nd->pn_modem_network, pn_modem_network_reachable_cb, netreg, NULL); return 0; } static void isi_netreg_remove(struct ofono_netreg *netreg) { struct netreg_data *data = ofono_netreg_get_data(netreg); ofono_netreg_set_data(netreg, NULL); if (data == NULL) return; g_isi_client_destroy(data->pn_modem_network); g_isi_client_destroy(data->pn_network); g_free(data); } static struct ofono_netreg_driver isimodem = { .name = "isimodem", .probe = isi_netreg_probe, .remove = isi_netreg_remove, .registration_status = isi_registration_status, .current_operator = isi_current_operator, .list_operators = isi_list_operators, .register_auto = isi_register_auto, .register_manual = isi_register_manual, .strength = isi_strength, }; void isi_netreg_init(void) { ofono_netreg_driver_register(&isimodem); } void isi_netreg_exit(void) { ofono_netreg_driver_unregister(&isimodem); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/ussd.c0000644000015600001650000001435612671500024023016 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "smsutil.h" #include "util.h" #include "isimodem.h" #include "isiutil.h" #include "ss.h" #include "debug.h" struct ussd_info { uint8_t dcs; uint8_t type; uint8_t len; }; struct ussd_data { GIsiClient *client; GIsiVersion version; int mt_session; }; static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid) { if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", g_isi_msg_strerror(msg)); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", ss_message_id_name(g_isi_msg_id(msg))); return FALSE; } return TRUE; } static void ussd_notify_ack(struct ussd_data *ud) { const uint8_t msg[] = { SS_GSM_USSD_SEND_REQ, SS_GSM_USSD_NOTIFY, 0, /* subblock count */ }; g_isi_client_send(ud->client, msg, sizeof(msg), NULL, NULL, NULL); } static void ussd_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_ussd *ussd = data; struct ussd_data *ud = ofono_ussd_get_data(ussd); struct ussd_info *info; size_t len = sizeof(struct ussd_info); uint8_t *string; int status; if (g_isi_msg_id(msg) != SS_GSM_USSD_RECEIVE_IND) return; if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &info, len)) return; if (!g_isi_msg_data_get_struct(msg, len, (const void **) &string, info->len)) return; switch (info->type) { case 0: /* Nothing - this is response to NOTIFY_ACK REQ */ return; case SS_GSM_USSD_MT_REPLY: /* This never happens, but.. */ status = OFONO_USSD_STATUS_LOCAL_CLIENT_RESPONDED; break; case SS_GSM_USSD_COMMAND: /* Ignore, we get SS_GSM_USSD_REQUEST, too */ if (ud->mt_session) return; status = OFONO_USSD_STATUS_ACTION_REQUIRED; break; case SS_GSM_USSD_NOTIFY: status = OFONO_USSD_STATUS_NOTIFY; ussd_notify_ack(ud); break; case SS_GSM_USSD_END: status = OFONO_USSD_STATUS_TERMINATED; ud->mt_session = 0; break; case SS_GSM_USSD_REQUEST: ud->mt_session = 1; status = OFONO_USSD_STATUS_ACTION_REQUIRED; break; default: status = OFONO_USSD_STATUS_NOT_SUPPORTED; } DBG("type: %u %s, dcs: 0x%02x, len: %u", info->type, ss_ussd_type_name(info->type), info->dcs, info->len); ofono_ussd_notify(ussd, status, info->dcs, string, info->len); } static void ussd_send_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_ussd_cb_t cb = cbd->cb; if (check_response_status(msg, SS_GSM_USSD_SEND_RESP)) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); } static void isi_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 isi_cb_data *cbd = isi_cb_data_new(ussd, cb, data); size_t sb_len = ALIGN4(4 + len); size_t pad_len = sb_len - (4 + len); const uint8_t padding[4] = { 0 }; const uint8_t msg[] = { SS_GSM_USSD_SEND_REQ, ud->mt_session ? SS_GSM_USSD_MT_REPLY : SS_GSM_USSD_COMMAND, 1, /* subblock count */ SS_GSM_USSD_STRING, sb_len, dcs, /* DCS */ len, /* string length */ /* USSD string goes here */ }; struct iovec iov[3] = { { (uint8_t *) msg, sizeof(msg) }, { (uint8_t *) pdu, len }, { (uint8_t *) padding, pad_len }, }; if (cbd == NULL || ud == NULL) goto error; if (g_isi_client_vsend(ud->client, iov, 3, ussd_send_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void isi_cancel(struct ofono_ussd *ussd, ofono_ussd_cb_t cb, void *data) { struct ussd_data *ud = ofono_ussd_get_data(ussd); struct isi_cb_data *cbd = isi_cb_data_new(ussd, cb, data); const uint8_t msg[] = { SS_GSM_USSD_SEND_REQ, SS_GSM_USSD_END, 0, /* subblock count */ }; if (cbd == NULL || ud == NULL) goto error; if (g_isi_client_send(ud->client, msg, sizeof(msg), ussd_send_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void ussd_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_ussd *ussd = data; struct ussd_data *ud = ofono_ussd_get_data(ussd); if (g_isi_msg_error(msg) < 0) { ofono_ussd_remove(ussd); return; } ISI_RESOURCE_DBG(msg); g_isi_client_ind_subscribe(ud->client, SS_GSM_USSD_RECEIVE_IND, ussd_ind_cb, ussd); ofono_ussd_register(ussd); } static int isi_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor, void *user) { GIsiModem *modem = user; struct ussd_data *ud; ud = g_try_new0(struct ussd_data, 1); if (ud == NULL) return -ENOMEM; ud->client = g_isi_client_create(modem, PN_SS); if (ud->client == NULL) { g_free(ud); return -ENOMEM; } ofono_ussd_set_data(ussd, ud); g_isi_client_verify(ud->client, ussd_reachable_cb, ussd, NULL); return 0; } static void isi_ussd_remove(struct ofono_ussd *ussd) { struct ussd_data *data = ofono_ussd_get_data(ussd); ofono_ussd_set_data(ussd, NULL); if (data == NULL) return; g_isi_client_destroy(data->client); g_free(data); } static struct ofono_ussd_driver driver = { .name = "isimodem", .probe = isi_ussd_probe, .remove = isi_ussd_remove, .request = isi_request, .cancel = isi_cancel }; void isi_ussd_init(void) { ofono_ussd_driver_register(&driver); } void isi_ussd_exit(void) { ofono_ussd_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/call-settings.c0000644000015600001650000002202212671500024024576 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "isimodem.h" #include "isiutil.h" #include "ss.h" #include "debug.h" struct settings_data { GIsiClient *client; }; static void update_status_mask(uint32_t *mask, uint8_t bsc) { switch (bsc) { case SS_GSM_TELEPHONY: *mask |= 1; break; case SS_GSM_ALL_DATA_TELE: *mask |= 1 << 1; break; case SS_GSM_FACSIMILE: *mask |= 1 << 2; break; case SS_GSM_SMS: *mask |= 1 << 3; break; case SS_GSM_ALL_DATA_CIRCUIT_SYNC: *mask |= 1 << 4; break; case SS_GSM_ALL_DATA_CIRCUIT_ASYNC: *mask |= 1 << 5; break; case SS_GSM_ALL_DATA_PACKET_SYNC: *mask |= 1 << 6; break; case SS_GSM_ALL_PAD_ACCESS: *mask |= 1 << 7; break; default: DBG("Unknown BSC value %d, please report", bsc); break; } } static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid, uint8_t type) { uint8_t service; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", g_isi_msg_strerror(msg)); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", ss_message_id_name(g_isi_msg_id(msg))); return FALSE; } if (!g_isi_msg_data_get_byte(msg, 0, &service) || service != type) { DBG("Unexpected service type: 0x%02X", service); return FALSE; } return TRUE; } static gboolean decode_gsm_bsc_info(GIsiSubBlockIter *iter, uint32_t *mask) { uint8_t *bsc; uint8_t num, i; if (!g_isi_sb_iter_get_byte(iter, &num, 2)) return FALSE; if (!g_isi_sb_iter_get_struct(iter, (void **) &bsc, num, 3)) return FALSE; for (i = 0; i < num; i++) update_status_mask(mask, bsc[i]); return TRUE; } static void status_query_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_call_settings_status_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint32_t mask = 0; uint8_t status; if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, SS_INTERROGATION)) goto error; for (g_isi_sb_iter_init(&iter, msg, 6); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case SS_STATUS_RESULT: if (!g_isi_sb_iter_get_byte(&iter, &status, 2)) goto error; if (status & SS_GSM_PROVISIONED) mask = 1; break; case SS_GSM_ADDITIONAL_INFO: break; case SS_GSM_BSC_INFO: if (!decode_gsm_bsc_info(&iter, &mask)) goto error; break; } } CALLBACK_WITH_SUCCESS(cb, mask, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, 0, cbd->data); } static void isi_clip_query(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data) { struct settings_data *sd = ofono_call_settings_get_data(cs); struct isi_cb_data *cbd = isi_cb_data_new(cs, cb, data); const uint8_t msg[] = { SS_SERVICE_REQ, SS_INTERROGATION, SS_ALL_TELE_AND_BEARER, SS_GSM_CLIP >> 8, SS_GSM_CLIP & 0xFF, SS_SEND_ADDITIONAL_INFO, 0, }; if (sd == NULL || cbd == NULL) goto error; if (g_isi_client_send(sd->client, msg, sizeof(msg), status_query_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, 0, data); g_free(cbd); } static void isi_colp_query(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data) { struct settings_data *sd = ofono_call_settings_get_data(cs); struct isi_cb_data *cbd = isi_cb_data_new(cs, cb, data); const uint8_t msg[] = { SS_SERVICE_REQ, SS_INTERROGATION, SS_ALL_TELE_AND_BEARER, SS_GSM_COLP >> 8, SS_GSM_COLP & 0xFF, SS_SEND_ADDITIONAL_INFO, 0, }; if (sd == NULL || cbd == NULL) goto error; if (g_isi_client_send(sd->client, msg, sizeof(msg), status_query_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, 0, data); g_free(cbd); } static void isi_colr_query(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data) { struct settings_data *sd = ofono_call_settings_get_data(cs); struct isi_cb_data *cbd = isi_cb_data_new(cs, cb, data); const uint8_t msg[] = { SS_SERVICE_REQ, SS_INTERROGATION, SS_ALL_TELE_AND_BEARER, SS_GSM_COLR >> 8, SS_GSM_COLR & 0xFF, SS_SEND_ADDITIONAL_INFO, 0, }; if (sd == NULL || cbd == NULL) goto error; if (g_isi_client_send(sd->client, msg, sizeof(msg), status_query_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, 0, data); g_free(cbd); } static void isi_cw_query(struct ofono_call_settings *cs, int cls, ofono_call_settings_status_cb_t cb, void *data) { struct settings_data *sd = ofono_call_settings_get_data(cs); struct isi_cb_data *cbd = isi_cb_data_new(cs, cb, data); const uint8_t msg[] = { SS_SERVICE_REQ, SS_INTERROGATION, SS_ALL_TELE_AND_BEARER, SS_GSM_CALL_WAITING >> 8, SS_GSM_CALL_WAITING & 0xFF, SS_SEND_ADDITIONAL_INFO, 0, }; if (sd == NULL || cbd == NULL) goto error; if (g_isi_client_send(sd->client, msg, sizeof(msg), status_query_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, 0, data); g_free(cbd); } static gboolean check_cw_resp(const GIsiMessage *msg, uint8_t type) { GIsiSubBlockIter iter; uint8_t status; if (!check_resp(msg, SS_SERVICE_COMPLETED_RESP, type)) return FALSE; for (g_isi_sb_iter_init(&iter, msg, 6); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != SS_GSM_DATA) continue; if (!g_isi_sb_iter_get_byte(&iter, &status, 2)) return FALSE; if (status & SS_GSM_ACTIVE) return type == SS_ACTIVATION; else return type == SS_DEACTIVATION; } return FALSE; } static void cw_set_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_call_settings_set_cb_t cb = cbd->cb; if (check_cw_resp(msg, SS_ACTIVATION)) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); } static void cw_unset_resp_cb(const GIsiMessage *msg, void *data) { struct isi_cb_data *cbd = data; ofono_call_settings_set_cb_t cb = cbd->cb; if (check_cw_resp(msg, SS_DEACTIVATION)) CALLBACK_WITH_SUCCESS(cb, cbd->data); else CALLBACK_WITH_FAILURE(cb, cbd->data); } static void isi_cw_set(struct ofono_call_settings *cs, int mode, int cls, ofono_call_settings_set_cb_t cb, void *data) { struct settings_data *sd = ofono_call_settings_get_data(cs); struct isi_cb_data *cbd = isi_cb_data_new(cs, cb, data); const uint8_t msg[] = { SS_SERVICE_REQ, mode ? SS_ACTIVATION : SS_DEACTIVATION, SS_ALL_TELE_AND_BEARER, SS_GSM_CALL_WAITING >> 8, SS_GSM_CALL_WAITING & 0xFF, SS_SEND_ADDITIONAL_INFO, 0, /* Subblock count */ }; if (cbd == NULL || sd == NULL) goto error; if (g_isi_client_send(sd->client, msg, sizeof(msg), mode ? cw_set_resp_cb : cw_unset_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_call_settings *cs = data; if (g_isi_msg_error(msg) < 0) { ofono_call_settings_remove(cs); return; } ISI_RESOURCE_DBG(msg); ofono_call_settings_register(cs); } static int isi_call_settings_probe(struct ofono_call_settings *cs, unsigned int vendor, void *user) { GIsiModem *modem = user; struct settings_data *sd; sd = g_try_new0(struct settings_data, 1); if (sd == NULL) return -ENOMEM; sd->client = g_isi_client_create(modem, PN_SS); if (sd->client == NULL) { g_free(sd); return -ENOMEM; } ofono_call_settings_set_data(cs, sd); g_isi_client_verify(sd->client, reachable_cb, cs, NULL); return 0; } static void isi_call_settings_remove(struct ofono_call_settings *cs) { struct settings_data *data = ofono_call_settings_get_data(cs); ofono_call_settings_set_data(cs, NULL); if (data == NULL) return; g_isi_client_destroy(data->client); g_free(data); } static struct ofono_call_settings_driver driver = { .name = "isimodem", .probe = isi_call_settings_probe, .remove = isi_call_settings_remove, .clip_query = isi_clip_query, .colp_query = isi_colp_query, .colr_query = isi_colr_query, .clir_query = NULL, .clir_set = NULL, .cw_query = isi_cw_query, .cw_set = isi_cw_set }; void isi_call_settings_init(void) { ofono_call_settings_driver_register(&driver); } void isi_call_settings_exit(void) { ofono_call_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/debug.c0000644000015600001650000011455712671500024023132 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include "debug.h" #define COMMON_MESSAGE 0xF0 #define _(X) case X: return #X const char *pn_resource_name(int value) { switch (value) { _(PN_NETWORK); _(PN_MODEM_NETWORK); _(PN_PHONE_INFO); _(PN_MODEM_INFO); _(PN_EPOC_INFO); _(PN_SS); _(PN_CALL); _(PN_MODEM_CALL); _(PN_SMS); _(PN_SIM); _(PN_SECURITY); _(PN_MTC); _(PN_MODEM_MCE); _(PN_GSS); _(PN_GPDS); _(PN_WRAN); _(PN_UICC); } return "PN_"; } const char *ss_message_id_name(enum ss_message_id value) { switch (value) { _(SS_SERVICE_REQ); _(SS_SERVICE_COMPLETED_RESP); _(SS_SERVICE_FAILED_RESP); _(SS_SERVICE_NOT_SUPPORTED_RESP); _(SS_GSM_USSD_SEND_REQ); _(SS_GSM_USSD_SEND_RESP); _(SS_GSM_USSD_RECEIVE_IND); _(SS_STATUS_IND); _(SS_SERVICE_COMPLETED_IND); } return "SS_"; } const char *ss_ussd_type_name(enum ss_ussd_type value) { switch (value) { _(SS_GSM_USSD_MT_REPLY); _(SS_GSM_USSD_COMMAND); _(SS_GSM_USSD_REQUEST); _(SS_GSM_USSD_NOTIFY); _(SS_GSM_USSD_END); } return "SS_"; } const char *ss_subblock_name(enum ss_subblock value) { switch (value) { _(SS_FORWARDING); _(SS_STATUS_RESULT); _(SS_GSM_PASSWORD); _(SS_GSM_FORWARDING_INFO); _(SS_GSM_FORWARDING_FEATURE); _(SS_GSM_DATA); _(SS_GSM_BSC_INFO); _(SS_GSM_GENERIC_SERVICE_INFO); _(SS_GSM_CLIR_INFO); _(SS_GSM_PASSWORD_INFO); _(SS_GSM_INDICATE_PASSWORD_ERROR); _(SS_GSM_INDICATE_ERROR); _(SS_GSM_ADDITIONAL_INFO); _(SS_GSM_BARRING_INFO); _(SS_GSM_BARRING_FEATURE); _(SS_GSM_USSD_STRING); } return "SS_"; } const char *mtc_isi_cause_name(enum mtc_isi_cause value) { switch (value) { _(MTC_OK); _(MTC_FAIL); _(MTC_NOT_ALLOWED); _(MTC_STATE_TRANSITION_GOING_ON); _(MTC_ALREADY_ACTIVE); _(MTC_SERVICE_DISABLED); _(MTC_NOT_READY_YET); _(MTC_NOT_SUPPORTED); _(MTC_TRANSITION_ONGOING); _(MTC_RESET_REQUIRED); } return "MTC_"; } const char *mtc_message_id_name(enum mtc_message_id value) { switch (value) { _(MTC_STATE_REQ); _(MTC_STATE_QUERY_REQ); _(MTC_POWER_OFF_REQ); _(MTC_POWER_ON_REQ); _(MTC_STARTUP_SYNQ_REQ); _(MTC_SHUTDOWN_SYNC_REQ); _(MTC_STATE_RESP); _(MTC_STATE_QUERY_RESP); _(MTC_POWER_OFF_RESP); _(MTC_POWER_ON_RESP); _(MTC_STARTUP_SYNQ_RESP); _(MTC_SHUTDOWN_SYNC_RESP); _(MTC_STATE_INFO_IND); } return "MTC_"; } const char *mtc_modem_state_name(enum mtc_modem_state value) { switch (value) { _(MTC_POWER_OFF); _(MTC_NORMAL); _(MTC_CHARGING); _(MTC_ALARM); _(MTC_TEST); _(MTC_LOCAL); _(MTC_WARRANTY); _(MTC_RELIABILITY); _(MTC_SELFTEST_FAIL); _(MTC_SWDL); _(MTC_RF_INACTIVE); _(MTC_ID_WRITE); _(MTC_DISCHARGING); _(MTC_DISK_WIPE); _(MTC_SW_RESET); _(MTC_CMT_ONLY_MODE); _(MTC_STATE_NONE); } return "MTC_"; } const char *mce_message_id_name(enum mce_message_id value) { switch (value) { _(MCE_MODEM_STATE_IND); _(MCE_MODEM_STATE_QUERY_REQ); _(MCE_MODEM_STATE_QUERY_RESP); _(MCE_RF_STATE_REQ); _(MCE_RF_STATE_RESP); _(MCE_RF_STATE_IND); _(MCE_RF_STATE_QUERY_REQ); _(MCE_RF_STATE_QUERY_RESP); _(MCE_POWER_OFF_REQ); _(MCE_POWER_OFF_RESP); } return "MCE_"; } const char *mce_modem_state_name(enum mce_modem_state value) { switch (value) { _(MCE_NORMAL); _(MCE_LOCAL); _(MCE_SW_RESET); _(MCE_POWER_OFF); } return "MCE_"; } const char *mce_status_info(enum mce_status_info value) { switch (value) { _(MCE_OK); _(MCE_FAIL); _(MCE_ALREADY_ACTIVE); _(MCE_TRANSITION_ONGOING); } return "MCE_"; } const char *mce_rf_state_name(enum mce_rf_state value) { switch (value) { _(MCE_RF_OFF); _(MCE_RF_ON); } return "MCE_RF"; } const char *uicc_service_type_name(uint8_t value) { switch (value) { _(UICC_APPL_LIST); _(UICC_APPL_HOST_ACTIVATE); /*_(UICC_APPL_DEACTIVATE);*/ _(UICC_APPL_START_UP_COMPLETE); /*_(UICC_SHUT_DOWN_INITIATED);*/ _(UICC_APPL_SHUT_DOWN_INITIATED); _(UICC_APPL_STATUS_GET); _(UICC_APPL_HOST_DEACTIVATE); _(UICC_PIN_VERIFY); _(UICC_PIN_UNBLOCK); _(UICC_PIN_DISABLE); _(UICC_PIN_ENABLE); _(UICC_PIN_CHANGE); _(UICC_PIN_SUBSTITUTE); _(UICC_PIN_INFO); _(UICC_PIN_PROMPT_VERIFY); _(UICC_APPL_READ_TRANSPARENT); _(UICC_APPL_UPDATE_TRANSPARENT); _(UICC_APPL_READ_LINEAR_FIXED); _(UICC_APPL_UPDATE_LINEAR_FIXED); _(UICC_APPL_FILE_INFO); _(UICC_APPL_APDU_SEND); _(UICC_APPL_CLEAR_CACHE); _(UICC_APPL_SESSION_START); _(UICC_APPL_SESSION_END); _(UICC_APPL_READ_CYCLIC); _(UICC_APPL_UPDATE_CYCLIC); /*_(UICC_APPL_CACHE_UPDATED);*/ _(UICC_CONNECT); _(UICC_DISCONNECT); _(UICC_RECONNECT); _(UICC_CAT_ENABLE); _(UICC_CAT_DISABLE); _(UICC_CAT_TERMINAL_PROFILE); _(UICC_CAT_TERMINAL_RESPONSE); _(UICC_CAT_ENVELOPE); _(UICC_CAT_POLLING_SET); _(UICC_CAT_REFRESH); _(UICC_CAT_POLL); _(UICC_APDU_SEND); _(UICC_APDU_ATR_GET); _(UICC_APDU_CONTROL); _(UICC_REFRESH_STATUS); _(UICC_APPL_TERMINATED); _(UICC_APPL_RECOVERED); /*_(UICC_APPL_UNAVAILABLE);*/ /*_(UICC_APPL_SHUT_DOWN);*/ _(UICC_APPL_ACTIVATED); _(UICC_PIN_VERIFY_NEEDED); _(UICC_PIN_UNBLOCK_NEEDED); _(UICC_PIN_PERMANENTLY_BLOCKED); _(UICC_PIN_VERIFIED); _(UICC_CAT_FETCHED_CMD); _(UICC_CAT_NOT_SUPPORTED); _(UICC_CAT_REG_FAILED); _(UICC_CAT_REG_OK); _(UICC_REFRESH_PERMISSION); _(UICC_REFRESH_STARTING); _(UICC_REFRESH_CANCELLED); _(UICC_REFRESH_NOW); _(UICC_START_UP_COMPLETE); _(UICC_STATUS_GET); _(UICC_READY); /*_(UICC_READY_FOR_ACTIVATION);*/ _(UICC_INITIALIZED); _(UICC_SHUTTING_DOWN); /*_(UICC_SHUT_DOWN_CONFIG);*/ _(UICC_ERROR); _(UICC_CARD_DISCONNECTED); _(UICC_CARD_REMOVED); _(UICC_CARD_NOT_PRESENT); /*_(UICC_CARD_RESET);*/ _(UICC_CARD_READY); _(UICC_CARD_STATUS_GET); _(UICC_CARD_REJECTED); _(UICC_CARD_INFO_GET); _(UICC_SIMLOCK_ACTIVE); _(UICC_APDU_SAP_ACTIVATE); _(UICC_APDU_SAP_DEACTIVATE); _(UICC_APDU_SAP_ATR_GET); _(UICC_APDU_SAP_COLD_RESET); _(UICC_APDU_SAP_WARM_RESET); _(UICC_APDU_SAP_APDU_SEND); _(UICC_APDU_SAP_RECOVERY); _(UICC_APDU_SAP_CONFIG_GET); _(UICC_PWR_CTRL_ENABLE); _(UICC_PWR_CTRL_DISABLE); _(UICC_PWR_CTRL_WAIT); _(UICC_PWR_CTRL_PROCEED); _(UICC_PWR_CTRL_PERMISSION); } return "UICC_SERVICE_"; } const char *uicc_details_name(uint8_t value) { switch (value) { /* Used when status differs from UICC_STATUS_FAIL */ _(UICC_NO_DETAILS); /* Request was sent with one or more invalid parameter */ _(UICC_INVALID_PARAMETERS); /* The file wasn't found */ _(UICC_FILE_NOT_FOUND); /* User does not have the required priviledges for this */ _(UICC_SECURITY_CONDITIONS_NOT_SATISFIED); /* Application can not be activated due to already active app */ _(UICC_APPL_CONFLICT); /* Card Communication error */ _(UICC_CARD_ERROR); /* Operation not supported */ _(UICC_SERVICE_NOT_SUPPORTED); /* Session expired */ _(UICC_SESSION_EXPIRED); } return "UICC_STATUS"; } const char *uicc_message_id_name(enum uicc_message_id value) { switch (value) { _(UICC_REQ); _(UICC_RESP); _(UICC_IND); _(UICC_CARD_REQ); _(UICC_CARD_RESP); _(UICC_CARD_IND); _(UICC_APPLICATION_REQ); _(UICC_APPLICATION_RESP); _(UICC_APPLICATION_IND); _(UICC_PIN_REQ); _(UICC_PIN_RESP); _(UICC_PIN_IND); _(UICC_APPL_CMD_REQ); _(UICC_APPL_CMD_RESP); _(UICC_APPL_CMD_IND); _(UICC_CONNECTOR_REQ); _(UICC_CONNECTOR_RESP); _(UICC_CAT_REQ); _(UICC_CAT_RESP); _(UICC_CAT_IND); _(UICC_APDU_REQ); _(UICC_APDU_RESP); _(UICC_APDU_RESET_IND); _(UICC_REFRESH_REQ); _(UICC_REFRESH_RESP); _(UICC_REFRESH_IND); _(UICC_SIMLOCK_REQ); _(UICC_SIMLOCK_RESP); _(UICC_APDU_SAP_REQ); _(UICC_APDU_SAP_RESP); _(UICC_APDU_SAP_IND); _(UICC_PWR_CTRL_REQ); _(UICC_PWR_CTRL_RESP); _(UICC_PWR_CTRL_IND); _(UICC_CARD_READER_IND); } return "UICC_"; } const char *uicc_status_name(uint8_t value) { switch (value) { /* Request performed successfully */ _(UICC_STATUS_OK); /* Error in performing the command */ _(UICC_STATUS_FAIL); /* Status is Unknown */ _(UICC_STATUS_UNKNOWN); /* Server is not ready */ _(UICC_STATUS_NOT_READY); /* Server start up is completed */ _(UICC_STATUS_START_UP_COMPLETED); /* Server is shutting down */ _(UICC_STATUS_SHUTTING_DOWN); /* Smart card is not ready */ _(UICC_STATUS_CARD_NOT_READY); /* Smart card is ready */ _(UICC_STATUS_CARD_READY); /* Smart card is disconnected */ _(UICC_STATUS_CARD_DISCONNECTED); /* Smart card is not present */ _(UICC_STATUS_CARD_NOT_PRESENT); /* Smart card has been rejected */ _(UICC_STATUS_CARD_REJECTED); /* Application is active */ _(UICC_STATUS_APPL_ACTIVE); /* Application is not active */ _(UICC_STATUS_APPL_NOT_ACTIVE); /* PIN verification used */ _(UICC_STATUS_PIN_ENABLED); /* PIN verification not used */ _(UICC_STATUS_PIN_DISABLED); } return "UICC_STATUS"; } const char *uicc_subblock_name(uint8_t value) { switch (value) { _(UICC_SB_SHUT_DOWN_CONFIG); _(UICC_SB_CARD_STATUS); _(UICC_SB_CARD_INFO); _(UICC_SB_CARD_REJECT_CAUSE); _(UICC_SB_CLIENT); _(UICC_SB_APPL_DATA_OBJECT); _(UICC_SB_APPLICATION); _(UICC_SB_APPL_INFO); _(UICC_SB_APPL_STATUS); _(UICC_SB_FCP); _(UICC_SB_FCI); _(UICC_SB_CHV); _(UICC_SB_PIN); _(UICC_SB_PIN_REF); _(UICC_SB_PUK); _(UICC_SB_PIN_SUBST); _(UICC_SB_PIN_INFO); _(UICC_SB_APPL_PATH); _(UICC_SB_SESSION); _(UICC_SB_FILE_DATA); _(UICC_SB_APDU); _(UICC_SB_TRANSPARENT_READ); _(UICC_SB_TRANSPARENT_UPDATE); _(UICC_SB_TRANSPARENT); _(UICC_SB_LINEAR_FIXED); _(UICC_SB_CYCLIC); _(UICC_SB_TERMINAL_PROFILE); _(UICC_SB_TERMINAL_RESPONSE); _(UICC_SB_ENVELOPE); _(UICC_SB_POLLING_SET); _(UICC_SB_REFRESH); _(UICC_SB_AID); _(UICC_SB_REFRESH_RESULT); _(UICC_SB_APDU_ACTIONS); _(UICC_SB_OBJECT_ID); _(UICC_SB_STATUS_WORD); _(UICC_SB_APDU_SAP_INFO); _(UICC_SB_ACCESS_MODE); _(UICC_SB_RESP_INFO); _(UICC_SB_APDU_SAP_CONFIG); } return "UICC_"; } const char *sms_isi_cause_name(enum sms_isi_cause value) { switch (value) { _(SMS_OK); _(SMS_ERR_ROUTING_RELEASED); _(SMS_ERR_INVALID_PARAMETER); _(SMS_ERR_DEVICE_FAILURE); _(SMS_ERR_PP_RESERVED); _(SMS_ERR_ROUTE_NOT_AVAILABLE); _(SMS_ERR_ROUTE_NOT_ALLOWED); _(SMS_ERR_SERVICE_RESERVED); _(SMS_ERR_INVALID_LOCATION); _(SMS_ERR_NO_SIM); _(SMS_ERR_SIM_NOT_READY); _(SMS_ERR_NO_NETW_RESPONSE); _(SMS_ERR_DEST_ADDR_FDN_RESTRICTED); _(SMS_ERR_SMSC_ADDR_FDN_RESTRICTED); _(SMS_ERR_RESEND_ALREADY_DONE); _(SMS_ERR_SMSC_ADDR_NOT_AVAILABLE); _(SMS_ERR_ROUTING_FAILED); _(SMS_ERR_CS_INACTIVE); _(SMS_ERR_SAT_MO_CONTROL_MODIFIED); _(SMS_ERR_SAT_MO_CONTROL_REJECT); _(SMS_ERR_TRACFONE_FAILED); } return "SMS_"; } const char *sms_gsm_cause_name(enum sms_gsm_cause value) { switch (value) { _(SMS_GSM_ERR_UNASSIGNED_NUMBER); _(SMS_GSM_ERR_OPER_DETERMINED_BARR); _(SMS_GSM_ERR_CALL_BARRED); _(SMS_GSM_ERR_RESERVED); _(SMS_GSM_ERR_MSG_TRANSFER_REJ); _(SMS_GSM_ERR_MEMORY_CAPACITY_EXC); _(SMS_GSM_ERR_DEST_OUT_OF_ORDER); _(SMS_GSM_ERR_UNDEFINED_SUBSCRIBER); _(SMS_GSM_ERR_FACILITY_REJECTED); _(SMS_GSM_ERR_UNKNOWN_SUBSCRIBER); _(SMS_GSM_ERR_NETW_OUT_OF_ORDER); _(SMS_GSM_ERR_TEMPORARY_FAILURE); _(SMS_GSM_ERR_CONGESTION); _(SMS_GSM_ERR_RESOURCE_UNAVAILABLE); _(SMS_GSM_ERR_REQ_FACILITY_NOT_SUB); _(SMS_GSM_ERR_REQ_FACILITY_NOT_IMP); _(SMS_GSM_ERR_INVALID_REFERENCE); _(SMS_GSM_ERR_INCORRECT_MESSAGE); _(SMS_GSM_ERR_INVALID_MAND_INFO); _(SMS_GSM_ERR_INVALID_MSG_TYPE); _(SMS_GSM_ERR_MSG_NOT_COMP_WITH_ST); _(SMS_GSM_ERR_INVALID_INFO_ELEMENT); _(SMS_GSM_ERR_PROTOCOL_ERROR); _(SMS_GSM_ERR_INTERWORKING); _(SMS_GSM_ERR_NO_CAUSE); _(SMS_GSM_ERR_IMSI_UNKNOWN_HLR); _(SMS_GSM_ERR_ILLEGAL_MS); _(SMS_GSM_ERR_IMSI_UNKNOWN_VLR); _(SMS_GSM_ERR_IMEI_NOT_ACCEPTED); _(SMS_GSM_ERR_ILLEGAL_ME); _(SMS_GSM_ERR_PLMN_NOT_ALLOWED); _(SMS_GSM_ERR_LA_NOT_ALLOWED); _(SMS_GSM_ERR_ROAM_NOT_ALLOWED_LA); _(SMS_GSM_ERR_NO_SUITABLE_CELLS_LA); _(SMS_GSM_ERR_NETWORK_FAILURE); _(SMS_GSM_ERR_MAC_FAILURE); _(SMS_GSM_ERR_SYNC_FAILURE); _(SMS_GSM_ERR_LOW_LAYER_CONGESTION); _(SMS_GSM_ERR_AUTH_UNACCEPTABLE); _(SMS_GSM_ERR_SERV_OPT_NOT_SUPPORTED); _(SMS_GSM_ERR_SERV_OPT_NOT_SUBSCRIBED); _(SMS_GSM_ERR_SERV_OPT_TEMP_OUT_OF_ORDER); _(SMS_GSM_ERR_CALL_CANNOT_BE_IDENTIFIED); _(SMS_GSM_ERR_SEMANTICALLY_INCORR_MSG); _(SMS_GSM_ERR_LOW_LAYER_INVALID_MAND_INFO); _(SMS_GSM_ERR_LOW_LAYER_INVALID_MSG_TYPE); _(SMS_GSM_ERR_LOW_LAYER_MSG_TYPE_NOT_COMP_WITH_ST); _(SMS_GSM_ERR_LOW_LAYER_INVALID_INFO_ELEMENT); _(SMS_GSM_ERR_CONDITIONAL_IE_ERROR); _(SMS_GSM_ERR_LOW_LAYER_MSG_NOT_COMP_WITH_ST); _(SMS_GSM_ERR_CS_BARRED); _(SMS_GSM_ERR_LOW_LAYER_PROTOCOL_ERROR); } return "SMS_"; } const char *sms_message_id_name(enum sms_message_id value) { switch (value) { _(SMS_MESSAGE_SEND_REQ); _(SMS_MESSAGE_SEND_RESP); _(SMS_PP_ROUTING_REQ); _(SMS_PP_ROUTING_RESP); _(SMS_PP_ROUTING_NTF); _(SMS_GSM_RECEIVED_PP_REPORT_REQ); _(SMS_GSM_RECEIVED_PP_REPORT_RESP); _(SMS_GSM_CB_ROUTING_REQ); _(SMS_GSM_CB_ROUTING_RESP); _(SMS_GSM_CB_ROUTING_NTF); _(SMS_MESSAGE_SEND_STATUS_IND); _(SMS_SETTINGS_UPDATE_REQ); _(SMS_SETTINGS_UPDATE_RESP); _(SMS_SETTINGS_READ_REQ); _(SMS_SETTINGS_READ_RESP); _(SMS_RECEIVED_MSG_REPORT_REQ); _(SMS_RECEIVED_MSG_REPORT_RESP); _(SMS_RECEIVE_MESSAGE_REQ); _(SMS_RECEIVE_MESSAGE_RESP); _(SMS_RECEIVED_MSG_IND); } return "SMS_"; } const char *sms_subblock_name(enum sms_subblock value) { switch (value) { _(SMS_GSM_DELIVER); _(SMS_GSM_STATUS_REPORT); _(SMS_GSM_SUBMIT); _(SMS_GSM_COMMAND); _(SMS_GSM_DELIVER_REPORT); _(SMS_GSM_REPORT); _(SMS_GSM_ROUTING); _(SMS_GSM_CB_MESSAGE); _(SMS_GSM_TPDU); _(SMS_SB_TPDU); _(SMS_SB_ROUTE_INFO); _(SMS_SB_SMS_PARAMETERS); _(SMS_COMMON_DATA); _(SMS_ADDRESS); /* _(SMS_SB_ADDRESS); */ } return "SMS_"; } const char *sim_isi_cause_name(enum sim_isi_cause value) { switch (value) { _(SIM_SERV_NOT_AVAIL); _(SIM_SERV_OK); _(SIM_SERV_PIN_VERIFY_REQUIRED); _(SIM_SERV_PIN_REQUIRED); _(SIM_SERV_SIM_BLOCKED); _(SIM_SERV_SIM_PERMANENTLY_BLOCKED); _(SIM_SERV_SIM_DISCONNECTED); _(SIM_SERV_SIM_REJECTED); _(SIM_SERV_LOCK_ACTIVE); _(SIM_SERV_AUTOLOCK_CLOSED); _(SIM_SERV_AUTOLOCK_ERROR); _(SIM_SERV_INIT_OK); _(SIM_SERV_INIT_NOT_OK); _(SIM_SERV_WRONG_OLD_PIN); _(SIM_SERV_PIN_DISABLED); _(SIM_SERV_COMMUNICATION_ERROR); _(SIM_SERV_UPDATE_IMPOSSIBLE); _(SIM_SERV_NO_SECRET_CODE_IN_SIM); _(SIM_SERV_PIN_ENABLE_OK); _(SIM_SERV_PIN_DISABLE_OK); _(SIM_SERV_WRONG_UNBLOCKING_KEY); _(SIM_SERV_ILLEGAL_NUMBER); _(SIM_SERV_NOT_OK); _(SIM_SERV_PN_LIST_ENABLE_OK); _(SIM_SERV_PN_LIST_DISABLE_OK); _(SIM_SERV_NO_PIN); _(SIM_SERV_PIN_VERIFY_OK); _(SIM_SERV_PIN_BLOCKED); _(SIM_SERV_PIN_PERM_BLOCKED); _(SIM_SERV_DATA_NOT_AVAIL); _(SIM_SERV_IN_HOME_ZONE); _(SIM_SERV_STATE_CHANGED); _(SIM_SERV_INF_NBR_READ_OK); _(SIM_SERV_INF_NBR_READ_NOT_OK); _(SIM_SERV_IMSI_EQUAL); _(SIM_SERV_IMSI_NOT_EQUAL); _(SIM_SERV_INVALID_LOCATION); _(SIM_SERV_STA_SIM_REMOVED); _(SIM_SERV_SECOND_SIM_REMOVED_CS); _(SIM_SERV_CONNECTED_INDICATION_CS); _(SIM_SERV_SECOND_SIM_CONNECTED_CS); _(SIM_SERV_PIN_RIGHTS_LOST_IND_CS); _(SIM_SERV_PIN_RIGHTS_GRANTED_IND_CS); _(SIM_SERV_INIT_OK_CS); _(SIM_SERV_INIT_NOT_OK_CS); _(SIM_FDN_ENABLED); _(SIM_FDN_DISABLED); _(SIM_SERV_INVALID_FILE); _(SIM_SERV_DATA_AVAIL); _(SIM_SERV_ICC_EQUAL); _(SIM_SERV_ICC_NOT_EQUAL); _(SIM_SERV_SIM_NOT_INITIALISED); _(SIM_SERV_SERVICE_NOT_AVAIL); _(SIM_SERV_FDN_STATUS_ERROR); _(SIM_SERV_FDN_CHECK_PASSED); _(SIM_SERV_FDN_CHECK_FAILED); _(SIM_SERV_FDN_CHECK_DISABLED); _(SIM_SERV_FDN_CHECK_NO_FDN_SIM); _(SIM_STA_ISIM_AVAILEBLE_PIN_REQUIRED); _(SIM_STA_ISIM_AVAILEBLE); _(SIM_STA_USIM_AVAILEBLE); _(SIM_STA_SIM_AVAILEBLE); _(SIM_STA_ISIM_NOT_INITIALIZED); _(SIM_STA_IMS_READY); _(SIM_STA_APP_DATA_READ_OK); _(SIM_STA_APP_ACTIVATE_OK); _(SIM_STA_APP_ACTIVATE_NOT_OK); _(SIM_SERV_NOT_DEFINED); _(SIM_SERV_NOSERVICE); _(SIM_SERV_NOTREADY); _(SIM_SERV_ERROR); _(SIM_SERV_CIPHERING_INDICATOR_DISPLAY_REQUIRED); _(SIM_SERV_CIPHERING_INDICATOR_DISPLAY_NOT_REQUIRED); _(SIM_SERV_FILE_NOT_AVAILABLE); } return "SIM_"; } const char *sim_message_id_name(enum sim_message_id value) { switch (value) { _(SIM_NETWORK_INFO_REQ); _(SIM_NETWORK_INFO_RESP); _(SIM_IMSI_REQ_READ_IMSI); _(SIM_IMSI_RESP_READ_IMSI); _(SIM_SERV_PROV_NAME_REQ); _(SIM_SERV_PROV_NAME_RESP); _(SIM_DYNAMIC_FLAGS_REQ); _(SIM_DYNAMIC_FLAGS_RESP); _(SIM_READ_FIELD_REQ); _(SIM_READ_FIELD_RESP); _(SIM_SMS_REQ); _(SIM_SMS_RESP); _(SIM_STATUS_REQ); _(SIM_STATUS_RESP); _(SIM_PB_REQ_SIM_PB_READ); _(SIM_PB_RESP_SIM_PB_READ); _(SIM_SERVER_READY_IND); _(SIM_IND); } return "SIM_"; } const char *sim_password_name(enum ofono_sim_password_type type) { static const char *const passwd_name[] = { [OFONO_SIM_PASSWORD_NONE] = "none", [OFONO_SIM_PASSWORD_SIM_PIN] = "pin", [OFONO_SIM_PASSWORD_SIM_PUK] = "puk", [OFONO_SIM_PASSWORD_PHSIM_PIN] = "phone", [OFONO_SIM_PASSWORD_PHFSIM_PIN] = "firstphone", [OFONO_SIM_PASSWORD_PHFSIM_PUK] = "firstphonepuk", [OFONO_SIM_PASSWORD_SIM_PIN2] = "pin2", [OFONO_SIM_PASSWORD_SIM_PUK2] = "puk2", [OFONO_SIM_PASSWORD_PHNET_PIN] = "network", [OFONO_SIM_PASSWORD_PHNET_PUK] = "networkpuk", [OFONO_SIM_PASSWORD_PHNETSUB_PIN] = "netsub", [OFONO_SIM_PASSWORD_PHNETSUB_PUK] = "netsubpuk", [OFONO_SIM_PASSWORD_PHSP_PIN] = "service", [OFONO_SIM_PASSWORD_PHSP_PUK] = "servicepuk", [OFONO_SIM_PASSWORD_PHCORP_PIN] = "corp", [OFONO_SIM_PASSWORD_PHCORP_PUK] = "corppuk", [OFONO_SIM_PASSWORD_INVALID] = "invalid", }; if (OFONO_SIM_PASSWORD_NONE <= (int)type && type <= OFONO_SIM_PASSWORD_PHCORP_PUK) return passwd_name[type]; else return "UNKNOWN"; } const char *sec_message_id_name(enum sec_message_id value) { switch (value) { _(SEC_CODE_STATE_REQ); _(SEC_CODE_STATE_OK_RESP); _(SEC_CODE_STATE_FAIL_RESP); _(SEC_CODE_CHANGE_REQ); _(SEC_CODE_CHANGE_OK_RESP); _(SEC_CODE_CHANGE_FAIL_RESP); _(SEC_CODE_VERIFY_REQ); _(SEC_CODE_VERIFY_OK_RESP); _(SEC_CODE_VERIFY_FAIL_RESP); _(SEC_STATE_REQ); _(SEC_STATE_RESP); } return "SEC_"; } const char *sim_subblock_name(enum sim_subblock value) { switch (value) { _(SIM_PB_INFO_REQUEST); _(SIM_PB_STATUS); _(SIM_PB_LOCATION); _(SIM_PB_LOCATION_SEARCH); } return "SIM_"; } const char *info_isi_cause_name(enum info_isi_cause value) { switch (value) { _(INFO_OK); _(INFO_FAIL); _(INFO_NO_NUMBER); _(INFO_NOT_SUPPORTED); } return "INFO_"; } const char *info_message_id_name(enum info_message_id value) { switch (value) { _(INFO_SERIAL_NUMBER_READ_REQ); _(INFO_SERIAL_NUMBER_READ_RESP); _(INFO_PP_READ_REQ); _(INFO_PP_READ_RESP); _(INFO_VERSION_READ_REQ); _(INFO_VERSION_READ_RESP); _(INFO_PRODUCT_INFO_READ_REQ); _(INFO_PRODUCT_INFO_READ_RESP); } return "INFO_"; } const char *info_subblock_name(enum info_subblock value) { switch (value) { _(INFO_SB_MODEMSW_VERSION); _(INFO_SB_PRODUCT_INFO_NAME); _(INFO_SB_PRODUCT_INFO_MANUFACTURER); _(INFO_SB_SN_IMEI_PLAIN); _(INFO_SB_SN_IMEI_SV_TO_NET); _(INFO_SB_PP); _(INFO_SB_MCUSW_VERSION); } return "INFO_"; } const char *call_status_name(enum call_status value) { switch (value) { _(CALL_STATUS_IDLE); _(CALL_STATUS_CREATE); _(CALL_STATUS_COMING); _(CALL_STATUS_PROCEEDING); _(CALL_STATUS_MO_ALERTING); _(CALL_STATUS_MT_ALERTING); _(CALL_STATUS_WAITING); _(CALL_STATUS_ANSWERED); _(CALL_STATUS_ACTIVE); _(CALL_STATUS_MO_RELEASE); _(CALL_STATUS_MT_RELEASE); _(CALL_STATUS_HOLD_INITIATED); _(CALL_STATUS_HOLD); _(CALL_STATUS_RETRIEVE_INITIATED); _(CALL_STATUS_RECONNECT_PENDING); _(CALL_STATUS_TERMINATED); _(CALL_STATUS_SWAP_INITIATED); } return "CALL_"; } char const *call_message_id_name(enum call_message_id value) { switch (value) { _(CALL_CREATE_REQ); _(CALL_CREATE_RESP); _(CALL_COMING_IND); _(CALL_MO_ALERT_IND); _(CALL_MT_ALERT_IND); _(CALL_WAITING_IND); _(CALL_ANSWER_REQ); _(CALL_ANSWER_RESP); _(CALL_RELEASE_REQ); _(CALL_RELEASE_RESP); _(CALL_RELEASE_IND); _(CALL_TERMINATED_IND); _(CALL_STATUS_REQ); _(CALL_STATUS_RESP); _(CALL_STATUS_IND); _(CALL_SERVER_STATUS_IND); _(CALL_CONTROL_REQ); _(CALL_CONTROL_RESP); _(CALL_CONTROL_IND); _(CALL_MODE_SWITCH_REQ); _(CALL_MODE_SWITCH_RESP); _(CALL_MODE_SWITCH_IND); _(CALL_DTMF_SEND_REQ); _(CALL_DTMF_SEND_RESP); _(CALL_DTMF_STOP_REQ); _(CALL_DTMF_STOP_RESP); _(CALL_DTMF_STATUS_IND); _(CALL_DTMF_TONE_IND); _(CALL_RECONNECT_IND); _(CALL_PROPERTY_GET_REQ); _(CALL_PROPERTY_GET_RESP); _(CALL_PROPERTY_SET_REQ); _(CALL_PROPERTY_SET_RESP); _(CALL_PROPERTY_SET_IND); _(CALL_EMERGENCY_NBR_CHECK_REQ); _(CALL_EMERGENCY_NBR_CHECK_RESP); _(CALL_EMERGENCY_NBR_GET_REQ); _(CALL_EMERGENCY_NBR_GET_RESP); _(CALL_EMERGENCY_NBR_MODIFY_REQ); _(CALL_EMERGENCY_NBR_MODIFY_RESP); _(CALL_GSM_NOTIFICATION_IND); _(CALL_GSM_USER_TO_USER_REQ); _(CALL_GSM_USER_TO_USER_RESP); _(CALL_GSM_USER_TO_USER_IND); _(CALL_GSM_BLACKLIST_CLEAR_REQ); _(CALL_GSM_BLACKLIST_CLEAR_RESP); _(CALL_GSM_BLACKLIST_TIMER_IND); _(CALL_GSM_DATA_CH_INFO_IND); _(CALL_GSM_CCP_GET_REQ); _(CALL_GSM_CCP_GET_RESP); _(CALL_GSM_CCP_CHECK_REQ); _(CALL_GSM_CCP_CHECK_RESP); _(CALL_GSM_COMING_REJ_IND); _(CALL_GSM_RAB_IND); _(CALL_GSM_IMMEDIATE_MODIFY_IND); _(CALL_CREATE_NO_SIMATK_REQ); _(CALL_GSM_SS_DATA_IND); _(CALL_TIMER_REQ); _(CALL_TIMER_RESP); _(CALL_TIMER_NTF); _(CALL_TIMER_IND); _(CALL_TIMER_RESET_REQ); _(CALL_TIMER_RESET_RESP); _(CALL_EMERGENCY_NBR_IND); _(CALL_SERVICE_DENIED_IND); _(CALL_RELEASE_END_REQ); _(CALL_RELEASE_END_RESP); _(CALL_USER_CONNECT_IND); _(CALL_AUDIO_CONNECT_IND); _(CALL_KODIAK_ALLOW_CTRL_REQ); _(CALL_KODIAK_ALLOW_CTRL_RESP); _(CALL_SERVICE_ACTIVATE_IND); _(CALL_SERVICE_ACTIVATE_REQ); _(CALL_SERVICE_ACTIVATE_RESP); _(CALL_SIM_ATK_IND); _(CALL_CONTROL_OPER_IND); _(CALL_TEST_CALL_STATUS_IND); _(CALL_SIM_ATK_INFO_IND); _(CALL_SECURITY_IND); _(CALL_MEDIA_HANDLE_REQ); _(CALL_MEDIA_HANDLE_RESP); } return "CALL_"; } char const *call_isi_cause_name(enum call_isi_cause value) { switch (value) { _(CALL_CAUSE_NO_CAUSE); _(CALL_CAUSE_NO_CALL); _(CALL_CAUSE_TIMEOUT); _(CALL_CAUSE_RELEASE_BY_USER); _(CALL_CAUSE_BUSY_USER_REQUEST); _(CALL_CAUSE_ERROR_REQUEST); _(CALL_CAUSE_COST_LIMIT_REACHED); _(CALL_CAUSE_CALL_ACTIVE); _(CALL_CAUSE_NO_CALL_ACTIVE); _(CALL_CAUSE_INVALID_CALL_MODE); _(CALL_CAUSE_SIGNALLING_FAILURE); _(CALL_CAUSE_TOO_LONG_ADDRESS); _(CALL_CAUSE_INVALID_ADDRESS); _(CALL_CAUSE_EMERGENCY); _(CALL_CAUSE_NO_TRAFFIC_CHANNEL); _(CALL_CAUSE_NO_COVERAGE); _(CALL_CAUSE_CODE_REQUIRED); _(CALL_CAUSE_NOT_ALLOWED); _(CALL_CAUSE_NO_DTMF); _(CALL_CAUSE_CHANNEL_LOSS); _(CALL_CAUSE_FDN_NOT_OK); _(CALL_CAUSE_USER_TERMINATED); _(CALL_CAUSE_BLACKLIST_BLOCKED); _(CALL_CAUSE_BLACKLIST_DELAYED); _(CALL_CAUSE_NUMBER_NOT_FOUND); _(CALL_CAUSE_NUMBER_CANNOT_REMOVE); _(CALL_CAUSE_EMERGENCY_FAILURE); _(CALL_CAUSE_CS_SUSPENDED); _(CALL_CAUSE_DCM_DRIVE_MODE); _(CALL_CAUSE_MULTIMEDIA_NOT_ALLOWED); _(CALL_CAUSE_SIM_REJECTED); _(CALL_CAUSE_NO_SIM); _(CALL_CAUSE_SIM_LOCK_OPERATIVE); _(CALL_CAUSE_SIMATKCC_REJECTED); _(CALL_CAUSE_SIMATKCC_MODIFIED); _(CALL_CAUSE_DTMF_INVALID_DIGIT); _(CALL_CAUSE_DTMF_SEND_ONGOING); _(CALL_CAUSE_CS_INACTIVE); _(CALL_CAUSE_SECURITY_MODE); _(CALL_CAUSE_TRACFONE_FAILED); _(CALL_CAUSE_TRACFONE_WAIT_FAILED); _(CALL_CAUSE_TRACFONE_CONF_FAILED); _(CALL_CAUSE_TEMPERATURE_LIMIT); _(CALL_CAUSE_KODIAK_POC_FAILED); _(CALL_CAUSE_NOT_REGISTERED); _(CALL_CAUSE_CS_CALLS_ONLY); _(CALL_CAUSE_VOIP_CALLS_ONLY); _(CALL_CAUSE_LIMITED_CALL_ACTIVE); _(CALL_CAUSE_LIMITED_CALL_NOT_ALLOWED); _(CALL_CAUSE_SECURE_CALL_NOT_POSSIBLE); _(CALL_CAUSE_INTERCEPT); } return "CALL_"; } char const *call_gsm_cause_name(enum call_gsm_cause value) { switch (value) { _(CALL_GSM_CAUSE_UNASSIGNED_NUMBER); _(CALL_GSM_CAUSE_NO_ROUTE); _(CALL_GSM_CAUSE_CH_UNACCEPTABLE); _(CALL_GSM_CAUSE_OPER_BARRING); _(CALL_GSM_CAUSE_NORMAL); _(CALL_GSM_CAUSE_USER_BUSY); _(CALL_GSM_CAUSE_NO_USER_RESPONSE); _(CALL_GSM_CAUSE_ALERT_NO_ANSWER); _(CALL_GSM_CAUSE_CALL_REJECTED); _(CALL_GSM_CAUSE_NUMBER_CHANGED); _(CALL_GSM_CAUSE_NON_SELECT_CLEAR); _(CALL_GSM_CAUSE_DEST_OUT_OF_ORDER); _(CALL_GSM_CAUSE_INVALID_NUMBER); _(CALL_GSM_CAUSE_FACILITY_REJECTED); _(CALL_GSM_CAUSE_RESP_TO_STATUS); _(CALL_GSM_CAUSE_NORMAL_UNSPECIFIED); _(CALL_GSM_CAUSE_NO_CHANNEL); _(CALL_GSM_CAUSE_NETW_OUT_OF_ORDER); _(CALL_GSM_CAUSE_TEMPORARY_FAILURE); _(CALL_GSM_CAUSE_CONGESTION); _(CALL_GSM_CAUSE_ACCESS_INFO_DISC); _(CALL_GSM_CAUSE_CHANNEL_NA); _(CALL_GSM_CAUSE_RESOURCES_NA); _(CALL_GSM_CAUSE_QOS_NA); _(CALL_GSM_CAUSE_FACILITY_UNSUBS); _(CALL_GSM_CAUSE_COMING_BARRED_CUG); _(CALL_GSM_CAUSE_BC_UNAUTHORIZED); _(CALL_GSM_CAUSE_BC_NA); _(CALL_GSM_CAUSE_SERVICE_NA); _(CALL_GSM_CAUSE_BEARER_NOT_IMPL); _(CALL_GSM_CAUSE_ACM_MAX); _(CALL_GSM_CAUSE_FACILITY_NOT_IMPL); _(CALL_GSM_CAUSE_ONLY_RDI_BC); _(CALL_GSM_CAUSE_SERVICE_NOT_IMPL); _(CALL_GSM_CAUSE_INVALID_TI); _(CALL_GSM_CAUSE_NOT_IN_CUG); _(CALL_GSM_CAUSE_INCOMPATIBLE_DEST); _(CALL_GSM_CAUSE_INV_TRANS_NET_SEL); _(CALL_GSM_CAUSE_SEMANTICAL_ERR); _(CALL_GSM_CAUSE_INVALID_MANDATORY); _(CALL_GSM_CAUSE_MSG_TYPE_INEXIST); _(CALL_GSM_CAUSE_MSG_TYPE_INCOMPAT); _(CALL_GSM_CAUSE_IE_NON_EXISTENT); _(CALL_GSM_CAUSE_COND_IE_ERROR); _(CALL_GSM_CAUSE_MSG_INCOMPATIBLE); _(CALL_GSM_CAUSE_TIMER_EXPIRY); _(CALL_GSM_CAUSE_PROTOCOL_ERROR); _(CALL_GSM_CAUSE_INTERWORKING); } return "CALL_"; } const char *net_gsm_cause_name(enum net_gsm_cause value) { switch (value) { _(NET_GSM_IMSI_UNKNOWN_IN_HLR); _(NET_GSM_ILLEGAL_MS); _(NET_GSM_IMSI_UNKNOWN_IN_VLR); _(NET_GSM_IMEI_NOT_ACCEPTED); _(NET_GSM_ILLEGAL_ME); _(NET_GSM_GPRS_SERVICES_NOT_ALLOWED); _(NET_GSM_GPRS_AND_NON_GPRS_NA); _(NET_GSM_MS_ID_CANNOT_BE_DERIVED); _(NET_GSM_IMPLICITLY_DETACHED); _(NET_GSM_PLMN_NOT_ALLOWED); _(NET_GSM_LA_NOT_ALLOWED); _(NET_GSM_ROAMING_NOT_IN_THIS_LA); _(NET_GSM_GPRS_SERV_NA_IN_THIS_PLMN); _(NET_GSM_NO_SUITABLE_CELLS_IN_LA); _(NET_GSM_MSC_TEMP_NOT_REACHABLE); _(NET_GSM_NETWORK_FAILURE); _(NET_GSM_MAC_FAILURE); _(NET_GSM_SYNCH_FAILURE); _(NET_GSM_CONGESTION); _(NET_GSM_AUTH_UNACCEPTABLE); _(NET_GSM_SERV_OPT_NOT_SUPPORTED); _(NET_GSM_SERV_OPT_NOT_SUBSCRIBED); _(NET_GSM_SERV_TEMP_OUT_OF_ORDER); _(NET_GSM_RETRY_ENTRY_NEW_CELL_LOW); _(NET_GSM_RETRY_ENTRY_NEW_CELL_HIGH); _(NET_GSM_SEMANTICALLY_INCORRECT); _(NET_GSM_INVALID_MANDATORY_INFO); _(NET_GSM_MSG_TYPE_NONEXISTENT); _(NET_GSM_CONDITIONAL_IE_ERROR); _(NET_GSM_MSG_TYPE_WRONG_STATE); _(NET_GSM_PROTOCOL_ERROR_UNSPECIFIED); } return "NET_"; } const char *net_isi_cause_name(enum net_isi_cause value) { switch (value) { _(NET_CAUSE_OK); _(NET_CAUSE_COMMUNICATION_ERROR); _(NET_CAUSE_INVALID_PARAMETER); _(NET_CAUSE_NO_SIM); _(NET_CAUSE_SIM_NOT_YET_READY); _(NET_CAUSE_NET_NOT_FOUND); _(NET_CAUSE_REQUEST_NOT_ALLOWED); _(NET_CAUSE_CALL_ACTIVE); _(NET_CAUSE_SERVER_BUSY); _(NET_CAUSE_SECURITY_CODE_REQUIRED); _(NET_CAUSE_NOTHING_TO_CANCEL); _(NET_CAUSE_UNABLE_TO_CANCEL); _(NET_CAUSE_NETWORK_FORBIDDEN); _(NET_CAUSE_REQUEST_REJECTED); _(NET_CAUSE_CS_NOT_SUPPORTED); _(NET_CAUSE_PAR_INFO_NOT_AVAILABLE); _(NET_CAUSE_NOT_DONE); _(NET_CAUSE_NO_SELECTED_NETWORK); _(NET_CAUSE_REQUEST_INTERRUPTED); _(NET_CAUSE_TOO_BIG_INDEX); _(NET_CAUSE_MEMORY_FULL); _(NET_CAUSE_SERVICE_NOT_ALLOWED); _(NET_CAUSE_NOT_SUPPORTED_IN_TECH); } return "NET_"; } const char *net_status_name(enum net_reg_status value) { switch (value) { _(NET_REG_STATUS_HOME); _(NET_REG_STATUS_ROAM); _(NET_REG_STATUS_ROAM_BLINK); _(NET_REG_STATUS_NOSERV); _(NET_REG_STATUS_NOSERV_SEARCHING); _(NET_REG_STATUS_NOSERV_NOTSEARCHING); _(NET_REG_STATUS_NOSERV_NOSIM); _(NET_REG_STATUS_POWER_OFF); _(NET_REG_STATUS_NSPS); _(NET_REG_STATUS_NSPS_NO_COVERAGE); _(NET_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW); } return "NET_"; } const char *net_message_id_name(enum net_message_id value) { switch (value) { _(NET_MODEM_REG_STATUS_GET_REQ); _(NET_MODEM_REG_STATUS_GET_RESP); _(NET_MODEM_REG_STATUS_IND); _(NET_MODEM_AVAILABLE_GET_REQ); _(NET_MODEM_AVAILABLE_GET_RESP); _(NET_SET_REQ); _(NET_SET_RESP); _(NET_RSSI_GET_REQ); _(NET_RSSI_GET_RESP); _(NET_CS_STATE_IND); _(NET_RSSI_IND); _(NET_CIPHERING_IND); _(NET_TIME_IND); _(NET_CHANNEL_INFO_IND); _(NET_RAT_IND); _(NET_RAT_REQ); _(NET_RAT_RESP); _(NET_CS_STATE_REQ); _(NET_CS_STATE_RESP); _(NET_CELL_INFO_GET_REQ); _(NET_CELL_INFO_GET_RESP); _(NET_CELL_INFO_IND); _(NET_NITZ_NAME_IND); _(NET_NW_ACCESS_CONF_REQ); _(NET_NW_ACCESS_CONF_RESP); _(NET_REG_STATUS_GET_REQ); _(NET_REG_STATUS_GET_RESP); _(NET_REG_STATUS_IND); _(NET_AVAILABLE_GET_REQ); _(NET_AVAILABLE_GET_RESP); _(NET_OPER_NAME_READ_REQ); _(NET_OPER_NAME_READ_RESP); _(NET_OLD_OPER_NAME_READ_REQ); _(NET_OLD_OPER_NAME_READ_RESP); } return "NET_"; } const char *net_subblock_name(enum net_subblock value) { switch (value) { _(NET_REG_INFO_COMMON); _(NET_MODEM_AVAIL_NETWORK_INFO_COMMON); _(NET_OPERATOR_INFO_COMMON); _(NET_RSSI_CURRENT); _(NET_GSM_REG_INFO); _(NET_DETAILED_NETWORK_INFO); _(NET_GSM_OPERATOR_INFO); _(NET_TIME_INFO); _(NET_GSM_BAND_INFO); _(NET_RAT_INFO); _(NET_GSM_CELL_INFO); _(NET_WCDMA_CELL_INFO); _(NET_FULL_NITZ_NAME); _(NET_SHORT_NITZ_NAME); _(NET_REGISTRATION_CONF_INFO); _(NET_ROAMING_CONF_INFO); _(NET_REGISTRATION_CONF1_INFO); _(NET_ROAMING_CONF1_INFO); _(NET_AVAIL_NETWORK_INFO_COMMON); _(NET_OPER_NAME_INFO); } return "NET_"; } const char *gss_message_id_name(enum gss_message_id value) { switch (value) { _(GSS_CS_SERVICE_REQ); _(GSS_CS_SERVICE_RESP); _(GSS_CS_SERVICE_FAIL_RESP); } return "GSS_"; } const char *gss_subblock_name(enum gss_subblock value) { switch (value) { _(GSS_RAT_INFO); } return "GSS_"; } const char *gpds_message_id_name(enum gpds_message_id value) { switch (value) { _(GPDS_LL_CONFIGURE_REQ); _(GPDS_LL_CONFIGURE_RESP); _(GPDS_CONTEXT_ID_CREATE_REQ); _(GPDS_CONTEXT_ID_CREATE_RESP); _(GPDS_CONTEXT_ID_CREATE_IND); _(GPDS_CONTEXT_ID_DELETE_IND); _(GPDS_CONTEXT_CONFIGURE_REQ); _(GPDS_CONTEXT_CONFIGURE_RESP); _(GPDS_CONTEXT_ACTIVATE_REQ); _(GPDS_CONTEXT_ACTIVATE_RESP); _(GPDS_CONTEXT_ACTIVATE_IND); _(GPDS_CONTEXT_DEACTIVATE_REQ); _(GPDS_CONTEXT_DEACTIVATE_RESP); _(GPDS_CONTEXT_DEACTIVATE_IND); _(GPDS_CONTEXT_MWI_ACT_REQUEST_IND); _(GPDS_CONTEXT_NWI_ACT_REJECT_REQ); _(GPDS_CONTEXT_NWI_ACT_REJECT_RESP); _(GPDS_CONFIGURE_REQ); _(GPDS_CONFIGURE_RESP); _(GPDS_ATTACH_REQ); _(GPDS_ATTACH_RESP); _(GPDS_ATTACH_IND); _(GPDS_DETACH_REQ); _(GPDS_DETACH_RESP); _(GPDS_DETACH_IND); _(GPDS_STATUS_REQ); _(GPDS_STATUS_RESP); _(GPDS_SMS_PDU_SEND_REQ); _(GPDS_SMS_PDU_SEND_RESP); _(GPDS_SMS_PDU_RECEIVE_IND); _(GPDS_TRANSFER_STATUS_IND); _(GPDS_CONTEXT_ACTIVATE_FAIL_IND); _(GPDS_LL_BIND_REQ); _(GPDS_LL_BIND_RESP); _(GPDS_CONTEXT_STATUS_REQ); _(GPDS_CONTEXT_STATUS_RESP); _(GPDS_CONTEXT_STATUS_IND); _(GPDS_CONTEXT_ACTIVATING_IND); _(GPDS_CONTEXT_MODIFY_REQ); _(GPDS_CONTEXT_MODIFY_RESP); _(GPDS_CONTEXT_MODIFY_IND); _(GPDS_ATTACH_FAIL_IND); _(GPDS_CONTEXT_DEACTIVATING_IND); _(GPDS_CONFIGURATION_INFO_REQ); _(GPDS_CONFIGURATION_INFO_RESP); _(GPDS_CONFIGURATION_INFO_IND); _(GPDS_CONTEXT_AUTH_REQ); _(GPDS_CONTEXT_AUTH_RESP); _(GPDS_TEST_MODE_REQ); _(GPDS_TEST_MODE_RESP); _(GPDS_RADIO_ACTIVITY_IND); _(GPDS_FORCED_READY_STATE_REQ); _(GPDS_FORCED_READY_STATE_RESP); _(GPDS_CONTEXTS_CLEAR_REQ); _(GPDS_CONTEXTS_CLEAR_RESP); _(GPDS_MBMS_SERVICE_SELECTION_REQ); _(GPDS_MBMS_SERVICE_SELECTION_RESP); _(GPDS_MBMS_STATUS_IND); _(GPDS_MBMS_CONTEXT_CREATE_REQ); _(GPDS_MBMS_CONTEXT_CREATE_RESP); _(GPDS_MBMS_CONTEXT_ACTIVATE_REQ); _(GPDS_MBMS_CONTEXT_ACTIVATE_RESP); _(GPDS_MBMS_CONTEXT_DELETE_REQ); _(GPDS_MBMS_CONTEXT_DELETE_RESP); _(GPDS_MBMS_CONTEXT_DELETE_IND); _(GPDS_MBMS_SERVICE_SELECTION_IND); _(GPDS_MBMS_SERVICE_AVAILABLE_IND); _(GPDS_TEST_REQ); _(GPDS_TEST_RESP); } return "GPSD_"; } const char *gpds_subblock_name(enum gpds_subblock value) { switch (value) { _(GPDS_COMP_INFO); _(GPDS_QOS_REQ_INFO); _(GPDS_QOS_MIN_INFO); _(GPDS_QOS_NEG_INFO); _(GPDS_PDP_ADDRESS_INFO); _(GPDS_APN_INFO); _(GPDS_QOS99_REQ_INFO); _(GPDS_QOS99_MIN_INFO); _(GPDS_QOS99_NEG_INFO); _(GPDS_TFT_INFO); _(GPDS_TFT_FILTER_INFO); _(GPDS_USER_NAME_INFO); _(GPDS_PASSWORD_INFO); _(GPDS_PDNS_ADDRESS_INFO); _(GPDS_SDNS_ADDRESS_INFO); _(GPDS_CHALLENGE_INFO); _(GPDS_DNS_ADDRESS_REQ_INFO); } return "GPDS_"; } const char *gpds_status_name(enum gpds_status value) { switch (value) { _(GPDS_ERROR); _(GPDS_OK); _(GPDS_FAIL); } return "GPDS_"; } const char *gpds_isi_cause_name(enum gpds_isi_cause value) { switch (value) { _(GPDS_CAUSE_UNKNOWN); _(GPDS_CAUSE_IMSI); _(GPDS_CAUSE_MS_ILLEGAL); _(GPDS_CAUSE_ME_ILLEGAL); _(GPDS_CAUSE_GPRS_NOT_ALLOWED); _(GPDS_NOT_ALLOWED); _(GPDS_CAUSE_MS_IDENTITY); _(GPDS_CAUSE_DETACH); _(GPDS_PLMN_NOT_ALLOWED); _(GPDS_LA_NOT_ALLOWED); _(GPDS_ROAMING_NOT_ALLOWED); _(GPDS_CAUSE_GPRS_NOT_ALLOWED_IN_PLMN); _(GPDS_CAUSE_MSC_NOT_REACH); _(GPDS_CAUSE_PLMN_FAIL); _(GPDS_CAUSE_NETWORK_CONGESTION); _(GPDS_CAUSE_MBMS_BEARER_CAPABILITY_INSUFFICIENT); _(GPDS_CAUSE_LLC_SNDCP_FAILURE); _(GPDS_CAUSE_RESOURCE_INSUFF); _(GPDS_CAUSE_APN); _(GPDS_CAUSE_PDP_UNKNOWN); _(GPDS_CAUSE_AUTHENTICATION); _(GPDS_CAUSE_ACT_REJECT_GGSN); _(GPDS_CAUSE_ACT_REJECT); _(GPDS_CAUSE_SERV_OPT_NOT_SUPPORTED); _(GPDS_CAUSE_SERV_OPT_NOT_SUBSCRIBED); _(GPDS_CAUSE_SERV_OPT_OUT_OF_ORDER); _(GPDS_CAUSE_NSAPI_ALREADY_USED); _(GPDS_CAUSE_DEACT_REGULAR); _(GPDS_CAUSE_QOS); _(GPDS_CAUSE_NETWORK_FAIL); _(GPDS_CAUSE_REACTIVATION_REQ); _(GPDS_CAUSE_FEAT_NOT_SUPPORTED); _(GPDS_CAUSE_TFT_SEMANTIC_ERROR); _(GPDS_CAUSE_TFT_SYNTAX_ERROR); _(GPDS_CAUSE_CONTEXT_UNKNOWN); _(GPDS_CAUSE_FILTER_SEMANTIC_ERROR); _(GPDS_CAUSE_FILTER_SYNTAX_ERROR); _(GPDS_CAUSE_CONT_WITHOUT_TFT); _(GPDS_CAUSE_MULTICAST_MEMBERSHIP_TIMEOUT); _(GPDS_CAUSE_INVALID_MANDATORY_INFO); _(GPDS_CAUSE_MSG_TYPE_NON_EXISTENTOR_NOT_IMPLTD); _(GPDS_CAUSE_MSG_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE); _(GPDS_CAUSE_IE_NON_EXISTENT_OR_NOT_IMPLEMENTED); _(GPDS_CAUSE_CONDITIONAL_IE_ERROR); _(GPDS_CUASEMSG_NOT_COMPATIBLE_WITH_PROTOCOL_STATE); _(GPDS_CAUSE_UNSPECIFIED); _(GPDS_CAUSE_APN_INCOMPATIBLE_WITH_CURR_CTXT); _(GPDS_CAUSE_FDN); _(GPDS_CAUSE_USER_ABORT); _(GPDS_CAUSE_CS_INACTIVE); _(GPDS_CAUSE_CSD_OVERRIDE); _(GPDS_CAUSE_APN_CONTROL); _(GPDS_CAUSE_CALL_CONTROL); _(GPDS_CAUSE_TEMPERATURE_LIMIT); _(GPDS_CAUSE_RETRY_COUNTER_EXPIRED); _(GPDS_CAUSE_NO_CONNECTION); _(GPDS_CAUSE_DETACHED); _(GPDS_CAUSE_NO_SERVICE_POWER_SAVE); _(GPDS_CAUSE_SIM_REMOVED); _(GPDS_CAUSE_POWER_OFF); _(GPDS_CAUSE_LAI_FORBIDDEN_NATIONAL_ROAM_LIST); _(GPDS_CAUSE_LAI_FORBIDDEN_REG_PROVISION_LIST); _(GPDS_CAUSE_ACCESS_BARRED); _(GPDS_CAUSE_FATAL_FAILURE); _(GPDS_CAUSE_AUT_FAILURE); } return "GPDS_"; } const char *gpds_transfer_status_name(enum gpds_transfer_status value) { switch (value) { _(GPDS_TRANSFER_NOT_AVAIL); _(GPDS_TRANSFER_AVAIL); } return "GPDS_"; } const char *gpds_transfer_cause_name(enum gpds_transfer_cause value) { switch (value) { _(GPDS_TRANSFER_CAUSE_ATTACHED); _(GPDS_TRANSFER_CAUSE_DETACHED); _(GPDS_TRANSFER_CAUSE_RESUMED); _(GPDS_TRANSFER_CAUSE_SUSPENDED_NO_COVERAGE); _(GPDS_TRANSFER_CAUSE_SUSPENDED_CALL_SMS); _(GPDS_TRANSFER_CAUSE_SUSPENDED_CALL); _(GPDS_TRANSFER_CAUSE_SUSPENDED_RAU); _(GPDS_TRANSFER_CAUSE_SUSPENDED_LU); _(GPDS_TRANSFER_CAUSE_DSAC_RESTRICTION); } return "GPDS_"; } #undef _ static void hex_dump(const char *resname, uint8_t res, const char *name, uint8_t id, uint8_t utid, const uint8_t m[], size_t len) { char hex[3 * 16 + 1]; char ascii[16 + 1]; size_t i, j, k; ofono_debug("%s (0x%02X): %s [id=0x%02X utid=0x%02X len=%zu]:", resname, res, name, id, utid, len); strcpy(hex, ""), j = 0; strcpy(ascii, "."), k = 1; for (i = 0; i < len; i++) { sprintf(hex + j, " %02X", m[i]), j += 3; ascii[k++] = g_ascii_isgraph(m[i]) ? m[i] : '.'; if ((j & 48) == 48) { ofono_debug(" *%-48s : %.*s", hex, (int) k, ascii); j = 0, k = 0; } } if (j) ofono_debug(" *%-48s : %.*s", hex, (int) k, ascii); } static const char *res_to_name(uint8_t res, uint8_t id) { if (id == COMMON_MESSAGE) return "COMMON_MESSAGE"; switch (res) { case PN_MODEM_NETWORK: case PN_NETWORK: return net_message_id_name(id); case PN_PHONE_INFO: case PN_MODEM_INFO: case PN_EPOC_INFO: return info_message_id_name(id); case PN_SS: return ss_message_id_name(id); case PN_MODEM_CALL: case PN_CALL: return call_message_id_name(id); case PN_SECURITY: return sec_message_id_name(id); case PN_SMS: return sms_message_id_name(id); case PN_SIM: return sim_message_id_name(id); case PN_MTC: return mtc_message_id_name(id); case PN_GSS: return gss_message_id_name(id); case PN_GPDS: return gpds_message_id_name(id); case PN_UICC: return uicc_message_id_name(id); } return "UNKNOWN"; } void isi_trace(const GIsiMessage *msg, void *data) { uint8_t id = g_isi_msg_id(msg); uint8_t res = g_isi_msg_resource(msg); const char *resname = pn_resource_name(res); const char *name = res_to_name(res, id); uint8_t const *dump = g_isi_msg_data(msg); hex_dump(resname, res, name, id, g_isi_msg_utid(msg), dump - 2, g_isi_msg_data_len(msg) + 2); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/gprs.c0000644000015600001650000002545512671500024023015 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "isimodem.h" #include "isiutil.h" #include "gpds.h" #include "info.h" #include "debug.h" /* 27.007 Section 10.1.20 */ enum network_registration_status { GPRS_STAT_NOT_REGISTERED = 0, GPRS_STAT_REGISTERED = 1, GPRS_STAT_SEARCHING = 2, GPRS_STAT_DENIED = 3, GPRS_STAT_UNKNOWN = 4, GPRS_STAT_ROAMING = 5, }; struct gprs_data { GIsiClient *client; GIsiClient *info_client; }; static void configure_resp_cb(const GIsiMessage *msg, void *opaque) { const uint8_t *data = g_isi_msg_data(msg); if (g_isi_msg_error(msg) < 0) { DBG("ISI message error: %d", g_isi_msg_error(msg)); return; } if (g_isi_msg_id(msg) != GPDS_CONFIGURE_RESP) return; if (g_isi_msg_data_len(msg) < 1) return; if (data[0] != GPDS_OK) DBG("GPDS configure failed: %s", gpds_status_name(data[0])); } static void set_attach_mode(struct ofono_gprs *gprs, int attached) { struct gprs_data *gd = ofono_gprs_get_data(gprs); const unsigned char msg[] = { GPDS_CONFIGURE_REQ, attached ? GPDS_ATTACH_MODE_AUTOMATIC : GPDS_ATTACH_MODE_MANUAL, GPDS_MT_ACT_MODE_REJECT, GPDS_CLASSC_MODE_DEFAULT, GPDS_AOL_CTX_DEFAULT, 0x00, 0x00 }; g_isi_client_send(gd->client, msg, sizeof(msg), configure_resp_cb, gprs, NULL); } static void detach_ind_cb(const GIsiMessage *msg, void *opaque) { struct ofono_gprs *gprs = opaque; const uint8_t *data = g_isi_msg_data(msg); if (g_isi_msg_error(msg) < 0) return; if (g_isi_msg_id(msg) != GPDS_DETACH_IND) return; if (g_isi_msg_data_len(msg) < 2) return; DBG("detached: %s (0x%02"PRIx8")", gpds_isi_cause_name(data[0]), data[0]); set_attach_mode(gprs, FALSE); ofono_gprs_detached_notify(gprs); } static void suspend_notify(struct ofono_gprs *gprs, uint8_t suspend_status, uint8_t suspend_cause) { int cause; DBG("transfer status: %s (0x%02"PRIx8") cause %s (0x%02"PRIx8")", gpds_transfer_status_name(suspend_status), suspend_status, gpds_transfer_cause_name(suspend_cause), suspend_cause); if (suspend_status == GPDS_TRANSFER_AVAIL) { ofono_gprs_resume_notify(gprs); return; } switch (suspend_cause) { case GPDS_TRANSFER_CAUSE_SUSPENDED_NO_COVERAGE: cause = GPRS_SUSPENDED_NO_COVERAGE; break; case GPDS_TRANSFER_CAUSE_SUSPENDED_CALL: cause = GPRS_SUSPENDED_CALL; break; case GPDS_TRANSFER_CAUSE_SUSPENDED_CALL_SMS: case GPDS_TRANSFER_CAUSE_SUSPENDED_RAU: case GPDS_TRANSFER_CAUSE_SUSPENDED_LU: cause = GPRS_SUSPENDED_SIGNALLING; break; default: return; } ofono_gprs_suspend_notify(gprs, cause); } static void transfer_status_ind_cb(const GIsiMessage *msg, void *opaque) { struct ofono_gprs *gprs = opaque; const uint8_t *data = g_isi_msg_data(msg); if (g_isi_msg_error(msg) < 0) return; if (g_isi_msg_id(msg) != GPDS_TRANSFER_STATUS_IND) return; if (g_isi_msg_data_len(msg) < 2) return; suspend_notify(gprs, data[0], data[1]); } static void create_contexts(struct ofono_gprs *gprs, int count) { struct gprs_data *gd = ofono_gprs_get_data(gprs); GIsiModem *modem = g_isi_client_modem(gd->client); struct ofono_modem *omodem = g_isi_modem_get_userdata(modem); struct ofono_gprs_context *gc; int i; for (i = 0; i < count; i++) { gc = ofono_gprs_context_create(omodem, 0, "isimodem", modem); if (gc == NULL) break; ofono_gprs_add_context(gprs, gc); } ofono_gprs_set_cid_range(gprs, 1, i); DBG("%d GPRS contexts created", count); } static void info_pp_read_resp_cb(const GIsiMessage *msg, void *opaque) { struct ofono_gprs *gprs = opaque; uint8_t count = GPDS_MAX_CONTEXT_COUNT; GIsiSubBlockIter iter; if (g_isi_msg_error(msg) == -ESHUTDOWN) return; if (g_isi_msg_error(msg) < 0) goto out; if (g_isi_msg_id(msg) != INFO_PP_READ_RESP) goto out; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { switch (g_isi_sb_iter_get_id(&iter)) { case INFO_SB_PP: { guint16 fea; guint8 n; unsigned pp; if (!g_isi_sb_iter_get_byte(&iter, &n, 1)) goto out; for (pp = 4; n--; pp += 2) { if (!g_isi_sb_iter_get_word(&iter, &fea, pp)) goto out; if ((fea >> 8) != INFO_PP_MAX_PDP_CONTEXTS) goto out; count = fea & 0xff; break; } break; } default: break; } } out: create_contexts(gprs, count); } static void gpds_reachable_cb(const GIsiMessage *msg, void *opaque) { struct ofono_gprs *gprs = opaque; struct gprs_data *gd = ofono_gprs_get_data(gprs); GIsiModem *modem = g_isi_client_modem(gd->client); const unsigned char req[] = { INFO_PP_READ_REQ, 0, /* filler */ 1, /* subblocks */ INFO_SB_PP, 8, /* subblock length */ 0, 1, /* N */ INFO_PP_MAX_PDP_CONTEXTS, /* PP feature */ 0, /* PP value */ 0, /* filler */ 0 /* filler */ }; if (g_isi_msg_error(msg) < 0) { DBG("unable to bootstrap gprs driver"); ofono_gprs_remove(gprs); return; } ISI_RESOURCE_DBG(msg); g_isi_client_ind_subscribe(gd->client, GPDS_DETACH_IND, detach_ind_cb, gprs); g_isi_client_ind_subscribe(gd->client, GPDS_TRANSFER_STATUS_IND, transfer_status_ind_cb, gprs); ofono_gprs_register(gprs); gd->info_client = g_isi_client_create(modem, PN_PHONE_INFO); if (gd->info_client == NULL) { create_contexts(gprs, GPDS_MAX_CONTEXT_COUNT); return; } g_isi_client_send(gd->info_client, req, sizeof(req), info_pp_read_resp_cb, gprs, NULL); } static int isi_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor, void *user) { GIsiModem *modem = user; struct gprs_data *gd = g_try_new0(struct gprs_data, 1); if (gd == NULL) return -ENOMEM; gd->client = g_isi_client_create(modem, PN_GPDS); if (gd->client == NULL) { g_free(gd); return -ENOMEM; } ofono_gprs_set_data(gprs, gd); g_isi_client_set_timeout(gd->client, GPDS_TIMEOUT); g_isi_client_verify(gd->client, gpds_reachable_cb, gprs, NULL); return 0; } static void isi_gprs_remove(struct ofono_gprs *gprs) { struct gprs_data *gd = ofono_gprs_get_data(gprs); ofono_gprs_set_data(gprs, NULL); if (gd == NULL) return; g_isi_client_destroy(gd->client); g_isi_client_destroy(gd->info_client); g_free(gd); } static void attach_resp_cb(const GIsiMessage *msg, void *opaque) { struct isi_cb_data *cbd = opaque; ofono_gprs_cb_t cb = cbd->cb; const uint8_t *data = g_isi_msg_data(msg); if (g_isi_msg_error(msg) < 0) { DBG("ISI message error: %d", g_isi_msg_error(msg)); goto error; } if (g_isi_msg_id(msg) != GPDS_ATTACH_RESP) return; if (g_isi_msg_data_len(msg) < 2) goto error; if (data[0] != GPDS_OK) { DBG("attach failed: %s", gpds_status_name(data[0])); goto error; } set_attach_mode(cbd->user, TRUE); CALLBACK_WITH_SUCCESS(cb, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, cbd->data); } static void detach_resp_cb(const GIsiMessage *msg, void *opaque) { struct isi_cb_data *cbd = opaque; ofono_gprs_cb_t cb = cbd->cb; const uint8_t *data = g_isi_msg_data(msg); if (g_isi_msg_error(msg) < 0) { DBG("ISI client error: %d", g_isi_msg_error(msg)); goto error; } if (g_isi_msg_id(msg) != GPDS_DETACH_RESP) return; if (g_isi_msg_data_len(msg) < 2) goto error; if (data[0] != GPDS_OK) { DBG("detach failed: %s", gpds_status_name(data[0])); goto error; } set_attach_mode(cbd->user, FALSE); CALLBACK_WITH_SUCCESS(cb, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, cbd->data); } static void isi_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 isi_cb_data *cbd = isi_cb_data_new(gprs, cb, data); if (cbd == NULL || gd == NULL) goto error; if (attached) { const unsigned char msg[] = { GPDS_ATTACH_REQ, GPDS_FOLLOW_OFF }; if (g_isi_client_send_with_timeout(gd->client, msg, sizeof(msg), GPDS_ATTACH_TIMEOUT, attach_resp_cb, cbd, g_free)) return; } else { const unsigned char msg[] = { GPDS_DETACH_REQ, 0x00, /* filler */ 0x00 /* sub-blocks */ }; if (g_isi_client_send_with_timeout(gd->client, msg, sizeof(msg), GPDS_DETACH_TIMEOUT, detach_resp_cb, cbd, g_free)) return; } error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void status_resp_cb(const GIsiMessage *msg, void *opaque) { struct isi_cb_data *cbd = opaque; ofono_gprs_status_cb_t cb = cbd->cb; struct ofono_gprs *gprs = cbd->data; int status; const uint8_t *data = g_isi_msg_data(msg); if (g_isi_msg_error(msg) < 0) { DBG("ISI message error: %d", g_isi_msg_error(msg)); goto error; } if (g_isi_msg_id(msg) != GPDS_STATUS_RESP) return; if (g_isi_msg_data_len(msg) < 12) goto error; /* FIXME: the core still expects reg status, and not a boolean * attached status here.*/ switch (data[0]) { case GPDS_ATTACHED: status = GPRS_STAT_REGISTERED; break; case GPDS_DETACHED: status = GPRS_STAT_NOT_REGISTERED; break; default: status = GPRS_STAT_UNKNOWN; } suspend_notify(gprs, data[10], data[11]); CALLBACK_WITH_SUCCESS(cb, status, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void isi_gprs_attached_status(struct ofono_gprs *gprs, ofono_gprs_status_cb_t cb, void *data) { struct gprs_data *gd = ofono_gprs_get_data(gprs); struct isi_cb_data *cbd = isi_cb_data_new(NULL, cb, data); const unsigned char msg[] = { GPDS_STATUS_REQ, }; if (cbd == NULL || gd == NULL) goto error; if (g_isi_client_send(gd->client, msg, sizeof(msg), status_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } static struct ofono_gprs_driver driver = { .name = "isimodem", .probe = isi_gprs_probe, .remove = isi_gprs_remove, .set_attached = isi_gprs_set_attached, .attached_status = isi_gprs_attached_status, }; void isi_gprs_init(void) { ofono_gprs_driver_register(&driver); } void isi_gprs_exit(void) { ofono_gprs_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/isimodem.c0000644000015600001650000000374412671500024023645 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "isimodem.h" static int isimodem_init(void) { isi_devinfo_init(); isi_phonebook_init(); isi_netreg_init(); isi_voicecall_init(); isi_sms_init(); isi_cbs_init(); isi_sim_init(); isi_ussd_init(); isi_call_forwarding_init(); isi_call_settings_init(); isi_call_barring_init(); isi_call_meter_init(); isi_radio_settings_init(); isi_gprs_init(); isi_gprs_context_init(); isi_audio_settings_init(); isi_uicc_init(); return 0; } static void isimodem_exit(void) { isi_devinfo_exit(); isi_phonebook_exit(); isi_netreg_exit(); isi_voicecall_exit(); isi_sms_exit(); isi_cbs_exit(); isi_sim_exit(); isi_ussd_exit(); isi_call_forwarding_exit(); isi_call_settings_exit(); isi_call_barring_exit(); isi_call_meter_exit(); isi_radio_settings_exit(); isi_gprs_exit(); isi_gprs_context_exit(); isi_audio_settings_exit(); isi_uicc_exit(); } OFONO_PLUGIN_DEFINE(isimodem, "PhoNet / ISI modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, isimodem_init, isimodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/uicc.c0000644000015600001650000011577512671500024022772 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 ST-Ericsson AB. * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "simutil.h" #include "isimodem.h" #include "isiutil.h" #include "sim.h" #include "uicc.h" #include "uicc-util.h" #include "debug.h" /* File info parameters */ #define FCP_TEMPLATE 0x62 #define FCP_FILE_SIZE 0x80 #define FCP_FILE_DESC 0x82 #define FCP_FILE_ID 0x83 #define FCP_FILE_LIFECYCLE 0x8A #define FCP_FILE_SECURITY_ARR 0x8B #define FCP_FILE_SECURITY_COMPACT 0x8C #define FCP_FILE_SECURITY_EXPANDED 0xAB #define FCP_PIN_STATUS 0xC6 #define SIM_EFARR_FILEID 0x6f06 #define MAX_SIM_APPS 10 #define MAX_IMSI_LENGTH 15 enum uicc_flag { UICC_FLAG_APP_STARTED = 1 << 0, UICC_FLAG_PIN_STATE_RECEIVED = 1 << 1, UICC_FLAG_PASSWD_REQUIRED = 1 << 2, }; static GHashTable *g_modems; struct file_info { int fileid; int length; int structure; int record_length; uint8_t access[3]; uint8_t file_status; }; static const struct file_info static_file_info[] = { { SIM_EFSPN_FILEID, 17, 0, 0, { 0x0e, 0xff, 0xee }, 1 }, { SIM_EF_ICCID_FILEID, 10, 0, 10, { 0x0f, 0xff, 0xee }, 1 }, { SIM_EFPL_FILEID, 1, 0, 1, { 0x0f, 0xff, 0xff }, 1 }, { SIM_EFLI_FILEID, 1, 0, 1, { 0x0f, 0xff, 0xff }, 1 }, { SIM_EFMSISDN_FILEID, 28, 1, 28, { 0x01, 0xff, 0xee }, 1 }, { SIM_EFAD_FILEID, 20, 0, 20, { 0x0e, 0xff, 0xee }, 1 }, { SIM_EFPHASE_FILEID, 1, 0, 1, { 0x0e, 0xff, 0xee }, 1 }, { SIM_EFPNN_FILEID, 4 * 18, 1, 18, { 0x0e, 0xff, 0xee }, 1 }, { SIM_EFOPL_FILEID, 4 * 24, 1, 24, { 0x0e, 0xff, 0xee }, 1 }, { SIM_EFMBI_FILEID, 5, 1, 5, { 0x0e, 0xff, 0xee }, 1 }, { SIM_EFMWIS_FILEID, 6, 1, 6, { 0x01, 0xff, 0xee }, 1 }, { SIM_EFSPDI_FILEID, 64, 0, 64, { 0x0e, 0xff, 0xee }, 1 }, { SIM_EFECC_FILEID, 5 * 3, 0, 3, { 0x0e, 0xff, 0xee }, 1 }, { SIM_EFCBMIR_FILEID, 8 * 4, 0, 4, { 0x01, 0xff, 0xee }, 1 }, { SIM_EFCBMI_FILEID, 8 * 2, 0, 2, { 0x01, 0xff, 0xee }, 1 }, { SIM_EFCBMID_FILEID, 8 * 2, 0, 2, { 0x01, 0xff, 0x11 }, 1 }, { SIM_EFSMSP_FILEID, 56, 1, 56, { 0x01, 0xff, 0xee }, 1 }, { SIM_EFIMSI_FILEID, 9, 0, 9, { 0x0e, 0xff, 0xee }, 1 }, }; static gboolean check_resp(const GIsiMessage *msg, uint8_t msgid, uint8_t service) { uint8_t type; uint8_t cause; if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", g_isi_msg_strerror(msg)); return FALSE; } if (g_isi_msg_id(msg) != msgid) { DBG("Unexpected msg: %s", sim_message_id_name(g_isi_msg_id(msg))); return FALSE; } if (!g_isi_msg_data_get_byte(msg, 1, &cause) || cause != UICC_STATUS_OK) { DBG("Request failed: %s", uicc_status_name(cause)); return FALSE; } if (!g_isi_msg_data_get_byte(msg, 0, &type) || type != service) { DBG("Unexpected service: 0x%02X (0x%02X)", type, service); return FALSE; } return TRUE; } struct uicc_file_info_cb_data { void *cb; void *data; void *user; struct ofono_sim *sim; }; static gboolean decode_uicc_usim_type(GIsiSubBlockIter *iter, uint16_t *length, uint16_t *file_id, uint16_t *record_length, uint8_t *records, uint8_t *structure) { uint8_t fcp = 0; uint8_t desc = 0; uint8_t coding = 0; uint8_t fcp_len = 0; uint8_t read = 0; uint8_t item_len = 0; if (!g_isi_sb_iter_get_byte(iter, &fcp, 8)) return FALSE; if (fcp != FCP_TEMPLATE) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &fcp_len, 9)) return FALSE; for (read = 0; read < fcp_len; read += item_len + 2) { uint8_t id; if (!g_isi_sb_iter_get_byte(iter, &id, read + 10)) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &item_len, read + 11)) return FALSE; switch (id) { case FCP_FILE_SIZE: if (item_len != 2) return FALSE; if (!g_isi_sb_iter_get_word(iter, length, read + 10 + 2)) return FALSE; break; case FCP_FILE_ID: if (item_len != 2) return FALSE; if (!g_isi_sb_iter_get_word(iter, file_id, read + 10 + 2)) return FALSE; break; case FCP_FILE_DESC: if (item_len < 2) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &desc, read + 10 + 2)) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &coding, read + 10 + 3)) return FALSE; if (item_len < 4) break; if (!g_isi_sb_iter_get_word(iter, record_length, read + 10 + 4)) return FALSE; if (!g_isi_sb_iter_get_byte(iter, records, read + 10 + 6)) return FALSE; break; /* * Not implemented, using static access rules * as these are used only for cacheing See * ETSI TS 102 221, ch 11.1.1.4.7 and Annexes * E, F and G. */ case FCP_FILE_SECURITY_ARR: case FCP_FILE_SECURITY_COMPACT: case FCP_FILE_SECURITY_EXPANDED: case FCP_FILE_LIFECYCLE: default: DBG("FCP id %02X not supported", id); break; } } if ((desc & 7) == 1) *structure = OFONO_SIM_FILE_STRUCTURE_TRANSPARENT; else if ((desc & 7) == 2) *structure = OFONO_SIM_FILE_STRUCTURE_FIXED; else if ((desc & 7) == 6) *structure = OFONO_SIM_FILE_STRUCTURE_CYCLIC; return TRUE; } static void uicc_file_info_resp_cb(const GIsiMessage *msg, void *opaque) { struct uicc_file_info_cb_data *cbd = opaque; struct uicc_sim_data *sd = ofono_sim_get_data(cbd->sim); struct file_info const *info = cbd->user; ofono_sim_file_info_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint16_t length = 0; uint16_t record_length = 0; uint8_t structure = 0xFF; uint8_t records = 0; uint16_t file_id = 0; uint8_t access[3] = {0, 0, 0}; uint8_t item_len = 0; uint8_t message_id = 0; uint8_t service_type = 0; uint8_t status = 0; uint8_t details = 0; uint8_t num_subblocks = 0; uint8_t file_status = 1; message_id = g_isi_msg_id(msg); DBG("uicc_file_info_resp_cb: msg_id=%d, msg len=%zu", message_id, g_isi_msg_data_len(msg)); if (message_id != UICC_APPL_CMD_RESP) goto error; if (!g_isi_msg_data_get_byte(msg, 0, &service_type) || !g_isi_msg_data_get_byte(msg, 1, &status) || !g_isi_msg_data_get_byte(msg, 2, &details) || !g_isi_msg_data_get_byte(msg, 5, &num_subblocks)) goto error; DBG("%s, service %s, status %s, details %s, nm_sb %d", uicc_message_id_name(message_id), uicc_service_type_name(service_type), uicc_status_name(status), uicc_details_name(details), num_subblocks); if (info) { access[0] = info->access[0]; access[1] = info->access[1]; access[2] = info->access[2]; file_status = info->file_status; } for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_subblocks); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { uint8_t sb_id = g_isi_sb_iter_get_id(&iter); DBG("Subblock %s", uicc_subblock_name(sb_id)); if (sb_id != UICC_SB_FCI) continue; DBG("Decoding UICC_SB_FCI"); switch (sd->app_type) { case UICC_APPL_TYPE_UICC_USIM: DBG("UICC_APPL_TYPE_UICC_USIM"); if (!decode_uicc_usim_type(&iter, &length, &file_id, &record_length, &records, &structure)) goto error; break; case UICC_APPL_TYPE_ICC_SIM: DBG("UICC_APPL_TYPE_ICC_SIM"); if (!g_isi_sb_iter_get_word(&iter, &length, 10)) goto error; if (!g_isi_sb_iter_get_word(&iter, &file_id, 12)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &access[0], 16)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &access[0], 17)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &access[0], 18)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &item_len, 20)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &structure, 21)) goto error; if (item_len == 2) { uint8_t byte; if (!g_isi_sb_iter_get_byte(&iter, &byte, 22)) goto error; record_length = byte; } break; default: DBG("Application type %d not supported", sd->app_type); break; } DBG("fileid=%04X, filelen=%d, records=%d, reclen=%d, structure=%d", file_id, length, records, record_length, structure); CALLBACK_WITH_SUCCESS(cb, length, structure, record_length, access, file_status, cbd->data); return; } error: DBG("Error reading file info"); CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, cbd->data); } static gboolean send_uicc_read_file_info(GIsiClient *client, uint8_t app_id, int fileid, uint8_t df_len, int mf_path, int df1_path, int df2_path, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { const uint8_t msg[] = { UICC_APPL_CMD_REQ, UICC_APPL_FILE_INFO, /* Service type */ app_id, UICC_SESSION_ID_NOT_USED, 0, 0, /* Filler */ 1, /* Number of subblocks */ ISI_16BIT(UICC_SB_APPL_PATH), ISI_16BIT(16), /* Subblock length */ ISI_16BIT(fileid), uicc_get_sfi(fileid), /* Elementary file short file id */ 0, /* Filler */ df_len, 0, /* Filler */ ISI_16BIT(mf_path), ISI_16BIT(df1_path), ISI_16BIT(df2_path), }; return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy); } static void uicc_read_file_info(struct ofono_sim *sim, int fileid, const unsigned char *path, unsigned int path_len, ofono_sim_file_info_cb_t cb, void *data) { struct uicc_sim_data *sd = ofono_sim_get_data(sim); struct uicc_file_info_cb_data *cbd; /* Prepare for static file info used for access rights */ int i; int N = sizeof(static_file_info) / sizeof(static_file_info[0]); int mf_path = 0; int df1_path = 0; int df2_path = 0; uint8_t df_len = 0; cbd = g_try_new0(struct uicc_file_info_cb_data, 1); if (!cbd) goto error; cbd->cb = cb; cbd->data = data; cbd->sim = sim; cbd->user = NULL; DBG("File info for ID=%04X app id %d", fileid, sd->app_id); for (i = 0; i < N; i++) { if (fileid == static_file_info[i].fileid) { cbd->user = (void *) &static_file_info[i]; break; } } DBG("File info for ID=%04X: %p", fileid, cbd->user); if (!uicc_get_fileid_path(sd, &mf_path, &df1_path, &df2_path, &df_len, fileid)) goto error; if (send_uicc_read_file_info(sd->client, sd->app_id, fileid, df_len, mf_path, df1_path, df2_path, uicc_file_info_resp_cb, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, data); g_free(cbd); } static void uicc_read_file_transp_resp_cb(const GIsiMessage *msg, void *opaque) { struct isi_cb_data *cbd = opaque; ofono_sim_read_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint32_t filelen = 0; uint8_t *filedata = NULL; uint8_t num_sb = 0; DBG(""); if (!check_resp(msg, UICC_APPL_CMD_RESP, UICC_APPL_READ_TRANSPARENT)) goto error; if (!g_isi_msg_data_get_byte(msg, 5, &num_sb)) goto error; for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { int sb_id = g_isi_sb_iter_get_id(&iter); DBG("Subblock %s", uicc_subblock_name(sb_id)); if (sb_id != UICC_SB_FILE_DATA) continue; if (!g_isi_sb_iter_get_dword(&iter, &filelen, 4)) goto error; if (!g_isi_sb_iter_get_struct(&iter, (void **) &filedata, filelen, 8)) goto error; DBG("Transparent EF read: 1st byte %02x, len %d", filedata[0], filelen); CALLBACK_WITH_SUCCESS(cb, filedata, filelen, cbd->data); return; } error: DBG("Error reading transparent EF"); CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); } static gboolean send_uicc_read_file_transparent(GIsiClient *client, uint8_t app_id, uint8_t client_id, int fileid, uint8_t df_len, int mf_path, int df1_path, int df2_path, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { const uint8_t msg[] = { UICC_APPL_CMD_REQ, UICC_APPL_READ_TRANSPARENT, app_id, UICC_SESSION_ID_NOT_USED, 0, 0, /* Filler */ 3, /* Number of subblocks */ ISI_16BIT(UICC_SB_CLIENT), ISI_16BIT(8), /* Subblock length*/ 0, 0, 0, /* Filler */ client_id, ISI_16BIT(UICC_SB_TRANSPARENT), ISI_16BIT(8), /* Subblock length */ ISI_16BIT(0), /* File offset */ ISI_16BIT(0), /* Data amount (0=all) */ ISI_16BIT(UICC_SB_APPL_PATH), ISI_16BIT(16), /* Subblock length */ ISI_16BIT(fileid), uicc_get_sfi(fileid), /* Elementary file short file id */ 0, /* Filler */ df_len, 0, ISI_16BIT(mf_path), ISI_16BIT(df1_path), ISI_16BIT(df2_path), }; return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy); } static void uicc_read_file_transparent(struct ofono_sim *sim, int fileid, int start, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_cb_t cb, void *data) { struct uicc_sim_data *sd = ofono_sim_get_data(sim); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); int mf_path = 0; int df1_path = 0; int df2_path = 0; uint8_t df_len = 0; if (!cbd || !sd) goto error; DBG("File ID=%04X, client %d, AID %d", fileid, sd->client_id, sd->app_id); if (!uicc_get_fileid_path(sd, &mf_path, &df1_path, &df2_path, &df_len, fileid)) goto error; if (send_uicc_read_file_transparent(sd->client, sd->app_id, sd->client_id, fileid, df_len, mf_path, df1_path, df2_path, uicc_read_file_transp_resp_cb, cbd, g_free)) return; error: DBG("Read file transparent failed"); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); g_free(cbd); } static void read_file_linear_resp(const GIsiMessage *msg, void *opaque) { struct isi_cb_data *cbd = opaque; ofono_sim_read_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint8_t num_sb = 0; uint8_t *filedata = NULL; uint32_t filelen = 0; DBG(""); if (!check_resp(msg, UICC_APPL_CMD_RESP, UICC_APPL_READ_LINEAR_FIXED)) goto error; if (!g_isi_msg_data_get_byte(msg, 5, &num_sb)) goto error; for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { uint8_t sb_id = g_isi_sb_iter_get_id(&iter); DBG("Subblock %s", uicc_subblock_name(sb_id)); if (sb_id != UICC_SB_FILE_DATA) continue; if (!g_isi_sb_iter_get_dword(&iter, &filelen, 4)) goto error; if (!g_isi_sb_iter_get_struct(&iter, (void **) &filedata, filelen, 8)) goto error; DBG("Linear fixed EF read: 1st byte %02x, len %d", filedata[0], filelen); CALLBACK_WITH_SUCCESS(cb, filedata, filelen, cbd->data); return; } error: CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); } static gboolean send_uicc_read_file_linear(GIsiClient *client, uint8_t app_id, uint8_t client_id, int fileid, int record, int rec_length, unsigned char df_len, int mf_path, int df1_path, int df2_path, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { const uint8_t msg[] = { UICC_APPL_CMD_REQ, UICC_APPL_READ_LINEAR_FIXED, app_id, UICC_SESSION_ID_NOT_USED, 0, 0, /* Filler */ 3, /* Number of subblocks */ ISI_16BIT(UICC_SB_CLIENT), ISI_16BIT(8), /*Subblock length */ 0, 0, 0, /* Filler */ client_id, ISI_16BIT(UICC_SB_LINEAR_FIXED), ISI_16BIT(8), /*Subblock length */ record, 0, /* Record offset */ rec_length & 0xff, /*Data amount (0=all)*/ 0, ISI_16BIT(UICC_SB_APPL_PATH), ISI_16BIT(16), /* Subblock length */ ISI_16BIT(fileid), uicc_get_sfi(fileid), /* Elementary file short file id */ 0, /* Filler */ df_len, 0, ISI_16BIT(mf_path), ISI_16BIT(df1_path), ISI_16BIT(df2_path), }; return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy); } static void uicc_read_file_linear(struct ofono_sim *sim, int fileid, int record, int rec_length, const unsigned char *path, unsigned int path_len, ofono_sim_read_cb_t cb, void *data) { struct uicc_sim_data *sd = ofono_sim_get_data(sim); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); int mf_path = 0; int df1_path = 0; int df2_path = 0; uint8_t df_len = 0; if (!sd || !cbd) goto error; DBG("File ID=%04X, record %d, client %d AID %d", fileid, record, sd->client_id, sd->app_id); if (!uicc_get_fileid_path(sd, &mf_path, &df1_path, &df2_path, &df_len, fileid)) goto error; if (send_uicc_read_file_linear(sd->client, sd->app_id, sd->client_id, fileid, record, rec_length, df_len, mf_path, df1_path, df2_path, read_file_linear_resp, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, NULL, 0, data); g_free(cbd); } static void uicc_read_file_cyclic(struct ofono_sim *sim, int fileid, int record, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_cb_t cb, void *data) { DBG("Not implemented"); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } static void uicc_write_file_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 *data) { DBG("Not implemented"); CALLBACK_WITH_FAILURE(cb, data); } static void uicc_write_file_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 *data) { DBG("Not implemented"); CALLBACK_WITH_FAILURE(cb, data); } static void uicc_write_file_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 *data) { DBG("Not implemented"); CALLBACK_WITH_FAILURE(cb, data); } static gboolean decode_imsi(uint8_t *data, int len, char *imsi) { int i = 1; /* Skip first byte, the length field */ int j = 0; if (data == NULL || len == 0) return FALSE; if (data[0] != 8 || data[0] > len) return FALSE; /* Ignore low-order semi-octet of the first byte */ imsi[j] = ((data[i] & 0xF0) >> 4) + '0'; for (i++, j++; i - 1 < data[0] && j < MAX_IMSI_LENGTH; i++) { char nibble; imsi[j++] = (data[i] & 0x0F) + '0'; nibble = (data[i] & 0xF0) >> 4; if (nibble != 0x0F) imsi[j++] = nibble + '0'; } imsi[j] = '\0'; return TRUE; } static void uicc_read_imsi_resp(const GIsiMessage *msg, void *opaque) { struct isi_cb_data *cbd = opaque; ofono_sim_imsi_cb_t cb = cbd->cb; GIsiSubBlockIter iter; uint32_t filelen = 0; uint8_t *filedata = NULL; uint8_t num_sb = 0; char imsi[MAX_IMSI_LENGTH + 1] = { 0 }; DBG(""); if (!check_resp(msg, UICC_APPL_CMD_RESP, UICC_APPL_READ_TRANSPARENT)) goto error; if (!g_isi_msg_data_get_byte(msg, 5, &num_sb)) goto error; for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { int sb_id = g_isi_sb_iter_get_id(&iter); DBG("Subblock %s", uicc_subblock_name(sb_id)); if (sb_id != UICC_SB_FILE_DATA) continue; if (!g_isi_sb_iter_get_dword(&iter, &filelen, 4)) goto error; if (!g_isi_sb_iter_get_struct(&iter, (void **) &filedata, filelen, 8)) goto error; DBG("Transparent EF read: 1st byte %02x, len %d", filedata[0], filelen); if (!decode_imsi(filedata, filelen, imsi)) goto error; DBG("IMSI %s", imsi); CALLBACK_WITH_SUCCESS(cb, imsi, cbd->data); return; } error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void uicc_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb, void *data) { struct uicc_sim_data *sd = ofono_sim_get_data(sim); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); int mf_path = 0; int df1_path = 0; int df2_path = 0; uint8_t df_len = 0; if (!cbd) goto error; DBG("Client %d, AID %d", sd->client_id, sd->app_id); if (!uicc_get_fileid_path(sd, &mf_path, &df1_path, &df2_path, &df_len, SIM_EFIMSI_FILEID)) goto error; if (send_uicc_read_file_transparent(sd->client, sd->app_id, sd->client_id, SIM_EFIMSI_FILEID, df_len, mf_path, df1_path, df2_path, uicc_read_imsi_resp, cbd, g_free)) return; error: CALLBACK_WITH_FAILURE(cb, NULL, data); g_free(cbd); } static void uicc_query_passwd_state_resp(const GIsiMessage *msg, void *opaque) { struct isi_cb_data *cbd = opaque; ofono_sim_passwd_cb_t cb = cbd->cb; uint8_t type; uint8_t cause; DBG(""); if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", g_isi_msg_strerror(msg)); goto error; } if (g_isi_msg_id(msg) != UICC_PIN_RESP) { DBG("Unexpected msg: %s", sim_message_id_name(g_isi_msg_id(msg))); goto error; } if (!g_isi_msg_data_get_byte(msg, 0, &type) || type != UICC_PIN_PROMPT_VERIFY) { DBG("Unexpected service: 0x%02X (0x%02X)", type, UICC_PIN_PROMPT_VERIFY); goto error; } if (!g_isi_msg_data_get_byte(msg, 1, &cause)) goto error; DBG("Status: %d %s", cause, uicc_status_name(cause)); if (cause == UICC_STATUS_PIN_DISABLED) { CALLBACK_WITH_SUCCESS(cb, OFONO_SIM_PASSWORD_NONE, cbd->data); return; } DBG("Request failed or not implemented: %s", uicc_status_name(cause)); error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void uicc_query_passwd_state(struct ofono_sim *sim, ofono_sim_passwd_cb_t cb, void *data) { struct uicc_sim_data *sd = ofono_sim_get_data(sim); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); const uint8_t req[] = { UICC_PIN_REQ, UICC_PIN_PROMPT_VERIFY, sd->app_id, 0, 0, 0, /* Filler */ 1, /* Number of subblocks */ ISI_16BIT(UICC_SB_PIN_REF), ISI_16BIT(8), /*Sub block length*/ sd->pin1_id, /* Pin ID */ 0, 0, 0, /* Filler */ }; DBG(""); if (g_isi_client_send(sd->client, req, sizeof(req), uicc_query_passwd_state_resp, cbd, g_free)) return; CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } static void uicc_send_passwd(struct ofono_sim *sim, const char *passwd, ofono_sim_lock_unlock_cb_t cb, void *data) { DBG("Not implemented"); CALLBACK_WITH_FAILURE(cb, data); } static void uicc_query_pin_retries_resp(const GIsiMessage *msg, void *opaque) { struct isi_cb_data *cbd = opaque; ofono_sim_pin_retries_cb_t cb = cbd->cb; int retries[OFONO_SIM_PASSWORD_INVALID]; GIsiSubBlockIter iter; uint8_t num_sb = 0; uint8_t pins = 0; uint8_t pina = 0; uint8_t puka = 0; DBG(""); if (!check_resp(msg, UICC_PIN_RESP, UICC_PIN_INFO)) goto error; if (!g_isi_msg_data_get_byte(msg, 5, &num_sb)) goto error; DBG("Subblock count %d", num_sb); for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { uint8_t sb_id = g_isi_sb_iter_get_id(&iter); DBG("Sub-block %s", uicc_subblock_name(sb_id)); if (sb_id != UICC_SB_PIN_INFO) continue; if (!g_isi_sb_iter_get_byte(&iter, &pins, 4)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &pina, 5)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &puka, 6)) goto error; DBG("PIN status %X PIN Attrib %d PUK attrib %d", pins, pina, puka); retries[OFONO_SIM_PASSWORD_SIM_PIN] = pina; retries[OFONO_SIM_PASSWORD_SIM_PUK] = puka; CALLBACK_WITH_SUCCESS(cb, retries, cbd->data); return; } error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void uicc_query_pin_retries(struct ofono_sim *sim, ofono_sim_pin_retries_cb_t cb, void *data) { struct uicc_sim_data *sd = ofono_sim_get_data(sim); struct isi_cb_data *cbd = isi_cb_data_new(sim, cb, data); const uint8_t req[] = { UICC_PIN_REQ, UICC_PIN_INFO, sd->app_id, 0, 0, 0, /* Filler */ 1, /* Number of subblocks */ ISI_16BIT(UICC_SB_PIN_REF), ISI_16BIT(8), /* Subblock length */ sd->pin1_id, /* Pin ID */ 0, 0, 0, /* Filler */ }; DBG(""); if (g_isi_client_send(sd->client, req, sizeof(req), uicc_query_pin_retries_resp, cbd, g_free)) return; CALLBACK_WITH_FAILURE(cb, NULL, data); g_free(cbd); } static void uicc_reset_passwd(struct ofono_sim *sim, const char *puk, const char *passwd, ofono_sim_lock_unlock_cb_t cb, void *data) { DBG("Not implemented"); CALLBACK_WITH_FAILURE(cb, data); } static void uicc_change_passwd(struct ofono_sim *sim, enum ofono_sim_password_type passwd_type, const char *old, const char *new, ofono_sim_lock_unlock_cb_t cb, void *data) { DBG("Not implemented"); CALLBACK_WITH_FAILURE(cb, data); } static void uicc_lock(struct ofono_sim *sim, enum ofono_sim_password_type type, int enable, const char *passwd, ofono_sim_lock_unlock_cb_t cb, void *data) { DBG("Not implemented"); CALLBACK_WITH_FAILURE(cb, data); } static void uicc_query_locked(struct ofono_sim *sim, enum ofono_sim_password_type type, ofono_sim_locked_cb_t cb, void *data) { DBG("Not implemented"); CALLBACK_WITH_FAILURE(cb, -1, data); } static gboolean decode_fcp_pin_status(const GIsiSubBlockIter *iter, uint8_t read, uint8_t *pin1, uint8_t *pin2) { uint8_t do_len; uint8_t len; uint8_t tag; uint8_t id; uint8_t tag_pos; DBG("Decoding PIN status"); if (!g_isi_sb_iter_get_byte(iter, &do_len, read)) return FALSE; tag_pos = read + 1 + do_len; if (!g_isi_sb_iter_get_byte(iter, &tag, tag_pos)) return FALSE; while (tag == 0x83) { if (!g_isi_sb_iter_get_byte(iter, &len, tag_pos + 1)) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &id, tag_pos + 2)) return FALSE; tag_pos += 2 + len; if (!g_isi_sb_iter_get_byte(iter, &tag, tag_pos)) return FALSE; DBG("PIN_len %d, PIN id %02x, PIN tag %02x", len, id, tag); if (id >= 0x01 && id <= 0x08) *pin1 = id; else if (id >= 0x81 && id <= 0x88) *pin2 = id; } return TRUE; } static gboolean decode_fci_sb(const GIsiSubBlockIter *iter, int app_type, uint8_t *pin1, uint8_t *pin2) { uint8_t fcp = 0; uint8_t fcp_len = 0; uint8_t read = 0; uint8_t item_len = 0; DBG("Decoding UICC_SB_FCI"); if (app_type != UICC_APPL_TYPE_UICC_USIM) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &fcp, 8)) return FALSE; if (fcp != FCP_TEMPLATE) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &fcp_len, 9)) return FALSE; for (read = 0; read < fcp_len; read += item_len + 2) { uint8_t id; if (!g_isi_sb_iter_get_byte(iter, &id, read + 10)) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &item_len, read + 11)) return FALSE; if (id != FCP_PIN_STATUS) continue; if (!decode_fcp_pin_status(iter, read + 13, pin1, pin2)) return FALSE; } return TRUE; } static gboolean decode_chv_sb(const GIsiSubBlockIter *iter, int app_type, uint8_t *pin1, uint8_t *pin2) { uint8_t chv_id = 0; uint8_t pin_id = 0; DBG("Decoding UICC_SB_CHV"); if (app_type != UICC_APPL_TYPE_ICC_SIM) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &chv_id, 4)) return FALSE; if (!g_isi_sb_iter_get_byte(iter, &pin_id, 5)) return FALSE; switch (chv_id) { case 1: *pin1 = pin_id; break; case 2: *pin2 = pin_id; break; default: return FALSE; } DBG("CHV=%d, pin_id=%2x, PIN1 %02x, PIN2 %02x", chv_id, pin_id, *pin1, *pin2); return TRUE; } static void uicc_application_activate_resp(const GIsiMessage *msg, void *opaque) { struct ofono_sim *sim = opaque; struct uicc_sim_data *sd = ofono_sim_get_data(sim); GIsiSubBlockIter iter; uint8_t cause, num_sb; DBG(""); if (g_isi_msg_error(msg) < 0) { DBG("Error: %s", g_isi_msg_strerror(msg)); return; } if (g_isi_msg_id(msg) != UICC_APPLICATION_RESP) { DBG("Unexpected msg: %s", sim_message_id_name(g_isi_msg_id(msg))); return; } if (!g_isi_msg_data_get_byte(msg, 1, &cause)) return; if (cause != UICC_STATUS_OK && cause != UICC_STATUS_APPL_ACTIVE) { DBG("TODO: handle application activation"); return; } if (!sd->uicc_app_started) { sd->app_id = sd->trying_app_id; sd->app_type = sd->trying_app_type; sd->uicc_app_started = TRUE; DBG("UICC application activated"); ofono_sim_inserted_notify(sim, TRUE); ofono_sim_register(sim); g_hash_table_remove_all(sd->app_table); } if (!g_isi_msg_data_get_byte(msg, 5, &num_sb)) return; for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { uint8_t sb_id = g_isi_sb_iter_get_id(&iter); DBG("Subblock %s", uicc_subblock_name(sb_id)); switch (sb_id) { case UICC_SB_CLIENT: if (!g_isi_sb_iter_get_byte(&iter, &sd->client_id, 7)) return; DBG("Client id %d", sd->client_id); break; case UICC_SB_FCI: if (!decode_fci_sb(&iter, sd->app_type, &sd->pin1_id, &sd->pin2_id)) return; DBG("PIN1 %02x, PIN2 %02x", sd->pin1_id, sd->pin2_id); break; case UICC_SB_CHV: if (!decode_chv_sb(&iter, sd->app_type, &sd->pin1_id, &sd->pin2_id)) return; DBG("PIN1 %02x, PIN2 %02x", sd->pin1_id, sd->pin2_id); break; default: DBG("Skipping sub-block: %s (%zu bytes)", uicc_subblock_name(g_isi_sb_iter_get_id(&iter)), g_isi_sb_iter_get_len(&iter)); break; } } } static gboolean send_application_activate_req(GIsiClient *client, uint8_t app_type, uint8_t app_id, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { const uint8_t msg[] = { UICC_APPLICATION_REQ, UICC_APPL_HOST_ACTIVATE, 2, /* Number of subblocks */ ISI_16BIT(UICC_SB_APPLICATION), ISI_16BIT(8), /* Subblock length */ 0, 0, /* Filler */ app_type, app_id, ISI_16BIT(UICC_SB_APPL_INFO), ISI_16BIT(8), /* Subblock length */ 0, 0, 0, /* Filler */ /* * Next field indicates whether the application * initialization procedure will follow the activation * or not */ UICC_APPL_START_UP_INIT_PROC, }; DBG("App type %d, AID %d", app_type, app_id); return g_isi_client_send(client, msg, sizeof(msg), notify, data, destroy); } static void uicc_application_list_resp(const GIsiMessage *msg, void *data) { struct ofono_sim *sim = data; struct uicc_sim_data *sd = ofono_sim_get_data(sim); GIsiSubBlockIter iter; uint8_t num_sb; struct uicc_sim_application *sim_app; /* Throw away old app table */ g_hash_table_remove_all(sd->app_table); if (!check_resp(msg, UICC_APPLICATION_RESP, UICC_APPL_LIST)) goto error; if (!g_isi_msg_data_get_byte(msg, 5, &num_sb)) goto error; /* Iterate through the application list */ for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { uint8_t app_type; uint8_t app_id; uint8_t app_status; uint8_t app_len; if (g_isi_sb_iter_get_id(&iter) != UICC_SB_APPL_DATA_OBJECT) continue; if (!g_isi_sb_iter_get_byte(&iter, &app_type, 6)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &app_id, 7)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &app_status, 8)) goto error; if (!g_isi_sb_iter_get_byte(&iter, &app_len, 9)) goto error; if (app_type != UICC_APPL_TYPE_ICC_SIM && app_type != UICC_APPL_TYPE_UICC_USIM) continue; sim_app = g_try_new0(struct uicc_sim_application, 1); if (!sim_app) { DBG("out of memory!"); goto error; } sim_app->type = app_type; sim_app->id = app_id; sim_app->status = app_status; sim_app->length = app_len; sim_app->sim = sd; g_hash_table_replace(sd->app_table, &sim_app->id, sim_app); } if (!sd->uicc_app_started) { GHashTableIter app_iter; struct uicc_sim_application *app; gpointer key; gpointer value; g_hash_table_iter_init(&app_iter, sd->app_table); if (!g_hash_table_iter_next(&app_iter, &key, &value)) return; app = value; sd->trying_app_type = app->type; sd->trying_app_id = app->id; g_hash_table_remove(sd->app_table, &app->id); if (!send_application_activate_req(sd->client, app->type, app->id, uicc_application_activate_resp, data, NULL)) { DBG("Failed to activate: 0x%02X (type=0x%02X)", app->id, app->type); return; } } return; error: DBG("Decoding application list failed"); g_isi_client_destroy(sd->client); sd->client = NULL; ofono_sim_remove(sim); } static void uicc_card_status_resp(const GIsiMessage *msg, void *data) { struct ofono_sim *sim = data; struct uicc_sim_data *sd = ofono_sim_get_data(sim); GIsiSubBlockIter iter; uint8_t card_status = 0; uint8_t num_sb = 0; DBG(""); if (!sd->server_running) return; if (!check_resp(msg, UICC_CARD_RESP, UICC_CARD_STATUS_GET)) goto error; if (!g_isi_msg_data_get_byte(msg, 1, &card_status)) goto error; if (!g_isi_msg_data_get_byte(msg, 5, &num_sb)) goto error; DBG("Subblock count %d", num_sb); for (g_isi_sb_iter_init_full(&iter, msg, 6, TRUE, num_sb); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { if (g_isi_sb_iter_get_id(&iter) != UICC_SB_CARD_STATUS) continue; if (!g_isi_sb_iter_get_byte(&iter, &card_status, 7)) goto error; DBG("card_status = 0x%X", card_status); /* Check if card is ready */ if (card_status == 0x21) { const uint8_t req[] = { UICC_APPLICATION_REQ, UICC_APPL_LIST, 0, /* Number of subblocks */ }; DBG("card is ready"); ofono_sim_inserted_notify(sim, TRUE); if (g_isi_client_send(sd->client, req, sizeof(req), uicc_application_list_resp, data, NULL)) return; DBG("Failed to query application list"); goto error; } else { DBG("card not ready"); ofono_sim_inserted_notify(sim, FALSE); return; } } error: g_isi_client_destroy(sd->client); sd->client = NULL; ofono_sim_remove(sim); } static void uicc_card_status_req(struct ofono_sim *sim, struct uicc_sim_data *sd) { const uint8_t req[] = { UICC_CARD_REQ, UICC_CARD_STATUS_GET, 0, }; DBG(""); if (g_isi_client_send(sd->client, req, sizeof(req), uicc_card_status_resp, sim, NULL)) return; g_isi_client_destroy(sd->client); sd->client = NULL; ofono_sim_remove(sim); } static void uicc_card_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_sim *sim = data; struct uicc_sim_data *sd = ofono_sim_get_data(sim); DBG(""); if (g_isi_msg_id(msg) != UICC_CARD_IND) return; /* We're not interested in card indications if server isn't running */ if (!sd->server_running) return; /* Request card status */ uicc_card_status_req(sim, sd); } static void uicc_status_resp(const GIsiMessage *msg, void *data) { struct ofono_sim *sim = data; struct uicc_sim_data *sd = ofono_sim_get_data(sim); uint8_t status = 0, server_status = 0; gboolean server_running = FALSE; if (!check_resp(msg, UICC_RESP, UICC_STATUS_GET)) goto error; if (g_isi_msg_error(msg) < 0) goto error; if (!g_isi_msg_data_get_byte(msg, 1, &status) || !g_isi_msg_data_get_byte(msg, 3, &server_status)) goto error; DBG("status=0x%X, server_status=0x%X", status, server_status); if (status == UICC_STATUS_OK && server_status == UICC_STATUS_START_UP_COMPLETED) { DBG("server is up!"); server_running = TRUE; } if (!server_running) { sd->server_running = FALSE; /* TODO: Remove SIM etc... */ return; } if (sd->server_running && server_running) { DBG("Server status didn't change..."); return; } /* Server is running */ sd->server_running = TRUE; /* Request card status */ uicc_card_status_req(sim, sd); return; error: g_isi_client_destroy(sd->client); sd->client = NULL; ofono_sim_remove(sim); } static void uicc_ind_cb(const GIsiMessage *msg, void *data) { struct ofono_sim *sim = data; struct uicc_sim_data *sd = ofono_sim_get_data(sim); const uint8_t req[] = { UICC_REQ, UICC_STATUS_GET, 0 }; int msg_id = g_isi_msg_id(msg); DBG("%s", uicc_message_id_name(msg_id)); if (msg_id != UICC_IND) return; /* Request status */ if (g_isi_client_send(sd->client, req, sizeof(req), uicc_status_resp, data, NULL)) return; DBG("status request failed!"); g_isi_client_destroy(sd->client); sd->client = NULL; ofono_sim_remove(sim); } static void uicc_reachable_cb(const GIsiMessage *msg, void *data) { struct ofono_sim *sim = data; struct uicc_sim_data *sd = ofono_sim_get_data(sim); const uint8_t req[] = { UICC_REQ, UICC_STATUS_GET, 0, /* Number of Sub Blocks (only from version 4.0) */ }; ISI_RESOURCE_DBG(msg); if (g_isi_msg_error(msg) < 0) goto error; sd->version.major = g_isi_msg_version_major(msg); sd->version.minor = g_isi_msg_version_minor(msg); /* UICC server is reachable: request indications */ g_isi_client_ind_subscribe(sd->client, UICC_IND, uicc_ind_cb, sim); g_isi_client_ind_subscribe(sd->client, UICC_CARD_IND, uicc_card_ind_cb, sim); /* Update status */ if (g_isi_client_send(sd->client, req, sizeof(req) - ((sd->version.major < 4) ? 1 : 0), uicc_status_resp, data, NULL)) return; error: g_isi_client_destroy(sd->client); sd->client = NULL; ofono_sim_remove(sim); } static void sim_app_destroy(gpointer p) { struct uicc_sim_application *app = p; if (!app) return; g_free(app); } static int uicc_sim_probe(struct ofono_sim *sim, unsigned int vendor, void *user) { GIsiModem *modem = user; struct uicc_sim_data *sd; sd = g_try_new0(struct uicc_sim_data, 1); if (sd == NULL) return -ENOMEM; /* Create hash table for the UICC applications */ sd->app_table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, sim_app_destroy); if (sd->app_table == NULL) { g_free(sd); return -ENOMEM; } sd->client = g_isi_client_create(modem, PN_UICC); if (sd->client == NULL) { g_hash_table_destroy(sd->app_table); g_free(sd); return -ENOMEM; } g_hash_table_insert(g_modems, g_isi_client_modem(sd->client), sim); sd->server_running = FALSE; sd->uicc_app_started = FALSE; sd->pin_state_received = FALSE; sd->passwd_required = TRUE; ofono_sim_set_data(sim, sd); g_isi_client_verify(sd->client, uicc_reachable_cb, sim, NULL); return 0; } static void uicc_sim_remove(struct ofono_sim *sim) { struct uicc_sim_data *data = ofono_sim_get_data(sim); ofono_sim_set_data(sim, NULL); if (data == NULL) return; g_hash_table_remove(g_modems, g_isi_client_modem(data->client)); g_hash_table_destroy(data->app_table); g_isi_client_destroy(data->client); g_free(data); } static struct ofono_sim_driver driver = { .name = "wgmodem2.5", .probe = uicc_sim_probe, .remove = uicc_sim_remove, .read_file_info = uicc_read_file_info, .read_file_transparent = uicc_read_file_transparent, .read_file_linear = uicc_read_file_linear, .read_file_cyclic = uicc_read_file_cyclic, .write_file_transparent = uicc_write_file_transparent, .write_file_linear = uicc_write_file_linear, .write_file_cyclic = uicc_write_file_cyclic, .read_imsi = uicc_read_imsi, .query_passwd_state = uicc_query_passwd_state, .send_passwd = uicc_send_passwd, .query_pin_retries = uicc_query_pin_retries, .reset_passwd = uicc_reset_passwd, .change_passwd = uicc_change_passwd, .lock = uicc_lock, .query_locked = uicc_query_locked, }; void isi_uicc_init(void) { g_modems = g_hash_table_new(g_direct_hash, g_direct_equal); ofono_sim_driver_register(&driver); } void isi_uicc_exit(void) { g_hash_table_destroy(g_modems); ofono_sim_driver_unregister(&driver); } gboolean isi_uicc_properties(GIsiModem *modem, int *app_id, int *app_type, int *client_id) { struct ofono_sim *sim; struct uicc_sim_data *sd; sim = g_hash_table_lookup(g_modems, modem); if (sim == NULL) return FALSE; sd = ofono_sim_get_data(sim); if (sd == NULL) return FALSE; if (app_id != NULL) *app_id = sd->app_id; if (app_type != NULL) *app_type = sd->app_type; if (client_id != NULL) *client_id = sd->client_id; return TRUE; } ofono-1.17.bzr6912+16.04.20160314.3/drivers/isimodem/gprs-context.c0000644000015600001650000003646012671500024024475 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "isimodem.h" #include "isiutil.h" #include "gpds.h" #include "debug.h" #define STATIC_IP_NETMASK "255.255.255.255" #define INVALID_ID (0xff) # if (INVALID_ID < GPDS_MAX_CONTEXT_COUNT) # error Uho! This should not happen! #endif struct context_data { GIsiClient *client; GIsiModem *idx; uint16_t gpds; /* GPDS object handle */ unsigned cid; /* oFono core context ID */ struct ofono_gprs_context *context; ofono_gprs_context_cb_t cb; void *data; GIsiPEP *pep; GIsiPipe *pipe; guint reset; char apn[GPDS_MAX_APN_STRING_LENGTH + 1]; char username[GPDS_MAX_USERNAME_LENGTH + 1]; char password[GPDS_MAX_PASSWORD_LENGTH + 1]; uint8_t handle; /* GPDS context ID */ uint8_t type; }; static gboolean client_reset(gpointer data) { struct context_data *cd = data; g_isi_client_reset(cd->client); cd->reset = 0; return FALSE; } static void reset_context(struct context_data *cd) { if (cd == NULL) return; if (cd->pipe) g_isi_pipe_destroy(cd->pipe); if (cd->pep) g_isi_pep_destroy(cd->pep); cd->pep = NULL; cd->pipe = NULL; cd->handle = INVALID_ID; cd->reset = g_idle_add(client_reset, cd); } typedef void (*ContextFailFunc)(struct context_data *cd); static void gprs_up_fail(struct context_data *cd) { reset_context(cd); CALLBACK_WITH_FAILURE(cd->cb, cd->data); } static void gprs_down_fail(struct context_data *cd) { reset_context(cd); CALLBACK_WITH_FAILURE(cd->cb, cd->data); } static gboolean check_resp(const GIsiMessage *msg, uint8_t id, size_t minlen, struct context_data *cd, ContextFailFunc fail_cb) { const uint8_t *data = g_isi_msg_data(msg); if (g_isi_msg_error(msg) < 0) { DBG("ISI message error: %d", g_isi_msg_error(msg)); goto error; } if (g_isi_msg_id(msg) != id) return FALSE; if (g_isi_msg_data_len(msg) < minlen) { DBG("truncated message"); goto error; } if (cd->handle != INVALID_ID && data[0] != cd->handle) return FALSE; if (data[1] != GPDS_OK) { DBG("context error: %s (0x%02"PRIx8")", gpds_status_name(data[1]), data[1]); if (minlen > 2) DBG(" fail cause: %s (0x%02"PRIx8")", gpds_isi_cause_name(data[2]), data[2]); goto error; } return TRUE; error: if (fail_cb) fail_cb(cd); return FALSE; } static gboolean check_ind(const GIsiMessage *msg, size_t minlen, struct context_data *cd) { const uint8_t *data = g_isi_msg_data(msg); if (g_isi_msg_error(msg) < 0) { DBG("ISI message error: %d", g_isi_msg_error(msg)); return FALSE; } if (g_isi_msg_data_len(msg) < minlen) { DBG("truncated message"); return FALSE; } if (cd->handle != INVALID_ID && data[0] != cd->handle) return FALSE; return TRUE; } static void deactivate_ind_cb(const GIsiMessage *msg, void *opaque) { struct context_data *cd = opaque; const uint8_t *data = g_isi_msg_data(msg); if (!check_ind(msg, 2, cd)) return; DBG("context deactivated: %s (0x%02"PRIx8")", gpds_isi_cause_name(data[2]), data[2]); ofono_gprs_context_deactivated(cd->context, cd->cid); reset_context(cd); } static void activate_ind_cb(const GIsiMessage *msg, void *opaque) { struct context_data *cd = opaque; GIsiSubBlockIter iter; const char *dns[5]; int dns_count = 0; char ifname[IF_NAMESIZE]; char *ip_addr = NULL; char *ipv6_addr = NULL; if (!check_ind(msg, 2, cd)) return; for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); g_isi_sb_iter_next(&iter)) { uint8_t *addr_value = NULL; uint8_t addr_len = 0; switch (g_isi_sb_iter_get_id(&iter)) { case GPDS_PDP_ADDRESS_INFO: if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3)) goto error; if (!g_isi_sb_iter_get_data(&iter, (void *)&addr_value, 4)) goto error; if (addr_len == 4) { ip_addr = alloca(INET_ADDRSTRLEN); inet_ntop(AF_INET, (const void *)addr_value, ip_addr, INET_ADDRSTRLEN); } else if (addr_len == 16) { ipv6_addr = alloca(INET6_ADDRSTRLEN); inet_ntop(AF_INET6, (const void *)addr_value, ipv6_addr, INET6_ADDRSTRLEN); } break; case GPDS_PDNS_ADDRESS_INFO: if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3)) break; if (!g_isi_sb_iter_get_data(&iter, (void *)&addr_value, 4)) break; if (addr_len == 4) { char *addr = alloca(INET_ADDRSTRLEN); inet_ntop(AF_INET, (const void *)addr_value, addr, INET_ADDRSTRLEN); dns[dns_count++] = addr; } else if (addr_len == 16) { char *addr = alloca(INET6_ADDRSTRLEN); inet_ntop(AF_INET6, (const void *)addr_value, addr, INET6_ADDRSTRLEN); dns[dns_count++] = addr; } break; case GPDS_SDNS_ADDRESS_INFO: if (!g_isi_sb_iter_get_byte(&iter, &addr_len, 3)) break; if (!g_isi_sb_iter_get_data(&iter, (void *)&addr_value, 4)) break; if (addr_len == 4) { char *addr = alloca(INET_ADDRSTRLEN); inet_ntop(AF_INET, (const void *)addr_value, addr, INET_ADDRSTRLEN); dns[dns_count++] = addr; } else if (addr_len == 16) { char *addr = alloca(INET6_ADDRSTRLEN); inet_ntop(AF_INET6, (const void *)addr_value, addr, INET6_ADDRSTRLEN); dns[dns_count++] = addr; } break; default: DBG("skipped sub-block: %s (%zu bytes)", gpds_subblock_name(g_isi_sb_iter_get_id(&iter)), g_isi_sb_iter_get_len(&iter)); } } if (!g_isi_pep_get_ifname(cd->pep, ifname)) goto error; dns[dns_count] = 0; ofono_gprs_context_set_interface(cd->context, ifname); if (ip_addr != NULL) { ofono_gprs_context_set_ipv4_address(cd->context, ip_addr, TRUE); ofono_gprs_context_set_ipv4_netmask(cd->context, STATIC_IP_NETMASK); ofono_gprs_context_set_ipv4_dns_servers(cd->context, dns); } else if (ipv6_addr != NULL) { ofono_gprs_context_set_ipv6_address(cd->context, ipv6_addr); ofono_gprs_context_set_ipv6_dns_servers(cd->context, dns); } CALLBACK_WITH_SUCCESS(cd->cb, cd->data); return; error: gprs_up_fail(cd); } static void context_activate_cb(const GIsiMessage *msg, void *cd) { check_resp(msg, GPDS_CONTEXT_ACTIVATE_RESP, 6, cd, gprs_up_fail); } static void send_context_activate(GIsiClient *client, void *opaque) { struct context_data *cd = opaque; const unsigned char msg[] = { GPDS_CONTEXT_ACTIVATE_REQ, cd->handle, /* context ID */ 0, /* sub blocks */ }; g_isi_client_ind_subscribe(client, GPDS_CONTEXT_ACTIVATE_IND, activate_ind_cb, cd); g_isi_client_ind_subscribe(client, GPDS_CONTEXT_DEACTIVATE_IND, deactivate_ind_cb, cd); if (g_isi_client_send_with_timeout(client, msg, sizeof(msg), GPDS_CTX_ACTIVATE_TIMEOUT, context_activate_cb, cd, NULL)) g_isi_pipe_start(cd->pipe); else gprs_up_fail(cd); } static void context_auth_cb(const GIsiMessage *msg, void *opaque) { struct context_data *cd = opaque; if (!check_resp(msg, GPDS_CONTEXT_AUTH_RESP, 2, cd, gprs_up_fail)) return; send_context_activate(cd->client, cd); } static void send_context_authenticate(GIsiClient *client, void *opaque) { struct context_data *cd = opaque; size_t username_len = strlen(cd->username); size_t password_len = strlen(cd->password); /* Pad the fields to the next 32bit boundary */ size_t sb_userinfo_len = ALIGN4(3 + username_len); size_t userinfo_pad_len = sb_userinfo_len - (3 + username_len); size_t sb_password_info_len = ALIGN4(3 + password_len); size_t password_pad_len = sb_password_info_len - (3 + password_len); const uint8_t padding[4] = { 0 }; const uint8_t top[] = { GPDS_CONTEXT_AUTH_REQ, cd->handle, 2, /* sub blocks */ GPDS_USER_NAME_INFO, sb_userinfo_len, username_len, /* Username goes here */ /* Possible padding goes here */ }; const uint8_t bottom[] = { GPDS_PASSWORD_INFO, sb_password_info_len, password_len, /* Password goes here */ /* Possible padding goes here */ }; const struct iovec iov[6] = { { (uint8_t *) top, sizeof(top) }, { cd->username, username_len }, { (uint8_t *) padding, userinfo_pad_len }, { (uint8_t *) bottom, sizeof(bottom) }, { cd->password, password_len }, { (uint8_t *) padding, password_pad_len }, }; if (!g_isi_client_vsend(client, iov, 6, context_auth_cb, cd, NULL)) gprs_up_fail(cd); } static void context_conf_cb(const GIsiMessage *msg, void *opaque) { struct context_data *cd = opaque; if (!check_resp(msg, GPDS_CONTEXT_CONFIGURE_RESP, 2, cd, gprs_up_fail)) return; if (cd->username[0] != '\0') send_context_authenticate(cd->client, cd); else send_context_activate(cd->client, cd); } static void link_conf_cb(const GIsiMessage *msg, void *opaque) { struct context_data *cd = opaque; size_t apn_len = strlen(cd->apn); size_t sb_apn_info_len = ALIGN4(3 + apn_len); size_t apn_pad_len = sb_apn_info_len - (3 + apn_len); const uint8_t padding[4] = { 0 }; const uint8_t req[] = { GPDS_CONTEXT_CONFIGURE_REQ, cd->handle, /* context ID */ cd->type, /* PDP type */ GPDS_CONT_TYPE_NORMAL, cd->handle, /* primary context ID */ 0x00, /* filler */ 2, /* sub blocks */ GPDS_DNS_ADDRESS_REQ_INFO, 4, /* subblock length */ 0, 0, /* padding */ GPDS_APN_INFO, sb_apn_info_len, apn_len, /* Possible padding goes here */ }; const struct iovec iov[3] = { { (uint8_t *) req, sizeof(req) }, { cd->apn, apn_len }, { (uint8_t *) padding, apn_pad_len }, }; if (!check_resp(msg, GPDS_LL_CONFIGURE_RESP, 2, cd, gprs_up_fail)) return; if (!g_isi_client_vsend(cd->client, iov, 3, context_conf_cb, cd, NULL)) gprs_up_fail(cd); } static void create_context_cb(const GIsiMessage *msg, void *opaque) { struct context_data *cd = opaque; const uint8_t *data = g_isi_msg_data(msg); uint8_t req[] = { GPDS_LL_CONFIGURE_REQ, 0x00, /* GPDS context ID, added later */ g_isi_pipe_get_handle(cd->pipe), GPDS_LL_PLAIN, /* link type */ }; if (!check_resp(msg, GPDS_CONTEXT_ID_CREATE_RESP, 2, cd, gprs_up_fail)) return; cd->handle = req[1] = data[0]; if (!g_isi_client_send(cd->client, req, sizeof(req), link_conf_cb, cd, NULL)) gprs_up_fail(cd); } static void create_pipe_cb(GIsiPipe *pipe) { struct context_data *cd = g_isi_pipe_get_userdata(pipe); const uint8_t msg[] = { GPDS_CONTEXT_ID_CREATE_REQ, }; if (!g_isi_client_send(cd->client, msg, sizeof(msg), create_context_cb, cd, NULL)) gprs_up_fail(cd); } static void isi_gprs_activate_primary(struct ofono_gprs_context *gc, const struct ofono_gprs_primary_context *ctx, ofono_gprs_context_cb_t cb, void *data) { struct context_data *cd = ofono_gprs_context_get_data(gc); DBG("activate: gpds = 0x%04x", cd->gpds); if (cd == NULL || !cd->gpds) { /* GPDS is not reachable */ CALLBACK_WITH_FAILURE(cb, data); return; } if (cd->reset) { g_isi_client_reset(cd->client); g_source_remove(cd->reset); cd->reset = 0; } cd->cid = ctx->cid; cd->cb = cb; cd->data = data; cd->pep = NULL; cd->pipe = NULL; cd->handle = INVALID_ID; switch (ctx->proto) { case OFONO_GPRS_PROTO_IP: cd->type = GPDS_PDP_TYPE_IPV4; break; case OFONO_GPRS_PROTO_IPV6: cd->type = GPDS_PDP_TYPE_IPV6; break; case OFONO_GPRS_PROTO_IPV4V6: /* Not supported by modem */ CALLBACK_WITH_FAILURE(cb, data); return; } if (strlen(ctx->apn) >= GPDS_MAX_APN_STRING_LENGTH || strlen(ctx->username) >= GPDS_MAX_USERNAME_LENGTH || strlen(ctx->password) >= GPDS_MAX_PASSWORD_LENGTH) goto error; strncpy(cd->apn, ctx->apn, GPDS_MAX_APN_STRING_LENGTH); cd->apn[GPDS_MAX_APN_STRING_LENGTH] = '\0'; strncpy(cd->username, ctx->username, GPDS_MAX_USERNAME_LENGTH); cd->username[GPDS_MAX_USERNAME_LENGTH] = '\0'; strncpy(cd->password, ctx->password, GPDS_MAX_PASSWORD_LENGTH); cd->username[GPDS_MAX_PASSWORD_LENGTH] = '\0'; cd->pep = g_isi_pep_create(cd->idx, NULL, NULL); if (cd->pep == NULL) goto error; cd->pipe = g_isi_pipe_create(cd->idx, create_pipe_cb, g_isi_pep_get_object(cd->pep), cd->gpds, PN_PEP_TYPE_GPRS, PN_PEP_TYPE_GPRS); if (cd->pipe == NULL) goto error; g_isi_pipe_set_userdata(cd->pipe, cd); return; error: gprs_up_fail(cd); } static void context_deactivate_cb(const GIsiMessage *msg, void *opaque) { struct context_data *cd = opaque; if (!check_resp(msg, GPDS_CONTEXT_DEACTIVATE_RESP, 2, cd, gprs_down_fail)) return; CALLBACK_WITH_SUCCESS(cd->cb, cd->data); reset_context(cd); } static void isi_gprs_deactivate_primary(struct ofono_gprs_context *gc, unsigned int cid, ofono_gprs_context_cb_t cb, void *data) { struct context_data *cd = ofono_gprs_context_get_data(gc); unsigned char msg[] = { GPDS_CONTEXT_DEACTIVATE_REQ, 0x00, /* GPDS context ID, added later */ }; if (cd == NULL) return; cd->cb = cb; cd->data = data; msg[1] = cd->handle; if (!g_isi_client_send_with_timeout(cd->client, msg, sizeof(msg), GPDS_CTX_DEACTIVATE_TIMEOUT, context_deactivate_cb, cd, NULL)) gprs_down_fail(cd); } static void gpds_ctx_reachable_cb(const GIsiMessage *msg, void *opaque) { struct ofono_gprs_context *gc = opaque; struct context_data *cd = ofono_gprs_context_get_data(gc); if (g_isi_msg_error(msg) < 0) { DBG("unable to bootstrap gprs context driver"); ofono_gprs_context_remove(gc); return; } cd->gpds = g_isi_msg_object(msg); } static int isi_gprs_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *user) { GIsiModem *idx = user; struct context_data *cd = g_try_new0(struct context_data, 1); if (cd == NULL) return -ENOMEM; cd->client = g_isi_client_create(idx, PN_GPDS); if (cd->client == NULL) { g_free(cd); return -ENOMEM; } cd->idx = idx; cd->context = gc; ofono_gprs_context_set_data(gc, cd); g_isi_client_verify(cd->client, gpds_ctx_reachable_cb, gc, NULL); return 0; } static void isi_gprs_context_remove(struct ofono_gprs_context *gc) { struct context_data *cd = ofono_gprs_context_get_data(gc); ofono_gprs_context_set_data(gc, NULL); if (cd == NULL) return; if (cd->reset) g_source_remove(cd->reset); if (cd->pipe != NULL) g_isi_pipe_destroy(cd->pipe); if (cd->pep != NULL) g_isi_pep_destroy(cd->pep); g_isi_client_destroy(cd->client); g_free(cd); } static struct ofono_gprs_context_driver driver = { .name = "isimodem", .probe = isi_gprs_context_probe, .remove = isi_gprs_context_remove, .activate_primary = isi_gprs_activate_primary, .deactivate_primary = isi_gprs_deactivate_primary, }; void isi_gprs_context_init(void) { ofono_gprs_context_driver_register(&driver); } void isi_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/ztemodem/0000755000015600001650000000000012671500304021702 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/ztemodem/ztemodem.h0000644000015600001650000000161112671500024023675 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 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 extern void zte_radio_settings_init(void); extern void zte_radio_settings_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/ztemodem/radio-settings.c0000644000015600001650000001150412671500024025002 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "ztemodem.h" static const char *none_prefix[] = { NULL }; static const char *zsnt_prefix[] = { "+ZSNT:", NULL }; struct radio_settings_data { GAtChat *chat; }; static void zsnt_query_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; int value; 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, "+ZSNT:") == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &value) == FALSE) goto error; switch (value) { case 0: mode = OFONO_RADIO_ACCESS_MODE_ANY; break; case 1: mode = OFONO_RADIO_ACCESS_MODE_GSM; break; case 2: mode = OFONO_RADIO_ACCESS_MODE_UMTS; break; default: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } cb(&error, mode, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void zte_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+ZSNT?", zsnt_prefix, zsnt_query_cb, cbd, g_free) == 0) { CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } } static void zsnt_modify_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void zte_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); struct cb_data *cbd = cb_data_new(cb, data); char buf[20]; int value = 0; switch (mode) { case OFONO_RADIO_ACCESS_MODE_ANY: value = 0; break; case OFONO_RADIO_ACCESS_MODE_GSM: value = 1; break; case OFONO_RADIO_ACCESS_MODE_UMTS: value = 2; break; case OFONO_RADIO_ACCESS_MODE_LTE: goto error; } snprintf(buf, sizeof(buf), "AT+ZSNT=%u,0,0", value); if (g_at_chat_send(rsd->chat, buf, none_prefix, zsnt_modify_cb, cbd, g_free) > 0) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void zsnt_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_radio_settings *rs = user_data; if (!ok) { ofono_radio_settings_remove(rs); return; } ofono_radio_settings_register(rs); } static int zte_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *data) { GAtChat *chat = data; struct radio_settings_data *rsd; rsd = g_try_new0(struct radio_settings_data, 1); if (rsd == NULL) return -ENOMEM; rsd->chat = g_at_chat_clone(chat); ofono_radio_settings_set_data(rs, rsd); g_at_chat_send(rsd->chat, "AT+ZSNT=?", none_prefix, zsnt_support_cb, rs, NULL); return 0; } static void zte_radio_settings_remove(struct ofono_radio_settings *rs) { struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs); ofono_radio_settings_set_data(rs, NULL); g_at_chat_unref(rsd->chat); g_free(rsd); } static struct ofono_radio_settings_driver driver = { .name = "ztemodem", .probe = zte_radio_settings_probe, .remove = zte_radio_settings_remove, .query_rat_mode = zte_query_rat_mode, .set_rat_mode = zte_set_rat_mode }; void zte_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void zte_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/ztemodem/ztemodem.c0000644000015600001650000000234112671500024023671 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "ztemodem.h" static int ztemodem_init(void) { zte_radio_settings_init(); return 0; } static void ztemodem_exit(void) { zte_radio_settings_exit(); } OFONO_PLUGIN_DEFINE(ztemodem, "ZTE modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, ztemodem_init, ztemodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/cdmamodem/0000755000015600001650000000000012671500304022004 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/cdmamodem/voicecall.c0000644000015600001650000000746512671500024024124 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "cdmamodem.h" static const char *none_prefix[] = { NULL }; struct voicecall_data { GAtChat *chat; unsigned int vendor; }; static void cdma_template(const char *cmd, struct ofono_cdma_voicecall *vc, GAtResultFunc result_cb, ofono_cdma_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_cdma_voicecall_get_data(vc); struct cb_data *cbd = cb_data_new(cb, data); cbd->user = vc; if (g_at_chat_send(vd->chat, cmd, none_prefix, result_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void cdma_generic_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_cdma_voicecall_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void cdma_dial(struct ofono_cdma_voicecall *vc, const struct ofono_cdma_phone_number *ph, ofono_cdma_voicecall_cb_t cb, void *data) { char buf[OFONO_CDMA_MAX_PHONE_NUMBER_LENGTH + 8]; snprintf(buf, sizeof(buf), "AT+CDV=%s", ph->number); cdma_template(buf, vc, cdma_generic_cb, cb, data); } static void cdma_hangup_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; cdma_generic_cb(ok, result, user_data); /* TODO: this should come from a modem solicited notification */ ofono_cdma_voicecall_disconnected(cbd->user, OFONO_DISCONNECT_REASON_LOCAL_HANGUP, NULL); } static void cdma_hangup(struct ofono_cdma_voicecall *vc, ofono_cdma_voicecall_cb_t cb, void *data) { /* Hangup active call */ cdma_template("AT+CHV", vc, cdma_hangup_cb, cb, data); } static gboolean cdma_voicecall_initialized(gpointer user_data) { struct ofono_cdma_voicecall *vc = user_data; ofono_cdma_voicecall_register(vc); return FALSE; } static int cdma_voicecall_probe(struct ofono_cdma_voicecall *vc, unsigned int vendor, void *data) { GAtChat *chat = data; struct voicecall_data *vd; vd = g_try_new0(struct voicecall_data, 1); if (vd == NULL) return -ENOMEM; vd->chat = g_at_chat_clone(chat); vd->vendor = vendor; ofono_cdma_voicecall_set_data(vc, vd); g_idle_add(cdma_voicecall_initialized, vc); return 0; } static void cdma_voicecall_remove(struct ofono_cdma_voicecall *vc) { struct voicecall_data *vd = ofono_cdma_voicecall_get_data(vc); ofono_cdma_voicecall_set_data(vc, NULL); g_at_chat_unref(vd->chat); g_free(vd); } static struct ofono_cdma_voicecall_driver driver = { .name = "cdmamodem", .probe = cdma_voicecall_probe, .remove = cdma_voicecall_remove, .dial = cdma_dial, .hangup = cdma_hangup, }; void cdma_voicecall_init(void) { ofono_cdma_voicecall_driver_register(&driver); } void cdma_voicecall_exit(void) { ofono_cdma_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/cdmamodem/cdmamodem.h0000644000015600001650000000203412671500024024101 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 extern void cdma_voicecall_init(void); extern void cdma_voicecall_exit(void); extern void cdma_devinfo_init(void); extern void cdma_devinfo_exit(void); extern void cdma_connman_init(void); extern void cdma_connman_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/cdmamodem/cdmamodem.c0000644000015600001650000000247512671500024024105 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "cdmamodem.h" static int cdmamodem_init(void) { cdma_voicecall_init(); cdma_devinfo_init(); cdma_connman_init(); return 0; } static void cdmamodem_exit(void) { cdma_voicecall_exit(); cdma_devinfo_exit(); cdma_connman_exit(); } OFONO_PLUGIN_DEFINE(cdmamodem, "CDMA AT modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, cdmamodem_init, cdmamodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/cdmamodem/connman.c0000644000015600001650000001663512671500024023613 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "gatppp.h" #include "cdmamodem.h" #include "drivers/atmodem/vendor.h" #define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun" #define STATIC_IP_NETMASK "255.255.255.255" static const char *none_prefix[] = { NULL }; enum state { STATE_IDLE, STATE_ENABLING, STATE_DISABLING, STATE_ACTIVE, }; struct connman_data { GAtChat *chat; GAtPPP *ppp; unsigned int vendor; enum state state; char username[OFONO_CDMA_CONNMAN_MAX_USERNAME_LENGTH + 1]; char password[OFONO_CDMA_CONNMAN_MAX_PASSWORD_LENGTH + 1]; union { ofono_cdma_connman_cb_t down_cb; /* Down callback */ ofono_cdma_connman_up_cb_t up_cb; /* Up callback */ }; void *cb_data; /* Callback data */ }; static void ppp_debug(const char *str, void *data) { ofono_info("%s: %s", (const char *) data, str); } static void ppp_connect(const char *interface, const char *local, const char *remote, const char *dns1, const char *dns2, gpointer user_data) { struct ofono_cdma_connman *cm = user_data; struct connman_data *cd = ofono_cdma_connman_get_data(cm); const char *dns[3]; DBG(""); dns[0] = dns1; dns[1] = dns2; dns[2] = 0; ofono_info("IP: %s", local); ofono_info("DNS: %s, %s", dns1, dns2); cd->state = STATE_ACTIVE; CALLBACK_WITH_SUCCESS(cd->up_cb, interface, TRUE, local, STATIC_IP_NETMASK, NULL, dns, cd->cb_data); } static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data) { struct ofono_cdma_connman *cm = user_data; struct connman_data *cd = ofono_cdma_connman_get_data(cm); DBG(""); g_at_ppp_unref(cd->ppp); cd->ppp = NULL; switch (cd->state) { case STATE_ENABLING: CALLBACK_WITH_FAILURE(cd->up_cb, NULL, FALSE, NULL, NULL, NULL, NULL, cd->cb_data); break; case STATE_DISABLING: CALLBACK_WITH_SUCCESS(cd->down_cb, cd->cb_data); break; default: ofono_cdma_connman_deactivated(cm); break; } cd->state = STATE_IDLE; g_at_chat_resume(cd->chat); } static gboolean setup_ppp(struct ofono_cdma_connman *cm) { struct connman_data *cd = ofono_cdma_connman_get_data(cm); GAtIO *io; DBG(""); io = g_at_chat_get_io(cd->chat); g_at_chat_suspend(cd->chat); /* open ppp */ cd->ppp = g_at_ppp_new(); if (cd->ppp == NULL) { g_at_chat_resume(cd->chat); return FALSE; } if (getenv("OFONO_PPP_DEBUG")) g_at_ppp_set_debug(cd->ppp, ppp_debug, "PPP"); /* set connect and disconnect callbacks */ g_at_ppp_set_connect_function(cd->ppp, ppp_connect, cm); g_at_ppp_set_disconnect_function(cd->ppp, ppp_disconnect, cm); g_at_ppp_set_credentials(cd->ppp, cd->username, cd->password); /* open the ppp connection */ g_at_ppp_open(cd->ppp, io); return TRUE; } static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_cdma_connman *cm = user_data; struct connman_data *cd = ofono_cdma_connman_get_data(cm); DBG("ok %d", ok); if (ok == FALSE) { struct ofono_error error; ofono_info("Unable to enter data state"); cd->state = STATE_IDLE; decode_at_error(&error, g_at_result_final_response(result)); cd->up_cb(&error, NULL, 0, NULL, NULL, NULL, NULL, cd->cb_data); return; } setup_ppp(cm); } static void cdma_connman_activate(struct ofono_cdma_connman *cm, const char *username, const char *password, ofono_cdma_connman_up_cb_t cb, void *data) { struct connman_data *cd = ofono_cdma_connman_get_data(cm); DBG(""); cd->up_cb = cb; cd->cb_data = data; strcpy(cd->username, username); strcpy(cd->password, password); cd->state = STATE_ENABLING; if (g_at_chat_send(cd->chat, "ATD#777", none_prefix, atd_cb, cm, NULL) > 0) return; CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL, NULL, NULL, data); } static void cdma_connman_deactivate(struct ofono_cdma_connman *cm, ofono_cdma_connman_cb_t cb, void *data) { struct connman_data *cd = ofono_cdma_connman_get_data(cm); DBG(""); cd->state = STATE_DISABLING; cd->down_cb = cb; cd->cb_data = data; g_at_ppp_shutdown(cd->ppp); } static void huawei_dsdormant_notify(GAtResult *result, gpointer user_data) { struct ofono_cdma_connman *cm = user_data; int dormant; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^DSDORMANT:")) return; if (!g_at_result_iter_next_number(&iter, &dormant)) return; switch (dormant) { case 0: ofono_cdma_connman_dormant_notify(cm, FALSE); break; case 1: ofono_cdma_connman_dormant_notify(cm, TRUE); break; default: ofono_error("Invalid DSDORMANT value"); break; } } static void at_c0_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_cdma_connman *cm = user_data; struct connman_data *cd = ofono_cdma_connman_get_data(cm); GAtChat *chat; DBG("ok %d", ok); if (ok == FALSE) { ofono_info("Unable to configure circuit 109"); ofono_cdma_connman_remove(cm); return; } switch (cd->vendor) { case OFONO_VENDOR_HUAWEI: chat = g_at_chat_get_slave(cd->chat); g_at_chat_register(chat, "^DSDORMANT", huawei_dsdormant_notify, FALSE, cm, NULL); break; default: break; } ofono_cdma_connman_register(cm); } static int cdma_connman_probe(struct ofono_cdma_connman *cm, unsigned int vendor, void *data) { GAtChat *chat = data; struct connman_data *cd; struct stat st; DBG(""); if (stat(TUN_SYSFS_DIR, &st) < 0) { ofono_error("Missing support for TUN/TAP devices"); return -ENODEV; } cd = g_try_new0(struct connman_data, 1); if (cd == NULL) return -ENOMEM; cd->chat = g_at_chat_clone(chat); cd->vendor = vendor; ofono_cdma_connman_set_data(cm, cd); /* Turn off any modem-initiated dormancy timeout */ g_at_chat_send(cd->chat, "AT+CTA=0", none_prefix, NULL, NULL, NULL); g_at_chat_send(cd->chat, "AT&C0", none_prefix, at_c0_cb, cm, NULL); return 0; } static void cdma_connman_remove(struct ofono_cdma_connman *cm) { struct connman_data *cd = ofono_cdma_connman_get_data(cm); DBG(""); if (cd->state != STATE_IDLE && cd->ppp) { g_at_ppp_unref(cd->ppp); g_at_chat_resume(cd->chat); } ofono_cdma_connman_set_data(cm, NULL); g_at_chat_unref(cd->chat); g_free(cd); } static struct ofono_cdma_connman_driver driver = { .name = "cdmamodem", .probe = cdma_connman_probe, .remove = cdma_connman_remove, .activate = cdma_connman_activate, .deactivate = cdma_connman_deactivate, }; void cdma_connman_init(void) { ofono_cdma_connman_driver_register(&driver); } void cdma_connman_exit(void) { ofono_cdma_connman_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/cdmamodem/devinfo.c0000644000015600001650000001027712671500024023610 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "cdmamodem.h" static const char *gcap_prefix[] = { "+GCAP:", NULL }; static void attr_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_devinfo_query_cb_t cb = cbd->cb; const char *prefix = cbd->user; struct ofono_error error; const char *attr; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, NULL, cbd->data); return; } if (at_util_parse_attr(result, prefix, &attr) == FALSE) { CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); return; } cb(&error, attr, cbd->data); } static void cdma_query_manufacturer(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data); GAtChat *chat = ofono_devinfo_get_data(info); cbd->user = "+GMI:"; if (g_at_chat_send(chat, "AT+GMI", NULL, attr_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void cdma_query_model(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data); GAtChat *chat = ofono_devinfo_get_data(info); cbd->user = "+GMM:"; if (g_at_chat_send(chat, "AT+GMM", NULL, attr_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void cdma_query_revision(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data); GAtChat *chat = ofono_devinfo_get_data(info); cbd->user = "+GMR:"; if (g_at_chat_send(chat, "AT+GMR", NULL, attr_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void cdma_query_serial(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data); GAtChat *chat = ofono_devinfo_get_data(info); cbd->user = "+GSN:"; if (g_at_chat_send(chat, "AT+GSN", NULL, attr_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void capability_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_devinfo *info = user_data; ofono_devinfo_register(info); } static int cdma_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor, void *data) { GAtChat *chat = data; ofono_devinfo_set_data(info, g_at_chat_clone(chat)); g_at_chat_send(chat, "AT+GCAP", gcap_prefix, capability_cb, info, NULL); return 0; } static void cdma_devinfo_remove(struct ofono_devinfo *info) { GAtChat *chat = ofono_devinfo_get_data(info); g_at_chat_unref(chat); ofono_devinfo_set_data(info, NULL); } static struct ofono_devinfo_driver driver = { .name = "cdmamodem", .probe = cdma_devinfo_probe, .remove = cdma_devinfo_remove, .query_manufacturer = cdma_query_manufacturer, .query_model = cdma_query_model, .query_revision = cdma_query_revision, .query_serial = cdma_query_serial }; void cdma_devinfo_init(void) { ofono_devinfo_driver_register(&driver); } void cdma_devinfo_exit(void) { ofono_devinfo_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/0000755000015600001650000000000012671500304021504 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/sms.c0000644000015600001650000007235512671500024022465 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "smsutil.h" #include "util.h" #include "vendor.h" #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" static const char *csca_prefix[] = { "+CSCA:", NULL }; static const char *cgsms_prefix[] = { "+CGSMS:", NULL }; static const char *csms_prefix[] = { "+CSMS:", NULL }; static const char *cmgf_prefix[] = { "+CMGF:", NULL }; static const char *cpms_prefix[] = { "+CPMS:", NULL }; static const char *cnmi_prefix[] = { "+CNMI:", NULL }; static const char *cmgs_prefix[] = { "+CMGS:", NULL }; static const char *cmgl_prefix[] = { "+CMGL:", NULL }; static const char *none_prefix[] = { NULL }; static gboolean set_cmgf(gpointer user_data); static gboolean set_cpms(gpointer user_data); static void at_cmgl_set_cpms(struct ofono_sms *sms, int store); #define MAX_CMGF_RETRIES 10 #define MAX_CPMS_RETRIES 10 static const char *storages[] = { "SM", "ME", "MT", "SR", "BM", }; struct sms_data { int store; int incoming; int retries; gboolean expect_sr; gboolean cnma_enabled; char *cnma_ack_pdu; int cnma_ack_pdu_len; guint timeout_source; GAtChat *chat; unsigned int vendor; }; struct cpms_request { struct ofono_sms *sms; int store; int index; gboolean expect_sr; }; static void at_csca_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sms_sca_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_csca_set(struct ofono_sms *sms, const struct ofono_phone_number *sca, ofono_sms_sca_set_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); char buf[64]; snprintf(buf, sizeof(buf), "AT+CSCA=\"%s\",%d", sca->number, sca->type); if (g_at_chat_send(data->chat, buf, csca_prefix, at_csca_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } static void at_csca_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; GAtResultIter iter; ofono_sms_sca_query_cb_t cb = cbd->cb; struct ofono_error error; struct ofono_phone_number sca; const char *number; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSCA:")) goto err; if (!g_at_result_iter_next_string(&iter, &number)) goto err; if (number[0] == '+') { number = number + 1; sca.type = 145; } else { sca.type = 129; } strncpy(sca.number, number, OFONO_MAX_PHONE_NUMBER_LENGTH); sca.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0'; g_at_result_iter_next_number(&iter, &sca.type); DBG("csca_query_cb: %s, %d", sca.number, sca.type); cb(&error, &sca, cbd->data); return; err: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void at_csca_query(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); if (g_at_chat_send(data->chat, "AT+CSCA?", csca_prefix, at_csca_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, user_data); } static void at_cmgs_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; GAtResultIter iter; ofono_sms_submit_cb_t cb = cbd->cb; struct ofono_error error; int mr; 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, "+CMGS:")) goto err; if (!g_at_result_iter_next_number(&iter, &mr)) goto err; DBG("Got MR: %d", mr); cb(&error, mr, cbd->data); return; err: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void at_cmgs(struct ofono_sms *sms, const unsigned char *pdu, int pdu_len, int tpdu_len, int mms, ofono_sms_submit_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); char buf[512]; int len; if (mms) { snprintf(buf, sizeof(buf), "AT+CMMS=%d", mms); g_at_chat_send(data->chat, buf, none_prefix, NULL, NULL, NULL); } len = snprintf(buf, sizeof(buf), "AT+CMGS=%d\r", tpdu_len); encode_hex_own_buf(pdu, pdu_len, 0, buf+len); if (g_at_chat_send(data->chat, buf, cmgs_prefix, at_cmgs_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, user_data); } static void at_cgsms_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sms_sca_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_cgsms_set(struct ofono_sms *sms, int bearer, ofono_sms_bearer_set_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); char buf[64]; snprintf(buf, sizeof(buf), "AT+CGSMS=%d", bearer); if (g_at_chat_send(data->chat, buf, none_prefix, at_cgsms_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } static void at_cgsms_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sms_bearer_query_cb_t cb = cbd->cb; struct ofono_error error; GAtResultIter iter; int bearer; 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, "+CGSMS:")) goto err; g_at_result_iter_next_number(&iter, &bearer); cb(&error, bearer, cbd->data); return; err: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void at_cgsms_query(struct ofono_sms *sms, ofono_sms_bearer_query_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); if (g_at_chat_send(data->chat, "AT+CGSMS?", cgsms_prefix, at_cgsms_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, user_data); } static void at_cnma_cb(gboolean ok, GAtResult *result, gpointer user_data) { if (!ok) ofono_error("CNMA acknowledgement failed: " "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); char buf[256]; 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 */ snprintf(buf, sizeof(buf), "AT+CNMA=0"); g_at_chat_send(data->chat, buf, none_prefix, at_cnma_cb, NULL, NULL); } static void at_cds_notify(GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); long pdu_len; int tpdu_len; const char *hexpdu; unsigned char pdu[176]; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CDS:")) goto err; /* * Quirk for ZTE firmware which is not compliant with 27.005 * The +CDS syntax used by ZTE is including a comma before the length * +CDS: , * As a result, we need to skip this omitted subparameter */ if (data->vendor == OFONO_VENDOR_ZTE) g_at_result_iter_skip_next(&iter); if (!g_at_result_iter_next_number(&iter, &tpdu_len)) goto err; hexpdu = g_at_result_pdu(result); if (strlen(hexpdu) > sizeof(pdu) * 2) { ofono_error("Bad PDU length in CDS notification"); return; } DBG("Got new Status-Report PDU via CDS: %s, %d", hexpdu, tpdu_len); /* Decode pdu and notify about new SMS status report */ decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu); ofono_sms_status_notify(sms, pdu, pdu_len, tpdu_len); if (data->cnma_enabled) at_ack_delivery(sms); return; err: ofono_error("Unable to parse CDS notification"); } 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); const char *hexpdu; 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; } if (strlen(hexpdu) > sizeof(pdu) * 2) { ofono_error("Bad PDU length in CMT notification"); return; } DBG("Got new SMS Deliver PDU via CMT: %s, %d", hexpdu, tpdu_len); decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu); ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len); if (data->vendor != OFONO_VENDOR_SIMCOM) at_ack_delivery(sms); } static void at_cmgr_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; DBG(""); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CMGR:")) goto err; if (!g_at_result_iter_skip_next(&iter)) goto err; if (!g_at_result_iter_skip_next(&iter)) goto err; if (!g_at_result_iter_next_number(&iter, &tpdu_len)) goto err; hexpdu = g_at_result_pdu(result); if (strlen(hexpdu) > sizeof(pdu) * 2) goto err; DBG("Got PDU: %s, with len: %d", hexpdu, tpdu_len); decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu); if (data->expect_sr) ofono_sms_status_notify(sms, pdu, pdu_len, tpdu_len); else ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len); return; err: ofono_error("Unable to parse CMGR response"); } static void at_cmgr_cb(gboolean ok, GAtResult *result, gpointer user_data) { if (!ok) ofono_error("Received a CMTI indication but CMGR failed!"); } static void at_cmgd_cb(gboolean ok, GAtResult *result, gpointer user_data) { if (!ok) ofono_error("Unable to delete received SMS"); } static void at_cmgr_cpms_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cpms_request *req = user_data; struct ofono_sms *sms = req->sms; struct sms_data *data = ofono_sms_get_data(sms); char buf[128]; if (!ok) { ofono_error("Received CMTI/CDSI, but CPMS request failed"); return; } data->store = req->store; data->expect_sr = req->expect_sr; snprintf(buf, sizeof(buf), "AT+CMGR=%d", req->index); g_at_chat_send(data->chat, buf, none_prefix, at_cmgr_cb, NULL, NULL); /* We don't buffer SMS on the SIM/ME, send along a CMGD as well */ snprintf(buf, sizeof(buf), "AT+CMGD=%d", req->index); g_at_chat_send(data->chat, buf, none_prefix, at_cmgd_cb, NULL, NULL); } static void at_send_cmgr_cpms(struct ofono_sms *sms, int store, int index, gboolean expect_sr) { struct sms_data *data = ofono_sms_get_data(sms); if (store == data->store) { struct cpms_request req; req.sms = sms; req.store = store; req.index = index; req.expect_sr = expect_sr; at_cmgr_cpms_cb(TRUE, NULL, &req); } else { char buf[128]; const char *incoming = storages[data->incoming]; struct cpms_request *req = g_new(struct cpms_request, 1); req->sms = sms; req->store = store; req->index = index; req->expect_sr = expect_sr; snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"", storages[store], storages[store], incoming); g_at_chat_send(data->chat, buf, cpms_prefix, at_cmgr_cpms_cb, req, g_free); } } static void at_cmti_notify(GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; enum at_util_sms_store store; int index; if (at_util_parse_sms_index_delivery(result, "+CMTI:", &store, &index) == FALSE) goto error; if (store != AT_UTIL_SMS_STORE_SM && store != AT_UTIL_SMS_STORE_ME) goto error; DBG("Got a CMTI indication at %s, index: %d", storages[store], index); at_send_cmgr_cpms(sms, store, index, FALSE); return; error: ofono_error("Unable to parse CMTI notification"); } static void at_cdsi_notify(GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; enum at_util_sms_store store; int index; if (at_util_parse_sms_index_delivery(result, "+CDSI:", &store, &index) == FALSE) goto error; /* Some modems actually store status reports in SM, and not SR */ if (store != AT_UTIL_SMS_STORE_SR && store != AT_UTIL_SMS_STORE_SM && store != AT_UTIL_SMS_STORE_ME) goto error; DBG("Got a CDSI indication at %s, index: %d", storages[store], index); at_send_cmgr_cpms(sms, store, index, TRUE); return; error: ofono_error("Unable to parse CDSI notification"); } static void at_cmgl_done(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); DBG(""); if (data->incoming == AT_UTIL_SMS_STORE_MT && data->store == AT_UTIL_SMS_STORE_ME) { at_cmgl_set_cpms(sms, AT_UTIL_SMS_STORE_SM); return; } g_at_chat_register(data->chat, "+CMTI:", at_cmti_notify, FALSE, sms, NULL); g_at_chat_register(data->chat, "+CMT:", at_cmt_notify, TRUE, sms, NULL); g_at_chat_register(data->chat, "+CDS:", at_cds_notify, TRUE, sms, NULL); g_at_chat_register(data->chat, "+CDSI:", at_cdsi_notify, FALSE, sms, NULL); /* We treat CMGR just like a notification */ g_at_chat_register(data->chat, "+CMGR:", at_cmgr_notify, TRUE, sms, NULL); } static void at_cmgl_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; int index; int status; char buf[16]; DBG(""); g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+CMGL:")) { if (!g_at_result_iter_next_number(&iter, &index)) goto err; if (!g_at_result_iter_next_number(&iter, &status)) goto err; if (!g_at_result_iter_skip_next(&iter)) goto err; if (!g_at_result_iter_next_number(&iter, &tpdu_len)) goto err; /* Only MT messages */ if (status != 0 && status != 1) continue; hexpdu = g_at_result_pdu(result); DBG("Found an old SMS PDU: %s, with len: %d", hexpdu, tpdu_len); if (strlen(hexpdu) > sizeof(pdu) * 2) continue; decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu); ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len); /* We don't buffer SMS on the SIM/ME, send along a CMGD */ snprintf(buf, sizeof(buf), "AT+CMGD=%d", index); g_at_chat_send(data->chat, buf, none_prefix, at_cmgd_cb, NULL, NULL); } return; err: ofono_error("Unable to parse CMGL response"); } static void at_cmgl_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; if (!ok) DBG("Initial listing SMS storage failed!"); at_cmgl_done(sms); } static void at_cmgl_cpms_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cpms_request *req = user_data; struct ofono_sms *sms = req->sms; struct sms_data *data = ofono_sms_get_data(sms); if (!ok) { ofono_error("Initial CPMS request failed"); at_cmgl_done(sms); return; } data->store = req->store; g_at_chat_send_pdu_listing(data->chat, "AT+CMGL=4", cmgl_prefix, at_cmgl_notify, at_cmgl_cb, sms, NULL); } static void at_cmgl_set_cpms(struct ofono_sms *sms, int store) { struct sms_data *data = ofono_sms_get_data(sms); if (store == data->store) { struct cpms_request req; req.sms = sms; req.store = store; at_cmgl_cpms_cb(TRUE, NULL, &req); } else { char buf[128]; const char *readwrite = storages[store]; const char *incoming = storages[data->incoming]; struct cpms_request *req = g_new(struct cpms_request, 1); req->sms = sms; req->store = store; snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"", readwrite, readwrite, incoming); g_at_chat_send(data->chat, buf, cpms_prefix, at_cmgl_cpms_cb, req, g_free); } } static void at_sms_initialized(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); /* Inspect and free the incoming SMS storage */ if (data->incoming == AT_UTIL_SMS_STORE_MT) at_cmgl_set_cpms(sms, AT_UTIL_SMS_STORE_ME); else at_cmgl_set_cpms(sms, data->incoming); ofono_sms_register(sms); } static void at_sms_not_supported(struct ofono_sms *sms) { ofono_error("SMS not supported by this modem. If this is in error" " please submit patches to support this hardware"); ofono_sms_remove(sms); } static void at_cnmi_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; if (!ok) return at_sms_not_supported(sms); at_sms_initialized(sms); } static inline char wanted_cnmi(int supported, const char *pref) { while (*pref) { if (supported & (1 << (*pref - '0'))) return *pref; pref++; } return '\0'; } static inline gboolean append_cnmi_element(char *buf, int *len, int cap, const char *wanted, gboolean last) { char setting = wanted_cnmi(cap, wanted); if (!setting) return FALSE; buf[*len] = setting; if (last) buf[*len + 1] = '\0'; else buf[*len + 1] = ','; *len += 2; return TRUE; } static gboolean build_cnmi_string(char *buf, int *cnmi_opts, struct sms_data *data) { const char *mode; int len = sprintf(buf, "AT+CNMI="); DBG(""); switch (data->vendor) { case OFONO_VENDOR_GOBI: case OFONO_VENDOR_QUALCOMM_MSM: case OFONO_VENDOR_NOVATEL: case OFONO_VENDOR_HUAWEI: case OFONO_VENDOR_ZTE: case OFONO_VENDOR_SIMCOM: /* MSM devices advertise support for mode 2, but return an * error if we attempt to actually use it. */ mode = "1"; break; default: /* Sounds like 2 is the sanest mode */ mode = "2310"; break; } if (!append_cnmi_element(buf, &len, cnmi_opts[0], mode, FALSE)) return FALSE; /* Prefer to deliver SMS via +CMT if CNMA is supported */ if (!append_cnmi_element(buf, &len, cnmi_opts[1], data->cnma_enabled ? "21" : "1", FALSE)) return FALSE; /* Always deliver CB via +CBM, otherwise don't deliver at all */ if (!append_cnmi_element(buf, &len, cnmi_opts[2], "20", FALSE)) return FALSE; /* * Some manufacturers seem to have trouble with delivery via +CDS. * They report the status report properly, however refuse to +CNMA * ack it with error "CNMA not expected." However, not acking it * sends the device into la-la land. */ switch (data->vendor) { case OFONO_VENDOR_NOVATEL: mode = "20"; break; default: mode = "120"; break; } /* * Try to deliver Status-Reports via +CDS, then CDSI or don't * deliver at all * */ if (!append_cnmi_element(buf, &len, cnmi_opts[3], mode, FALSE)) return FALSE; /* Don't care about buffering, 0 seems safer */ if (!append_cnmi_element(buf, &len, cnmi_opts[4], "01", TRUE)) return FALSE; return TRUE; } static void construct_ack_pdu(struct sms_data *d) { struct sms ackpdu; unsigned char pdu[164]; int len; int tpdu_len; DBG(""); memset(&ackpdu, 0, sizeof(ackpdu)); ackpdu.type = SMS_TYPE_DELIVER_REPORT_ACK; if (!sms_encode(&ackpdu, &len, &tpdu_len, pdu)) goto err; /* Constructing an according to 27.005 Section 4.6 */ if (len != tpdu_len) goto err; d->cnma_ack_pdu = encode_hex(pdu, tpdu_len, 0); if (d->cnma_ack_pdu == NULL) goto err; d->cnma_ack_pdu_len = tpdu_len; return; err: ofono_error("Unable to construct Deliver ACK PDU"); } static void at_cnmi_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); GAtResultIter iter; int cnmi_opts[5]; /* See 27.005 Section 3.4.1 */ int opt; int mode; gboolean supported = FALSE; char buf[128]; if (!ok) goto out; memset(cnmi_opts, 0, sizeof(cnmi_opts)); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CNMI:")) goto out; for (opt = 0; opt < 5; opt++) { int min, max; if (!g_at_result_iter_open_list(&iter)) goto out; while (g_at_result_iter_next_range(&iter, &min, &max)) { for (mode = min; mode <= max; mode++) cnmi_opts[opt] |= 1 << mode; } if (!g_at_result_iter_close_list(&iter)) goto out; } if (build_cnmi_string(buf, cnmi_opts, data)) supported = TRUE; /* support for ack pdu is not working */ switch (data->vendor) { case OFONO_VENDOR_IFX: case OFONO_VENDOR_GOBI: case OFONO_VENDOR_ZTE: case OFONO_VENDOR_ICERA: case OFONO_VENDOR_HUAWEI: case OFONO_VENDOR_NOVATEL: case OFONO_VENDOR_OPTION_HSO: goto out; default: break; } if (data->cnma_enabled) construct_ack_pdu(data); out: if (!supported) return at_sms_not_supported(sms); g_at_chat_send(data->chat, buf, cnmi_prefix, at_cnmi_set_cb, sms, NULL); } static void at_query_cnmi(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); g_at_chat_send(data->chat, "AT+CNMI=?", cnmi_prefix, at_cnmi_query_cb, sms, NULL); } static void at_cpms_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); if (ok) return at_query_cnmi(sms); data->retries += 1; if (data->retries == MAX_CPMS_RETRIES) { ofono_error("Unable to set preferred storage"); return at_sms_not_supported(sms); } data->timeout_source = g_timeout_add_seconds(1, set_cpms, sms); } static gboolean set_cpms(gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); const char *store = storages[data->store]; const char *incoming = storages[data->incoming]; char buf[128]; if (data->vendor == OFONO_VENDOR_WAVECOM_Q2XXX) snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\"", store); else snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"", store, store, incoming); g_at_chat_send(data->chat, buf, cpms_prefix, at_cpms_set_cb, sms, NULL); data->timeout_source = 0; return FALSE; } static void at_cmgf_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); if (ok) { data->retries = 0; set_cpms(sms); return; } data->retries += 1; if (data->retries == MAX_CMGF_RETRIES) { DBG("Unable to enter PDU mode"); return at_sms_not_supported(sms); } data->timeout_source = g_timeout_add_seconds(1, set_cmgf, sms); } static gboolean set_cmgf(gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); g_at_chat_send(data->chat, "AT+CMGF=0", cmgf_prefix, at_cmgf_set_cb, sms, NULL); data->timeout_source = 0; return FALSE; } static void at_cpms_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); gboolean supported = FALSE; if (ok) { int mem = 0, mem_max; GAtResultIter iter; const char *store; gboolean me_supported[3]; gboolean sm_supported[3]; gboolean mt_supported[3]; memset(me_supported, 0, sizeof(me_supported)); memset(sm_supported, 0, sizeof(sm_supported)); memset(mt_supported, 0, sizeof(mt_supported)); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CPMS:")) goto out; if (data->vendor == OFONO_VENDOR_WAVECOM_Q2XXX) { /* skip initial `(' */ if (!g_at_result_iter_open_list(&iter)) goto out; /* * Wavecom Q2 replies: +CPMS: (("SM","BM","SR"),("SM")) * This reply is broken according to 3GPP TS 07.05. */ mem_max = 2; } else mem_max = 3; for (mem = 0; mem < mem_max; mem++) { if (!g_at_result_iter_open_list(&iter)) goto out; while (g_at_result_iter_next_string(&iter, &store)) { if (!strcmp(store, "ME")) me_supported[mem] = TRUE; else if (!strcmp(store, "SM")) sm_supported[mem] = TRUE; else if (!strcmp(store, "MT")) mt_supported[mem] = TRUE; } if (!g_at_result_iter_close_list(&iter)) goto out; } if (data->vendor != OFONO_VENDOR_WAVECOM_Q2XXX && !sm_supported[2] && !me_supported[2] && !mt_supported[2]) goto out; if (sm_supported[0] && sm_supported[1]) { supported = TRUE; data->store = AT_UTIL_SMS_STORE_SM; } if (me_supported[0] && me_supported[1]) { supported = TRUE; data->store = AT_UTIL_SMS_STORE_ME; } /* This seems to be a special case, where the modem will * pick & route the SMS to any of the storages supported by * mem1 */ if (mt_supported[2] && (sm_supported[0] || me_supported[0])) data->incoming = AT_UTIL_SMS_STORE_MT; if (sm_supported[2]) data->incoming = AT_UTIL_SMS_STORE_SM; if (me_supported[2]) data->incoming = AT_UTIL_SMS_STORE_ME; } out: if (!supported) return at_sms_not_supported(sms); set_cmgf(sms); } static void at_cmgf_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); gboolean supported = FALSE; if (ok) { GAtResultIter iter; int mode; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CMGF:")) goto out; if (!g_at_result_iter_open_list(&iter)) goto out; /* Look for mode 0 (PDU mode) */ while (g_at_result_iter_next_number(&iter, &mode)) if (mode == 0) supported = TRUE; } out: if (!supported) return at_sms_not_supported(sms); g_at_chat_send(data->chat, "AT+CPMS=?", cpms_prefix, at_cpms_query_cb, sms, NULL); } static void at_csms_status_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); gboolean supported = FALSE; if (ok) { GAtResultIter iter; int service; int mt; int mo; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSMS:")) goto out; switch (data->vendor) { case OFONO_VENDOR_HUAWEI: case OFONO_VENDOR_NOVATEL: g_at_result_iter_skip_next(&iter); service = 0; break; default: if (!g_at_result_iter_next_number(&iter, &service)) goto out; break; } if (!g_at_result_iter_next_number(&iter, &mt)) goto out; if (!g_at_result_iter_next_number(&iter, &mo)) goto out; if (service == 1) data->cnma_enabled = TRUE; if (mt == 1 && mo == 1) supported = TRUE; } out: if (!supported) return at_sms_not_supported(sms); /* Now query supported text format */ g_at_chat_send(data->chat, "AT+CMGF=?", cmgf_prefix, at_cmgf_query_cb, sms, NULL); } static void at_csms_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); g_at_chat_send(data->chat, "AT+CSMS?", csms_prefix, at_csms_status_cb, sms, NULL); } static void at_csms_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); gboolean cnma_supported = FALSE; GAtResultIter iter; int status_min, status_max; char buf[128]; if (!ok) return at_sms_not_supported(sms); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSMS:")) goto out; if (!g_at_result_iter_open_list(&iter)) goto out; while (g_at_result_iter_next_range(&iter, &status_min, &status_max)) if (status_min <= 1 && 1 <= status_max) cnma_supported = TRUE; DBG("CSMS query parsed successfully"); out: snprintf(buf, sizeof(buf), "AT+CSMS=%d", cnma_supported ? 1 : 0); g_at_chat_send(data->chat, buf, csms_prefix, at_csms_set_cb, sms, NULL); } static int at_sms_probe(struct ofono_sms *sms, unsigned int vendor, void *user) { GAtChat *chat = user; struct sms_data *data; data = g_new0(struct sms_data, 1); data->chat = g_at_chat_clone(chat); data->vendor = vendor; ofono_sms_set_data(sms, data); g_at_chat_send(data->chat, "AT+CSMS=?", csms_prefix, at_csms_query_cb, sms, NULL); return 0; } static void at_sms_remove(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); g_free(data->cnma_ack_pdu); if (data->timeout_source > 0) g_source_remove(data->timeout_source); g_at_chat_unref(data->chat); g_free(data); ofono_sms_set_data(sms, NULL); } static struct ofono_sms_driver driver = { .name = "atmodem", .probe = at_sms_probe, .remove = at_sms_remove, .sca_query = at_csca_query, .sca_set = at_csca_set, .submit = at_cmgs, .bearer_query = at_cgsms_query, .bearer_set = at_cgsms_set, }; void at_sms_init(void) { ofono_sms_driver_register(&driver); } void at_sms_exit(void) { ofono_sms_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/gnss.c0000644000015600001650000001351312671500024022624 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 ST-Ericsson AB. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" #include "vendor.h" struct gnss_data { GAtChat *chat; unsigned int vendor; }; static const char *none_prefix[] = { NULL }; static const char *cpos_prefix[] = { "+CPOS:", NULL }; static const char *cposr_prefix[] = { "+CPOSR:", NULL }; static void gnss_pr_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gnss_cb_t cb = cbd->cb; struct ofono_error error; DBG(""); decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_gnss_position_reporting(struct ofono_gnss *gnss, ofono_bool_t enable, ofono_gnss_cb_t cb, void *data) { struct gnss_data *ad = ofono_gnss_get_data(gnss); struct cb_data *cbd = cb_data_new(cb, data); DBG(""); if (enable) { g_at_chat_send(ad->chat, "AT+CPOSR=1", cposr_prefix, gnss_pr_cb, cbd, g_free); if (ad->vendor == OFONO_VENDOR_STE) g_at_chat_send(ad->chat, "AT*EPOSADRR=1", NULL, NULL, NULL, NULL); } else { g_at_chat_send(ad->chat, "AT+CPOSR=0", cposr_prefix, gnss_pr_cb, cbd, g_free); if (ad->vendor == OFONO_VENDOR_STE) g_at_chat_send(ad->chat, "AT*EPOSADRR=0", NULL, NULL, NULL, NULL); } } static void gnss_se_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gnss_cb_t cb = cbd->cb; struct ofono_error error; DBG(""); decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_gnss_send_element(struct ofono_gnss *gnss, const char *xml, ofono_gnss_cb_t cb, void *data) { struct gnss_data *ad = ofono_gnss_get_data(gnss); struct cb_data *cbd = cb_data_new(cb, data); char *buf = g_try_new(char, strlen(xml) + 10); int len; DBG(""); if (buf == NULL) goto error; len = sprintf(buf, "AT+CPOS\r"); len += sprintf(buf + len, "%s", xml); if (g_at_chat_send_and_expect_short_prompt(ad->chat, buf, cpos_prefix, gnss_se_cb, cbd, g_free) > 0) { g_free(buf); return; } error: g_free(buf); g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static gboolean gnss_parse_report(GAtResult *result, const char *prefix, const char **xml) { GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, prefix)) return FALSE; if (!g_at_result_iter_next_unquoted_string(&iter, xml)) return FALSE; return TRUE; } static void gnss_report(GAtResult *result, gpointer user_data) { const char *xml; DBG(""); xml = NULL; if (!gnss_parse_report(result, "+CPOSR:", &xml)) { ofono_error("Unable to parse CPOSR notification"); return; } if (xml == NULL) { ofono_error("Unable to parse CPOSR notification"); return; } DBG("%s", xml); } static void at_gnss_reset_notify(GAtResult *result, gpointer user_data) { struct ofono_gnss *gnss = user_data; DBG(""); ofono_gnss_notify_posr_reset(gnss); } static void at_gnss_not_supported(struct ofono_gnss *gnss) { ofono_error("gnss not supported by this modem."); ofono_gnss_remove(gnss); } static void at_gnss_cposr_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_gnss *gnss = user_data; struct gnss_data *ad = ofono_gnss_get_data(gnss); DBG(""); if (!ok) { at_gnss_not_supported(gnss); return; } g_at_chat_register(ad->chat, "+CPOSR:", gnss_report, FALSE, gnss, NULL); if (ad->vendor == OFONO_VENDOR_STE) g_at_chat_register(ad->chat, "*EPOSADRR:", at_gnss_reset_notify, FALSE, gnss, NULL); ofono_gnss_register(gnss); } static void at_gnss_cpos_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_gnss *gnss = user_data; struct gnss_data *ad = ofono_gnss_get_data(gnss); DBG(""); if (!ok) { at_gnss_not_supported(gnss); return; } g_at_chat_send(ad->chat, "AT+CPOSR=?", none_prefix, at_gnss_cposr_support_cb, gnss, NULL); } static int at_gnss_probe(struct ofono_gnss *gnss, unsigned int vendor, void *user) { GAtChat *chat = user; struct gnss_data *gd; DBG(""); gd = g_try_new0(struct gnss_data, 1); if (gd == NULL) return -ENOMEM; gd->chat = g_at_chat_clone(chat); gd->vendor = vendor; ofono_gnss_set_data(gnss, gd); g_at_chat_send(gd->chat, "AT+CPOS=?", none_prefix, at_gnss_cpos_support_cb, gnss, NULL); return 0; } static void at_gnss_remove(struct ofono_gnss *gnss) { struct gnss_data *gd = ofono_gnss_get_data(gnss); DBG(""); ofono_gnss_set_data(gnss, NULL); g_at_chat_unref(gd->chat); g_free(gd); } static struct ofono_gnss_driver driver = { .name = "atmodem", .probe = at_gnss_probe, .remove = at_gnss_remove, .send_element = at_gnss_send_element, .set_position_reporting = at_gnss_position_reporting, }; void at_gnss_init(void) { ofono_gnss_driver_register(&driver); } void at_gnss_exit(void) { ofono_gnss_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/call-meter.c0000644000015600001650000002015512671500024023677 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" static const char *none_prefix[] = { NULL }; static const char *caoc_prefix[] = { "+CAOC:", NULL }; static const char *cacm_prefix[] = { "+CACM:", NULL }; static const char *camm_prefix[] = { "+CAMM:", NULL }; static const char *cpuc_prefix[] = { "+CPUC:", NULL }; static void caoc_cacm_camm_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_meter_query_cb_t cb = cbd->cb; struct ofono_error error; GAtResultIter iter; const char *meter_hex; char *end; int meter; 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, cbd->user)) goto error; if (g_at_result_iter_next_string(&iter, &meter_hex) == FALSE) goto error; meter = strtol(meter_hex, &end, 16); if (*end) goto error; cb(&error, meter, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void cccm_notify(GAtResult *result, gpointer user_data) { struct ofono_call_meter *cm = user_data; GAtResultIter iter; const char *meter_hex; char *end; int meter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CCCM:")) return; if (g_at_result_iter_next_string(&iter, &meter_hex) == FALSE) goto error; meter = strtol(meter_hex, &end, 16); if (*end) goto error; ofono_call_meter_changed_notify(cm, meter); return; error: ofono_error("Invalid CCCM value"); } static void at_caoc_query(struct ofono_call_meter *cm, ofono_call_meter_query_cb_t cb, void *data) { GAtChat *chat = ofono_call_meter_get_data(cm); struct cb_data *cbd = cb_data_new(cb, data); cbd->user = "+CAOC:"; if (g_at_chat_send(chat, "AT+CAOC=0", caoc_prefix, caoc_cacm_camm_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void at_cacm_query(struct ofono_call_meter *cm, ofono_call_meter_query_cb_t cb, void *data) { GAtChat *chat = ofono_call_meter_get_data(cm); struct cb_data *cbd = cb_data_new(cb, data); cbd->user = "+CACM:"; if (g_at_chat_send(chat, "AT+CACM?", cacm_prefix, caoc_cacm_camm_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void generic_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_meter_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_cacm_set(struct ofono_call_meter *cm, const char *passwd, ofono_call_meter_set_cb_t cb, void *data) { GAtChat *chat = ofono_call_meter_get_data(cm); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; snprintf(buf, sizeof(buf), "AT+CACM=\"%s\"", passwd); if (g_at_chat_send(chat, buf, none_prefix, generic_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_camm_query(struct ofono_call_meter *cm, ofono_call_meter_query_cb_t cb, void *data) { GAtChat *chat = ofono_call_meter_get_data(cm); struct cb_data *cbd = cb_data_new(cb, data); cbd->user = "+CAMM:"; if (g_at_chat_send(chat, "AT+CAMM?", camm_prefix, caoc_cacm_camm_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void at_camm_set(struct ofono_call_meter *cm, int accmax, const char *passwd, ofono_call_meter_set_cb_t cb, void *data) { GAtChat *chat = ofono_call_meter_get_data(cm); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; snprintf(buf, sizeof(buf), "AT+CAMM=\"%06X\",\"%s\"", accmax, passwd); if (g_at_chat_send(chat, buf, none_prefix, generic_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void cpuc_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_meter_puct_query_cb_t cb = cbd->cb; struct ofono_error error; GAtResultIter iter; const char *currency, *ppu; char currency_buf[64]; double ppuval; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, 0, 0, cbd->data); return; } g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, cbd->user) != TRUE) goto error; if (g_at_result_iter_next_string(&iter, ¤cy) != TRUE) goto error; strncpy(currency_buf, currency, sizeof(currency_buf)); if (g_at_result_iter_next_string(&iter, &ppu) != TRUE) goto error; ppuval = strtod(ppu, NULL); cb(&error, currency_buf, ppuval, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, 0, 0, cbd->data); } static void at_cpuc_query(struct ofono_call_meter *cm, ofono_call_meter_puct_query_cb_t cb, void *data) { GAtChat *chat = ofono_call_meter_get_data(cm); struct cb_data *cbd = cb_data_new(cb, data); cbd->user = "+CPUC:"; if (g_at_chat_send(chat, "AT+CPUC?", cpuc_prefix, cpuc_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, 0, 0, data); } static void at_cpuc_set(struct ofono_call_meter *cm, const char *currency, double ppu, const char *passwd, ofono_call_meter_set_cb_t cb, void *data) { GAtChat *chat = ofono_call_meter_get_data(cm); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; snprintf(buf, sizeof(buf), "AT+CPUC=\"%s\",\"%f\",\"%s\"", currency, ppu, passwd); if (g_at_chat_send(chat, buf, none_prefix, generic_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void ccwv_notify(GAtResult *result, gpointer user_data) { struct ofono_call_meter *cm = user_data; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CCWV")) return; ofono_call_meter_maximum_notify(cm); } static void at_call_meter_initialized(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_call_meter *cm = user_data; GAtChat *chat = ofono_call_meter_get_data(cm); g_at_chat_register(chat, "+CCCM:", cccm_notify, FALSE, cm, NULL); g_at_chat_register(chat, "+CCWV", ccwv_notify, FALSE, cm, NULL); ofono_call_meter_register(cm); } static int at_caoc_probe(struct ofono_call_meter *cm, unsigned int vendor, void *data) { GAtChat *chat = data; chat = g_at_chat_clone(chat); ofono_call_meter_set_data(cm, chat); g_at_chat_send(chat, "AT+CAOC=2", NULL, NULL, NULL, NULL); g_at_chat_send(chat, "AT+CCWE=1", NULL, at_call_meter_initialized, cm, NULL); return 0; } static void at_caoc_remove(struct ofono_call_meter *cm) { GAtChat *chat = ofono_call_meter_get_data(cm); g_at_chat_unref(chat); ofono_call_meter_set_data(cm, NULL); } static struct ofono_call_meter_driver driver = { .name = "atmodem", .probe = at_caoc_probe, .remove = at_caoc_remove, .call_meter_query = at_caoc_query, .acm_query = at_cacm_query, .acm_reset = at_cacm_set, .acm_max_query = at_camm_query, .acm_max_set = at_camm_set, .puct_query = at_cpuc_query, .puct_set = at_cpuc_set, }; void at_call_meter_init(void) { ofono_call_meter_driver_register(&driver); } void at_call_meter_exit(void) { ofono_call_meter_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/voicecall.c0000644000015600001650000007163712671500024023626 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "vendor.h" #include "gatchat.h" #include "gatresult.h" #include "common.h" #include "atmodem.h" /* Amount of ms we wait between CLCC calls */ #define POLL_CLCC_INTERVAL 500 /* Amount of time we give for CLIP to arrive before we commence CLCC poll */ #define CLIP_INTERVAL 200 /* When +VTD returns 0, an unspecified manufacturer-specific delay is used */ #define TONE_DURATION 1000 static const char *clcc_prefix[] = { "+CLCC:", NULL }; static const char *none_prefix[] = { NULL }; /* According to 27.007 COLP is an intermediate status for ATD */ static const char *atd_prefix[] = { "+COLP:", NULL }; #define FLAG_NEED_CLIP 1 #define FLAG_NEED_CNAP 2 #define FLAG_NEED_CDIP 4 struct voicecall_data { GSList *calls; unsigned int local_release; unsigned int clcc_source; GAtChat *chat; unsigned int vendor; unsigned int tone_duration; guint vts_source; unsigned int vts_delay; unsigned char flags; }; struct release_id_req { struct ofono_voicecall *vc; ofono_voicecall_cb_t cb; void *data; int id; }; struct change_state_req { struct ofono_voicecall *vc; ofono_voicecall_cb_t cb; void *data; int affected_types; }; static gboolean poll_clcc(gpointer user_data); static int class_to_call_type(int cls) { switch (cls) { case 1: return 0; case 4: return 2; case 8: return 9; default: return 1; } } static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, int direction, int status, const char *num, int num_type, int clip) { struct voicecall_data *d = ofono_voicecall_get_data(vc); struct ofono_call *call; /* Generate a call structure for the waiting call */ call = g_try_new(struct ofono_call, 1); if (call == NULL) return NULL; ofono_call_init(call); call->id = ofono_voicecall_get_next_callid(vc); call->type = type; call->direction = direction; call->status = status; if (clip != 2) { strncpy(call->phone_number.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH); call->phone_number.type = num_type; } call->clip_validity = clip; call->cnap_validity = CNAP_VALIDITY_NOT_AVAILABLE; d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare); return call; } static void clcc_poll_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GSList *calls; GSList *n, *o; struct ofono_call *nc, *oc; gboolean poll_again = FALSE; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { /* * On certain Option GTM modems CLCC polling can fail * with a CME ERROR: 100. It seems to be safe to ignore * it and continue polling anyway */ if (vd->vendor == OFONO_VENDOR_QUALCOMM_MSM && error.type == OFONO_ERROR_TYPE_CME && error.error == 100) { poll_again = TRUE; goto poll_again; } ofono_error("We are polling CLCC and received an error"); ofono_error("All bets are off for call management"); return; } calls = at_util_parse_clcc(result, NULL); n = calls; o = vd->calls; while (n || o) { nc = n ? n->data : NULL; oc = o ? o->data : NULL; switch (vd->vendor) { case OFONO_VENDOR_QUALCOMM_MSM: poll_again = TRUE; break; default: if (nc && nc->status >= CALL_STATUS_DIALING && nc->status <= CALL_STATUS_WAITING) poll_again = TRUE; break; } if (oc && (nc == NULL || (nc->id > oc->id))) { enum ofono_disconnect_reason reason; if (vd->local_release & (1 << oc->id)) reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP; else reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP; if (!oc->type) ofono_voicecall_disconnected(vc, oc->id, reason, NULL); o = o->next; } else if (nc && (oc == NULL || (nc->id < oc->id))) { /* new call, signal it */ if (nc->type == 0) ofono_voicecall_notify(vc, nc); n = n->next; } else { /* * Always use the clip_validity from old call * the only place this is truly told to us is * in the CLIP notify, the rest are fudged * anyway. Useful when RING, CLIP is used, * and we're forced to use CLCC and clip_validity * is 1 */ if (oc->clip_validity == 1) nc->clip_validity = oc->clip_validity; /* * CNAP doesn't arrive as part of CLCC, always * re-use from the old call */ strncpy(nc->name, oc->name, OFONO_MAX_CALLER_NAME_LENGTH); nc->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0'; nc->cnap_validity = oc->cnap_validity; /* * CDIP doesn't arrive as part of CLCC, always * re-use from the old call */ memcpy(&nc->called_number, &oc->called_number, sizeof(oc->called_number)); /* * If the CLIP is not provided and the CLIP never * arrives, or RING is used, then signal the call * here */ if (nc->status == CALL_STATUS_INCOMING && (vd->flags & FLAG_NEED_CLIP)) { if (nc->type == 0) ofono_voicecall_notify(vc, nc); vd->flags &= ~FLAG_NEED_CLIP; } else if (memcmp(nc, oc, sizeof(*nc)) && nc->type == 0) ofono_voicecall_notify(vc, nc); n = n->next; o = o->next; } } g_slist_foreach(vd->calls, (GFunc) g_free, NULL); g_slist_free(vd->calls); vd->calls = calls; vd->local_release = 0; poll_again: if (poll_again && !vd->clcc_source) vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL, poll_clcc, vc); } static gboolean poll_clcc(gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); vd->clcc_source = 0; return FALSE; } static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct change_state_req *req = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (ok && req->affected_types) { GSList *l; struct ofono_call *call; for (l = vd->calls; l; l = l->next) { call = l->data; if (req->affected_types & (1 << call->status)) vd->local_release |= (1 << call->id); } } g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, req->vc, NULL); /* We have to callback after we schedule a poll if required */ req->cb(&error, req->data); } static void release_id_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct release_id_req *req = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (ok) vd->local_release = 1 << req->id; g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, req->vc, NULL); /* We have to callback after we schedule a poll if required */ req->cb(&error, req->data); } static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_voicecall *vc = cbd->user; struct voicecall_data *vd = ofono_voicecall_get_data(vc); ofono_voicecall_cb_t cb = cbd->cb; GAtResultIter iter; const char *num; int type = 128; int validity = 2; struct ofono_error error; struct ofono_call *call; GSList *l; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) goto out; /* On a success, make sure to put all active calls on hold */ for (l = vd->calls; l; l = l->next) { call = l->data; if (call->status != CALL_STATUS_ACTIVE) continue; call->status = CALL_STATUS_HELD; ofono_voicecall_notify(vc, call); } g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "+COLP:")) { g_at_result_iter_next_string(&iter, &num); g_at_result_iter_next_number(&iter, &type); if (strlen(num) > 0) validity = 0; else validity = 2; DBG("colp_notify: %s %d %d", num, type, validity); } /* Generate a voice call that was just dialed, we guess the ID */ call = create_call(vc, 0, 0, CALL_STATUS_DIALING, num, type, validity); if (call == NULL) { ofono_error("Unable to malloc, call tracking will fail!"); return; } /* oFono core will generate a call with the dialed number * inside its dial callback. Unless we got COLP information * we do not need to communicate that a call is being * dialed */ if (validity != 2) ofono_voicecall_notify(vc, call); if (!vd->clcc_source) vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL, poll_clcc, vc); out: cb(&error, cbd->data); } static void at_dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, enum ofono_clir_option clir, 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; if (ph->type == 145) snprintf(buf, sizeof(buf), "ATD+%s", ph->number); else snprintf(buf, sizeof(buf), "ATD%s", ph->number); switch (clir) { case OFONO_CLIR_OPTION_INVOCATION: strcat(buf, "I"); break; case OFONO_CLIR_OPTION_SUPPRESSION: strcat(buf, "i"); break; default: break; } strcat(buf, ";"); if (g_at_chat_send(vd->chat, buf, atd_prefix, atd_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_template(const char *cmd, struct ofono_voicecall *vc, GAtResultFunc result_cb, unsigned int affected_types, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct change_state_req *req = g_try_new0(struct change_state_req, 1); if (req == NULL) goto error; req->vc = vc; req->cb = cb; req->data = data; req->affected_types = affected_types; if (g_at_chat_send(vd->chat, cmd, none_prefix, result_cb, req, g_free) > 0) return; error: g_free(req); CALLBACK_WITH_FAILURE(cb, data); } static void at_answer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { at_template("ATA", vc, generic_cb, 0, cb, data); } static void at_hangup(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { /* Hangup active call */ at_template("AT+CHUP", vc, generic_cb, 0x3f, cb, data); } static void clcc_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GSList *l; if (!ok) return; vd->calls = at_util_parse_clcc(result, NULL); for (l = vd->calls; l; l = l->next) ofono_voicecall_notify(vc, l->data); } static void at_hold_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { at_template("AT+CHLD=2", vc, generic_cb, 0, cb, data); } static void at_release_all_held(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { unsigned int held_status = 1 << CALL_STATUS_HELD; at_template("AT+CHLD=0", vc, generic_cb, held_status, cb, data); } static void at_set_udub(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { unsigned int incoming_or_waiting = (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING); at_template("AT+CHLD=0", vc, generic_cb, incoming_or_waiting, cb, data); } static void at_release_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { at_template("AT+CHLD=1", vc, generic_cb, 0x1, cb, data); } static void at_release_specific(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct release_id_req *req = g_try_new0(struct release_id_req, 1); char buf[32]; if (req == NULL) goto error; req->vc = vc; req->cb = cb; req->data = data; req->id = id; snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id); if (g_at_chat_send(vd->chat, buf, none_prefix, release_id_cb, req, g_free) > 0) return; error: g_free(req); CALLBACK_WITH_FAILURE(cb, data); } static void at_private_chat(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { char buf[32]; snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id); at_template(buf, vc, generic_cb, 0, cb, data); } static void at_create_multiparty(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { at_template("AT+CHLD=3", vc, generic_cb, 0, cb, data); } static void at_transfer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { /* Held & Active */ unsigned int transfer = 0x1 | 0x2; /* Transfer can puts held & active calls together and disconnects * from both. However, some networks support transferring of * dialing/ringing calls as well. */ transfer |= 0x4 | 0x8; at_template("AT+CHLD=4", vc, generic_cb, transfer, cb, data); } static void at_deflect(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, ofono_voicecall_cb_t cb, void *data) { char buf[128]; unsigned int incoming_or_waiting = (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING); snprintf(buf, sizeof(buf), "AT+CTFR=%s,%d", ph->number, ph->type); at_template(buf, vc, generic_cb, incoming_or_waiting, cb, data); } static gboolean vts_timeout_cb(gpointer user_data) { struct cb_data *cbd = user_data; struct voicecall_data *vd = cbd->user; ofono_voicecall_cb_t cb = cbd->cb; vd->vts_source = 0; CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); return FALSE; } static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct voicecall_data *vd = cbd->user; ofono_voicecall_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, cbd->data); g_free(cbd); return; } vd->vts_source = g_timeout_add(vd->vts_delay, vts_timeout_cb, cbd); } static void at_send_dtmf(struct ofono_voicecall *vc, const char *dtmf, 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); int len = strlen(dtmf); int s; int i; char *buf; cbd->user = vd; /* strlen("+VTS=T;") = 7 + initial AT + null */ buf = g_try_new(char, len * 9 + 3); if (buf == NULL) goto error; s = sprintf(buf, "AT+VTS=%c", dtmf[0]); for (i = 1; i < len; i++) s += sprintf(buf + s, ";+VTS=%c", dtmf[i]); vd->vts_delay = vd->tone_duration * len; s = g_at_chat_send(vd->chat, buf, none_prefix, vts_cb, cbd, NULL); g_free(buf); if (s > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void ring_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct ofono_call *call; /* See comment in CRING */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), at_util_call_compare_by_status)) return; /* RING can repeat, ignore if we already have an incoming call */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status)) return; /* Generate an incoming call of unknown type */ call = create_call(vc, 9, 1, CALL_STATUS_INCOMING, NULL, 128, 2); if (call == NULL) { ofono_error("Couldn't create call, call management is fubar!"); return; } /* We don't know the call type, we must run clcc */ vd->clcc_source = g_timeout_add(CLIP_INTERVAL, poll_clcc, vc); vd->flags = FLAG_NEED_CLIP | FLAG_NEED_CNAP | FLAG_NEED_CDIP; } static void cring_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *line; int type; /* Handle the following situation: * Active Call + Waiting Call. Active Call is Released. The Waiting * call becomes Incoming and RING/CRING indications are signaled. * Sometimes these arrive before we managed to poll CLCC to find about * the stage change. If this happens, simply ignore the RING/CRING * when a waiting call exists (cannot have waiting + incoming in GSM) */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), at_util_call_compare_by_status)) return; /* CRING can repeat, ignore if we already have an incoming call */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status)) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CRING:")) return; line = g_at_result_iter_raw_line(&iter); if (line == NULL) return; /* Ignore everything that is not voice for now */ if (!strcasecmp(line, "VOICE")) type = 0; else type = 9; /* Generate an incoming call */ create_call(vc, type, 1, CALL_STATUS_INCOMING, NULL, 128, 2); /* We have a call, and call type but don't know the number and * must wait for the CLIP to arrive before announcing the call. * So we wait, and schedule the clcc call. If the CLIP arrives * earlier, we announce the call there */ vd->clcc_source = g_timeout_add(CLIP_INTERVAL, poll_clcc, vc); vd->flags = FLAG_NEED_CLIP | FLAG_NEED_CNAP | FLAG_NEED_CDIP; DBG(""); } static void clip_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *num; int type, validity; GSList *l; struct ofono_call *call; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status); if (l == NULL) { ofono_error("CLIP for unknown call"); return; } /* We have already saw a CLIP for this call, no need to parse again */ if ((vd->flags & FLAG_NEED_CLIP) == 0) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CLIP:")) return; if (!g_at_result_iter_next_string(&iter, &num)) return; if (!g_at_result_iter_next_number(&iter, &type)) return; if (strlen(num) > 0) validity = CLIP_VALIDITY_VALID; else validity = CLIP_VALIDITY_NOT_AVAILABLE; /* Skip subaddr, satype and alpha */ g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); /* If we have CLI validity field, override our guessed value */ g_at_result_iter_next_number(&iter, &validity); DBG("%s %d %d", num, type, validity); call = l->data; 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 = type; call->clip_validity = validity; if (call->type == 0) ofono_voicecall_notify(vc, call); vd->flags &= ~FLAG_NEED_CLIP; } static void cdip_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *num; int type; GSList *l; struct ofono_call *call; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status); if (l == NULL) { ofono_error("CDIP for unknown call"); return; } /* We have already saw a CDIP for this call, no need to parse again */ if ((vd->flags & FLAG_NEED_CDIP) == 0) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CDIP:")) return; if (!g_at_result_iter_next_string(&iter, &num)) return; if (!g_at_result_iter_next_number(&iter, &type)) return; DBG("%s %d", num, type); call = l->data; strncpy(call->called_number.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH); call->called_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0'; call->called_number.type = type; /* Only signal the call here if we already signaled it to the core */ if (call->type == 0 && (vd->flags & FLAG_NEED_CLIP) == 0) ofono_voicecall_notify(vc, call); vd->flags &= ~FLAG_NEED_CDIP; } static void cnap_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *name; int validity; GSList *l; struct ofono_call *call; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status); if (l == NULL) { ofono_error("CNAP for unknown call"); return; } /* We have already saw a CLIP for this call, no need to parse again */ if ((vd->flags & FLAG_NEED_CNAP) == 0) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CNAP:")) return; if (!g_at_result_iter_next_string(&iter, &name)) return; if (strlen(name) > 0) validity = CNAP_VALIDITY_VALID; else validity = CNAP_VALIDITY_NOT_AVAILABLE; /* If we have CNI validity field, override our guessed value */ g_at_result_iter_next_number(&iter, &validity); DBG("%s %d", name, validity); call = l->data; strncpy(call->name, name, OFONO_MAX_CALLER_NAME_LENGTH); call->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0'; call->cnap_validity = validity; /* Only signal the call here if we already signaled it to the core */ if (call->type == 0 && (vd->flags & FLAG_NEED_CLIP) == 0) ofono_voicecall_notify(vc, call); vd->flags &= ~FLAG_NEED_CNAP; } static void ccwa_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *num; int num_type, validity, cls; struct ofono_call *call; /* Some modems resend CCWA, ignore it the second time around */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), at_util_call_compare_by_status)) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CCWA:")) return; if (!g_at_result_iter_next_string(&iter, &num)) return; if (!g_at_result_iter_next_number(&iter, &num_type)) return; if (!g_at_result_iter_next_number(&iter, &cls)) return; /* Skip alpha field */ g_at_result_iter_skip_next(&iter); if (strlen(num) > 0) validity = 0; else validity = 2; /* If we have CLI validity field, override our guessed value */ g_at_result_iter_next_number(&iter, &validity); DBG("%s %d %d %d", num, num_type, cls, validity); call = create_call(vc, class_to_call_type(cls), 1, CALL_STATUS_WAITING, num, num_type, validity); if (call == NULL) { ofono_error("Unable to malloc. Call management is fubar"); return; } if (call->type == 0) /* Only notify voice calls */ ofono_voicecall_notify(vc, call); if (vd->clcc_source == 0) vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL, poll_clcc, vc); } static void no_carrier_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); } static void no_answer_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); } static void busy_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); /* Call was rejected, most likely due to network congestion * or UDUB on the other side * TODO: Handle UDUB or other conditions somehow */ g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); } static void cssi_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; GAtResultIter iter; int code, index; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSSI:")) return; if (!g_at_result_iter_next_number(&iter, &code)) return; if (!g_at_result_iter_next_number(&iter, &index)) index = 0; ofono_voicecall_ssn_mo_notify(vc, 0, code, index); } static void cssu_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; GAtResultIter iter; int code; int index; const char *num; struct ofono_phone_number ph; ph.number[0] = '\0'; ph.type = 129; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSSU:")) return; if (!g_at_result_iter_next_number(&iter, &code)) return; if (!g_at_result_iter_next_number_default(&iter, -1, &index)) goto out; if (!g_at_result_iter_next_string(&iter, &num)) goto out; strncpy(ph.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH); if (!g_at_result_iter_next_number(&iter, &ph.type)) return; out: ofono_voicecall_ssn_mt_notify(vc, 0, code, index, &ph); } static void vtd_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; int duration; if (!ok) return; g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, "+VTD:"); if (!g_at_result_iter_next_number(&iter, &duration)) return; if (duration) vd->tone_duration = duration * 100; } static void at_voicecall_initialized(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); DBG("voicecall_init: registering to notifications"); g_at_chat_register(vd->chat, "RING", ring_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CRING:", cring_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CDIP:", cdip_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CNAP:", cnap_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL); /* Modems with 'better' call progress indicators should * probably not even bother registering to these */ g_at_chat_register(vd->chat, "NO CARRIER", no_carrier_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "NO ANSWER", no_answer_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "BUSY", busy_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CSSI:", cssi_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CSSU:", cssu_notify, FALSE, vc, NULL); ofono_voicecall_register(vc); /* Populate the call list */ g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_cb, vc, NULL); } static int at_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, void *data) { GAtChat *chat = data; struct voicecall_data *vd; vd = g_try_new0(struct voicecall_data, 1); if (vd == NULL) return -ENOMEM; vd->chat = g_at_chat_clone(chat); vd->vendor = vendor; vd->tone_duration = TONE_DURATION; ofono_voicecall_set_data(vc, vd); g_at_chat_send(vd->chat, "AT+CRC=1", NULL, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CLIP=1", NULL, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CDIP=1", NULL, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CNAP=1", NULL, NULL, NULL, NULL); switch (vd->vendor) { case OFONO_VENDOR_QUALCOMM_MSM: g_at_chat_send(vd->chat, "AT+COLP=0", NULL, NULL, NULL, NULL); break; default: g_at_chat_send(vd->chat, "AT+COLP=1", NULL, NULL, NULL, NULL); break; } g_at_chat_send(vd->chat, "AT+CSSN=1,1", NULL, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+VTD?", NULL, vtd_query_cb, vc, NULL); g_at_chat_send(vd->chat, "AT+CCWA=1", NULL, at_voicecall_initialized, vc, NULL); return 0; } static void at_voicecall_remove(struct ofono_voicecall *vc) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); if (vd->clcc_source) g_source_remove(vd->clcc_source); if (vd->vts_source) g_source_remove(vd->vts_source); g_slist_foreach(vd->calls, (GFunc) g_free, NULL); g_slist_free(vd->calls); ofono_voicecall_set_data(vc, NULL); g_at_chat_unref(vd->chat); g_free(vd); } static struct ofono_voicecall_driver driver = { .name = "atmodem", .probe = at_voicecall_probe, .remove = at_voicecall_remove, .dial = at_dial, .answer = at_answer, .hangup_all = at_hangup, .hold_all_active = at_hold_all_active, .release_all_held = at_release_all_held, .set_udub = at_set_udub, .release_all_active = at_release_all_active, .release_specific = at_release_specific, .private_chat = at_private_chat, .create_multiparty = at_create_multiparty, .transfer = at_transfer, .deflect = at_deflect, .swap_without_accept = NULL, .send_tones = at_send_dtmf }; void at_voicecall_init(void) { ofono_voicecall_driver_register(&driver); } void at_voicecall_exit(void) { ofono_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/atutil.h0000644000015600001650000000740012671500024023157 0ustar pbuserpbgroup00000000000000/* * * 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 * */ enum at_util_sms_store { AT_UTIL_SMS_STORE_SM = 0, AT_UTIL_SMS_STORE_ME = 1, AT_UTIL_SMS_STORE_MT = 2, AT_UTIL_SMS_STORE_SR = 3, AT_UTIL_SMS_STORE_BM = 4, }; /* 3GPP TS 27.007 Release 8 Section 5.5 */ enum at_util_charset { AT_UTIL_CHARSET_GSM = 0x1, AT_UTIL_CHARSET_HEX = 0x2, AT_UTIL_CHARSET_IRA = 0x4, AT_UTIL_CHARSET_PCCP437 = 0x8, AT_UTIL_CHARSET_PCDN = 0x10, AT_UTIL_CHARSET_UCS2 = 0x20, AT_UTIL_CHARSET_UTF8 = 0x40, AT_UTIL_CHARSET_8859_1 = 0x80, AT_UTIL_CHARSET_8859_2 = 0x100, AT_UTIL_CHARSET_8859_3 = 0x200, AT_UTIL_CHARSET_8859_4 = 0x400, AT_UTIL_CHARSET_8859_5 = 0x800, AT_UTIL_CHARSET_8859_6 = 0x1000, AT_UTIL_CHARSET_8859_C = 0x2000, AT_UTIL_CHARSET_8859_A = 0x4000, AT_UTIL_CHARSET_8859_G = 0x8000, AT_UTIL_CHARSET_8859_H = 0x10000, }; typedef void (*at_util_sim_inserted_cb_t)(gboolean present, void *userdata); void decode_at_error(struct ofono_error *error, const char *final); gint at_util_call_compare_by_status(gconstpointer a, gconstpointer b); gint at_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b); gint at_util_call_compare_by_id(gconstpointer a, gconstpointer b); gint at_util_call_compare(gconstpointer a, gconstpointer b); GSList *at_util_parse_clcc(GAtResult *result, unsigned int *mpty_ids); gboolean at_util_parse_reg(GAtResult *result, const char *prefix, int *mode, int *status, int *lac, int *ci, int *tech, unsigned int vendor); gboolean at_util_parse_reg_unsolicited(GAtResult *result, const char *prefix, int *status, int *lac, int *ci, int *tech, unsigned int vendor); gboolean at_util_parse_sms_index_delivery(GAtResult *result, const char *prefix, enum at_util_sms_store *store, int *index); gboolean at_util_parse_cscs_supported(GAtResult *result, int *supported); gboolean at_util_parse_cscs_query(GAtResult *result, enum at_util_charset *charset); gboolean at_util_parse_attr(GAtResult *result, const char *prefix, const char **out_attr); struct at_util_sim_state_query *at_util_sim_state_query_new(GAtChat *chat, guint interval, guint num_times, at_util_sim_inserted_cb_t cb, void *userdata, GDestroyNotify destroy); void at_util_sim_state_query_free(struct at_util_sim_state_query *req); 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 = g_new0(struct cb_data, 1); ret->cb = cb; ret->data = data; return ret; } static inline int at_util_convert_signal_strength(int strength) { int result; if (strength == 99) result = -1; else result = (strength * 100) / 31; return result; } #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) ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/sim-auth.c0000644000015600001650000000676312671500024023412 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #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); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/call-forwarding.c0000644000015600001650000001525712671500024024734 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" static const char *none_prefix[] = { NULL }; static const char *ccfc_prefix[] = { "+CCFC:", NULL }; static void ccfc_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_forwarding_query_cb_t cb = cbd->cb; struct ofono_error error; GAtResultIter iter; int num = 0; struct ofono_call_forwarding_condition *list = NULL; int i; int maxlen; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) goto out; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+CCFC:")) num += 1; /* Specification is really unclear about this * generate status=0 for all classes just in case */ if (num == 0) { list = g_new0(struct ofono_call_forwarding_condition, 1); num = 1; list->status = 0; list->cls = GPOINTER_TO_INT(cbd->user); goto out; } list = g_new(struct ofono_call_forwarding_condition, num); g_at_result_iter_init(&iter, result); maxlen = OFONO_MAX_PHONE_NUMBER_LENGTH; for (num = 0; g_at_result_iter_next(&iter, "+CCFC:"); num++) { const char *str; g_at_result_iter_next_number(&iter, &(list[num].status)); g_at_result_iter_next_number(&iter, &(list[num].cls)); list[num].phone_number.number[0] = '\0'; list[num].phone_number.type = 129; list[num].time = 20; if (!g_at_result_iter_next_string(&iter, &str)) continue; strncpy(list[num].phone_number.number, str, maxlen); list[num].phone_number.number[maxlen] = '\0'; g_at_result_iter_next_number(&iter, &(list[num].phone_number.type)); if (!g_at_result_iter_skip_next(&iter)) continue; if (!g_at_result_iter_skip_next(&iter)) continue; g_at_result_iter_next_number(&iter, &(list[num].time)); } for (i = 0; i < num; i++) DBG("ccfc_cb: %d, %d, %s(%d) - %d sec", list[i].status, list[i].cls, list[i].phone_number.number, list[i].phone_number.type, list[i].time); out: cb(&error, num, list, cbd->data); g_free(list); } static void at_ccfc_query(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_query_cb_t cb, void *data) { GAtChat *chat = ofono_call_forwarding_get_data(cf); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; cbd->user = GINT_TO_POINTER(cls); if (cls == 7) snprintf(buf, sizeof(buf), "AT+CCFC=%d,2", type); else snprintf(buf, sizeof(buf), "AT+CCFC=%d,2,,,%d", type, cls); if (g_at_chat_send(chat, buf, ccfc_prefix, ccfc_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, 0, NULL, data); } static void ccfc_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_forwarding_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_ccfc_set(struct ofono_call_forwarding *cf, const char *buf, ofono_call_forwarding_set_cb_t cb, void *data) { GAtChat *chat = ofono_call_forwarding_get_data(cf); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(chat, buf, none_prefix, ccfc_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_ccfc_erasure(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data) { char buf[128]; int len; len = snprintf(buf, sizeof(buf), "AT+CCFC=%d,4", type); if (cls != 7) snprintf(buf + len, sizeof(buf) - len, ",,,%d", cls); at_ccfc_set(cf, buf, cb, data); } static void at_ccfc_deactivation(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data) { char buf[128]; int len; len = snprintf(buf, sizeof(buf), "AT+CCFC=%d,0", type); if (cls != 7) snprintf(buf + len, sizeof(buf) - len, ",,,%d", cls); at_ccfc_set(cf, buf, cb, data); } static void at_ccfc_activation(struct ofono_call_forwarding *cf, int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data) { char buf[128]; int len; len = snprintf(buf, sizeof(buf), "AT+CCFC=%d,1", type); if (cls != 7) snprintf(buf + len, sizeof(buf) - len, ",,,%d", cls); at_ccfc_set(cf, buf, cb, data); } static void at_ccfc_registration(struct ofono_call_forwarding *cf, int type, int cls, const struct ofono_phone_number *ph, int time, ofono_call_forwarding_set_cb_t cb, void *data) { char buf[128]; int offset; offset = snprintf(buf, sizeof(buf), "AT+CCFC=%d,3,\"%s\",%d,%d", type, ph->number, ph->type, cls); if (type == 2 || type == 4 || type == 5) snprintf(buf+offset, sizeof(buf) - offset, ",,,%d", time); at_ccfc_set(cf, buf, cb, data); } static gboolean at_ccfc_register(gpointer user) { struct ofono_call_forwarding *cf = user; ofono_call_forwarding_register(cf); return FALSE; } static int at_ccfc_probe(struct ofono_call_forwarding *cf, unsigned int vendor, void *data) { GAtChat *chat = data; ofono_call_forwarding_set_data(cf, g_at_chat_clone(chat)); g_idle_add(at_ccfc_register, cf); return 0; } static void at_ccfc_remove(struct ofono_call_forwarding *cf) { GAtChat *chat = ofono_call_forwarding_get_data(cf); g_idle_remove_by_data(cf); g_at_chat_unref(chat); ofono_call_forwarding_set_data(cf, NULL); } static struct ofono_call_forwarding_driver driver = { .name = "atmodem", .probe = at_ccfc_probe, .remove = at_ccfc_remove, .registration = at_ccfc_registration, .activation = at_ccfc_activation, .query = at_ccfc_query, .deactivation = at_ccfc_deactivation, .erasure = at_ccfc_erasure }; void at_call_forwarding_init(void) { ofono_call_forwarding_driver_register(&driver); } void at_call_forwarding_exit(void) { ofono_call_forwarding_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/cbs.c0000644000015600001650000001333412671500024022422 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "util.h" #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" #include "vendor.h" static const char *none_prefix[] = { NULL }; static const char *cscb_prefix[] = { "+CSCB:", NULL }; struct cbs_data { GAtChat *chat; unsigned int vendor; }; static void at_cbm_notify(GAtResult *result, gpointer user_data) { struct ofono_cbs *cbs = user_data; const char *hexpdu; int pdulen; GAtResultIter iter; unsigned char pdu[88]; long hexpdulen; DBG(""); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CBM:")) return; if (!g_at_result_iter_next_number(&iter, &pdulen)) return; if (pdulen != 88) { ofono_error("Got a CBM message with invalid PDU size!"); return; } hexpdu = g_at_result_pdu(result); if (hexpdu == NULL) { ofono_error("Got a CBM, but no PDU. Are we in text mode?"); return; } DBG("Got new Cell Broadcast via CBM: %s, %d", hexpdu, pdulen); if (decode_hex_own_buf(hexpdu, -1, &hexpdulen, 0, pdu) == NULL) { ofono_error("Unable to hex-decode the PDU"); return; } if (hexpdulen != pdulen) { ofono_error("hexpdu length not equal to reported pdu length"); return; } ofono_cbs_notify(cbs, pdu, pdulen); } static void at_cscb_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_cbs_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_cbs_set_topics(struct ofono_cbs *cbs, const char *topics, ofono_cbs_set_cb_t cb, void *user_data) { struct cbs_data *data = ofono_cbs_get_data(cbs); struct cb_data *cbd = cb_data_new(cb, user_data); char *buf; unsigned int id; DBG(""); /* For the Qualcomm based devices it is required to clear * the list of topics first. Otherwise setting the new * topic ranges will fail. */ switch (data->vendor) { case OFONO_VENDOR_GOBI: case OFONO_VENDOR_QUALCOMM_MSM: g_at_chat_send(data->chat, "AT+CSCB=0", none_prefix, NULL, NULL, NULL); break; default: break; } buf = g_strdup_printf("AT+CSCB=0,\"%s\"", topics); id = g_at_chat_send(data->chat, buf, none_prefix, at_cscb_set_cb, cbd, g_free); g_free(buf); if (id > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } static void at_cbs_clear_topics(struct ofono_cbs *cbs, ofono_cbs_set_cb_t cb, void *user_data) { struct cbs_data *data = ofono_cbs_get_data(cbs); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); if (g_at_chat_send(data->chat, "AT+CSCB=0", none_prefix, at_cscb_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } static void at_cbs_register(gboolean ok, GAtResult *result, gpointer user) { struct ofono_cbs *cbs = user; struct cbs_data *data = ofono_cbs_get_data(cbs); /* This driver assumes that something else will properly setup * CNMI notifications to deliver CBS broadcasts via +CBM. We do * not setup CNMI string ourselves here to avoid race conditions * with the SMS driver which will also be setting the CNMI itself * * The default SMS driver will setup the CNMI for +CBM delivery * appropriately for us */ g_at_chat_register(data->chat, "+CBM:", at_cbm_notify, TRUE, cbs, NULL); ofono_cbs_register(cbs); } static void at_cscb_support_cb(gboolean ok, GAtResult *result, gpointer user) { struct ofono_cbs *cbs = user; struct cbs_data *data = ofono_cbs_get_data(cbs); gint range[2]; GAtResultIter iter; if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSCB:")) goto error; if (!g_at_result_iter_open_list(&iter)) goto error; while (g_at_result_iter_next_range(&iter, &range[0], &range[1])) ; if (!g_at_result_iter_close_list(&iter)) goto error; if (g_at_chat_send(data->chat, "AT+CSCB=0", none_prefix, at_cbs_register, cbs, NULL) > 0) return; error: ofono_error("CSCB not supported"); ofono_cbs_remove(cbs); } static int at_cbs_probe(struct ofono_cbs *cbs, unsigned int vendor, void *user) { GAtChat *chat = user; struct cbs_data *data; data = g_new0(struct cbs_data, 1); data->chat = g_at_chat_clone(chat); data->vendor = vendor; ofono_cbs_set_data(cbs, data); g_at_chat_send(data->chat, "AT+CSCB=?", cscb_prefix, at_cscb_support_cb, cbs, NULL); return 0; } static void at_cbs_remove(struct ofono_cbs *cbs) { struct cbs_data *data = ofono_cbs_get_data(cbs); ofono_cbs_set_data(cbs, NULL); g_at_chat_unref(data->chat); g_free(data); } static struct ofono_cbs_driver driver = { .name = "atmodem", .probe = at_cbs_probe, .remove = at_cbs_remove, .set_topics = at_cbs_set_topics, .clear_topics = at_cbs_clear_topics, }; void at_cbs_init(void) { ofono_cbs_driver_register(&driver); } void at_cbs_exit(void) { ofono_cbs_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/phonebook.c0000644000015600001650000003340712671500024023642 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "util.h" #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" #include "vendor.h" #define INDEX_INVALID -1 #define CHARSET_UTF8 1 #define CHARSET_UCS2 2 #define CHARSET_IRA 4 #define CHARSET_SUPPORT (CHARSET_UTF8 | CHARSET_UCS2) static const char *none_prefix[] = { NULL }; static const char *cpbr_prefix[] = { "+CPBR:", NULL }; static const char *cscs_prefix[] = { "+CSCS:", NULL }; static const char *cpbs_prefix[] = { "+CPBS:", NULL }; struct pb_data { int index_min, index_max; char *old_charset; int supported; GAtChat *chat; unsigned int vendor; guint poll_source; guint poll_count; guint ready_id; }; static void warn_bad(void) { ofono_warn("Name field conversion to UTF8 failed, this can indicate a" " problem with modem integration, as this field" " is required by 27.007."); } static gboolean parse_text(GAtResultIter *iter, char **str, int encoding) { const char *string; const guint8 *hex; int len; char *utf8; /* charset_current is CHARSET_UCS2, CHARSET_IRA or CHARSET_UTF8 */ if (encoding == CHARSET_UCS2) { /* * Some devices omit the quotes, so use next_hexstring, * which handles quoted or unquoted hex strings */ if (g_at_result_iter_next_hexstring(iter, &hex, &len) == FALSE) return FALSE; utf8 = g_convert((const gchar*) hex, len, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); if (utf8) { *str = utf8; return TRUE; } return FALSE; } /* * In the case of IRA charset, assume these are Latin1 * characters, same as in UTF8 */ if (g_at_result_iter_next_string(iter, &string)) { *str = g_strdup(string); return TRUE; } return FALSE; } static const char *best_charset(int supported) { const char *charset = "Invalid"; if (supported & CHARSET_IRA) charset = "IRA"; if (supported & CHARSET_UCS2) charset = "UCS2"; if (supported & CHARSET_UTF8) charset = "UTF-8"; return charset; } static void at_cpbr_notify(GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); GAtResultIter iter; int current; if (pbd->supported & CHARSET_IRA) current = CHARSET_IRA; if (pbd->supported & CHARSET_UCS2) current = CHARSET_UCS2; if (pbd->supported & CHARSET_UTF8) current = CHARSET_UTF8; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+CPBR:")) { int index; const char *number; int type; char *text; int hidden = -1; char *group = NULL; const char *adnumber = NULL; int adtype = -1; char *secondtext = NULL; char *email = NULL; char *sip_uri = NULL; char *tel_uri = NULL; if (!g_at_result_iter_next_number(&iter, &index)) continue; if (!g_at_result_iter_next_string(&iter, &number)) continue; if (!g_at_result_iter_next_number(&iter, &type)) continue; if (!parse_text(&iter, &text, current)) { warn_bad(); continue; } g_at_result_iter_next_number_default(&iter, 0, &hidden); parse_text(&iter, &group, current); g_at_result_iter_next_string(&iter, &adnumber); g_at_result_iter_next_number_default(&iter, 0, &adtype); parse_text(&iter, &secondtext, current); parse_text(&iter, &email, current); parse_text(&iter, &sip_uri, current); parse_text(&iter, &tel_uri, current); ofono_phonebook_entry(pb, index, number, type, text, hidden, group, adnumber, adtype, secondtext, email, sip_uri, tel_uri); g_free(text); g_free(group); g_free(secondtext); g_free(email); g_free(sip_uri); g_free(tel_uri); } } static void export_failed(struct cb_data *cbd) { struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); ofono_phonebook_cb_t cb = cbd->cb; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); if (pbd->old_charset) { g_free(pbd->old_charset); pbd->old_charset = NULL; } } static void at_read_entries_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); ofono_phonebook_cb_t cb = cbd->cb; const char *charset; struct ofono_error error; char buf[32]; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); g_free(cbd); charset = best_charset(pbd->supported); if (strcmp(pbd->old_charset, charset)) { snprintf(buf, sizeof(buf), "AT+CSCS=\"%s\"", pbd->old_charset); g_at_chat_send(pbd->chat, buf, none_prefix, NULL, NULL, NULL); } g_free(pbd->old_charset); pbd->old_charset = NULL; } static void at_read_entries(struct cb_data *cbd) { struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); char buf[32]; snprintf(buf, sizeof(buf), "AT+CPBR=%d,%d", pbd->index_min, pbd->index_max); if (g_at_chat_send_listing(pbd->chat, buf, cpbr_prefix, at_cpbr_notify, at_read_entries_cb, cbd, NULL) > 0) return; /* If we get here, then most likely connection to the modem dropped * and we can't really restore the charset anyway */ export_failed(cbd); } static void at_set_charset_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; if (!ok) { export_failed(cbd); return; } at_read_entries(cbd); } static void at_read_charset_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); GAtResultIter iter; const char *charset; char buf[32]; if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSCS:")) goto error; g_at_result_iter_next_string(&iter, &charset); pbd->old_charset = g_strdup(charset); charset = best_charset(pbd->supported); if (!strcmp(pbd->old_charset, charset)) { at_read_entries(cbd); return; } snprintf(buf, sizeof(buf), "AT+CSCS=\"%s\"", charset); if (g_at_chat_send(pbd->chat, buf, none_prefix, at_set_charset_cb, cbd, NULL) > 0) return; error: export_failed(cbd); } static void at_list_indices_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); GAtResultIter iter; if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CPBR:")) goto error; if (!g_at_result_iter_open_list(&iter)) goto error; /* Retrieve index_min and index_max from indices * which seems like "(1-150),32,16" */ if (!g_at_result_iter_next_range(&iter, &pbd->index_min, &pbd->index_max)) goto error; if (!g_at_result_iter_close_list(&iter)) goto error; if (g_at_chat_send(pbd->chat, "AT+CSCS?", cscs_prefix, at_read_charset_cb, cbd, NULL) > 0) return; error: export_failed(cbd); } static void at_select_storage_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_phonebook *pb = cbd->user; struct pb_data *pbd = ofono_phonebook_get_data(pb); if (!ok) goto error; if (g_at_chat_send(pbd->chat, "AT+CPBR=?", cpbr_prefix, at_list_indices_cb, cbd, NULL) > 0) return; error: export_failed(cbd); } static void at_export_entries(struct ofono_phonebook *pb, const char *storage, ofono_phonebook_cb_t cb, void *data) { struct pb_data *pbd = ofono_phonebook_get_data(pb); struct cb_data *cbd = cb_data_new(cb, data); char buf[32]; cbd->user = pb; snprintf(buf, sizeof(buf), "AT+CPBS=\"%s\"", storage); if (g_at_chat_send(pbd->chat, buf, none_prefix, at_select_storage_cb, cbd, NULL) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void phonebook_not_supported(struct ofono_phonebook *pb) { ofono_error("Phonebook not supported by this modem. If this is in " "error please submit patches to support this hardware"); ofono_phonebook_remove(pb); } static void at_list_storages_cb(gboolean ok, GAtResult *result, gpointer user_data); static gboolean cpbs_support_check(gpointer user_data) { struct ofono_phonebook *pb = user_data; struct pb_data *pbd = ofono_phonebook_get_data(pb); pbd->poll_source = 0; if (g_at_chat_send(pbd->chat, "AT+CPBS=?", cpbs_prefix, at_list_storages_cb, pb, NULL) > 0) return FALSE; phonebook_not_supported(pb); return FALSE; } static void ifx_pbready_notify(GAtResult *result, gpointer user_data) { struct ofono_phonebook *pb = user_data; struct pb_data *pbd = ofono_phonebook_get_data(pb); g_at_chat_unregister(pbd->chat, pbd->ready_id); pbd->ready_id = 0; cpbs_support_check(pb); } static void at_list_storages_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_phonebook *pb = user_data; struct pb_data *pbd = ofono_phonebook_get_data(pb); struct ofono_error error; gboolean sm_supported = FALSE; gboolean me_supported = FALSE; gboolean in_list = FALSE; GAtResultIter iter; const char *storage; decode_at_error(&error, g_at_result_final_response(result)); switch (error.type) { case OFONO_ERROR_TYPE_NO_ERROR: break; case OFONO_ERROR_TYPE_CME: /* Check for SIM busy - try again later */ if (error.error == 14) { if (pbd->poll_count++ < 12) { pbd->poll_source = g_timeout_add_seconds(5, cpbs_support_check, pb); return; } } /* fall through */ default: goto error; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CPBS:")) goto error; /* Some modems don't report CPBS in a proper list */ if (g_at_result_iter_open_list(&iter)) in_list = TRUE; while (g_at_result_iter_next_string(&iter, &storage)) { if (!strcmp(storage, "ME")) me_supported = TRUE; else if (!strcmp(storage, "SM")) sm_supported = TRUE; } if (in_list && !g_at_result_iter_close_list(&iter)) goto vendor; if (!me_supported && !sm_supported) goto vendor; ofono_phonebook_register(pb); return; vendor: switch (pbd->vendor) { case OFONO_VENDOR_IFX: pbd->ready_id = g_at_chat_register(pbd->chat, "+PBREADY", ifx_pbready_notify, FALSE, pb, NULL); return; } error: phonebook_not_supported(pb); } static void at_list_charsets_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_phonebook *pb = user_data; struct pb_data *pbd = ofono_phonebook_get_data(pb); gboolean in_list = FALSE; GAtResultIter iter; const char *charset; if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSCS:")) goto error; /* Some modems don't report CSCS in a proper list */ if (g_at_result_iter_open_list(&iter)) in_list = TRUE; while (g_at_result_iter_next_string(&iter, &charset)) { if (!strcmp(charset, "UTF-8")) pbd->supported |= CHARSET_UTF8; else if (!strcmp(charset, "UCS2")) pbd->supported |= CHARSET_UCS2; else if (!strcmp(charset, "IRA")) pbd->supported |= CHARSET_IRA; } if (in_list && !g_at_result_iter_close_list(&iter)) goto error; if (!(pbd->supported & CHARSET_SUPPORT)) { /* Some modems, like the Google G1, do not support UCS2 or UTF8 * Such modems are effectively junk, but we can still get some * useful information out of them by using IRA charset, which * is essentially Latin1. Still, all bets are off if a SIM * with UCS2 encoded entries is present. */ if (pbd->supported & CHARSET_IRA) { ofono_error("This modem does not support UCS2 or UTF8 " "character sets. This means no i18n " "phonebook is possible on this modem," " if this is in error, submit patches " "to properly support this hardware"); } else { goto error; } } pbd->poll_count = 0; cpbs_support_check(pb); return; error: phonebook_not_supported(pb); } static void at_list_charsets(struct ofono_phonebook *pb) { struct pb_data *pbd = ofono_phonebook_get_data(pb); if (g_at_chat_send(pbd->chat, "AT+CSCS=?", cscs_prefix, at_list_charsets_cb, pb, NULL) > 0) return; phonebook_not_supported(pb); } static int at_phonebook_probe(struct ofono_phonebook *pb, unsigned int vendor, void *data) { GAtChat *chat = data; struct pb_data *pbd; pbd = g_try_new0(struct pb_data, 1); if (pbd == NULL) return -ENOMEM; pbd->chat = g_at_chat_clone(chat); pbd->vendor = vendor; ofono_phonebook_set_data(pb, pbd); at_list_charsets(pb); return 0; } static void at_phonebook_remove(struct ofono_phonebook *pb) { struct pb_data *pbd = ofono_phonebook_get_data(pb); if (pbd->poll_source > 0) g_source_remove(pbd->poll_source); if (pbd->old_charset) g_free(pbd->old_charset); ofono_phonebook_set_data(pb, NULL); g_at_chat_unref(pbd->chat); g_free(pbd); } static struct ofono_phonebook_driver driver = { .name = "atmodem", .probe = at_phonebook_probe, .remove = at_phonebook_remove, .export_entries = at_export_entries }; void at_phonebook_init(void) { ofono_phonebook_driver_register(&driver); } void at_phonebook_exit(void) { ofono_phonebook_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/call-barring.c0000644000015600001650000001270512671500024024211 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" static const char *clck_prefix[] = { "+CLCK:", NULL }; static const char *none_prefix[] = { NULL }; static void clck_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_barring_query_cb_t callback = cbd->cb; struct ofono_error error; GAtResultIter iter; int status_mask, status, class, line; decode_at_error(&error, g_at_result_final_response(result)); status_mask = 0; line = 0; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+CLCK:")) { line++; if (!g_at_result_iter_next_number(&iter, &status)) continue; if (!g_at_result_iter_next_number(&iter, &class)) { if (line > 1) continue; else class = 7; } if (status) status_mask |= class; else status_mask &= ~class; } callback(&error, status_mask, cbd->data); } static void at_call_barring_query(struct ofono_call_barring *cb, const char *lock, int cls, ofono_call_barring_query_cb_t callback, void *data) { GAtChat *chat = ofono_call_barring_get_data(cb); struct cb_data *cbd = cb_data_new(callback, data); char buf[64]; if (strlen(lock) != 2) goto error; if (cls == 7) snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",2", lock); else snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",2,,%d", lock, cls); if (g_at_chat_send(chat, buf, clck_prefix, clck_query_cb, cbd, g_free) > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(callback, 0, data); } static void clck_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_barring_set_cb_t callback = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); callback(&error, cbd->data); } static void at_call_barring_set(struct ofono_call_barring *cb, const char *lock, int enable, const char *passwd, int cls, ofono_call_barring_set_cb_t callback, void *data) { GAtChat *chat = ofono_call_barring_get_data(cb); struct cb_data *cbd = cb_data_new(callback, data); char buf[64]; int len; if (strlen(lock) != 2 || (cls && passwd == NULL)) goto error; len = snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",%i", lock, enable); if (passwd) { len += snprintf(buf + len, sizeof(buf) - len, ",\"%s\"", passwd); /* Assume cls == 7 means use defaults */ if (cls != 7) snprintf(buf + len, sizeof(buf) - len, ",%i", cls); } if (g_at_chat_send(chat, buf, none_prefix, clck_set_cb, cbd, g_free) > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(callback, data); } static void cpwd_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_barring_set_cb_t callback = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); callback(&error, cbd->data); } static void at_call_barring_set_passwd(struct ofono_call_barring *cb, const char *lock, const char *old_passwd, const char *new_passwd, ofono_call_barring_set_cb_t callback, void *data) { GAtChat *chat = ofono_call_barring_get_data(cb); struct cb_data *cbd = cb_data_new(callback, data); char buf[64]; if (strlen(lock) != 2) goto error; snprintf(buf, sizeof(buf), "AT+CPWD=\"%s\",\"%s\",\"%s\"", lock, old_passwd, new_passwd); if (g_at_chat_send(chat, buf, none_prefix, cpwd_set_cb, cbd, g_free) > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(callback, data); } static gboolean at_call_barring_register(gpointer user) { struct ofono_call_barring *cb = user; ofono_call_barring_register(cb); return FALSE; } static int at_call_barring_probe(struct ofono_call_barring *cb, unsigned int vendor, void *user) { GAtChat *chat = user; ofono_call_barring_set_data(cb, g_at_chat_clone(chat)); g_idle_add(at_call_barring_register, cb); return 0; } static void at_call_barring_remove(struct ofono_call_barring *cb) { GAtChat *chat = ofono_call_barring_get_data(cb); g_idle_remove_by_data(cb); g_at_chat_unref(chat); ofono_call_barring_set_data(cb, NULL); } static struct ofono_call_barring_driver driver = { .name = "atmodem", .probe = at_call_barring_probe, .remove = at_call_barring_remove, .set = at_call_barring_set, .query = at_call_barring_query, .set_passwd = at_call_barring_set_passwd, }; void at_call_barring_init(void) { ofono_call_barring_driver_register(&driver); } void at_call_barring_exit(void) { ofono_call_barring_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/stk.h0000644000015600001650000000151412671500024022456 0ustar pbuserpbgroup00000000000000/* * * 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 * */ void at_sim_fetch_command(struct ofono_stk *stk, int length); ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/call-volume.c0000644000015600001650000001231312671500024024067 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" static const char *clvl_prefix[] = { "+CLVL:", NULL }; static const char *cmut_prefix[] = { "+CMUT:", NULL }; static const char *none_prefix[] = { NULL }; struct cv_data { int clvl_min; int clvl_max; GAtChat *chat; }; static void cmut_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_call_volume *cv = user_data; GAtResultIter iter; int muted; if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CMUT:")) return; if (g_at_result_iter_next_number(&iter, &muted) == FALSE) return; ofono_call_volume_set_muted(cv, muted); } static void clvl_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_call_volume *cv = user_data; struct cv_data *cvd = ofono_call_volume_get_data(cv); GAtResultIter iter; int lvl; int percent; if (!ok) return; if (cvd->clvl_max == 0 && cvd->clvl_min == 0) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CLVL:")) return; if (g_at_result_iter_next_number(&iter, &lvl) == FALSE) return; percent = ((lvl - cvd->clvl_min) * 100) / (cvd->clvl_max - cvd->clvl_min); ofono_call_volume_set_speaker_volume(cv, percent); ofono_call_volume_register(cv); } static void clvl_range_query(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_call_volume *cv = user_data; struct cv_data *cvd = ofono_call_volume_get_data(cv); GAtResultIter iter; if (!ok) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CLVL:")) return; /* Try opening the list, but don't fail */ g_at_result_iter_open_list(&iter); g_at_result_iter_next_range(&iter, &cvd->clvl_min, &cvd->clvl_max); g_at_result_iter_close_list(&iter); } static void cv_generic_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_volume_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_call_volume_speaker_volume(struct ofono_call_volume *cv, unsigned char percent, ofono_call_volume_cb_t cb, void *data) { struct cv_data *cvd = ofono_call_volume_get_data(cv); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; int level; level = ((cvd->clvl_max - cvd->clvl_min) * percent) / 100 + cvd->clvl_min; snprintf(buf, sizeof(buf), "AT+CLVL=%d", level); if (g_at_chat_send(cvd->chat, buf, none_prefix, cv_generic_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_call_volume_mute(struct ofono_call_volume *cv, int muted, ofono_call_volume_cb_t cb, void *data) { struct cv_data *cvd = ofono_call_volume_get_data(cv); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; snprintf(buf, sizeof(buf), "AT+CMUT=%d", muted); if (g_at_chat_send(cvd->chat, buf, none_prefix, cv_generic_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static int at_call_volume_probe(struct ofono_call_volume *cv, unsigned int vendor, void *data) { GAtChat *chat = data; struct cv_data *cvd; DBG("%p", cv); cvd = g_new0(struct cv_data, 1); cvd->chat = g_at_chat_clone(chat); ofono_call_volume_set_data(cv, cvd); g_at_chat_send(cvd->chat, "AT+CMUT?", cmut_prefix, cmut_query, cv, NULL); g_at_chat_send(cvd->chat, "AT+CLVL=?", clvl_prefix, clvl_range_query, cv, NULL); g_at_chat_send(cvd->chat, "AT+CLVL?", clvl_prefix, clvl_query, cv, NULL); /* Generic driver does not support microphone level */ ofono_call_volume_set_microphone_volume(cv, 100); return 0; } static void at_call_volume_remove(struct ofono_call_volume *cv) { struct cv_data *cvd = ofono_call_volume_get_data(cv); ofono_call_volume_set_data(cv, NULL); g_at_chat_unref(cvd->chat); g_free(cvd); } static struct ofono_call_volume_driver driver = { .name = "atmodem", .probe = at_call_volume_probe, .remove = at_call_volume_remove, .speaker_volume = at_call_volume_speaker_volume, .mute = at_call_volume_mute, }; void at_call_volume_init(void) { ofono_call_volume_driver_register(&driver); } void at_call_volume_exit(void) { ofono_call_volume_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/sim.c0000644000015600001650000011573312671500024022451 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "simutil.h" #include "vendor.h" #include "atmodem.h" #define EF_STATUS_INVALIDATED 0 #define EF_STATUS_VALID 1 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) struct sim_data { GAtChat *chat; unsigned int vendor; guint ready_id; struct at_util_sim_state_query *sim_state_query; }; static const char *crsm_prefix[] = { "+CRSM:", NULL }; static const char *cpin_prefix[] = { "+CPIN:", NULL }; static const char *clck_prefix[] = { "+CLCK:", NULL }; static const char *huawei_cpin_prefix[] = { "^CPIN:", NULL }; static const char *xpincnt_prefix[] = { "+XPINCNT:", NULL }; static const char *zpinpuk_prefix[] = { "+ZPINPUK:", NULL }; 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 *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 *none_prefix[] = { NULL }; static void at_crsm_info_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; GAtResultIter iter; ofono_sim_file_info_cb_t cb = cbd->cb; struct ofono_error error; const guint8 *response; gint sw1, sw2, len; int flen, rlen; int str; unsigned char access[3]; unsigned char file_status; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CRSM:")) goto error; g_at_result_iter_next_number(&iter, &sw1); g_at_result_iter_next_number(&iter, &sw2); if (!g_at_result_iter_next_hexstring(&iter, &response, &len) || (sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92) || (sw1 == 0x90 && sw2 != 0x00)) { memset(&error, 0, sizeof(error)); error.type = OFONO_ERROR_TYPE_SIM; error.error = (sw1 << 8) | sw2; cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data); return; } DBG("crsm_info_cb: %02x, %02x, %i", sw1, sw2, len); if (response[0] == 0x62) { ok = sim_parse_3g_get_response(response, len, &flen, &rlen, &str, access, NULL); file_status = EF_STATUS_VALID; } else ok = sim_parse_2g_get_response(response, len, &flen, &rlen, &str, access, &file_status); if (!ok) goto error; cb(&error, flen, str, rlen, access, file_status, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data); } static void at_sim_read_info(struct ofono_sim *sim, 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; char buf[128]; unsigned int len; if (sd->vendor == OFONO_VENDOR_OPTION_HSO) { unsigned char access[3] = { 0x00, 0x00, 0x00 }; if (fileid == SIM_EFAD_FILEID) { CALLBACK_WITH_SUCCESS(cb, 4, 0, 0, access, EF_STATUS_VALID, data); return; } } cbd = cb_data_new(cb, data); len = snprintf(buf, sizeof(buf), "AT+CRSM=192,%i", fileid); switch (sd->vendor) { default: if (path_len == 0) break; /* Fall through */ case OFONO_VENDOR_ZTE: case OFONO_VENDOR_HUAWEI: case OFONO_VENDOR_SIERRA: case OFONO_VENDOR_SPEEDUP: case OFONO_VENDOR_QUALCOMM_MSM: case OFONO_VENDOR_SIMCOM: /* Maximum possible length */ len += sprintf(buf + len, ",0,0,255"); 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'; } if (g_at_chat_send(sd->chat, buf, crsm_prefix, at_crsm_info_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, data); } static void at_crsm_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, "+CRSM:")) { 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("crsm_read_cb: %02x, %02x, %d", sw1, sw2, len); cb(&error, response, len, cbd->data); } static void at_sim_read_binary(struct ofono_sim *sim, 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+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'; } if (g_at_chat_send(sd->chat, buf, crsm_prefix, at_crsm_read_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } static void at_sim_read_record(struct ofono_sim *sim, 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]; snprintf(buf, sizeof(buf), "AT+CRSM=178,%i,%i,4,%i", fileid, record, length); if (g_at_chat_send(sd->chat, buf, crsm_prefix, at_crsm_read_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } static void at_crsm_update_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; GAtResultIter iter; ofono_sim_write_cb_t cb = cbd->cb; struct ofono_error error; gint sw1, sw2; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CRSM:")) { CALLBACK_WITH_FAILURE(cb, 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; } DBG("crsm_update_cb: %02x, %02x", sw1, sw2); cb(&error, cbd->data); } static void at_sim_update_file(struct ofono_sim *sim, int cmd, int fileid, int p1, int p2, int p3, const unsigned char *value, const unsigned char *path, unsigned int path_len, ofono_sim_write_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; int len, ret; int size = 38 + p3 * 2; DBG(""); buf = g_try_new(char, size); if (buf == NULL) goto error; len = sprintf(buf, "AT+CRSM=%i,%i,%i,%i,%i,\"", cmd, fileid,p1, p2, p3); for (; p3; p3--) len += sprintf(buf + len, "%02hhX", *value++); buf[len++] = '\"'; buf[len] = '\0'; ret = g_at_chat_send(sd->chat, buf, crsm_prefix, at_crsm_update_cb, cbd, g_free); g_free(buf); if (ret > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_sim_update_binary(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 *data) { at_sim_update_file(sim, 214, fileid, start >> 8, start & 0xff, length, value, path, path_len, cb, data); } static void at_sim_update_record(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 *data) { at_sim_update_file(sim, 220, fileid, record, 4, length, value, path, path_len, cb, data); } static void at_sim_update_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 *data) { at_sim_update_file(sim, 220, fileid, 0, 3, length, value, path, path_len, cb, data); } static void at_cimi_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; GAtResultIter iter; ofono_sim_imsi_cb_t cb = cbd->cb; struct ofono_error error; const char *imsi; int i; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); for (i = 0; i < g_at_result_num_response_lines(result); i++) g_at_result_iter_next(&iter, NULL); imsi = g_at_result_iter_raw_line(&iter); DBG("cimi_cb: %s", imsi); cb(&error, imsi, cbd->data); } static void at_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_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+CIMI", NULL, at_cimi_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static struct { enum ofono_sim_password_type type; const char *name; } const at_sim_name[] = { { OFONO_SIM_PASSWORD_NONE, "READY" }, { OFONO_SIM_PASSWORD_SIM_PIN, "SIM PIN" }, { OFONO_SIM_PASSWORD_SIM_PUK, "SIM PUK" }, { OFONO_SIM_PASSWORD_PHSIM_PIN, "PH-SIM PIN" }, { OFONO_SIM_PASSWORD_PHFSIM_PIN, "PH-FSIM PIN" }, { OFONO_SIM_PASSWORD_PHFSIM_PUK, "PH-FSIM PUK" }, { OFONO_SIM_PASSWORD_SIM_PIN2, "SIM PIN2" }, { OFONO_SIM_PASSWORD_SIM_PUK2, "SIM PUK2" }, { OFONO_SIM_PASSWORD_PHNET_PIN, "PH-NET PIN" }, { OFONO_SIM_PASSWORD_PHNET_PUK, "PH-NET PUK" }, { OFONO_SIM_PASSWORD_PHNETSUB_PIN, "PH-NETSUB PIN" }, { OFONO_SIM_PASSWORD_PHNETSUB_PUK, "PH-NETSUB PUK" }, { OFONO_SIM_PASSWORD_PHSP_PIN, "PH-SP PIN" }, { OFONO_SIM_PASSWORD_PHSP_PUK, "PH-SP PUK" }, { OFONO_SIM_PASSWORD_PHCORP_PIN, "PH-CORP PIN" }, { OFONO_SIM_PASSWORD_PHCORP_PUK, "PH-CORP PUK" }, }; #define BUILD_PIN_RETRIES_ARRAY(passwd_types, passwd_types_cnt, retry) \ for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) \ retry[i] = -1; \ \ for (i = 0; i < passwd_types_cnt; i++) { \ int val; \ \ if (!g_at_result_iter_next_number(&iter, &val)) \ goto error; \ \ retry[passwd_types[i]] = val; \ \ DBG("retry counter id=%d, val=%d", passwd_types[i], \ retry[passwd_types[i]]); \ } \ static void huawei_cpin_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; 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; static enum ofono_sim_password_type password_types[] = { OFONO_SIM_PASSWORD_SIM_PUK, OFONO_SIM_PASSWORD_SIM_PIN, OFONO_SIM_PASSWORD_SIM_PUK2, OFONO_SIM_PASSWORD_SIM_PIN2, }; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^CPIN:")) goto error; /* Skip status since we are not interested in this */ if (!g_at_result_iter_skip_next(&iter)) goto error; /* Skip "overall counter" since we'll grab each one individually */ if (!g_at_result_iter_skip_next(&iter)) goto error; BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types), retries); cb(&error, retries, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void pinnum_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; 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; static enum ofono_sim_password_type password_types[] = { OFONO_SIM_PASSWORD_SIM_PIN, OFONO_SIM_PASSWORD_SIM_PUK, OFONO_SIM_PASSWORD_SIM_PIN2, OFONO_SIM_PASSWORD_SIM_PUK2, }; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "%PINNUM:")) goto error; BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types), retries); cb(&error, retries, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void zpinpuk_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; 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; static enum ofono_sim_password_type password_types[] = { OFONO_SIM_PASSWORD_SIM_PIN, OFONO_SIM_PASSWORD_SIM_PUK, }; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+ZPINPUK:")) goto error; BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types), retries); cb(&error, retries, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void xpincnt_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; 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; static enum ofono_sim_password_type password_types[] = { OFONO_SIM_PASSWORD_SIM_PIN, OFONO_SIM_PASSWORD_SIM_PIN2, OFONO_SIM_PASSWORD_SIM_PUK, OFONO_SIM_PASSWORD_SIM_PUK2, }; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+XPINCNT:")) goto error; BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types), retries); cb(&error, retries, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void oercn_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; 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; static enum ofono_sim_password_type password_types[] = { OFONO_SIM_PASSWORD_SIM_PIN, OFONO_SIM_PASSWORD_SIM_PUK, }; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "_OERCN:")) goto error; BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types), retries); cb(&error, retries, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void cpnnum_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_pin_retries_cb_t cb = cbd->cb; const char *final = g_at_result_final_response(result); GAtResultIter iter; struct ofono_error error; const char *line; int num; char **entries; int retries[OFONO_SIM_PASSWORD_INVALID]; size_t i; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); for (num = 0; num < g_at_result_num_response_lines(result); num++) g_at_result_iter_next(&iter, NULL); line = g_at_result_iter_raw_line(&iter); DBG("%s", line); for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) retries[i] = -1; entries = g_strsplit(line, "; ", -1); for (num = 0; entries[num]; num++) { int retry; if (strlen(entries[num]) < 5) continue; retry = strtol(entries[num] + 5, NULL, 10); if (retry == 0 && errno == EINVAL) continue; if (g_str_has_prefix(entries[num], "PIN1=") == TRUE) retries[OFONO_SIM_PASSWORD_SIM_PIN] = retry; else if (g_str_has_prefix(entries[num], "PUK1=") == TRUE) retries[OFONO_SIM_PASSWORD_SIM_PUK] = retry; else if (g_str_has_prefix(entries[num], "PIN2=") == TRUE) retries[OFONO_SIM_PASSWORD_SIM_PIN2] = retry; else if (g_str_has_prefix(entries[num], "PUK2=") == TRUE) retries[OFONO_SIM_PASSWORD_SIM_PUK2] = retry; } g_strfreev(entries); cb(&error, retries, cbd->data); } static void at_epin_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; 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; static enum ofono_sim_password_type password_types[] = { OFONO_SIM_PASSWORD_SIM_PIN, OFONO_SIM_PASSWORD_SIM_PUK, OFONO_SIM_PASSWORD_SIM_PIN2, OFONO_SIM_PASSWORD_SIM_PUK2, }; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "*EPIN:")) goto error; BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types), retries); cb(&error, retries, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void at_cpinr_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_pin_retries_cb_t cb = cbd->cb; GAtResultIter iter; struct ofono_error error; int retries[OFONO_SIM_PASSWORD_INVALID]; size_t len = sizeof(at_sim_name) / sizeof(*at_sim_name); size_t i; decode_at_error(&error, g_at_result_final_response(result)); 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); /* Ignore +CPINRE results... */ while (g_at_result_iter_next(&iter, "+CPINR:")) { const char *name; int val; if (!g_at_result_iter_next_unquoted_string(&iter, &name)) continue; if (!g_at_result_iter_next_number(&iter, &val)) continue; for (i = 1; i < len; i++) { if (!strcmp(name, at_sim_name[i].name)) { retries[at_sim_name[i].type] = val; break; } } } cb(&error, retries, cbd->data); } static void at_spic_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; 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; static enum ofono_sim_password_type password_types[] = { OFONO_SIM_PASSWORD_SIM_PIN, OFONO_SIM_PASSWORD_SIM_PUK, OFONO_SIM_PASSWORD_SIM_PIN2, OFONO_SIM_PASSWORD_SIM_PUK2, }; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+SPIC:")) goto error; BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types), retries); cb(&error, retries, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } #define AT_PCT_SET_RETRIES(retries, pin_type, value) \ retries[pin_type] = value; \ DBG("retry counter id=%d, val=%d", pin_type, value); static void at_pct_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_pin_retries_cb_t cb = cbd->cb; struct ofono_sim *sim = cbd->user; const char *final = g_at_result_final_response(result); GAtResultIter iter; struct ofono_error error; int retries[OFONO_SIM_PASSWORD_INVALID]; size_t i; enum ofono_sim_password_type pin_type; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) retries[i] = -1; pin_type = ofono_sim_get_password_type(sim); if (pin_type == OFONO_SIM_PASSWORD_NONE) { DBG("Note: No password required, returning maximum retries:"); AT_PCT_SET_RETRIES(retries, OFONO_SIM_PASSWORD_SIM_PIN, 3); AT_PCT_SET_RETRIES(retries, OFONO_SIM_PASSWORD_SIM_PIN2, 3); AT_PCT_SET_RETRIES(retries, OFONO_SIM_PASSWORD_SIM_PUK, 10); AT_PCT_SET_RETRIES(retries, OFONO_SIM_PASSWORD_SIM_PUK2, 10); goto callback; } if (g_at_result_iter_next(&iter, "#PCT:") == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &retries[pin_type]) == FALSE) goto error; DBG("retry counter id=%d, val=%d", pin_type, retries[pin_type]); callback: cb(&error, retries, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void at_pnnm_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; 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; static enum ofono_sim_password_type password_types[] = { OFONO_SIM_PASSWORD_SIM_PIN, OFONO_SIM_PASSWORD_SIM_PUK, }; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+PNNM:")) goto error; BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types), retries); cb(&error, retries, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void at_qpinc_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; 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; 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); while (g_at_result_iter_next(&iter, "+QPINC:")) { const char *name; int pin, puk; if (!g_at_result_iter_next_string(&iter, &name)) continue; if (!g_at_result_iter_next_number(&iter, &pin)) continue; if (!g_at_result_iter_next_number(&iter, &puk)) continue; if (!strcmp(name, "SC")) { retries[OFONO_SIM_PASSWORD_SIM_PIN] = pin; retries[OFONO_SIM_PASSWORD_SIM_PUK] = puk; } else if (!strcmp(name, "P2")) { retries[OFONO_SIM_PASSWORD_SIM_PIN2] = pin; retries[OFONO_SIM_PASSWORD_SIM_PUK2] = puk; } } cb(&error, retries, cbd->data); } static void upincnt_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; 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; static enum ofono_sim_password_type password_types[] = { OFONO_SIM_PASSWORD_SIM_PIN, OFONO_SIM_PASSWORD_SIM_PIN2, OFONO_SIM_PASSWORD_SIM_PUK, OFONO_SIM_PASSWORD_SIM_PUK2, }; decode_at_error(&error, final); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+UPINCNT:")) goto error; BUILD_PIN_RETRIES_ARRAY(password_types, ARRAY_SIZE(password_types), retries); 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) { struct sim_data *sd = ofono_sim_get_data(sim); struct cb_data *cbd = cb_data_new(cb, data); cbd->user = sim; DBG(""); switch (sd->vendor) { case OFONO_VENDOR_IFX: if (g_at_chat_send(sd->chat, "AT+XPINCNT", xpincnt_prefix, xpincnt_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_SPEEDUP: if (g_at_chat_send(sd->chat, "AT+CPNNUM", NULL, cpnnum_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_OPTION_HSO: if (g_at_chat_send(sd->chat, "AT_OERCN?", oercn_prefix, oercn_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_HUAWEI: if (g_at_chat_send(sd->chat, "AT^CPIN?", huawei_cpin_prefix, huawei_cpin_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_ICERA: if (g_at_chat_send(sd->chat, "AT%PINNUM?", pinnum_prefix, pinnum_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_ZTE: if (g_at_chat_send(sd->chat, "AT+ZPINPUK=?", zpinpuk_prefix, zpinpuk_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_MBM: if (g_at_chat_send(sd->chat, "AT*EPIN?", epin_prefix, at_epin_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_SIMCOM: if (g_at_chat_send(sd->chat, "AT+SPIC", spic_prefix, at_spic_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_TELIT: if (g_at_chat_send(sd->chat, "AT#PCT", pct_prefix, at_pct_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_ALCATEL: if (g_at_chat_send(sd->chat, "AT+PNNM?", pnnm_prefix, at_pnnm_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_QUECTEL: if (g_at_chat_send(sd->chat, "AT+QPINC?", qpinc_prefix, at_qpinc_cb, cbd, g_free) > 0) return; break; case OFONO_VENDOR_UBLOX: if (g_at_chat_send(sd->chat, "AT+UPINCNT", upincnt_prefix, upincnt_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) return; break; } g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static int needs_wavecom_sim_quirk(int vendor) { return vendor == OFONO_VENDOR_WAVECOM || vendor == OFONO_VENDOR_WAVECOM_Q2XXX; } static void at_cpin_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct sim_data *sd = ofono_sim_get_data(cbd->user); GAtResultIter iter; ofono_sim_passwd_cb_t cb = cbd->cb; struct ofono_error error; const char *pin_required; int pin_type = OFONO_SIM_PASSWORD_INVALID; int i; int len = sizeof(at_sim_name) / sizeof(*at_sim_name); const char *final = g_at_result_final_response(result); if (needs_wavecom_sim_quirk(sd->vendor) && ok && strlen(final) > 7) decode_at_error(&error, "OK"); else decode_at_error(&error, final); if (!ok) { cb(&error, -1, cbd->data); return; } if (needs_wavecom_sim_quirk(sd->vendor)) { /* +CPIN: */ pin_required = final + 7; } else { g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CPIN:")) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } g_at_result_iter_next_unquoted_string(&iter, &pin_required); } for (i = 0; i < len; i++) { if (strcmp(pin_required, at_sim_name[i].name)) continue; pin_type = at_sim_name[i].type; break; } if (pin_type == OFONO_SIM_PASSWORD_INVALID) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } DBG("crsm_pin_cb: %s", pin_required); cb(&error, pin_type, cbd->data); } static void at_pin_query(struct ofono_sim *sim, ofono_sim_passwd_cb_t cb, void *data) { struct sim_data *sd = ofono_sim_get_data(sim); struct cb_data *cbd = cb_data_new(cb, data); cbd->user = sim; if (g_at_chat_send(sd->chat, "AT+CPIN?", cpin_prefix, at_cpin_cb, cbd, g_free) > 0) return; g_free(cbd); 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; 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); } 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; ofono_sim_lock_unlock_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) 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: case OFONO_VENDOR_SIMCOM: case OFONO_VENDOR_SIERRA: /* * On ZTE modems, after pin is entered, SIM state is checked * by polling CPIN as their modem doesn't provide unsolicited * notification of SIM readiness. * * On SIMCOM modems, SIM is busy after pin is entered (we * got a "+CME ERROR: 14" for the "AT+CPIN?" request) and * ofono don't catch the "+CPIN: READY" message sent by the * modem when SIM is ready. So, use extra CPIN to check the * state. */ sd->sim_state_query = at_util_sim_state_query_new(sd->chat, 2, 20, sim_state_cb, cbd, g_free); return; } done: cb(&error, cbd->data); g_free(cbd); } static void at_pin_send(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); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; int ret; cbd->user = sd; snprintf(buf, sizeof(buf), "AT+CPIN=\"%s\"", passwd); ret = g_at_chat_send(sd->chat, buf, none_prefix, at_pin_send_cb, cbd, NULL); memset(buf, 0, sizeof(buf)); if (ret > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_pin_send_puk(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); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; int ret; cbd->user = sd; snprintf(buf, sizeof(buf), "AT+CPIN=\"%s\",\"%s\"", puk, passwd); ret = g_at_chat_send(sd->chat, buf, none_prefix, at_pin_send_cb, cbd, NULL); memset(buf, 0, sizeof(buf)); if (ret > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_lock_unlock_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_sim_lock_unlock_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static const char *const at_clck_cpwd_fac[] = { [OFONO_SIM_PASSWORD_SIM_PIN] = "SC", [OFONO_SIM_PASSWORD_SIM_PIN2] = "P2", [OFONO_SIM_PASSWORD_PHSIM_PIN] = "PS", [OFONO_SIM_PASSWORD_PHFSIM_PIN] = "PF", [OFONO_SIM_PASSWORD_PHNET_PIN] = "PN", [OFONO_SIM_PASSWORD_PHNETSUB_PIN] = "PU", [OFONO_SIM_PASSWORD_PHSP_PIN] = "PP", [OFONO_SIM_PASSWORD_PHCORP_PIN] = "PC", }; static void at_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) { struct sim_data *sd = ofono_sim_get_data(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) goto error; snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",%i,\"%s\"", at_clck_cpwd_fac[passwd_type], enable ? 1 : 0, passwd); ret = g_at_chat_send(sd->chat, buf, none_prefix, at_lock_unlock_cb, cbd, g_free); memset(buf, 0, sizeof(buf)); if (ret > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_change_passwd(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) { struct sim_data *sd = ofono_sim_get_data(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) goto error; snprintf(buf, sizeof(buf), "AT+CPWD=\"%s\",\"%s\",\"%s\"", at_clck_cpwd_fac[passwd_type], old_passwd, new_passwd); ret = g_at_chat_send(sd->chat, buf, none_prefix, at_lock_unlock_cb, cbd, g_free); memset(buf, 0, sizeof(buf)); if (ret > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_lock_status_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; GAtResultIter iter; ofono_sim_locked_cb_t cb = cbd->cb; struct ofono_error error; int locked; 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, "+CLCK:")) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } g_at_result_iter_next_number(&iter, &locked); DBG("lock_status_cb: %i", locked); cb(&error, locked, cbd->data); } static void at_pin_query_enabled(struct ofono_sim *sim, enum ofono_sim_password_type passwd_type, ofono_sim_locked_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 = sizeof(at_clck_cpwd_fac) / sizeof(*at_clck_cpwd_fac); if (passwd_type >= len || at_clck_cpwd_fac[passwd_type] == NULL) goto error; snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",2", at_clck_cpwd_fac[passwd_type]); if (g_at_chat_send(sd->chat, buf, clck_prefix, at_lock_status_cb, cbd, g_free) > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static gboolean at_sim_register(gpointer user) { struct ofono_sim *sim = user; ofono_sim_register(sim); return FALSE; } static int at_sim_probe(struct ofono_sim *sim, unsigned int vendor, void *data) { GAtChat *chat = data; struct sim_data *sd; 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; } static void at_sim_remove(struct ofono_sim *sim) { struct sim_data *sd = ofono_sim_get_data(sim); g_idle_remove_by_data(sim); /* Cleanup potential SIM state polling */ at_util_sim_state_query_free(sd->sim_state_query); ofono_sim_set_data(sim, NULL); g_at_chat_unref(sd->chat); g_free(sd); } static struct ofono_sim_driver driver = { .name = "atmodem", .probe = at_sim_probe, .remove = at_sim_remove, .read_file_info = at_sim_read_info, .read_file_transparent = at_sim_read_binary, .read_file_linear = at_sim_read_record, .read_file_cyclic = at_sim_read_record, .write_file_transparent = at_sim_update_binary, .write_file_linear = at_sim_update_record, .write_file_cyclic = at_sim_update_cyclic, .read_imsi = at_read_imsi, .query_passwd_state = at_pin_query, .query_pin_retries = at_pin_retries_query, .send_passwd = at_pin_send, .reset_passwd = at_pin_send_puk, .lock = at_pin_enable, .change_passwd = at_change_passwd, .query_locked = at_pin_query_enabled, }; static struct ofono_sim_driver driver_noef = { .name = "atmodem-noef", .probe = at_sim_probe, .remove = at_sim_remove, .read_imsi = at_read_imsi, .query_passwd_state = at_pin_query, .query_pin_retries = at_pin_retries_query, .send_passwd = at_pin_send, .reset_passwd = at_pin_send_puk, .lock = at_pin_enable, .change_passwd = at_change_passwd, .query_locked = at_pin_query_enabled, }; void at_sim_init(void) { ofono_sim_driver_register(&driver); ofono_sim_driver_register(&driver_noef); } void at_sim_exit(void) { ofono_sim_driver_unregister(&driver); ofono_sim_driver_unregister(&driver_noef); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/atutil.c0000644000015600001650000003163012671500024023154 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "atutil.h" #include "vendor.h" static const char *cpin_prefix[] = { "+CPIN:", NULL }; struct at_util_sim_state_query { GAtChat *chat; guint cpin_poll_source; guint cpin_poll_count; guint interval; guint num_times; at_util_sim_inserted_cb_t cb; void *userdata; GDestroyNotify destroy; }; static gboolean cpin_check(gpointer userdata); void decode_at_error(struct ofono_error *error, const char *final) { if (!strcmp(final, "OK")) { error->type = OFONO_ERROR_TYPE_NO_ERROR; error->error = 0; } else if (g_str_has_prefix(final, "+CMS ERROR:")) { error->type = OFONO_ERROR_TYPE_CMS; error->error = strtol(&final[11], NULL, 0); } else if (g_str_has_prefix(final, "+CME ERROR:")) { error->type = OFONO_ERROR_TYPE_CME; error->error = strtol(&final[11], NULL, 0); } else { error->type = OFONO_ERROR_TYPE_FAILURE; error->error = 0; } } gint at_util_call_compare_by_status(gconstpointer a, gconstpointer b) { const struct ofono_call *call = a; int status = GPOINTER_TO_INT(b); if (status != call->status) return 1; return 0; } gint at_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b) { const struct ofono_call *call = a; const struct ofono_phone_number *pb = b; return memcmp(&call->phone_number, pb, sizeof(struct ofono_phone_number)); } gint at_util_call_compare_by_id(gconstpointer a, gconstpointer b) { const struct ofono_call *call = a; unsigned int id = GPOINTER_TO_UINT(b); if (id < call->id) return -1; if (id > call->id) return 1; return 0; } gint at_util_call_compare(gconstpointer a, gconstpointer b) { const struct ofono_call *ca = a; const struct ofono_call *cb = b; if (ca->id < cb->id) return -1; if (ca->id > cb->id) return 1; return 0; } GSList *at_util_parse_clcc(GAtResult *result, unsigned int *ret_mpty_ids) { GAtResultIter iter; GSList *l = NULL; int id, dir, status, type; ofono_bool_t mpty; struct ofono_call *call; unsigned int mpty_ids = 0; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+CLCC:")) { const char *str = ""; int number_type = 129; if (!g_at_result_iter_next_number(&iter, &id)) continue; if (id == 0) continue; if (!g_at_result_iter_next_number(&iter, &dir)) continue; if (!g_at_result_iter_next_number(&iter, &status)) continue; if (status > 5) continue; if (!g_at_result_iter_next_number(&iter, &type)) continue; if (!g_at_result_iter_next_number(&iter, &mpty)) continue; if (g_at_result_iter_next_string(&iter, &str)) g_at_result_iter_next_number(&iter, &number_type); call = g_try_new(struct ofono_call, 1); if (call == NULL) break; ofono_call_init(call); call->id = id; call->direction = dir; call->status = status; call->type = type; strncpy(call->phone_number.number, str, OFONO_MAX_PHONE_NUMBER_LENGTH); call->phone_number.type = number_type; if (strlen(call->phone_number.number) > 0) call->clip_validity = 0; else call->clip_validity = 2; l = g_slist_insert_sorted(l, call, at_util_call_compare); if (mpty) mpty_ids |= 1 << id; } if (ret_mpty_ids) *ret_mpty_ids = mpty_ids; return l; } gboolean at_util_parse_reg_unsolicited(GAtResult *result, const char *prefix, int *status, int *lac, int *ci, int *tech, unsigned int vendor) { GAtResultIter iter; int s; int l = -1, c = -1, t = -1; const char *str; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, prefix) == FALSE) return FALSE; if (g_at_result_iter_next_number(&iter, &s) == FALSE) return FALSE; /* Some firmware will report bogus lac/ci when unregistered */ if (s != 1 && s != 5) goto out; switch (vendor) { case OFONO_VENDOR_GOBI: case OFONO_VENDOR_ZTE: case OFONO_VENDOR_HUAWEI: case OFONO_VENDOR_NOVATEL: case OFONO_VENDOR_SPEEDUP: if (g_at_result_iter_next_unquoted_string(&iter, &str) == TRUE) l = strtol(str, NULL, 16); else goto out; if (g_at_result_iter_next_unquoted_string(&iter, &str) == TRUE) c = strtol(str, NULL, 16); else goto out; break; default: if (g_at_result_iter_next_string(&iter, &str) == TRUE) l = strtol(str, NULL, 16); else goto out; if (g_at_result_iter_next_string(&iter, &str) == TRUE) c = strtol(str, NULL, 16); else goto out; } g_at_result_iter_next_number(&iter, &t); out: if (status) *status = s; if (lac) *lac = l; if (ci) *ci = c; if (tech) *tech = t; return TRUE; } gboolean at_util_parse_reg(GAtResult *result, const char *prefix, int *mode, int *status, int *lac, int *ci, int *tech, unsigned int vendor) { GAtResultIter iter; int m, s; int l = -1, c = -1, t = -1; const char *str; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, prefix)) { gboolean r; g_at_result_iter_next_number(&iter, &m); /* Sometimes we get an unsolicited CREG/CGREG here, skip it */ switch (vendor) { case OFONO_VENDOR_ZTE: case OFONO_VENDOR_HUAWEI: case OFONO_VENDOR_NOVATEL: case OFONO_VENDOR_SPEEDUP: r = g_at_result_iter_next_unquoted_string(&iter, &str); if (r == FALSE || strlen(str) != 1) continue; s = strtol(str, NULL, 10); break; default: if (g_at_result_iter_next_number(&iter, &s) == FALSE) continue; break; } /* Some firmware will report bogus lac/ci when unregistered */ if (s != 1 && s != 5) goto out; switch (vendor) { case OFONO_VENDOR_GOBI: case OFONO_VENDOR_ZTE: case OFONO_VENDOR_HUAWEI: case OFONO_VENDOR_NOVATEL: case OFONO_VENDOR_SPEEDUP: r = g_at_result_iter_next_unquoted_string(&iter, &str); if (r == TRUE) l = strtol(str, NULL, 16); else goto out; r = g_at_result_iter_next_unquoted_string(&iter, &str); if (r == TRUE) c = strtol(str, NULL, 16); else goto out; break; default: if (g_at_result_iter_next_string(&iter, &str) == TRUE) l = strtol(str, NULL, 16); else goto out; if (g_at_result_iter_next_string(&iter, &str) == TRUE) c = strtol(str, NULL, 16); else goto out; } g_at_result_iter_next_number(&iter, &t); out: if (mode) *mode = m; if (status) *status = s; if (lac) *lac = l; if (ci) *ci = c; if (tech) *tech = t; return TRUE; } return FALSE; } gboolean at_util_parse_sms_index_delivery(GAtResult *result, const char *prefix, enum at_util_sms_store *out_st, int *out_index) { GAtResultIter iter; const char *strstore; enum at_util_sms_store st; int index; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, prefix)) return FALSE; if (!g_at_result_iter_next_string(&iter, &strstore)) return FALSE; if (g_str_equal(strstore, "ME")) st = AT_UTIL_SMS_STORE_ME; else if (g_str_equal(strstore, "SM")) st = AT_UTIL_SMS_STORE_SM; else if (g_str_equal(strstore, "SR")) st = AT_UTIL_SMS_STORE_SR; else if (g_str_equal(strstore, "BM")) st = AT_UTIL_SMS_STORE_BM; else return FALSE; if (!g_at_result_iter_next_number(&iter, &index)) return FALSE; if (out_index) *out_index = index; if (out_st) *out_st = st; return TRUE; } static gboolean at_util_charset_string_to_charset(const char *str, enum at_util_charset *charset) { if (!g_strcmp0(str, "GSM")) *charset = AT_UTIL_CHARSET_GSM; else if (!g_strcmp0(str, "HEX")) *charset = AT_UTIL_CHARSET_HEX; else if (!g_strcmp0(str, "IRA")) *charset = AT_UTIL_CHARSET_IRA; else if (!g_strcmp0(str, "PCCP437")) *charset = AT_UTIL_CHARSET_PCCP437; else if (!g_strcmp0(str, "PCDN")) *charset = AT_UTIL_CHARSET_PCDN; else if (!g_strcmp0(str, "UCS2")) *charset = AT_UTIL_CHARSET_UCS2; else if (!g_strcmp0(str, "UTF-8")) *charset = AT_UTIL_CHARSET_UTF8; else if (!g_strcmp0(str, "8859-1")) *charset = AT_UTIL_CHARSET_8859_1; else if (!g_strcmp0(str, "8859-2")) *charset = AT_UTIL_CHARSET_8859_2; else if (!g_strcmp0(str, "8859-3")) *charset = AT_UTIL_CHARSET_8859_3; else if (!g_strcmp0(str, "8859-4")) *charset = AT_UTIL_CHARSET_8859_4; else if (!g_strcmp0(str, "8859-5")) *charset = AT_UTIL_CHARSET_8859_5; else if (!g_strcmp0(str, "8859-6")) *charset = AT_UTIL_CHARSET_8859_6; else if (!g_strcmp0(str, "8859-C")) *charset = AT_UTIL_CHARSET_8859_C; else if (!g_strcmp0(str, "8859-A")) *charset = AT_UTIL_CHARSET_8859_A; else if (!g_strcmp0(str, "8859-G")) *charset = AT_UTIL_CHARSET_8859_G; else if (!g_strcmp0(str, "8859-H")) *charset = AT_UTIL_CHARSET_8859_H; else return FALSE; return TRUE; } gboolean at_util_parse_cscs_supported(GAtResult *result, int *supported) { GAtResultIter iter; const char *str; enum at_util_charset charset; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSCS:")) return FALSE; /* Some modems don't report CSCS in a proper list */ g_at_result_iter_open_list(&iter); while (g_at_result_iter_next_string(&iter, &str)) { if (at_util_charset_string_to_charset(str, &charset)) *supported |= charset; } g_at_result_iter_close_list(&iter); return TRUE; } gboolean at_util_parse_cscs_query(GAtResult *result, enum at_util_charset *charset) { GAtResultIter iter; const char *str; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSCS:")) return FALSE; if (g_at_result_iter_next_string(&iter, &str)) return at_util_charset_string_to_charset(str, charset); return FALSE; } static const char *at_util_fixup_return(const char *line, const char *prefix) { if (g_str_has_prefix(line, prefix) == FALSE) return line; line += strlen(prefix); while (line[0] == ' ') line++; return line; } gboolean at_util_parse_attr(GAtResult *result, const char *prefix, const char **out_attr) { int numlines = g_at_result_num_response_lines(result); GAtResultIter iter; const char *line; int i; if (numlines == 0) return FALSE; g_at_result_iter_init(&iter, result); /* * We have to be careful here, sometimes a stray unsolicited * notification will appear as part of the response and we * cannot rely on having a prefix to recognize the actual * response line. So use the last line only as the response */ for (i = 0; i < numlines; i++) g_at_result_iter_next(&iter, NULL); line = g_at_result_iter_raw_line(&iter); if (out_attr) *out_attr = at_util_fixup_return(line, prefix); return TRUE; } static void cpin_check_cb(gboolean ok, GAtResult *result, gpointer userdata) { struct at_util_sim_state_query *req = userdata; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (error.type == OFONO_ERROR_TYPE_NO_ERROR) goto done; /* * If we got a generic error the AT port might not be ready, * try again */ if (error.type == OFONO_ERROR_TYPE_FAILURE) goto tryagain; /* If we got any other error besides CME, fail */ if (error.type != OFONO_ERROR_TYPE_CME) goto done; switch (error.error) { case 10: case 13: goto done; case 14: goto tryagain; default: /* Assume SIM is present */ ok = TRUE; goto done; } tryagain: if (req->cpin_poll_count++ < req->num_times) { req->cpin_poll_source = g_timeout_add_seconds(req->interval, cpin_check, req); return; } done: if (req->cb) req->cb(ok, req->userdata); } static gboolean cpin_check(gpointer userdata) { struct at_util_sim_state_query *req = userdata; req->cpin_poll_source = 0; g_at_chat_send(req->chat, "AT+CPIN?", cpin_prefix, cpin_check_cb, req, NULL); return FALSE; } struct at_util_sim_state_query *at_util_sim_state_query_new(GAtChat *chat, guint interval, guint num_times, at_util_sim_inserted_cb_t cb, void *userdata, GDestroyNotify destroy) { struct at_util_sim_state_query *req; req = g_new0(struct at_util_sim_state_query, 1); req->chat = chat; req->interval = interval; req->num_times = num_times; req->cb = cb; req->userdata = userdata; req->destroy = destroy; cpin_check(req); return req; } void at_util_sim_state_query_free(struct at_util_sim_state_query *req) { if (req == NULL) return; if (req->cpin_poll_source > 0) g_source_remove(req->cpin_poll_source); if (req->destroy) req->destroy(req->userdata); g_free(req); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/devinfo.c0000644000015600001650000000771612671500024023314 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" static const char *gcap_prefix[] = { "+GCAP:", NULL }; static void attr_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_devinfo_query_cb_t cb = cbd->cb; const char *prefix = cbd->user; struct ofono_error error; const char *attr; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, NULL, cbd->data); return; } if (at_util_parse_attr(result, prefix, &attr) == FALSE) { CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); return; } cb(&error, attr, cbd->data); } static void at_query_manufacturer(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data); GAtChat *chat = ofono_devinfo_get_data(info); cbd->user = "+CGMI:"; if (g_at_chat_send(chat, "AT+CGMI", NULL, attr_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void at_query_model(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data); GAtChat *chat = ofono_devinfo_get_data(info); cbd->user = "+CGMM:"; if (g_at_chat_send(chat, "AT+CGMM", NULL, attr_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void at_query_revision(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data); GAtChat *chat = ofono_devinfo_get_data(info); cbd->user = "+CGMR:"; if (g_at_chat_send(chat, "AT+CGMR", NULL, attr_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void at_query_serial(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct cb_data *cbd = cb_data_new(cb, data); GAtChat *chat = ofono_devinfo_get_data(info); cbd->user = "+CGSN:"; if (g_at_chat_send(chat, "AT+CGSN", NULL, attr_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void capability_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_devinfo *info = user_data; ofono_devinfo_register(info); } static int at_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor, void *data) { GAtChat *chat = g_at_chat_clone(data); ofono_devinfo_set_data(info, chat); g_at_chat_send(chat, "AT+GCAP", gcap_prefix, capability_cb, info, NULL); return 0; } static void at_devinfo_remove(struct ofono_devinfo *info) { GAtChat *chat = ofono_devinfo_get_data(info); ofono_devinfo_set_data(info, NULL); g_at_chat_unref(chat); } static struct ofono_devinfo_driver driver = { .name = "atmodem", .probe = at_devinfo_probe, .remove = at_devinfo_remove, .query_manufacturer = at_query_manufacturer, .query_model = at_query_model, .query_revision = at_query_revision, .query_serial = at_query_serial, }; void at_devinfo_init(void) { ofono_devinfo_driver_register(&driver); } void at_devinfo_exit(void) { ofono_devinfo_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/network-registration.c0000644000015600001650000014170012671500024026053 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "common.h" #include "atmodem.h" #include "vendor.h" static const char *none_prefix[] = { NULL }; static const char *creg_prefix[] = { "+CREG:", NULL }; static const char *cops_prefix[] = { "+COPS:", NULL }; static const char *csq_prefix[] = { "+CSQ:", NULL }; static const char *cind_prefix[] = { "+CIND:", NULL }; static const char *cmer_prefix[] = { "+CMER:", NULL }; static const char *zpas_prefix[] = { "+ZPAS:", NULL }; static const char *option_tech_prefix[] = { "_OCTI:", "_OUWCTI:", NULL }; struct netreg_data { GAtChat *chat; char mcc[OFONO_MAX_MCC_LENGTH + 1]; char mnc[OFONO_MAX_MNC_LENGTH + 1]; int signal_index; /* If strength is reported via CIND */ int signal_min; /* min strength reported via CIND */ int signal_max; /* max strength reported via CIND */ int signal_invalid; /* invalid strength reported via CIND */ int tech; struct ofono_network_time time; guint nitz_timeout; unsigned int vendor; }; struct tech_query { int status; int lac; int ci; struct ofono_netreg *netreg; }; static void extract_mcc_mnc(const char *str, char *mcc, char *mnc) { /* Three digit country code */ strncpy(mcc, str, OFONO_MAX_MCC_LENGTH); mcc[OFONO_MAX_MCC_LENGTH] = '\0'; /* Usually a 2 but sometimes 3 digit network code */ strncpy(mnc, str + OFONO_MAX_MCC_LENGTH, OFONO_MAX_MNC_LENGTH); mnc[OFONO_MAX_MNC_LENGTH] = '\0'; } static int zte_parse_tech(GAtResult *result) { GAtResultIter iter; const char *network, *domain; int tech; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+ZPAS:")) return -1; if (!g_at_result_iter_next_string(&iter, &network)) return -1; if (!g_at_result_iter_next_string(&iter, &domain)) return -1; if (g_str_equal(network, "GSM") == TRUE || g_str_equal(network, "GPRS") == TRUE) tech = ACCESS_TECHNOLOGY_GSM; else if (g_str_equal(network, "EDGE") == TRUE) tech = ACCESS_TECHNOLOGY_GSM_EGPRS; else if (g_str_equal(network, "UMTS") == TRUE) tech = ACCESS_TECHNOLOGY_UTRAN; else if (g_str_equal(network, "HSDPA") == TRUE) tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA; else tech = -1; DBG("network %s domain %s tech %d", network, domain, tech); return tech; } static int option_parse_tech(GAtResult *result) { GAtResultIter iter; int s, octi, ouwcti; int tech; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "_OCTI:")) return -1; if (!g_at_result_iter_next_number(&iter, &s)) return -1; if (!g_at_result_iter_next_number(&iter, &octi)) return -1; if (!g_at_result_iter_next(&iter, "_OUWCTI:")) return -1; if (!g_at_result_iter_next_number(&iter, &s)) return -1; if (!g_at_result_iter_next_number(&iter, &ouwcti)) return -1; switch (octi) { case 1: /* GSM */ tech = ACCESS_TECHNOLOGY_GSM; break; case 2: /* GPRS */ tech = ACCESS_TECHNOLOGY_GSM; break; case 3: /* EDGE */ tech = ACCESS_TECHNOLOGY_GSM_EGPRS; break; default: tech = -1; break; } switch (ouwcti) { case 1: /* UMTS */ tech = ACCESS_TECHNOLOGY_UTRAN; break; case 2: /* HSDPA */ tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA; break; case 3: /* HSUPA */ tech = ACCESS_TECHNOLOGY_UTRAN_HSUPA; break; case 4: /* HSPA */ tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA; break; } DBG("octi %d ouwcti %d tech %d", octi, ouwcti, tech); return tech; } static void at_creg_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_status_cb_t cb = cbd->cb; int status, lac, ci, tech; struct ofono_error error; struct netreg_data *nd = cbd->user; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, -1, -1, -1, -1, cbd->data); return; } if (at_util_parse_reg(result, "+CREG:", NULL, &status, &lac, &ci, &tech, nd->vendor) == FALSE) { CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data); return; } if ((status == 1 || status == 5) && (tech == -1)) tech = nd->tech; cb(&error, status, lac, ci, tech, cbd->data); } static void zte_tech_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_netreg *netreg = cbd->data; struct netreg_data *nd = ofono_netreg_get_data(netreg); if (ok) nd->tech = zte_parse_tech(result); else nd->tech = -1; } static void option_tech_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_netreg *netreg = cbd->data; struct netreg_data *nd = ofono_netreg_get_data(netreg); if (ok) nd->tech = option_parse_tech(result); else nd->tech = -1; } static void at_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); cbd->user = nd; switch (nd->vendor) { case OFONO_VENDOR_MBM: /* * Send *ERINFO to find out the current tech, it will be * intercepted in mbm_erinfo_notify */ g_at_chat_send(nd->chat, "AT*ERINFO?", none_prefix, NULL, NULL, NULL); break; case OFONO_VENDOR_GOBI: /* * Send *CNTI=0 to find out the current tech, it will be * intercepted in gobi_cnti_notify */ g_at_chat_send(nd->chat, "AT*CNTI=0", none_prefix, NULL, NULL, NULL); break; case OFONO_VENDOR_NOVATEL: /* * Send $CNTI=0 to find out the current tech, it will be * intercepted in nw_cnti_notify */ g_at_chat_send(nd->chat, "AT$CNTI=0", none_prefix, NULL, NULL, NULL); break; case OFONO_VENDOR_ZTE: /* * Send +ZPAS? to find out the current tech, zte_tech_cb * will call, fire CREG? to do the rest. */ if (g_at_chat_send(nd->chat, "AT+ZPAS?", zpas_prefix, zte_tech_cb, cbd, NULL) == 0) nd->tech = -1; break; case OFONO_VENDOR_OPTION_HSO: /* * Send AT_OCTI?;_OUWCTI? to find out the current tech, * option_tech_cb will call, fire CREG? to do the rest. */ if (g_at_chat_send(nd->chat, "AT_OCTI?;_OUWCTI?", option_tech_prefix, option_tech_cb, cbd, NULL) == 0) nd->tech = -1; break; } if (g_at_chat_send(nd->chat, "AT+CREG?", creg_prefix, at_creg_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data); } static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct netreg_data *nd = ofono_netreg_get_data(cbd->user); ofono_netreg_operator_cb_t cb = cbd->cb; struct ofono_network_operator op; GAtResultIter iter; int format, tech; const char *name; struct ofono_error error; 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, "+COPS:")) goto error; g_at_result_iter_skip_next(&iter); ok = g_at_result_iter_next_number(&iter, &format); if (ok == FALSE || format != 0) goto error; if (g_at_result_iter_next_string(&iter, &name) == FALSE) goto error; /* Default to GSM */ if (g_at_result_iter_next_number(&iter, &tech) == FALSE) tech = ACCESS_TECHNOLOGY_GSM; strncpy(op.name, name, OFONO_MAX_OPERATOR_NAME_LENGTH); op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0'; strncpy(op.mcc, nd->mcc, OFONO_MAX_MCC_LENGTH); op.mcc[OFONO_MAX_MCC_LENGTH] = '\0'; strncpy(op.mnc, nd->mnc, OFONO_MAX_MNC_LENGTH); op.mnc[OFONO_MAX_MNC_LENGTH] = '\0'; /* Set to current */ op.status = 2; op.tech = tech; DBG("cops_cb: %s, %s %s %d", name, nd->mcc, nd->mnc, tech); cb(&error, &op, cbd->data); g_free(cbd); return; error: cb(&error, NULL, cbd->data); g_free(cbd); } static void cops_numeric_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct netreg_data *nd = ofono_netreg_get_data(cbd->user); ofono_netreg_operator_cb_t cb = cbd->cb; GAtResultIter iter; const char *str; int format; int len; struct ofono_error error; 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, "+COPS:")) goto error; g_at_result_iter_skip_next(&iter); ok = g_at_result_iter_next_number(&iter, &format); if (ok == FALSE || format != 2) goto error; if (g_at_result_iter_next_string(&iter, &str) == FALSE) goto error; len = strspn(str, "0123456789"); if (len != 5 && len != 6) goto error; extract_mcc_mnc(str, nd->mcc, nd->mnc); DBG("Cops numeric got mcc: %s, mnc: %s", nd->mcc, nd->mnc); ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", none_prefix, NULL, NULL, NULL); if (ok) ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix, cops_cb, cbd, NULL); if (ok) return; error: cb(&error, NULL, cbd->data); g_free(cbd); } static void at_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); gboolean ok; cbd->user = netreg; /* Nokia modems have a broken return value for the string * returned for the numeric value. It misses a " at the end. * Trying to read this will stall the parser. So skip it. */ if (nd->vendor == OFONO_VENDOR_NOKIA) { ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", none_prefix, NULL, NULL, NULL); if (ok) ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix, cops_cb, cbd, NULL); } else { ok = g_at_chat_send(nd->chat, "AT+COPS=3,2", none_prefix, NULL, NULL, NULL); if (ok) ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix, cops_numeric_cb, cbd, NULL); } if (ok) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void cops_list_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_operator_list_cb_t cb = cbd->cb; struct ofono_network_operator *list; GAtResultIter iter; int num = 0; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, 0, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+COPS:")) { while (g_at_result_iter_skip_next(&iter)) num += 1; } DBG("Got %d elements", num); list = g_try_new0(struct ofono_network_operator, num); if (list == NULL) { CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data); return; } num = 0; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+COPS:")) { int status, tech, plmn; const char *l, *s, *n; gboolean have_long = FALSE; while (1) { if (!g_at_result_iter_open_list(&iter)) break; if (!g_at_result_iter_next_number(&iter, &status)) break; list[num].status = status; if (!g_at_result_iter_next_string(&iter, &l)) break; if (strlen(l) > 0) { have_long = TRUE; strncpy(list[num].name, l, OFONO_MAX_OPERATOR_NAME_LENGTH); } if (!g_at_result_iter_next_string(&iter, &s)) break; if (strlen(s) > 0 && !have_long) strncpy(list[num].name, s, OFONO_MAX_OPERATOR_NAME_LENGTH); list[num].name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0'; if (!g_at_result_iter_next_string(&iter, &n)) break; extract_mcc_mnc(n, list[num].mcc, list[num].mnc); if (!g_at_result_iter_next_number(&iter, &tech)) tech = ACCESS_TECHNOLOGY_GSM; list[num].tech = tech; if (!g_at_result_iter_next_number(&iter, &plmn)) plmn = 0; if (!g_at_result_iter_close_list(&iter)) break; num += 1; } } DBG("Got %d operators", num); { int i = 0; for (; i < num; i++) { DBG("Operator: %s, %s, %s, status: %d, %d", list[i].name, list[i].mcc, list[i].mnc, list[i].status, list[i].tech); } } cb(&error, num, list, cbd->data); g_free(list); } static void at_list_operators(struct ofono_netreg *netreg, ofono_netreg_operator_list_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(nd->chat, "AT+COPS=?", cops_prefix, cops_list_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, 0, NULL, data); } static void register_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_register_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_register_auto(struct ofono_netreg *netreg, ofono_netreg_register_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(nd->chat, "AT+COPS=0", none_prefix, register_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_register_manual(struct ofono_netreg *netreg, const char *mcc, const char *mnc, ofono_netreg_register_cb_t cb, void *data) { struct netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data); char buf[128]; snprintf(buf, sizeof(buf), "AT+COPS=1,2,\"%s%s\"", mcc, mnc); if (g_at_chat_send(nd->chat, buf, none_prefix, register_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void csq_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; int strength; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CSQ:")) return; if (!g_at_result_iter_next_number(&iter, &strength)) return; ofono_netreg_strength_notify(netreg, at_util_convert_signal_strength(strength)); } static void calypso_csq_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; int strength; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "%CSQ:")) return; if (!g_at_result_iter_next_number(&iter, &strength)) return; ofono_netreg_strength_notify(netreg, at_util_convert_signal_strength(strength)); } static void option_osigq_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; int strength; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "_OSIGQ:")) return; if (!g_at_result_iter_next_number(&iter, &strength)) return; ofono_netreg_strength_notify(netreg, at_util_convert_signal_strength(strength)); } static void ifx_xhomezr_notify(GAtResult *result, gpointer user_data) { //struct ofono_netreg *netreg = user_data; const char *label; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+XHOMEZR:")) return; if (!g_at_result_iter_next_string(&iter, &label)) return; ofono_info("Home zone: %s", label); } static void ifx_xreg_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); int state; const char *band; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+XREG:")) return; if (!g_at_result_iter_next_number(&iter, &state)) return; if (!g_at_result_iter_next_unquoted_string(&iter, &band)) DBG("state %d band %s", state, band); switch (state) { case 0: /* not registered */ nd->tech = -1; break; case 1: /* registered, GPRS attached */ nd->tech = ACCESS_TECHNOLOGY_GSM; break; case 2: /* registered, EDGE attached */ nd->tech = ACCESS_TECHNOLOGY_GSM_EGPRS; break; case 3: /* registered, WCDMA attached */ nd->tech = ACCESS_TECHNOLOGY_UTRAN; break; case 4: /* registered, HSDPA attached */ nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA; break; case 5: /* registered, HSUPA attached */ nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSUPA; break; case 6: /* registered, HSUPA and HSDPA attached */ nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA; break; case 7: /* registered, GSM */ nd->tech = ACCESS_TECHNOLOGY_GSM; break; } } static void ifx_xciev_notify(GAtResult *result, gpointer user_data) { //struct ofono_netreg *netreg = user_data; int ind; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+XCIEV:")) return; if (!g_at_result_iter_next_number(&iter, &ind)) return; DBG("ind %d", ind); /* * Radio signal strength indicators are defined for 0-7, * but this notification seems to return CSQ 0-31,99 values. * * Ignore this indication for now since it can not be trusted. */ } static void ifx_xcsq_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; int rssi, ber, strength; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+XCSQ:")) return; if (!g_at_result_iter_next_number(&iter, &rssi)) return; if (!g_at_result_iter_next_number(&iter, &ber)) return; DBG("rssi %d ber %d", rssi, ber); if (rssi == 99) strength = -1; else strength = (rssi * 100) / 31; ofono_netreg_strength_notify(netreg, strength); } static void ciev_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); int strength, ind; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CIEV:")) return; if (!g_at_result_iter_next_number(&iter, &ind)) return; if (ind != nd->signal_index) return; if (!g_at_result_iter_next_number(&iter, &strength)) return; if (strength == nd->signal_invalid) strength = -1; else strength = (strength * 100) / (nd->signal_max - nd->signal_min); ofono_netreg_strength_notify(netreg, strength); } static void telit_ciev_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); const char *signal_identifier = "rssi"; const char *ind_str; int strength; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CIEV:")) return; if (!g_at_result_iter_next_unquoted_string(&iter, &ind_str)) return; if (!g_str_equal(signal_identifier, ind_str)) return; if (!g_at_result_iter_next_number(&iter, &strength)) return; if (strength == nd->signal_invalid) strength = -1; else strength = (strength * 100) / (nd->signal_max - nd->signal_min); ofono_netreg_strength_notify(netreg, strength); } static void cinterion_ciev_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); const char *signal_identifier = "rssi"; const char *ind_str; int strength; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CIEV:")) return; if (!g_at_result_iter_next_unquoted_string(&iter, &ind_str)) return; if (!g_str_equal(signal_identifier, ind_str)) return; if (!g_at_result_iter_next_number(&iter, &strength)) return; DBG("rssi %d", strength); if (strength == nd->signal_invalid) strength = -1; else strength = (strength * 100) / (nd->signal_max - nd->signal_min); ofono_netreg_strength_notify(netreg, strength); } static void ctzv_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); const char *tz; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CTZV:")) return; if (!g_at_result_iter_next_unquoted_string(&iter, &tz)) return; DBG("tz %s", tz); nd->time.utcoff = atoi(tz) * 15 * 60; ofono_netreg_time_notify(netreg, &nd->time); } static void tlts_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); int year, mon, mday, hour, min, sec; char tz[4]; const char *time; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "*TLTS:")) return; if (!g_at_result_iter_next_string(&iter, &time)) return; DBG("time %s", time); if (sscanf(time, "%02u/%02u/%02u,%02u:%02u:%02u%s", &year, &mon, &mday, &hour, &min, &sec, tz) != 7) return; nd->time.sec = sec; nd->time.min = min; nd->time.hour = hour; nd->time.mday = mday; nd->time.mon = mon; nd->time.year = 2000 + year; nd->time.utcoff = atoi(tz) * 15 * 60; ofono_netreg_time_notify(netreg, &nd->time); } static gboolean notify_time(gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); nd->nitz_timeout = 0; ofono_netreg_time_notify(netreg, &nd->time); return FALSE; } static void ifx_ctzv_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); int year, mon, mday, hour, min, sec; const char *tz, *time; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CTZV:")) return; if (!g_at_result_iter_next_unquoted_string(&iter, &tz)) return; if (!g_at_result_iter_next_string(&iter, &time)) return; DBG("tz %s time %s", tz, time); if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday, &hour, &min, &sec) != 6) return; nd->time.sec = sec; nd->time.min = min; nd->time.hour = hour; nd->time.mday = mday; nd->time.mon = mon; nd->time.year = 2000 + year; if (nd->nitz_timeout > 0) g_source_remove(nd->nitz_timeout); nd->nitz_timeout = g_timeout_add_seconds(1, notify_time, user_data); } static void ifx_ctzdst_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); int dst; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CTZDST:")) return; if (!g_at_result_iter_next_number(&iter, &dst)) return; DBG("dst %d", dst); nd->time.dst = dst; if (nd->nitz_timeout > 0) { g_source_remove(nd->nitz_timeout); nd->nitz_timeout = 0; } ofono_netreg_time_notify(netreg, &nd->time); } static void cind_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_strength_cb_t cb = cbd->cb; struct netreg_data *nd = cbd->user; int index; int strength; GAtResultIter iter; struct ofono_error error; 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, "+CIND:")) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } for (index = 1; index < nd->signal_index; index++) g_at_result_iter_skip_next(&iter); g_at_result_iter_next_number(&iter, &strength); if (strength == nd->signal_invalid) strength = -1; else strength = (strength * 100) / (nd->signal_max - nd->signal_min); cb(&error, strength, cbd->data); } static void huawei_rssi_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; GAtResultIter iter; int strength; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^RSSI:")) return; if (!g_at_result_iter_next_number(&iter, &strength)) return; ofono_netreg_strength_notify(netreg, at_util_convert_signal_strength(strength)); } static void huawei_mode_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; int mode, submode; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^MODE:")) return; if (!g_at_result_iter_next_number(&iter, &mode)) return; if (!g_at_result_iter_next_number(&iter, &submode)) return; switch (mode) { case 3: nd->tech = ACCESS_TECHNOLOGY_GSM; break; case 5: nd->tech = ACCESS_TECHNOLOGY_UTRAN; break; } } static void huawei_nwtime_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); int year, mon, mday, hour, min, sec; char tz[4]; const char *date, *time, *dst; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^NWTIME:")) return; if (!g_at_result_iter_next_unquoted_string(&iter, &date)) return; if (!g_at_result_iter_next_unquoted_string(&iter, &time)) return; if (!g_at_result_iter_next_unquoted_string(&iter, &dst)) return; DBG("date %s time %s dst %s", date, time, dst); if (sscanf(date, "%u/%u/%u", &year, &mon, &mday) != 3) return; if (sscanf(time, "%u:%u:%u%s", &hour, &min, &sec, tz) != 4) return; nd->time.utcoff = atoi(tz) * 15 * 60; nd->time.dst = atoi(dst); nd->time.sec = sec; nd->time.min = min; nd->time.hour = hour; nd->time.mday = mday; nd->time.mon = mon; nd->time.year = 2000 + year; ofono_netreg_time_notify(netreg, &nd->time); } static void csq_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_strength_cb_t cb = cbd->cb; int strength; GAtResultIter iter; struct ofono_error error; 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, "+CSQ:")) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } g_at_result_iter_next_number(&iter, &strength); DBG("csq_cb: %d", strength); if (strength == 99) strength = -1; else strength = (strength * 100) / 31; cb(&error, strength, cbd->data); } static void at_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); cbd->user = nd; /* * If we defaulted to using CIND, then keep using it, * otherwise fall back to CSQ */ if (nd->signal_index > 0) { if (g_at_chat_send(nd->chat, "AT+CIND?", cind_prefix, cind_cb, cbd, g_free) > 0) return; } else { if (g_at_chat_send(nd->chat, "AT+CSQ", csq_prefix, csq_cb, cbd, g_free) > 0) return; } g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void mbm_etzv_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); int year, mon, mday, hour, min, sec; const char *tz, *time, *timestamp; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "*ETZV:") == FALSE) return; if (g_at_result_iter_next_string(&iter, &tz) == FALSE) return; if (g_at_result_iter_next_string(&iter, &time) == FALSE) time = NULL; if (g_at_result_iter_next_string(&iter, ×tamp) == FALSE) timestamp = NULL; DBG("tz %s time %s timestamp %s", tz, time, timestamp); if (time == NULL) { year = -1; mon = -1; mday = -1; hour = -1; min = -1; sec = -1; } else { if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday, &hour, &min, &sec) != 6) return; } nd->time.utcoff = atoi(tz) * 15 * 60; nd->time.sec = sec; nd->time.min = min; nd->time.hour = hour; nd->time.mday = mday; nd->time.mon = mon; nd->time.year = year; ofono_netreg_time_notify(netreg, &nd->time); } static void mbm_erinfo_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; int mode, gsm, umts; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "*ERINFO:") == FALSE) return; if (g_at_result_iter_next_number(&iter, &mode) == FALSE) return; if (g_at_result_iter_next_number(&iter, &gsm) == FALSE) return; /* * According to MBM the ERINFO unsolicited response does not contain * the mode parameter, however at least the MD300 does report it. So * we handle both 2 and 3 argument versions */ if (g_at_result_iter_next_number(&iter, &umts) == FALSE) { gsm = mode; umts = gsm; } ofono_info("network capability: GSM %d UMTS %d", gsm, umts); /* Convert to tech values from 27.007 */ switch (gsm) { case 1: /* GSM */ nd->tech = ACCESS_TECHNOLOGY_GSM; break; case 2: /* EDGE */ nd->tech = ACCESS_TECHNOLOGY_GSM_EGPRS; break; default: nd->tech = -1; } switch (umts) { case 1: /* UMTS */ nd->tech = ACCESS_TECHNOLOGY_UTRAN; break; case 2: /* UMTS + HSDPA */ nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA; break; } } static void icera_nwstate_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 *mccmnc, *tech, *state; int rssi; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "%NWSTATE:") == FALSE) return; if (g_at_result_iter_next_number(&iter, &rssi) == FALSE) return; if (g_at_result_iter_next_unquoted_string(&iter, &mccmnc) == FALSE) return; if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE) return; if (g_at_result_iter_next_unquoted_string(&iter, &state) == FALSE) return; DBG("rssi %d tech %s state %s", rssi, tech, state); /* small 'g' means CS, big 'G' means PS */ if (g_str_equal(tech, "2g") == TRUE || g_str_equal(tech, "2G") == TRUE || g_str_equal(tech, "2G-GPRS") == TRUE) { nd->tech = ACCESS_TECHNOLOGY_GSM; } else if (g_str_equal(tech, "2G-EDGE") == TRUE) { nd->tech = ACCESS_TECHNOLOGY_GSM_EGPRS; } else if (g_str_equal(tech, "3g") == TRUE || g_str_equal(tech, "3G") == TRUE || g_str_equal(tech, "R99") == TRUE) { if (g_str_equal(state, "HSDPA") == TRUE) nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA; else if (g_str_equal(state, "HSUPA") == TRUE) nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSUPA; else if (g_str_equal(state, "HSDPA-HSUPA") == TRUE) nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA; else if (g_str_equal(state, "HSDPA-HSUPA-HSPA+") == TRUE) nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA; else nd->tech = ACCESS_TECHNOLOGY_UTRAN; } else nd->tech = -1; } static int cnti_to_tech(const char *cnti) { if (g_str_equal(cnti, "GSM") == TRUE || g_str_equal(cnti, "GPRS") == TRUE) return ACCESS_TECHNOLOGY_GSM; else if (g_str_equal(cnti, "EDGE") == TRUE) return ACCESS_TECHNOLOGY_GSM_EGPRS; else if (g_str_equal(cnti, "UMTS") == TRUE) return ACCESS_TECHNOLOGY_UTRAN; else if (g_str_equal(cnti, "HSDPA") == TRUE) return ACCESS_TECHNOLOGY_UTRAN_HSDPA; else if (g_str_equal(cnti, "HSUPA") == TRUE) return ACCESS_TECHNOLOGY_UTRAN_HSUPA; return -1; } static void gobi_cnti_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 *tech; int option; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "*CNTI:") == FALSE) return; if (g_at_result_iter_next_number(&iter, &option) == FALSE) return; if (option != 0) return; if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE) return; nd->tech = cnti_to_tech(tech); } static void nw_cnti_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 *tech; int option; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "$CNTI:") == FALSE) return; if (g_at_result_iter_next_number(&iter, &option) == FALSE) return; if (option != 0) return; if (g_at_result_iter_next_unquoted_string(&iter, &tech) == FALSE) return; nd->tech = cnti_to_tech(tech); } static void cnti_query_tech_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct tech_query *tq = user_data; struct netreg_data *nd = ofono_netreg_get_data(tq->netreg); ofono_netreg_status_notify(tq->netreg, tq->status, tq->lac, tq->ci, nd->tech); } static void zte_query_tech_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct tech_query *tq = user_data; int tech; if (ok) tech = zte_parse_tech(result); else tech = -1; ofono_netreg_status_notify(tq->netreg, tq->status, tq->lac, tq->ci, tech); } static void option_query_tech_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct tech_query *tq = user_data; int tech; if (ok) tech = option_parse_tech(result); else tech = -1; ofono_netreg_status_notify(tq->netreg, tq->status, tq->lac, tq->ci, tech); } static void creg_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; int status, lac, ci, tech; struct netreg_data *nd = ofono_netreg_get_data(netreg); struct tech_query *tq; if (at_util_parse_reg_unsolicited(result, "+CREG:", &status, &lac, &ci, &tech, nd->vendor) == FALSE) return; if (status != 1 && status != 5) goto notify; tq = g_try_new0(struct tech_query, 1); if (tq == NULL) goto notify; tq->status = status; tq->lac = lac; tq->ci = ci; tq->netreg = netreg; switch (nd->vendor) { case OFONO_VENDOR_GOBI: if (g_at_chat_send(nd->chat, "AT*CNTI=0", none_prefix, cnti_query_tech_cb, tq, g_free) > 0) return; break; case OFONO_VENDOR_NOVATEL: if (g_at_chat_send(nd->chat, "AT$CNTI=0", none_prefix, cnti_query_tech_cb, tq, g_free) > 0) return; break; case OFONO_VENDOR_ZTE: if (g_at_chat_send(nd->chat, "AT+ZPAS?", zpas_prefix, zte_query_tech_cb, tq, g_free) > 0) return; break; case OFONO_VENDOR_OPTION_HSO: if (g_at_chat_send(nd->chat, "AT_OCTI?;_OUWCTI?", option_tech_prefix, option_query_tech_cb, tq, g_free) > 0) return; break; } g_free(tq); if ((status == 1 || status == 5) && tech == -1) tech = nd->tech; notify: ofono_netreg_status_notify(netreg, status, lac, ci, tech); } static void at_cmer_not_supported(struct ofono_netreg *netreg) { ofono_error("+CMER not supported by this modem. If this is an error" " please submit patches to support this hardware"); ofono_netreg_remove(netreg); } static void at_cmer_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); if (!ok) { at_cmer_not_supported(netreg); return; } /* * Telit uses strings instead of numbers to identify indicators * in a +CIEV URC. * Handle them in a separate function to keep the code clean. */ if (nd->vendor == OFONO_VENDOR_TELIT) g_at_chat_register(nd->chat, "+CIEV:", telit_ciev_notify, FALSE, netreg, NULL); else g_at_chat_register(nd->chat, "+CIEV:", ciev_notify, FALSE, netreg, NULL); g_at_chat_register(nd->chat, "+CREG:", creg_notify, FALSE, netreg, NULL); ofono_netreg_register(netreg); } static inline char wanted_cmer(int supported, const char *pref) { while (*pref) { if (supported & (1 << (*pref - '0'))) return *pref; pref++; } return '\0'; } static inline ofono_bool_t append_cmer_element(char *buf, int *len, int cap, const char *wanted, ofono_bool_t last) { char setting = wanted_cmer(cap, wanted); if (!setting) return FALSE; buf[*len] = setting; if (last) buf[*len + 1] = '\0'; else buf[*len + 1] = ','; *len += 2; return TRUE; } static ofono_bool_t build_cmer_string(char *buf, int *cmer_opts, struct netreg_data *nd) { const char *mode; int len = sprintf(buf, "AT+CMER="); DBG(""); /* * Forward unsolicited result codes directly to the TE; * TA‑TE link specific inband technique used to embed result codes and * data when TA is in on‑line data mode */ if (!append_cmer_element(buf, &len, cmer_opts[0], "3", FALSE)) return FALSE; /* No keypad event reporting */ if (!append_cmer_element(buf, &len, cmer_opts[1], "0", FALSE)) return FALSE; /* No display event reporting */ if (!append_cmer_element(buf, &len, cmer_opts[2], "0", FALSE)) return FALSE; switch (nd->vendor) { case OFONO_VENDOR_TELIT: /* * Telit does not support mode 1. * All indicator events shall be directed from TA to TE. */ mode = "2"; break; default: /* * Only those indicator events, which are not caused by +CIND * shall be indicated by the TA to the TE. */ mode = "1"; break; } /* * Indicator event reporting using URC +CIEV: ,. * indicates the indicator order number (as specified for +CIND) * and is the new value of indicator. */ if (!append_cmer_element(buf, &len, cmer_opts[3], mode, TRUE)) return FALSE; return TRUE; } static void at_cmer_query_cb(ofono_bool_t ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; int cmer_opts_cnt = 5; /* See 27.007 Section 8.10 */ int cmer_opts[cmer_opts_cnt]; int opt; int mode; char buf[128]; if (!ok) goto error; memset(cmer_opts, 0, sizeof(cmer_opts)); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CMER:")) goto error; for (opt = 0; opt < cmer_opts_cnt; opt++) { int min, max; if (!g_at_result_iter_open_list(&iter)) goto error; while (g_at_result_iter_next_range(&iter, &min, &max)) { for (mode = min; mode <= max; mode++) cmer_opts[opt] |= 1 << mode; } if (!g_at_result_iter_close_list(&iter)) goto error; } if (build_cmer_string(buf, cmer_opts, nd) == FALSE) goto error; g_at_chat_send(nd->chat, buf, cmer_prefix, at_cmer_set_cb, netreg, NULL); return; error: at_cmer_not_supported(netreg); } static void cind_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; const char *str; char *signal_identifier = "signal"; int index; int min = 0; int max = 0; int tmp_min, tmp_max, invalid; int i, len; char buf[256]; if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CIND:")) goto error; index = 1; /* * Telit encapsulates the CIND=? tokens with braces * so we need to skip them */ if (nd->vendor == OFONO_VENDOR_TELIT) { g_at_result_iter_open_list(&iter); signal_identifier = "rssi"; } while (g_at_result_iter_open_list(&iter)) { /* Reset invalid default value for every token */ invalid = 99; if (!g_at_result_iter_next_string(&iter, &str)) goto error; if (!g_at_result_iter_open_list(&iter)) goto error; while (g_at_result_iter_next_range(&iter, &tmp_min, &tmp_max)) { if (tmp_min != tmp_max) { min = tmp_min; max = tmp_max; } else invalid = tmp_min; } if (!g_at_result_iter_close_list(&iter)) goto error; if (!g_at_result_iter_close_list(&iter)) goto error; if (g_str_equal(signal_identifier, str) == TRUE) { nd->signal_index = index; nd->signal_min = min; nd->signal_max = max; nd->signal_invalid = invalid; } index += 1; } if (nd->vendor == OFONO_VENDOR_TELIT) g_at_result_iter_close_list(&iter); if (nd->signal_index == 0) goto error; /* Turn off all CIEV indicators except the signal indicator */ len = sprintf(buf, "AT+CIND="); for (i = 1; i < index - 1; i++) len += sprintf(buf + len, i == nd->signal_index ? "1," : "0,"); len += sprintf(buf + len, i == nd->signal_index ? "1" : "0"); g_at_chat_send(nd->chat, buf, NULL, NULL, NULL, NULL); switch (nd->vendor) { case OFONO_VENDOR_MBM: /* * MBM devices report 'CMER: (0,3),(0,2),0,(0-1),0' when * +CMER=? is executed, which cannot be parsed. Simply * send the desired settings in this case. */ g_at_chat_send(nd->chat, "AT+CMER=3,0,0,1", none_prefix, at_cmer_set_cb, netreg, NULL); break; default: g_at_chat_send(nd->chat, "AT+CMER=?", cmer_prefix, at_cmer_query_cb, netreg, NULL); break; } return; error: ofono_error("This driver is not setup with Signal Strength reporting" " via CIND indications, please write proper netreg" " handling for this device"); ofono_netreg_remove(netreg); } static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); if (!ok) { ofono_error("Unable to initialize Network Registration"); ofono_netreg_remove(netreg); return; } switch (nd->vendor) { case OFONO_VENDOR_SIMCOM: /* Register for CSQ changes */ g_at_chat_send(nd->chat, "AT+AUTOCSQ=1,1", none_prefix, NULL, NULL, NULL); g_at_chat_register(nd->chat, "+CSQ:", csq_notify, FALSE, netreg, NULL); break; case OFONO_VENDOR_PHONESIM: g_at_chat_register(nd->chat, "+CSQ:", csq_notify, FALSE, netreg, NULL); break; case OFONO_VENDOR_CALYPSO: g_at_chat_send(nd->chat, "AT%CSQ=1", none_prefix, NULL, NULL, NULL); g_at_chat_register(nd->chat, "%CSQ:", calypso_csq_notify, FALSE, netreg, NULL); break; case OFONO_VENDOR_OPTION_HSO: g_at_chat_send(nd->chat, "AT_OSSYS=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(nd->chat, "AT_OSQI=1", none_prefix, NULL, NULL, NULL); g_at_chat_register(nd->chat, "_OSIGQ:", option_osigq_notify, FALSE, netreg, NULL); g_at_chat_send(nd->chat, "AT_OSSYS?", none_prefix, NULL, NULL, NULL); g_at_chat_send(nd->chat, "AT_OSQI?", none_prefix, NULL, NULL, NULL); /* Register for network time update reports */ g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify, FALSE, netreg, NULL); g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix, NULL, NULL, NULL); break; case OFONO_VENDOR_MBM: /* Enable network registration updates */ g_at_chat_send(nd->chat, "AT*E2REG=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(nd->chat, "AT*EREG=2", none_prefix, NULL, NULL, NULL); g_at_chat_send(nd->chat, "AT*EPSB=1", none_prefix, NULL, NULL, NULL); /* Register for network technology updates */ g_at_chat_send(nd->chat, "AT*ERINFO=1", none_prefix, NULL, NULL, NULL); g_at_chat_register(nd->chat, "*ERINFO:", mbm_erinfo_notify, FALSE, netreg, NULL); /* Register for network time update reports */ g_at_chat_register(nd->chat, "*ETZV:", mbm_etzv_notify, FALSE, netreg, NULL); g_at_chat_send(nd->chat, "AT*ETZR=2", none_prefix, NULL, NULL, NULL); g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix, cind_support_cb, netreg, NULL); return; case OFONO_VENDOR_GOBI: /* * Gobi devices don't support unsolicited notifications * of technology changes, but register a handle for * CNTI so we get notified by any query. */ g_at_chat_register(nd->chat, "*CNTI:", gobi_cnti_notify, FALSE, netreg, NULL); break; case OFONO_VENDOR_NOVATEL: /* * Novatel doesn't support unsolicited notifications * of technology changes, but register a handle for * CNTI so we get notified by any query. */ g_at_chat_register(nd->chat, "$CNTI:", nw_cnti_notify, FALSE, netreg, NULL); break; case OFONO_VENDOR_HUAWEI: /* Register for RSSI reports */ g_at_chat_register(nd->chat, "^RSSI:", huawei_rssi_notify, FALSE, netreg, NULL); /* Register for system mode reports */ g_at_chat_register(nd->chat, "^MODE:", huawei_mode_notify, FALSE, netreg, NULL); /* Register for network time reports */ g_at_chat_register(nd->chat, "^NWTIME:", huawei_nwtime_notify, FALSE, netreg, NULL); break; case OFONO_VENDOR_IFX: /* Register for specific signal strength reports */ g_at_chat_register(nd->chat, "+XCIEV:", ifx_xciev_notify, FALSE, netreg, NULL); g_at_chat_register(nd->chat, "+XCSQ:", ifx_xcsq_notify, FALSE, netreg, NULL); g_at_chat_send(nd->chat, "AT+XCSQ=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(nd->chat, "AT+XMER=1", none_prefix, NULL, NULL, NULL); /* Register for network technology updates */ g_at_chat_register(nd->chat, "+XREG:", ifx_xreg_notify, FALSE, netreg, NULL); g_at_chat_send(nd->chat, "AT+XREG=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(nd->chat, "AT+XBANDSEL?", none_prefix, NULL, NULL, NULL); g_at_chat_send(nd->chat, "AT+XUBANDSEL?", none_prefix, NULL, NULL, NULL); /* Register for home zone reports */ g_at_chat_register(nd->chat, "+XHOMEZR:", ifx_xhomezr_notify, FALSE, netreg, NULL); g_at_chat_send(nd->chat, "AT+XHOMEZR=1", none_prefix, NULL, NULL, NULL); /* Register for network time update reports */ g_at_chat_register(nd->chat, "+CTZV:", ifx_ctzv_notify, FALSE, netreg, NULL); g_at_chat_register(nd->chat, "+CTZDST:", ifx_ctzdst_notify, FALSE, netreg, NULL); g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix, NULL, NULL, NULL); break; case OFONO_VENDOR_ZTE: /* Register for network time update reports */ g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify, FALSE, netreg, NULL); g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix, NULL, NULL, NULL); break; case OFONO_VENDOR_ICERA: /* Register for network technology updates */ g_at_chat_register(nd->chat, "%NWSTATE:", icera_nwstate_notify, FALSE, netreg, NULL); g_at_chat_send(nd->chat, "AT%NWSTATE=1", none_prefix, NULL, NULL, NULL); /* Register for radio access technology updates */ g_at_chat_send(nd->chat, "AT*TRATD=1", none_prefix, NULL, NULL, NULL); /* Register for network time update reports */ g_at_chat_register(nd->chat, "*TLTS:", tlts_notify, FALSE, netreg, NULL); g_at_chat_send(nd->chat, "AT*TLTS=1", none_prefix, NULL, NULL, NULL); break; case OFONO_VENDOR_CINTERION: /* * We can't set rssi bounds from Cinterion responses * so set them up to specified values here * * Cinterion rssi signal strength specified as: * 0 <= -112dBm * 1 - 4 signal strengh in 15 dB steps * 5 >= -51 dBm * 99 not known or undetectable */ nd->signal_min = 0; nd->signal_max = 5; nd->signal_invalid = 99; /* Register for specific signal strength reports */ g_at_chat_send(nd->chat, "AT^SIND=\"rssi\",1", none_prefix, NULL, NULL, NULL); g_at_chat_register(nd->chat, "+CIEV:", cinterion_ciev_notify, FALSE, netreg, NULL); break; case OFONO_VENDOR_NOKIA: case OFONO_VENDOR_SAMSUNG: /* Signal strength reporting via CIND is not supported */ break; default: g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix, cind_support_cb, netreg, NULL); return; } g_at_chat_register(nd->chat, "+CREG:", creg_notify, FALSE, netreg, NULL); ofono_netreg_register(netreg); } static void at_creg_test_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); gint range[2]; GAtResultIter iter; int creg1 = 0; int creg2 = 0; if (!ok) goto error; g_at_result_iter_init(&iter, result); retry: if (!g_at_result_iter_next(&iter, "+CREG:")) goto error; if (!g_at_result_iter_open_list(&iter)) goto retry; while (g_at_result_iter_next_range(&iter, &range[0], &range[1])) { if (1 >= range[0] && 1 <= range[1]) creg1 = 1; if (2 >= range[0] && 2 <= range[1]) creg2 = 1; } g_at_result_iter_close_list(&iter); if (creg2) { g_at_chat_send(nd->chat, "AT+CREG=2", none_prefix, at_creg_set_cb, netreg, NULL); return; } if (creg1) { g_at_chat_send(nd->chat, "AT+CREG=1", none_prefix, at_creg_set_cb, netreg, NULL); return; } error: ofono_error("Unable to initialize Network Registration"); ofono_netreg_remove(netreg); } static int at_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor, void *data) { GAtChat *chat = data; struct netreg_data *nd; nd = g_new0(struct netreg_data, 1); nd->chat = g_at_chat_clone(chat); nd->vendor = vendor; nd->tech = -1; nd->time.sec = -1; nd->time.min = -1; nd->time.hour = -1; nd->time.mday = -1; nd->time.mon = -1; nd->time.year = -1; nd->time.dst = 0; nd->time.utcoff = 0; ofono_netreg_set_data(netreg, nd); g_at_chat_send(nd->chat, "AT+CREG=?", creg_prefix, at_creg_test_cb, netreg, NULL); return 0; } static void at_netreg_remove(struct ofono_netreg *netreg) { struct netreg_data *nd = ofono_netreg_get_data(netreg); if (nd->nitz_timeout) g_source_remove(nd->nitz_timeout); ofono_netreg_set_data(netreg, NULL); g_at_chat_unref(nd->chat); g_free(nd); } static struct ofono_netreg_driver driver = { .name = "atmodem", .probe = at_netreg_probe, .remove = at_netreg_remove, .registration_status = at_registration_status, .current_operator = at_current_operator, .list_operators = at_list_operators, .register_auto = at_register_auto, .register_manual = at_register_manual, .strength = at_signal_strength, }; void at_netreg_init(void) { ofono_netreg_driver_register(&driver); } void at_netreg_exit(void) { ofono_netreg_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/ussd.c0000644000015600001650000001711212671500024022627 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "util.h" #include "smsutil.h" #include "vendor.h" #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" static const char *cusd_prefix[] = { "+CUSD:", NULL }; static const char *none_prefix[] = { NULL }; static const char *cscs_prefix[] = { "+CSCS:", NULL }; struct ussd_data { GAtChat *chat; unsigned int vendor; enum at_util_charset charset; }; static void read_charset_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ussd_data *data = user_data; if (!ok) return; at_util_parse_cscs_query(result, &data->charset); } static const unsigned char *ucs2_gsm_to_packed(const char *content, long *msg_len, unsigned char *msg) { unsigned char *decoded; long len; unsigned char *gsm; long written; unsigned char *packed; unsigned char buf[182 * 2]; /* 182 USSD chars * 2 (UCS2) */ if (strlen(content) > sizeof(buf) * 2) /* Hex, 2 chars / byte */ return NULL; decoded = decode_hex_own_buf(content, -1, &len, 0, buf); if (decoded == NULL) return NULL; gsm = convert_ucs2_to_gsm(decoded, len, NULL, &written, 0); if (gsm == NULL) return NULL; if (written > 182) { g_free(gsm); return NULL; } packed = pack_7bit_own_buf(gsm, written, 0, TRUE, msg_len, 0, msg); g_free(gsm); return packed; } static void cusd_parse(GAtResult *result, struct ofono_ussd *ussd) { struct ussd_data *data = ofono_ussd_get_data(ussd); GAtResultIter iter; int status; const char *content; int dcs; enum sms_charset charset; unsigned char msg[160]; const unsigned char *msg_ptr = NULL; long msg_len; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CUSD:")) return; if (!g_at_result_iter_next_number(&iter, &status)) return; if (!g_at_result_iter_next_string(&iter, &content)) goto out; if (!g_at_result_iter_next_number(&iter, &dcs)) dcs = 0; if (!cbs_dcs_decode(dcs, NULL, NULL, &charset, NULL, NULL, NULL)) { ofono_error("Unsupported USSD data coding scheme (%02x)", dcs); status = 4; /* Not supported */ goto out; } DBG("response charset %d modem charset %d", charset, data->charset); switch (charset) { case SMS_CHARSET_7BIT: switch (data->charset) { case AT_UTIL_CHARSET_GSM: msg_ptr = pack_7bit_own_buf((const guint8 *) content, -1, 0, TRUE, &msg_len, 0, msg); break; case AT_UTIL_CHARSET_UTF8: if (ussd_encode(content, &msg_len, msg) == TRUE) msg_ptr = msg; break; case AT_UTIL_CHARSET_UCS2: msg_ptr = ucs2_gsm_to_packed(content, &msg_len, msg); break; default: msg_ptr = NULL; } break; case SMS_CHARSET_8BIT: case SMS_CHARSET_UCS2: msg_ptr = decode_hex_own_buf(content, -1, &msg_len, 0, msg); break; } DBG("msg ptr %p msg len %ld", msg_ptr, msg_len); out: ofono_ussd_notify(ussd, status, dcs, msg_ptr, msg_ptr ? msg_len : 0); } static void cusd_request_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_ussd_cb_t cb = cbd->cb; struct ofono_ussd *ussd = cbd->user; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); cusd_parse(result, ussd); } static void at_ussd_request(struct ofono_ussd *ussd, int dcs, const unsigned char *pdu, int len, ofono_ussd_cb_t cb, void *user_data) { struct ussd_data *data = ofono_ussd_get_data(ussd); struct cb_data *cbd = cb_data_new(cb, user_data); char buf[512]; enum sms_charset charset; cbd->user = ussd; if (!cbs_dcs_decode(dcs, NULL, NULL, &charset, NULL, NULL, NULL)) goto error; if (charset == SMS_CHARSET_7BIT) { unsigned char unpacked_buf[182]; long written; unpack_7bit_own_buf(pdu, len, 0, TRUE, sizeof(unpacked_buf), &written, 0, unpacked_buf); if (written < 1) goto error; snprintf(buf, sizeof(buf), "AT+CUSD=1,\"%.*s\",%d", (int) written, unpacked_buf, dcs); } else { char coded_buf[321]; char *converted = encode_hex_own_buf(pdu, len, 0, coded_buf); if (converted == NULL) goto error; snprintf(buf, sizeof(buf), "AT+CUSD=1,\"%s\",%d", converted, dcs); } if (g_at_chat_send(data->chat, buf, cusd_prefix, cusd_request_cb, cbd, g_free) > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } static void cusd_cancel_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_ussd_cb_t cb = cbd->cb; struct ussd_data *data = cbd->user; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); switch (data->vendor) { case OFONO_VENDOR_GOBI: case OFONO_VENDOR_QUALCOMM_MSM: /* All errors and notifications arrive unexpected and * thus just reset the state here. This is safer than * getting stuck in a dead-lock. */ error.type = OFONO_ERROR_TYPE_NO_ERROR; error.error = 0; break; default: break; } cb(&error, cbd->data); } static void at_ussd_cancel(struct ofono_ussd *ussd, ofono_ussd_cb_t cb, void *user_data) { struct ussd_data *data = ofono_ussd_get_data(ussd); struct cb_data *cbd = cb_data_new(cb, user_data); cbd->user = data; if (g_at_chat_send(data->chat, "AT+CUSD=2", none_prefix, cusd_cancel_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } static void cusd_notify(GAtResult *result, gpointer user_data) { struct ofono_ussd *ussd = user_data; cusd_parse(result, ussd); } static void at_ussd_register(gboolean ok, GAtResult *result, gpointer user) { struct ofono_ussd *ussd = user; struct ussd_data *data = ofono_ussd_get_data(ussd); if (!ok) { ofono_error("Could not enable CUSD notifications"); ofono_ussd_remove(ussd); return; } g_at_chat_register(data->chat, "+CUSD:", cusd_notify, FALSE, ussd, NULL); ofono_ussd_register(ussd); } static int at_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor, void *user) { GAtChat *chat = user; struct ussd_data *data; data = g_new0(struct ussd_data, 1); data->chat = g_at_chat_clone(chat); data->vendor = vendor; ofono_ussd_set_data(ussd, data); g_at_chat_send(data->chat, "AT+CSCS?", cscs_prefix, read_charset_cb, data, NULL); g_at_chat_send(data->chat, "AT+CUSD=1", NULL, at_ussd_register, ussd, NULL); return 0; } static void at_ussd_remove(struct ofono_ussd *ussd) { struct ussd_data *data = ofono_ussd_get_data(ussd); ofono_ussd_set_data(ussd, NULL); g_at_chat_unref(data->chat); g_free(data); } static struct ofono_ussd_driver driver = { .name = "atmodem", .probe = at_ussd_probe, .remove = at_ussd_remove, .request = at_ussd_request, .cancel = at_ussd_cancel }; void at_ussd_init(void) { ofono_ussd_driver_register(&driver); } void at_ussd_exit(void) { ofono_ussd_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/atmodem.c0000644000015600001650000000360212671500024023276 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "atmodem.h" static int atmodem_init(void) { at_voicecall_init(); at_devinfo_init(); at_call_barring_init(); at_call_forwarding_init(); at_call_meter_init(); at_call_settings_init(); at_phonebook_init(); at_ussd_init(); at_sms_init(); at_sim_init(); at_stk_init(); at_netreg_init(); at_cbs_init(); at_call_volume_init(); at_gprs_init(); at_gprs_context_init(); at_sim_auth_init(); at_gnss_init(); return 0; } static void atmodem_exit(void) { at_sim_auth_exit(); at_stk_exit(); at_sim_exit(); at_sms_exit(); at_ussd_exit(); at_phonebook_exit(); at_call_settings_exit(); at_call_meter_exit(); at_call_forwarding_exit(); at_call_barring_exit(); at_netreg_exit(); at_devinfo_exit(); at_voicecall_exit(); at_cbs_exit(); at_call_volume_exit(); at_gprs_exit(); at_gprs_context_exit(); at_gnss_exit(); } OFONO_PLUGIN_DEFINE(atmodem, "AT modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, atmodem_init, atmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/atmodem.h0000644000015600001650000000406412671500024023306 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include "atutil.h" extern void at_netreg_init(void); extern void at_netreg_exit(void); extern void at_call_forwarding_init(void); extern void at_call_forwarding_exit(void); extern void at_call_settings_init(void); extern void at_call_settings_exit(void); extern void at_ussd_init(void); extern void at_ussd_exit(void); extern void at_voicecall_init(void); extern void at_voicecall_exit(void); extern void at_call_meter_init(void); extern void at_call_meter_exit(void); extern void at_call_barring_init(void); extern void at_call_barring_exit(void); extern void at_sim_init(void); extern void at_sim_exit(void); extern void at_stk_init(void); extern void at_stk_exit(void); extern void at_sms_init(void); extern void at_sms_exit(void); extern void at_phonebook_init(void); extern void at_phonebook_exit(void); extern void at_devinfo_init(void); extern void at_devinfo_exit(void); extern void at_cbs_init(void); extern void at_cbs_exit(void); extern void at_call_volume_init(void); extern void at_call_volume_exit(void); extern void at_gprs_init(void); extern void at_gprs_exit(void); extern void at_gprs_context_init(void); extern void at_gprs_context_exit(void); extern void at_sim_auth_init(void); extern void at_sim_auth_exit(void); extern void at_gnss_init(void); extern void at_gnss_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/call-settings.c0000644000015600001650000002372312671500024024427 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" static const char *none_prefix[] = { NULL }; static const char *clir_prefix[] = { "+CLIR:", NULL }; static const char *colp_prefix[] = { "+COLP:", NULL }; static const char *clip_prefix[] = { "+CLIP:", NULL }; static const char *ccwa_prefix[] = { "+CCWA:", NULL }; static const char *colr_prefix[] = { "+COLR:", NULL }; static const char *cnap_prefix[] = { "+CNAP:", NULL }; static const char *cdip_prefix[] = { "+CDIP:", NULL }; static void ccwa_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_settings_status_cb_t cb = cbd->cb; int conditions = 0; int status; int cls; struct ofono_error error; GAtResultIter iter; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) goto out; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+CCWA:")) { g_at_result_iter_next_number(&iter, &status); g_at_result_iter_next_number(&iter, &cls); if (status == 1) conditions |= cls; } DBG("CW enabled for: %d", conditions); out: cb(&error, conditions, cbd->data); } static void at_ccwa_query(struct ofono_call_settings *cs, int cls, ofono_call_settings_status_cb_t cb, void *data) { GAtChat *chat = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; cbd->user = GINT_TO_POINTER(cls); if (cls == 7) snprintf(buf, sizeof(buf), "AT+CCWA=1,2"); else snprintf(buf, sizeof(buf), "AT+CCWA=1,2,%d", cls); if (g_at_chat_send(chat, buf, ccwa_prefix, ccwa_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, 0, data); } static void ccwa_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_settings_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_ccwa_set(struct ofono_call_settings *cs, int mode, int cls, ofono_call_settings_set_cb_t cb, void *data) { GAtChat *chat = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; snprintf(buf, sizeof(buf), "AT+CCWA=1,%d,%d", mode, cls); if (g_at_chat_send(chat, buf, none_prefix, ccwa_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void query_template(const char *prefix, gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_settings_status_cb_t cb = cbd->cb; struct ofono_error error; GAtResultIter iter; int status = -1; 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, prefix) == FALSE) goto error; /* Skip the local presentation setting */ if (g_at_result_iter_skip_next(&iter) == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &status) == FALSE) goto error; DBG("prefix: %s, network: %d", prefix, status); cb(&error, status, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void clip_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { query_template("+CLIP:", ok, result, user_data); } static void at_clip_query(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data) { GAtChat *chat = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(chat, "AT+CLIP?", clip_prefix, clip_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void cdip_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { query_template("+CDIP:", ok, result, user_data); } static void at_cdip_query(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data) { GAtChat *chat = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(chat, "AT+CDIP?", cdip_prefix, cdip_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void cnap_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { query_template("+CNAP:", ok, result, user_data); } static void at_cnap_query(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data) { GAtChat *chat = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(chat, "AT+CNAP?", cnap_prefix, cnap_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void colp_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { query_template("+COLP:", ok, result, user_data); } static void at_colp_query(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data) { GAtChat *chat = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(chat, "AT+COLP?", colp_prefix, colp_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void clir_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_settings_clir_cb_t cb = cbd->cb; struct ofono_error error; GAtResultIter iter; int override = 0, network = 2; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, -1, -1, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CLIR:")) { CALLBACK_WITH_FAILURE(cb, -1, -1, cbd->data); return; } g_at_result_iter_next_number(&iter, &override); g_at_result_iter_next_number(&iter, &network); DBG("override: %d, network: %d", override, network); cb(&error, override, network, cbd->data); } static void at_clir_query(struct ofono_call_settings *cs, ofono_call_settings_clir_cb_t cb, void *data) { GAtChat *chat = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(chat, "AT+CLIR?", clir_prefix, clir_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, -1, data); } static void clir_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_settings_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_clir_set(struct ofono_call_settings *cs, int mode, ofono_call_settings_set_cb_t cb, void *data) { GAtChat *chat = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; snprintf(buf, sizeof(buf), "AT+CLIR=%d", mode); if (g_at_chat_send(chat, buf, none_prefix, clir_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void colr_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_settings_status_cb_t cb = cbd->cb; struct ofono_error error; GAtResultIter iter; int status; 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, "+COLR:") == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &status) == FALSE) goto error; DBG("network: %d", status); cb(&error, status, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void at_colr_query(struct ofono_call_settings *cs, ofono_call_settings_status_cb_t cb, void *data) { GAtChat *chat = ofono_call_settings_get_data(cs); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(chat, "AT+COLR", colr_prefix, colr_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static gboolean at_call_settings_register(gpointer user) { struct ofono_call_settings *cs = user; ofono_call_settings_register(cs); return FALSE; } static int at_call_settings_probe(struct ofono_call_settings *cs, unsigned int vendor, void *data) { GAtChat *chat = data; ofono_call_settings_set_data(cs, g_at_chat_clone(chat)); g_idle_add(at_call_settings_register, cs); return 0; } static void at_call_settings_remove(struct ofono_call_settings *cs) { GAtChat *chat = ofono_call_settings_get_data(cs); g_idle_remove_by_data(cs); g_at_chat_unref(chat); ofono_call_settings_set_data(cs, NULL); } static struct ofono_call_settings_driver driver = { .name = "atmodem", .probe = at_call_settings_probe, .remove = at_call_settings_remove, .clip_query = at_clip_query, .cnap_query = at_cnap_query, .cdip_query = at_cdip_query, .colp_query = at_colp_query, .clir_query = at_clir_query, .clir_set = at_clir_set, .colr_query = at_colr_query, .cw_query = at_ccwa_query, .cw_set = at_ccwa_set, }; void at_call_settings_init(void) { ofono_call_settings_driver_register(&driver); } void at_call_settings_exit(void) { ofono_call_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/gprs.c0000644000015600001650000002747712671500024022643 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" #include "vendor.h" static const char *cgreg_prefix[] = { "+CGREG:", NULL }; static const char *cgdcont_prefix[] = { "+CGDCONT:", NULL }; static const char *none_prefix[] = { NULL }; struct gprs_data { GAtChat *chat; unsigned int vendor; }; static void at_cgatt_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_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); char buf[64]; 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) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void at_cgreg_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_status_cb_t cb = cbd->cb; struct ofono_error error; int status; struct gprs_data *gd = cbd->user; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, -1, cbd->data); return; } if (at_util_parse_reg(result, "+CGREG:", NULL, &status, NULL, NULL, NULL, gd->vendor) == FALSE) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } cb(&error, status, cbd->data); } static void at_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); cbd->user = gd; switch (gd->vendor) { case OFONO_VENDOR_GOBI: /* * Send *CNTI=0 to find out the current tech, it will be * intercepted in gobi_cnti_notify in network registration */ g_at_chat_send(gd->chat, "AT*CNTI=0", none_prefix, NULL, NULL, NULL); break; case OFONO_VENDOR_NOVATEL: /* * Send $CNTI=0 to find out the current tech, it will be * intercepted in nw_cnti_notify in network registration */ g_at_chat_send(gd->chat, "AT$CNTI=0", none_prefix, NULL, NULL, NULL); break; } if (g_at_chat_send(gd->chat, "AT+CGREG?", cgreg_prefix, at_cgreg_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static void cgreg_notify(GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; int status; struct gprs_data *gd = ofono_gprs_get_data(gprs); if (at_util_parse_reg_unsolicited(result, "+CGREG:", &status, NULL, NULL, NULL, gd->vendor) == FALSE) return; ofono_gprs_status_notify(gprs, status); } static void cgev_notify(GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; GAtResultIter iter; const char *event; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CGEV:")) return; if (!g_at_result_iter_next_unquoted_string(&iter, &event)) return; if (g_str_equal(event, "NW DETACH") || g_str_equal(event, "ME DETACH")) { ofono_gprs_detached_notify(gprs); return; } } static void xdatastat_notify(GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; GAtResultIter iter; int stat; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+XDATASTAT:")) return; if (!g_at_result_iter_next_number(&iter, &stat)) DBG("stat %d", stat); switch (stat) { case 0: ofono_gprs_suspend_notify(gprs, GPRS_SUSPENDED_UNKNOWN_CAUSE); break; case 1: ofono_gprs_resume_notify(gprs); break; } } static void huawei_mode_notify(GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; GAtResultIter iter; int mode, submode; gint bearer; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^MODE:")) return; if (!g_at_result_iter_next_number(&iter, &mode)) return; if (!g_at_result_iter_next_number(&iter, &submode)) return; switch (submode) { case 1: case 2: bearer = 1; /* GPRS */ break; case 3: bearer = 2; /* EDGE */ break; case 4: bearer = 3; /* UMTS */ break; case 5: bearer = 5; /* HSDPA */ break; case 6: bearer = 4; /* HSUPA */ break; case 7: case 9: bearer = 6; /* HSUPA + HSDPA */ break; default: bearer = 0; break; } ofono_gprs_bearer_notify(gprs, bearer); } static void telit_mode_notify(GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; GAtResultIter iter; gint nt, bearer; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "#PSNT:")) return; if (!g_at_result_iter_next_number(&iter,&nt)) return; switch (nt) { case 0: bearer = 1; /* GPRS */ break; case 1: bearer = 2; /* EDGE */ break; case 2: bearer = 3; /* UMTS */ break; case 3: bearer = 5; /* HSDPA */ break; default: bearer = 0; break; } ofono_gprs_bearer_notify(gprs, bearer); } static void ublox_ureg_notify(GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; GAtResultIter iter; gint state, bearer; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+UREG:")) return; if (!g_at_result_iter_next_number(&iter, &state)) return; switch (state) { case 4: bearer = 5; break; case 5: bearer = 4; break; case 7: /* XXX: reserved - assume none. */ bearer = 0; break; case 8: bearer = 1; break; case 9: bearer = 2; break; default: bearer = state; } ofono_gprs_bearer_notify(gprs, bearer); } static void cpsb_notify(GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; GAtResultIter iter; gint bearer; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CPSB:")) return; if (!g_at_result_iter_next_number(&iter, NULL)) return; if (!g_at_result_iter_next_number(&iter, &bearer)) return; ofono_gprs_bearer_notify(gprs, bearer); } static void gprs_initialized(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; struct gprs_data *gd = ofono_gprs_get_data(gprs); g_at_chat_register(gd->chat, "+CGEV:", cgev_notify, FALSE, gprs, NULL); g_at_chat_register(gd->chat, "+CGREG:", cgreg_notify, FALSE, gprs, NULL); switch (gd->vendor) { case OFONO_VENDOR_HUAWEI: g_at_chat_register(gd->chat, "^MODE:", huawei_mode_notify, FALSE, gprs, NULL); break; case OFONO_VENDOR_UBLOX: g_at_chat_register(gd->chat, "+UREG:", ublox_ureg_notify, FALSE, gprs, NULL); g_at_chat_send(gd->chat, "AT+UREG=1", none_prefix, NULL, NULL, NULL); break; case OFONO_VENDOR_TELIT: g_at_chat_register(gd->chat, "#PSNT:", telit_mode_notify, FALSE, gprs, NULL); g_at_chat_send(gd->chat, "AT#PSNT=1", none_prefix, NULL, NULL, NULL); default: g_at_chat_register(gd->chat, "+CPSB:", cpsb_notify, FALSE, gprs, NULL); g_at_chat_send(gd->chat, "AT+CPSB=1", none_prefix, NULL, NULL, NULL); break; } switch (gd->vendor) { case OFONO_VENDOR_IFX: /* Register for GPRS suspend notifications */ g_at_chat_register(gd->chat, "+XDATASTAT:", xdatastat_notify, FALSE, gprs, NULL); g_at_chat_send(gd->chat, "AT+XDATASTAT=1", none_prefix, NULL, NULL, NULL); break; } ofono_gprs_register(gprs); } static void at_cgreg_test_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; struct gprs_data *gd = ofono_gprs_get_data(gprs); gint range[2]; GAtResultIter iter; int cgreg1 = 0; int cgreg2 = 0; const char *cmd; if (!ok) goto error; g_at_result_iter_init(&iter, result); retry: if (!g_at_result_iter_next(&iter, "+CGREG:")) goto error; if (!g_at_result_iter_open_list(&iter)) goto retry; while (g_at_result_iter_next_range(&iter, &range[0], &range[1])) { if (1 >= range[0] && 1 <= range[1]) cgreg1 = 1; if (2 >= range[0] && 2 <= range[1]) cgreg2 = 1; } g_at_result_iter_close_list(&iter); if (cgreg2) cmd = "AT+CGREG=2"; else if (cgreg1) cmd = "AT+CGREG=1"; else goto error; g_at_chat_send(gd->chat, cmd, none_prefix, NULL, NULL, NULL); g_at_chat_send(gd->chat, "AT+CGAUTO=0", none_prefix, NULL, NULL, NULL); switch (gd->vendor) { case OFONO_VENDOR_MBM: /* Ericsson MBM and ST-E modems don't support AT+CGEREP=2,1 */ g_at_chat_send(gd->chat, "AT+CGEREP=1,0", none_prefix, gprs_initialized, gprs, NULL); break; case OFONO_VENDOR_NOKIA: /* Nokia data cards don't support AT+CGEREP=1,0 either */ g_at_chat_send(gd->chat, "AT+CGEREP=1", none_prefix, gprs_initialized, gprs, NULL); break; default: g_at_chat_send(gd->chat, "AT+CGEREP=2,1", none_prefix, gprs_initialized, gprs, NULL); break; } return; error: ofono_info("GPRS not supported on this device"); ofono_gprs_remove(gprs); } static void at_cgdcont_test_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; struct gprs_data *gd = ofono_gprs_get_data(gprs); GAtResultIter iter; int min, max; const char *pdp_type; gboolean found = FALSE; if (!ok) goto error; g_at_result_iter_init(&iter, result); while (!found && g_at_result_iter_next(&iter, "+CGDCONT:")) { gboolean in_list = FALSE; if (!g_at_result_iter_open_list(&iter)) continue; if (g_at_result_iter_next_range(&iter, &min, &max) == FALSE) continue; if (!g_at_result_iter_close_list(&iter)) continue; if (g_at_result_iter_open_list(&iter)) in_list = TRUE; if (!g_at_result_iter_next_string(&iter, &pdp_type)) continue; if (in_list && !g_at_result_iter_close_list(&iter)) continue; /* We look for IP PDPs */ if (g_str_equal(pdp_type, "IP")) found = TRUE; } if (found == FALSE) goto error; ofono_gprs_set_cid_range(gprs, min, max); g_at_chat_send(gd->chat, "AT+CGREG=?", cgreg_prefix, at_cgreg_test_cb, gprs, NULL); return; error: ofono_info("GPRS not supported on this device"); ofono_gprs_remove(gprs); } static int at_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor, void *data) { GAtChat *chat = data; struct gprs_data *gd; gd = g_try_new0(struct gprs_data, 1); if (gd == NULL) return -ENOMEM; gd->chat = g_at_chat_clone(chat); gd->vendor = vendor; ofono_gprs_set_data(gprs, gd); g_at_chat_send(gd->chat, "AT+CGDCONT=?", cgdcont_prefix, at_cgdcont_test_cb, gprs, NULL); return 0; } static void at_gprs_remove(struct ofono_gprs *gprs) { struct gprs_data *gd = ofono_gprs_get_data(gprs); ofono_gprs_set_data(gprs, NULL); g_at_chat_unref(gd->chat); g_free(gd); } static struct ofono_gprs_driver driver = { .name = "atmodem", .probe = at_gprs_probe, .remove = at_gprs_remove, .set_attached = at_gprs_set_attached, .attached_status = at_gprs_registration_status, }; void at_gprs_init(void) { ofono_gprs_driver_register(&driver); } void at_gprs_exit(void) { ofono_gprs_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/stk.c0000644000015600001650000001302212671500024022446 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "atmodem.h" #include "stk.h" #include "vendor.h" struct stk_data { GAtChat *chat; unsigned int vendor; }; static const char *none_prefix[] = { NULL }; static const char *cusate_prefix[] = { "+CUSATER:", NULL }; static void at_cusate_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_stk_envelope_cb_t cb = cbd->cb; struct ofono_error error; GAtResultIter iter; const guint8 *response = NULL; gint len = 0; decode_at_error(&error, g_at_result_final_response(result)); if (ok == FALSE) goto done; /* * According to 27.007, Section 12.2.5 the envelope response is * returned in +CUSATER intermediate response */ g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CUSATER:")) goto done; if (!g_at_result_iter_next_hexstring(&iter, &response, &len)) goto done; done: cb(&error, response, len, cbd->data); } static void at_stk_envelope(struct ofono_stk *stk, int length, const unsigned char *command, ofono_stk_envelope_cb_t cb, void *data) { struct stk_data *sd = ofono_stk_get_data(stk); struct cb_data *cbd = cb_data_new(cb, data); char *buf = alloca(64 + length * 2); int len; len = sprintf(buf, "AT+CUSATE="); for (; length; length--) len += sprintf(buf + len, "%02hhX", *command++); if (g_at_chat_send(sd->chat, buf, cusate_prefix, at_cusate_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } static void at_cusatt_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_stk_generic_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_stk_terminal_response(struct ofono_stk *stk, int length, const unsigned char *value, ofono_stk_generic_cb_t cb, void *data) { struct stk_data *sd = ofono_stk_get_data(stk); struct cb_data *cbd = cb_data_new(cb, data); char *buf = alloca(64 + length * 2); int len; len = sprintf(buf, "AT+CUSATT="); for (; length; length--) len += sprintf(buf + len, "%02hhX", *value++); if (g_at_chat_send(sd->chat, buf, none_prefix, at_cusatt_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void phonesim_cusatp_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; GAtResultIter iter; const guint8 *response; gint len; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CUSATP:")) return; if (!g_at_result_iter_next_hexstring(&iter, &response, &len)) return; ofono_stk_proactive_command_notify(stk, len, response); } static void phonesim_hcmd_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; GAtResultIter iter; const guint8 *response; gint len; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "*HCMD:")) return; if (!g_at_result_iter_next_hexstring(&iter, &response, &len)) return; ofono_stk_proactive_command_handled_notify(stk, len, response); } static void phonesim_cusatend_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; ofono_stk_proactive_session_end_notify(stk); } static gboolean at_stk_register(gpointer user) { struct ofono_stk *stk = user; struct stk_data *sd = ofono_stk_get_data(stk); g_at_chat_register(sd->chat, "+CUSATP:", phonesim_cusatp_notify, FALSE, stk, NULL); g_at_chat_register(sd->chat, "+CUSATEND", phonesim_cusatend_notify, FALSE, stk, NULL); if (sd->vendor == OFONO_VENDOR_PHONESIM) g_at_chat_register(sd->chat, "*HCMD:", phonesim_hcmd_notify, FALSE, stk, NULL); ofono_stk_register(stk); return FALSE; } static int at_stk_probe(struct ofono_stk *stk, unsigned int vendor, void *data) { GAtChat *chat = data; struct stk_data *sd; sd = g_new0(struct stk_data, 1); sd->chat = g_at_chat_clone(chat); sd->vendor = vendor; ofono_stk_set_data(stk, sd); g_idle_add(at_stk_register, stk); return 0; } static void at_stk_remove(struct ofono_stk *stk) { struct stk_data *sd = ofono_stk_get_data(stk); g_idle_remove_by_data(stk); ofono_stk_set_data(stk, NULL); g_at_chat_unref(sd->chat); g_free(sd); } static struct ofono_stk_driver driver = { .name = "atmodem", .probe = at_stk_probe, .remove = at_stk_remove, .envelope = at_stk_envelope, .terminal_response = at_stk_terminal_response, }; void at_stk_init(void) { ofono_stk_driver_register(&driver); } void at_stk_exit(void) { ofono_stk_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/gprs-context.c0000644000015600001650000002464412671500024024316 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "gatppp.h" #include "atmodem.h" #include "vendor.h" #define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun" #define STATIC_IP_NETMASK "255.255.255.255" static const char *none_prefix[] = { NULL }; enum state { STATE_IDLE, STATE_ENABLING, STATE_DISABLING, STATE_ACTIVE, }; struct gprs_context_data { GAtChat *chat; unsigned int active_context; GAtPPPAuthMethod auth_method; char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1]; char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1]; GAtPPP *ppp; enum state state; ofono_gprs_context_cb_t cb; void *cb_data; /* Callback data */ unsigned int vendor; }; static void ppp_debug(const char *str, void *data) { ofono_info("%s: %s", (const char *) data, str); } static void ppp_connect(const char *interface, const char *local, const char *remote, const char *dns1, const char *dns2, gpointer user_data) { struct ofono_gprs_context *gc = user_data; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); const char *dns[3]; DBG(""); dns[0] = dns1; dns[1] = dns2; dns[2] = 0; ofono_info("IP: %s", local); ofono_info("DNS: %s, %s", dns1, dns2); gcd->state = STATE_ACTIVE; ofono_gprs_context_set_interface(gc, interface); ofono_gprs_context_set_ipv4_address(gc, local, TRUE); ofono_gprs_context_set_ipv4_netmask(gc, STATIC_IP_NETMASK); ofono_gprs_context_set_ipv4_dns_servers(gc, dns); CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); } static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data) { struct ofono_gprs_context *gc = user_data; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); DBG("Reason: %d", reason); g_at_ppp_unref(gcd->ppp); gcd->ppp = NULL; switch (gcd->state) { case STATE_ENABLING: CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data); break; case STATE_DISABLING: CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); break; default: ofono_gprs_context_deactivated(gc, gcd->active_context); break; } gcd->active_context = 0; gcd->state = STATE_IDLE; /* * If the channel of gcd->chat is NULL, it might cause * gprs_context_remove get called and the gprs context will be * removed. */ g_at_chat_resume(gcd->chat); } static gboolean setup_ppp(struct ofono_gprs_context *gc) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); GAtIO *io; DBG(""); io = g_at_chat_get_io(gcd->chat); g_at_chat_suspend(gcd->chat); /* open ppp */ gcd->ppp = g_at_ppp_new(); if (gcd->ppp == NULL) { g_at_chat_resume(gcd->chat); return FALSE; } if (getenv("OFONO_PPP_DEBUG")) g_at_ppp_set_debug(gcd->ppp, ppp_debug, "PPP"); g_at_ppp_set_auth_method(gcd->ppp, gcd->auth_method); g_at_ppp_set_credentials(gcd->ppp, gcd->username, gcd->password); /* set connect and disconnect callbacks */ g_at_ppp_set_connect_function(gcd->ppp, ppp_connect, gc); g_at_ppp_set_disconnect_function(gcd->ppp, ppp_disconnect, gc); /* open the ppp connection */ g_at_ppp_open(gcd->ppp, io); return TRUE; } static void at_cgdata_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); DBG("ok %d", ok); if (!ok) { struct ofono_error error; ofono_info("Unable to enter data state"); gcd->active_context = 0; gcd->state = STATE_IDLE; decode_at_error(&error, g_at_result_final_response(result)); gcd->cb(&error, gcd->cb_data); return; } setup_ppp(gc); } static void at_cgdcont_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); if (!ok) { struct ofono_error error; gcd->active_context = 0; gcd->state = STATE_IDLE; decode_at_error(&error, g_at_result_final_response(result)); gcd->cb(&error, gcd->cb_data); return; } if (gcd->vendor == OFONO_VENDOR_SIMCOM_SIM900) sprintf(buf, "ATD*99***%u#", gcd->active_context); else sprintf(buf, "AT+CGDATA=\"PPP\",%u", gcd->active_context); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_cgdata_cb, gc, NULL) > 0) return; gcd->active_context = 0; gcd->state = STATE_IDLE; CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data); } static void at_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); char buf[OFONO_GPRS_MAX_APN_LENGTH + 128]; int len; /* IPv6 support not implemented */ if (ctx->proto != OFONO_GPRS_PROTO_IP) goto error; DBG("cid %u", ctx->cid); gcd->active_context = ctx->cid; gcd->cb = cb; gcd->cb_data = data; memcpy(gcd->username, ctx->username, sizeof(ctx->username)); memcpy(gcd->password, ctx->password, sizeof(ctx->password)); /* We only support CHAP and PAP */ switch (ctx->auth_method) { case OFONO_GPRS_AUTH_METHOD_CHAP: gcd->auth_method = G_AT_PPP_AUTH_METHOD_CHAP; break; case OFONO_GPRS_AUTH_METHOD_PAP: gcd->auth_method = G_AT_PPP_AUTH_METHOD_PAP; break; default: goto error; } gcd->state = STATE_ENABLING; if (gcd->vendor == OFONO_VENDOR_ZTE) { GAtChat *chat = g_at_chat_get_slave(gcd->chat); /* * The modem port of ZTE devices with certain firmware * versions ends up getting suspended. It will no longer * signal POLLOUT and becomes pretty unresponsive. * * To wake up the modem port, the only reliable method * found so far is AT+ZOPRT power mode command. It is * enough to ask for the current mode and the modem * port wakes up and accepts commands again. * * And since the modem port is suspended, this command * needs to be send on the control port of course. * */ g_at_chat_send(chat, "AT+ZOPRT?", none_prefix, NULL, NULL, NULL); } len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); if (ctx->apn) { switch (gcd->vendor) { case OFONO_VENDOR_UBLOX: /* * U-blox modems require a magic prefix to the APN to * specify the authentication method to use in the * network. See UBX-13002752 - R21. * * As the response of the read command omits this magic * prefix, this is the least invasive place to set it. */ switch (ctx->auth_method) { case OFONO_GPRS_AUTH_METHOD_CHAP: snprintf(buf + len, sizeof(buf) - len - 3, ",\"CHAP:%s\"", ctx->apn); break; case OFONO_GPRS_AUTH_METHOD_PAP: snprintf(buf + len, sizeof(buf) - len - 3, ",\"PAP:%s\"", ctx->apn); break; } break; default: snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); break; } } if (g_at_chat_send(gcd->chat, buf, none_prefix, at_cgdcont_cb, gc, NULL) > 0) return; error: CALLBACK_WITH_FAILURE(cb, data); } static void at_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); DBG("cid %u", cid); gcd->state = STATE_DISABLING; gcd->cb = cb; gcd->cb_data = data; g_at_ppp_shutdown(gcd->ppp); } static void at_gprs_detach_shutdown(struct ofono_gprs_context *gc, unsigned int cid) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); DBG("cid %u", cid); g_at_ppp_shutdown(gcd->ppp); } static void cgev_notify(GAtResult *result, gpointer user_data) { struct ofono_gprs_context *gc = user_data; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); const char *event; int cid; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CGEV:")) return; if (!g_at_result_iter_next_unquoted_string(&iter, &event)) return; if (g_str_has_prefix(event, "NW DEACT") == FALSE) return; if (!g_at_result_iter_skip_next(&iter)) return; if (!g_at_result_iter_next_number(&iter, &cid)) return; DBG("cid %d", cid); if ((unsigned int) cid != gcd->active_context) return; if (gcd->state != STATE_IDLE && gcd->ppp) g_at_ppp_shutdown(gcd->ppp); } static int at_gprs_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *data) { GAtChat *chat = data; struct gprs_context_data *gcd; struct stat st; DBG(""); if (stat(TUN_SYSFS_DIR, &st) < 0) { ofono_error("Missing support for TUN/TAP devices"); return -ENODEV; } gcd = g_try_new0(struct gprs_context_data, 1); if (gcd == NULL) return -ENOMEM; gcd->chat = g_at_chat_clone(chat); gcd->vendor = vendor; ofono_gprs_context_set_data(gc, gcd); chat = g_at_chat_get_slave(gcd->chat); if (chat == NULL) return 0; g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL); return 0; } static void at_gprs_context_remove(struct ofono_gprs_context *gc) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); DBG(""); if (gcd->state != STATE_IDLE && gcd->ppp) { g_at_ppp_unref(gcd->ppp); g_at_chat_resume(gcd->chat); } ofono_gprs_context_set_data(gc, NULL); g_at_chat_unref(gcd->chat); g_free(gcd); } static struct ofono_gprs_context_driver driver = { .name = "atmodem", .probe = at_gprs_context_probe, .remove = at_gprs_context_remove, .activate_primary = at_gprs_activate_primary, .deactivate_primary = at_gprs_deactivate_primary, .detach_shutdown = at_gprs_detach_shutdown, }; void at_gprs_context_init(void) { ofono_gprs_context_driver_register(&driver); } void at_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/atmodem/vendor.h0000644000015600001650000000257012671500024023155 0ustar pbuserpbgroup00000000000000/* * * 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 * */ enum ofono_vendor { OFONO_VENDOR_GENERIC = 0, OFONO_VENDOR_CALYPSO, OFONO_VENDOR_IFX, OFONO_VENDOR_STE, OFONO_VENDOR_MBM, OFONO_VENDOR_GOBI, OFONO_VENDOR_QUALCOMM_MSM, OFONO_VENDOR_OPTION_HSO, OFONO_VENDOR_ZTE, OFONO_VENDOR_HUAWEI, OFONO_VENDOR_SIERRA, OFONO_VENDOR_NOVATEL, OFONO_VENDOR_WAVECOM, OFONO_VENDOR_NOKIA, OFONO_VENDOR_PHONESIM, OFONO_VENDOR_TELIT, OFONO_VENDOR_SPEEDUP, OFONO_VENDOR_SAMSUNG, OFONO_VENDOR_SIMCOM, OFONO_VENDOR_SIMCOM_SIM900, OFONO_VENDOR_ICERA, OFONO_VENDOR_WAVECOM_Q2XXX, OFONO_VENDOR_ALCATEL, OFONO_VENDOR_QUECTEL, OFONO_VENDOR_UBLOX, OFONO_VENDOR_CINTERION, }; ofono-1.17.bzr6912+16.04.20160314.3/drivers/stemodem/0000755000015600001650000000000012671500304021673 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/stemodem/voicecall.c0000644000015600001650000003465212671500024024011 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "common.h" #include "stemodem.h" enum call_status_ste { STE_CALL_STATUS_IDLE = 0, STE_CALL_STATUS_CALLING = 1, STE_CALL_STATUS_CONNECTING = 2, STE_CALL_STATUS_ACTIVE = 3, STE_CALL_STATUS_HOLD = 4, STE_CALL_STATUS_WAITING = 5, STE_CALL_STATUS_ALERTING = 6, STE_CALL_STATUS_BUSY = 7, STE_CALL_STATUS_RELEASED = 8, }; static const char *none_prefix[] = { NULL }; struct voicecall_data { GSList *calls; unsigned int local_release; GAtChat *chat; }; struct release_id_req { struct ofono_voicecall *vc; ofono_voicecall_cb_t cb; void *data; int id; }; struct change_state_req { struct ofono_voicecall *vc; ofono_voicecall_cb_t cb; void *data; int affected_types; }; /* Translate from the ECAV-based STE-status to CLCC based status */ static int call_status_ste_to_ofono(enum call_status_ste status) { switch (status) { case STE_CALL_STATUS_IDLE: case STE_CALL_STATUS_RELEASED: return CALL_STATUS_DISCONNECTED; case STE_CALL_STATUS_CALLING: return CALL_STATUS_DIALING; case STE_CALL_STATUS_CONNECTING: return CALL_STATUS_ALERTING; case STE_CALL_STATUS_ACTIVE: return CALL_STATUS_ACTIVE; case STE_CALL_STATUS_HOLD: return CALL_STATUS_HELD; case STE_CALL_STATUS_WAITING: return CALL_STATUS_WAITING; case STE_CALL_STATUS_ALERTING: return CALL_STATUS_INCOMING; case STE_CALL_STATUS_BUSY: return CALL_STATUS_DISCONNECTED; } return CALL_STATUS_DISCONNECTED; } static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, int direction, int status, const char *num, int num_type, int clip) { struct voicecall_data *d = ofono_voicecall_get_data(vc); struct ofono_call *call; /* Generate a call structure for the waiting call */ call = g_try_new(struct ofono_call, 1); if (call == NULL) return NULL; ofono_call_init(call); call->type = type; call->direction = direction; call->status = status; if (clip != CLIP_VALIDITY_NOT_AVAILABLE) { strncpy(call->phone_number.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH); call->phone_number.type = num_type; } call->clip_validity = clip; d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare); return call; } static void ste_generic_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct change_state_req *req = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (ok && req->affected_types) { GSList *l; struct ofono_call *call; for (l = vd->calls; l; l = l->next) { call = l->data; if (req->affected_types & (1 << call->status)) vd->local_release |= (1 << call->id); } } req->cb(&error, req->data); } static void release_id_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct release_id_req *req = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (ok) vd->local_release = 1 << req->id; req->cb(&error, req->data); } static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_error error; ofono_voicecall_cb_t cb = cbd->cb; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void ste_dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, enum ofono_clir_option clir, 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; if (ph->type == 145) snprintf(buf, sizeof(buf), "ATD+%s", ph->number); else snprintf(buf, sizeof(buf), "ATD%s", ph->number); switch (clir) { case OFONO_CLIR_OPTION_DEFAULT: break; case OFONO_CLIR_OPTION_INVOCATION: strcat(buf, "I"); break; case OFONO_CLIR_OPTION_SUPPRESSION: strcat(buf, "i"); break; } strcat(buf, ";"); if (g_at_chat_send(vd->chat, buf, none_prefix, atd_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void ste_template(const char *cmd, struct ofono_voicecall *vc, GAtResultFunc result_cb, unsigned int affected_types, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct change_state_req *req = g_try_new0(struct change_state_req, 1); if (req == NULL) goto error; req->vc = vc; req->cb = cb; req->data = data; req->affected_types = affected_types; if (g_at_chat_send(vd->chat, cmd, none_prefix, result_cb, req, g_free) > 0) return; error: g_free(req); CALLBACK_WITH_FAILURE(cb, data); } static void ste_answer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ste_template("ATA", vc, ste_generic_cb, 0, cb, data); } static void ste_hangup(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { unsigned int active_dial_alert_or_incoming = (1 << CALL_STATUS_ACTIVE) | (1 << CALL_STATUS_DIALING) | (1 << CALL_STATUS_ALERTING) | (1 << CALL_STATUS_INCOMING); ste_template("AT+CHUP", vc, ste_generic_cb, active_dial_alert_or_incoming, cb, data); } static void ste_hold_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ste_template("AT+CHLD=2", vc, ste_generic_cb, 0, cb, data); } static void ste_release_all_held(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { unsigned int held = 1 << CALL_STATUS_HELD; ste_template("AT+CHLD=0", vc, ste_generic_cb, held, cb, data); } static void ste_set_udub(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { unsigned int incoming_or_waiting = (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING); ste_template("AT+CHLD=0", vc, ste_generic_cb, incoming_or_waiting, cb, data); } static void ste_release_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { unsigned int active = 1 << CALL_STATUS_ACTIVE; ste_template("AT+CHLD=1", vc, ste_generic_cb, active, cb, data); } static void ste_release_specific(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct release_id_req *req = g_try_new0(struct release_id_req, 1); char buf[32]; if (req == NULL) goto error; req->vc = vc; req->cb = cb; req->data = data; req->id = id; snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id); if (g_at_chat_send(vd->chat, buf, none_prefix, release_id_cb, req, g_free) > 0) return; error: g_free(req); CALLBACK_WITH_FAILURE(cb, data); } static void ste_private_chat(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { char buf[32]; snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id); ste_template(buf, vc, ste_generic_cb, 0, cb, data); } static void ste_create_multiparty(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { ste_template("AT+CHLD=3", vc, ste_generic_cb, 0, cb, data); } static void ste_transfer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { /* Held & Active */ unsigned int transfer = 0x1 | 0x2; /* Transfer can puts held & active calls together and disconnects * from both. However, some networks support transferring of * dialing/ringing calls as well. */ transfer |= 0x4 | 0x8; ste_template("AT+CHLD=4", vc, ste_generic_cb, transfer, cb, data); } static void ste_deflect(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, ofono_voicecall_cb_t cb, void *data) { char buf[128]; unsigned int incoming_or_waiting = (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING); snprintf(buf, sizeof(buf), "AT+CTFR=\"%s\",%d", ph->number, ph->type); ste_template(buf, vc, ste_generic_cb, incoming_or_waiting, cb, data); } static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_voicecall_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void ste_send_dtmf(struct ofono_voicecall *vc, const char *dtmf, 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); int s; char *buf; /* strlen("AT+VTS=) = 7 + NULL */ buf = g_try_new(char, strlen(dtmf) + 8); if (buf == NULL) goto error; sprintf(buf, "AT+VTS=%s", dtmf); s = g_at_chat_send(vd->chat, buf, none_prefix, vts_cb, cbd, g_free); g_free(buf); if (s > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void ecav_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *num; int id; int status; int call_type; int num_type; struct ofono_call *new_call; struct ofono_call *existing_call = NULL; GSList *l; /* Parse ECAV */ g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "*ECAV:")) return; if (!g_at_result_iter_next_number(&iter, &id)) return; if (!g_at_result_iter_next_number(&iter, &status)) return; if (!g_at_result_iter_next_number(&iter, &call_type)) return; if (call_type != BEARER_CLASS_VOICE) return; /* Skip process id and exit cause */ g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); status = call_status_ste_to_ofono(status); if (status == CALL_STATUS_DIALING || status == CALL_STATUS_WAITING || status == CALL_STATUS_INCOMING) { /* * If caller uses hidden id, the number and * number type might not be present. Don't * look for type if number is not present. */ if (!g_at_result_iter_next_string(&iter, &num)) { num = ""; num_type = 128; } else if (!g_at_result_iter_next_number(&iter, &num_type)) return; } /* * Handle the call according to the status. * If it doesn't exists we make a new one */ l = g_slist_find_custom(vd->calls, GUINT_TO_POINTER(id), at_util_call_compare_by_id); if (l) existing_call = l->data; if (l == NULL && status != CALL_STATUS_DIALING && status != CALL_STATUS_WAITING && status != CALL_STATUS_INCOMING) { ofono_error("ECAV notification for unknown call." " id: %d, status: %d", id, status); return; } switch (status) { case CALL_STATUS_DISCONNECTED: { enum ofono_disconnect_reason reason; existing_call->status = status; if (vd->local_release & (1 << existing_call->id)) reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP; else reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP; ofono_voicecall_disconnected(vc, existing_call->id, reason, NULL); vd->local_release &= ~(1 << existing_call->id); vd->calls = g_slist_remove(vd->calls, l->data); g_free(existing_call); break; } case CALL_STATUS_DIALING: case CALL_STATUS_WAITING: case CALL_STATUS_INCOMING: { int clip_validity; int direction; if (status == CALL_STATUS_DIALING) direction = CALL_DIRECTION_MOBILE_ORIGINATED; else direction = CALL_DIRECTION_MOBILE_TERMINATED; if (strlen(num) > 0) clip_validity = CLIP_VALIDITY_VALID; else clip_validity = CLIP_VALIDITY_NOT_AVAILABLE; new_call = create_call(vc, call_type, direction, status, num, num_type, clip_validity); if (new_call == NULL) { ofono_error("Unable to malloc. " "Call management is fubar"); return; } new_call->id = id; ofono_voicecall_notify(vc, new_call); break; } case CALL_STATUS_ALERTING: case CALL_STATUS_ACTIVE: case CALL_STATUS_HELD: existing_call->status = status; ofono_voicecall_notify(vc, existing_call); break; } } static void ste_voicecall_initialized(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); if (!ok) { ofono_error("*ECAV not enabled. " "Do not have proper call handling"); ofono_voicecall_remove(vc); return; } g_at_chat_register(vd->chat, "*ECAV:", ecav_notify, FALSE, vc, NULL); ofono_voicecall_register(vc); } static int ste_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, void *data) { GAtChat *chat = data; struct voicecall_data *vd; vd = g_try_new0(struct voicecall_data, 1); if (vd == NULL) return -ENOMEM; vd->chat = g_at_chat_clone(chat); ofono_voicecall_set_data(vc, vd); g_at_chat_send(vd->chat, "AT*ECAM=2", none_prefix, ste_voicecall_initialized, vc, NULL); return 0; } static void ste_voicecall_remove(struct ofono_voicecall *vc) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); g_slist_foreach(vd->calls, (GFunc) g_free, NULL); g_slist_free(vd->calls); ofono_voicecall_set_data(vc, NULL); g_at_chat_unref(vd->chat); g_free(vd); } static struct ofono_voicecall_driver driver = { .name = "stemodem", .probe = ste_voicecall_probe, .remove = ste_voicecall_remove, .dial = ste_dial, .answer = ste_answer, .hangup_active = ste_hangup, .hold_all_active = ste_hold_all_active, .release_all_held = ste_release_all_held, .set_udub = ste_set_udub, .release_all_active = ste_release_all_active, .release_specific = ste_release_specific, .private_chat = ste_private_chat, .create_multiparty = ste_create_multiparty, .transfer = ste_transfer, .deflect = ste_deflect, .swap_without_accept = NULL, .send_tones = ste_send_dtmf }; void ste_voicecall_init(void) { ofono_voicecall_driver_register(&driver); } void ste_voicecall_exit(void) { ofono_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/stemodem/caif_socket.h0000644000015600001650000001376612671500024024332 0ustar pbuserpbgroup00000000000000/* linux/caif_socket.h * CAIF Definitions for CAIF socket and network layer * Copyright (C) ST-Ericsson AB 2010 * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ #ifndef _LINUX_CAIF_SOCKET_H #define _LINUX_CAIF_SOCKET_H #include #ifdef __KERNEL__ #include #else #include #endif /* Copy definitions from include/linux/socket.h */ #ifndef AF_CAIF #define AF_CAIF 37 /* CAIF Socket Address Family */ #endif #ifndef PF_CAIF #define PF_CAIF AF_CAIF /* CAIF Socket Protocol Family */ #endif #ifndef SOL_CAIF #define SOL_CAIF 278 /* CAIF Socket Option Level */ #endif /** * enum caif_link_selector - Physical Link Selection. * @CAIF_LINK_HIGH_BANDW: Physical interface for high-bandwidth * traffic. * @CAIF_LINK_LOW_LATENCY: Physical interface for low-latency * traffic. * * CAIF Link Layers can register their link properties. * This enum is used for choosing between CAIF Link Layers when * setting up CAIF Channels when multiple CAIF Link Layers exists. */ enum caif_link_selector { CAIF_LINK_HIGH_BANDW, CAIF_LINK_LOW_LATENCY }; /** * enum caif_channel_priority - CAIF channel priorities. * * @CAIF_PRIO_MIN: Min priority for a channel. * @CAIF_PRIO_LOW: Low-priority channel. * @CAIF_PRIO_NORMAL: Normal/default priority level. * @CAIF_PRIO_HIGH: High priority level * @CAIF_PRIO_MAX: Max priority for channel * * Priority can be set on CAIF Channels in order to * prioritize between traffic on different CAIF Channels. * These priority levels are recommended, but the priority value * is not restricted to the values defined in this enum, any value * between CAIF_PRIO_MIN and CAIF_PRIO_MAX could be used. */ enum caif_channel_priority { CAIF_PRIO_MIN = 0x01, CAIF_PRIO_LOW = 0x04, CAIF_PRIO_NORMAL = 0x0f, CAIF_PRIO_HIGH = 0x14, CAIF_PRIO_MAX = 0x1F }; /** * enum caif_protocol_type - CAIF Channel type. * @CAIFPROTO_AT: Classic AT channel. * @CAIFPROTO_DATAGRAM: Datagram channel. * @CAIFPROTO_DATAGRAM_LOOP: Datagram loopback channel, used for testing. * @CAIFPROTO_UTIL: Utility (Psock) channel. * @CAIFPROTO_RFM: Remote File Manager * @CAIFPROTO_DEBUG: Debug link * * This enum defines the CAIF Channel type to be used. This defines * the service to connect to on the modem. */ enum caif_protocol_type { CAIFPROTO_AT, CAIFPROTO_DATAGRAM, CAIFPROTO_DATAGRAM_LOOP, CAIFPROTO_UTIL, CAIFPROTO_RFM, CAIFPROTO_DEBUG, _CAIFPROTO_MAX }; #define CAIFPROTO_MAX _CAIFPROTO_MAX /** * enum caif_at_type - AT Service Endpoint * @CAIF_ATTYPE_PLAIN: Connects to a plain vanilla AT channel. */ enum caif_at_type { CAIF_ATTYPE_PLAIN = 2 }; /** * enum caif_debug_type - Content selection for debug connection * @CAIF_DEBUG_TRACE_INTERACTIVE: Connection will contain * both trace and interactive debug. * @CAIF_DEBUG_TRACE: Connection contains trace only. * @CAIF_DEBUG_INTERACTIVE: Connection to interactive debug. */ enum caif_debug_type { CAIF_DEBUG_TRACE_INTERACTIVE = 0, CAIF_DEBUG_TRACE, CAIF_DEBUG_INTERACTIVE, }; /** * enum caif_debug_service - Debug Service Endpoint * @CAIF_RADIO_DEBUG_SERVICE: Debug service on the Radio sub-system * @CAIF_APP_DEBUG_SERVICE: Debug for the applications sub-system */ enum caif_debug_service { CAIF_RADIO_DEBUG_SERVICE = 1, CAIF_APP_DEBUG_SERVICE }; /** * struct sockaddr_caif - the sockaddr structure for CAIF sockets. * @family: Address family number, must be AF_CAIF. * @u: Union of address data 'switched' by family. * : * @u.at: Applies when family = CAIFPROTO_AT. * * @u.at.type: Type of AT link to set up (enum caif_at_type). * * @u.util: Applies when family = CAIFPROTO_UTIL * * @u.util.service: Utility service name. * * @u.dgm: Applies when family = CAIFPROTO_DATAGRAM * * @u.dgm.connection_id: Datagram connection id. * * @u.dgm.nsapi: NSAPI of the PDP-Context. * * @u.rfm: Applies when family = CAIFPROTO_RFM * * @u.rfm.connection_id: Connection ID for RFM. * * @u.rfm.volume: Volume to mount. * * @u.dbg: Applies when family = CAIFPROTO_DEBUG. * * @u.dbg.type: Type of debug connection to set up * (caif_debug_type). * * @u.dbg.service: Service sub-system to connect (caif_debug_service * Description: * This structure holds the connect parameters used for setting up a * CAIF Channel. It defines the service to connect to on the modem. */ struct sockaddr_caif { sa_family_t family; union { struct { __u8 type; /* type: enum caif_at_type */ } at; /* CAIFPROTO_AT */ struct { char service[16]; } util; /* CAIFPROTO_UTIL */ union { __u32 connection_id; __u8 nsapi; } dgm; /* CAIFPROTO_DATAGRAM(_LOOP)*/ struct { __u32 connection_id; char volume[16]; } rfm; /* CAIFPROTO_RFM */ struct { __u8 type; /* type:enum caif_debug_type */ __u8 service; /* service:caif_debug_service */ } dbg; /* CAIFPROTO_DEBUG */ } u; }; /** * enum caif_socket_opts - CAIF option values for getsockopt and setsockopt. * * @CAIFSO_LINK_SELECT: Selector used if multiple CAIF Link layers are * available. Either a high bandwidth * link can be selected (CAIF_LINK_HIGH_BANDW) or * or a low latency link (CAIF_LINK_LOW_LATENCY). * This option is of type __u32. * Alternatively SO_BINDTODEVICE can be used. * * @CAIFSO_REQ_PARAM: Used to set the request parameters for a * utility channel. (maximum 256 bytes). This * option must be set before connecting. * * @CAIFSO_RSP_PARAM: Gets the response parameters for a utility * channel. (maximum 256 bytes). This option * is valid after a successful connect. * * * This enum defines the CAIF Socket options to be used on a socket * of type PF_CAIF. * */ enum caif_socket_opts { CAIFSO_LINK_SELECT = 127, CAIFSO_REQ_PARAM = 128, CAIFSO_RSP_PARAM = 129, }; #endif /* _LINUX_CAIF_SOCKET_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/stemodem/radio-settings.c0000644000015600001650000001275212671500024025001 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "stemodem.h" static const char *none_prefix[] = { NULL }; static const char *cfun_prefix[] = { "+CFUN:", NULL }; struct radio_settings_data { GAtChat *chat; }; enum ste_radio_mode { STE_RADIO_OFF = 0, STE_RADIO_ON = 1, STE_RADIO_FLIGHT_MODE = 4, STE_RADIO_GSM_ONLY = 5, STE_RADIO_WCDMA_ONLY = 6 }; static gboolean ste_mode_to_ofono_mode(enum ste_radio_mode stemode, enum ofono_radio_access_mode *mode) { switch (stemode) { case STE_RADIO_ON: *mode = OFONO_RADIO_ACCESS_MODE_ANY; return TRUE; case STE_RADIO_GSM_ONLY: *mode = OFONO_RADIO_ACCESS_MODE_GSM; return TRUE; case STE_RADIO_WCDMA_ONLY: *mode = OFONO_RADIO_ACCESS_MODE_UMTS; return TRUE; case STE_RADIO_OFF: case STE_RADIO_FLIGHT_MODE: break; } return FALSE; } static gboolean ofono_mode_to_ste_mode(enum ofono_radio_access_mode mode, enum ste_radio_mode *stemode) { switch (mode) { case OFONO_RADIO_ACCESS_MODE_ANY: *stemode = STE_RADIO_ON; return TRUE; case OFONO_RADIO_ACCESS_MODE_GSM: *stemode = STE_RADIO_GSM_ONLY; return TRUE; case OFONO_RADIO_ACCESS_MODE_UMTS: *stemode = STE_RADIO_WCDMA_ONLY; return TRUE; case OFONO_RADIO_ACCESS_MODE_LTE: break; } return FALSE; } static void rat_query_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; int value; 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, "+CFUN:")) goto err; if (!g_at_result_iter_next_number(&iter, &value)) goto err; if (!ste_mode_to_ofono_mode(value, &mode)) goto err; CALLBACK_WITH_SUCCESS(cb, mode, cbd->data); return; err: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void ste_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+CFUN?", cfun_prefix, rat_query_cb, cbd, g_free) == 0) { CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } } static void rat_modify_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, cbd->data); return; } CALLBACK_WITH_SUCCESS(cb, cbd->data); } static void ste_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); struct cb_data *cbd = cb_data_new(cb, data); char buf[20]; enum ste_radio_mode value; if (!ofono_mode_to_ste_mode(mode, &value)) { CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); return; } snprintf(buf, sizeof(buf), "AT+CFUN=%u", value); if (g_at_chat_send(rsd->chat, buf, none_prefix, rat_modify_cb, cbd, g_free) == 0) { CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } } static gboolean ste_radio_settings_register(gpointer user) { struct ofono_radio_settings *rs = user; ofono_radio_settings_register(rs); return FALSE; } static int ste_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *data) { GAtChat *chat = data; struct radio_settings_data *rsd; rsd = g_try_new0(struct radio_settings_data, 1); if (rsd == NULL) return -ENOMEM; rsd->chat = g_at_chat_clone(chat); ofono_radio_settings_set_data(rs, rsd); g_idle_add(ste_radio_settings_register, rs); return 0; } static void ste_radio_settings_remove(struct ofono_radio_settings *rs) { struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs); ofono_radio_settings_set_data(rs, NULL); g_at_chat_unref(rsd->chat); g_free(rsd); } static struct ofono_radio_settings_driver driver = { .name = "stemodem", .probe = ste_radio_settings_probe, .remove = ste_radio_settings_remove, .query_rat_mode = ste_query_rat_mode, .set_rat_mode = ste_set_rat_mode }; void ste_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void ste_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/stemodem/caif_rtnl.h0000644000015600001650000000207512671500024024010 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 ST-Ericsson AB. * * 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 * */ typedef void (*caif_rtnl_create_cb_t) (int index, const char *ifname, void *user_data); extern int caif_rtnl_create_interface(int type, int connid, int loop, caif_rtnl_create_cb_t cb, void *user_data); extern int caif_rtnl_delete_interface(int index); extern int caif_rtnl_init(void); extern void caif_rtnl_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/stemodem/caif_rtnl.c0000644000015600001650000001624212671500024024004 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2010 ST-Ericsson AB. * * 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 #endif #include #include #include #include #include #include #include #include #include #include "if_caif.h" #include "caif_rtnl.h" #define NLMSG_TAIL(nmsg) \ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) #define RTNL_MSG_SIZE 1024 struct rtnl_msg { struct nlmsghdr n; struct ifinfomsg i; char data[RTNL_MSG_SIZE]; }; struct iplink_req { __u32 rtnlmsg_seqnr; void *user_data; caif_rtnl_create_cb_t callback; }; static GSList *pending_requests; static __u32 rtnl_seqnr; static guint rtnl_watch; static GIOChannel *rtnl_channel; static struct iplink_req *find_request(__u32 seq) { GSList *list; for (list = pending_requests; list; list = list->next) { struct iplink_req *req = list->data; if (req->rtnlmsg_seqnr == seq) return req; } return NULL; } static void parse_newlink_param(struct ifinfomsg *msg, int size, int *index, char *ifname) { struct rtattr *attr; for (attr = IFLA_RTA(msg); RTA_OK(attr, size); attr = RTA_NEXT(attr, size)) { if (attr->rta_type == IFLA_IFNAME && ifname != NULL) { strncpy(ifname, RTA_DATA(attr), IF_NAMESIZE); ifname[IF_NAMESIZE-1] = '\0'; break; } } *index = msg->ifi_index; } static void parse_rtnl_message(const void *buf, size_t len) { struct ifinfomsg *msg; struct iplink_req *req; char ifname[IF_NAMESIZE]; int index; while (len > 0) { const struct nlmsghdr *hdr = buf; if (!NLMSG_OK(hdr, len)) break; switch (hdr->nlmsg_type) { case RTM_NEWLINK: req = g_slist_nth_data(pending_requests, 0); if (req == NULL) break; msg = (struct ifinfomsg *) NLMSG_DATA(hdr); parse_newlink_param(msg, IFA_PAYLOAD(hdr), &index, ifname); if (req->callback) req->callback(index, ifname, req->user_data); break; case NLMSG_ERROR: req = find_request(hdr->nlmsg_seq); if (req == NULL) break; DBG("nlmsg error req"); if (req->callback) req->callback(-1, ifname, req->user_data); break; default: req = NULL; break; } len -= hdr->nlmsg_len; buf += hdr->nlmsg_len; if (req) { pending_requests = g_slist_remove(pending_requests, req); g_free(req); } } } static int add_attribute(struct nlmsghdr *n, unsigned int maxlen, int type, const void *data, int datalen) { int len = RTA_LENGTH(datalen); struct rtattr *rta; if ((NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) { DBG("attribute to large for message %d %d %d", n->nlmsg_len, len, maxlen); return -1; } rta = NLMSG_TAIL(n); rta->rta_type = type; rta->rta_len = len; memcpy(RTA_DATA(rta), data, datalen); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); return 0; } static inline void prep_rtnl_req(struct rtnl_msg *msg, int reqtype, __u32 seqnr) { msg->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); msg->n.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; msg->n.nlmsg_type = reqtype; msg->n.nlmsg_seq = seqnr; msg->i.ifi_family = AF_UNSPEC; } static gboolean netlink_event(GIOChannel *chan, GIOCondition cond, void *data) { unsigned char buf[RTNL_MSG_SIZE]; int len, sk; if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) { rtnl_watch = 0; return FALSE; } sk = g_io_channel_unix_get_fd(rtnl_channel); len = recv(sk, buf, sizeof(buf), MSG_DONTWAIT); if (len < 0) { if (len == -EAGAIN) return TRUE; rtnl_watch = 0; return FALSE; } parse_rtnl_message(buf, len); return TRUE; } int caif_rtnl_init(void) { struct sockaddr_nl addr; int sk, err; sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (sk < 0) return sk; memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_groups = RTMGRP_LINK; err = bind(sk, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { close(sk); return err; } rtnl_channel = g_io_channel_unix_new(sk); g_io_channel_set_flags(rtnl_channel, G_IO_FLAG_NONBLOCK, NULL); g_io_channel_set_close_on_unref(rtnl_channel, TRUE); rtnl_watch = g_io_add_watch(rtnl_channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, netlink_event, NULL); return 0; } void caif_rtnl_exit(void) { GSList *list; if (rtnl_watch > 0) g_source_remove(rtnl_watch); g_io_channel_unref(rtnl_channel); for (list = pending_requests; list; list = list->next) { struct iplink_req *req = list->data; g_free(req); } g_slist_free(pending_requests); } int caif_rtnl_create_interface(int type, int connid, int loop, caif_rtnl_create_cb_t cb, void *user_data) { struct iplink_req *req; struct sockaddr_nl addr; struct rtnl_msg msg; struct rtattr *linkinfo; struct rtattr *data_start; int err, sk; req = g_try_new0(struct iplink_req, 1); if (req == NULL) return -ENOMEM; req->user_data = user_data; req->callback = cb; memset(&msg, 0, RTNL_MSG_SIZE); req->rtnlmsg_seqnr = ++rtnl_seqnr; prep_rtnl_req(&msg, RTM_NEWLINK, req->rtnlmsg_seqnr); linkinfo = NLMSG_TAIL(&msg.n); add_attribute(&msg.n, sizeof(msg), IFLA_LINKINFO, NULL, 0); add_attribute(&msg.n, sizeof(msg), IFLA_INFO_KIND, "caif", 4); data_start = NLMSG_TAIL(&msg.n); add_attribute(&msg.n, sizeof(msg), IFLA_INFO_DATA, NULL, 0); switch (type) { case IFLA_CAIF_IPV4_CONNID: case IFLA_CAIF_IPV6_CONNID: add_attribute(&msg.n, sizeof(msg), type, &connid, sizeof(connid)); break; default: DBG("unsupported linktype"); g_free(req); return -EINVAL; } if (loop) add_attribute(&msg.n, sizeof(msg), IFLA_CAIF_LOOPBACK, &loop, sizeof(loop)); data_start->rta_len = (void *)NLMSG_TAIL(&msg.n) - (void *)data_start; linkinfo->rta_len = (void *)NLMSG_TAIL(&msg.n) - (void *)linkinfo; memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; sk = g_io_channel_unix_get_fd(rtnl_channel); err = sendto(sk, &msg, msg.n.nlmsg_len, 0, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { g_free(req); return err; } pending_requests = g_slist_append(pending_requests, req); return 0; } int caif_rtnl_delete_interface(int index) { struct sockaddr_nl addr; struct rtnl_msg msg; int err, sk; if (index < 0) return -EINVAL; sk = g_io_channel_unix_get_fd(rtnl_channel); memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; memset(&msg, 0, sizeof(msg)); prep_rtnl_req(&msg, RTM_DELLINK, ++rtnl_seqnr); msg.i.ifi_index = index; err = sendto(sk, &msg, msg.n.nlmsg_len, 0, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) return err; return 0; } ofono-1.17.bzr6912+16.04.20160314.3/drivers/stemodem/gprs-context.c0000644000015600001650000002535212671500024024502 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "stemodem.h" #include "caif_socket.h" #include "if_caif.h" #include "caif_rtnl.h" #include "common.h" #define MAX_DNS 2 #define IP_ADDR_LEN 20 #define AUTH_BUF_LENGTH (OFONO_GPRS_MAX_USERNAME_LENGTH + \ OFONO_GPRS_MAX_PASSWORD_LENGTH + 128) static const char *none_prefix[] = { NULL }; struct gprs_context_data { GAtChat *chat; unsigned int active_context; /* Id used by CAIF and EPPSD to identify the CAIF channel*/ unsigned int channel_id; /* Linux Interface Id */ unsigned int ifindex; /* Linux Interface name */ char interface[IF_NAMESIZE]; gboolean created; }; struct eppsd_response { char *current; char ip_address[IP_ADDR_LEN]; char subnet_mask[IP_ADDR_LEN]; char mtu[IP_ADDR_LEN]; char dns_server1[IP_ADDR_LEN]; char dns_server2[IP_ADDR_LEN]; char p_cscf_server[IP_ADDR_LEN]; }; static void start_element_handler(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) { struct eppsd_response *rsp = user_data; rsp->current = NULL; if (!strcmp(element_name, "ip_address")) rsp->current = rsp->ip_address; else if (!strcmp(element_name, "subnet_mask")) rsp->current = rsp->subnet_mask; else if (!strcmp(element_name, "mtu")) rsp->current = rsp->mtu; else if (!strcmp(element_name, "dns_server") && rsp->dns_server1[0] == '\0') rsp->current = rsp->dns_server1; else if (!strcmp(element_name, "dns_server")) rsp->current = rsp->dns_server2; else if (!strcmp(element_name, "p_cscf_server")) rsp->current = rsp->p_cscf_server; } static void end_element_handler(GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { struct eppsd_response *rsp = user_data; rsp->current = NULL; } static void text_handler(GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { struct eppsd_response *rsp = user_data; if (rsp->current) { strncpy(rsp->current, text, IP_ADDR_LEN); rsp->current[IP_ADDR_LEN] = '\0'; } } static void error_handler(GMarkupParseContext *context, GError *error, gpointer user_data) { DBG("Error parsing xml response from eppsd: %s", error->message); } static GMarkupParser parser = { start_element_handler, end_element_handler, text_handler, NULL, error_handler }; static void rtnl_callback(int ifindex, const char *ifname, void *user_data) { struct gprs_context_data *gcd = user_data; if (ifindex < 0) { gcd->created = FALSE; ofono_error("Failed to create caif interface"); return; } strncpy(gcd->interface, ifname, sizeof(gcd->interface)); gcd->ifindex = ifindex; gcd->created = TRUE; } static void ste_eppsd_down_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); if (!ok) { struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); return; } gcd->active_context = 0; CALLBACK_WITH_SUCCESS(cb, cbd->data); } static void ste_eppsd_up_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); GAtResultIter iter; int i; gsize length; const char *res_string; const char *dns[MAX_DNS + 1]; struct eppsd_response rsp; GMarkupParseContext *context; if (!ok) { struct ofono_error error; gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); return; } rsp.current = NULL; context = g_markup_parse_context_new(&parser, 0, &rsp, NULL); memset(&rsp, 0, sizeof(rsp)); g_at_result_iter_init(&iter, result); for (i = 0; i < g_at_result_num_response_lines(result); i++) { g_at_result_iter_next(&iter, NULL); res_string = g_at_result_iter_raw_line(&iter); length = strlen(res_string); if (!g_markup_parse_context_parse(context, res_string, length, NULL)) goto error; } if (!g_markup_parse_context_end_parse(context, NULL)) goto error; g_markup_parse_context_free(context); dns[0] = rsp.dns_server1; dns[1] = rsp.dns_server2; dns[2] = NULL; ofono_gprs_context_set_interface(gc, gcd->interface); ofono_gprs_context_set_ipv4_address(gc, rsp.ip_address, TRUE); ofono_gprs_context_set_ipv4_netmask(gc, rsp.subnet_mask); ofono_gprs_context_set_ipv4_dns_servers(gc, dns); CALLBACK_WITH_SUCCESS(cb, cbd->data); return; error: DBG("ste_eppsd_up_cb error"); if (context) g_markup_parse_context_free(context); gcd->active_context = 0; CALLBACK_WITH_FAILURE(cb, cbd->data); } static void ste_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *ncbd; char buf[128]; if (!ok) { struct ofono_error error; gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); return; } snprintf(buf, sizeof(buf), "AT*EPPSD=1,%x,%u", gcd->channel_id, gcd->active_context); ncbd = g_memdup(cbd, sizeof(struct cb_data)); if (g_at_chat_send(gcd->chat, buf, NULL, ste_eppsd_up_cb, ncbd, g_free) > 0) return; g_free(ncbd); gcd->active_context = 0; CALLBACK_WITH_FAILURE(cb, cbd->data); } static void ste_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 cb_data *cbd = cb_data_new(cb, data); char buf[AUTH_BUF_LENGTH]; int len; /* IPv6 support not implemented */ if (ctx->proto != OFONO_GPRS_PROTO_IP) goto error; gcd->active_context = ctx->cid; cbd->user = gc; if (!gcd->created) { DBG("CAIF interface not created (rtnl error?)"); goto error; } len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); if (ctx->apn) snprintf(buf + len, sizeof(buf) - len, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, ste_cgdcont_cb, cbd, g_free) == 0) goto error; /* * Set username and password, this should be done after CGDCONT * or an error can occur. We don't bother with error checking * here */ snprintf(buf, sizeof(buf), "AT*EIAAUW=%d,1,\"%s\",\"%s\"", ctx->cid, ctx->username, ctx->password); g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL); return; error: gcd->active_context = 0; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void ste_gprs_deactivate_primary(struct ofono_gprs_context *gc, unsigned int id, ofono_gprs_context_cb_t cb, void *data) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; cbd->user = gc; snprintf(buf, sizeof(buf), "AT*EPPSD=0,%x,%u", gcd->channel_id, id); if (g_at_chat_send(gcd->chat, buf, none_prefix, ste_eppsd_down_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void cgev_notify(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 *event; int cid; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CGEV:")) return; if (!g_at_result_iter_next_unquoted_string(&iter, &event)) return; if (g_str_has_prefix(event, "NW DEACT") == FALSE) return; if (!g_at_result_iter_skip_next(&iter)) return; if (!g_at_result_iter_next_number(&iter, &cid)) return; if ((unsigned int) cid != gcd->active_context) return; ofono_gprs_context_deactivated(gc, gcd->active_context); gcd->active_context = 0; } static int ste_gprs_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *data) { GAtChat *chat = data; struct gprs_context_data *gcd; int err; gcd = g_new0(struct gprs_context_data, 1); gcd->chat = g_at_chat_clone(chat); g_at_chat_register(gcd->chat, "+CGEV:", cgev_notify, FALSE, gc, NULL); /* Need a unique channel id */ gcd->channel_id = (unsigned int)(unsigned long)gc; ofono_gprs_context_set_data(gc, gcd); err = caif_rtnl_create_interface(IFLA_CAIF_IPV4_CONNID, gcd->channel_id, FALSE, rtnl_callback, gcd); if (err < 0) { DBG("Failed to create IP interface for CAIF"); return err; } return 0; } static void ste_gprs_context_remove(struct ofono_gprs_context *gc) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); /* * Removes IP interface for CAIF. */ if (!gcd->created) goto out; if (caif_rtnl_delete_interface(gcd->ifindex) < 0) { ofono_error("Failed to delete caif interface %s", gcd->interface); goto out; } DBG("removed CAIF interface ch:%d ifname:%s ifindex:%d\n", gcd->channel_id, gcd->interface, gcd->ifindex); out: ofono_gprs_context_set_data(gc, NULL); g_at_chat_unref(gcd->chat); g_free(gcd); } static struct ofono_gprs_context_driver driver = { .name = "stemodem", .probe = ste_gprs_context_probe, .remove = ste_gprs_context_remove, .activate_primary = ste_gprs_activate_primary, .deactivate_primary = ste_gprs_deactivate_primary, }; void ste_gprs_context_init(void) { caif_rtnl_init(); ofono_gprs_context_driver_register(&driver); } void ste_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); caif_rtnl_exit(); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/stemodem/stemodem.h0000644000015600001650000000212112671500024023654 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * * 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 extern void ste_gprs_context_init(void); extern void ste_gprs_context_exit(void); extern void ste_voicecall_init(void); extern void ste_voicecall_exit(void); extern void ste_radio_settings_init(void); extern void ste_radio_settings_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/stemodem/stemodem.c0000644000015600001650000000255612671500024023663 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2010 ST-Ericsson AB. * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "stemodem.h" static int stemodem_init(void) { ste_voicecall_init(); ste_gprs_context_init(); ste_radio_settings_init(); return 0; } static void stemodem_exit(void) { ste_voicecall_exit(); ste_gprs_context_exit(); ste_radio_settings_exit(); } OFONO_PLUGIN_DEFINE(stemodem, "STE modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, stemodem_init, stemodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/stemodem/if_caif.h0000644000015600001650000000176212671500024023431 0ustar pbuserpbgroup00000000000000/* * Copyright (C) ST-Ericsson AB 2010 * Author: Sjur Brendeland/ sjur.brandeland@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ #ifndef IF_CAIF_H_ #define IF_CAIF_H_ #include #include #include /** * enum ifla_caif - CAIF NetlinkRT parameters. * @IFLA_CAIF_IPV4_CONNID: Connection ID for IPv4 PDP Context. * The type of attribute is NLA_U32. * @IFLA_CAIF_IPV6_CONNID: Connection ID for IPv6 PDP Context. * The type of attribute is NLA_U32. * @IFLA_CAIF_LOOPBACK: If different from zero, device is doing loopback * The type of attribute is NLA_U8. * * When using RT Netlink to create, destroy or configure a CAIF IP interface, * enum ifla_caif is used to specify the configuration attributes. */ enum ifla_caif { __IFLA_CAIF_UNSPEC, IFLA_CAIF_IPV4_CONNID, IFLA_CAIF_IPV6_CONNID, IFLA_CAIF_LOOPBACK, __IFLA_CAIF_MAX }; #define IFLA_CAIF_MAX (__IFLA_CAIF_MAX-1) #endif /*IF_CAIF_H_*/ ofono-1.17.bzr6912+16.04.20160314.3/drivers/hsomodem/0000755000015600001650000000000012671500304021671 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/hsomodem/hsomodem.c0000644000015600001650000000242512671500024023652 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "hsomodem.h" static int hsomodem_init(void) { hso_gprs_context_init(); hso_radio_settings_init(); return 0; } static void hsomodem_exit(void) { hso_gprs_context_exit(); hso_radio_settings_exit(); } OFONO_PLUGIN_DEFINE(hsomodem, "HSO modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, hsomodem_init, hsomodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/hsomodem/radio-settings.c0000644000015600001650000001165212671500024024775 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "hsomodem.h" static const char *none_prefix[] = { NULL }; static const char *opsys_prefix[] = { "_OPSYS:", NULL }; struct radio_settings_data { GAtChat *chat; }; static void opsys_query_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; int value; 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, "_OPSYS:") == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &value) == FALSE) goto error; switch (value) { case 0: mode = OFONO_RADIO_ACCESS_MODE_GSM; break; case 1: mode = OFONO_RADIO_ACCESS_MODE_UMTS; break; case 2: case 3: case 5: mode = OFONO_RADIO_ACCESS_MODE_ANY; break; default: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } cb(&error, mode, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void hso_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_OPSYS?", opsys_prefix, opsys_query_cb, cbd, g_free) == 0) { CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } } static void opsys_modify_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void hso_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); struct cb_data *cbd = cb_data_new(cb, data); char buf[20]; int value = 5; switch (mode) { case OFONO_RADIO_ACCESS_MODE_ANY: value = 5; break; case OFONO_RADIO_ACCESS_MODE_GSM: value = 0; break; case OFONO_RADIO_ACCESS_MODE_UMTS: value = 1; break; case OFONO_RADIO_ACCESS_MODE_LTE: goto error; } snprintf(buf, sizeof(buf), "AT_OPSYS=%u,2", value); if (g_at_chat_send(rsd->chat, buf, none_prefix, opsys_modify_cb, cbd, g_free) > 0) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void opsys_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_radio_settings *rs = user_data; if (!ok) { ofono_radio_settings_remove(rs); return; } ofono_radio_settings_register(rs); } static int hso_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *data) { GAtChat *chat = data; struct radio_settings_data *rsd; rsd = g_try_new0(struct radio_settings_data, 1); if (rsd == NULL) return -ENOMEM; rsd->chat = g_at_chat_clone(chat); ofono_radio_settings_set_data(rs, rsd); g_at_chat_send(rsd->chat, "AT_OPBM?", none_prefix, NULL, NULL, NULL); g_at_chat_send(rsd->chat, "AT_OPSYS=?", opsys_prefix, opsys_support_cb, rs, NULL); return 0; } static void hso_radio_settings_remove(struct ofono_radio_settings *rs) { struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs); ofono_radio_settings_set_data(rs, NULL); g_at_chat_unref(rsd->chat); g_free(rsd); } static struct ofono_radio_settings_driver driver = { .name = "hsomodem", .probe = hso_radio_settings_probe, .remove = hso_radio_settings_remove, .query_rat_mode = hso_query_rat_mode, .set_rat_mode = hso_set_rat_mode }; void hso_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void hso_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/hsomodem/hsomodem.h0000644000015600001650000000173412671500024023661 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void hso_gprs_context_init(void); extern void hso_gprs_context_exit(void); extern void hso_radio_settings_init(void); extern void hso_radio_settings_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/hsomodem/gprs-context.c0000644000015600001650000002257512671500024024504 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "hsomodem.h" #define HSO_DISCONNECTED 0 #define HSO_CONNECTED 1 #define HSO_CONNECTING 2 #define HSO_FAILED 3 #define AUTH_BUF_LENGTH OFONO_GPRS_MAX_USERNAME_LENGTH + \ OFONO_GPRS_MAX_PASSWORD_LENGTH + 128 #define STATIC_IP_NETMASK "255.255.255.255" static const char *none_prefix[] = { NULL }; static const char *owandata_prefix[] = { "_OWANDATA:", NULL }; enum hso_state { HSO_NONE = 0, HSO_ENABLING = 1, HSO_DISABLING = 2, }; struct gprs_context_data { GAtChat *chat; unsigned int active_context; /* Currently active */ enum hso_state hso_state; /* Are we in req ? */ ofono_gprs_context_cb_t cb; void *cb_data; /* Callback data */ int owancall; /* State of the call */ }; static void at_owancall_down_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; /* Now we have to wait for the unsolicited notification to arrive */ if (ok && gcd->owancall != 0) { gcd->hso_state = HSO_DISABLING; gcd->cb = cb; gcd->cb_data = cbd->data; return; } decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_owancall_up_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; if (ok) { gcd->hso_state = HSO_ENABLING; gcd->cb = cb; gcd->cb_data = cbd->data; return; } gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void hso_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *ncbd; char buf[64]; if (!ok) { struct ofono_error error; gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); return; } ncbd = g_memdup(cbd, sizeof(struct cb_data)); snprintf(buf, sizeof(buf), "AT_OWANCALL=%u,1,1", gcd->active_context); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_owancall_up_cb, ncbd, g_free) > 0) return; g_free(ncbd); gcd->active_context = 0; CALLBACK_WITH_FAILURE(cb, cbd->data); } static void hso_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 cb_data *cbd = cb_data_new(cb, data); char buf[AUTH_BUF_LENGTH]; int len; /* IPv6 support not implemented */ if (ctx->proto != OFONO_GPRS_PROTO_IP) goto error; gcd->active_context = ctx->cid; cbd->user = gc; if (ctx->username[0] && ctx->password[0]) snprintf(buf, sizeof(buf), "AT$QCPDPP=%u,1,\"%s\",\"%s\"", ctx->cid, ctx->password, ctx->username); else if (ctx->password[0]) snprintf(buf, sizeof(buf), "AT$QCPDPP=%u,2,,\"%s\"", ctx->cid, ctx->password); else snprintf(buf, sizeof(buf), "AT$QCPDPP=%u,0", ctx->cid); if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0) goto error; len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); if (ctx->apn) snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, hso_cgdcont_cb, cbd, g_free) > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void hso_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 cb_data *cbd = cb_data_new(cb, data); char buf[128]; cbd->user = gc; snprintf(buf, sizeof(buf), "AT_OWANCALL=%u,0,1", cid); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_owancall_down_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void owandata_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; int cid; const char *ip = NULL; const char *gateway = NULL; const char *dns1 = NULL; const char *dns2 = NULL; const char *dns[3]; struct ofono_modem *modem; const char *interface; if (!ok) return; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "_OWANDATA:") == FALSE) return; g_at_result_iter_next_number(&iter, &cid); g_at_result_iter_next_unquoted_string(&iter, &ip); g_at_result_iter_next_unquoted_string(&iter, &gateway); g_at_result_iter_next_unquoted_string(&iter, &dns1); g_at_result_iter_next_unquoted_string(&iter, &dns2); if (ip && ip[0] == ' ') ip += 1; if (gateway && gateway[0] == ' ') gateway += 1; if (dns1 && dns1[0] == ' ') dns1 += 1; if (dns2 && dns2[0] == ' ') dns2 += 1; /* Don't bother reporting the same DNS twice */ if (g_str_equal(dns1, dns2)) dns2 = NULL; dns[0] = dns1; dns[1] = dns2; dns[2] = 0; modem = ofono_gprs_context_get_modem(gc); interface = ofono_modem_get_string(modem, "NetworkInterface"); ofono_info("Got the following parameters for context: %d", cid); ofono_info("IP: %s, Gateway: %s", ip, gateway); ofono_info("DNS: %s, %s", dns1, dns2); ofono_gprs_context_set_interface(gc, interface); ofono_gprs_context_set_ipv4_address(gc, ip, TRUE); ofono_gprs_context_set_ipv4_netmask(gc, STATIC_IP_NETMASK); ofono_gprs_context_set_ipv4_gateway(gc, gateway); ofono_gprs_context_set_ipv4_dns_servers(gc, dns); CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); gcd->hso_state = HSO_NONE; gcd->cb = NULL; gcd->cb_data = NULL; } static void owancall_notifier(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; int state; int cid; if (gcd->active_context == 0) return; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "_OWANCALL:") == FALSE) return; g_at_result_iter_next_number(&iter, &cid); g_at_result_iter_next_number(&iter, &state); if (gcd->active_context != (unsigned int) cid) return; switch (state) { case HSO_DISCONNECTED: DBG("HSO Context: disconnected"); if (gcd->hso_state == HSO_DISABLING) { CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); gcd->hso_state = HSO_NONE; gcd->cb = NULL; gcd->cb_data = NULL; } else { ofono_gprs_context_deactivated(gc, gcd->active_context); } gcd->active_context = 0; break; case HSO_CONNECTED: DBG("HSO Context: connected"); if (gcd->hso_state == HSO_ENABLING) { char buf[128]; snprintf(buf, sizeof(buf), "AT_OWANDATA=%u", gcd->active_context); g_at_chat_send(gcd->chat, buf, owandata_prefix, owandata_cb, gc, NULL); } break; case HSO_FAILED: DBG("HSO Context: failed"); if (gcd->hso_state == HSO_ENABLING) { CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data); gcd->hso_state = HSO_NONE; gcd->cb = NULL; gcd->cb_data = NULL; } gcd->active_context = 0; break; default: break; }; gcd->owancall = state; } static int hso_gprs_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *data) { GAtChat *chat = data; struct gprs_context_data *gcd; gcd = g_new0(struct gprs_context_data, 1); gcd->chat = g_at_chat_clone(chat); g_at_chat_register(gcd->chat, "_OWANCALL:", owancall_notifier, FALSE, gc, NULL); ofono_gprs_context_set_data(gc, gcd); return 0; } static void hso_gprs_context_remove(struct ofono_gprs_context *gc) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); ofono_gprs_context_set_data(gc, NULL); g_at_chat_unref(gcd->chat); g_free(gcd); } static struct ofono_gprs_context_driver driver = { .name = "hsomodem", .probe = hso_gprs_context_probe, .remove = hso_gprs_context_remove, .activate_primary = hso_gprs_activate_primary, .deactivate_primary = hso_gprs_deactivate_primary, }; void hso_gprs_context_init(void) { ofono_gprs_context_driver_register(&driver); } void hso_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/iceramodem/0000755000015600001650000000000012671500304022163 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/iceramodem/iceramodem.h0000644000015600001650000000174412671500024024446 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void icera_gprs_context_init(void); extern void icera_gprs_context_exit(void); extern void icera_radio_settings_init(void); extern void icera_radio_settings_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/iceramodem/radio-settings.c0000644000015600001650000001241712671500024025267 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "iceramodem.h" static const char *none_prefix[] = { NULL }; static const char *ipbm_prefix[] = { "%IPBM:", NULL }; static const char *ipsys_prefix[] = { "%IPSYS:", NULL }; struct radio_settings_data { GAtChat *chat; }; static void ipsys_query_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; int value; 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, "%IPSYS:") == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &value) == FALSE) goto error; switch (value) { case 0: mode = OFONO_RADIO_ACCESS_MODE_GSM; break; case 1: mode = OFONO_RADIO_ACCESS_MODE_UMTS; break; case 2: case 3: case 5: mode = OFONO_RADIO_ACCESS_MODE_ANY; break; default: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } cb(&error, mode, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void icera_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%IPSYS?", ipsys_prefix, ipsys_query_cb, cbd, g_free) == 0) { CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } } static void ipsys_modify_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void icera_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); struct cb_data *cbd = cb_data_new(cb, data); char buf[20]; int value = 5; switch (mode) { case OFONO_RADIO_ACCESS_MODE_ANY: value = 5; break; case OFONO_RADIO_ACCESS_MODE_GSM: value = 0; break; case OFONO_RADIO_ACCESS_MODE_UMTS: value = 1; break; case OFONO_RADIO_ACCESS_MODE_LTE: goto error; } snprintf(buf, sizeof(buf), "AT%%IPSYS=%u,2", value); if (g_at_chat_send(rsd->chat, buf, none_prefix, ipsys_modify_cb, cbd, g_free) > 0) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void ipbm_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_radio_settings *rs = user_data; if (!ok) { ofono_radio_settings_remove(rs); return; } ofono_radio_settings_register(rs); } static void ipsys_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) { ofono_radio_settings_remove(rs); return; } g_at_chat_send(rsd->chat, "AT%IPBM=?", ipbm_prefix, ipbm_support_cb, rs, NULL); } static int icera_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *data) { GAtChat *chat = data; struct radio_settings_data *rsd; rsd = g_try_new0(struct radio_settings_data, 1); if (rsd == NULL) return -ENOMEM; rsd->chat = g_at_chat_clone(chat); ofono_radio_settings_set_data(rs, rsd); g_at_chat_send(rsd->chat, "AT%IPSYS=?", ipsys_prefix, ipsys_support_cb, rs, NULL); return 0; } static void icera_radio_settings_remove(struct ofono_radio_settings *rs) { struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs); ofono_radio_settings_set_data(rs, NULL); g_at_chat_unref(rsd->chat); g_free(rsd); } static struct ofono_radio_settings_driver driver = { .name = "iceramodem", .probe = icera_radio_settings_probe, .remove = icera_radio_settings_remove, .query_rat_mode = icera_query_rat_mode, .set_rat_mode = icera_set_rat_mode }; void icera_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void icera_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/iceramodem/iceramodem.c0000644000015600001650000000245312671500024024437 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "iceramodem.h" static int iceramodem_init(void) { icera_gprs_context_init(); icera_radio_settings_init(); return 0; } static void iceramodem_exit(void) { icera_gprs_context_exit(); icera_radio_settings_exit(); } OFONO_PLUGIN_DEFINE(iceramodem, "Icera modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, iceramodem_init, iceramodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/iceramodem/gprs-context.c0000644000015600001650000002347012671500024024771 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "gattty.h" #include "iceramodem.h" #define ICERA_DISCONNECTED 0 #define ICERA_CONNECTED 1 #define ICERA_CONNECTING 2 #define ICERA_FAILED 3 #define AUTH_BUF_LENGTH OFONO_GPRS_MAX_USERNAME_LENGTH + \ OFONO_GPRS_MAX_PASSWORD_LENGTH + 128 #define STATIC_IP_NETMASK "255.255.255.255" static const char *none_prefix[] = { NULL }; static const char *ipdpact_prefix[] = { "%IPDPACT", NULL }; static const char *ipdpaddr_prefix[] = { "%IPDPADDR", NULL }; enum state { STATE_NONE, STATE_ENABLING, STATE_DISABLING, }; struct gprs_context_data { GAtChat *chat; unsigned int active_context; char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1]; char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1]; enum state state; ofono_gprs_context_cb_t cb; void *cb_data; }; static void ipdpaddr_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; int cid; const char *ip = NULL; const char *gateway = NULL; const char *dns1 = NULL; const char *dns2 = NULL; const char *dns[3]; struct ofono_modem *modem; const char *interface; if (!ok) return; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "%IPDPADDR:") == FALSE) return; g_at_result_iter_next_number(&iter, &cid); g_at_result_iter_next_unquoted_string(&iter, &ip); g_at_result_iter_next_unquoted_string(&iter, &gateway); g_at_result_iter_next_unquoted_string(&iter, &dns1); g_at_result_iter_next_unquoted_string(&iter, &dns2); if (ip && ip[0] == ' ') ip += 1; if (gateway && gateway[0] == ' ') gateway += 1; if (dns1 && dns1[0] == ' ') dns1 += 1; if (dns2 && dns2[0] == ' ') dns2 += 1; /* Don't bother reporting the same DNS twice */ if (g_str_equal(dns1, dns2)) dns2 = NULL; dns[0] = dns1; dns[1] = dns2; dns[2] = 0; modem = ofono_gprs_context_get_modem(gc); interface = ofono_modem_get_string(modem, "NetworkInterface"); ofono_info("Got the following parameters for context: %d", cid); ofono_info("IP: %s, Gateway: %s", ip, gateway); ofono_info("DNS: %s, %s", dns1, dns2); ofono_gprs_context_set_interface(gc, interface); ofono_gprs_context_set_ipv4_address(gc, ip, TRUE); ofono_gprs_context_set_ipv4_netmask(gc, STATIC_IP_NETMASK); ofono_gprs_context_set_ipv4_gateway(gc, gateway); ofono_gprs_context_set_ipv4_dns_servers(gc, dns); CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); gcd->state = STATE_NONE; gcd->cb = NULL; gcd->cb_data = NULL; } static void ipdpact_notifier(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; int cid, state; if (gcd->active_context == 0) return; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "%IPDPACT:") == FALSE) return; g_at_result_iter_next_number(&iter, &cid); g_at_result_iter_next_number(&iter, &state); DBG("cid %d state %d", cid, state); if (gcd->active_context != (unsigned int) cid) return; switch (state) { case ICERA_DISCONNECTED: if (gcd->state == STATE_DISABLING) { CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); gcd->state = STATE_NONE; gcd->cb = NULL; gcd->cb_data = NULL; } else ofono_gprs_context_deactivated(gc, gcd->active_context); gcd->active_context = 0; break; case ICERA_CONNECTED: if (gcd->state == STATE_ENABLING) { char buf[128]; snprintf(buf, sizeof(buf), "AT%%IPDPADDR=%u", gcd->active_context); g_at_chat_send(gcd->chat, buf, ipdpaddr_prefix, ipdpaddr_cb, gc, NULL); } break; case ICERA_CONNECTING: break; case ICERA_FAILED: if (gcd->state == STATE_ENABLING) { CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data); gcd->state = STATE_NONE; gcd->cb = NULL; gcd->cb_data = NULL; } gcd->active_context = 0; break; } } static void ipdpact_down_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; DBG("ok %d", ok); if (ok) { gcd->state = STATE_DISABLING; gcd->cb = cb; gcd->cb_data = cbd->data; ipdpact_notifier(result, gc); return; } decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void ipdpact_up_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; DBG("ok %d", ok); if (ok) { gcd->state = STATE_ENABLING; gcd->cb = cb; gcd->cb_data = cbd->data; ipdpact_notifier(result, gc); return; } gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *ncbd; char buf[AUTH_BUF_LENGTH]; DBG("ok %d", ok); if (!ok) { struct ofono_error error; gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); return; } if (gcd->username[0] && gcd->password[0]) sprintf(buf, "AT%%IPDPCFG=%u,0,1,\"%s\",\"%s\"", gcd->active_context, gcd->username, gcd->password); else sprintf(buf, "AT%%IPDPCFG=%u,0,0,\"\",\"\"", gcd->active_context); if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0) goto error; ncbd = g_memdup(cbd, sizeof(struct cb_data)); snprintf(buf, sizeof(buf), "AT%%IPDPACT=%u,1", gcd->active_context); if (g_at_chat_send(gcd->chat, buf, ipdpact_prefix, ipdpact_up_cb, ncbd, g_free) > 0) return; g_free(ncbd); error: gcd->active_context = 0; CALLBACK_WITH_FAILURE(cb, cbd->data); } static void icera_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 cb_data *cbd = cb_data_new(cb, data); char buf[OFONO_GPRS_MAX_APN_LENGTH + 128]; int len = 0; DBG("cid %u", ctx->cid); gcd->active_context = ctx->cid; memcpy(gcd->username, ctx->username, sizeof(ctx->username)); memcpy(gcd->password, ctx->password, sizeof(ctx->password)); cbd->user = gc; switch (ctx->proto) { case OFONO_GPRS_PROTO_IP: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); break; case OFONO_GPRS_PROTO_IPV6: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"", ctx->cid); break; case OFONO_GPRS_PROTO_IPV4V6: len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"", ctx->cid); break; } if (ctx->apn) snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_cgdcont_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void icera_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 cb_data *cbd = cb_data_new(cb, data); char buf[128]; DBG("cid %u", cid); cbd->user = gc; snprintf(buf, sizeof(buf), "AT%%IPDPACT=%u,0", cid); if (g_at_chat_send(gcd->chat, buf, ipdpact_prefix, ipdpact_down_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static int icera_gprs_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *data) { GAtChat *chat = data; struct gprs_context_data *gcd; DBG(""); gcd = g_try_new0(struct gprs_context_data, 1); if (gcd == NULL) return -ENOMEM; gcd->chat = g_at_chat_clone(chat); ofono_gprs_context_set_data(gc, gcd); g_at_chat_register(gcd->chat, "%IPDPACT:", ipdpact_notifier, FALSE, gc, NULL); return 0; } static void icera_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); g_at_chat_unref(gcd->chat); g_free(gcd); } static struct ofono_gprs_context_driver driver = { .name = "iceramodem", .probe = icera_gprs_context_probe, .remove = icera_gprs_context_remove, .activate_primary = icera_gprs_activate_primary, .deactivate_primary = icera_gprs_deactivate_primary, }; void icera_gprs_context_init(void) { ofono_gprs_context_driver_register(&driver); } void icera_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/nwmodem/0000755000015600001650000000000012671500304021524 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/nwmodem/radio-settings.c0000644000015600001650000001150412671500024024624 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "nwmodem.h" static const char *none_prefix[] = { NULL }; static const char *nwrat_prefix[] = { "$NWRAT:", NULL }; struct radio_settings_data { GAtChat *chat; }; static void nwrat_query_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; int value; 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, "$NWRAT:") == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &value) == FALSE) goto error; switch (value) { case 0: mode = OFONO_RADIO_ACCESS_MODE_ANY; break; case 1: mode = OFONO_RADIO_ACCESS_MODE_GSM; break; case 2: mode = OFONO_RADIO_ACCESS_MODE_UMTS; break; default: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } cb(&error, mode, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); } static void nw_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$NWRAT?", nwrat_prefix, nwrat_query_cb, cbd, g_free) == 0) { CALLBACK_WITH_FAILURE(cb, -1, data); g_free(cbd); } } static void nwrat_modify_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void nw_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); struct cb_data *cbd = cb_data_new(cb, data); char buf[20]; int value = 0; switch (mode) { case OFONO_RADIO_ACCESS_MODE_ANY: value = 0; break; case OFONO_RADIO_ACCESS_MODE_GSM: value = 1; break; case OFONO_RADIO_ACCESS_MODE_UMTS: value = 2; break; case OFONO_RADIO_ACCESS_MODE_LTE: goto error; } snprintf(buf, sizeof(buf), "AT$NWRAT=%u,2", value); if (g_at_chat_send(rsd->chat, buf, none_prefix, nwrat_modify_cb, cbd, g_free) > 0) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void nwrat_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_radio_settings *rs = user_data; if (!ok) { ofono_radio_settings_remove(rs); return; } ofono_radio_settings_register(rs); } static int nw_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *data) { GAtChat *chat = data; struct radio_settings_data *rsd; rsd = g_try_new0(struct radio_settings_data, 1); if (rsd == NULL) return -ENOMEM; rsd->chat = g_at_chat_clone(chat); ofono_radio_settings_set_data(rs, rsd); g_at_chat_send(rsd->chat, "AT$NWRAT=?", nwrat_prefix, nwrat_support_cb, rs, NULL); return 0; } static void nw_radio_settings_remove(struct ofono_radio_settings *rs) { struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs); ofono_radio_settings_set_data(rs, NULL); g_at_chat_unref(rsd->chat); g_free(rsd); } static struct ofono_radio_settings_driver driver = { .name = "nwmodem", .probe = nw_radio_settings_probe, .remove = nw_radio_settings_remove, .query_rat_mode = nw_query_rat_mode, .set_rat_mode = nw_set_rat_mode }; void nw_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void nw_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/nwmodem/nwmodem.h0000644000015600001650000000160712671500024023346 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void nw_radio_settings_init(void); extern void nw_radio_settings_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/nwmodem/nwmodem.c0000644000015600001650000000233512671500024023340 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "nwmodem.h" static int nwmodem_init(void) { nw_radio_settings_init(); return 0; } static void nwmodem_exit(void) { nw_radio_settings_exit(); } OFONO_PLUGIN_DEFINE(nwmodem, "Novatel modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, nwmodem_init, nwmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/infineonmodem/0000755000015600001650000000000012671500304022705 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/infineonmodem/infineon_constants.h0000644000015600001650000000666312671500024026771 0ustar pbuserpbgroup00000000000000/* * * RIL constants for infineon modem * * Copyright (C) 2014 Canonical 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. * * 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 * */ #ifndef INFINEON_CONSTANTS_H #define INFINEON_CONSTANTS_H /* Messages encapsulated in RIL_REQUEST_OEM_HOOK_RAW requests */ #define INF_RIL_REQUEST_OEM_QUERY_SELECT_BAND 1 #define INF_RIL_REQUEST_OEM_SET_SELECT_BAND 2 #define INF_RIL_REQUEST_OEM_SET_CIRCUIT_SWITCHING_PAGING 3 #define INF_RIL_REQUEST_OEM_GET_LAST_FAILURE_REPORT_FOR_CS_REGISTRATION 4 #define INF_RIL_REQUEST_OEM_GET_SELECT_BEARER_SERVICE_TYPE 5 #define INF_RIL_REQUEST_OEM_GET_XPROGRESS_STATUS 6 #define INF_RIL_REQUEST_OEM_SET_SS_NOTIFY 7 #define INF_RIL_REQUEST_OEM_GET_SS_NOTIFY 8 #define INF_RIL_REQUEST_OEM_SET_AUTHENTICATION_TYPE 9 #define INF_RIL_REQUEST_OEM_SWITCH_OFF_MS 10 #define INF_RIL_REQUEST_OEM_SET_AUTO_TIMEZONE_UPDATE 11 #define INF_RIL_REQUEST_OEM_SET_TIMEZONE_RESPORTING 12 #define INF_RIL_REQUEST_OEM_SET_DISPLAY_SIM_AND_PB_STATUS 13 #define INF_RIL_REQUEST_OEM_GET_REMAIN_SIM_PIN_ATTEMPTS 14 #define INF_RIL_REQUEST_OEM_SET_AUTO_REDIAL 15 #define INF_RIL_REQUEST_OEM_QUERY_CALL_STATUS_REPORTING 16 #define INF_RIL_REQUEST_OEM_SET_AUTO_ANSWER 17 #define INF_RIL_REQUEST_OEM_SET_LINE 18 #define INF_RIL_REQUEST_OEM_PDP_ACTIVATE_OR_DEACTIVATE 19 #define INF_RIL_REQUEST_OEM_QUERY_GPRS_MS_CLASS 20 #define INF_RIL_REQUEST_OEM_SET_TRACE_AND_AT_INTERFACES 21 #define INF_RIL_REQUEST_OEM_QUERY_TRACE_AND_AT_INTERFACES_CONFIGURE 22 #define INF_RIL_REQUEST_OEM_SWITCH_TRACE_ON_OR_OFF 23 #define INF_RIL_REQUEST_OEM_READ_EXCEPTION_LOG 24 #define INF_RIL_REQUEST_OEM_GET_PHONE_ACTIVITY_STATUS 25 #define INF_RIL_REQUEST_OEM_INITIATE_RESEND_SMS_IF_GPRS_FAILS 26 #define INF_RIL_REQUEST_OEM_GET_DEVICE_NUMBER 27 #define INF_RIL_REQUEST_OEM_ENABLE_STK 28 #define INF_RIL_REQUEST_OEM_GET_SUBSCRIBER_NUMBER 29 #define INF_RIL_REQUEST_OEM_SELECT_PHONE_BOOK 30 #define INF_RIL_REQUEST_OEM_READ_PHONE_BOOK 31 #define INF_RIL_REQUEST_OEM_INSERT_RECORD_TO_PHONE_BOOK 32 #define INF_RIL_REQUEST_OEM_DELECT_RECORD_IN_PHONE_BOOK 33 #define INF_RIL_REQUEST_OEM_GET_RECORD_FIELDS_MAX_LEN 34 #define INF_RIL_REQUEST_OEM_SET_SERIAL_PORT 35 #define INF_RIL_REQUEST_OEM_SET_DATA_PREFERED 36 #define INF_RIL_REQUEST_OEM_SET_MODEM_ROUTING 37 #define INF_RIL_REQUEST_OEM_CLEAR_MISS_NUMBER 38 #define INF_RIL_REQUEST_OEM_ATH 39 #define INF_RIL_REQUEST_OEM_NOSIG_MODE_TEST 40 #define INF_RIL_REQUEST_OEM_SELECT_3G_BAND 41 #define INF_RIL_REQUEST_OEM_QUERY_3G_BAND 42 #define INF_RIL_REQUEST_OEM_HW_RESET_MODEM 43 #define INF_RIL_REQUEST_OEM_QUERY_DIRECT 44 #define INF_RIL_REQUEST_OEM_USER_PLMN_QUERY 45 #define INF_RIL_REQUEST_OEM_USER_PLMN_SET 46 #define INF_RIL_REQUEST_OEM_USER_PLMN_DELTE 47 #define INF_RIL_REQUEST_OEM_SET_USB_LOG 48 #define INF_RIL_REQUEST_OEM_UPDATE_CSQ 49 #define INF_RIL_REQUEST_OEM_DUMP_CELL_ENV 50 #endif /* INFINEON_CONSTANTS_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/speedupmodem/0000755000015600001650000000000012671500304022545 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/speedupmodem/speedupmodem.h0000644000015600001650000000157512671500024025414 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void speedup_ussd_init(void); extern void speedup_ussd_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/speedupmodem/speedupmodem.c0000644000015600001650000000236112671500024025401 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "speedupmodem.h" static int speedupmodem_init(void) { speedup_ussd_init(); return 0; } static void speedupmodem_exit(void) { speedup_ussd_exit(); } OFONO_PLUGIN_DEFINE(speedupmodem, "SpeedUp modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, speedupmodem_init, speedupmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/speedupmodem/ussd.c0000644000015600001650000001200212671500024023661 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 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 #endif #include #include #include #include #include #include #include "util.h" #include "gatchat.h" #include "speedupmodem.h" static const char *cusd_prefix[] = { "+CUSD:", NULL }; static const char *none_prefix[] = { NULL }; struct ussd_data { GAtChat *chat; }; static void cusd_parse(GAtResult *result, struct ofono_ussd *ussd) { GAtResultIter iter; int status, dcs; const char *content; unsigned char msg[160]; const unsigned char *msg_ptr = NULL; long msg_len; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CUSD:")) return; if (!g_at_result_iter_next_number(&iter, &status)) return; if (!g_at_result_iter_next_string(&iter, &content)) goto out; if (!g_at_result_iter_next_number(&iter, &dcs)) dcs = 0; msg_ptr = decode_hex_own_buf(content, -1, &msg_len, 0, msg); out: ofono_ussd_notify(ussd, status, dcs, msg_ptr, msg_ptr ? msg_len : 0); } static void cusd_request_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_ussd_cb_t cb = cbd->cb; struct ofono_ussd *ussd = cbd->user; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); cusd_parse(result, ussd); } static void speedup_ussd_request(struct ofono_ussd *ussd, int dcs, const unsigned char *pdu, int len, ofono_ussd_cb_t cb, void *user_data) { struct ussd_data *data = ofono_ussd_get_data(ussd); struct cb_data *cbd = cb_data_new(cb, user_data); char buf[512], coded_buf[182]; long written; cbd->user = ussd; unpack_7bit_own_buf(pdu, len, 0, TRUE, sizeof(coded_buf), &written, 0, (unsigned char *)coded_buf); if (written < 1) goto error; snprintf(buf, sizeof(buf), "AT+CUSD=1,\"%.*s\",%d", (int) written, coded_buf, dcs); if (g_at_chat_send(data->chat, buf, cusd_prefix, cusd_request_cb, cbd, g_free) > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } static void cusd_cancel_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_ussd_cb_t cb = cbd->cb; struct ofono_error error; /* * All errors and notifications arrive unexpected and * thus just reset the state here. This is safer than * getting stuck in a dead-lock. */ error.type = OFONO_ERROR_TYPE_NO_ERROR; error.error = 0; cb(&error, cbd->data); } static void speedup_ussd_cancel(struct ofono_ussd *ussd, ofono_ussd_cb_t cb, void *user_data) { struct ussd_data *data = ofono_ussd_get_data(ussd); struct cb_data *cbd = cb_data_new(cb, user_data); cbd->user = data; if (g_at_chat_send(data->chat, "AT+CUSD=2", none_prefix, cusd_cancel_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } static void cusd_notify(GAtResult *result, gpointer user_data) { struct ofono_ussd *ussd = user_data; cusd_parse(result, ussd); } static void cusd_register(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_ussd *ussd = user_data; struct ussd_data *data = ofono_ussd_get_data(ussd); if (!ok) { ofono_error("Could not enable CUSD notifications"); ofono_ussd_remove(ussd); return; } g_at_chat_register(data->chat, "+CUSD:", cusd_notify, FALSE, ussd, NULL); ofono_ussd_register(ussd); } static int speedup_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor, void *user) { GAtChat *chat = user; struct ussd_data *data; data = g_try_new0(struct ussd_data, 1); if (data == NULL) return -ENOMEM; data->chat = g_at_chat_clone(chat); ofono_ussd_set_data(ussd, data); g_at_chat_send(data->chat, "AT+CUSD=1", none_prefix, cusd_register, ussd, NULL); return 0; } static void speedup_ussd_remove(struct ofono_ussd *ussd) { struct ussd_data *data = ofono_ussd_get_data(ussd); ofono_ussd_set_data(ussd, NULL); g_at_chat_unref(data->chat); g_free(data); } static struct ofono_ussd_driver driver = { .name = "speedupmodem", .probe = speedup_ussd_probe, .remove = speedup_ussd_remove, .request = speedup_ussd_request, .cancel = speedup_ussd_cancel, }; void speedup_ussd_init(void) { ofono_ussd_driver_register(&driver); } void speedup_ussd_exit(void) { ofono_ussd_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/0000755000015600001650000000000012671500304021666 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/qmi.c0000644000015600001650000012564312671500024022632 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #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 qmi_device { int ref_count; int fd; GIOChannel *io; bool close_on_unref; guint read_watch; guint write_watch; GQueue *req_queue; GQueue *control_queue; GQueue *service_queue; uint8_t next_control_tid; uint16_t next_service_tid; qmi_debug_func_t debug_func; void *debug_data; uint16_t control_major; uint16_t control_minor; char *version_str; struct qmi_version *version_list; uint8_t version_count; GHashTable *service_list; unsigned int release_users; }; struct qmi_service { int ref_count; struct qmi_device *device; bool shared; uint8_t type; uint16_t major; uint16_t minor; uint8_t client_id; uint16_t next_notify_id; GList *notify_list; }; struct qmi_param { void *data; uint16_t length; }; struct qmi_result { uint16_t message; uint16_t result; uint16_t error; const void *data; uint16_t length; }; struct qmi_request { uint16_t tid; uint8_t client; void *buf; size_t len; qmi_message_func_t callback; void *user_data; }; struct qmi_notify { uint16_t id; uint16_t message; qmi_result_func_t callback; void *user_data; qmi_destroy_func_t destroy; }; struct qmi_mux_hdr { uint8_t frame; /* Always 0x01 */ uint16_t length; /* Packet size without frame byte */ uint8_t flags; /* Either 0x00 or 0x80 */ uint8_t service; /* Service type (0x00 for control) */ uint8_t client; /* Client identifier (0x00 for control) */ } __attribute__ ((packed)); #define QMI_MUX_HDR_SIZE 6 struct qmi_control_hdr { uint8_t type; /* Bit 1 = response, Bit 2 = indication */ uint8_t transaction; /* Transaction identifier */ } __attribute__ ((packed)); #define QMI_CONTROL_HDR_SIZE 2 struct qmi_service_hdr { uint8_t type; /* Bit 2 = response, Bit 3 = indication */ uint16_t transaction; /* Transaction identifier */ } __attribute__ ((packed)); #define QMI_SERVICE_HDR_SIZE 3 struct qmi_message_hdr { uint16_t message; /* Message identifier */ uint16_t length; /* Message size without header */ uint8_t data[0]; } __attribute__ ((packed)); #define QMI_MESSAGE_HDR_SIZE 4 struct qmi_tlv_hdr { uint8_t type; uint16_t length; uint8_t value[0]; } __attribute__ ((packed)); #define QMI_TLV_HDR_SIZE 3 void qmi_free(void *ptr) { free(ptr); } static struct qmi_request *__request_alloc(uint8_t service, uint8_t client, uint16_t message, uint16_t headroom, const void *data, uint16_t length, qmi_message_func_t func, void *user_data, void **head) { struct qmi_request *req; struct qmi_mux_hdr *hdr; struct qmi_message_hdr *msg; req = g_try_new0(struct qmi_request, 1); if (!req) return NULL; req->len = QMI_MUX_HDR_SIZE + headroom + QMI_MESSAGE_HDR_SIZE + length; req->buf = g_try_malloc(req->len); if (!req->buf) { g_free(req); return NULL; } req->client = client; hdr = req->buf; hdr->frame = 0x01; hdr->length = GUINT16_TO_LE(req->len - 1); hdr->flags = 0x00; hdr->service = service; hdr->client = client; msg = req->buf + QMI_MUX_HDR_SIZE + headroom; msg->message = GUINT16_TO_LE(message); msg->length = GUINT16_TO_LE(length); if (data && length > 0) memcpy(req->buf + QMI_MUX_HDR_SIZE + headroom + QMI_MESSAGE_HDR_SIZE, data, length); req->callback = func; req->user_data = user_data; *head = req->buf + QMI_MUX_HDR_SIZE; return req; } static void __request_free(gpointer data, gpointer user_data) { struct qmi_request *req = data; g_free(req->buf); g_free(req); } static gint __request_compare(gconstpointer a, gconstpointer b) { const struct qmi_request *req = a; uint16_t tid = GPOINTER_TO_UINT(b); return req->tid - tid; } static void __notify_free(gpointer data, gpointer user_data) { struct qmi_notify *notify = data; if (notify->destroy) notify->destroy(notify->user_data); g_free(notify); } static gint __notify_compare(gconstpointer a, gconstpointer b) { const struct qmi_notify *notify = a; uint16_t id = GPOINTER_TO_UINT(b); return notify->id - id; } static gboolean __service_compare_shared(gpointer key, gpointer value, gpointer user_data) { struct qmi_service *service = value; uint8_t type = GPOINTER_TO_UINT(user_data); if (!service->shared) return FALSE; if (service->type == type) return TRUE; return FALSE; } static void __hexdump(const char dir, const unsigned char *buf, size_t len, qmi_debug_func_t function, void *user_data) { static const char hexdigits[] = "0123456789abcdef"; char str[68]; size_t i; if (!function || !len) return; str[0] = dir; for (i = 0; i < len; i++) { str[((i % 16) * 3) + 1] = ' '; str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4]; str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf]; str[(i % 16) + 51] = isprint(buf[i]) ? buf[i] : '.'; if ((i + 1) % 16 == 0) { str[49] = ' '; str[50] = ' '; str[67] = '\0'; function(str, user_data); str[0] = ' '; } } if (i % 16 > 0) { size_t j; for (j = (i % 16); j < 16; j++) { str[(j * 3) + 1] = ' '; str[(j * 3) + 2] = ' '; str[(j * 3) + 3] = ' '; str[j + 51] = ' '; } str[49] = ' '; str[50] = ' '; str[67] = '\0'; function(str, user_data); } } static const char *__service_type_to_string(uint8_t type) { switch (type) { case QMI_SERVICE_CONTROL: return "CTL"; case QMI_SERVICE_WDS: return "WDS"; case QMI_SERVICE_DMS: return "DMS"; case QMI_SERVICE_NAS: return "NAS"; case QMI_SERVICE_QOS: return "QOS"; case QMI_SERVICE_WMS: return "WMS"; case QMI_SERVICE_PDS: return "PDS"; case QMI_SERVICE_AUTH: return "AUTH"; case QMI_SERVICE_AT: return "AT"; case QMI_SERVICE_VOICE: return "VOICE"; case QMI_SERVICE_CAT: return "CAT"; case QMI_SERVICE_UIM: return "UIM"; case QMI_SERVICE_PBM: return "PBM"; case QMI_SERVICE_RMTFS: return "RMTFS"; case QMI_SERVICE_LOC: return "LOC"; case QMI_SERVICE_SAR: return "SAR"; case QMI_SERVICE_CSD: return "CSD"; case QMI_SERVICE_EFS: return "EFS"; case QMI_SERVICE_TS: return "TS"; case QMI_SERVICE_TMD: return "TMS"; case QMI_SERVICE_PDC: return "PDC"; case QMI_SERVICE_CAT_OLD: return "CAT"; case QMI_SERVICE_RMS: return "RMS"; case QMI_SERVICE_OMA: return "OMA"; } return NULL; } static const struct { uint16_t err; const char *str; } __error_table[] = { { 0x0000, "NONE" }, { 0x0001, "MALFORMED_MSG" }, { 0x0002, "NO_MEMORY" }, { 0x0003, "INTERNAL" }, { 0x0004, "ABORTED" }, { 0x0005, "CLIENT_IDS_EXHAUSTED" }, { 0x0006, "UNABORTABLE_TRANSACTION" }, { 0x0007, "INVALID_CLIENT_ID" }, { 0x0008, "NO_THRESHOLDS" }, { 0x0009, "INVALID_HANDLE" }, { 0x000a, "INVALID_PROFILE" }, { 0x000b, "INVALID_PINID" }, { 0x000c, "INCORRECT_PIN" }, { 0x000d, "NO_NETWORK_FOUND" }, { 0x000e, "CALL_FAILED" }, { 0x000f, "OUT_OF_CALL" }, { 0x0010, "NOT_PROVISIONED" }, { 0x0011, "MISSING_ARG" }, { 0x0013, "ARG_TOO_LONG" }, { 0x0016, "INVALID_TX_ID" }, { 0x0017, "DEVICE_IN_USE" }, { 0x0018, "OP_NETWORK_UNSUPPORTED" }, { 0x0019, "OP_DEVICE_UNSUPPORTED" }, { 0x001a, "NO_EFFECT" }, { 0x001b, "NO_FREE_PROFILE" }, { 0x001c, "INVALID_PDP_TYPE" }, { 0x001d, "INVALID_TECH_PREF" }, { 0x001e, "INVALID_PROFILE_TYPE" }, { 0x001f, "INVALID_SERVICE_TYPE" }, { 0x0020, "INVALID_REGISTER_ACTION" }, { 0x0021, "INVALID_PS_ATTACH_ACTION" }, { 0x0022, "AUTHENTICATION_FAILED" }, { 0x0023, "PIN_BLOCKED" }, { 0x0024, "PIN_PERM_BLOCKED" }, { 0x0025, "UIM_NOT_INITIALIZED" }, { 0x0026, "MAX_QOS_REQUESTS_IN_USE" }, { 0x0027, "INCORRECT_FLOW_FILTER" }, { 0x0028, "NETWORK_QOS_UNAWARE" }, { 0x0029, "INVALID_QOS_ID/INVALID_ID" }, { 0x002a, "REQUESTED_NUM_UNSUPPORTED" }, { 0x002b, "INTERFACE_NOT_FOUND" }, { 0x002c, "FLOW_SUSPENDED" }, { 0x002d, "INVALID_DATA_FORMAT" }, { 0x002e, "GENERAL" }, { 0x002f, "UNKNOWN" }, { 0x0030, "INVALID_ARG" }, { 0x0031, "INVALID_INDEX" }, { 0x0032, "NO_ENTRY" }, { 0x0033, "DEVICE_STORAGE_FULL" }, { 0x0034, "DEVICE_NOT_READY" }, { 0x0035, "NETWORK_NOT_READY" }, { 0x0036, "CAUSE_CODE" }, { 0x0037, "MESSAGE_NOT_SENT" }, { 0x0038, "MESSAGE_DELIVERY_FAILURE" }, { 0x0039, "INVALID_MESSAGE_ID" }, { 0x003a, "ENCODING" }, { 0x003b, "AUTHENTICATION_LOCK" }, { 0x003c, "INVALID_TRANSACTION" }, { 0x0041, "SESSION_INACTIVE" }, { 0x0042, "SESSION_INVALID" }, { 0x0043, "SESSION_OWNERSHIP" }, { 0x0044, "INSUFFICIENT_RESOURCES" }, { 0x0045, "DISABLED" }, { 0x0046, "INVALID_OPERATION" }, { 0x0047, "INVALID_QMI_CMD" }, { 0x0048, "TPDU_TYPE" }, { 0x0049, "SMSC_ADDR" }, { 0x004a, "INFO_UNAVAILABLE" }, { 0x004b, "SEGMENT_TOO_LONG" }, { 0x004c, "SEGEMENT_ORDER" }, { 0x004d, "BUNDLING_NOT_SUPPORTED" }, { 0x004f, "POLICY_MISMATCH" }, { 0x0050, "SIM_FILE_NOT_FOUND" }, { 0x0051, "EXTENDED_INTERNAL" }, { 0x0052, "ACCESS_DENIED" }, { 0x0053, "HARDWARE_RESTRICTED" }, { 0x0054, "ACK_NOT_SENT" }, { 0x0055, "INJECT_TIMEOUT" }, { } }; static const char *__error_to_string(uint16_t error) { int i; for (i = 0; __error_table[i].str; i++) { if (__error_table[i].err == error) return __error_table[i].str; } return NULL; } static void __debug_msg(const char dir, const void *buf, size_t len, qmi_debug_func_t function, void *user_data) { const struct qmi_mux_hdr *hdr; const struct qmi_message_hdr *msg; const char *service; const void *ptr; uint16_t offset; char strbuf[72 + 16], *str; bool pending_print = false; if (!function || !len) return; hdr = buf; str = strbuf; service = __service_type_to_string(hdr->service); if (service) str += sprintf(str, "%c %s", dir, service); else str += sprintf(str, "%c %d", dir, hdr->service); if (hdr->service == QMI_SERVICE_CONTROL) { const struct qmi_control_hdr *ctl; const char *type; ctl = buf + QMI_MUX_HDR_SIZE; msg = buf + QMI_MUX_HDR_SIZE + QMI_CONTROL_HDR_SIZE; ptr = buf + QMI_MUX_HDR_SIZE + QMI_CONTROL_HDR_SIZE + QMI_MESSAGE_HDR_SIZE; switch (ctl->type) { case 0x00: type = "_req"; break; case 0x01: type = "_resp"; break; case 0x02: type = "_ind"; break; default: type = ""; break; } str += sprintf(str, "%s msg=%d len=%d", type, GUINT16_FROM_LE(msg->message), GUINT16_FROM_LE(msg->length)); str += sprintf(str, " [client=%d,type=%d,tid=%d,len=%d]", hdr->client, ctl->type, ctl->transaction, GUINT16_FROM_LE(hdr->length)); } else { const struct qmi_service_hdr *srv; const char *type; srv = buf + QMI_MUX_HDR_SIZE; msg = buf + QMI_MUX_HDR_SIZE + QMI_SERVICE_HDR_SIZE; ptr = buf + QMI_MUX_HDR_SIZE + QMI_SERVICE_HDR_SIZE + QMI_MESSAGE_HDR_SIZE; switch (srv->type) { case 0x00: type = "_req"; break; case 0x02: type = "_resp"; break; case 0x04: type = "_ind"; break; default: type = ""; break; } str += sprintf(str, "%s msg=%d len=%d", type, GUINT16_FROM_LE(msg->message), GUINT16_FROM_LE(msg->length)); str += sprintf(str, " [client=%d,type=%d,tid=%d,len=%d]", hdr->client, srv->type, GUINT16_FROM_LE(srv->transaction), GUINT16_FROM_LE(hdr->length)); } function(strbuf, user_data); if (!msg->length) return; str = strbuf; str += sprintf(str, " "); offset = 0; while (offset + QMI_TLV_HDR_SIZE < GUINT16_FROM_LE(msg->length)) { const struct qmi_tlv_hdr *tlv = ptr + offset; uint16_t tlv_length = GUINT16_FROM_LE(tlv->length); if (tlv->type == 0x02 && tlv_length == QMI_RESULT_CODE_SIZE) { const struct qmi_result_code *result = ptr + offset + QMI_TLV_HDR_SIZE; uint16_t error = GUINT16_FROM_LE(result->error); const char *error_str; error_str = __error_to_string(error); if (error_str) str += sprintf(str, " {type=%d,error=%s}", tlv->type, error_str); else str += sprintf(str, " {type=%d,error=%d}", tlv->type, error); } else { str += sprintf(str, " {type=%d,len=%d}", tlv->type, tlv_length); } if (str - strbuf > 60) { function(strbuf, user_data); str = strbuf; str += sprintf(str, " "); pending_print = false; } else pending_print = true; offset += QMI_TLV_HDR_SIZE + tlv_length; } if (pending_print) function(strbuf, user_data); } static void __debug_device(struct qmi_device *device, const char *format, ...) { char strbuf[72 + 16]; va_list ap; if (!device->debug_func) return; va_start(ap, format); vsnprintf(strbuf, sizeof(strbuf), format, ap); va_end(ap); device->debug_func(strbuf, device->debug_data); } static gboolean can_write_data(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct qmi_device *device = user_data; struct qmi_mux_hdr *hdr; struct qmi_request *req; ssize_t bytes_written; req = g_queue_pop_head(device->req_queue); if (!req) return FALSE; bytes_written = write(device->fd, req->buf, req->len); if (bytes_written < 0) return FALSE; __hexdump('>', req->buf, bytes_written, device->debug_func, device->debug_data); __debug_msg(' ', req->buf, bytes_written, device->debug_func, device->debug_data); hdr = req->buf; if (hdr->service == QMI_SERVICE_CONTROL) g_queue_push_tail(device->control_queue, req); else g_queue_push_tail(device->service_queue, req); g_free(req->buf); req->buf = NULL; if (g_queue_get_length(device->req_queue) > 0) return TRUE; return FALSE; } static void write_watch_destroy(gpointer user_data) { struct qmi_device *device = user_data; device->write_watch = 0; } static void wakeup_writer(struct qmi_device *device) { if (device->write_watch > 0) return; device->write_watch = g_io_add_watch_full(device->io, G_PRIORITY_HIGH, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, can_write_data, device, write_watch_destroy); } static void __request_submit(struct qmi_device *device, struct qmi_request *req, uint16_t transaction) { req->tid = transaction; g_queue_push_tail(device->req_queue, req); wakeup_writer(device); } static void service_notify(gpointer key, gpointer value, gpointer user_data) { struct qmi_service *service = value; struct qmi_result *result = user_data; GList *list; for (list = g_list_first(service->notify_list); list; list = g_list_next(list)) { struct qmi_notify *notify = list->data; if (notify->message == result->message) notify->callback(result, notify->user_data); } } static void handle_indication(struct qmi_device *device, uint8_t service_type, uint8_t client_id, uint16_t message, uint16_t length, const void *data) { struct qmi_service *service; struct qmi_result result; unsigned int hash_id; if (service_type == QMI_SERVICE_CONTROL) return; result.result = 0; result.error = 0; result.message = message; result.data = data; result.length = length; if (client_id == 0xff) { g_hash_table_foreach(device->service_list, service_notify, &result); return; } hash_id = service_type | (client_id << 8); service = g_hash_table_lookup(device->service_list, GUINT_TO_POINTER(hash_id)); if (!service) return; service_notify(NULL, service, &result); } static void handle_packet(struct qmi_device *device, const struct qmi_mux_hdr *hdr, const void *buf) { struct qmi_request *req; uint16_t message, length; const void *data; if (hdr->service == QMI_SERVICE_CONTROL) { const struct qmi_control_hdr *control = buf; const struct qmi_message_hdr *msg; unsigned int tid; GList *list; /* Ignore control messages with client identifier */ if (hdr->client != 0x00) return; msg = buf + QMI_CONTROL_HDR_SIZE; message = GUINT16_FROM_LE(msg->message); length = GUINT16_FROM_LE(msg->length); data = buf + QMI_CONTROL_HDR_SIZE + QMI_MESSAGE_HDR_SIZE; tid = control->transaction; if (control->type == 0x02 && control->transaction == 0x00) { handle_indication(device, hdr->service, hdr->client, message, length, data); return; } list = g_queue_find_custom(device->control_queue, GUINT_TO_POINTER(tid), __request_compare); if (!list) return; req = list->data; g_queue_delete_link(device->control_queue, list); } else { const struct qmi_service_hdr *service = buf; const struct qmi_message_hdr *msg; unsigned int tid; GList *list; msg = buf + QMI_SERVICE_HDR_SIZE; message = GUINT16_FROM_LE(msg->message); length = GUINT16_FROM_LE(msg->length); data = buf + QMI_SERVICE_HDR_SIZE + QMI_MESSAGE_HDR_SIZE; tid = GUINT16_FROM_LE(service->transaction); if (service->type == 0x04 && tid == 0x0000) { handle_indication(device, hdr->service, hdr->client, message, length, data); return; } list = g_queue_find_custom(device->service_queue, GUINT_TO_POINTER(tid), __request_compare); if (!list) return; req = list->data; g_queue_delete_link(device->service_queue, list); } if (req->callback) req->callback(message, length, data, req->user_data); __request_free(req, NULL); } static gboolean received_data(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct qmi_device *device = user_data; struct qmi_mux_hdr *hdr; unsigned char buf[2048]; ssize_t bytes_read; uint16_t offset; if (cond & G_IO_NVAL) return FALSE; bytes_read = read(device->fd, buf, sizeof(buf)); if (bytes_read < 0) return TRUE; __hexdump('<', buf, bytes_read, device->debug_func, device->debug_data); offset = 0; while (offset < bytes_read) { uint16_t len; /* Check if QMI mux header fits into packet */ if (bytes_read - offset < QMI_MUX_HDR_SIZE) break; hdr = (void *) (buf + offset); /* Check for fixed frame and flags value */ if (hdr->frame != 0x01 || hdr->flags != 0x80) break; len = GUINT16_FROM_LE(hdr->length) + 1; /* Check that packet size matches frame size */ if (bytes_read - offset < len) break; __debug_msg(' ', buf + offset, len, device->debug_func, device->debug_data); handle_packet(device, hdr, buf + offset + QMI_MUX_HDR_SIZE); offset += len; } return TRUE; } static void read_watch_destroy(gpointer user_data) { struct qmi_device *device = user_data; device->read_watch = 0; } static void service_destroy(gpointer data) { struct qmi_service *service = data; if (!service->device) return; service->device = NULL; } struct qmi_device *qmi_device_new(int fd) { struct qmi_device *device; long flags; device = g_try_new0(struct qmi_device, 1); if (!device) return NULL; __debug_device(device, "device %p new", device); device->ref_count = 1; device->fd = fd; device->close_on_unref = false; flags = fcntl(device->fd, F_GETFL, NULL); if (flags < 0) { g_free(device); return NULL; } if (!(flags & O_NONBLOCK)) { if (fcntl(device->fd, F_SETFL, flags | O_NONBLOCK) < 0) { g_free(device); return NULL; } } device->io = g_io_channel_unix_new(device->fd); g_io_channel_set_encoding(device->io, NULL, NULL); g_io_channel_set_buffered(device->io, FALSE); device->read_watch = g_io_add_watch_full(device->io, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, received_data, device, read_watch_destroy); g_io_channel_unref(device->io); device->req_queue = g_queue_new(); device->control_queue = g_queue_new(); device->service_queue = g_queue_new(); device->service_list = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, service_destroy); return device; } struct qmi_device *qmi_device_ref(struct qmi_device *device) { if (!device) return NULL; __sync_fetch_and_add(&device->ref_count, 1); return device; } void qmi_device_unref(struct qmi_device *device) { if (!device) return; if (__sync_sub_and_fetch(&device->ref_count, 1)) return; __debug_device(device, "device %p free", device); g_queue_foreach(device->control_queue, __request_free, NULL); g_queue_free(device->control_queue); g_queue_foreach(device->service_queue, __request_free, NULL); g_queue_free(device->service_queue); g_queue_foreach(device->req_queue, __request_free, NULL); g_queue_free(device->req_queue); if (device->write_watch > 0) g_source_remove(device->write_watch); if (device->read_watch > 0) g_source_remove(device->read_watch); if (device->close_on_unref) close(device->fd); g_hash_table_destroy(device->service_list); g_free(device->version_str); g_free(device->version_list); g_free(device); } void qmi_device_set_debug(struct qmi_device *device, qmi_debug_func_t func, void *user_data) { if (device == NULL) return; device->debug_func = func; device->debug_data = user_data; } void qmi_device_set_close_on_unref(struct qmi_device *device, bool do_close) { if (!device) return; device->close_on_unref = do_close; } static const void *tlv_get(const void *data, uint16_t size, uint8_t type, uint16_t *length) { const void *ptr = data; uint16_t len = size; while (len > QMI_TLV_HDR_SIZE) { const struct qmi_tlv_hdr *tlv = ptr; uint16_t tlv_length = GUINT16_FROM_LE(tlv->length); if (tlv->type == type) { if (length) *length = tlv_length; return ptr + QMI_TLV_HDR_SIZE; } ptr += QMI_TLV_HDR_SIZE + tlv_length; len -= QMI_TLV_HDR_SIZE + tlv_length; } return NULL; } struct discover_data { struct qmi_device *device; qmi_discover_func_t func; void *user_data; qmi_destroy_func_t destroy; guint timeout; }; static void discover_callback(uint16_t message, uint16_t length, const void *buffer, void *user_data) { struct discover_data *data = user_data; struct qmi_device *device = data->device; const struct qmi_result_code *result_code; const struct qmi_service_list *service_list; const void *ptr; uint16_t len; struct qmi_version *list; uint8_t count; unsigned int i; g_source_remove(data->timeout); count = 0; list = NULL; result_code = tlv_get(buffer, length, 0x02, &len); if (!result_code) goto done; if (len != QMI_RESULT_CODE_SIZE) goto done; service_list = tlv_get(buffer, length, 0x01, &len); if (!service_list) goto done; if (len < QMI_SERVICE_LIST_SIZE) goto done; list = g_try_malloc(sizeof(struct qmi_version) * service_list->count); if (!list) goto done; for (i = 0; i < service_list->count; i++) { uint16_t major = GUINT16_FROM_LE(service_list->services[i].major); uint16_t minor = GUINT16_FROM_LE(service_list->services[i].minor); uint8_t type = service_list->services[i].type; const char *name = __service_type_to_string(type); if (type == QMI_SERVICE_CONTROL) { device->control_major = major; device->control_minor = minor; continue; } list[count].type = type; list[count].major = major; list[count].minor = minor; 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); if (!ptr) goto done; 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; if (data->func) data->func(count, list, data->user_data); if (data->destroy) data->destroy(data->user_data); g_free(data); } static gboolean discover_reply(gpointer user_data) { struct discover_data *data = user_data; struct qmi_device *device = data->device; data->timeout = 0; 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); return FALSE; } bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func, void *user_data, qmi_destroy_func_t destroy) { struct discover_data *data; struct qmi_request *req; struct qmi_control_hdr *hdr; if (!device) return false; __debug_device(device, "device %p discover", device); data = g_try_new0(struct discover_data, 1); if (!data) return false; 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); return true; } req = __request_alloc(QMI_SERVICE_CONTROL, 0x00, QMI_CTL_GET_VERSION_INFO, QMI_CONTROL_HDR_SIZE, NULL, 0, discover_callback, data, (void **) &hdr); if (!req) { g_free(data); return false; } 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); data->timeout = g_timeout_add_seconds(5, discover_reply, data); return true; } static void release_client(struct qmi_device *device, uint8_t type, uint8_t client_id, qmi_message_func_t func, void *user_data) { unsigned char release_req[] = { 0x01, 0x02, 0x00, type, client_id }; struct qmi_request *req; struct qmi_control_hdr *hdr; req = __request_alloc(QMI_SERVICE_CONTROL, 0x00, QMI_CTL_RELEASE_CLIENT_ID, QMI_CONTROL_HDR_SIZE, release_req, sizeof(release_req), func, user_data, (void **) &hdr); if (!req) { func(0x0000, 0x0000, NULL, user_data); return; } 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); } 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) { struct shutdown_data *data = user_data; if (data->func) data->func(data->user_data); g_free(data); return FALSE; } static gboolean shutdown_timeout(gpointer user_data) { struct shutdown_data *data = user_data; struct qmi_device *device = data->device; if (device->release_users > 0) return TRUE; return shutdown_reply(data); } 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; __debug_device(device, "device %p shutdown", device); data = g_try_new0(struct shutdown_data, 1); if (!data) 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); return true; } struct qmi_param *qmi_param_new(void) { struct qmi_param *param; param = g_try_new0(struct qmi_param, 1); if (!param) return NULL; return param; } void qmi_param_free(struct qmi_param *param) { if (!param) return; g_free(param->data); g_free(param); } bool qmi_param_append(struct qmi_param *param, uint8_t type, uint16_t length, const void *data) { struct qmi_tlv_hdr *tlv; void *ptr; if (!param || !type) return false; if (!length) return true; if (!data) return false; if (param->data) ptr = g_try_realloc(param->data, param->length + QMI_TLV_HDR_SIZE + length); else ptr = g_try_malloc(QMI_TLV_HDR_SIZE + length); if (!ptr) return false; tlv = ptr + param->length; tlv->type = type; tlv->length = GUINT16_TO_LE(length); memcpy(tlv->value, data, length); param->data = ptr; param->length += QMI_TLV_HDR_SIZE + length; return true; } bool qmi_param_append_uint8(struct qmi_param *param, uint8_t type, uint8_t value) { unsigned char buf[1] = { value }; return qmi_param_append(param, type, sizeof(buf), buf); } bool qmi_param_append_uint16(struct qmi_param *param, uint8_t type, uint16_t value) { unsigned char buf[2] = { value & 0xff, (value & 0xff00) >> 8 }; return qmi_param_append(param, type, sizeof(buf), buf); } bool qmi_param_append_uint32(struct qmi_param *param, uint8_t type, uint32_t value) { unsigned char buf[4] = { value & 0xff, (value & 0xff00) >> 8, (value & 0xff0000) >> 16, (value & 0xff000000) >> 24 }; return qmi_param_append(param, type, sizeof(buf), buf); } struct qmi_param *qmi_param_new_uint8(uint8_t type, uint8_t value) { struct qmi_param *param; param = qmi_param_new(); if (!param) return NULL; if (!qmi_param_append_uint8(param, type, value)) { qmi_param_free(param); return NULL; } return param; } struct qmi_param *qmi_param_new_uint16(uint8_t type, uint16_t value) { struct qmi_param *param; param = qmi_param_new(); if (!param) return NULL; if (!qmi_param_append_uint16(param, type, value)) { qmi_param_free(param); return NULL; } return param; } struct qmi_param *qmi_param_new_uint32(uint8_t type, uint32_t value) { struct qmi_param *param; param = qmi_param_new(); if (!param) return NULL; if (!qmi_param_append_uint32(param, type, value)) { qmi_param_free(param); return NULL; } return param; } bool qmi_result_set_error(struct qmi_result *result, uint16_t *error) { if (!result) { if (error) *error = 0xffff; return true; } if (result->result == 0x0000) return false; if (error) *error = result->error; return true; } const char *qmi_result_get_error(struct qmi_result *result) { if (!result) return NULL; if (result->result == 0x0000) return NULL; return __error_to_string(result->error); } const void *qmi_result_get(struct qmi_result *result, uint8_t type, uint16_t *length) { if (!result || !type) return NULL; return tlv_get(result->data, result->length, type, length); } char *qmi_result_get_string(struct qmi_result *result, uint8_t type) { const void *ptr; uint16_t len; if (!result || !type) return NULL; ptr = tlv_get(result->data, result->length, type, &len); if (!ptr) return NULL; return strndup(ptr, len); } bool qmi_result_get_uint8(struct qmi_result *result, uint8_t type, uint8_t *value) { const unsigned char *ptr; uint16_t len; if (!result || !type) return false; ptr = tlv_get(result->data, result->length, type, &len); if (!ptr) return false; if (value) *value = *ptr; return true; } bool qmi_result_get_uint16(struct qmi_result *result, uint8_t type, uint16_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 = GUINT16_FROM_LE(tmp); return true; } bool qmi_result_get_uint32(struct qmi_result *result, uint8_t type, uint32_t *value) { const unsigned char *ptr; uint16_t len; uint32_t tmp; if (!result || !type) return false; ptr = tlv_get(result->data, result->length, type, &len); if (!ptr) return false; memcpy(&tmp, ptr, 4); if (value) *value = GUINT32_FROM_LE(tmp); return true; } bool qmi_result_get_uint64(struct qmi_result *result, uint8_t type, uint64_t *value) { const unsigned char *ptr; uint16_t len; uint64_t tmp; if (!result || !type) return false; ptr = tlv_get(result->data, result->length, type, &len); if (!ptr) return false; memcpy(&tmp, ptr, 8); if (value) *value = GUINT64_FROM_LE(tmp); return true; } struct service_create_data { struct qmi_device *device; bool shared; uint8_t type; uint16_t major; uint16_t minor; qmi_create_func_t func; void *user_data; qmi_destroy_func_t destroy; guint timeout; }; static gboolean service_create_reply(gpointer user_data) { struct service_create_data *data = user_data; data->func(NULL, data->user_data); if (data->destroy) data->destroy(data->user_data); g_free(data); return FALSE; } static void service_create_callback(uint16_t message, uint16_t length, const void *buffer, void *user_data) { struct service_create_data *data = user_data; struct qmi_device *device = data->device; struct qmi_service *service = NULL; const struct qmi_result_code *result_code; const struct qmi_client_id *client_id; 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; if (len != QMI_RESULT_CODE_SIZE) goto done; client_id = tlv_get(buffer, length, 0x01, &len); if (!client_id) goto done; if (len != QMI_CLIENT_ID_SIZE) goto done; if (client_id->service != data->type) goto done; service = g_try_new0(struct qmi_service, 1); if (!service) goto done; service->ref_count = 1; service->device = data->device; service->shared = data->shared; service->type = data->type; service->major = data->major; service->minor = data->minor; service->client_id = client_id->client; __debug_device(device, "service created [client=%d,type=%d]", service->client_id, service->type); hash_id = service->type | (service->client_id << 8); g_hash_table_replace(device->service_list, GUINT_TO_POINTER(hash_id), service); done: data->func(service, data->user_data); qmi_service_unref(service); if (data->destroy) data->destroy(data->user_data); g_free(data); } static void service_create_discover(uint8_t count, const struct qmi_version *list, void *user_data) { struct service_create_data *data = user_data; struct qmi_device *device = data->device; struct qmi_request *req; struct qmi_control_hdr *hdr; unsigned char client_req[] = { 0x01, 0x01, 0x00, data->type }; unsigned int i; __debug_device(device, "service create [type=%d]", data->type); for (i = 0; i < count; i++) { if (list[i].type == data->type) { data->major = list[i].major; data->minor = list[i].minor; break; } } req = __request_alloc(QMI_SERVICE_CONTROL, 0x00, QMI_CTL_GET_CLIENT_ID, QMI_CONTROL_HDR_SIZE, client_req, sizeof(client_req), service_create_callback, data, (void **) &hdr); if (!req) { if (data->timeout > 0) g_source_remove(data->timeout); g_timeout_add_seconds(0, service_create_reply, data); return; } 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); } static bool service_create(struct qmi_device *device, bool shared, uint8_t type, qmi_create_func_t func, void *user_data, qmi_destroy_func_t destroy) { struct service_create_data *data; data = g_try_new0(struct service_create_data, 1); if (!data) return false; data->device = device; data->shared = shared; data->type = type; data->func = func; data->user_data = user_data; data->destroy = destroy; if (device->version_list) { service_create_discover(device->version_count, device->version_list, data); goto done; } if (qmi_device_discover(device, service_create_discover, data, NULL)) goto done; g_free(data); return false; done: data->timeout = g_timeout_add_seconds(8, service_create_reply, data); return true; } bool qmi_service_create(struct qmi_device *device, uint8_t type, qmi_create_func_t func, void *user_data, qmi_destroy_func_t destroy) { if (!device || !func) return false; if (type == QMI_SERVICE_CONTROL) return false; return service_create(device, false, type, func, user_data, destroy); } struct service_create_shared_data { struct qmi_service *service; qmi_create_func_t func; void *user_data; qmi_destroy_func_t destroy; }; static gboolean service_create_shared_reply(gpointer user_data) { struct service_create_shared_data *data = user_data; data->func(data->service, data->user_data); qmi_service_unref(data->service); if (data->destroy) data->destroy(data->user_data); g_free(data); return FALSE; } bool qmi_service_create_shared(struct qmi_device *device, uint8_t type, qmi_create_func_t func, void *user_data, qmi_destroy_func_t destroy) { struct qmi_service *service; unsigned int type_val = type; if (!device || !func) return false; if (type == QMI_SERVICE_CONTROL) return false; service = g_hash_table_find(device->service_list, __service_compare_shared, GUINT_TO_POINTER(type_val)); if (service) { struct service_create_shared_data *data; data = g_try_new0(struct service_create_shared_data, 1); if (!data) return false; data->service = qmi_service_ref(service); data->func = func; data->user_data = user_data; data->destroy = destroy; g_timeout_add(0, service_create_shared_reply, data); return 0; } return service_create(device, true, type, func, user_data, destroy); } static void service_release_callback(uint16_t message, uint16_t length, const void *buffer, void *user_data) { struct qmi_service *service = user_data; if (service->device) service->device->release_users--; g_free(service); } struct qmi_service *qmi_service_ref(struct qmi_service *service) { if (!service) return NULL; __sync_fetch_and_add(&service->ref_count, 1); return service; } void qmi_service_unref(struct qmi_service *service) { unsigned int hash_id; if (!service) return; if (__sync_sub_and_fetch(&service->ref_count, 1)) return; if (!service->device) { g_free(service); return; } qmi_service_cancel_all(service); qmi_service_unregister_all(service); hash_id = service->type | (service->client_id << 8); g_hash_table_steal(service->device->service_list, GUINT_TO_POINTER(hash_id)); service->device->release_users++; release_client(service->device, service->type, service->client_id, service_release_callback, service); } const char *qmi_service_get_identifier(struct qmi_service *service) { if (!service) return NULL; return __service_type_to_string(service->type); } bool qmi_service_get_version(struct qmi_service *service, uint16_t *major, uint16_t *minor) { if (!service) return false; if (major) *major = service->major; if (minor) *minor = service->minor; return true; } struct service_send_data { struct qmi_service *service; struct qmi_param *param; qmi_result_func_t func; void *user_data; qmi_destroy_func_t destroy; }; static void service_send_free(struct service_send_data *data) { if (data->destroy) data->destroy(data->user_data); qmi_param_free(data->param); g_free(data); } static void service_send_callback(uint16_t message, uint16_t length, const void *buffer, void *user_data) { struct service_send_data *data = user_data; const struct qmi_result_code *result_code; uint16_t len; struct qmi_result result; result.message = message; result.data = buffer; result.length = length; result_code = tlv_get(buffer, length, 0x02, &len); if (!result_code) goto done; if (len != QMI_RESULT_CODE_SIZE) goto done; result.result = GUINT16_FROM_LE(result_code->result); result.error = GUINT16_FROM_LE(result_code->error); done: if (data->func) data->func(&result, data->user_data); service_send_free(data); } uint16_t qmi_service_send(struct qmi_service *service, uint16_t message, struct qmi_param *param, qmi_result_func_t func, void *user_data, qmi_destroy_func_t destroy) { struct qmi_device *device; struct service_send_data *data; struct qmi_request *req; struct qmi_service_hdr *hdr; if (!service) return 0; if (!service->client_id) return 0; device = service->device; if (!device) return 0; data = g_try_new0(struct service_send_data, 1); if (!data) return 0; data->service = service; data->param = param; data->func = func; data->user_data = user_data; data->destroy = destroy; req = __request_alloc(service->type, service->client_id, message, QMI_SERVICE_HDR_SIZE, data->param ? data->param->data : NULL, data->param ? data->param->length : 0, service_send_callback, data, (void **) &hdr); if (!req) { g_free(data); return 0; } if (device->next_service_tid < 256) device->next_service_tid = 256; hdr->type = 0x00; hdr->transaction = device->next_service_tid++; __request_submit(device, req, hdr->transaction); return hdr->transaction; } bool qmi_service_cancel(struct qmi_service *service, uint16_t id) { unsigned int tid = id; struct qmi_device *device; struct qmi_request *req; GList *list; if (!service || !tid) return false; if (!service->client_id) return false; device = service->device; if (!device) return false; 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->service_queue, GUINT_TO_POINTER(tid), __request_compare); if (!list) return false; req = list->data; g_queue_delete_link(device->service_queue, list); } service_send_free(req->user_data); __request_free(req, NULL); return true; } static GQueue *remove_client(GQueue *queue, uint8_t client) { GQueue *new_queue; GList *list; new_queue = g_queue_new(); while (1) { struct qmi_request *req; list = g_queue_pop_head_link(queue); if (!list) break; req = list->data; if (!req->client || req->client != client) { g_queue_push_tail_link(new_queue, list); continue; } service_send_free(req->user_data); __request_free(req, NULL); } g_queue_free(queue); return new_queue; } bool qmi_service_cancel_all(struct qmi_service *service) { struct qmi_device *device; if (!service) return false; if (!service->client_id) return false; device = service->device; if (!device) return false; device->req_queue = remove_client(device->req_queue, service->client_id); device->service_queue = remove_client(device->service_queue, service->client_id); return true; } uint16_t qmi_service_register(struct qmi_service *service, uint16_t message, qmi_result_func_t func, void *user_data, qmi_destroy_func_t destroy) { struct qmi_notify *notify; if (!service || !func) return 0; notify = g_try_new0(struct qmi_notify, 1); if (!notify) return 0; if (service->next_notify_id < 1) service->next_notify_id = 1; notify->id = service->next_notify_id++; notify->message = message; notify->callback = func; notify->user_data = user_data; notify->destroy = destroy; service->notify_list = g_list_append(service->notify_list, notify); return notify->id; } bool qmi_service_unregister(struct qmi_service *service, uint16_t id) { unsigned int nid = id; struct qmi_notify *notify; GList *list; if (!service || !id) return false; list = g_list_find_custom(service->notify_list, GUINT_TO_POINTER(nid), __notify_compare); if (!list) return false; notify = list->data; service->notify_list = g_list_delete_link(service->notify_list, list); __notify_free(notify, NULL); return true; } bool qmi_service_unregister_all(struct qmi_service *service) { if (!service) return false; g_list_foreach(service->notify_list, __notify_free, NULL); g_list_free(service->notify_list); service->notify_list = NULL; return true; } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/dms.h0000644000015600001650000001252512671500024022626 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 QMI_DMS_RESET 0 /* Reset DMS service */ #define QMI_DMS_EVENT 1 /* Event report indication */ #define QMI_DMS_SET_EVENT 1 /* Set report conditions */ #define QMI_DMS_GET_CAPS 32 /* Get device capabilities */ #define QMI_DMS_GET_MANUFACTURER 33 /* Get device manfacturer */ #define QMI_DMS_GET_MODEL_ID 34 /* Get device model ID */ #define QMI_DMS_GET_REV_ID 35 /* Get device revision ID */ #define QMI_DMS_GET_NUMBER 36 /* Get assigned voice number */ #define QMI_DMS_GET_IDS 37 /* Get ESN/IMEI/MEID */ #define QMI_DMS_GET_POWER_STATE 38 /* Get power state */ #define QMI_DMS_SET_PIN_PROTECT 39 /* Set PIN protection */ #define QMI_DMS_PIN_VERIFY 40 /* Verify PIN */ #define QMI_DMS_PIN_UNBLOCK 41 /* Unblock PIN */ #define QMI_DMS_PIN_CHANGE 42 /* Change PIN */ #define QMI_DMS_GET_PIN_STATUS 43 /* Get PIN status */ #define QMI_DMS_GET_MSM_ID 44 /* Get MSM ID */ #define QMI_DMS_GET_OPER_MODE 45 /* Get operating mode */ #define QMI_DMS_SET_OPER_MODE 46 /* Set operating mode */ #define QMI_DMS_GET_TIME 47 /* Get timestamp from the device */ #define QMI_DMS_GET_PRL_VERSION 48 /* Get PRL version */ #define QMI_DMS_GET_ICCID 60 /* Get UIM ICCID */ #define QMI_DMS_GET_IMSI 67 /* Get IMSI */ #define QMI_DMS_GET_UIM_STATE 68 /* Get UIM state */ #define QMI_DMS_GET_BAND_CAPS 69 /* Get device band capabilities */ #define QMI_DMS_GET_FACTORY_ID 70 /* Get device factory ID */ #define QMI_DMS_GET_SW_VERSION 81 /* Get software version */ /* Report indication */ #define QMI_DMS_NOTIFY_PIN1_STATE 0x11 #define QMI_DMS_NOTIFY_PIN2_STATE 0x12 #define QMI_DMS_NOTIFY_OPER_MODE 0x14 /* uint8 */ #define QMI_DMS_NOTIFY_UIM_STATE 0x15 /* uint8 */ #define QMI_DMS_UIM_STATE_INIT_COMPLETE 0x00 #define QMI_DMS_UIM_STATE_INIT_FAILED 0x01 #define QMI_DMS_UIM_STATE_NOT_PRESENT 0x02 #define QMI_DMS_UIM_STATE_INVALID 0xff #define QMI_DMS_OPER_MODE_ONLINE 0x00 #define QMI_DMS_OPER_MODE_LOW_POWER 0x01 #define QMI_DMS_OPER_MODE_FACTORY_TEST 0x02 #define QMI_DMS_OPER_MODE_OFFLINE 0x03 #define QMI_DMS_OPER_MODE_RESET 0x04 #define QMI_DMS_OPER_MODE_SHUTDOWN 0x05 #define QMI_DMS_OPER_MODE_PERSIST_LOW_POWER 0x06 #define QMI_DMS_OPER_MODE_ONLY_LOW_POWER 0x07 /* Set report conditions */ #define QMI_DMS_PARAM_REPORT_PIN_STATUS 0x12 /* bool */ #define QMI_DMS_PARAM_REPORT_OPER_MODE 0x14 /* bool */ #define QMI_DMS_PARAM_REPORT_UIM_STATE 0x15 /* bool */ /* Get device capabilities */ #define QMI_DMS_RESULT_DEVICE_CAPS 0x01 struct qmi_dms_device_caps { uint32_t max_tx_rate; uint32_t max_rx_rate; uint8_t data_capa; int8_t sim_supported; uint8_t radio_if_count; uint8_t radio_if[0]; } __attribute__ ((packed)); #define QMI_DMS_DATA_CAPA_NOT_SUPPORTED 0x00 #define QMI_DMS_DATA_CAPA_CS_ONLY 0x01 #define QMI_DMS_DATA_CAPA_PS_ONLY 0x02 #define QMI_DMS_DATA_CAPA_SIMUL_CS_PS 0x03 #define QMI_DMS_DATA_CAPA_NONSIMUL_CS_PS 0x04 #define QMI_DMS_RADIO_IF_CDMA2000_1X 0x01 #define QMI_DMS_RADIO_IF_CDMA2000_HRPD 0x02 #define QMI_DMS_RADIO_IF_GSM 0x04 #define QMI_DMS_RADIO_IF_UMTS 0x05 #define QMI_DMS_RADIO_IF_LTE 0x08 /* Get device manfacturer */ #define QMI_DMS_RESULT_MANUFACTURER 0x01 /* Get device model ID */ #define QMI_DMS_RESULT_MODEL_ID 0x01 /* Get device revision ID */ #define QMI_DMS_RESULT_REV_ID 0x01 /* Get assigned voice number */ #define QMI_DMS_RESULT_VOICE_NUMBER 0x01 #define QMI_DMS_RESULT_MOBILE_ID 0x10 #define QMI_DMS_RESULT_IMSI_NUMBER 0x11 /* Get ESN/IMEI/MEID */ #define QMI_DMS_RESULT_ESN 0x10 /* optional */ #define QMI_DMS_RESULT_IMEI 0x11 /* optional */ #define QMI_DMS_RESULT_MEID 0x12 /* optional */ /* Get PIN status */ #define QMI_DMS_RESULT_PIN1_STATUS 0x11 #define QMI_DMS_RESULT_PIN2_STATUS 0x12 struct qmi_dms_pin_status { uint8_t status; uint8_t verify_retries; uint8_t unblock_retries; } __attribute__ ((packed)); #define QMI_DMS_PIN_UNINITIALIZED 0x00 #define QMI_DMS_PIN_ENABLED_UNVERIFIED 0x01 #define QMI_DMS_PIN_ENABLED_VERIFIED 0x02 #define QMI_DMS_PIN_DISABLED 0x03 #define QMI_DMS_PIN_BLOCKED 0x04 #define QMI_DMS_PIN_BLOCKED_PERMANENTLY 0x05 #define QMI_DMS_PIN_UNBLOCKED 0x06 #define QMI_DMS_PIN_CHANGED 0x07 /* Get operating mode */ #define QMI_DMS_RESULT_OPER_MODE 0x01 /* uint8 */ /* Set operating mode */ #define QMI_DMS_PARAM_OPER_MODE 0x01 /* uint8 */ /* Get UIM ICCID */ #define QMI_DMS_RESULT_ICCID 0x01 /* string */ /* Get IMSI */ #define QMI_DMS_RESULT_IMSI 0x01 /* string */ /* Get UIM state */ #define QMI_DMS_RESULT_UIM_STATE 0x01 /* uint8 */ /* Get device band capabilities */ #define QMI_DMS_RESULT_BAND_CAPS 0x01 /* uint64 bitmask */ #define QMI_DMS_RESULT_LTE_BAND_CAPS 0x10 /* uint64 bitmask */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/sms.c0000644000015600001650000002755212671500024022646 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #include #include #include #include #include #include "qmi.h" #include "wms.h" #include "qmimodem.h" struct sms_data { struct qmi_service *wms; uint16_t major; uint16_t minor; }; static void get_smsc_addr_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_sms_sca_query_cb_t cb = cbd->cb; struct ofono_phone_number sca; const struct qmi_wms_result_smsc_addr *smsc; uint16_t len; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); return; } smsc = qmi_result_get(result, QMI_WMS_RESULT_SMSC_ADDR, &len); if (!smsc) { CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); return; } if (!smsc->addr_len) { CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); return; } if (smsc->addr[0] == '+') { strncpy(sca.number, smsc->addr + 1, smsc->addr_len - 1); sca.number[smsc->addr_len - 1] = '\0'; sca.type = 145; } else { strncpy(sca.number, smsc->addr, smsc->addr_len); sca.number[smsc->addr_len] = '\0'; sca.type = 129; } CALLBACK_WITH_SUCCESS(cb, &sca, cbd->data); } static void qmi_sca_query(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); if (qmi_service_send(data->wms, QMI_WMS_GET_SMSC_ADDR, NULL, get_smsc_addr_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); g_free(cbd); } static void set_smsc_addr_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_sms_sca_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_sca_set(struct ofono_sms *sms, const struct ofono_phone_number *sca, ofono_sms_sca_set_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); char type[4], number[OFONO_MAX_PHONE_NUMBER_LENGTH + 2]; struct qmi_param *param; DBG("type %d name %s", sca->type, sca->number); switch (sca->type) { case 129: snprintf(number, sizeof(number), "%s", sca->number); break; case 145: snprintf(number, sizeof(number), "+%s", sca->number); break; default: goto error; } snprintf(type, sizeof(type), "%d", sca->type); param = qmi_param_new(); if (!param) goto error; qmi_param_append(param, QMI_WMS_PARAM_SMSC_ADDR, strlen(number), number); qmi_param_append(param, QMI_WMS_PARAM_SMSC_ADDR_TYPE, strlen(type), type); if (qmi_service_send(data->wms, QMI_WMS_SET_SMSC_ADDR, param, set_smsc_addr_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void raw_send_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_sms_submit_cb_t cb = cbd->cb; uint16_t msgid; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } if (!qmi_result_get_uint16(result, QMI_WMS_RESULT_MESSAGE_ID, &msgid)) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } CALLBACK_WITH_SUCCESS(cb, msgid, cbd->data); } static void qmi_submit(struct ofono_sms *sms, const unsigned char *pdu, int pdu_len, int tpdu_len, int mms, ofono_sms_submit_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); struct qmi_wms_param_message *message; struct qmi_param *param; DBG("pdu_len %d tpdu_len %d mms %d", pdu_len, tpdu_len, mms); message = alloca(3 + pdu_len); message->msg_format = 0x06; message->msg_length = GUINT16_TO_LE(pdu_len); memcpy(message->msg_data, pdu, pdu_len); param = qmi_param_new(); if (!param) goto error; qmi_param_append(param, QMI_WMS_PARAM_MESSAGE, 3 + pdu_len, message); if (qmi_service_send(data->wms, QMI_WMS_RAW_SEND, param, raw_send_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); } static int domain_to_bearer(uint8_t domain) { switch (domain) { case QMI_WMS_DOMAIN_CS_PREFERRED: return 3; case QMI_WMS_DOMAIN_PS_PREFERRED: return 2; case QMI_WMS_DOMAIN_CS_ONLY: return 1; case QMI_WMS_DOMAIN_PS_ONLY: return 0; } return -1; } static uint8_t bearer_to_domain(int bearer) { switch (bearer) { case 0: return QMI_WMS_DOMAIN_PS_ONLY; case 1: return QMI_WMS_DOMAIN_CS_ONLY; case 2: return QMI_WMS_DOMAIN_PS_PREFERRED; case 3: return QMI_WMS_DOMAIN_CS_PREFERRED; } return QMI_WMS_DOMAIN_CS_PREFERRED; } static void get_domain_pref_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_sms_bearer_query_cb_t cb = cbd->cb; uint8_t domain; int bearer; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } if (!qmi_result_get_uint8(result, QMI_WMS_RESULT_DOMAIN, &domain)) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } bearer = domain_to_bearer(domain); CALLBACK_WITH_SUCCESS(cb, bearer, cbd->data); } static void qmi_bearer_query(struct ofono_sms *sms, ofono_sms_bearer_query_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); if (data->major < 1 && data->minor < 2) goto error; if (qmi_service_send(data->wms, QMI_WMS_GET_DOMAIN_PREF, NULL, get_domain_pref_cb, cbd, g_free) > 0) return; error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); } static void set_domain_pref_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_sms_bearer_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_bearer_set(struct ofono_sms *sms, int bearer, ofono_sms_bearer_set_cb_t cb, void *user_data) { struct sms_data *data = ofono_sms_get_data(sms); struct cb_data *cbd = cb_data_new(cb, user_data); struct qmi_param *param; uint8_t domain; DBG("bearer %d", bearer); if (data->major < 1 && data->minor < 2) goto error; domain = bearer_to_domain(bearer); param = qmi_param_new_uint8(QMI_WMS_PARAM_DOMAIN, domain); if (!param) goto error; if (qmi_service_send(data->wms, QMI_WMS_SET_DOMAIN_PREF, param, set_domain_pref_cb, cbd, g_free) > 0) return; error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void event_notify(struct qmi_result *result, void *user_data) { struct ofono_sms *sms = user_data; const struct qmi_wms_result_new_msg_notify *notify; const struct qmi_wms_result_message *message; uint16_t len; DBG(""); notify = qmi_result_get(result, QMI_WMS_RESULT_NEW_MSG_NOTIFY, &len); if (notify) { DBG("storage type %d index %d", notify->storage_type, GUINT32_FROM_LE(notify->storage_index)); } message = qmi_result_get(result, QMI_WMS_RESULT_MESSAGE, &len); if (message) { uint16_t plen; plen = GUINT16_FROM_LE(message->msg_length); DBG("ack_required %d transaction id %u", message->ack_required, GUINT32_FROM_LE(message->transaction_id)); DBG("msg format %d PDU length %d", message->msg_format, plen); ofono_sms_deliver_notify(sms, message->msg_data, plen, plen); } } static void set_routes_cb(struct qmi_result *result, void *user_data) { struct ofono_sms *sms = user_data; DBG(""); ofono_sms_register(sms); } static void get_routes_cb(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_route_list *list; struct qmi_wms_route_list *new_list; struct qmi_param *param; uint16_t len, num, i; uint8_t value; DBG(""); if (qmi_result_set_error(result, NULL)) goto done; list = qmi_result_get(result, QMI_WMS_RESULT_ROUTE_LIST, &len); if (!list) goto done; num = GUINT16_FROM_LE(list->count); DBG("found %d routes", num); for (i = 0; i < num; i++) DBG("type %d class %d => type %d value %d", list->route[i].msg_type, list->route[i].msg_class, list->route[i].storage_type, list->route[i].action); if (qmi_result_get_uint8(result, QMI_WMS_RESULT_STATUS_REPORT, &value)) DBG("transfer status report %d", value); len = 2 + (1 * 4); new_list = alloca(len); 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].action = QMI_WMS_ACTION_TRANSFER_AND_ACK; param = qmi_param_new(); if (!param) goto done; qmi_param_append(param, QMI_WMS_PARAM_ROUTE_LIST, len, new_list); qmi_param_append_uint8(param, QMI_WMS_PARAM_STATUS_REPORT, 0x01); if (qmi_service_send(data->wms, QMI_WMS_SET_ROUTES, param, set_routes_cb, sms, NULL) > 0) return; qmi_param_free(param); done: ofono_sms_register(sms); } static void set_event_cb(struct qmi_result *result, void *user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); DBG(""); if (qmi_service_send(data->wms, QMI_WMS_GET_ROUTES, NULL, get_routes_cb, sms, NULL) > 0) return; ofono_sms_register(sms); } static void create_wms_cb(struct qmi_service *service, void *user_data) { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); struct qmi_param *param; DBG(""); if (!service) { ofono_error("Failed to request WMS service"); ofono_sms_remove(sms); return; } if (!qmi_service_get_version(service, &data->major, &data->minor)) { ofono_error("Failed to get WMS service version"); ofono_sms_remove(sms); return; } data->wms = qmi_service_ref(service); qmi_service_register(data->wms, QMI_WMS_EVENT, event_notify, sms, NULL); param = qmi_param_new_uint8(QMI_WMS_PARAM_NEW_MSG_REPORT, 0x01); if (!param) goto done; if (qmi_service_send(data->wms, QMI_WMS_SET_EVENT, param, set_event_cb, sms, NULL) > 0) return; done: ofono_sms_register(sms); } static int qmi_sms_probe(struct ofono_sms *sms, unsigned int vendor, void *user_data) { struct qmi_device *device = user_data; struct sms_data *data; DBG(""); data = g_new0(struct sms_data, 1); ofono_sms_set_data(sms, data); qmi_service_create(device, QMI_SERVICE_WMS, create_wms_cb, sms, NULL); return 0; } static void qmi_sms_remove(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); DBG(""); ofono_sms_set_data(sms, NULL); qmi_service_unregister_all(data->wms); qmi_service_unref(data->wms); g_free(data); } static struct ofono_sms_driver driver = { .name = "qmimodem", .probe = qmi_sms_probe, .remove = qmi_sms_remove, .sca_query = qmi_sca_query, .sca_set = qmi_sca_set, .submit = qmi_submit, .bearer_query = qmi_bearer_query, .bearer_set = qmi_bearer_set, }; void qmi_sms_init(void) { ofono_sms_driver_register(&driver); } void qmi_sms_exit(void) { ofono_sms_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/qmimodem.h0000644000015600001650000000313512671500024023650 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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" extern void qmi_devinfo_init(void); extern void qmi_devinfo_exit(void); extern void qmi_netreg_init(void); extern void qmi_netreg_exit(void); extern void qmi_voicecall_init(void); extern void qmi_voicecall_exit(void); extern void qmi_sim_legacy_init(void); extern void qmi_sim_legacy_exit(void); extern void qmi_sim_init(void); extern void qmi_sim_exit(void); extern void qmi_sms_init(void); extern void qmi_sms_exit(void); extern void qmi_ussd_init(void); extern void qmi_ussd_exit(void); extern void qmi_gprs_init(void); extern void qmi_gprs_exit(void); extern void qmi_gprs_context_init(void); extern void qmi_gprs_context_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); ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/voicecall.c0000644000015600001650000000472212671500024023777 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #include #include #include #include "qmi.h" #include "qmimodem.h" struct voicecall_data { struct qmi_service *voice; uint16_t major; uint16_t minor; }; static void create_voice_cb(struct qmi_service *service, void *user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *data = ofono_voicecall_get_data(vc); DBG(""); if (!service) { ofono_error("Failed to request Voice service"); ofono_voicecall_remove(vc); return; } if (!qmi_service_get_version(service, &data->major, &data->minor)) { ofono_error("Failed to get Voice service version"); ofono_voicecall_remove(vc); return; } data->voice = qmi_service_ref(service); ofono_voicecall_register(vc); } static int qmi_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, void *user_data) { struct qmi_device *device = user_data; struct voicecall_data *data; DBG(""); data = g_new0(struct voicecall_data, 1); ofono_voicecall_set_data(vc, data); qmi_service_create(device, QMI_SERVICE_VOICE, create_voice_cb, vc, NULL); return 0; } static void qmi_voicecall_remove(struct ofono_voicecall *vc) { struct voicecall_data *data = ofono_voicecall_get_data(vc); DBG(""); ofono_voicecall_set_data(vc, NULL); qmi_service_unregister_all(data->voice); qmi_service_unref(data->voice); g_free(data); } static struct ofono_voicecall_driver driver = { .name = "qmimodem", .probe = qmi_voicecall_probe, .remove = qmi_voicecall_remove, }; void qmi_voicecall_init(void) { ofono_voicecall_driver_register(&driver); } void qmi_voicecall_exit(void) { ofono_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/ctl.h0000644000015600001650000000401312671500024022616 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 QMI_CTL_SET_INSTANCE_ID 32 /* Set the unique link instance ID */ #define QMI_CTL_GET_VERSION_INFO 33 /* Get supported service version info */ #define QMI_CTL_GET_CLIENT_ID 34 /* Get a unique client ID */ #define QMI_CTL_RELEASE_CLIENT_ID 35 /* Release the unique client ID */ #define QMI_CTL_REVOKE_CLIENT_ID 36 /* Indication of client ID revocation */ #define QMI_CTL_INVALID_CLIENT_ID 37 /* Indication of invalid client ID */ #define QMI_CTL_SET_DATA_FORMAT 38 /* Set host driver data format */ #define QMI_CTL_SYNC 39 /* Synchronize client/server */ #define QMI_CTL_SET_EVENT 40 /* Set event report conditions */ #define QMI_CTL_SET_POWER_SAVE_CONFIG 41 /* Set power save config */ #define QMI_CTL_SET_POWER_SAVE_MODE 42 /* Set power save mode */ #define QMI_CTL_GET_POWER_SAVE_MODE 43 /* Get power save mode */ struct qmi_result_code { uint16_t result; uint16_t error; } __attribute__ ((packed)); #define QMI_RESULT_CODE_SIZE 4 struct qmi_service_list { uint8_t count; struct { uint8_t type; uint16_t major; uint16_t minor; } __attribute__((__packed__)) services[0]; } __attribute__((__packed__)); #define QMI_SERVICE_LIST_SIZE 1 struct qmi_client_id { uint8_t service; uint8_t client; } __attribute__ ((packed)); #define QMI_CLIENT_ID_SIZE 2 ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/common.h0000644000015600001650000001054112671500024023327 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 QMI_BAND_MASK_CLASS_0A 0x0000000000000001 #define QMI_BAND_MASK_CLASS_0B 0x0000000000000002 #define QMI_BAND_MASK_CLASS_1 0x0000000000000004 #define QMI_BAND_MASK_CLASS_2 0x0000000000000008 #define QMI_BAND_MASK_CLASS_3A 0x0000000000000010 #define QMI_BAND_MASK_CLASS_4 0x0000000000000020 #define QMI_BAND_MASK_CLASS_5 0x0000000000000040 #define QMI_BAND_MASK_GSM_DCS 0x0000000000000080 #define QMI_BAND_MASK_GSM_EXTENDED 0x0000000000000100 #define QMI_BAND_MASK_GSM_PRIMARY 0x0000000000000200 #define QMI_BAND_MASK_CLASS_6 0x0000000000000400 #define QMI_BAND_MASK_CLASS_7 0x0000000000000800 #define QMI_BAND_MASK_CLASS_8 0x0000000000001000 #define QMI_BAND_MASK_CLASS_9 0x0000000000002000 #define QMI_BAND_MASK_CLASS_10 0x0000000000004000 #define QMI_BAND_MASK_CLASS_11 0x0000000000008000 #define QMI_BAND_MASK_GSM_450 0x0000000000010000 #define QMI_BAND_MASK_GSM_480 0x0000000000020000 #define QMI_BAND_MASK_GSM_750 0x0000000000040000 #define QMI_BAND_MASK_GSM_850 0x0000000000080000 #define QMI_BAND_MASK_GSM_RAILWAYS 0x0000000000100000 #define QMI_BAND_MASK_GSM_PCS 0x0000000000200000 #define QMI_BAND_MASK_WCDMA_2100 0x0000000000400000 #define QMI_BAND_MASK_WCDMA_PCS 0x0000000000800000 #define QMI_BAND_MASK_WCDMA_DCS 0x0000000001000000 #define QMI_BAND_MASK_WCDMA_1700_US 0x0000000002000000 #define QMI_BAND_MASK_WCDMA_850 0x0000000004000000 #define QMI_BAND_MASK_WCDMA_800 0x0000000008000000 #define QMI_BAND_MASK_CLASS_12 0x0000000010000000 #define QMI_BAND_MASK_CLASS_14 0x0000000020000000 #define QMI_BAND_MASK_CLASS_15 0x0000000080000000 #define QMI_BAND_MASK_WCDMA_2600 0x0001000000000000 #define QMI_BAND_MASK_WCDMA_900 0x0002000000000000 #define QMI_BAND_MASK_WCDMA_1700_JP 0x0004000000000000 #define QMI_BAND_MASK_CLASS_16 0x0100000000000000 #define QMI_BAND_MASK_CLASS_17 0x0200000000000000 #define QMI_BAND_MASK_CLASS_18 0x0400000000000000 #define QMI_BAND_MASK_CLASS_19 0x0800000000000000 #define QMI_LTE_BAND_MASK_EUTRA_1 0x0000000000000001 #define QMI_LTE_BAND_MASK_EUTRA_2 0x0000000000000002 #define QMI_LTE_BAND_MASK_EUTRA_3 0x0000000000000004 #define QMI_LTE_BAND_MASK_EUTRA_4 0x0000000000000008 #define QMI_LTE_BAND_MASK_EUTRA_5 0x0000000000000010 #define QMI_LTE_BAND_MASK_EUTRA_6 0x0000000000000020 #define QMI_LTE_BAND_MASK_EUTRA_7 0x0000000000000040 #define QMI_LTE_BAND_MASK_EUTRA_8 0x0000000000000080 #define QMI_LTE_BAND_MASK_EUTRA_9 0x0000000000000100 #define QMI_LTE_BAND_MASK_EUTRA_10 0x0000000000000200 #define QMI_LTE_BAND_MASK_EUTRA_11 0x0000000000000400 #define QMI_LTE_BAND_MASK_EUTRA_12 0x0000000000000800 #define QMI_LTE_BAND_MASK_EUTRA_13 0x0000000000001000 #define QMI_LTE_BAND_MASK_EUTRA_14 0x0000000000002000 #define QMI_LTE_BAND_MASK_EUTRA_17 0x0000000000010000 #define QMI_LTE_BAND_MASK_EUTRA_18 0x0000000000020000 #define QMI_LTE_BAND_MASK_EUTRA_19 0x0000000000040000 #define QMI_LTE_BAND_MASK_EUTRA_20 0x0000000000080000 #define QMI_LTE_BAND_MASK_EUTRA_21 0x0000000000100000 #define QMI_LTE_BAND_MASK_EUTRA_24 0x0000000000800000 #define QMI_LTE_BAND_MASK_EUTRA_25 0x0000000001000000 #define QMI_LTE_BAND_MASK_EUTRA_33 0x0000000100000000 #define QMI_LTE_BAND_MASK_EUTRA_34 0x0000000200000000 #define QMI_LTE_BAND_MASK_EUTRA_35 0x0000000400000000 #define QMI_LTE_BAND_MASK_EUTRA_36 0x0000000800000000 #define QMI_LTE_BAND_MASK_EUTRA_37 0x0000001000000000 #define QMI_LTE_BAND_MASK_EUTRA_38 0x0000002000000000 #define QMI_LTE_BAND_MASK_EUTRA_39 0x0000004000000000 #define QMI_LTE_BAND_MASK_EUTRA_40 0x0000008000000000 #define QMI_LTE_BAND_MASK_EUTRA_41 0x0000010000000000 #define QMI_LTE_BAND_MASK_EUTRA_42 0x0000020000000000 #define QMI_LTE_BAND_MASK_EUTRA_43 0x0000040000000000 ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/qmimodem.c0000644000015600001650000000312512671500024023642 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #define OFONO_API_SUBJECT_TO_CHANGE #include #include "qmimodem.h" static int qmimodem_init(void) { qmi_devinfo_init(); qmi_netreg_init(); qmi_voicecall_init(); qmi_sim_legacy_init(); qmi_sim_init(); qmi_sms_init(); qmi_ussd_init(); qmi_gprs_init(); qmi_gprs_context_init(); qmi_radio_settings_init(); qmi_location_reporting_init(); return 0; } static void qmimodem_exit(void) { qmi_location_reporting_exit(); qmi_radio_settings_exit(); qmi_gprs_context_exit(); qmi_gprs_exit(); qmi_ussd_exit(); qmi_sms_exit(); qmi_sim_exit(); qmi_sim_legacy_exit(); qmi_voicecall_exit(); qmi_netreg_exit(); qmi_devinfo_exit(); } OFONO_PLUGIN_DEFINE(qmimodem, "Qualcomm QMI modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, qmimodem_init, qmimodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/nas.h0000644000015600001650000001142512671500024022622 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 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 */ #define QMI_NAS_SET_EVENT 2 /* Set NAS state report conditions */ #define QMI_NAS_SET_REG_EVENT 3 /* Set NAS registration report conditions */ #define QMI_NAS_GET_RSSI 32 /* Get the signal strength */ #define QMI_NAS_SCAN_NETS 33 /* Scan for visible network */ #define QMI_NAS_REGISTER_NET 34 /* Initiate a network registration */ #define QMI_NAS_ATTACH_DETACH 35 /* Initiate an attach or detach action */ #define QMI_NAS_GET_SS_INFO 36 /* Get info about current serving system */ #define QMI_NAS_SS_INFO_IND 36 /* Current serving system info indication */ #define QMI_NAS_GET_HOME_INFO 37 /* Get info about home network */ /* Set NAS state report conditions */ #define QMI_NAS_PARAM_REPORT_SIGNAL_STRENGTH 0x10 struct qmi_nas_param_event_signal_strength { uint8_t report; /* bool */ uint8_t count; int8_t dbm[5]; } __attribute__((__packed__)); #define QMI_NAS_PARAM_REPORT_RF_INFO 0x11 struct qmi_nas_param_event_rf_info { uint8_t report; /* bool */ } __attribute__((__packed__)); #define QMI_NAS_NOTIFY_SIGNAL_STRENGTH 0x10 struct qmi_nas_signal_strength { int8_t dbm; uint8_t rat; } __attribute__((__packed__)); #define QMI_NAS_NOTIFY_RF_INFO 0x11 struct qmi_nas_rf_info { uint8_t count; struct { uint8_t rat; uint16_t band; uint16_t channel; } __attribute__((__packed__)) info[0]; } __attribute__((__packed__)); /* Get the signal strength */ #define QMI_NAS_RESULT_SIGNAL_STRENGTH 0x10 /* Scan for visible network */ #define QMI_NAS_PARAM_NETWORK_MASK 0x10 /* uint8 bitmask */ #define QMI_NAS_NETWORK_MASK_GSM (1 << 0) #define QMI_NAS_NETWORK_MASK_UMTS (1 << 1) #define QMI_NAS_NETWORK_MASK_LTE (1 << 2) #define QMI_NAS_NETWORK_MASK_TDSCDMA (1 << 3) #define QMI_NAS_RESULT_NETWORK_LIST 0x10 struct qmi_nas_network_info { uint16_t mcc; uint16_t mnc; uint8_t status; uint8_t desc_len; char desc[0]; } __attribute__((__packed__)); struct qmi_nas_network_list { uint16_t count; struct qmi_nas_network_info info[0]; } __attribute__((__packed__)); #define QMI_NAS_RESULT_NETWORK_RAT 0x11 struct qmi_nas_network_rat { uint16_t count; struct { uint16_t mcc; uint16_t mnc; uint8_t rat; } __attribute__((__packed__)) info[0]; } __attribute__((__packed__)); #define QMI_NAS_NETWORK_RAT_GSM 0x04 #define QMI_NAS_NETWORK_RAT_UMTS 0x05 #define QMI_NAS_NETWORK_RAT_LTE 0x08 #define QMI_NAS_NETWORK_RAT_TDSCDMA 0x09 #define QMI_NAS_NETWORK_RAT_NO_CHANGE 0xff /* Initiate a network registration */ #define QMI_NAS_PARAM_REGISTER_ACTION 0x01 /* uint8 */ #define QMI_NAS_PARAM_REGISTER_MANUAL_INFO 0x10 struct qmi_nas_param_register_manual_info { uint16_t mcc; uint16_t mnc; uint8_t rat; } __attribute__((__packed__)); #define QMI_NAS_REGISTER_ACTION_AUTO 0x01 #define QMI_NAS_REGISTER_ACTION_MANUAL 0x02 /* Initiate an attach or detach action */ #define QMI_NAS_PARAM_ATTACH_ACTION 0x10 /* uint8 */ #define QMI_NAS_ATTACH_ACTION_ATTACH 0x01 #define QMI_NAS_ATTACH_ACTION_DETACH 0x02 /* Get info about current serving system */ #define QMI_NAS_RESULT_SERVING_SYSTEM 0x01 struct qmi_nas_serving_system { uint8_t status; uint8_t cs_state; uint8_t ps_state; uint8_t network; uint8_t radio_if_count; uint8_t radio_if[0]; } __attribute__((__packed__)); #define QMI_NAS_RESULT_ROAMING_STATUS 0x10 /* uint8 */ #define QMI_NAS_RESULT_CURRENT_PLMN 0x12 struct qmi_nas_current_plmn { uint16_t mcc; uint16_t mnc; uint8_t desc_len; char desc[0]; } __attribute__((__packed__)); #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 /* Get info about home network */ #define QMI_NAS_RESULT_HOME_NETWORK 0x01 struct qmi_nas_home_network { uint16_t mcc; uint16_t mnc; uint8_t desc_len; char desc[0]; } __attribute__((__packed__)); ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/wds.h0000644000015600001650000000447612671500024022646 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 QMI_WDS_START_NET 32 /* Start WDS network interface */ #define QMI_WDS_STOP_NET 33 /* Stop WDS network interface */ #define QMI_WDS_GET_PKT_STATUS 34 /* Get packet data connection status */ #define QMI_WDS_PKT_STATUS_IND 34 /* Packet data connection status indication */ #define QMI_WDS_GET_SETTINGS 45 /* Get the runtime data session settings */ /* Start WDS network interface */ #define QMI_WDS_PARAM_APN 0x14 /* string */ #define QMI_WDS_PARAM_IP_FAMILY 0x19 /* uint8 */ #define QMI_WDS_RESULT_PKT_HANDLE 0x01 /* uint32 */ /* Stop WDS network interface */ #define QMI_WDS_PARAM_PKT_HANDLE 0x01 /* uint32 */ /* Packet data connection status indication */ #define QMI_WDS_NOTIFY_CONN_STATUS 0x01 struct qmi_wds_notify_conn_status { uint8_t status; uint8_t reconf; } __attribute__((__packed__)); #define QMI_WDS_NOTIFY_IP_FAMILY 0x12 /* uint8 */ #define QMI_WDS_CONN_STATUS_DISCONNECTED 0x01 #define QMI_WDS_CONN_STATUS_CONNECTED 0x02 #define QMI_WDS_CONN_STATUS_SUSPENDED 0x03 #define QMI_WDS_CONN_STATUS_AUTHENTICATING 0x04 /* Get the runtime data session settings */ #define QMI_WDS_RESULT_PDP_TYPE 0x11 /* uint8 */ #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_IP_FAMILY 0x2b /* uint8 */ #define QMI_WDS_PDP_TYPE_IPV4 0x00 #define QMI_WDS_PDP_TYPE_PPP 0x01 #define QMI_WDS_PDP_TYPE_IPV6 0x02 #define QMI_WDS_PDP_TYPE_IPV4V6 0x03 ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/radio-settings.c0000644000015600001650000000505312671500024024770 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #include #include #include #include "qmi.h" #include "nas.h" #include "qmimodem.h" struct settings_data { struct qmi_service *nas; uint16_t major; uint16_t minor; }; static void create_nas_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) { ofono_error("Failed to request NAS service"); ofono_radio_settings_remove(rs); return; } if (!qmi_service_get_version(service, &data->major, &data->minor)) { ofono_error("Failed to get NAS service version"); ofono_radio_settings_remove(rs); return; } data->nas = qmi_service_ref(service); ofono_radio_settings_register(rs); } static int qmi_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *user_data) { struct qmi_device *device = user_data; struct settings_data *data; DBG(""); data = g_new0(struct settings_data, 1); ofono_radio_settings_set_data(rs, data); qmi_service_create(device, QMI_SERVICE_NAS, create_nas_cb, rs, NULL); return 0; } static void qmi_radio_settings_remove(struct ofono_radio_settings *rs) { struct settings_data *data = ofono_radio_settings_get_data(rs); DBG(""); ofono_radio_settings_set_data(rs, NULL); qmi_service_unregister_all(data->nas); qmi_service_unref(data->nas); g_free(data); } static struct ofono_radio_settings_driver driver = { .name = "qmimodem", .probe = qmi_radio_settings_probe, .remove = qmi_radio_settings_remove, }; void qmi_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void qmi_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/qmi.h0000644000015600001650000001360712671500024022633 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #include #define QMI_SERVICE_CONTROL 0 /* Control service */ #define QMI_SERVICE_WDS 1 /* Wireless data service */ #define QMI_SERVICE_DMS 2 /* Device management service */ #define QMI_SERVICE_NAS 3 /* Network access service */ #define QMI_SERVICE_QOS 4 /* Quality of service, error service */ #define QMI_SERVICE_WMS 5 /* Wireless messaging service */ #define QMI_SERVICE_PDS 6 /* Position determination service */ #define QMI_SERVICE_AUTH 7 /* Authentication service */ #define QMI_SERVICE_AT 8 /* AT command processor service */ #define QMI_SERVICE_VOICE 9 /* Voice service */ #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_RMTFS 14 /* Remote file system service */ #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_PDC 36 /* Persistent device configuration service */ #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 */ struct qmi_version { uint8_t type; uint16_t major; uint16_t minor; const char *name; }; void qmi_free(void *ptr); 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_shutdown_func_t)(void *user_data); typedef void (*qmi_discover_func_t)(uint8_t count, const struct qmi_version *list, void *user_data); struct qmi_device *qmi_device_new(int fd); struct qmi_device *qmi_device_ref(struct qmi_device *device); void qmi_device_unref(struct qmi_device *device); void qmi_device_set_debug(struct qmi_device *device, qmi_debug_func_t func, void *user_data); void qmi_device_set_close_on_unref(struct qmi_device *device, bool do_close); bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func, void *user_data, qmi_destroy_func_t destroy); bool qmi_device_shutdown(struct qmi_device *device, qmi_shutdown_func_t func, void *user_data, qmi_destroy_func_t destroy); struct qmi_param; struct qmi_param *qmi_param_new(void); void qmi_param_free(struct qmi_param *param); bool qmi_param_append(struct qmi_param *param, uint8_t type, uint16_t length, const void *data); bool qmi_param_append_uint8(struct qmi_param *param, uint8_t type, uint8_t value); bool qmi_param_append_uint16(struct qmi_param *param, uint8_t type, uint16_t value); bool qmi_param_append_uint32(struct qmi_param *param, uint8_t type, uint32_t value); struct qmi_param *qmi_param_new_uint8(uint8_t type, uint8_t value); struct qmi_param *qmi_param_new_uint16(uint8_t type, uint16_t value); struct qmi_param *qmi_param_new_uint32(uint8_t type, uint32_t value); struct qmi_result; bool qmi_result_set_error(struct qmi_result *result, uint16_t *error); const char *qmi_result_get_error(struct qmi_result *result); const void *qmi_result_get(struct qmi_result *result, uint8_t type, uint16_t *length); 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_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); struct qmi_service; typedef void (*qmi_result_func_t)(struct qmi_result *result, void *user_data); typedef void (*qmi_create_func_t)(struct qmi_service *service, void *user_data); bool qmi_service_create(struct qmi_device *device, uint8_t type, qmi_create_func_t func, void *user_data, qmi_destroy_func_t destroy); bool qmi_service_create_shared(struct qmi_device *device, uint8_t type, qmi_create_func_t func, void *user_data, qmi_destroy_func_t destroy); struct qmi_service *qmi_service_ref(struct qmi_service *service); void qmi_service_unref(struct qmi_service *service); const char *qmi_service_get_identifier(struct qmi_service *service); bool qmi_service_get_version(struct qmi_service *service, uint16_t *major, uint16_t *minor); uint16_t qmi_service_send(struct qmi_service *service, uint16_t message, struct qmi_param *param, qmi_result_func_t func, void *user_data, qmi_destroy_func_t destroy); bool qmi_service_cancel(struct qmi_service *service, uint16_t id); bool qmi_service_cancel_all(struct qmi_service *service); uint16_t qmi_service_register(struct qmi_service *service, uint16_t message, qmi_result_func_t func, void *user_data, qmi_destroy_func_t destroy); bool qmi_service_unregister(struct qmi_service *service, uint16_t id); bool qmi_service_unregister_all(struct qmi_service *service); ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/sim.c0000644000015600001650000003054412671500024022627 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 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 #endif #include #include #include #include #include "qmi.h" #include "uim.h" #include "qmimodem.h" #include "simutil.h" #define EF_STATUS_INVALIDATED 0 #define EF_STATUS_VALID 1 struct sim_data { struct qmi_service *uim; uint32_t event_mask; uint8_t card_state; uint8_t app_type; uint8_t passwd_state; int retries[OFONO_SIM_PASSWORD_INVALID]; }; static int create_fileid_data(uint8_t app_type, int fileid, const unsigned char *path, unsigned int path_len, unsigned char *fileid_data) { unsigned char db_path[6]; unsigned int len; if (path_len > 0) { memcpy(db_path, path, path_len); len = path_len; } else { switch (app_type) { case 0x01: /* SIM card */ len = sim_ef_db_get_path_2g(fileid, db_path); break; case 0x02: /* USIM application */ len = sim_ef_db_get_path_3g(fileid, db_path); break; default: len = 0; break; } } /* Minimum length of path is 2 bytes */ if (len < 2) return -1; fileid_data[0] = fileid & 0xff; fileid_data[1] = (fileid & 0xff00) >> 8; fileid_data[2] = len; fileid_data[3] = db_path[1]; fileid_data[4] = db_path[0]; fileid_data[5] = db_path[3]; fileid_data[6] = db_path[2]; fileid_data[7] = db_path[5]; fileid_data[8] = db_path[4]; return len + 3; } static void get_file_attributes_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_sim_file_info_cb_t cb = cbd->cb; struct sim_data *data = ofono_sim_get_data(cbd->user); const struct qmi_uim_file_attributes *attr; uint16_t len, raw_len; int flen, rlen, str; unsigned char access[3]; unsigned char file_status; gboolean ok; DBG(""); if (qmi_result_set_error(result, NULL)) goto error; attr = qmi_result_get(result, 0x11, &len); if (!attr) goto error; raw_len = GUINT16_FROM_LE(attr->raw_len); switch (data->app_type) { case 0x01: /* SIM card */ ok = sim_parse_2g_get_response(attr->raw_value, raw_len, &flen, &rlen, &str, access, &file_status); break; case 0x02: /* USIM application */ ok = sim_parse_3g_get_response(attr->raw_value, raw_len, &flen, &rlen, &str, access, NULL); file_status = EF_STATUS_VALID; break; default: ok = FALSE; break; } if (ok) { CALLBACK_WITH_SUCCESS(cb, flen, str, rlen, access, file_status, cbd->data); return; } error: CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data); } static void qmi_read_attributes(struct ofono_sim *sim, int fileid, const unsigned char *path, unsigned int path_len, ofono_sim_file_info_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] = { 0x06, 0x00 }; unsigned char fileid_data[9]; int fileid_len; struct qmi_param *param; DBG("file id 0x%04x path len %d", fileid, path_len); cbd->user = sim; fileid_len = create_fileid_data(data->app_type, fileid, path, path_len, fileid_data); if (fileid_len < 0) goto error; 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); if (qmi_service_send(data->uim, QMI_UIM_GET_FILE_ATTRIBUTES, param, get_file_attributes_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data); g_free(cbd); } static void read_generic_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_sim_read_cb_t cb = cbd->cb; const unsigned char *content; uint16_t len; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); return; } content = qmi_result_get(result, 0x11, &len); if (!content) { CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); return; } CALLBACK_WITH_SUCCESS(cb, content + 2, len - 2, cbd->data); } static void qmi_read_transparent(struct ofono_sim *sim, int fileid, int start, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_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] = { 0x06, 0x00 }; unsigned char read_data[4]; 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; read_data[0] = start & 0xff; read_data[1] = (start & 0xff00) >> 8; read_data[2] = length & 0xff; read_data[3] = (length & 0xff00) >> 8; 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, sizeof(read_data), read_data); if (qmi_service_send(data->uim, QMI_UIM_READ_TRANSPARENT, param, read_generic_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, NULL, 0, user_data); g_free(cbd); } static void qmi_read_record(struct ofono_sim *sim, int fileid, int record, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_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] = { 0x06, 0x00 }; unsigned char read_data[4]; 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; read_data[0] = record & 0xff; read_data[1] = (record & 0xff00) >> 8; read_data[2] = length & 0xff; read_data[3] = (length & 0xff00) >> 8; 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, sizeof(read_data), read_data); if (qmi_service_send(data->uim, QMI_UIM_READ_RECORD, param, read_generic_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, NULL, 0, user_data); g_free(cbd); } 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); DBG("passwd state %d", data->passwd_state); if (data->passwd_state == OFONO_SIM_PASSWORD_INVALID) { CALLBACK_WITH_FAILURE(cb, -1, user_data); return; } CALLBACK_WITH_SUCCESS(cb, data->passwd_state, user_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); DBG("passwd state %d", data->passwd_state); if (data->passwd_state == OFONO_SIM_PASSWORD_INVALID) { CALLBACK_WITH_FAILURE(cb, NULL, user_data); return; } CALLBACK_WITH_SUCCESS(cb, data->retries, user_data); } static void card_setup(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) { data->card_state = slot->card_state; data->app_type = info1->app_type; switch (info1->app_state) { case 0x02: /* PIN1 or UPIN is required */ data->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; break; case 0x07: /* Ready */ data->passwd_state = OFONO_SIM_PASSWORD_NONE; break; default: data->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; data->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = info2->pin2_retries; data->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = info2->puk2_retries; } 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); const void *ptr; const struct qmi_uim_card_status *status; uint16_t len, offset; uint8_t i; DBG(""); if (qmi_result_set_error(result, NULL)) goto done; ptr = qmi_result_get(result, QMI_UIM_RESULT_CARD_STATUS, &len); if (!ptr) goto done; status = ptr; offset = sizeof(struct qmi_uim_card_status); for (i = 0; i < status->num_slot; i++) { const struct qmi_uim_slot_info *slot; uint8_t n; slot = ptr + offset; offset += sizeof(struct qmi_uim_slot_info); for (n = 0; n < slot->num_app; n++) { const struct qmi_uim_app_info1 *info1; const struct qmi_uim_app_info2 *info2; uint16_t index; info1 = ptr + offset; offset += sizeof(struct qmi_uim_app_info1); offset += info1->aid_len; info2 = ptr + offset; offset += sizeof(struct qmi_uim_app_info2); index = GUINT16_FROM_LE(status->index_gw_pri); if ((index & 0xff) == i && (index >> 8) == n) card_setup(slot, info1, info2, data); } } done: ofono_sim_register(sim); switch (data->card_state) { case 0x00: /* Absent */ case 0x02: /* Error */ break; case 0x01: /* Present */ ofono_sim_inserted_notify(sim, TRUE); break; } } static void event_registration_cb(struct qmi_result *result, void *user_data) { struct ofono_sim *sim = user_data; struct sim_data *data = ofono_sim_get_data(sim); DBG(""); if (qmi_result_set_error(result, NULL)) goto error; if (!qmi_result_get_uint32(result, QMI_UIM_RESULT_EVENT_MASK, &data->event_mask)) goto error; DBG("event mask 0x%04x", data->event_mask); if (qmi_service_send(data->uim, QMI_UIM_GET_CARD_STATUS, NULL, get_card_status_cb, sim, NULL) > 0) return; error: ofono_sim_remove(sim); } static void create_uim_cb(struct qmi_service *service, void *user_data) { struct ofono_sim *sim = user_data; struct sim_data *data = ofono_sim_get_data(sim); struct qmi_param *param; uint32_t mask = 0x0003; DBG(""); if (!service) { ofono_error("Failed to request UIM service"); goto error; } data->uim = qmi_service_ref(service); param = qmi_param_new_uint32(QMI_UIM_PARAM_EVENT_MASK, mask); if (!param) goto error; if (qmi_service_send(data->uim, QMI_UIM_EVENT_REGISTRATION, param, event_registration_cb, sim, NULL) > 0) return; error: qmi_service_unref(data->uim); ofono_sim_remove(sim); } 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; ofono_sim_set_data(sim, data); qmi_service_create(device, QMI_SERVICE_UIM, create_uim_cb, sim, NULL); return 0; } static void qmi_sim_remove(struct ofono_sim *sim) { struct sim_data *data = ofono_sim_get_data(sim); DBG(""); ofono_sim_set_data(sim, NULL); qmi_service_unregister_all(data->uim); qmi_service_unref(data->uim); g_free(data); } static struct ofono_sim_driver driver = { .name = "qmimodem", .probe = qmi_sim_probe, .remove = qmi_sim_remove, .read_file_info = qmi_read_attributes, .read_file_transparent = qmi_read_transparent, .read_file_linear = qmi_read_record, .read_file_cyclic = qmi_read_record, .query_passwd_state = qmi_query_passwd_state, .query_pin_retries = qmi_query_pin_retries, }; void qmi_sim_init(void) { ofono_sim_driver_register(&driver); } void qmi_sim_exit(void) { ofono_sim_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/util.h0000644000015600001650000000264112671500024023016 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 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 = g_new0(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) ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/devinfo.c0000644000015600001650000001171012671500024023463 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #include #include #include #include "qmi.h" #include "dms.h" #include "qmimodem.h" struct devinfo_data { struct qmi_service *dms; }; static void string_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_devinfo_query_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, 0x01); if (!str) { CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); return; } CALLBACK_WITH_SUCCESS(cb, str, cbd->data); qmi_free(str); } static void qmi_query_manufacturer(struct ofono_devinfo *devinfo, ofono_devinfo_query_cb_t cb, void *user_data) { struct devinfo_data *data = ofono_devinfo_get_data(devinfo); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); if (qmi_service_send(data->dms, QMI_DMS_GET_MANUFACTURER, NULL, string_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); g_free(cbd); } static void qmi_query_model(struct ofono_devinfo *devinfo, ofono_devinfo_query_cb_t cb, void *user_data) { struct devinfo_data *data = ofono_devinfo_get_data(devinfo); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); if (qmi_service_send(data->dms, QMI_DMS_GET_MODEL_ID, NULL, string_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); g_free(cbd); } static void qmi_query_revision(struct ofono_devinfo *devinfo, ofono_devinfo_query_cb_t cb, void *user_data) { struct devinfo_data *data = ofono_devinfo_get_data(devinfo); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); if (qmi_service_send(data->dms, QMI_DMS_GET_REV_ID, NULL, string_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); g_free(cbd); } static void get_ids_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_devinfo_query_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_ESN); if (!str) { str = qmi_result_get_string(result, QMI_DMS_RESULT_IMEI); if (!str) { CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); return; } } CALLBACK_WITH_SUCCESS(cb, str, cbd->data); qmi_free(str); } static void qmi_query_serial(struct ofono_devinfo *devinfo, ofono_devinfo_query_cb_t cb, void *user_data) { struct devinfo_data *data = ofono_devinfo_get_data(devinfo); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); if (qmi_service_send(data->dms, QMI_DMS_GET_IDS, NULL, get_ids_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); g_free(cbd); } static void create_dms_cb(struct qmi_service *service, void *user_data) { struct ofono_devinfo *devinfo = user_data; struct devinfo_data *data = ofono_devinfo_get_data(devinfo); DBG(""); if (!service) { ofono_error("Failed to request DMS service"); ofono_devinfo_remove(devinfo); return; } data->dms = qmi_service_ref(service); ofono_devinfo_register(devinfo); } static int qmi_devinfo_probe(struct ofono_devinfo *devinfo, unsigned int vendor, void *user_data) { struct qmi_device *device = user_data; struct devinfo_data *data; DBG(""); data = g_new0(struct devinfo_data, 1); ofono_devinfo_set_data(devinfo, data); qmi_service_create_shared(device, QMI_SERVICE_DMS, create_dms_cb, devinfo, NULL); return 0; } static void qmi_devinfo_remove(struct ofono_devinfo *devinfo) { struct devinfo_data *data = ofono_devinfo_get_data(devinfo); DBG(""); ofono_devinfo_set_data(devinfo, NULL); qmi_service_unref(data->dms); g_free(data); } static struct ofono_devinfo_driver driver = { .name = "qmimodem", .probe = qmi_devinfo_probe, .remove = qmi_devinfo_remove, .query_manufacturer = qmi_query_manufacturer, .query_model = qmi_query_model, .query_revision = qmi_query_revision, .query_serial = qmi_query_serial, }; void qmi_devinfo_init(void) { ofono_devinfo_driver_register(&driver); } void qmi_devinfo_exit(void) { ofono_devinfo_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/sim-legacy.c0000644000015600001650000002133112671500024024063 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #include #include #include #include #include #include "qmi.h" #include "dms.h" #include "qmimodem.h" #include "simutil.h" struct sim_data { struct qmi_service *dms; int retries[OFONO_SIM_PASSWORD_INVALID]; }; static void qmi_read_file_info(struct ofono_sim *sim, int fileid, const unsigned char *path, unsigned int path_len, ofono_sim_file_info_cb_t cb, void *user_data) { unsigned char access[3] = { 0x0f, 0xff, 0xff }; DBG("file id 0x%04x", fileid); switch (fileid) { case SIM_EF_ICCID_FILEID: CALLBACK_WITH_SUCCESS(cb, 10, 0, 0, access, 1, user_data); break; default: CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, user_data); break; } } static void get_iccid_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_sim_read_cb_t cb = cbd->cb; unsigned char iccid[10]; int iccid_len, len; char *str; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); return; } str = qmi_result_get_string(result, QMI_DMS_RESULT_ICCID); if (!str) { CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); return; } len = strlen(str); if (len > 20) { CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); return; } sim_encode_bcd_number(str, iccid); iccid_len = len / 2; qmi_free(str); CALLBACK_WITH_SUCCESS(cb, iccid, iccid_len, cbd->data); } static void qmi_read_file_transparent(struct ofono_sim *sim, int fileid, int start, int length, const unsigned char *path, unsigned int path_len, ofono_sim_read_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("file id 0x%04x", fileid); switch (fileid) { case SIM_EF_ICCID_FILEID: if (qmi_service_send(data->dms, QMI_DMS_GET_ICCID, NULL, get_iccid_cb, cbd, g_free) > 0) return; break; } CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); g_free(cbd); } 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); } static void get_pin_status_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_sim_passwd_cb_t cb = cbd->cb; struct sim_data *data = cbd->user; const struct qmi_dms_pin_status *pin; uint16_t len; int pin_type; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } pin = qmi_result_get(result, QMI_DMS_RESULT_PIN1_STATUS, &len); if (!pin) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } DBG("PIN 1 status %d", pin->status); switch (pin->status) { case QMI_DMS_PIN_ENABLED_UNVERIFIED: pin_type = OFONO_SIM_PASSWORD_SIM_PIN; break; case QMI_DMS_PIN_ENABLED_VERIFIED: case QMI_DMS_PIN_DISABLED: pin_type = OFONO_SIM_PASSWORD_NONE; break; default: pin_type = OFONO_SIM_PASSWORD_INVALID; break; } data->retries[OFONO_SIM_PASSWORD_SIM_PIN] = pin->verify_retries; data->retries[OFONO_SIM_PASSWORD_SIM_PUK] = pin->unblock_retries; pin = qmi_result_get(result, QMI_DMS_RESULT_PIN2_STATUS, &len); if (!pin) goto done; DBG("PIN 2 status %d", pin->status); data->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = pin->verify_retries; data->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = pin->unblock_retries; done: CALLBACK_WITH_SUCCESS(cb, pin_type, cbd->data); } 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 = data; if (qmi_service_send(data->dms, QMI_DMS_GET_PIN_STATUS, NULL, get_pin_status_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); } 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(""); CALLBACK_WITH_SUCCESS(cb, data->retries, user_data); } static void process_uim_state(struct ofono_sim *sim, uint8_t state) { DBG("UIM state %d", state); switch (state) { case QMI_DMS_UIM_STATE_INIT_COMPLETE: ofono_sim_inserted_notify(sim, TRUE); break; case QMI_DMS_UIM_STATE_INIT_FAILED: case QMI_DMS_UIM_STATE_NOT_PRESENT: case QMI_DMS_UIM_STATE_INVALID: ofono_sim_inserted_notify(sim, FALSE); break; } } static void event_notify(struct qmi_result *result, void *user_data) { struct ofono_sim *sim = user_data; uint8_t state; DBG(""); if (qmi_result_get_uint8(result, QMI_DMS_NOTIFY_UIM_STATE, &state)) process_uim_state(sim, state); } static void get_uim_state(struct qmi_result *result, void *user_data) { struct ofono_sim *sim = user_data; uint8_t state; DBG(""); if (qmi_result_set_error(result, NULL)) goto done; if (qmi_result_get_uint8(result, QMI_DMS_RESULT_UIM_STATE, &state)) process_uim_state(sim, state); done: ofono_sim_register(sim); } static void set_event_cb(struct qmi_result *result, void *user_data) { struct ofono_sim *sim = user_data; struct sim_data *data = ofono_sim_get_data(sim); DBG(""); if (qmi_result_set_error(result, NULL)) goto done; if (qmi_service_send(data->dms, QMI_DMS_GET_UIM_STATE, NULL, get_uim_state, sim, NULL) > 0) return; done: ofono_sim_register(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); struct qmi_param *param; DBG(""); if (!service) { ofono_error("Failed to request DMS service"); ofono_sim_remove(sim); return; } data->dms = qmi_service_ref(service); qmi_service_register(data->dms, QMI_DMS_EVENT, event_notify, sim, NULL); param = qmi_param_new(); if (!param) goto done; qmi_param_append_uint8(param, QMI_DMS_PARAM_REPORT_PIN_STATUS, 0x01); qmi_param_append_uint8(param, QMI_DMS_PARAM_REPORT_OPER_MODE, 0x01); qmi_param_append_uint8(param, QMI_DMS_PARAM_REPORT_UIM_STATE, 0x01); if (qmi_service_send(data->dms, QMI_DMS_SET_EVENT, param, set_event_cb, sim, NULL) > 0) return; qmi_param_free(param); done: ofono_sim_register(sim); } 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); for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) data->retries[i] = -1; ofono_sim_set_data(sim, data); qmi_service_create_shared(device, QMI_SERVICE_DMS, create_dms_cb, sim, NULL); return 0; } static void qmi_sim_remove(struct ofono_sim *sim) { struct sim_data *data = ofono_sim_get_data(sim); DBG(""); ofono_sim_set_data(sim, NULL); qmi_service_unregister_all(data->dms); qmi_service_unref(data->dms); g_free(data); } static struct ofono_sim_driver driver = { .name = "qmimodem-legacy", .probe = qmi_sim_probe, .remove = qmi_sim_remove, .read_file_info = qmi_read_file_info, .read_file_transparent = qmi_read_file_transparent, .read_imsi = qmi_read_imsi, .query_passwd_state = qmi_query_passwd_state, .query_pin_retries = qmi_query_pin_retries, }; void qmi_sim_legacy_init(void) { ofono_sim_driver_register(&driver); } void qmi_sim_legacy_exit(void) { ofono_sim_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/uim.h0000644000015600001650000000512712671500024022635 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 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 QMI_UIM_READ_TRANSPARENT 32 /* Read data */ #define QMI_UIM_READ_RECORD 33 /* Read one or more records */ #define QMI_UIM_WRITE_TRANSPARENT 34 /* Write data */ #define QMI_UIM_WRITE_RECORD 35 /* Write a record */ #define QMI_UIM_GET_FILE_ATTRIBUTES 36 /* Get file attributes */ #define QMI_UIM_EVENT_REGISTRATION 46 /* Register for indications */ #define QMI_UIM_GET_CARD_STATUS 47 /* Get card status */ /* Register for indications */ #define QMI_UIM_PARAM_EVENT_MASK 0x01 /* uint32 */ #define QMI_UIM_RESULT_EVENT_MASK 0x10 /* uint32 */ #define QMI_UIM_RESULT_CARD_STATUS 0x10 struct qmi_uim_card_status { uint16_t index_gw_pri; uint16_t index_1x_pri; uint16_t index_gw_sec; uint16_t index_1x_sec; uint8_t num_slot; } __attribute__((__packed__)); struct qmi_uim_slot_info { uint8_t card_state; uint8_t upin_state; uint8_t upin_retries; uint8_t upuk_retries; uint8_t error_code; uint8_t num_app; } __attribute__((__packed__)); struct qmi_uim_app_info1 { uint8_t app_type; uint8_t app_state; uint8_t perso_state; uint8_t perso_feature; uint8_t perso_retries; uint8_t perso_unblock_retries; uint8_t aid_len; uint8_t aid_value[0]; } __attribute__((__packed__)); struct qmi_uim_app_info2 { uint8_t univ_pin; uint8_t pin1_state; uint8_t pin1_retries; uint8_t puk1_retries; uint8_t pin2_state; uint8_t pin2_retries; uint8_t puk2_retries; } __attribute__((__packed__)); struct qmi_uim_file_attributes { uint16_t file_size; uint16_t file_id; uint8_t file_type; uint16_t rec_size; uint16_t rec_count; uint8_t sec_read; uint16_t sec_read_mask; uint8_t sec_write; uint16_t sec_write_mask; uint8_t sec_increase; uint16_t sec_increase_mask; uint8_t sec_deactivate; uint16_t sec_deactivate_mask; uint8_t sec_activate; uint16_t sec_activate_mask; uint16_t raw_len; uint8_t raw_value[0]; } __attribute__((__packed__)); ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/network-registration.c0000644000015600001650000003232612671500024026240 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #include #include #include #include #include #include #include "qmi.h" #include "nas.h" #include "qmimodem.h" #include "src/common.h" struct netreg_data { struct qmi_service *nas; struct ofono_network_operator operator; uint8_t current_rat; }; static int 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; } static bool extract_ss_info(struct qmi_result *result, int *status, int *lac, int *cellid, int *tech, struct ofono_network_operator *operator) { const struct qmi_nas_serving_system *ss; const struct qmi_nas_current_plmn *plmn; uint8_t i, roaming; uint16_t value16, len; uint32_t value32; DBG(""); ss = qmi_result_get(result, QMI_NAS_RESULT_SERVING_SYSTEM, &len); if (!ss) return false; *status = ss->status; DBG("serving system status %d", ss->status); *tech = -1; 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]); } if (qmi_result_get_uint8(result, QMI_NAS_RESULT_ROAMING_STATUS, &roaming)) { if (ss->status == 1 && roaming == 0) *status = 5; } if (!operator) return true; plmn = qmi_result_get(result, QMI_NAS_RESULT_CURRENT_PLMN, &len); if (plmn) { snprintf(operator->mcc, OFONO_MAX_MCC_LENGTH + 1, "%03d", 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'; DBG("%s (%s:%s)", operator->name, operator->mcc, operator->mnc); } if (qmi_result_get_uint16(result, QMI_NAS_RESULT_LOCATION_AREA_CODE, &value16)) *lac = value16; else *lac = -1; if (qmi_result_get_uint32(result, QMI_NAS_RESULT_CELL_ID, &value32)) *cellid = value32; else *cellid = -1; DBG("lac %d cellid %d tech %d", *lac, *cellid, *tech); return true; } static void ss_info_notify(struct qmi_result *result, void *user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *data = ofono_netreg_get_data(netreg); int status, lac, cellid, tech; DBG(""); if (!extract_ss_info(result, &status, &lac, &cellid, &tech, &data->operator)) return; ofono_netreg_status_notify(netreg, status, lac, cellid, tech); } static void get_ss_info_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_netreg_status_cb_t cb = cbd->cb; struct netreg_data *data = cbd->user; int status, lac, cellid, tech; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data); return; } if (!extract_ss_info(result, &status, &lac, &cellid, &tech, &data->operator)) { CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data); return; } CALLBACK_WITH_SUCCESS(cb, status, lac, cellid, tech, cbd->data); } static void qmi_registration_status(struct ofono_netreg *netreg, ofono_netreg_status_cb_t cb, void *user_data) { struct netreg_data *data = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); cbd->user = data; if (qmi_service_send(data->nas, QMI_NAS_GET_SS_INFO, NULL, get_ss_info_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data); g_free(cbd); } static void qmi_current_operator(struct ofono_netreg *netreg, ofono_netreg_operator_cb_t cb, void *user_data) { struct netreg_data *data = ofono_netreg_get_data(netreg); DBG(""); CALLBACK_WITH_SUCCESS(cb, &data->operator, user_data); } static void scan_nets_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_netreg_operator_list_cb_t cb = cbd->cb; struct ofono_network_operator *list; const struct qmi_nas_network_list *netlist; const struct qmi_nas_network_rat *netrat; const void *ptr; uint16_t len, num, offset, i; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data); return; } ptr = qmi_result_get(result, QMI_NAS_RESULT_NETWORK_LIST, &len); if (!ptr) { CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data); return; } netlist = ptr; num = GUINT16_FROM_LE(netlist->count); DBG("found %d operators", num); list = g_try_new0(struct ofono_network_operator, num); if (!list) { CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data); return; } offset = 2; for (i = 0; i < num; i++) { const struct qmi_nas_network_info *netinfo = ptr + offset; snprintf(list[i].mcc, OFONO_MAX_MCC_LENGTH + 1, "%03d", GUINT16_FROM_LE(netinfo->mcc)); snprintf(list[i].mnc, OFONO_MAX_MNC_LENGTH + 1, "%02d", GUINT16_FROM_LE(netinfo->mnc)); strncpy(list[i].name, netinfo->desc, netinfo->desc_len); list[i].name[netinfo->desc_len] = '\0'; if (netinfo->status & 0x10) list[i].status = 3; else if (netinfo->status & 0x01) list[i].status = 2; else if (netinfo->status & 0x02) list[i].status = 1; else list[i].status = 0; list[i].tech = -1; DBG("%s (%s:%s) status %d", list[i].name, list[i].mcc, list[i].mnc, list[i].status); offset += sizeof(struct qmi_nas_network_info) + netinfo->desc_len; } netrat = qmi_result_get(result, QMI_NAS_RESULT_NETWORK_RAT, &len); if (!netrat) goto done; if (GUINT16_FROM_LE(netrat->count) != num) goto done; for (i = 0; i < num; i++) { 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); } done: CALLBACK_WITH_SUCCESS(cb, num, list, cbd->data); g_free(list); } static void qmi_list_operators(struct ofono_netreg *netreg, ofono_netreg_operator_list_cb_t cb, void *user_data) { struct netreg_data *data = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); if (qmi_service_send(data->nas, QMI_NAS_SCAN_NETS, NULL, scan_nets_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data); g_free(cbd); } 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; DBG(""); if (qmi_result_set_error(result, &error)) { if (error == 26) { /* no effect */ goto done; } CALLBACK_WITH_FAILURE(cb, cbd->data); return; } done: CALLBACK_WITH_SUCCESS(cb, cbd->data); } static void qmi_register_auto(struct ofono_netreg *netreg, ofono_netreg_register_cb_t cb, void *user_data) { struct netreg_data *data = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, user_data); struct qmi_param *param; DBG(""); param = qmi_param_new_uint8(QMI_NAS_PARAM_REGISTER_ACTION, QMI_NAS_REGISTER_ACTION_AUTO); if (!param) goto error; if (qmi_service_send(data->nas, QMI_NAS_REGISTER_NET, param, register_net_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void qmi_register_manual(struct ofono_netreg *netreg, const char *mcc, const char *mnc, ofono_netreg_register_cb_t cb, void *user_data) { struct netreg_data *data = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, user_data); struct qmi_nas_param_register_manual_info info; struct qmi_param *param; DBG(""); param = qmi_param_new_uint8(QMI_NAS_PARAM_REGISTER_ACTION, QMI_NAS_REGISTER_ACTION_MANUAL); if (!param) goto error; info.mcc = atoi(mcc); info.mnc = atoi(mnc); info.rat = data->current_rat; qmi_param_append(param, QMI_NAS_PARAM_REGISTER_MANUAL_INFO, sizeof(info), &info); if (qmi_service_send(data->nas, QMI_NAS_REGISTER_NET, param, register_net_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static int dbm_to_strength(int8_t dbm) { if (dbm > -55) return 100; else if (dbm > -65) return 80; else if (dbm > -75) return 60; else if (dbm > -85) return 40; else if (dbm > -95) return 20; else if (dbm > -105) return 0; return -1; } static void get_rssi_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_netreg_strength_cb_t cb = cbd->cb; const struct qmi_nas_signal_strength *ss; uint16_t len; int strength; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } ss = qmi_result_get(result, QMI_NAS_RESULT_SIGNAL_STRENGTH, &len); if (!ss) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } DBG("signal with %d dBm on %d", ss->dbm, ss->rat); strength = dbm_to_strength(ss->dbm); CALLBACK_WITH_SUCCESS(cb, strength, cbd->data); } static void qmi_signal_strength(struct ofono_netreg *netreg, ofono_netreg_strength_cb_t cb, void *user_data) { struct netreg_data *data = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); if (qmi_service_send(data->nas, QMI_NAS_GET_RSSI, NULL, get_rssi_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); } static void event_notify(struct qmi_result *result, void *user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *data = ofono_netreg_get_data(netreg); const struct qmi_nas_signal_strength *ss; const struct qmi_nas_rf_info *rf; uint16_t len; DBG(""); ss = qmi_result_get(result, QMI_NAS_NOTIFY_SIGNAL_STRENGTH, &len); if (ss) { int strength; DBG("signal with %d dBm on %d", ss->dbm, ss->rat); strength = dbm_to_strength(ss->dbm); ofono_netreg_strength_notify(netreg, strength); } rf = qmi_result_get(result, QMI_NAS_NOTIFY_RF_INFO, &len); if (rf) { uint8_t i; for (i = 0; i < rf->count; i++) { DBG("rat %d band %d channel %d", rf->info[i].rat, rf->info[i].band, rf->info[i].channel); } data->current_rat = rf->info[i].rat; } } static void set_event_cb(struct qmi_result *result, void *user_data) { struct ofono_netreg *netreg = user_data; DBG(""); ofono_netreg_register(netreg); } static void create_nas_cb(struct qmi_service *service, void *user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *data = ofono_netreg_get_data(netreg); struct qmi_param *param; struct qmi_nas_param_event_signal_strength ss = { .report = 0x01, .count = 5, .dbm[0] = -55, .dbm[1] = -65, .dbm[2] = -75, .dbm[3] = -85, .dbm[4] = -95 }; DBG(""); if (!service) { ofono_error("Failed to request NAS service"); ofono_netreg_remove(netreg); return; } 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; qmi_param_append(param, QMI_NAS_PARAM_REPORT_SIGNAL_STRENGTH, sizeof(ss), &ss); qmi_param_append_uint8(param, QMI_NAS_PARAM_REPORT_RF_INFO, 0x01); if (qmi_service_send(data->nas, QMI_NAS_SET_EVENT, param, set_event_cb, netreg, NULL) > 0) return; qmi_param_free(param); done: ofono_netreg_register(netreg); } static int qmi_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor, void *user_data) { struct qmi_device *device = user_data; struct netreg_data *data; DBG(""); data = g_new0(struct netreg_data, 1); data->operator.name[0] = '\0'; data->operator.mcc[0] = '\0'; data->operator.mnc[0] = '\0'; data->operator.status = -1; data->operator.tech = -1; data->current_rat = QMI_NAS_NETWORK_RAT_NO_CHANGE; ofono_netreg_set_data(netreg, data); qmi_service_create(device, QMI_SERVICE_NAS, create_nas_cb, netreg, NULL); return 0; } static void qmi_netreg_remove(struct ofono_netreg *netreg) { struct netreg_data *data = ofono_netreg_get_data(netreg); DBG(""); ofono_netreg_set_data(netreg, NULL); qmi_service_unregister_all(data->nas); qmi_service_unref(data->nas); g_free(data); } static struct ofono_netreg_driver driver = { .name = "qmimodem", .probe = qmi_netreg_probe, .remove = qmi_netreg_remove, .registration_status = qmi_registration_status, .current_operator = qmi_current_operator, .list_operators = qmi_list_operators, .register_auto = qmi_register_auto, .register_manual = qmi_register_manual, .strength = qmi_signal_strength, }; void qmi_netreg_init(void) { ofono_netreg_driver_register(&driver); } void qmi_netreg_exit(void) { ofono_netreg_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/ussd.c0000644000015600001650000000450712671500024023015 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #include #include #include #include "qmi.h" #include "qmimodem.h" struct ussd_data { struct qmi_service *voice; uint16_t major; uint16_t minor; }; static void create_voice_cb(struct qmi_service *service, void *user_data) { struct ofono_ussd *ussd = user_data; struct ussd_data *data = ofono_ussd_get_data(ussd); DBG(""); if (!service) { ofono_error("Failed to request Voice service"); ofono_ussd_remove(ussd); return; } if (!qmi_service_get_version(service, &data->major, &data->minor)) { ofono_error("Failed to get Voice service version"); ofono_ussd_remove(ussd); return; } data->voice = qmi_service_ref(service); ofono_ussd_register(ussd); } static int qmi_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor, void *user_data) { struct qmi_device *device = user_data; struct ussd_data *data; DBG(""); data = g_new0(struct ussd_data, 1); ofono_ussd_set_data(ussd, data); qmi_service_create_shared(device, QMI_SERVICE_VOICE, create_voice_cb, ussd, NULL); return 0; } static void qmi_ussd_remove(struct ofono_ussd *ussd) { struct ussd_data *data = ofono_ussd_get_data(ussd); DBG(""); ofono_ussd_set_data(ussd, NULL); qmi_service_unref(data->voice); g_free(data); } static struct ofono_ussd_driver driver = { .name = "qmimodem", .probe = qmi_ussd_probe, .remove = qmi_ussd_remove, }; void qmi_ussd_init(void) { ofono_ussd_driver_register(&driver); } void qmi_ussd_exit(void) { ofono_ussd_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/gprs.c0000644000015600001650000001141512671500024023006 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #include #include #include #include "qmi.h" #include "nas.h" #include "qmimodem.h" struct gprs_data { struct qmi_service *nas; }; static bool extract_ss_info(struct qmi_result *result, int *status) { const struct qmi_nas_serving_system *ss; uint16_t len; DBG(""); ss = qmi_result_get(result, QMI_NAS_RESULT_SERVING_SYSTEM, &len); if (!ss) return false; if (ss->ps_state == QMI_NAS_ATTACH_STATUS_ATTACHED) *status = 0x01; else *status = 0x00; return true; } static void ss_info_notify(struct qmi_result *result, void *user_data) { struct ofono_gprs *gprs = user_data; int status; DBG(""); if (!extract_ss_info(result, &status)) return; ofono_gprs_status_notify(gprs, status); } static void attach_detach_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_gprs_cb_t cb = cbd->cb; uint16_t error; DBG(""); if (qmi_result_set_error(result, &error)) { if (error == 26) { /* no effect */ goto done; } CALLBACK_WITH_FAILURE(cb, cbd->data); return; } done: CALLBACK_WITH_SUCCESS(cb, cbd->data); } static void qmi_set_attached(struct ofono_gprs *gprs, int attached, ofono_gprs_cb_t cb, void *user_data) { struct gprs_data *data = ofono_gprs_get_data(gprs); struct cb_data *cbd = cb_data_new(cb, user_data); struct qmi_param *param; uint8_t action; DBG("attached %d", attached); if (attached) action = QMI_NAS_ATTACH_ACTION_ATTACH; else action = QMI_NAS_ATTACH_ACTION_DETACH; param = qmi_param_new_uint8(QMI_NAS_PARAM_ATTACH_ACTION, action); if (!param) goto error; if (qmi_service_send(data->nas, QMI_NAS_ATTACH_DETACH, param, attach_detach_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void get_ss_info_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; 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 (!extract_ss_info(result, &status)) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } CALLBACK_WITH_SUCCESS(cb, status, cbd->data); } static void qmi_attached_status(struct ofono_gprs *gprs, ofono_gprs_status_cb_t cb, void *user_data) { struct gprs_data *data = ofono_gprs_get_data(gprs); struct cb_data *cbd = cb_data_new(cb, user_data); DBG(""); if (qmi_service_send(data->nas, QMI_NAS_GET_SS_INFO, NULL, get_ss_info_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); } static void create_nas_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 NAS service"); ofono_gprs_remove(gprs); return; } 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); } static int qmi_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor, void *user_data) { struct qmi_device *device = user_data; struct gprs_data *data; DBG(""); data = g_new0(struct gprs_data, 1); ofono_gprs_set_data(gprs, data); qmi_service_create(device, QMI_SERVICE_NAS, create_nas_cb, gprs, NULL); return 0; } static void qmi_gprs_remove(struct ofono_gprs *gprs) { struct gprs_data *data = ofono_gprs_get_data(gprs); DBG(""); ofono_gprs_set_data(gprs, NULL); qmi_service_unregister_all(data->nas); qmi_service_unref(data->nas); g_free(data); } static struct ofono_gprs_driver driver = { .name = "qmimodem", .probe = qmi_gprs_probe, .remove = qmi_gprs_remove, .set_attached = qmi_set_attached, .attached_status = qmi_attached_status, }; void qmi_gprs_init(void) { ofono_gprs_driver_register(&driver); } void qmi_gprs_exit(void) { ofono_gprs_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/wms.h0000644000015600001650000001006612671500024022647 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 QMI_WMS_RESET 0 /* Reset WMS service */ #define QMI_WMS_EVENT 1 /* New message indication */ #define QMI_WMS_SET_EVENT 1 /* Set new message conditions */ #define QMI_WMS_RAW_SEND 32 /* Send a raw message */ #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 */ #define QMI_WMS_GET_SMSC_ADDR 52 /* Get SMSC address */ #define QMI_WMS_SET_SMSC_ADDR 53 /* Set SMSC address */ #define QMI_WMS_GET_MSG_LIST_MAX 54 /* Get maximum size of SMS storage */ #define QMI_WMS_GET_DOMAIN_PREF 64 /* Get domain preference */ #define QMI_WMS_SET_DOMAIN_PREF 65 /* Set domain preference */ /* New message indication */ #define QMI_WMS_RESULT_NEW_MSG_NOTIFY 0x10 struct qmi_wms_result_new_msg_notify { uint8_t storage_type; uint32_t storage_index; } __attribute__((__packed__)); /* Set new message conditions */ #define QMI_WMS_PARAM_NEW_MSG_REPORT 0x10 /* bool */ /* Send a raw message */ #define QMI_WMS_PARAM_MESSAGE 0x01 struct qmi_wms_param_message { uint8_t msg_format; uint16_t msg_length; uint8_t msg_data[0]; } __attribute__((__packed__)); #define QMI_WMS_RESULT_MESSAGE_ID 0x01 /* uint16 */ /* Get list of messages from the device */ #define QMI_WMS_PARAM_STORAGE_TYPE 0x01 /* uint8 */ #define QMI_WMS_PARAM_MESSAGE_MODE 0x11 /* uint8 */ #define QMI_WMS_STORAGE_TYPE_UIM 0 #define QMI_WMS_STORAGE_TYPE_NV 1 #define QMI_WMS_STORAGE_TYPE_UNKNOWN 2 #define QMI_WMS_MESSAGE_MODE_GSMWCDMA 1 /* Get routes for message memory storage */ #define QMI_WMS_RESULT_ROUTE_LIST 0x01 #define QMI_WMS_PARAM_ROUTE_LIST 0x01 struct qmi_wms_route_list { uint16_t count; struct { uint8_t msg_type; uint8_t msg_class; uint8_t storage_type; uint8_t action; } __attribute__((__packed__)) route[0]; } __attribute__((__packed__)); #define QMI_WMS_RESULT_STATUS_REPORT 0x10 /* bool */ #define QMI_WMS_PARAM_STATUS_REPORT 0x10 /* bool */ #define QMI_WMS_RESULT_MESSAGE 0x11 struct qmi_wms_result_message { uint8_t ack_required; /* bool */ uint32_t transaction_id; uint8_t msg_format; uint16_t msg_length; uint8_t msg_data[0]; } __attribute__((__packed__)); #define QMI_WMS_MSG_TYPE_P2P 0x00 #define QMI_WMS_MSG_TYPE_BROADCAST 0x01 #define QMI_WMS_MSG_CLASS_0 0x00 #define QMI_WMS_MSG_CLASS_1 0x01 #define QMI_WMS_MSG_CLASS_2 0x02 #define QMI_WMS_MSG_CLASS_3 0x03 #define QMI_WMS_MSG_CLASS_NONE 0x04 #define QMI_WMS_MSG_CLASS_CDMA 0x05 #define QMI_WMS_ACTION_DISCARD 0x00 #define QMI_WMS_ACTION_STORE_AND_NOTIFY 0x01 #define QMI_WMS_ACTION_TRANSFER_ONLY 0x02 #define QMI_WMS_ACTION_TRANSFER_AND_ACK 0x03 #define QMI_WMS_ACTION_UNKNOWN 0xff /* Get SMSC address */ #define QMI_WMS_RESULT_SMSC_ADDR 0x01 struct qmi_wms_result_smsc_addr { char type[3]; uint8_t addr_len; char addr[0]; } __attribute__((__packed__)); /* Set SMSC address */ #define QMI_WMS_PARAM_SMSC_ADDR 0x01 /* string */ #define QMI_WMS_PARAM_SMSC_ADDR_TYPE 0x10 /* string */ /* Get domain preference */ #define QMI_WMS_RESULT_DOMAIN 0x01 /* uint8 */ #define QMI_WMS_PARAM_DOMAIN 0x01 /* uint8 */ #define QMI_WMS_DOMAIN_CS_PREFERRED 0x00 #define QMI_WMS_DOMAIN_PS_PREFERRED 0x01 #define QMI_WMS_DOMAIN_CS_ONLY 0x02 #define QMI_WMS_DOMAIN_PS_ONLY 0x03 ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/location-reporting.c0000644000015600001650000001505112671500024025652 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include "qmi.h" #include "pds.h" #include "qmimodem.h" struct location_data { struct qmi_service *pds; int fd; }; static void event_notify(struct qmi_result *result, void *user_data) { struct ofono_location_reporting *lr = user_data; struct location_data *data = ofono_location_reporting_get_data(lr); const void *ptr; uint16_t len; ssize_t written; DBG(""); if (data->fd < 0) return; ptr = qmi_result_get(result, QMI_PDS_NOTIFY_NMEA, &len); if (ptr) { written = write(data->fd, ptr, len); if (written < 0) ofono_warn("Failed to write NMEA data"); } ptr = qmi_result_get(result, QMI_PDS_NOTIFY_NMEA_DEBUG, &len); if (ptr) { written = write(data->fd, ptr, len); if (written < 0) ofono_warn("Failed to write NMEA debug"); } } static void state_notify(struct qmi_result *result, void *user_data) { DBG(""); } static int enable_data_stream(struct ofono_location_reporting *lr) { struct location_data *data = ofono_location_reporting_get_data(lr); int pipefd[2]; DBG(""); if (pipe2(pipefd, O_NONBLOCK | O_CLOEXEC) < 0) return -1; data->fd = pipefd[1]; return pipefd[0]; } static void disable_data_stream(struct ofono_location_reporting *lr) { struct location_data *data = ofono_location_reporting_get_data(lr); DBG(""); close(data->fd); data->fd = -1; } static void autotrack_enable_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_location_reporting_enable_cb_t cb = cbd->cb; struct ofono_location_reporting *lr = cbd->user; int fd; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } fd = enable_data_stream(lr); if (fd < 0) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } CALLBACK_WITH_SUCCESS(cb, fd, cbd->data); close(fd); } static void qmi_location_reporting_enable(struct ofono_location_reporting *lr, ofono_location_reporting_enable_cb_t cb, void *user_data) { struct location_data *data = ofono_location_reporting_get_data(lr); struct cb_data *cbd = cb_data_new(cb, user_data); struct qmi_param *param; DBG(""); cbd->user = lr; param = qmi_param_new_uint8(QMI_PDS_PARAM_AUTO_TRACKING, 0x01); if (!param) goto error; if (qmi_service_send(data->pds, QMI_PDS_SET_AUTOTRACK, param, autotrack_enable_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); } static void autotrack_disable_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_location_reporting_disable_cb_t cb = cbd->cb; struct ofono_location_reporting *lr = cbd->user; DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, cbd->data); return; } disable_data_stream(lr); CALLBACK_WITH_SUCCESS(cb, cbd->data); } static void qmi_location_reporting_disable(struct ofono_location_reporting *lr, ofono_location_reporting_disable_cb_t cb, void *user_data) { struct location_data *data = ofono_location_reporting_get_data(lr); struct cb_data *cbd = cb_data_new(cb, user_data); struct qmi_param *param; DBG(""); cbd->user = lr; param = qmi_param_new_uint8(QMI_PDS_PARAM_AUTO_TRACKING, 0x00); if (!param) goto error; if (qmi_service_send(data->pds, QMI_PDS_SET_AUTOTRACK, param, autotrack_disable_cb, cbd, g_free) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void set_event_cb(struct qmi_result *result, void *user_data) { struct ofono_location_reporting *lr = user_data; DBG(""); ofono_location_reporting_register(lr); } static void create_pds_cb(struct qmi_service *service, void *user_data) { struct ofono_location_reporting *lr = user_data; struct location_data *data = ofono_location_reporting_get_data(lr); struct qmi_param *param; DBG(""); if (!service) { ofono_error("Failed to request PDS service"); ofono_location_reporting_remove(lr); return; } data->pds = qmi_service_ref(service); qmi_service_register(data->pds, QMI_PDS_EVENT, event_notify, lr, NULL); qmi_service_register(data->pds, QMI_PDS_STATE_IND, state_notify, lr, NULL); param = qmi_param_new(); if (!param) goto done; qmi_param_append_uint8(param, QMI_PDS_PARAM_REPORT_NMEA, 0x01); qmi_param_append_uint8(param, QMI_PDS_PARAM_REPORT_NMEA_DEBUG, 0x00); if (qmi_service_send(data->pds, QMI_PDS_SET_EVENT, param, set_event_cb, lr, NULL) > 0) return; qmi_param_free(param); done: ofono_location_reporting_register(lr); } static int qmi_location_reporting_probe(struct ofono_location_reporting *lr, unsigned int vendor, void *user_data) { struct qmi_device *device = user_data; struct location_data *data; DBG(""); data = g_new0(struct location_data, 1); data->fd = -1; ofono_location_reporting_set_data(lr, data); qmi_service_create(device, QMI_SERVICE_PDS, create_pds_cb, lr, NULL); return 0; } static void qmi_location_reporting_remove(struct ofono_location_reporting *lr) { struct location_data *data = ofono_location_reporting_get_data(lr); DBG(""); ofono_location_reporting_set_data(lr, NULL); qmi_service_unregister_all(data->pds); qmi_service_unref(data->pds); g_free(data); } static struct ofono_location_reporting_driver driver = { .name = "qmimodem", .type = OFONO_LOCATION_REPORTING_TYPE_NMEA, .probe = qmi_location_reporting_probe, .remove = qmi_location_reporting_remove, .enable = qmi_location_reporting_enable, .disable = qmi_location_reporting_disable, }; void qmi_location_reporting_init() { ofono_location_reporting_driver_register(&driver); } void qmi_location_reporting_exit() { ofono_location_reporting_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/gprs-context.c0000644000015600001650000001573612671500024024502 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #include #include #include #include #include "qmi.h" #include "wds.h" #include "qmimodem.h" struct gprs_context_data { struct qmi_service *wds; unsigned int active_context; uint32_t pkt_handle; }; static void pkt_status_notify(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); const struct qmi_wds_notify_conn_status *status; uint16_t len; uint8_t ip_family; DBG(""); status = qmi_result_get(result, QMI_WDS_NOTIFY_CONN_STATUS, &len); if (!status) return; DBG("conn status %d", status->status); if (qmi_result_get_uint8(result, QMI_WDS_NOTIFY_IP_FAMILY, &ip_family)) DBG("ip family %d", ip_family); switch (status->status) { case QMI_WDS_CONN_STATUS_DISCONNECTED: ofono_gprs_context_deactivated(gc, data->active_context); data->active_context = 0; break; } } static void get_settings_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct ofono_modem *modem; const char *interface; uint8_t pdp_type, ip_family; DBG(""); if (qmi_result_set_error(result, NULL)) goto done; 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); done: modem = ofono_gprs_context_get_modem(gc); interface = ofono_modem_get_string(modem, "NetworkInterface"); 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) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *data = ofono_gprs_context_get_data(gc); struct ofono_modem *modem; const char *interface; uint32_t handle; DBG(""); if (qmi_result_set_error(result, NULL)) goto error; if (!qmi_result_get_uint32(result, QMI_WDS_RESULT_PKT_HANDLE, &handle)) goto error; DBG("packet handle %d", handle); data->pkt_handle = handle; if (qmi_service_send(data->wds, QMI_WDS_GET_SETTINGS, NULL, get_settings_cb, cbd, NULL) > 0) return; modem = ofono_gprs_context_get_modem(gc); interface = ofono_modem_get_string(modem, "NetworkInterface"); ofono_gprs_context_set_interface(gc, interface); CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); return; error: data->active_context = 0; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void qmi_activate_primary(struct ofono_gprs_context *gc, const struct ofono_gprs_primary_context *ctx, 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); struct qmi_param *param; uint8_t ip_family; DBG("cid %u", ctx->cid); cbd->user = gc; data->active_context = ctx->cid; switch (ctx->proto) { case OFONO_GPRS_PROTO_IP: ip_family = 4; break; case OFONO_GPRS_PROTO_IPV6: ip_family = 6; break; default: goto error; } param = qmi_param_new(); if (!param) goto error; qmi_param_append(param, QMI_WDS_PARAM_APN, strlen(ctx->apn), ctx->apn); qmi_param_append_uint8(param, QMI_WDS_PARAM_IP_FAMILY, ip_family); if (qmi_service_send(data->wds, QMI_WDS_START_NET, param, start_net_cb, cbd, NULL) > 0) return; qmi_param_free(param); error: data->active_context = 0; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void stop_net_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *data = ofono_gprs_context_get_data(gc); DBG(""); if (qmi_result_set_error(result, NULL)) { CALLBACK_WITH_FAILURE(cb, cbd->data); return; } data->active_context = 0; data->pkt_handle = 0; CALLBACK_WITH_SUCCESS(cb, cbd->data); g_free(cbd); } static void qmi_deactivate_primary(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); struct qmi_param *param; DBG("cid %u", cid); cbd->user = gc; param = qmi_param_new_uint32(QMI_WDS_PARAM_PKT_HANDLE, data->pkt_handle); if (!param) goto error; if (qmi_service_send(data->wds, QMI_WDS_STOP_NET, param, stop_net_cb, cbd, NULL) > 0) return; qmi_param_free(param); error: CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } static void create_wds_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) { ofono_error("Failed to request WDS service"); ofono_gprs_context_remove(gc); return; } data->wds = qmi_service_ref(service); qmi_service_register(data->wds, QMI_WDS_PKT_STATUS_IND, pkt_status_notify, gc, NULL); } static int qmi_gprs_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *user_data) { struct qmi_device *device = user_data; struct gprs_context_data *data; DBG(""); data = g_new0(struct gprs_context_data, 1); ofono_gprs_context_set_data(gc, data); qmi_service_create(device, QMI_SERVICE_WDS, create_wds_cb, gc, NULL); return 0; } static void qmi_gprs_context_remove(struct ofono_gprs_context *gc) { struct gprs_context_data *data = ofono_gprs_context_get_data(gc); DBG(""); ofono_gprs_context_set_data(gc, NULL); qmi_service_unregister_all(data->wds); qmi_service_unref(data->wds); g_free(data); } static struct ofono_gprs_context_driver driver = { .name = "qmimodem", .probe = qmi_gprs_context_probe, .remove = qmi_gprs_context_remove, .activate_primary = qmi_activate_primary, .deactivate_primary = qmi_deactivate_primary, }; void qmi_gprs_context_init(void) { ofono_gprs_context_driver_register(&driver); } void qmi_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qmimodem/pds.h0000644000015600001650000000324512671500024022630 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 QMI_PDS_RESET 0 /* Reset PDS service state variables */ #define QMI_PDS_EVENT 1 /* PDS report indication */ #define QMI_PDS_SET_EVENT 1 /* Set PDS report conditions */ #define QMI_PDS_GET_STATE 32 /* Return PDS service state */ #define QMI_PDS_STATE_IND 32 /* PDS service state indication */ #define QMI_PDS_GET_AUTOTRACK 48 /* Get the service auto-tracking state */ #define QMI_PDS_SET_AUTOTRACK 49 /* Set the service auto-tracking state */ /* PDS report indication */ #define QMI_PDS_NOTIFY_NMEA 0x10 /* string */ #define QMI_PDS_NOTIFY_NMEA_DEBUG 0x25 /* string */ /* Set PDS report conditions */ #define QMI_PDS_PARAM_REPORT_NMEA 0x10 /* bool */ #define QMI_PDS_PARAM_REPORT_NMEA_DEBUG 0x22 /* bool */ /* Get the service auto-tracking state */ #define QMI_PDS_RESULT_AUTO_TRACKING 0x01 /* bool */ /* Set the service auto-tracking state */ #define QMI_PDS_PARAM_AUTO_TRACKING 0x01 /* bool */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/huaweimodem/0000755000015600001650000000000012671500304022362 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/huaweimodem/voicecall.c0000644000015600001650000003031512671500024024470 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "common.h" #include "huaweimodem.h" static const char *none_prefix[] = { NULL }; struct voicecall_data { GAtChat *chat; GSList *calls; }; static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, int direction, int status, const char *num, int num_type, int clip, int id) { struct voicecall_data *d = ofono_voicecall_get_data(vc); struct ofono_call *call; /* Generate a call structure for the waiting call */ call = g_try_new(struct ofono_call, 1); if (call == NULL) return NULL; ofono_call_init(call); call->id = id; call->type = type; call->direction = direction; call->status = status; if (clip != 2) { strncpy(call->phone_number.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH); call->phone_number.type = num_type; } call->clip_validity = clip; d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare); return call; } static void huawei_generic_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_voicecall_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void huawei_template(struct ofono_voicecall *vc, const char *cmd, 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); if (g_at_chat_send(vd->chat, cmd, none_prefix, huawei_generic_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void huawei_dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, enum ofono_clir_option clir, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); char buf[256]; if (ph->type == 145) snprintf(buf, sizeof(buf), "ATD+%s", ph->number); else snprintf(buf, sizeof(buf), "ATD%s", ph->number); switch (clir) { case OFONO_CLIR_OPTION_INVOCATION: strcat(buf, "I"); break; case OFONO_CLIR_OPTION_SUPPRESSION: strcat(buf, "i"); break; default: break; } strcat(buf, ";"); huawei_template(vc, buf, cb, data); g_at_chat_send(vd->chat, "AT^DDSETEX=2", none_prefix, NULL, NULL, NULL); } static void huawei_answer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); huawei_template(vc, "ATA", cb, data); g_at_chat_send(vd->chat, "AT^DDSETEX=2", none_prefix, NULL, NULL, NULL); } static void huawei_hangup(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { /* Hangup active call */ huawei_template(vc, "AT+CHUP", cb, data); } static void huawei_release_specific(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { char buf[32]; snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id); huawei_template(vc, buf, cb, data); } static void cring_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *line; int type; int id; /* CRING can repeat, ignore if we already have an incoming call */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status)) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CRING:")) return; line = g_at_result_iter_raw_line(&iter); if (line == NULL) return; /* Ignore everything that is not voice for now */ if (!strcasecmp(line, "VOICE")) type = 0; else type = 9; id = ofono_voicecall_get_next_callid(vc); /* Generate an incoming call */ create_call(vc, type, 1, CALL_STATUS_INCOMING, NULL, 128, 2, id); /* Assume the CLIP always arrives, and we signal the call there */ DBG("%d", type); } static void clip_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *num; int type, validity; GSList *l; struct ofono_call *call; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status); if (l == NULL) { ofono_error("CLIP for unknown call"); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CLIP:")) return; if (!g_at_result_iter_next_string(&iter, &num)) return; if (!g_at_result_iter_next_number(&iter, &type)) return; if (strlen(num) > 0) validity = 0; else validity = 2; /* Skip subaddr, satype and alpha */ g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); /* If we have CLI validity field, override our guessed value */ g_at_result_iter_next_number(&iter, &validity); DBG("%s %d %d", num, type, validity); call = l->data; 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 = type; call->clip_validity = validity; if (call->type == 0) ofono_voicecall_notify(vc, call); } static void ccwa_notify(GAtResult *result, gpointer user_data) { GAtResultIter iter; const char *num; int num_type, validity, cls; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CCWA:")) return; if (!g_at_result_iter_next_string(&iter, &num)) return; if (!g_at_result_iter_next_number(&iter, &num_type)) return; if (!g_at_result_iter_next_number(&iter, &cls)) return; /* Skip alpha field */ g_at_result_iter_skip_next(&iter); if (strlen(num) > 0) validity = 0; else validity = 2; /* If we have CLI validity field, override our guessed value */ g_at_result_iter_next_number(&iter, &validity); DBG("%s %d %d %d", num, num_type, cls, validity); } static void orig_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; GAtResultIter iter; gint call_id, call_type; struct ofono_call *call; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^ORIG:")) return; if (!g_at_result_iter_next_number(&iter, &call_id)) return; if (!g_at_result_iter_next_number(&iter, &call_type)) return; ofono_info("Call origin: id %d type %d", call_id, call_type); call = create_call(vc, call_type, 0, CALL_STATUS_DIALING, NULL, 128, 2, call_id); if (call == NULL) { ofono_error("Unable to malloc, call tracking will fail!"); return; } if (call->type == 0) ofono_voicecall_notify(vc, call); } static void conf_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; gint call_id; struct ofono_call *call; GSList *l; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^CONF:")) return; if (!g_at_result_iter_next_number(&iter, &call_id)) return; ofono_info("Call setup: id %d", call_id); l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id), at_util_call_compare_by_id); if (l == NULL) { ofono_error("Received CONF for untracked call"); return; } /* Set call to alerting */ call = l->data; call->status = CALL_STATUS_ALERTING; if (call->type == 0) ofono_voicecall_notify(vc, call); } static void conn_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; gint call_id, call_type; struct ofono_call *call; GSList *l; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^CONN:")) return; if (!g_at_result_iter_next_number(&iter, &call_id)) return; if (!g_at_result_iter_next_number(&iter, &call_type)) return; ofono_info("Call connect: id %d type %d", call_id, call_type); l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id), at_util_call_compare_by_id); if (l == NULL) { ofono_error("Received CONN for untracked call"); return; } /* Set call to active */ call = l->data; call->status = CALL_STATUS_ACTIVE; if (call->type == 0) ofono_voicecall_notify(vc, call); } static void cend_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; gint call_id, duration, end_status, cc_pause; struct ofono_call *call; GSList *l; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^CEND:")) return; if (!g_at_result_iter_next_number(&iter, &call_id)) return; if (!g_at_result_iter_next_number(&iter, &duration)) return; if (!g_at_result_iter_next_number(&iter, &end_status)) return; /* parameter is not present on errors */ g_at_result_iter_next_number(&iter, &cc_pause); ofono_info("Call end: id %d duration %ds status %d", call_id, duration, end_status); l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id), at_util_call_compare_by_id); if (l == NULL) { ofono_error("Received CEND for untracked call"); return; } call = l->data; if (call->type == 0) ofono_voicecall_disconnected(vc, call->id, OFONO_DISCONNECT_REASON_UNKNOWN, NULL); vd->calls = g_slist_remove(vd->calls, call); g_free(call); } static void huawei_voicecall_initialized(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); DBG("registering to notifications"); g_at_chat_register(vd->chat, "+CRING:", cring_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "^ORIG:", orig_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "^CONF:", conf_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "^CONN:", conn_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "^CEND:", cend_notify, FALSE, vc, NULL); ofono_voicecall_register(vc); } static int huawei_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, void *data) { GAtChat *chat = data; struct voicecall_data *vd; vd = g_try_new0(struct voicecall_data, 1); if (vd == NULL) return -ENOMEM; vd->chat = g_at_chat_clone(chat); ofono_voicecall_set_data(vc, vd); g_at_chat_send(vd->chat, "AT+CRC=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CLIP=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+COLP=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CCWA=1", none_prefix, huawei_voicecall_initialized, vc, NULL); return 0; } static void huawei_voicecall_remove(struct ofono_voicecall *vc) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); ofono_voicecall_set_data(vc, NULL); g_at_chat_unref(vd->chat); g_free(vd); } static struct ofono_voicecall_driver driver = { .name = "huaweimodem", .probe = huawei_voicecall_probe, .remove = huawei_voicecall_remove, .dial = huawei_dial, .answer = huawei_answer, .hangup_active = huawei_hangup, .release_specific = huawei_release_specific, }; void huawei_voicecall_init(void) { ofono_voicecall_driver_register(&driver); } void huawei_voicecall_exit(void) { ofono_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/huaweimodem/audio-settings.c0000644000015600001650000000621412671500024025467 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "huaweimodem.h" static const char *cvoice_prefix[] = { "^CVOICE:", NULL }; struct audio_settings_data { GAtChat *chat; }; static void cring_notify(GAtResult *result, gpointer user_data) { struct ofono_audio_settings *as = user_data; ofono_audio_settings_active_notify(as, TRUE); } static void orig_notify(GAtResult *result, gpointer user_data) { struct ofono_audio_settings *as = user_data; ofono_audio_settings_active_notify(as, TRUE); } static void cend_notify(GAtResult *result, gpointer user_data) { struct ofono_audio_settings *as = user_data; ofono_audio_settings_active_notify(as, FALSE); } static void cvoice_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_audio_settings *as = user_data; struct audio_settings_data *asd = ofono_audio_settings_get_data(as); if (!ok) return; g_at_chat_register(asd->chat, "+CRING:", cring_notify, FALSE, as, NULL); g_at_chat_register(asd->chat, "^ORIG:", orig_notify, FALSE, as, NULL); g_at_chat_register(asd->chat, "^CEND:", cend_notify, FALSE, as, NULL); ofono_audio_settings_register(as); } static int huawei_audio_settings_probe(struct ofono_audio_settings *as, unsigned int vendor, void *data) { GAtChat *chat = data; struct audio_settings_data *asd; asd = g_try_new0(struct audio_settings_data, 1); if (asd == NULL) return -ENOMEM; asd->chat = g_at_chat_clone(chat); ofono_audio_settings_set_data(as, asd); g_at_chat_send(asd->chat, "AT^CVOICE=?", cvoice_prefix, cvoice_support_cb, as, NULL); return 0; } static void huawei_audio_settings_remove(struct ofono_audio_settings *as) { struct audio_settings_data *asd = ofono_audio_settings_get_data(as); ofono_audio_settings_set_data(as, NULL); g_at_chat_unref(asd->chat); g_free(asd); } static struct ofono_audio_settings_driver driver = { .name = "huaweimodem", .probe = huawei_audio_settings_probe, .remove = huawei_audio_settings_remove, }; void huawei_audio_settings_init(void) { ofono_audio_settings_driver_register(&driver); } void huawei_audio_settings_exit(void) { ofono_audio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/huaweimodem/huaweimodem.c0000644000015600001650000000301412671500024025027 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "huaweimodem.h" static int huaweimodem_init(void) { huawei_ussd_init(); huawei_voicecall_init(); huawei_audio_settings_init(); huawei_radio_settings_init(); huawei_gprs_context_init(); huawei_cdma_netreg_init(); return 0; } static void huaweimodem_exit(void) { huawei_cdma_netreg_exit(); huawei_gprs_context_exit(); huawei_radio_settings_exit(); huawei_audio_settings_exit(); huawei_voicecall_exit(); huawei_ussd_exit(); } OFONO_PLUGIN_DEFINE(huaweimodem, "Huawei modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, huaweimodem_init, huaweimodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/huaweimodem/huaweimodem.h0000644000015600001650000000247012671500024025041 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void huawei_ussd_init(void); extern void huawei_ussd_exit(void); extern void huawei_voicecall_init(void); extern void huawei_voicecall_exit(void); extern void huawei_audio_settings_init(void); extern void huawei_audio_settings_exit(void); extern void huawei_radio_settings_init(void); extern void huawei_radio_settings_exit(void); extern void huawei_gprs_context_init(void); extern void huawei_gprs_context_exit(void); extern void huawei_cdma_netreg_init(void); extern void huawei_cdma_netreg_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/huaweimodem/radio-settings.c0000644000015600001650000002362112671500024025465 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "huaweimodem.h" static const char *none_prefix[] = { NULL }; static const char *syscfg_prefix[] = { "^SYSCFG:", NULL }; #define HUAWEI_BAND_ANY 0x3FFFFFFF struct radio_settings_data { GAtChat *chat; }; static const struct huawei_band_gsm_table { enum ofono_radio_band_gsm band_gsm; unsigned int band_huawei; } huawei_band_gsm_table[] = { { OFONO_RADIO_BAND_GSM_ANY, 0x80000 | 0x200 | 0x100 | 0x80 | 0x200000 }, { OFONO_RADIO_BAND_GSM_850, 0x80000 }, { OFONO_RADIO_BAND_GSM_900P, 0x200 }, { OFONO_RADIO_BAND_GSM_900E, 0x100 }, { OFONO_RADIO_BAND_GSM_1800, 0x80 }, { OFONO_RADIO_BAND_GSM_1900, 0x200000 }, }; static const struct huawei_band_umts_table { enum ofono_radio_band_umts band_umts; unsigned int band_huawei; } huawei_band_umts_table[] = { { OFONO_RADIO_BAND_UMTS_ANY, 0x4000000 | 0x20000 | 800000 | 400000 }, { OFONO_RADIO_BAND_UMTS_850, 0x4000000 }, { OFONO_RADIO_BAND_UMTS_900, 0x20000 }, { OFONO_RADIO_BAND_UMTS_1900, 0x800000 }, { OFONO_RADIO_BAND_UMTS_2100, 0x400000 }, }; #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static unsigned int band_gsm_to_huawei(enum ofono_radio_band_gsm band) { size_t i; for (i = 0; i < ARRAY_SIZE(huawei_band_gsm_table); i++) { if (huawei_band_gsm_table[i].band_gsm == band) return huawei_band_gsm_table[i].band_huawei; } return 0; } static unsigned int band_umts_to_huawei(enum ofono_radio_band_umts band) { size_t i; for (i = 0; i < ARRAY_SIZE(huawei_band_umts_table); i++) { if (huawei_band_umts_table[i].band_umts == band) return huawei_band_umts_table[i].band_huawei; } return 0; } static enum ofono_radio_band_gsm band_gsm_from_huawei(unsigned int band) { size_t i; if (band == HUAWEI_BAND_ANY) return OFONO_RADIO_BAND_UMTS_ANY; for (i = ARRAY_SIZE(huawei_band_gsm_table) - 1; i > 0; i--) { if (huawei_band_gsm_table[i].band_huawei & band) return huawei_band_gsm_table[i].band_gsm; } return OFONO_RADIO_BAND_GSM_ANY; } static enum ofono_radio_band_umts band_umts_from_huawei(unsigned int band) { size_t i; if (band == HUAWEI_BAND_ANY) return OFONO_RADIO_BAND_UMTS_ANY; for (i = ARRAY_SIZE(huawei_band_umts_table) - 1; i > 0; i--) { if (huawei_band_umts_table[i].band_huawei & band) return huawei_band_umts_table[i].band_umts; } return OFONO_RADIO_BAND_UMTS_ANY; } static void syscfg_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; int value; 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, "^SYSCFG:") == FALSE) goto error; if (g_at_result_iter_next_number(&iter, &value) == FALSE) goto error; switch (value) { case 2: mode = OFONO_RADIO_ACCESS_MODE_ANY; break; case 13: mode = OFONO_RADIO_ACCESS_MODE_GSM; break; case 14: mode = OFONO_RADIO_ACCESS_MODE_UMTS; break; default: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } 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); } } static void syscfg_modify_mode_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } 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); struct cb_data *cbd = cb_data_new(cb, data); char buf[40]; unsigned int value = 2, acq_order = 0; switch (mode) { case OFONO_RADIO_ACCESS_MODE_ANY: value = 2; acq_order = 0; break; case OFONO_RADIO_ACCESS_MODE_GSM: value = 13; acq_order = 1; break; case OFONO_RADIO_ACCESS_MODE_UMTS: value = 14; acq_order = 2; break; case OFONO_RADIO_ACCESS_MODE_LTE: goto error; } snprintf(buf, sizeof(buf), "AT^SYSCFG=%u,%u,40000000,2,4", value, acq_order); if (g_at_chat_send(rsd->chat, buf, none_prefix, syscfg_modify_mode_cb, cbd, g_free) > 0) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void syscfg_modify_band_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_radio_settings_band_set_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } 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); struct cb_data *cbd = cb_data_new(cb, data); char buf[40]; 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), "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) return; error: CALLBACK_WITH_FAILURE(cb, data); g_free(cbd); } static void syscfg_query_band_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_radio_settings_band_query_cb_t cb = cbd->cb; enum ofono_radio_band_gsm band_gsm; enum ofono_radio_band_umts band_umts; struct ofono_error error; GAtResultIter iter; unsigned int band; const char *band_str; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, -1, -1, cbd->data); return; } g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "^SYSCFG:") == FALSE) goto error; if (g_at_result_iter_skip_next(&iter) == FALSE) goto error; if (g_at_result_iter_skip_next(&iter) == FALSE) goto error; if(g_at_result_iter_next_unquoted_string(&iter, &band_str) == FALSE) goto error; sscanf((const char *) band_str, "%x", &band); band_gsm = band_gsm_from_huawei(band); band_umts = band_umts_from_huawei(band); cb(&error, band_gsm, band_umts, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, -1, -1, cbd->data); } static void huawei_query_band(struct ofono_radio_settings *rs, ofono_radio_settings_band_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_band_cb, cbd, g_free) == 0) { CALLBACK_WITH_FAILURE(cb, -1, -1, data); g_free(cbd); } } static void syscfg_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_radio_settings *rs = user_data; if (!ok) { ofono_radio_settings_remove(rs); return; } ofono_radio_settings_register(rs); } static int huawei_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *data) { GAtChat *chat = data; struct radio_settings_data *rsd; rsd = g_try_new0(struct radio_settings_data, 1); if (rsd == NULL) return -ENOMEM; rsd->chat = g_at_chat_clone(chat); ofono_radio_settings_set_data(rs, rsd); g_at_chat_send(rsd->chat, "AT^SYSCFG=?", syscfg_prefix, syscfg_support_cb, rs, NULL); return 0; } static void huawei_radio_settings_remove(struct ofono_radio_settings *rs) { struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs); ofono_radio_settings_set_data(rs, NULL); g_at_chat_unref(rsd->chat); g_free(rsd); } static struct ofono_radio_settings_driver driver = { .name = "huaweimodem", .probe = huawei_radio_settings_probe, .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, }; void huawei_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void huawei_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/huaweimodem/ussd.c0000644000015600001650000001176612671500024023516 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include "util.h" #include "gatchat.h" #include "gatresult.h" #include "huaweimodem.h" static const char *cusd_prefix[] = { "+CUSD:", NULL }; static const char *none_prefix[] = { NULL }; struct ussd_data { GAtChat *chat; }; static void cusd_parse(GAtResult *result, struct ofono_ussd *ussd) { GAtResultIter iter; int status, dcs; const char *content; unsigned char msg[160]; const unsigned char *msg_ptr = NULL; long msg_len; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CUSD:")) return; if (!g_at_result_iter_next_number(&iter, &status)) return; if (!g_at_result_iter_next_string(&iter, &content)) goto out; if (!g_at_result_iter_next_number(&iter, &dcs)) dcs = 0; msg_ptr = decode_hex_own_buf(content, -1, &msg_len, 0, msg); out: ofono_ussd_notify(ussd, status, dcs, msg_ptr, msg_ptr ? msg_len : 0); } static void cusd_request_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_ussd_cb_t cb = cbd->cb; struct ofono_ussd *ussd = cbd->user; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); cusd_parse(result, ussd); } static void huawei_ussd_request(struct ofono_ussd *ussd, int dcs, const unsigned char *pdu, int len, ofono_ussd_cb_t cb, void *user_data) { struct ussd_data *data = ofono_ussd_get_data(ussd); struct cb_data *cbd = cb_data_new(cb, user_data); char buf[512], coded_buf[321]; char *converted; cbd->user = ussd; converted = encode_hex_own_buf(pdu, len, 0, coded_buf); if (converted == NULL) goto error; snprintf(buf, sizeof(buf), "AT+CUSD=1,\"%s\",%d", converted, dcs); if (g_at_chat_send(data->chat, buf, cusd_prefix, cusd_request_cb, cbd, g_free) > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } static void cusd_cancel_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_ussd_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); /* * All errors and notifications arrive unexpected and * thus just reset the state here. This is safer than * getting stuck in a dead-lock. */ error.type = OFONO_ERROR_TYPE_NO_ERROR; error.error = 0; cb(&error, cbd->data); } static void huawei_ussd_cancel(struct ofono_ussd *ussd, ofono_ussd_cb_t cb, void *user_data) { struct ussd_data *data = ofono_ussd_get_data(ussd); struct cb_data *cbd = cb_data_new(cb, user_data); cbd->user = data; if (g_at_chat_send(data->chat, "AT+CUSD=2", none_prefix, cusd_cancel_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, user_data); } static void cusd_notify(GAtResult *result, gpointer user_data) { struct ofono_ussd *ussd = user_data; cusd_parse(result, ussd); } static void cusd_register(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_ussd *ussd = user_data; struct ussd_data *data = ofono_ussd_get_data(ussd); if (!ok) { ofono_error("Could not enable CUSD notifications"); return; } g_at_chat_register(data->chat, "+CUSD:", cusd_notify, FALSE, ussd, NULL); ofono_ussd_register(ussd); } static int huawei_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor, void *user) { GAtChat *chat = user; struct ussd_data *data; data = g_try_new0(struct ussd_data, 1); if (data == NULL) return -ENOMEM; data->chat = g_at_chat_clone(chat); ofono_ussd_set_data(ussd, data); g_at_chat_send(data->chat, "AT+CUSD=1", none_prefix, cusd_register, ussd, NULL); return 0; } static void huawei_ussd_remove(struct ofono_ussd *ussd) { struct ussd_data *data = ofono_ussd_get_data(ussd); ofono_ussd_set_data(ussd, NULL); g_at_chat_unref(data->chat); g_free(data); } static struct ofono_ussd_driver driver = { .name = "huaweimodem", .probe = huawei_ussd_probe, .remove = huawei_ussd_remove, .request = huawei_ussd_request, .cancel = huawei_ussd_cancel, }; void huawei_ussd_init(void) { ofono_ussd_driver_register(&driver); } void huawei_ussd_exit(void) { ofono_ussd_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/huaweimodem/cdma-netreg.c0000644000015600001650000001212212671500024024711 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include "gatchat.h" #include "huaweimodem.h" static const char *sysinfo_prefix[] = { "^SYSINFO:", NULL }; static gboolean parse_sysinfo(GAtResult *result, gint *status) { GAtResultIter iter; gint srv_status; gint srv_domain; gint roaming_status; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^SYSINFO:")) return FALSE; if (!g_at_result_iter_next_number(&iter, &srv_status)) return FALSE; if (!g_at_result_iter_next_number(&iter, &srv_domain)) return FALSE; if (!g_at_result_iter_next_number(&iter, &roaming_status)) return FALSE; DBG("%d, %d, %d", srv_status, srv_domain, roaming_status); switch (srv_status) { case 1: /* Restricted service */ case 2: /* Service valid */ case 3: /* Restricted region service */ if (roaming_status) *status = CDMA_NETWORK_REGISTRATION_STATUS_ROAMING; else *status = CDMA_NETWORK_REGISTRATION_STATUS_REGISTERED; break; case 0: /* No service */ case 4: /* Not registered */ default: *status = CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED; break; } switch (srv_domain) { case 0: /* No service */ *status = CDMA_NETWORK_REGISTRATION_STATUS_NOT_REGISTERED; break; case 1: /* Only CS */ case 2: /* Only PS */ case 3: /* CS PS */ case 4: /* CS registered, PS in searching state */ case 255: /* CDMA not supported */ break; } return TRUE; } static void sysinfo_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_cdma_netreg *netreg = user_data; int status; if (!ok) return; if (parse_sysinfo(result, &status) == FALSE) { ofono_error("Invalid SYSINFO values"); return; } ofono_cdma_netreg_status_notify(netreg, status); } static void mode_notify(GAtResult *result, gpointer user_data) { struct ofono_cdma_netreg *netreg = user_data; GAtChat *chat = ofono_cdma_netreg_get_data(netreg); g_at_chat_send(chat, "AT^SYSINFO", sysinfo_prefix, sysinfo_cb, netreg, NULL); } static void rssilvl_notify(GAtResult *result, gpointer user_data) { struct ofono_cdma_netreg *netreg = user_data; int strength; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^RSSILVL:")) goto error; if (!g_at_result_iter_next_number(&iter, &strength)) goto error; if (strength == 99) strength = 100; ofono_cdma_netreg_strength_notify(netreg, strength); return; error: ofono_error("Invalid RSSILVL value"); } static void hrssilvl_notify(GAtResult *result, gpointer user_data) { struct ofono_cdma_netreg *netreg = user_data; int strength; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "^HRSSILVL:")) goto error; if (!g_at_result_iter_next_number(&iter, &strength)) goto error; if (strength == 99) strength = 100; ofono_cdma_netreg_data_strength_notify(netreg, strength); return; error: ofono_error("Invalid HRSSILVL value"); } static void probe_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_cdma_netreg *netreg = user_data; GAtChat *chat = ofono_cdma_netreg_get_data(netreg); if (!ok) { ofono_cdma_netreg_remove(netreg); return; } g_at_chat_register(chat, "^MODE:", mode_notify, FALSE, netreg, NULL); g_at_chat_register(chat, "^RSSILVL:", rssilvl_notify, FALSE, netreg, NULL); g_at_chat_register(chat, "^HRSSILVL:", hrssilvl_notify, FALSE, netreg, NULL); ofono_cdma_netreg_register(netreg); } static int huawei_cdma_netreg_probe(struct ofono_cdma_netreg *netreg, unsigned int vendor, void *data) { GAtChat *chat = g_at_chat_clone(data); ofono_cdma_netreg_set_data(netreg, chat); g_at_chat_send(chat, "AT^SYSINFO", sysinfo_prefix, probe_cb, netreg, NULL); return 0; } static void huawei_cdma_netreg_remove(struct ofono_cdma_netreg *netreg) { GAtChat *chat = ofono_cdma_netreg_get_data(netreg); ofono_cdma_netreg_set_data(netreg, NULL); g_at_chat_unref(chat); } static struct ofono_cdma_netreg_driver driver = { .name = "huaweimodem", .probe = huawei_cdma_netreg_probe, .remove = huawei_cdma_netreg_remove, }; void huawei_cdma_netreg_init(void) { ofono_cdma_netreg_driver_register(&driver); } void huawei_cdma_netreg_exit(void) { ofono_cdma_netreg_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/huaweimodem/gprs-context.c0000644000015600001650000002003412671500024025161 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "gattty.h" #include "huaweimodem.h" static const char *none_prefix[] = { NULL }; static const char *dhcp_prefix[] = { "^DHCP:", NULL }; struct gprs_context_data { GAtChat *chat; unsigned int active_context; unsigned int dhcp_source; unsigned int dhcp_count; guint ndis_watch; ofono_gprs_context_cb_t cb; void *cb_data; /* Callback data */ }; static void check_dhcp(struct ofono_gprs_context *gc); static gboolean dhcp_poll(gpointer user_data) { struct ofono_gprs_context *gc = user_data; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); if (gcd->dhcp_count > 20) CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data); else check_dhcp(gc); gcd->dhcp_count++; gcd->dhcp_source = 0; return FALSE; } static gboolean get_next_addr(GAtResultIter *iter, char **addr) { const char *str; guint32 val; if (g_at_result_iter_next_unquoted_string(iter, &str) == FALSE) return FALSE; val = strtol(str, NULL, 16); if (addr) *addr = g_strdup_printf("%u.%u.%u.%u", (val & 0x000000ff), (val & 0x0000ff00) >> 8, (val & 0x00ff0000) >> 16, (val & 0xff000000) >> 24); return TRUE; } static void dhcp_query_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; struct ofono_modem *modem; const char *interface; char *ip = NULL; char *netmask = NULL; char *gateway = NULL; char *dns1 = NULL; char *dns2 = NULL; const char *dns[3]; DBG("ok %d", ok); if (!ok) { gcd->dhcp_source = g_timeout_add_seconds(1, dhcp_poll, gc); return; } g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "^DHCP:") == FALSE) return; get_next_addr(&iter, &ip); get_next_addr(&iter, &netmask); get_next_addr(&iter, &gateway); get_next_addr(&iter, NULL); get_next_addr(&iter, &dns1); get_next_addr(&iter, &dns2); dns[0] = dns1; dns[1] = dns2; dns[2] = 0; ofono_info("Got the following parameters for context: %d", gcd->active_context); ofono_info("IP: %s Gateway: %s", ip, gateway); ofono_info("DNS: %s, %s", dns1, dns2); modem = ofono_gprs_context_get_modem(gc); interface = ofono_modem_get_string(modem, "NetworkInterface"); ofono_gprs_context_set_interface(gc, interface); ofono_gprs_context_set_ipv4_address(gc, ip, TRUE); ofono_gprs_context_set_ipv4_netmask(gc, netmask); ofono_gprs_context_set_ipv4_gateway(gc, gateway); ofono_gprs_context_set_ipv4_dns_servers(gc, dns); CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); gcd->cb = NULL; gcd->cb_data = NULL; g_free(ip); g_free(netmask); g_free(gateway); g_free(dns1); g_free(dns2); } static void check_dhcp(struct ofono_gprs_context *gc) { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); g_at_chat_send(gcd->chat, "AT^DHCP?", dhcp_prefix, dhcp_query_cb, gc, NULL); } static void at_ndisdup_down_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; DBG("ok %d", ok); if (ok) { gcd->cb = cb; gcd->cb_data = cbd->data; if (gcd->ndis_watch > 0) { g_source_remove(gcd->ndis_watch); gcd->ndis_watch = 0; } } decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_ndisdup_up_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct ofono_error error; DBG("ok %d", ok); if (ok) { gcd->cb = cb; gcd->cb_data = cbd->data; gcd->dhcp_count = 0; check_dhcp(gc); return; } gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_gprs_context_cb_t cb = cbd->cb; struct ofono_gprs_context *gc = cbd->user; struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *ncbd; char buf[64]; DBG("ok %d", ok); if (!ok) { struct ofono_error error; gcd->active_context = 0; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); return; } ncbd = g_memdup(cbd, sizeof(struct cb_data)); snprintf(buf, sizeof(buf), "AT^NDISDUP=%u,1", gcd->active_context); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_ndisdup_up_cb, ncbd, g_free) > 0) return; g_free(ncbd); gcd->active_context = 0; CALLBACK_WITH_FAILURE(cb, cbd->data); } static void huawei_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 cb_data *cbd = cb_data_new(cb, data); char buf[64]; int len; /* IPv6 support not implemented */ if (ctx->proto != OFONO_GPRS_PROTO_IP) goto error; DBG("cid %u", ctx->cid); gcd->active_context = ctx->cid; cbd->user = gc; len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); if (ctx->apn) snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_cgdcont_cb, cbd, g_free) > 0) return; error: g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void huawei_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 cb_data *cbd = cb_data_new(cb, data); char buf[128]; DBG("cid %u", cid); cbd->user = gc; snprintf(buf, sizeof(buf), "AT^NDISDUP=%u,0", cid); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_ndisdup_down_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static int huawei_gprs_context_probe(struct ofono_gprs_context *gc, unsigned int vendor, void *data) { GAtChat *chat = data; struct gprs_context_data *gcd; DBG(""); gcd = g_try_new0(struct gprs_context_data, 1); if (gcd == NULL) return -ENOMEM; gcd->chat = g_at_chat_clone(chat); ofono_gprs_context_set_data(gc, gcd); return 0; } static void huawei_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); g_at_chat_unref(gcd->chat); g_free(gcd); } static struct ofono_gprs_context_driver driver = { .name = "huaweimodem", .probe = huawei_gprs_context_probe, .remove = huawei_gprs_context_remove, .activate_primary = huawei_gprs_activate_primary, .deactivate_primary = huawei_gprs_deactivate_primary, }; void huawei_gprs_context_init(void) { ofono_gprs_context_driver_register(&driver); } void huawei_gprs_context_exit(void) { ofono_gprs_context_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/calypsomodem/0000755000015600001650000000000012671500304022552 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/calypsomodem/calypsomodem.h0000644000015600001650000000172012671500024025416 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void calypso_voicecall_init(void); extern void calypso_voicecall_exit(void); extern void calypso_stk_init(void); extern void calypso_stk_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/calypsomodem/calypsomodem.c0000644000015600001650000000244512671500024025416 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "calypsomodem.h" static int calypsomodem_init(void) { calypso_voicecall_init(); calypso_stk_init(); return 0; } static void calypsomodem_exit(void) { calypso_stk_exit(); calypso_voicecall_exit(); } OFONO_PLUGIN_DEFINE(calypsomodem, "Calypso modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, calypsomodem_init, calypsomodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/calypsomodem/voicecall.c0000644000015600001650000002316412671500024024664 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "calypsomodem.h" static const char *none_prefix[] = { NULL }; struct voicecall_data { GAtChat *chat; }; static void calypso_generic_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_voicecall_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void calypso_template(struct ofono_voicecall *vc, const char *cmd, 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); if (g_at_chat_send(vd->chat, cmd, none_prefix, calypso_generic_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void calypso_dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, enum ofono_clir_option clir, ofono_voicecall_cb_t cb, void *data) { char buf[256]; if (ph->type == 145) snprintf(buf, sizeof(buf), "ATD+%s", ph->number); else snprintf(buf, sizeof(buf), "ATD%s", ph->number); switch (clir) { case OFONO_CLIR_OPTION_INVOCATION: strcat(buf, "I"); break; case OFONO_CLIR_OPTION_SUPPRESSION: strcat(buf, "i"); break; default: break; } strcat(buf, ";"); calypso_template(vc, buf, cb, data); } static void calypso_answer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { calypso_template(vc, "ATA", cb, data); } static void calypso_ath(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { calypso_template(vc, "ATH", cb, data); } static void calypso_chup(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { calypso_template(vc, "AT+CHUP", cb, data); } static void calypso_hold_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { calypso_template(vc, "AT+CHLD=2", cb, data); } static void calypso_release_all_held(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { calypso_template(vc, "AT+CHLD=0", cb, data); } static void calypso_set_udub(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { calypso_template(vc, "AT+CHLD=0", cb, data); } static void calypso_release_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { calypso_template(vc, "AT+CHLD=1", cb, data); } static void calypso_release_specific(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { char buf[32]; /* On calypso, 1X only releases active calls, while 7X releases * active or held calls */ snprintf(buf, sizeof(buf), "AT%%CHLD=7%d", id); calypso_template(vc, buf, cb, data); } static void calypso_private_chat(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { char buf[32]; snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id); calypso_template(vc, buf, cb, data); } static void calypso_create_multiparty(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { calypso_template(vc, "AT+CHLD=3", cb, data); } static void calypso_transfer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { calypso_template(vc, "AT+CHLD=4", cb, data); } static void calypso_deflect(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, ofono_voicecall_cb_t cb, void *data) { char buf[128]; snprintf(buf, sizeof(buf), "AT+CTFR=%s,%d", ph->number, ph->type); calypso_template(vc, buf, cb, data); } static void calypso_send_dtmf(struct ofono_voicecall *vc, const char *dtmf, ofono_voicecall_cb_t cb, void *data) { int len = strlen(dtmf); int s; int i; char *buf; /* strlen("+VTS=\"T\";") = 9 + initial AT + null */ buf = g_try_new(char, len * 9 + 3); if (buf == NULL) { CALLBACK_WITH_FAILURE(cb, data); return; } s = sprintf(buf, "AT+VTS=%c", dtmf[0]); for (i = 1; i < len; i++) s += sprintf(buf + s, ";+VTS=%c", dtmf[i]); calypso_template(vc, buf, cb, data); g_free(buf); } static void cpi_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; int id; int msgtype; int direction; int mode; const char *num; int type; int cause; int line = 0; int validity; struct ofono_call call; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "%CPI:")) return; if (!g_at_result_iter_next_number(&iter, &id)) return; /* msgtype * 0 - setup * 1 - disconnect * 2 - alert * 3 - call proceed * 4 - sync * 5 - progress * 6 - connected * 7 - release * 8 - reject * 9 - request (MO Setup) * 10 - hold */ if (!g_at_result_iter_next_number(&iter, &msgtype)) return; /* Skip in-band ring tone notification */ if (!g_at_result_iter_skip_next(&iter)) return; /* Skip traffic channel assignment */ if (!g_at_result_iter_skip_next(&iter)) return; if (!g_at_result_iter_next_number(&iter, &direction)) return; if (!g_at_result_iter_next_number(&iter, &mode)) return; DBG("id:%d, msgtype:%d, direction:%d, mode:%d", id, msgtype, direction, mode); if (!g_at_result_iter_next_string(&iter, &num)) return; if (strlen(num) > 0) { DBG("Len > 0"); validity = 0; if (!g_at_result_iter_next_number(&iter, &type)) return; DBG("type obtained"); } else { DBG("skip next"); validity = 2; type = 129; if (!g_at_result_iter_skip_next(&iter)) return; DBG("skipped"); } DBG("num:%s, type:%d", num, type); /* Skip alpha field */ if (!g_at_result_iter_skip_next(&iter)) return; g_at_result_iter_next_number(&iter, &cause); g_at_result_iter_next_number(&iter, &line); DBG("cause:%d, line:%d", cause, line); /* We only care about voice calls here */ if (mode != 0) return; if (line != 0) { ofono_error("Alternate Line service not yet handled"); return; } /* Need to send this on the calypso hardware to avoid echo issues */ if (msgtype == 3 || msgtype == 4) g_at_chat_send(vd->chat, "AT%N0187", none_prefix, NULL, NULL, NULL); ofono_call_init(&call); switch (msgtype) { case 0: /* Set call status to incoming */ call.status = 4; break; case 2: /* Set call status to alerting */ call.status = 3; break; case 3: case 9: /* Set call status to dialing */ call.status = 2; break; case 6: /* Set call status to connected */ call.status = 0; break; case 10: /* Set call status to held */ call.status = 1; break; case 1: case 8: ofono_voicecall_disconnected(vc, id, OFONO_DISCONNECT_REASON_UNKNOWN, NULL); return; default: return; }; call.id = id; call.type = mode; call.direction = direction; 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 = type; call.clip_validity = validity; ofono_voicecall_notify(vc, &call); } static void calypso_voicecall_initialized(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); DBG("voicecall_init: registering to notifications"); g_at_chat_register(vd->chat, "%CPI:", cpi_notify, FALSE, vc, NULL); ofono_voicecall_register(vc); } static int calypso_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, void *data) { GAtChat *chat = data; struct voicecall_data *vd; vd = g_try_new0(struct voicecall_data, 1); if (vd == NULL) return -ENOMEM; vd->chat = g_at_chat_clone(chat); ofono_voicecall_set_data(vc, vd); g_at_chat_send(vd->chat, "AT%CPI=3", NULL, calypso_voicecall_initialized, vc, NULL); return 0; } static void calypso_voicecall_remove(struct ofono_voicecall *vc) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); ofono_voicecall_set_data(vc, NULL); g_at_chat_unref(vd->chat); g_free(vd); } static struct ofono_voicecall_driver driver = { .name = "calypsomodem", .probe = calypso_voicecall_probe, .remove = calypso_voicecall_remove, .dial = calypso_dial, .answer = calypso_answer, .hangup_all = calypso_ath, .hangup_active = calypso_chup, .hold_all_active = calypso_hold_all_active, .release_all_held = calypso_release_all_held, .set_udub = calypso_set_udub, .release_all_active = calypso_release_all_active, .release_specific = calypso_release_specific, .private_chat = calypso_private_chat, .create_multiparty = calypso_create_multiparty, .transfer = calypso_transfer, .deflect = calypso_deflect, .swap_without_accept = NULL, .send_tones = calypso_send_dtmf }; void calypso_voicecall_init(void) { ofono_voicecall_driver_register(&driver); } void calypso_voicecall_exit(void) { ofono_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/calypsomodem/stk.c0000644000015600001650000001520412671500024023520 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "calypsomodem.h" struct stk_data { GAtChat *chat; }; static const char *none_prefix[] = { NULL }; static const char *sate_prefix[] = { "%SATE:", NULL }; static void sate_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_stk_envelope_cb_t cb = cbd->cb; GAtResultIter iter; struct ofono_error error; const guint8 *pdu = NULL; gint len = 0; DBG(""); decode_at_error(&error, g_at_result_final_response(result)); /* * Ignore errors "SIM memory failure" and "Unknown error", seem * to be generated for no reason. */ if (!ok && error.type == OFONO_ERROR_TYPE_CMS && error.error == 320) { ok = TRUE; error.type = OFONO_ERROR_TYPE_NO_ERROR; } if (!ok && error.type == OFONO_ERROR_TYPE_CME && error.error == 100) { ok = TRUE; error.type = OFONO_ERROR_TYPE_NO_ERROR; } if (!ok) goto done; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "%SATE:") == FALSE) goto done; /* Response data is optional */ g_at_result_iter_next_hexstring(&iter, &pdu, &len); DBG("len %d", len); done: cb(&error, pdu, len, cbd->data); } static void calypso_stk_envelope(struct ofono_stk *stk, int length, const unsigned char *command, ofono_stk_envelope_cb_t cb, void *data) { struct stk_data *sd = ofono_stk_get_data(stk); struct cb_data *cbd = cb_data_new(cb, data); char *buf = g_try_new(char, 64 + length * 2); int len; DBG(""); if (buf == NULL) goto error; len = sprintf(buf, "AT%%SATE=\""); for (; length; length--) len += sprintf(buf + len, "%02hhX", *command++); len += sprintf(buf + len, "\""); DBG("%s", buf); if (g_at_chat_send(sd->chat, buf, sate_prefix, sate_cb, cbd, g_free) > 0) { g_free(buf); return; } error: g_free(buf); g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, 0, data); } static void satr_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_stk_generic_cb_t cb = cbd->cb; struct ofono_error error; DBG(""); decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void calypso_stk_terminal_response(struct ofono_stk *stk, int length, const unsigned char *command, ofono_stk_generic_cb_t cb, void *data) { struct stk_data *sd = ofono_stk_get_data(stk); struct cb_data *cbd = cb_data_new(cb, data); char *buf = g_try_new(char, 64 + length * 2); int len; DBG(""); if (buf == NULL) goto error; len = sprintf(buf, "AT%%SATR=\""); for (; length; length--) len += sprintf(buf + len, "%02hhX", *command++); len += sprintf(buf + len, "\""); DBG("%s", buf); if (g_at_chat_send(sd->chat, buf, none_prefix, satr_cb, cbd, g_free) > 0) { g_free(buf); return; } error: g_free(buf); g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void sati_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; GAtResultIter iter; const guint8 *pdu; gint len; gboolean ret; DBG(""); g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "%SATI:")) return; ret = g_at_result_iter_next_hexstring(&iter, &pdu, &len); if (!ret || len == 0) { /* * An empty notification is a End Session notification on * the part of the UICC. */ ofono_stk_proactive_session_end_notify(stk); return; } ofono_stk_proactive_command_notify(stk, len, pdu); } static void sata_notify(GAtResult *result, gpointer user_data) { DBG(""); /* TODO: Pending call alert */ } static void satn_notify(GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; GAtResultIter iter; const guint8 *pdu; gint len; DBG(""); /* Proactive command has been handled by the modem. */ g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, "%SATN:") == FALSE) return; if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE) return; if (len == 0) return; ofono_stk_proactive_command_handled_notify(stk, len, pdu); } static void calypso_stk_register(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_stk *stk = user_data; struct stk_data *sd = ofono_stk_get_data(stk); DBG(""); if (!ok) return; g_at_chat_register(sd->chat, "%SATI:", sati_notify, FALSE, stk, NULL); g_at_chat_register(sd->chat, "%SATA:", sata_notify, FALSE, stk, NULL); g_at_chat_register(sd->chat, "%SATN:", satn_notify, FALSE, stk, NULL); ofono_stk_register(stk); } static int calypso_stk_probe(struct ofono_stk *stk, unsigned int vendor, void *data) { GAtChat *chat = data; struct stk_data *sd; DBG(""); sd = g_try_new0(struct stk_data, 1); if (sd == NULL) return -ENOMEM; sd->chat = g_at_chat_clone(chat); ofono_stk_set_data(stk, sd); /* * Provide terminal profile data needed for the download and * enable %SATI / %SATN. The actual PROFILE DOWNLOAD will * happen during AT+CFUN=1 later. */ g_at_chat_send(sd->chat, "AT%SATC=1,\"19E1FFFF0000FF7FFF03FEFF\"", none_prefix, NULL, stk, NULL); /* Enable Call Control / SMS Control */ g_at_chat_send(sd->chat, "AT%SATCC=1", none_prefix, calypso_stk_register, stk, NULL); return 0; } static void calypso_stk_remove(struct ofono_stk *stk) { struct stk_data *sd = ofono_stk_get_data(stk); DBG(""); ofono_stk_set_data(stk, NULL); g_at_chat_unref(sd->chat); g_free(sd); } static struct ofono_stk_driver driver = { .name = "calypsomodem", .probe = calypso_stk_probe, .remove = calypso_stk_remove, .envelope = calypso_stk_envelope, .terminal_response = calypso_stk_terminal_response, }; void calypso_stk_init(void) { ofono_stk_driver_register(&driver); } void calypso_stk_exit(void) { ofono_stk_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qcommsimmodem/0000755000015600001650000000000012671500304022725 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/qcommsimmodem/qcom_msim_modem.h0000644000015600001650000000162512671500024026246 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2015 Ratchanan Srirattanamet * * 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 QCOMMSIMMODEM "qcommsimmodem" extern void qcom_msim_radio_settings_init(void); extern void qcom_msim_radio_settings_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/qcommsimmodem/radio-settings.c0000644000015600001650000001661612671500073026042 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical Ltd. * Copyright (C) 2015 Ratchanan Srirattanamet * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "gril.h" #include "grilrequest.h" #include "grilreply.h" #include "drivers/rilmodem/radio-settings.h" #include "drivers/rilmodem/rilutil.h" #include "qcom_msim_modem.h" #include "qcom_msim_constants.h" struct qcom_msim_pending_pref_setting { struct ofono_radio_settings *rs; int pref; int pending_gsm_pref_remaining; struct cb_data *cbd; }; struct qcom_msim_set_2g_rat { struct ofono_radio_settings *rs; struct qcom_msim_pending_pref_setting *pps; }; static struct ofono_radio_settings *multisim_rs[QCOMMSIM_NUM_SLOTS_MAX]; static void qcom_msim_set_rat_cb(struct ril_msg *message, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_radio_settings *rs = cbd->user; struct radio_data *rd = ofono_radio_settings_get_data(rs); ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb; if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(rd->ril, message); CALLBACK_WITH_SUCCESS(cb, cbd->data); } else { ofono_error("%s: rat mode setting failed", __func__); CALLBACK_WITH_FAILURE(cb, cbd->data); } } static void qcom_msim_do_set_rat_mode(struct ofono_radio_settings *rs, int pref, struct cb_data *cbd) { struct radio_data *rd = ofono_radio_settings_get_data(rs); struct parcel rilp; ofono_radio_settings_rat_mode_set_cb_t cb; g_ril_request_set_preferred_network_type(rd->ril, pref, &rilp); if (g_ril_send(rd->ril, RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, &rilp, qcom_msim_set_rat_cb, cbd, g_free) == 0) { ofono_error("%s: unable to set rat mode", __func__); cb = cbd->cb; CALLBACK_WITH_FAILURE(cb, cbd->data); g_free(cbd); } } static void qcom_msim_set_2g_rat_cb(struct ril_msg *message, gpointer user_data) { struct qcom_msim_set_2g_rat *set_2g_rat_data = user_data; struct ofono_radio_settings *rs = set_2g_rat_data->rs; struct qcom_msim_pending_pref_setting *pps = set_2g_rat_data->pps; struct radio_data *rd = ofono_radio_settings_get_data(rs); ofono_radio_settings_rat_mode_set_cb_t cb; pps->pending_gsm_pref_remaining -= 1; if (message->error == RIL_E_SUCCESS) { g_ril_print_response_no_args(rd->ril, message); ofono_radio_settings_set_rat_mode(rs, OFONO_RADIO_ACCESS_MODE_GSM); } else { ofono_error("%s: rat mode setting failed", __func__); if (pps->cbd != NULL) { cb = pps->cbd->cb; CALLBACK_WITH_FAILURE(cb, pps->cbd->data); g_free(pps->cbd); pps->cbd = NULL; } } if (pps->pending_gsm_pref_remaining == 0) { if (pps->cbd != NULL) qcom_msim_do_set_rat_mode(pps->rs, pps->pref, pps->cbd); g_free(pps); } } static void qcom_msim_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 cb_data *cbd = cb_data_new(cb, data, rs); struct parcel rilp; int pref = PREF_NET_TYPE_GSM_WCDMA; struct qcom_msim_pending_pref_setting *pps = NULL; switch (mode) { case OFONO_RADIO_ACCESS_MODE_ANY: pref = PREF_NET_TYPE_LTE_GSM_WCDMA; break; case OFONO_RADIO_ACCESS_MODE_GSM: pref = PREF_NET_TYPE_GSM_ONLY; break; case OFONO_RADIO_ACCESS_MODE_UMTS: pref = PREF_NET_TYPE_GSM_WCDMA; break; case OFONO_RADIO_ACCESS_MODE_LTE: pref = PREF_NET_TYPE_LTE_GSM_WCDMA; break; } if (pref != PREF_NET_TYPE_GSM_ONLY) { int i; for (i = 0; i < QCOMMSIM_NUM_SLOTS_MAX; i++) { struct radio_data *temp_rd; struct qcom_msim_set_2g_rat *set_2g_rat_data; struct ofono_atom *sim_atom; struct ofono_sim *sim; if (multisim_rs[i] == rs || multisim_rs[i] == NULL) continue; temp_rd = ofono_radio_settings_get_data(multisim_rs[i]); sim_atom = __ofono_modem_find_atom(temp_rd->modem, OFONO_ATOM_TYPE_SIM); if (sim_atom == NULL) { if (pps != NULL) pps->cbd = NULL; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); break; } sim = __ofono_atom_get_data(sim_atom); if (ofono_sim_get_state(sim) == OFONO_SIM_STATE_NOT_PRESENT) continue; if (pps == NULL) { pps = g_try_new0( struct qcom_msim_pending_pref_setting, 1); pps->rs = rs; pps->pref = pref; pps->cbd = cbd; pps->pending_gsm_pref_remaining = 0; } set_2g_rat_data = g_try_new0(struct qcom_msim_set_2g_rat, 1); set_2g_rat_data->pps = pps; set_2g_rat_data->rs = multisim_rs[i]; g_ril_request_set_preferred_network_type(temp_rd->ril, PREF_NET_TYPE_GSM_ONLY, &rilp); if (g_ril_send(temp_rd->ril, RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, &rilp, qcom_msim_set_2g_rat_cb, set_2g_rat_data, g_free) == 0) { ofono_error("%s: unable to set rat mode", __func__); pps->cbd = NULL; g_free(cbd); g_free(set_2g_rat_data); CALLBACK_WITH_FAILURE(cb, data); break; } else { pps->pending_gsm_pref_remaining += 1; } } } if (pps && pps->pending_gsm_pref_remaining == 0) { g_free(pps); pps = NULL; } if (pps == NULL) qcom_msim_do_set_rat_mode(rs, pref, cbd); } static int qcom_msim_radio_settings_probe(struct ofono_radio_settings *rs, unsigned int vendor, void *user) { struct ril_radio_settings_driver_data *rs_init_data = user; struct radio_data *rsd = g_try_new0(struct radio_data, 1); int slot_id; if (rsd == NULL) { ofono_error("%s: cannot allocate memory", __func__); return -ENOMEM; } rsd->ril = g_ril_clone(rs_init_data->gril); rsd->modem = rs_init_data->modem; ofono_radio_settings_set_data(rs, rsd); ril_set_fast_dormancy(rs, FALSE, ril_delayed_register, rs); slot_id = ofono_modem_get_integer(rsd->modem, "Slot"); multisim_rs[slot_id] = rs; return 0; } static void qcom_msim_radio_settings_remove(struct ofono_radio_settings *rs) { struct radio_data *rd = ofono_radio_settings_get_data(rs); int slot_id = ofono_modem_get_integer(rd->modem, "Slot"); multisim_rs[slot_id] = NULL; ofono_radio_settings_set_data(rs, NULL); g_ril_unref(rd->ril); g_free(rd); } static struct ofono_radio_settings_driver driver = { .name = QCOMMSIMMODEM, .probe = qcom_msim_radio_settings_probe, .remove = qcom_msim_radio_settings_remove, .query_rat_mode = ril_query_rat_mode, .set_rat_mode = qcom_msim_set_rat_mode, .query_fast_dormancy = ril_query_fast_dormancy, .set_fast_dormancy = ril_set_fast_dormancy, .query_available_rats = ril_query_available_rats }; void qcom_msim_radio_settings_init(void) { ofono_radio_settings_driver_register(&driver); } void qcom_msim_radio_settings_exit(void) { ofono_radio_settings_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/qcommsimmodem/qcom_msim_modem.c0000644000015600001650000000272012671500024026236 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony - RIL Modem Support * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical, Ltd. All rights reserved. * Copyright (C) 2015 Ratchanan Srirattanamet. * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include "qcom_msim_modem.h" static int qcom_msim_modem_init(void) { DBG(""); qcom_msim_radio_settings_init(); return 0; } static void qcom_msim_modem_exit(void) { DBG(""); qcom_msim_radio_settings_exit(); } OFONO_PLUGIN_DEFINE(qcommsimmodem, "Qualcomm multi-sim modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, qcom_msim_modem_init, qcom_msim_modem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/qcommsimmodem/qcom_msim_constants.h0000644000015600001650000000170212671500024027155 0ustar pbuserpbgroup00000000000000/* * * RIL constants for Qualcomm multi-sim modem * * Copyright (C) 2015 Ratchanan Srirattanamet. * * 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 * */ #ifndef QCOM_MSIM_CONSTANTS_H #define QCOM_MSIM_CONSTANTS_H #define QCOMMSIM_NUM_SLOTS_MAX 2 #define QCOM_MSIM_RIL_REQUEST_SET_UICC_SUBSCRIPTION 115 #endif /* QCOM_MSIM_CONSTANTS_H */ ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/0000755000015600001650000000000012671500304021655 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/siri.c0000644000015600001650000001123712671500024022772 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2013 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "hfpmodem.h" #include "hfp.h" #include "slc.h" #define APPLE_SIRI_STATUS_FEATURE 8 static const char *xapl_prefix[] = { "+XAPL=", NULL }; static const char *aplsiri_prefix[] = { "+APLSIRI:", NULL }; static const char *aplefm_prefix[] = { "+APLEFM:", NULL }; struct siri_data { GAtChat *chat; }; static void aplsiri_notify(GAtResult *result, gpointer user_data) { struct ofono_siri *siri = user_data; GAtResultIter iter; gint value; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+APLSIRI:")) return; if (!g_at_result_iter_next_number(&iter, &value)) return; ofono_siri_set_status(siri, value); } static void aplsiri_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_siri *siri = user_data; struct siri_data *sd = ofono_siri_get_data(siri); struct ofono_error error; GAtResultIter iter; gint value; if (!ok) goto fail; decode_at_error(&error, g_at_result_final_response(result)); if (error.type != OFONO_ERROR_TYPE_NO_ERROR) goto fail; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+APLSIRI:")) goto fail; if (!g_at_result_iter_next_number(&iter, &value)) goto fail; if (value == 0) goto fail; g_at_chat_register(sd->chat, "+APLSIRI:", aplsiri_notify, FALSE, siri, NULL); ofono_siri_register(siri); ofono_siri_set_status(siri, value); return; fail: ofono_siri_remove(siri); } static void xapl_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_siri *siri = user_data; struct siri_data *sd = ofono_siri_get_data(siri); struct ofono_error error; if (!ok) { ofono_siri_remove(siri); return; } decode_at_error(&error, g_at_result_final_response(result)); if (error.type != OFONO_ERROR_TYPE_NO_ERROR) { ofono_siri_remove(siri); return; } g_at_chat_send(sd->chat, "AT+APLSIRI?", aplsiri_prefix, aplsiri_cb, siri, NULL); } static int hfp_siri_probe(struct ofono_siri *siri, unsigned int vendor, void *data) { struct hfp_slc_info *info = data; struct siri_data *sd; char at_command[64]; DBG(""); sd = g_new0(struct siri_data, 1); sd->chat = g_at_chat_clone(info->chat); ofono_siri_set_data(siri, sd); snprintf(at_command, sizeof(at_command), "AT+XAPL=Linux-oFono-%s,%d", VERSION, APPLE_SIRI_STATUS_FEATURE); g_at_chat_send(sd->chat, at_command, xapl_prefix, xapl_cb, siri, NULL); return 0; } static void hfp_siri_remove(struct ofono_siri *siri) { struct siri_data *sd = ofono_siri_get_data(siri); ofono_siri_set_data(siri, NULL); g_at_chat_unref(sd->chat); g_free(sd); } static void hfp_siri_eyes_free_mode_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_siri_cb_t cb = cbd->cb; struct ofono_siri *siri = cbd->data; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, siri); } static void hfp_siri_set_eyes_free_mode(struct ofono_siri *siri, ofono_siri_cb_t cb, unsigned int val) { struct siri_data *sd = ofono_siri_get_data(siri); struct cb_data *cbd = cb_data_new(cb, siri); char at_command[16]; snprintf(at_command, sizeof(at_command), "AT+APLEFM=%d", val); if (g_at_chat_send(sd->chat, at_command, aplefm_prefix, hfp_siri_eyes_free_mode_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL); } static struct ofono_siri_driver driver = { .name = "hfpmodem", .probe = hfp_siri_probe, .remove = hfp_siri_remove, .set_eyes_free_mode = hfp_siri_set_eyes_free_mode, }; void hfp_siri_init(void) { ofono_siri_driver_register(&driver); } void hfp_siri_exit(void) { ofono_siri_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/hfpmodem.h0000644000015600001650000000241012671500024023621 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include #include extern void hfp_netreg_init(void); extern void hfp_netreg_exit(void); extern void hfp_call_volume_init(void); extern void hfp_call_volume_exit(void); extern void hfp_voicecall_init(void); extern void hfp_voicecall_exit(void); extern void hfp_handsfree_init(void); extern void hfp_handsfree_exit(void); extern void hfp_siri_init(void); extern void hfp_siri_exit(void); extern void hfp_devinfo_init(void); extern void hfp_devinfo_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/voicecall.c0000644000015600001650000007431012671500024023766 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "common.h" #include "hfp.h" #include "hfpmodem.h" #include "slc.h" #define POLL_CLCC_INTERVAL 2000 #define POLL_CLCC_DELAY 50 #define EXPECT_RELEASE_DELAY 50 #define CLIP_TIMEOUT 500 #define EXPECT_RING_DELAY 200 static const char *none_prefix[] = { NULL }; static const char *clcc_prefix[] = { "+CLCC:", NULL }; struct voicecall_data { GAtChat *chat; GSList *calls; unsigned int ag_features; unsigned int ag_mpty_features; unsigned char cind_pos[HFP_INDICATOR_LAST]; int cind_val[HFP_INDICATOR_LAST]; unsigned int local_release; unsigned int clcc_source; unsigned int expect_release_source; unsigned int clip_source; }; struct release_id_req { struct ofono_voicecall *vc; ofono_voicecall_cb_t cb; void *data; int id; }; struct change_state_req { struct ofono_voicecall *vc; ofono_voicecall_cb_t cb; void *data; int affected_types; }; static gboolean poll_clcc(gpointer user_data); static GSList *find_dialing(GSList *calls) { GSList *c; c = g_slist_find_custom(calls, GINT_TO_POINTER(CALL_STATUS_DIALING), at_util_call_compare_by_status); if (c == NULL) c = g_slist_find_custom(calls, GINT_TO_POINTER(CALL_STATUS_ALERTING), at_util_call_compare_by_status); return c; } static void voicecall_notify(gpointer value, gpointer user) { struct ofono_call *call = value; struct ofono_voicecall *vc = user; ofono_voicecall_notify(vc, call); } static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, int direction, int status, const char *num, int num_type, int clip) { struct voicecall_data *d = ofono_voicecall_get_data(vc); struct ofono_call *call; /* Generate a call structure for the waiting call */ call = g_try_new(struct ofono_call, 1); if (call == NULL) return NULL; ofono_call_init(call); call->id = ofono_voicecall_get_next_callid(vc); call->type = type; call->direction = direction; call->status = status; if (clip != 2) { strncpy(call->phone_number.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH); call->phone_number.type = num_type; } d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare); call->clip_validity = clip; return call; } static void release_call(struct ofono_voicecall *vc, struct ofono_call *call) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); enum ofono_disconnect_reason reason; if (call == NULL) return; if (vd->local_release & (1 << call->id)) reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP; else reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP; ofono_voicecall_disconnected(vc, call->id, reason, NULL); vd->local_release &= ~(1 << call->id); g_free(call); } static void release_all_calls(struct ofono_voicecall *vc) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); GSList *l; struct ofono_call *call; for (l = vd->calls; l; l = l->next) { call = l->data; release_call(vc, call); } g_slist_free(vd->calls); vd->calls = NULL; } static void release_with_status(struct ofono_voicecall *vc, int status) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); GSList *p = NULL; GSList *c = vd->calls; GSList *t; struct ofono_call *call; while (c) { call = c->data; if (call->status != status) { p = c; c = c->next; continue; } release_call(vc, call); if (p) p->next = c->next; else vd->calls = c->next; t = c; c = c->next; g_slist_free_1(t); } if (vd->expect_release_source) { g_source_remove(vd->expect_release_source); vd->expect_release_source = 0; } } static void clcc_poll_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GSList *calls; GSList *n, *o; struct ofono_call *nc, *oc; unsigned int num_active = 0; unsigned int num_held = 0; GSList *notify_calls = NULL; unsigned int mpty_ids; if (!ok) return; calls = at_util_parse_clcc(result, &mpty_ids); n = calls; o = vd->calls; while (n || o) { nc = n ? n->data : NULL; oc = o ? o->data : NULL; if (nc && (nc->status == CALL_STATUS_ACTIVE)) num_active++; if (nc && (nc->status == CALL_STATUS_HELD)) num_held++; if (oc && (nc == NULL || (nc->id > oc->id))) { enum ofono_disconnect_reason reason; if (vd->local_release & (1 << oc->id)) reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP; else reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP; if (!oc->type) ofono_voicecall_disconnected(vc, oc->id, reason, NULL); vd->local_release &= ~(1 << oc->id); o = o->next; } else if (nc && (oc == NULL || (nc->id < oc->id))) { /* new call, signal it */ if (nc->type == 0) notify_calls = g_slist_append(notify_calls, nc); n = n->next; } else { /* Always use the clip_validity from old call * the only place this is truly told to us is * in the CLIP notify, the rest are fudged * anyway. Useful when RING, CLIP is used, * and we're forced to use CLCC and clip_validity * is 1 */ nc->clip_validity = oc->clip_validity; if (memcmp(nc, oc, sizeof(struct ofono_call)) && !nc->type) notify_calls = g_slist_prepend(notify_calls, nc); n = n->next; o = o->next; } } /* * Disconnections were already reported, so process the rest of the * notifications. Note that the new calls are placed at the end of the * list, after other state changes */ g_slist_foreach(notify_calls, voicecall_notify, vc); g_slist_free(notify_calls); ofono_voicecall_mpty_hint(vc, mpty_ids); g_slist_foreach(vd->calls, (GFunc) g_free, NULL); g_slist_free(vd->calls); vd->calls = calls; /* If either active/held call is more than 1, we are in mpty calls. * we won't get indicator update if any of them is released by CHLD=1x. * So we have to poll it. */ if ((num_active > 1 || num_held > 1) && !vd->clcc_source) vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL, poll_clcc, vc); } static gboolean poll_clcc(gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); vd->clcc_source = 0; return FALSE; } static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct change_state_req *req = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (ok && req->affected_types) { GSList *l; struct ofono_call *call; for (l = vd->calls; l; l = l->next) { call = l->data; if (req->affected_types & (1 << call->status)) vd->local_release |= (1 << call->id); } } if (!ok && vd->calls) g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, req->vc, NULL); req->cb(&error, req->data); } static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_voicecall *vc = cbd->user; struct voicecall_data *vd = ofono_voicecall_get_data(vc); ofono_voicecall_cb_t cb = cbd->cb; int type = 128; int validity = 2; struct ofono_error error; struct ofono_call *call; GSList *l; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) goto out; /* On a success, make sure to put all active calls on hold */ for (l = vd->calls; l; l = l->next) { call = l->data; if (call->status != CALL_STATUS_ACTIVE) continue; call->status = CALL_STATUS_HELD; ofono_voicecall_notify(vc, call); } call = create_call(vc, 0, 0, CALL_STATUS_DIALING, NULL, type, validity); if (call == NULL) { ofono_error("Unable to allocate call, " "call tracking will fail!"); return; } out: cb(&error, cbd->data); } static void hfp_dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, enum ofono_clir_option clir, 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; if (ph->type == 145) snprintf(buf, sizeof(buf), "ATD+%s", ph->number); else snprintf(buf, sizeof(buf), "ATD%s", ph->number); strcat(buf, ";"); if (g_at_chat_send(vd->chat, buf, none_prefix, atd_cb, cbd, g_free) > 0) return; g_free(cbd); 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) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct change_state_req *req = g_try_new0(struct change_state_req, 1); if (req == NULL) goto error; req->vc = vc; req->cb = cb; req->data = data; req->affected_types = affected_types; if (g_at_chat_send(vd->chat, cmd, none_prefix, result_cb, req, g_free) > 0) return; error: g_free(req); CALLBACK_WITH_FAILURE(cb, data); } static void hfp_answer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { hfp_template("ATA", vc, generic_cb, 0, cb, data); } static void hfp_hangup(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { unsigned int affected = (1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_DIALING) | (1 << CALL_STATUS_ALERTING) | (1 << CALL_STATUS_ACTIVE); /* Hangup current active call */ hfp_template("AT+CHUP", vc, generic_cb, affected, cb, data); } static void hfp_hold_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); if (vd->ag_mpty_features & HFP_AG_CHLD_2) { hfp_template("AT+CHLD=2", vc, generic_cb, 0, cb, data); return; } CALLBACK_WITH_FAILURE(cb, data); } static void hfp_release_all_held(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); unsigned int held_status = 1 << CALL_STATUS_HELD; if (vd->ag_mpty_features & HFP_AG_CHLD_0) { hfp_template("AT+CHLD=0", vc, generic_cb, held_status, cb, data); return; } CALLBACK_WITH_FAILURE(cb, data); } static void hfp_set_udub(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); unsigned int incoming_or_waiting = 1 << CALL_STATUS_WAITING; if (vd->ag_mpty_features & HFP_AG_CHLD_0) { hfp_template("AT+CHLD=0", vc, generic_cb, incoming_or_waiting, cb, data); return; } CALLBACK_WITH_FAILURE(cb, data); } static gboolean expect_release(gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); vd->expect_release_source = 0; return FALSE; } static gboolean expect_ring(gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); vd->clip_source = 0; return FALSE; } static void release_all_active_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct change_state_req *req = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); if (!ok) goto out; if (vd->expect_release_source) g_source_remove(vd->expect_release_source); /* * Some phones, like Nokia 500, do not send CIEV after accepting * the CHLD=1 command, even though the spec states that they should. * So simply poll to force the status update if the AG is misbehaving. */ vd->expect_release_source = g_timeout_add(EXPECT_RELEASE_DELAY, expect_release, req->vc); out: generic_cb(ok, result, user_data); } static void hfp_release_all_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); if (vd->ag_mpty_features & HFP_AG_CHLD_1) { hfp_template("AT+CHLD=1", vc, release_all_active_cb, 0x1, cb, data); return; } CALLBACK_WITH_FAILURE(cb, data); } static void release_id_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct release_id_req *req = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(req->vc); struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (ok) vd->local_release |= (1 << req->id); req->cb(&error, req->data); } static void hfp_release_specific(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct release_id_req *req = NULL; char buf[32]; if (!(vd->ag_mpty_features & HFP_AG_CHLD_1x)) goto error; req = g_try_new0(struct release_id_req, 1); if (req == NULL) goto error; req->vc = vc; req->cb = cb; req->data = data; req->id = id; snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id); if (g_at_chat_send(vd->chat, buf, none_prefix, release_id_cb, req, g_free) > 0) return; error: g_free(req); CALLBACK_WITH_FAILURE(cb, data); } static void hfp_private_chat(struct ofono_voicecall *vc, int id, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); char buf[32]; if (vd->ag_mpty_features & HFP_AG_CHLD_2x) { snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id); hfp_template(buf, vc, generic_cb, 0, cb, data); return; } CALLBACK_WITH_FAILURE(cb, data); } static void hfp_create_multiparty(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); if (vd->ag_mpty_features & HFP_AG_CHLD_3) { hfp_template("AT+CHLD=3", vc, generic_cb, 0, cb, data); return; } CALLBACK_WITH_FAILURE(cb, data); } static void hfp_transfer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); /* Transfer can puts held & active calls together and disconnects * from both. However, some networks support transferring of * dialing/ringing calls as well. */ unsigned int transfer = 0x1 | 0x2 | 0x4 | 0x8; if (vd->ag_mpty_features & HFP_AG_CHLD_4) { hfp_template("AT+CHLD=4", vc, generic_cb, transfer, cb, data); return; } CALLBACK_WITH_FAILURE(cb, data); } static void hfp_send_dtmf(struct ofono_voicecall *vc, const char *dtmf, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct change_state_req *req = g_try_new0(struct change_state_req, 1); int len = strlen(dtmf); char *buf; int s; int i; if (req == NULL) goto error; req->vc = vc; req->cb = cb; req->data = data; req->affected_types = 0; /* strlen("AT") + (n-1) * strlen("+VTS=T;") + strlen(+VTS=T) + null */ buf = g_try_new(char, len * 7 + 2); if (buf == NULL) goto error; s = sprintf(buf, "AT+VTS=%c", dtmf[0]); for (i = 1; i < len; i++) s += sprintf(buf + s, ";+VTS=%c", dtmf[i]); s = g_at_chat_send(vd->chat, buf, none_prefix, generic_cb, req, g_free); g_free(buf); if (s > 0) return; error: g_free(req); CALLBACK_WITH_FAILURE(cb, data); } static void no_carrier_notify(GAtResult *result, gpointer user_data) { DBG(""); } static void ccwa_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *num; int num_type, validity; struct ofono_call *call; /* CCWA can repeat, ignore if we already have an waiting call */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), at_util_call_compare_by_status)) return; /* some phones may send extra CCWA after active call is ended * this would trigger creation of second call in state 'WAITING' * as our previous WAITING call has been promoted to INCOMING */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status)) return; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CCWA:")) return; if (!g_at_result_iter_next_string(&iter, &num)) return; if (!g_at_result_iter_next_number(&iter, &num_type)) return; if (strlen(num) > 0) validity = 0; else validity = 2; DBG("ccwa_notify: %s %d %d", num, num_type, validity); call = create_call(vc, 0, 1, CALL_STATUS_WAITING, num, num_type, validity); if (call == NULL) { ofono_error("malloc call struct failed. " "Call management is fubar"); return; } ofono_voicecall_notify(vc, call); } static gboolean clip_timeout(gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GSList *l; struct ofono_call *call; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status); if (l == NULL) return FALSE; call = l->data; ofono_voicecall_notify(vc, call); vd->clip_source = 0; return FALSE; } static void ring_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct ofono_call *call; GSList *waiting; if (vd->clip_source) { g_source_remove(vd->clip_source); vd->clip_source = 0; } /* RING can repeat, ignore if we already have an incoming call */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status)) return; waiting = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), at_util_call_compare_by_status); /* If we started receiving RINGS but have a waiting call, most * likely all other calls were dropped and we just didn't get * notified yet, drop all other calls and update the status to * incoming */ if (waiting) { DBG("Triggering waiting -> incoming cleanup code"); vd->calls = g_slist_remove_link(vd->calls, waiting); release_all_calls(vc); vd->calls = waiting; call = waiting->data; call->status = CALL_STATUS_INCOMING; ofono_voicecall_notify(vc, call); return; } /* Generate an incoming call of voice type */ call = create_call(vc, 0, 1, CALL_STATUS_INCOMING, NULL, 128, 2); if (call == NULL) ofono_error("Couldn't create call, call management is fubar!"); /* We don't know the number must wait for CLIP to arrive before * announcing the call. If timeout, we notify the call as it is. */ vd->clip_source = g_timeout_add(CLIP_TIMEOUT, clip_timeout, vc); } static void clip_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); GAtResultIter iter; const char *num; int type, validity; GSList *l; struct ofono_call *call; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), at_util_call_compare_by_status); if (l == NULL) { ofono_error("CLIP for unknown call"); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CLIP:")) return; if (!g_at_result_iter_next_string(&iter, &num)) return; if (!g_at_result_iter_next_number(&iter, &type)) return; if (strlen(num) > 0) validity = 0; else validity = 2; /* Skip subaddr, satype, alpha and validity */ g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); DBG("clip_notify: %s %d %d", num, type, validity); call = l->data; 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 = type; call->clip_validity = validity; ofono_voicecall_notify(vc, call); if (vd->clip_source) { g_source_remove(vd->clip_source); vd->clip_source = 0; } } static void ciev_call_notify(struct ofono_voicecall *vc, unsigned int value) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct ofono_call *call; switch (value) { case 0: /* If call goes to 0, then we have no held or active calls * in the system. The waiting calls are promoted to incoming * calls, dialing calls are kept. This also handles the * situation when dialing and waiting calls exist */ release_with_status(vc, CALL_STATUS_HELD); release_with_status(vc, CALL_STATUS_ACTIVE); /* Promote waiting to incoming if it is the last call */ if (vd->calls && vd->calls->next == NULL) { call = vd->calls->data; if (call->status == CALL_STATUS_WAITING) { call->status = CALL_STATUS_INCOMING; ofono_voicecall_notify(vc, call); } } break; case 1: { GSList *l; /* In this case either dialing/alerting or the incoming call * is promoted to active */ for (l = vd->calls; l; l = l->next) { call = l->data; if (call->status == CALL_STATUS_DIALING || call->status == CALL_STATUS_ALERTING || call->status == CALL_STATUS_INCOMING) { call->status = CALL_STATUS_ACTIVE; ofono_voicecall_notify(vc, call); } } break; } default: break; } vd->cind_val[HFP_INDICATOR_CALL] = value; } static void ciev_callsetup_notify(struct ofono_voicecall *vc, unsigned int value) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); unsigned int ciev_call = vd->cind_val[HFP_INDICATOR_CALL]; unsigned int ciev_callheld = vd->cind_val[HFP_INDICATOR_CALLHELD]; GSList *dialing; GSList *waiting; dialing = find_dialing(vd->calls); waiting = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), at_util_call_compare_by_status); /* This is a truly bizarre case not covered at all by the specification * (yes, they are complete idiots). Here we assume the other side is * semi sane and will send callsetup updates in case the dialing call * connects or the call waiting drops. In which case we must poll */ if (waiting && dialing) { g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); goto out; } switch (value) { case 0: /* call=0 and callsetup=1: reject an incoming call * call=0 and callsetup=2,3: interrupt an outgoing call */ if (ciev_call == 0) { release_all_calls(vc); goto out; } /* If call=1 and no call is waiting or dialing, the call is * active and we moved it to active state when call=1 arrived */ if (waiting == NULL && dialing == NULL) goto out; /* * If call=1, in the waiting case we have to poll, since we * have no idea whether a waiting call gave up or we accepted * using release+accept or hold+accept * * If call=1, in the dialing + held case we have to poll as * well, we have no idea whether the call connected, or released */ if (waiting == NULL && ciev_callheld == 0) { struct ofono_call *call = dialing->data; /* We assume that the implementation follows closely * the sequence of events in Figure 4.21. That is * call=1 arrives first, then callsetup=0 */ call->status = CALL_STATUS_ACTIVE; ofono_voicecall_notify(vc, call); } else { g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); } break; case 1: /* * Handled in RING/CCWA most of the time, however sometimes * the call is answered before the RING unsolicited * notification has a chance to be generated on the device. * In this case, we use a failsafe CLCC poll in expect_ring * callback. * */ vd->clip_source = g_timeout_add(EXPECT_RING_DELAY, expect_ring, vc); break; case 2: /* two cases of outgoing call: dial from HF or AG. * from HF: query and sync the phone number. * from AG: query and create call. */ g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); break; case 3: { GSList *o = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_DIALING), at_util_call_compare_by_status); if (o) { struct ofono_call *call = o->data; call->status = CALL_STATUS_ALERTING; ofono_voicecall_notify(vc, call); } break; } default: break; } out: vd->cind_val[HFP_INDICATOR_CALLSETUP] = value; } static void ciev_callheld_notify(struct ofono_voicecall *vc, unsigned int value) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); GSList *l; struct ofono_call *call; unsigned int callheld = vd->cind_val[HFP_INDICATOR_CALLHELD]; switch (value) { case 0: /* We have to poll here, we have no idea whether the call was * dropped using CHLD=0 or simply retrieved, or the two calls * were merged */ g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); break; case 1: if (vd->clcc_source) { g_source_remove(vd->clcc_source); vd->clcc_source = 0; } /* We have to poll here, we have no idea whether the call was * accepted by CHLD=1 or swapped by CHLD=2 or one call was * chosed for private chat by CHLD=2x */ g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_poll_cb, vc, NULL); break; case 2: if (callheld == 0) { for (l = vd->calls; l; l = l->next) { call = l->data; if (call->status != CALL_STATUS_ACTIVE) continue; call->status = CALL_STATUS_HELD; ofono_voicecall_notify(vc, call); } } else if (callheld == 1) { if (vd->clcc_source) g_source_remove(vd->clcc_source); /* We have to schedule a poll here, we have no idea * whether active call was dropped by remote or if this * is an intermediate state during call swap */ vd->clcc_source = g_timeout_add(POLL_CLCC_DELAY, poll_clcc, vc); } } vd->cind_val[HFP_INDICATOR_CALLHELD] = value; } static void ciev_notify(GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); int index; int value; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CIEV:")) return; if (!g_at_result_iter_next_number(&iter, &index)) return; if (!g_at_result_iter_next_number(&iter, &value)) return; if (index == vd->cind_pos[HFP_INDICATOR_CALL]) ciev_call_notify(vc, value); else if (index == vd->cind_pos[HFP_INDICATOR_CALLSETUP]) ciev_callsetup_notify(vc, value); else if (index == vd->cind_pos[HFP_INDICATOR_CALLHELD]) ciev_callheld_notify(vc, value); } static void hfp_clcc_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); unsigned int mpty_ids; GSList *n; struct ofono_call *nc; unsigned int num_active = 0; unsigned int num_held = 0; if (!ok) return; vd->calls = at_util_parse_clcc(result, &mpty_ids); g_slist_foreach(vd->calls, voicecall_notify, vc); ofono_voicecall_mpty_hint(vc, mpty_ids); n = vd->calls; while (n) { nc = n->data; if (nc->status == CALL_STATUS_ACTIVE) num_active++; else if (nc->status == CALL_STATUS_HELD) num_held++; n = n->next; } if ((num_active > 1 || num_held > 1) && !vd->clcc_source) vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL, poll_clcc, vc); } static void hfp_voicecall_initialized(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_voicecall *vc = user_data; struct voicecall_data *vd = ofono_voicecall_get_data(vc); DBG("hfp_voicecall_init: registering to notifications"); g_at_chat_register(vd->chat, "RING", ring_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CIEV:", ciev_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL); g_at_chat_register(vd->chat, "NO CARRIER", no_carrier_notify, FALSE, vc, NULL); ofono_voicecall_register(vc); /* Populate the call list */ g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, hfp_clcc_cb, vc, NULL); } static int hfp_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor, gpointer user_data) { struct hfp_slc_info *info = user_data; struct voicecall_data *vd; vd = g_new0(struct voicecall_data, 1); vd->chat = g_at_chat_clone(info->chat); vd->ag_features = info->ag_features; vd->ag_mpty_features = info->ag_mpty_features; memcpy(vd->cind_pos, info->cind_pos, HFP_INDICATOR_LAST); memcpy(vd->cind_val, info->cind_val, HFP_INDICATOR_LAST); ofono_voicecall_set_data(vc, vd); g_at_chat_send(vd->chat, "AT+CLIP=1", none_prefix, NULL, NULL, NULL); g_at_chat_send(vd->chat, "AT+CCWA=1", none_prefix, hfp_voicecall_initialized, vc, NULL); return 0; } static void hfp_voicecall_remove(struct ofono_voicecall *vc) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); if (vd->clcc_source) g_source_remove(vd->clcc_source); if (vd->clip_source) g_source_remove(vd->clip_source); if (vd->expect_release_source) g_source_remove(vd->expect_release_source); g_slist_foreach(vd->calls, (GFunc) g_free, NULL); g_slist_free(vd->calls); ofono_voicecall_set_data(vc, NULL); g_at_chat_unref(vd->chat); g_free(vd); } static struct ofono_voicecall_driver driver = { .name = "hfpmodem", .probe = hfp_voicecall_probe, .remove = hfp_voicecall_remove, .dial = hfp_dial, .answer = hfp_answer, .hangup_active = hfp_hangup, .hold_all_active = hfp_hold_all_active, .release_all_held = hfp_release_all_held, .set_udub = hfp_set_udub, .release_all_active = hfp_release_all_active, .release_specific = hfp_release_specific, .private_chat = hfp_private_chat, .create_multiparty = hfp_create_multiparty, .transfer = hfp_transfer, .deflect = NULL, .swap_without_accept = NULL, .send_tones = hfp_send_dtmf }; void hfp_voicecall_init(void) { ofono_voicecall_driver_register(&driver); } void hfp_voicecall_exit(void) { ofono_voicecall_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/slc.h0000644000015600001650000000310712671500024022607 0ustar pbuserpbgroup00000000000000/* * * 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 * */ enum hfp_indicator { HFP_INDICATOR_SERVICE = 0, HFP_INDICATOR_CALL, HFP_INDICATOR_CALLSETUP, HFP_INDICATOR_CALLHELD, HFP_INDICATOR_SIGNAL, HFP_INDICATOR_ROAM, HFP_INDICATOR_BATTCHG, HFP_INDICATOR_LAST }; typedef void (*hfp_slc_cb_t)(void *userdata); struct hfp_slc_info { GAtChat *chat; unsigned int ag_features; unsigned int ag_mpty_features; unsigned int hf_features; unsigned char cind_pos[HFP_INDICATOR_LAST]; unsigned int cind_val[HFP_INDICATOR_LAST]; unsigned short hf_indicators[20]; unsigned char num_hf_indicators; unsigned int hf_indicator_active_map; }; void hfp_slc_info_init(struct hfp_slc_info *info, guint16 version); void hfp_slc_info_free(struct hfp_slc_info *info); void hfp_slc_establish(struct hfp_slc_info *info, hfp_slc_cb_t connect_cb, hfp_slc_cb_t failed_cb, void *userdata); ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/handsfree.c0000644000015600001650000002403212671500024023760 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 BMW Car IT GmbH. 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "hfpmodem.h" #include "hfp.h" #include "slc.h" static const char *binp_prefix[] = { "+BINP:", NULL }; static const char *bvra_prefix[] = { "+BVRA:", NULL }; static const char *none_prefix[] = { NULL }; struct hf_data { GAtChat *chat; unsigned int ag_features; unsigned int ag_chld_features; int battchg_index; guint register_source; }; static void hf_generic_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_handsfree_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void bsir_notify(GAtResult *result, gpointer user_data) { struct ofono_handsfree *hf = user_data; GAtResultIter iter; int value; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+BSIR:")) return; if (!g_at_result_iter_next_number(&iter, &value)) return; ofono_handsfree_set_inband_ringing(hf, (ofono_bool_t) value); } static void bvra_notify(GAtResult *result, gpointer user_data) { struct ofono_handsfree *hf = user_data; GAtResultIter iter; int value; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+BVRA:")) return; if (!g_at_result_iter_next_number(&iter, &value)) return; ofono_handsfree_voice_recognition_notify(hf, (ofono_bool_t) value); } static void ciev_notify(GAtResult *result, gpointer user_data) { struct ofono_handsfree *hf = user_data; struct hf_data *hd = ofono_handsfree_get_data(hf); int index; int value; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CIEV:")) return; if (!g_at_result_iter_next_number(&iter, &index)) return; if (index != hd->battchg_index) return; if (!g_at_result_iter_next_number(&iter, &value)) return; ofono_handsfree_battchg_notify(hf, value); } static void cnum_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_handsfree_cnum_query_cb_t cb = cbd->cb; GAtResultIter iter; struct ofono_phone_number *list = NULL; int num = 0; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) goto out; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+CNUM:")) num++; if (num == 0) goto out; list = g_new0(struct ofono_phone_number, num); g_at_result_iter_init(&iter, result); for (num = 0; g_at_result_iter_next(&iter, "+CNUM:"); ) { const char *number; int service; int type; if (!g_at_result_iter_skip_next(&iter)) continue; if (!g_at_result_iter_next_string(&iter, &number)) continue; if (!g_at_result_iter_next_number(&iter, &type)) continue; if (!g_at_result_iter_skip_next(&iter)) continue; if (!g_at_result_iter_next_number(&iter, &service)) continue; /* We are only interested in Voice services */ if (service != 4) continue; strncpy(list[num].number, number, OFONO_MAX_PHONE_NUMBER_LENGTH); list[num].number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0'; list[num].type = type; DBG("cnum_notify:%s", list[num].number); num++; } out: cb(&error, num, list, cbd->data); g_free(list); } static void hfp_cnum_query(struct ofono_handsfree *hf, ofono_handsfree_cnum_query_cb_t cb, void *data) { struct hf_data *hd = ofono_handsfree_get_data(hf); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(hd->chat, "AT+CNUM", none_prefix, cnum_query_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, NULL, data); } static void bind_notify(GAtResult *result, gpointer user_data) { struct ofono_handsfree *hf = user_data; int hf_indicator; int active; GAtResultIter iter; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+BIND:")) return; if (!g_at_result_iter_next_number(&iter, &hf_indicator)) return; if (!g_at_result_iter_next_number(&iter, &active)) return; ofono_handsfree_hf_indicator_active_notify(hf, hf_indicator, active); } static gboolean hfp_handsfree_register(gpointer user_data) { struct ofono_handsfree *hf = user_data; struct hf_data *hd = ofono_handsfree_get_data(hf); hd->register_source = 0; g_at_chat_register(hd->chat, "+BSIR:", bsir_notify, FALSE, hf, NULL); g_at_chat_register(hd->chat, "+BVRA:", bvra_notify, FALSE, hf, NULL); g_at_chat_register(hd->chat, "+CIEV:", ciev_notify, FALSE, hf, NULL); g_at_chat_register(hd->chat, "+BIND:", bind_notify, FALSE, hf, NULL); if (hd->ag_features & HFP_AG_FEATURE_IN_BAND_RING_TONE) ofono_handsfree_set_inband_ringing(hf, TRUE); ofono_handsfree_set_ag_features(hf, hd->ag_features); ofono_handsfree_set_ag_chld_features(hf, hd->ag_chld_features); ofono_handsfree_register(hf); return FALSE; } static int hfp_handsfree_probe(struct ofono_handsfree *hf, unsigned int vendor, void *data) { struct hfp_slc_info *info = data; struct hf_data *hd; unsigned int i; DBG(""); hd = g_new0(struct hf_data, 1); hd->chat = g_at_chat_clone(info->chat); hd->ag_features = info->ag_features; hd->ag_chld_features = info->ag_mpty_features; ofono_handsfree_set_data(hf, hd); hd->battchg_index = info->cind_pos[HFP_INDICATOR_BATTCHG]; ofono_handsfree_battchg_notify(hf, info->cind_val[HFP_INDICATOR_BATTCHG]); ofono_handsfree_set_hf_indicators(hf, info->hf_indicators, info->num_hf_indicators); for (i = 0; i < info->num_hf_indicators; i++) ofono_handsfree_hf_indicator_active_notify(hf, info->hf_indicators[i], info->hf_indicator_active_map & (1 << i)); hd->register_source = g_idle_add(hfp_handsfree_register, hf); return 0; } static void hfp_handsfree_remove(struct ofono_handsfree *hf) { struct hf_data *hd = ofono_handsfree_get_data(hf); if (hd->register_source != 0) g_source_remove(hd->register_source); ofono_handsfree_set_data(hf, NULL); g_at_chat_unref(hd->chat); g_free(hd); } static void hfp_request_phone_number_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_handsfree_phone_cb_t cb = cbd->cb; GAtResultIter iter; struct ofono_error error; const char *num; int type; struct ofono_phone_number phone_number; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+BINP:")) goto fail; if (!g_at_result_iter_next_string(&iter, &num)) goto fail; if (!g_at_result_iter_next_number(&iter, &type)) goto fail; DBG("AT+BINP=1 response: %s %d", num, type); strncpy(phone_number.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH); phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0'; phone_number.type = type; cb(&error, &phone_number, cbd->data); return; fail: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void hfp_request_phone_number(struct ofono_handsfree *hf, ofono_handsfree_phone_cb_t cb, void *data) { struct hf_data *hd = ofono_handsfree_get_data(hf); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(hd->chat, "AT+BINP=1", binp_prefix, hfp_request_phone_number_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void hfp_voice_recognition(struct ofono_handsfree *hf, ofono_bool_t enabled, ofono_handsfree_cb_t cb, void *data) { struct hf_data *hd = ofono_handsfree_get_data(hf); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; snprintf(buf, sizeof(buf), "AT+BVRA=%d", (int)(enabled)); if (g_at_chat_send(hd->chat, buf, bvra_prefix, hf_generic_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void hfp_disable_nrec(struct ofono_handsfree *hf, ofono_handsfree_cb_t cb, void *data) { struct hf_data *hd = ofono_handsfree_get_data(hf); struct cb_data *cbd = cb_data_new(cb, data); const char *buf = "AT+NREC=0"; if (g_at_chat_send(hd->chat, buf, none_prefix, hf_generic_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void hfp_hf_indicator(struct ofono_handsfree *hf, unsigned short indicator, unsigned int value, ofono_handsfree_cb_t cb, void *data) { struct hf_data *hd = ofono_handsfree_get_data(hf); struct cb_data *cbd = cb_data_new(cb, data); char buf[128]; snprintf(buf, sizeof(buf), "AT+BIEV=%u,%u", indicator, value); if (g_at_chat_send(hd->chat, buf, none_prefix, hf_generic_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static struct ofono_handsfree_driver driver = { .name = "hfpmodem", .probe = hfp_handsfree_probe, .remove = hfp_handsfree_remove, .cnum_query = hfp_cnum_query, .request_phone_number = hfp_request_phone_number, .voice_recognition = hfp_voice_recognition, .disable_nrec = hfp_disable_nrec, .hf_indicator = hfp_hf_indicator, }; void hfp_handsfree_init(void) { ofono_handsfree_driver_register(&driver); } void hfp_handsfree_exit(void) { ofono_handsfree_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/call-volume.c0000644000015600001650000001326412671500024024246 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "hfpmodem.h" #include "slc.h" #define HFP_CALL_VOLUME_MAX 15 static const char *vgs_prefix[] = { "+VGS:", NULL }; static const char *vgm_prefix[] = { "+VGM:", NULL }; struct cv_data { GAtChat *chat; unsigned char sp_volume; unsigned char mic_volume; guint register_source; }; static void cv_generic_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_call_volume_cb_t cb = cbd->cb; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } static void hfp_speaker_volume(struct ofono_call_volume *cv, unsigned char percent, ofono_call_volume_cb_t cb, void *data) { struct cv_data *vd = ofono_call_volume_get_data(data); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; vd->sp_volume = percent; snprintf(buf, sizeof(buf), "AT+VGS=%d", (int)(percent*HFP_CALL_VOLUME_MAX/100)); if (g_at_chat_send(vd->chat, buf, vgs_prefix, cv_generic_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void hfp_microphone_volume(struct ofono_call_volume *cv, unsigned char percent, ofono_call_volume_cb_t cb, void *data) { struct cv_data *vd = ofono_call_volume_get_data(data); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; vd->mic_volume = percent; snprintf(buf, sizeof(buf), "AT+VGM=%d", (int)(percent*HFP_CALL_VOLUME_MAX/100)); if (g_at_chat_send(vd->chat, buf, vgm_prefix, cv_generic_set_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, data); } static void vgs_notify(GAtResult *result, gpointer user_data) { struct ofono_call_volume *cv = user_data; struct cv_data *vd = ofono_call_volume_get_data(cv); GAtResultIter iter; gint value; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+VGS:")) return; if (!g_at_result_iter_next_number(&iter, &value)) return; vd->sp_volume = (unsigned char)(value*100/HFP_CALL_VOLUME_MAX); ofono_call_volume_set_speaker_volume(cv, vd->sp_volume); } static void vgm_notify(GAtResult *result, gpointer user_data) { struct ofono_call_volume *cv = user_data; struct cv_data *vd = ofono_call_volume_get_data(cv); GAtResultIter iter; gint value; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+VGM:")) return; if (!g_at_result_iter_next_number(&iter, &value)) return; vd->mic_volume = (unsigned char)(value*100/HFP_CALL_VOLUME_MAX); ofono_call_volume_set_microphone_volume(cv, vd->mic_volume); } static void sync_speaker_volume_cb(const struct ofono_error *error, void *user_data) { struct ofono_call_volume *cv = user_data; struct cv_data *vd = ofono_call_volume_get_data(cv); ofono_call_volume_set_speaker_volume(cv, vd->sp_volume); } static void sync_microphone_volume_cb(const struct ofono_error *error, void *user_data) { struct ofono_call_volume *cv = user_data; struct cv_data *vd = ofono_call_volume_get_data(cv); ofono_call_volume_set_microphone_volume(cv, vd->mic_volume); } static gboolean hfp_call_volume_register(gpointer user_data) { struct ofono_call_volume *cv = user_data; struct cv_data *vd = ofono_call_volume_get_data(cv); DBG(""); vd->register_source = 0; g_at_chat_register(vd->chat, "+VGS:", vgs_notify, FALSE, cv, NULL); g_at_chat_register(vd->chat, "+VGM:", vgm_notify, FALSE, cv, NULL); /* set sp and mic volume at 50 percents by default */ hfp_speaker_volume(cv, 50, sync_speaker_volume_cb, cv); hfp_microphone_volume(cv, 50, sync_microphone_volume_cb, cv); ofono_call_volume_register(cv); return FALSE; } static int hfp_call_volume_probe(struct ofono_call_volume *cv, unsigned int vendor, void *data) { struct hfp_slc_info *info = data; struct cv_data *vd; DBG(""); vd = g_new0(struct cv_data, 1); vd->chat = g_at_chat_clone(info->chat); ofono_call_volume_set_data(cv, vd); vd->register_source = g_idle_add(hfp_call_volume_register, cv); return 0; } static void hfp_call_volume_remove(struct ofono_call_volume *cv) { struct cv_data *vd = ofono_call_volume_get_data(cv); if (vd->register_source != 0) g_source_remove(vd->register_source); ofono_call_volume_set_data(cv, NULL); g_at_chat_unref(vd->chat); g_free(vd); } static struct ofono_call_volume_driver driver = { .name = "hfpmodem", .probe = hfp_call_volume_probe, .remove = hfp_call_volume_remove, .speaker_volume = hfp_speaker_volume, .microphone_volume = hfp_microphone_volume, .mute = NULL, }; void hfp_call_volume_init(void) { ofono_call_volume_driver_register(&driver); } void hfp_call_volume_exit(void) { ofono_call_volume_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/devinfo.c0000644000015600001650000000503512671500024023455 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2011 BMW Car IT GmbH. 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 #endif #include #include #include #include #include #include #include #include "hfpmodem.h" struct devinfo_data { char *device_address; guint register_source; }; static void hfp_query_serial(struct ofono_devinfo *info, ofono_devinfo_query_cb_t cb, void *data) { struct devinfo_data *dev = ofono_devinfo_get_data(info); CALLBACK_WITH_SUCCESS(cb, dev->device_address, data); } static gboolean hfp_devinfo_register(gpointer user_data) { struct ofono_devinfo *info = user_data; struct devinfo_data *dd = ofono_devinfo_get_data(info); dd->register_source = 0; ofono_devinfo_register(info); return FALSE; } static int hfp_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor, void *user) { const char *device_address = user; struct devinfo_data *dd; dd = g_new0(struct devinfo_data, 1); dd->device_address = g_strdup(device_address); ofono_devinfo_set_data(info, dd); dd->register_source = g_idle_add(hfp_devinfo_register, info); return 0; } static void hfp_devinfo_remove(struct ofono_devinfo *info) { struct devinfo_data *dd = ofono_devinfo_get_data(info); ofono_devinfo_set_data(info, NULL); if (dd == NULL) return; if (dd->register_source != 0) g_source_remove(dd->register_source); g_free(dd->device_address); g_free(dd); } static struct ofono_devinfo_driver driver = { .name = "hfpmodem", .probe = hfp_devinfo_probe, .remove = hfp_devinfo_remove, .query_serial = hfp_query_serial }; void hfp_devinfo_init(void) { ofono_devinfo_driver_register(&driver); } void hfp_devinfo_exit(void) { ofono_devinfo_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/slc.c0000644000015600001650000002603412671500024022606 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "hfp.h" #include "slc.h" static const char *none_prefix[] = { NULL }; static const char *brsf_prefix[] = { "+BRSF:", NULL }; static const char *cind_prefix[] = { "+CIND:", NULL }; static const char *cmer_prefix[] = { "+CMER:", NULL }; static const char *chld_prefix[] = { "+CHLD:", NULL }; static const char *bind_prefix[] = { "+BIND:", NULL }; struct slc_establish_data { gint ref_count; struct hfp_slc_info *info; hfp_slc_cb_t failed_cb; hfp_slc_cb_t connect_cb; gpointer userdata; }; void hfp_slc_info_init(struct hfp_slc_info *info, guint16 version) { info->ag_features = 0; info->ag_mpty_features = 0; info->hf_features = HFP_HF_FEATURE_ECNR; info->hf_features |= HFP_HF_FEATURE_3WAY; info->hf_features |= HFP_HF_FEATURE_CLIP; info->hf_features |= HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL; info->hf_features |= HFP_HF_FEATURE_VOICE_RECOGNITION; if (version < HFP_VERSION_1_5) goto done; info->hf_features |= HFP_HF_FEATURE_ENHANCED_CALL_STATUS; info->hf_features |= HFP_HF_FEATURE_ENHANCED_CALL_CONTROL; if (version < HFP_VERSION_1_6) goto done; info->hf_features |= HFP_HF_FEATURE_CODEC_NEGOTIATION; if (version < HFP_VERSION_1_7) goto done; info->hf_features |= HFP_HF_FEATURE_HF_INDICATORS; memset(info->hf_indicators, 0, sizeof(info->hf_indicators)); info->num_hf_indicators = 0; info->hf_indicator_active_map = 0; done: memset(info->cind_val, 0, sizeof(info->cind_val)); memset(info->cind_pos, 0, sizeof(info->cind_pos)); } static void slc_establish_data_unref(gpointer userdata) { struct slc_establish_data *sed = userdata; if (g_atomic_int_dec_and_test(&sed->ref_count)) g_free(sed); } static void slc_establish_data_ref(struct slc_establish_data *sed) { g_atomic_int_inc(&sed->ref_count); } static void slc_failed(struct slc_establish_data *sed) { sed->failed_cb(sed->userdata); } static void slc_established(struct slc_establish_data *sed) { struct hfp_slc_info *info = sed->info; g_at_chat_send(info->chat, "AT+CMEE=1", none_prefix, NULL, NULL, NULL); sed->connect_cb(sed->userdata); } static void bind_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct slc_establish_data *sed = user_data; struct hfp_slc_info *info = sed->info; GAtResultIter iter; int hf_indicator; int enabled; unsigned int i; if (!ok) goto error; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+BIND:")) { if (!g_at_result_iter_next_number(&iter, &hf_indicator)) goto error; if (!g_at_result_iter_next_number(&iter, &enabled)) goto error; ofono_info("AG wants indicator %d %s", hf_indicator, enabled ? "enabled" : "disabled"); for (i = 0; i < info->num_hf_indicators; i++) { if (info->hf_indicators[i] != hf_indicator) continue; info->hf_indicator_active_map |= enabled << i; } ofono_info("Active map: %02x", info->hf_indicator_active_map); } slc_established(sed); return; error: slc_failed(sed); } static void bind_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct slc_establish_data *sed = user_data; struct hfp_slc_info *info = sed->info; GAtResultIter iter; int hf_indicator; if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+BIND:")) goto error; if (!g_at_result_iter_open_list(&iter)) goto error; while (g_at_result_iter_next_number(&iter, &hf_indicator)) { if (info->num_hf_indicators >= 20) goto error; ofono_info("AG supports the following HF indicator: %d", hf_indicator); info->hf_indicators[info->num_hf_indicators] = hf_indicator; info->num_hf_indicators += 1; } if (!g_at_result_iter_close_list(&iter)) goto error; slc_establish_data_ref(sed); g_at_chat_send(info->chat, "AT+BIND?", bind_prefix, bind_query_cb, sed, slc_establish_data_unref); return; error: slc_failed(sed); } static void bind_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct slc_establish_data *sed = user_data; struct hfp_slc_info *info = sed->info; if (!ok) { slc_failed(sed); return; } slc_establish_data_ref(sed); g_at_chat_send(info->chat, "AT+BIND=?", bind_prefix, bind_support_cb, sed, slc_establish_data_unref); } static void chld_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct slc_establish_data *sed = user_data; struct hfp_slc_info *info = sed->info; unsigned int ag_mpty_feature = 0; GAtResultIter iter; const char *str; if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CHLD:")) goto error; if (!g_at_result_iter_open_list(&iter)) goto error; while (g_at_result_iter_next_unquoted_string(&iter, &str)) { if (!strcmp(str, "0")) ag_mpty_feature |= HFP_AG_CHLD_0; else if (!strcmp(str, "1")) ag_mpty_feature |= HFP_AG_CHLD_1; else if (!strcmp(str, "1x")) ag_mpty_feature |= HFP_AG_CHLD_1x; else if (!strcmp(str, "2")) ag_mpty_feature |= HFP_AG_CHLD_2; else if (!strcmp(str, "2x")) ag_mpty_feature |= HFP_AG_CHLD_2x; else if (!strcmp(str, "3")) ag_mpty_feature |= HFP_AG_CHLD_3; else if (!strcmp(str, "4")) ag_mpty_feature |= HFP_AG_CHLD_4; } if (!g_at_result_iter_close_list(&iter)) goto error; info->ag_mpty_features = ag_mpty_feature; if ((info->ag_features & HFP_AG_FEATURE_HF_INDICATORS) && (info->hf_features & HFP_HF_FEATURE_HF_INDICATORS)) { slc_establish_data_ref(sed); g_at_chat_send(info->chat, "AT+BIND=1", none_prefix, bind_set_cb, sed, slc_establish_data_unref); } else slc_established(sed); return; error: slc_failed(sed); } static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct slc_establish_data *sed = user_data; struct hfp_slc_info *info = sed->info; if (!ok) { slc_failed(sed); return; } if (info->ag_features & HFP_AG_FEATURE_3WAY) { slc_establish_data_ref(sed); g_at_chat_send(info->chat, "AT+CHLD=?", chld_prefix, chld_cb, sed, slc_establish_data_unref); } else slc_established(sed); } static void cind_status_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct slc_establish_data *sed = user_data; struct hfp_slc_info *info = sed->info; GAtResultIter iter; int index; int value; if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CIND:")) goto error; index = 1; while (g_at_result_iter_next_number(&iter, &value)) { int i; for (i = 0; i < HFP_INDICATOR_LAST; i++) { if (index != info->cind_pos[i]) continue; info->cind_val[i] = value; } index += 1; } slc_establish_data_ref(sed); g_at_chat_send(info->chat, "AT+CMER=3,0,0,1", cmer_prefix, cmer_cb, sed, slc_establish_data_unref); return; error: slc_failed(sed); } static void cind_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct slc_establish_data *sed = user_data; struct hfp_slc_info *info = sed->info; GAtResultIter iter; const char *str; int index; int min, max; if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CIND:")) goto error; index = 1; while (g_at_result_iter_open_list(&iter)) { if (!g_at_result_iter_next_string(&iter, &str)) goto error; if (!g_at_result_iter_open_list(&iter)) goto error; while (g_at_result_iter_next_range(&iter, &min, &max)) ; if (!g_at_result_iter_close_list(&iter)) goto error; if (!g_at_result_iter_close_list(&iter)) goto error; if (g_str_equal("service", str) == TRUE) info->cind_pos[HFP_INDICATOR_SERVICE] = index; else if (g_str_equal("call", str) == TRUE) info->cind_pos[HFP_INDICATOR_CALL] = index; else if (g_str_equal("callsetup", str) == TRUE) info->cind_pos[HFP_INDICATOR_CALLSETUP] = index; else if (g_str_equal("callheld", str) == TRUE) info->cind_pos[HFP_INDICATOR_CALLHELD] = index; else if (g_str_equal("signal", str) == TRUE) info->cind_pos[HFP_INDICATOR_SIGNAL] = index; else if (g_str_equal("roam", str) == TRUE) info->cind_pos[HFP_INDICATOR_ROAM] = index; else if (g_str_equal("battchg", str) == TRUE) info->cind_pos[HFP_INDICATOR_BATTCHG] = index; index += 1; } slc_establish_data_ref(sed); g_at_chat_send(info->chat, "AT+CIND?", cind_prefix, cind_status_cb, sed, slc_establish_data_unref); return; error: slc_failed(sed); } static void bac_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct slc_establish_data *sed = user_data; struct hfp_slc_info *info = sed->info; if (!ok) { slc_failed(sed); return; } slc_establish_data_ref(sed); g_at_chat_send(info->chat, "AT+CIND=?", cind_prefix, cind_cb, sed, slc_establish_data_unref); } static void brsf_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct slc_establish_data *sed = user_data; struct hfp_slc_info *info = sed->info; GAtResultIter iter; if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+BRSF:")) goto error; g_at_result_iter_next_number(&iter, (gint *)&info->ag_features); if (info->ag_features & HFP_AG_FEATURE_CODEC_NEGOTIATION && info->hf_features & HFP_HF_FEATURE_CODEC_NEGOTIATION) { char str[32]; memset(str, 0, sizeof(str)); if (ofono_handsfree_audio_has_wideband()) sprintf(str, "AT+BAC=%d,%d", HFP_CODEC_CVSD, HFP_CODEC_MSBC); else sprintf(str, "AT+BAC=%d", HFP_CODEC_CVSD); slc_establish_data_ref(sed); g_at_chat_send(info->chat, str, none_prefix, bac_cb, sed, slc_establish_data_unref); return; } slc_establish_data_ref(sed); g_at_chat_send(info->chat, "AT+CIND=?", cind_prefix, cind_cb, sed, slc_establish_data_unref); return; error: slc_failed(sed); } void hfp_slc_establish(struct hfp_slc_info *info, hfp_slc_cb_t connect_cb, hfp_slc_cb_t failed_cb, void *userdata) { char buf[64]; struct slc_establish_data *sed = g_new0(struct slc_establish_data, 1); sed->ref_count = 1; sed->connect_cb = connect_cb; sed->failed_cb = failed_cb; sed->userdata = userdata; sed->info = info; snprintf(buf, sizeof(buf), "AT+BRSF=%d", info->hf_features); g_at_chat_send(info->chat, buf, brsf_prefix, brsf_cb, sed, slc_establish_data_unref); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/network-registration.c0000644000015600001650000002064612671500024026231 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2009 ProFUSION embedded systems. 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "common.h" #include "hfpmodem.h" #include "slc.h" #define HFP_MAX_OPERATOR_NAME_LENGTH 16 static const char *cops_prefix[] = { "+COPS:", NULL }; static const char *cind_prefix[] = { "+CIND:", NULL }; static const char *none_prefix[] = { NULL }; struct netreg_data { GAtChat *chat; unsigned char cind_pos[HFP_INDICATOR_LAST]; int cind_val[HFP_INDICATOR_LAST]; guint register_source; }; static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_operator_cb_t cb = cbd->cb; struct ofono_network_operator op; GAtResultIter iter; int format; const char *name; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, NULL, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+COPS:")) goto error; g_at_result_iter_skip_next(&iter); ok = g_at_result_iter_next_number(&iter, &format); if (ok == FALSE || format != 0) goto error; if (g_at_result_iter_next_string(&iter, &name) == FALSE) goto error; strncpy(op.name, name, HFP_MAX_OPERATOR_NAME_LENGTH); op.name[HFP_MAX_OPERATOR_NAME_LENGTH] = '\0'; op.mcc[0] = '\0'; op.mnc[0] = '\0'; op.status = 2; op.tech = -1; cb(&error, &op, cbd->data); return; error: CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); } static void ciev_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; int index, value, status; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CIEV:")) return; if (!g_at_result_iter_next_number(&iter, &index)) return; if (!g_at_result_iter_next_number(&iter, &value)) return; if (index == nd->cind_pos[HFP_INDICATOR_SERVICE]) { nd->cind_val[HFP_INDICATOR_SERVICE] = value; if (value) status = NETWORK_REGISTRATION_STATUS_REGISTERED; else status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED; ofono_netreg_status_notify(netreg, status, -1, -1, -1); } else if (index == nd->cind_pos[HFP_INDICATOR_ROAM]) { nd->cind_val[HFP_INDICATOR_ROAM] = value; if (value) status = NETWORK_REGISTRATION_STATUS_ROAMING; else if (nd->cind_val[HFP_INDICATOR_SERVICE]) status = NETWORK_REGISTRATION_STATUS_REGISTERED; else status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED; ofono_netreg_status_notify(netreg, status, -1, -1, -1); } else if (index == nd->cind_pos[HFP_INDICATOR_SIGNAL]) { nd->cind_val[HFP_INDICATOR_SIGNAL] = value; ofono_netreg_strength_notify(netreg, value * 20); } return; } static void signal_strength_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_strength_cb_t cb = cbd->cb; struct netreg_data *nd = ofono_netreg_get_data(cbd->user); GAtResultIter iter; int index, strength; struct ofono_error error; 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, "+CIND:")) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); return; } index = 1; while (g_at_result_iter_next_number(&iter, &strength)) { if (index == nd->cind_pos[HFP_INDICATOR_SIGNAL]) { nd->cind_val[HFP_INDICATOR_SIGNAL] = strength; break; } index++; } DBG("signal_strength_cb: %d", strength); cb(&error, strength * 20, cbd->data); } static void registration_status_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_status_cb_t cb = cbd->cb; struct netreg_data *nd = ofono_netreg_get_data(cbd->user); GAtResultIter iter; int index, value, status; struct ofono_error error; decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, -1, -1, -1, -1, cbd->data); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CIND:")) { CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data); return; } index = 1; while (g_at_result_iter_next_number(&iter, &value)) { if (index == nd->cind_pos[HFP_INDICATOR_SERVICE]) nd->cind_val[HFP_INDICATOR_SERVICE] = value; if (index == nd->cind_pos[HFP_INDICATOR_ROAM]) nd->cind_val[HFP_INDICATOR_ROAM] = value; index++; } if (nd->cind_val[HFP_INDICATOR_SERVICE]) status = NETWORK_REGISTRATION_STATUS_REGISTERED; else status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED; if (nd->cind_val[HFP_INDICATOR_ROAM]) status = NETWORK_REGISTRATION_STATUS_ROAMING; cb(&error, status, -1, -1, -1, cbd->data); } static void hfp_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); gboolean ok; cbd->user = netreg; ok = g_at_chat_send(nd->chat, "AT+CIND?", cind_prefix, registration_status_cb, cbd, g_free); if (ok) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data); } static void hfp_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); gboolean ok; cbd->user = netreg; ok = g_at_chat_send(nd->chat, "AT+COPS=3,0", none_prefix, NULL, cbd, NULL); if (ok) ok = g_at_chat_send(nd->chat, "AT+COPS?", cops_prefix, cops_cb, cbd, g_free); if (ok) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, NULL, data); } static void hfp_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); cbd->user = netreg; if (g_at_chat_send(nd->chat, "AT+CIND?", cind_prefix, signal_strength_cb, cbd, g_free) > 0) return; g_free(cbd); CALLBACK_WITH_FAILURE(cb, -1, data); } static gboolean hfp_netreg_register(gpointer user_data) { struct ofono_netreg *netreg = user_data; struct netreg_data *nd = ofono_netreg_get_data(netreg); nd->register_source = 0; g_at_chat_register(nd->chat, "+CIEV:", ciev_notify, FALSE, netreg, NULL); ofono_netreg_register(netreg); return FALSE; } static int hfp_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor, void *user_data) { struct hfp_slc_info *info = user_data; struct netreg_data *nd; nd = g_new0(struct netreg_data, 1); nd->chat = g_at_chat_clone(info->chat); memcpy(nd->cind_pos, info->cind_pos, HFP_INDICATOR_LAST); memcpy(nd->cind_val, info->cind_val, HFP_INDICATOR_LAST); ofono_netreg_set_data(netreg, nd); nd->register_source = g_idle_add(hfp_netreg_register, netreg); return 0; } static void hfp_netreg_remove(struct ofono_netreg *netreg) { struct netreg_data *nd = ofono_netreg_get_data(netreg); if (nd->register_source != 0) g_source_remove(nd->register_source); ofono_netreg_set_data(netreg, NULL); g_at_chat_unref(nd->chat); g_free(nd); } static struct ofono_netreg_driver driver = { .name = "hfpmodem", .probe = hfp_netreg_probe, .remove = hfp_netreg_remove, .registration_status = hfp_registration_status, .current_operator = hfp_current_operator, .strength = hfp_signal_strength, }; void hfp_netreg_init(void) { ofono_netreg_driver_register(&driver); } void hfp_netreg_exit(void) { ofono_netreg_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/hfpmodem/hfpmodem.c0000644000015600001650000000266212671500024023625 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "hfpmodem.h" static int hfpmodem_init(void) { hfp_voicecall_init(); hfp_devinfo_init(); hfp_netreg_init(); hfp_call_volume_init(); hfp_handsfree_init(); hfp_siri_init(); return 0; } static void hfpmodem_exit(void) { hfp_voicecall_exit(); hfp_devinfo_exit(); hfp_netreg_exit(); hfp_call_volume_exit(); hfp_handsfree_exit(); hfp_siri_exit(); } OFONO_PLUGIN_DEFINE(hfpmodem, "Hands-Free Profile Driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, hfpmodem_init, hfpmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/telitmodem/0000755000015600001650000000000012671500304022221 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/telitmodem/telitmodem.c0000644000015600001650000000237312671500024024534 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "telitmodem.h" static int telitmodem_init(void) { telit_location_reporting_init(); return 0; } static void telitmodem_exit(void) { telit_location_reporting_exit(); } OFONO_PLUGIN_DEFINE(telitmodem, "Telit modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, telitmodem_init, telitmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/drivers/telitmodem/telitmodem.h0000644000015600001650000000161512671500024024537 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void telit_location_reporting_init(); extern void telit_location_reporting_exit(); ofono-1.17.bzr6912+16.04.20160314.3/drivers/telitmodem/location-reporting.c0000644000015600001650000001606612671500024026214 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2014 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "gatchat.h" #include "gatresult.h" #include "gattty.h" #include "telitmodem.h" static const char *none_prefix[] = { NULL }; static const char *portcfg_prefix[] = { "#PORTCFG:", NULL }; static const char *gpsctl_prefix[] = { "$GPSP:", NULL }; struct gps_data { GAtChat *chat; }; static void telit_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 telit_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$GPSP=0", none_prefix, telit_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 telit_gps_ctl_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 telit_gps_enable_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 gps_data *gd = ofono_location_reporting_get_data(lr); struct ofono_error error; DBG("lr=%p ok=%d", lr, ok); decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, -1, cbd->data); g_free(cbd); return; } if (g_at_chat_send(gd->chat, "AT$GPSNMUN=1,0,0,0,0,0,0", none_prefix, telit_gps_ctl_cb, cbd, g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); } static void telit_portcfg_check_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 gps_data *gd = ofono_location_reporting_get_data(lr); struct ofono_error error; int requested_portcfg, current_portcfg; GAtResultIter iter; DBG("lr=%p ok=%d", lr, ok); decode_at_error(&error, g_at_result_final_response(result)); if (!ok) { cb(&error, -1, cbd->data); g_free(cbd); return; } g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "#PORTCFG:")) goto fail; if (!g_at_result_iter_next_number(&iter, &requested_portcfg)) goto fail; if (!g_at_result_iter_next_number(&iter, ¤t_portcfg)) goto fail; if (current_portcfg != 8) { ofono_warn("Unable to start GPS, modem configuration invalid"); ofono_warn("Refer to doc/telit-modem.txt section HE910/GPS"); goto fail; } if (g_at_chat_send(gd->chat, "AT$GPSP=1", none_prefix, telit_gps_enable_cb, cbd, NULL) > 0) return; fail: CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); } static void telit_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#PORTCFG?", portcfg_prefix, telit_portcfg_check_cb, cbd, NULL) > 0) return; CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); } static void telit_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 telit_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$GPSP=?", gpsctl_prefix, telit_location_reporting_support_cb, lr, NULL); return 0; } static void telit_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 = "telitmodem", .type = OFONO_LOCATION_REPORTING_TYPE_NMEA, .probe = telit_location_reporting_probe, .remove = telit_location_reporting_remove, .enable = telit_location_reporting_enable, .disable = telit_location_reporting_disable, }; void telit_location_reporting_init() { ofono_location_reporting_driver_register(&driver); } void telit_location_reporting_exit() { ofono_location_reporting_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/dunmodem/0000755000015600001650000000000012671500304021666 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/drivers/dunmodem/dunmodem.h0000644000015600001650000000167412671500024023656 0ustar pbuserpbgroup00000000000000/* * * 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 * */ #include extern void dun_netreg_init(void); extern void dun_netreg_exit(void); extern void dun_gprs_init(void); extern void dun_gprs_exit(void); ofono-1.17.bzr6912+16.04.20160314.3/drivers/dunmodem/network-registration.c0000644000015600001650000000532512671500024026237 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include "common.h" #include "dunmodem.h" static const char *cops_prefix[] = { "+COPS:", NULL }; struct netreg_data { GAtChat *chat; }; static void dun_registration_status(struct ofono_netreg *netreg, ofono_netreg_status_cb_t cb, void *data) { int status = NETWORK_REGISTRATION_STATUS_REGISTERED; DBG(""); CALLBACK_WITH_SUCCESS(cb, status, -1, -1, -1, data); } static void dun_current_operator(struct ofono_netreg *netreg, ofono_netreg_operator_cb_t cb, void *data) { struct ofono_network_operator op; DBG(""); op.name[0] = '\0'; op.mcc[0] = '\0'; op.mnc[0] = '\0'; op.status = 2; op.tech = -1; CALLBACK_WITH_SUCCESS(cb, &op, data); } static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; if (!ok) return; ofono_netreg_register(netreg); } static int dun_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor, void *user_data) { GAtChat *chat = user_data; struct netreg_data *nd; nd = g_try_new0(struct netreg_data, 1); if (nd == NULL) return -ENOMEM; nd->chat = g_at_chat_clone(chat); ofono_netreg_set_data(netreg, nd); g_at_chat_send(nd->chat, "AT+COPS=0", cops_prefix, cops_cb, netreg, NULL); return 0; } static void dun_netreg_remove(struct ofono_netreg *netreg) { struct netreg_data *nd = ofono_netreg_get_data(netreg); ofono_netreg_set_data(netreg, NULL); g_free(nd); } static struct ofono_netreg_driver driver = { .name = "dunmodem", .probe = dun_netreg_probe, .remove = dun_netreg_remove, .registration_status = dun_registration_status, .current_operator = dun_current_operator, }; void dun_netreg_init(void) { ofono_netreg_driver_register(&driver); } void dun_netreg_exit(void) { ofono_netreg_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/dunmodem/gprs.c0000644000015600001650000000375212671500024023013 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 BMW Car IT GmbH. 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 #endif #include #include #include #include #include #include "dunmodem.h" static void dun_gprs_set_attached(struct ofono_gprs *gprs, int attached, ofono_gprs_cb_t cb, void *data) { DBG(""); CALLBACK_WITH_SUCCESS(cb, data); } static gboolean dun_gprs_finish_registration(gpointer user_data) { struct ofono_gprs *gprs = user_data; ofono_gprs_register(gprs); return FALSE; } static int dun_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor, void *data) { DBG(""); g_idle_add(dun_gprs_finish_registration, gprs); return 0; } static void dun_gprs_remove(struct ofono_gprs *gprs) { DBG(""); } static void dun_gprs_attached_status(struct ofono_gprs *gprs, ofono_gprs_status_cb_t cb, void *data) { DBG(""); CALLBACK_WITH_SUCCESS(cb, 1, data); } static struct ofono_gprs_driver driver = { .name = "dunmodem", .probe = dun_gprs_probe, .remove = dun_gprs_remove, .set_attached = dun_gprs_set_attached, .attached_status = dun_gprs_attached_status, }; void dun_gprs_init(void) { ofono_gprs_driver_register(&driver); } void dun_gprs_exit(void) { ofono_gprs_driver_unregister(&driver); } ofono-1.17.bzr6912+16.04.20160314.3/drivers/dunmodem/dunmodem.c0000644000015600001650000000237012671500024023643 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "dunmodem.h" static int dunmodem_init(void) { dun_netreg_init(); dun_gprs_init(); return 0; } static void dunmodem_exit(void) { dun_netreg_exit(); dun_gprs_exit(); } OFONO_PLUGIN_DEFINE(dunmodem, "Dialup modem driver", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, dunmodem_init, dunmodem_exit) ofono-1.17.bzr6912+16.04.20160314.3/ofono.pc.in0000644000015600001650000000050512671500024020450 0ustar pbuserpbgroup00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ plugindir=${libdir}/@PACKAGE@/plugins Name: @PACKAGE@ Description: oFono - Open Source Telephony Version: @VERSION@ Requires: glib-2.0 dbus-1 Cflags: -I${includedir} Libs: -module -avoid-version -export-symbols-regex '@PACKAGE@_plugin_desc' ofono-1.17.bzr6912+16.04.20160314.3/README0000644000015600001650000000156612671500024017267 0ustar pbuserpbgroup00000000000000oFono - Open Source Telephony ***************************** Copyright (C) 2008-2011 Intel Corporation. All rights reserved. Compilation and installation ============================ In order to compile telephony stack you need following software packages: - GCC compiler - GLib library - D-Bus library To configure run: ./configure --prefix=/usr --mandir=/usr/share/man \ --sysconfdir=/etc --localstatedir=/var Configure automatically searches for all required components and packages. To compile and install run: make && make install Kernel Dependencies =================== In order to have the PPP stack working in oFono you need to enable CONFIG_TUN (Universal TUN/TAP device driver support) in your kernel .config. Information =========== Mailing list: ofono@ofono.org For additional information about the project visit oFono web site: http://www.ofono.org ofono-1.17.bzr6912+16.04.20160314.3/Makefile.am0000644000015600001650000007432112671500073020446 0ustar pbuserpbgroup00000000000000 AM_MAKEFLAGS = --no-print-directory noinst_LTLIBRARIES = pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \ include/dbus.h include/modem.h include/types.h \ include/call-barring.h include/call-forwarding.h \ include/call-meter.h include/call-settings.h \ include/phonebook.h include/ussd.h \ include/sms.h include/sim.h include/message-waiting.h \ include/netreg.h include/voicecall.h include/devinfo.h \ include/cbs.h include/call-volume.h \ include/gprs.h include/gprs-context.h \ include/radio-settings.h include/stk.h \ include/audio-settings.h include/nettime.h \ include/ctm.h include/cdma-voicecall.h \ include/cdma-sms.h include/sim-auth.h \ include/gprs-provision.h include/emulator.h \ include/location-reporting.h \ include/cdma-connman.h include/gnss.h \ include/private-network.h include/cdma-netreg.h \ include/cdma-provision.h include/handsfree.h \ include/handsfree-audio.h include/siri.h \ include/sim-mnclength.h include/spn-table.h \ include/dns-client.h include/wakelock.h \ include/system-settings.h nodist_pkginclude_HEADERS = include/version.h local_headers = $(foreach file,$(pkginclude_HEADERS) \ $(nodist_pkginclude_HEADERS), \ include/ofono/$(notdir $(file))) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = ofono.pc if DATAFILES dbusconfdir = @DBUS_CONFDIR@ dist_dbusconf_DATA = src/ofono.conf if SYSTEMD systemdunitdir = @SYSTEMD_UNITDIR@ systemdunit_DATA = src/ofono.service endif confdir = $(sysconfdir)/ofono dist_conf_DATA = rules_files = plugins/ofono.rules plugins/ofono-speedup.rules rulesdir = @UDEV_DATADIR@ rules_DATA = $(foreach file,$(rules_files), plugins/97-$(notdir $(file))) statedir = $(localstatedir)/lib/ofono state_DATA = endif builtin_modules = builtin_sources = builtin_libadd = builtin_cflags = noinst_LTLIBRARIES += gdbus/libgdbus-internal.la gdbus_libgdbus_internal_la_SOURCES = gdbus/gdbus.h \ gdbus/mainloop.c gdbus/watch.c \ gdbus/object.c gdbus/client.c gdbus/polkit.c gatchat_sources = gatchat/gatchat.h gatchat/gatchat.c \ gatchat/gatresult.h gatchat/gatresult.c \ gatchat/gatsyntax.h gatchat/gatsyntax.c \ gatchat/ringbuffer.h gatchat/ringbuffer.c \ gatchat/gatio.h gatchat/gatio.c \ gatchat/crc-ccitt.h gatchat/crc-ccitt.c \ gatchat/gatmux.h gatchat/gatmux.c \ gatchat/gsm0710.h gatchat/gsm0710.c \ gatchat/gattty.h gatchat/gattty.c \ gatchat/gatutil.h gatchat/gatutil.c \ gatchat/gat.h \ gatchat/gatserver.h gatchat/gatserver.c \ gatchat/gatrawip.h gatchat/gatrawip.c \ gatchat/gathdlc.c gatchat/gathdlc.h \ gatchat/gatppp.c gatchat/gatppp.h \ gatchat/ppp.h gatchat/ppp_cp.h \ gatchat/ppp_cp.c gatchat/ppp_lcp.c \ gatchat/ppp_auth.c gatchat/ppp_net.c \ gatchat/ppp_ipcp.c gatchat/ppp_ipv6cp.c gisi_sources = gisi/client.c gisi/client.h gisi/common.h \ gisi/iter.c gisi/iter.h \ gisi/message.c gisi/message.h \ gisi/modem.c gisi/modem.h \ gisi/netlink.c gisi/netlink.h \ gisi/pep.c gisi/pep.h \ gisi/phonet.h \ gisi/pipe.c gisi/pipe.h \ gisi/server.c gisi/server.h \ gisi/socket.c gisi/socket.h gril_sources = gril/gril.h gril/gril.c gril/grilio.h \ gril/grilio.c gril/grilutil.h \ gril/grilutil.c gril/ringbuffer.h \ gril/gfunc.h gril/ril.h \ gril/parcel.c gril/parcel.h \ gril/grilreply.c gril/grilreply.h \ gril/grilrequest.c gril/grilrequest.h \ gril/grilunsol.c gril/grilunsol.h 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@ builtin_modules += udevng builtin_sources += plugins/udevng.c endif if RILMODEM builtin_sources += $(gril_sources) builtin_modules += rildev builtin_sources += plugins/rildev.c builtin_modules += ril builtin_sources += plugins/ril.c builtin_modules += mtk builtin_sources += plugins/mtk.c builtin_modules += infineon builtin_sources += plugins/infineon.c builtin_modules += qcom_msim builtin_sources += plugins/qcom-msim.c builtin_modules += androidspntable builtin_sources += plugins/android-spn-table.c builtin_modules += mtkmodem builtin_sources += drivers/mtkmodem/mtkmodem.h \ drivers/mtkmodem/mtkmodem.c \ drivers/mtkmodem/mtkutil.h \ drivers/mtkmodem/mtkutil.c \ drivers/mtkmodem/mtkrequest.h \ drivers/mtkmodem/mtkrequest.c \ drivers/mtkmodem/mtkunsol.h \ drivers/mtkmodem/mtkunsol.c \ drivers/mtkmodem/mtkreply.h \ drivers/mtkmodem/mtkreply.c \ drivers/mtkmodem/voicecall.c \ drivers/mtkmodem/gprs.c \ drivers/mtkmodem/radio-settings.c \ drivers/mtkmodem/mtksettings.c builtin_modules += rilmodem builtin_sources += drivers/rilmodem/rilmodem.h \ drivers/rilmodem/vendor.h \ drivers/rilmodem/rilmodem.c \ drivers/rilmodem/devinfo.c \ drivers/rilmodem/network-registration.c \ drivers/rilmodem/voicecall.c \ drivers/rilmodem/call-volume.c \ drivers/rilmodem/gprs.c \ drivers/rilmodem/gprs-context.c \ drivers/rilmodem/sim.c \ drivers/rilmodem/sms.c \ drivers/rilmodem/rilutil.c \ drivers/rilmodem/rilutil.h \ drivers/rilmodem/ussd.c \ drivers/rilmodem/call-settings.c \ drivers/rilmodem/call-forwarding.c \ drivers/rilmodem/radio-settings.c \ drivers/rilmodem/call-barring.c \ drivers/rilmodem/phonebook.c builtin_modules += qcommsimmodem builtin_sources += drivers/qcommsimmodem/qcom_msim_modem.c \ drivers/qcommsimmodem/radio-settings.c endif if ISIMODEM builtin_modules += isimodem builtin_sources += $(gisi_sources) \ drivers/isimodem/isimodem.h \ drivers/isimodem/isimodem.c \ drivers/isimodem/mtc.h \ drivers/isimodem/debug.h \ drivers/isimodem/isiutil.h \ drivers/isimodem/debug.c \ drivers/isimodem/phonebook.c \ drivers/isimodem/devinfo.c \ drivers/isimodem/info.h \ drivers/isimodem/network-registration.c \ drivers/isimodem/network.h \ drivers/isimodem/infoserver.h \ drivers/isimodem/infoserver.c \ drivers/isimodem/voicecall.c \ drivers/isimodem/call.h \ drivers/isimodem/sms.c \ drivers/isimodem/sms.h \ drivers/isimodem/cbs.c \ drivers/isimodem/sim.c \ drivers/isimodem/sim.h \ drivers/isimodem/ussd.c \ drivers/isimodem/call-forwarding.c \ drivers/isimodem/call-settings.c \ drivers/isimodem/call-barring.c \ drivers/isimodem/call-meter.c \ drivers/isimodem/ss.h \ drivers/isimodem/radio-settings.c \ drivers/isimodem/gss.h \ drivers/isimodem/gprs.c \ drivers/isimodem/gprs-context.c \ drivers/isimodem/gpds.h \ drivers/isimodem/audio-settings.c \ drivers/isimodem/uicc.h \ drivers/isimodem/uicc.c \ drivers/isimodem/uicc-util.h \ drivers/isimodem/uicc-util.c builtin_modules += isiusb builtin_sources += plugins/isiusb.c builtin_modules += n900 builtin_sources += plugins/n900.c plugins/nokia-gpio.h plugins/nokia-gpio.c builtin_modules += u8500 builtin_sources += plugins/u8500.c endif if QMIMODEM qmi_sources = drivers/qmimodem/qmi.h drivers/qmimodem/qmi.c \ drivers/qmimodem/ctl.h \ drivers/qmimodem/dms.h \ drivers/qmimodem/nas.h \ drivers/qmimodem/uim.h \ drivers/qmimodem/wms.h \ drivers/qmimodem/wds.h \ drivers/qmimodem/pds.h \ drivers/qmimodem/common.h builtin_modules += qmimodem builtin_sources += $(qmi_sources) \ drivers/qmimodem/util.h \ drivers/qmimodem/qmimodem.h \ drivers/qmimodem/qmimodem.c \ drivers/qmimodem/devinfo.c \ drivers/qmimodem/voicecall.c \ drivers/qmimodem/network-registration.c \ drivers/qmimodem/sim-legacy.c \ drivers/qmimodem/sim.c \ drivers/qmimodem/sms.c \ drivers/qmimodem/ussd.c \ drivers/qmimodem/gprs.c \ drivers/qmimodem/gprs-context.c \ drivers/qmimodem/radio-settings.c \ drivers/qmimodem/location-reporting.c builtin_modules += gobi builtin_sources += plugins/gobi.c endif if ATMODEM builtin_modules += atmodem builtin_sources += $(gatchat_sources) \ drivers/atmodem/atmodem.h \ drivers/atmodem/atmodem.c \ drivers/atmodem/call-settings.c \ drivers/atmodem/sms.c \ drivers/atmodem/cbs.c \ drivers/atmodem/call-forwarding.c \ drivers/atmodem/call-meter.c \ drivers/atmodem/network-registration.c \ drivers/atmodem/sim.c \ drivers/atmodem/stk.c \ drivers/atmodem/stk.h \ drivers/atmodem/ussd.c \ drivers/atmodem/voicecall.c \ drivers/atmodem/call-barring.c \ drivers/atmodem/phonebook.c \ drivers/atmodem/devinfo.c \ drivers/atmodem/call-volume.c \ drivers/atmodem/vendor.h \ drivers/atmodem/atutil.h \ drivers/atmodem/atutil.c \ drivers/atmodem/gprs.c \ drivers/atmodem/gprs-context.c \ drivers/atmodem/sim-auth.c \ drivers/atmodem/gnss.c builtin_modules += nwmodem builtin_sources += drivers/atmodem/atutil.h \ drivers/nwmodem/nwmodem.h \ drivers/nwmodem/nwmodem.c \ drivers/nwmodem/radio-settings.c builtin_modules += swmodem builtin_sources += drivers/atmodem/atutil.h \ drivers/swmodem/swmodem.h \ drivers/swmodem/swmodem.c \ drivers/swmodem/gprs-context.c builtin_modules += ztemodem builtin_sources += drivers/atmodem/atutil.h \ drivers/ztemodem/ztemodem.h \ drivers/ztemodem/ztemodem.c \ drivers/ztemodem/radio-settings.c builtin_modules += iceramodem builtin_sources += drivers/atmodem/atutil.h \ drivers/iceramodem/iceramodem.h \ drivers/iceramodem/iceramodem.c \ drivers/iceramodem/gprs-context.c \ drivers/iceramodem/radio-settings.c builtin_modules += huaweimodem builtin_sources += drivers/atmodem/atutil.h \ drivers/huaweimodem/huaweimodem.h \ drivers/huaweimodem/huaweimodem.c \ drivers/huaweimodem/ussd.c \ drivers/huaweimodem/voicecall.c \ drivers/huaweimodem/audio-settings.c \ drivers/huaweimodem/gprs-context.c \ drivers/huaweimodem/radio-settings.c \ drivers/huaweimodem/cdma-netreg.c builtin_modules += calypsomodem builtin_sources += drivers/atmodem/atutil.h \ drivers/calypsomodem/calypsomodem.h \ drivers/calypsomodem/calypsomodem.c \ drivers/calypsomodem/voicecall.c \ drivers/calypsomodem/stk.c builtin_modules += mbmmodem builtin_sources += drivers/atmodem/atutil.h \ drivers/mbmmodem/mbmmodem.h \ drivers/mbmmodem/mbmmodem.c \ drivers/mbmmodem/gprs-context.c \ drivers/mbmmodem/stk.c \ drivers/mbmmodem/location-reporting.c builtin_modules += telitmodem builtin_sources += drivers/atmodem/atutil.h \ drivers/telitmodem/telitmodem.h \ drivers/telitmodem/telitmodem.c \ drivers/telitmodem/location-reporting.c builtin_modules += hsomodem builtin_sources += drivers/atmodem/atutil.h \ drivers/hsomodem/hsomodem.h \ drivers/hsomodem/hsomodem.c \ drivers/hsomodem/gprs-context.c \ drivers/hsomodem/radio-settings.c builtin_modules += ifxmodem builtin_sources += drivers/atmodem/atutil.h \ drivers/ifxmodem/ifxmodem.h \ drivers/ifxmodem/ifxmodem.c \ drivers/ifxmodem/voicecall.c \ drivers/ifxmodem/audio-settings.c \ drivers/ifxmodem/radio-settings.c \ drivers/ifxmodem/gprs-context.c \ drivers/ifxmodem/stk.c \ drivers/ifxmodem/ctm.c builtin_modules += stemodem builtin_sources += drivers/atmodem/atutil.h \ drivers/stemodem/stemodem.h \ drivers/stemodem/stemodem.c \ drivers/stemodem/voicecall.c \ drivers/stemodem/radio-settings.c \ drivers/stemodem/caif_rtnl.c \ drivers/stemodem/caif_rtnl.h \ drivers/stemodem/gprs-context.c \ drivers/stemodem/caif_socket.h \ drivers/stemodem/if_caif.h builtin_modules += dunmodem builtin_sources += drivers/atmodem/atutil.h \ drivers/dunmodem/dunmodem.h \ drivers/dunmodem/dunmodem.c \ drivers/dunmodem/network-registration.c \ drivers/dunmodem/gprs.c builtin_modules += hfpmodem builtin_sources += drivers/atmodem/atutil.h \ drivers/hfpmodem/hfpmodem.h \ drivers/hfpmodem/hfpmodem.c \ drivers/hfpmodem/slc.h \ drivers/hfpmodem/slc.c \ drivers/hfpmodem/voicecall.c \ drivers/hfpmodem/network-registration.c \ drivers/hfpmodem/call-volume.c \ drivers/hfpmodem/devinfo.c \ drivers/hfpmodem/handsfree.c \ drivers/hfpmodem/siri.c builtin_modules += speedupmodem builtin_sources += drivers/atmodem/atutil.h \ drivers/speedupmodem/speedupmodem.h \ drivers/speedupmodem/speedupmodem.c \ drivers/speedupmodem/ussd.c if PHONESIM builtin_modules += phonesim builtin_sources += plugins/phonesim.c if DATAFILES dist_conf_DATA += plugins/phonesim.conf endif endif if CDMAMODEM builtin_modules += cdmamodem builtin_sources += drivers/cdmamodem/cdmamodem.h \ drivers/cdmamodem/cdmamodem.c \ drivers/cdmamodem/voicecall.c \ drivers/cdmamodem/devinfo.c \ drivers/cdmamodem/connman.c endif builtin_modules += g1 builtin_sources += plugins/g1.c builtin_modules += wavecom builtin_sources += plugins/wavecom.c builtin_modules += calypso builtin_sources += plugins/calypso.c builtin_modules += mbm builtin_sources += plugins/mbm.c builtin_modules += hso builtin_sources += plugins/hso.c builtin_modules += zte builtin_sources += plugins/zte.c builtin_modules += huawei builtin_sources += plugins/huawei.c builtin_modules += sierra builtin_sources += plugins/sierra.c builtin_modules += novatel builtin_sources += plugins/novatel.c builtin_modules += palmpre builtin_sources += plugins/palmpre.c builtin_modules += ifx builtin_sources += plugins/ifx.c builtin_modules += ste builtin_sources += plugins/ste.c builtin_modules += stemgr builtin_sources += plugins/stemgr.c builtin_modules += caif builtin_sources += plugins/caif.c builtin_modules += cinterion builtin_sources += plugins/cinterion.c builtin_modules += nokia builtin_sources += plugins/nokia.c builtin_modules += nokiacdma builtin_sources += plugins/nokiacdma.c builtin_modules += linktop builtin_sources += plugins/linktop.c builtin_modules += icera builtin_sources += plugins/icera.c builtin_modules += alcatel builtin_sources += plugins/alcatel.c builtin_modules += speedup builtin_sources += plugins/speedup.c builtin_modules += speedupcdma builtin_sources += plugins/speedupcdma.c builtin_modules += samsung builtin_sources += plugins/samsung.c builtin_modules += sim900 builtin_sources += plugins/sim900.c builtin_modules += connman builtin_sources += plugins/connman.c builtin_modules += he910 builtin_sources += plugins/he910.c builtin_modules += quectel builtin_sources += plugins/quectel.c builtin_modules += ublox builtin_sources += plugins/ublox.c if BLUETOOTH if BLUEZ4 builtin_modules += bluez4 builtin_sources += plugins/bluez4.c plugins/bluez4.h builtin_modules += telit builtin_sources += plugins/telit.c plugins/bluez4.h builtin_modules += sap builtin_sources += plugins/sap.c plugins/bluez4.h builtin_modules += hfp_bluez4 builtin_sources += plugins/hfp_hf_bluez4.c plugins/bluez4.h builtin_modules += hfp_ag_bluez4 builtin_sources += plugins/hfp_ag_bluez4.c plugins/bluez4.h builtin_modules += dun_gw_bluez4 builtin_sources += plugins/dun_gw_bluez4.c plugins/bluez4.h builtin_sources += $(btio_sources) builtin_cflags += @BLUEZ_CFLAGS@ builtin_libadd += @BLUEZ_LIBS@ else builtin_modules += bluez5 builtin_sources += plugins/bluez5.c plugins/bluez5.h builtin_modules += hfp_bluez5 builtin_sources += plugins/hfp_hf_bluez5.c plugins/bluez5.h builtin_modules += hfp_ag_bluez5 builtin_sources += plugins/hfp_ag_bluez5.c plugins/bluez5.h builtin_modules += dun_gw_bluez5 builtin_sources += plugins/dun_gw_bluez5.c plugins/bluez5.h if UPOWER builtin_modules += upower builtin_sources += plugins/upower.c endif endif endif endif if NETTIME builtin_modules += nettime builtin_sources += plugins/nettime.c endif if PROVISION builtin_sources += plugins/mbpi.h plugins/mbpi.c builtin_sources += plugins/ubuntu-apndb.h plugins/ubuntu-apndb.c builtin_modules += provision builtin_sources += plugins/provision.c builtin_modules += ubuntu_provision builtin_sources += plugins/ubuntu-provision.c builtin_modules += cdma_provision builtin_sources += plugins/cdma-provision.c builtin_modules += mnclength builtin_sources += plugins/mnclength.c endif if CARES builtin_modules += c_ares_dns_client builtin_sources += plugins/c-ares-dns-client.c builtin_cflags += @CARES_CFLAGS@ builtin_libadd += @CARES_LIBS@ endif if MAINTAINER_MODE builtin_modules += example_history builtin_sources += examples/history.c builtin_modules += example_nettime builtin_sources += examples/nettime.c builtin_modules += example_provision builtin_sources += examples/provision.c builtin_modules += example_emulator builtin_sources += examples/emulator.c builtin_modules += example_private_network builtin_sources += examples/private-network.c builtin_modules += stktest builtin_sources += plugins/stktest.c builtin_modules += emulator_fuzz builtin_sources += plugins/emulator_fuzz.c endif builtin_modules += smart_messaging builtin_sources += plugins/smart-messaging.c builtin_modules += push_notification builtin_sources += plugins/push-notification.c builtin_modules += smshistory builtin_sources += plugins/smshistory.c if ANDROID_WAKELOCK builtin_modules += android_wakelock builtin_sources += plugins/android-wakelock.c endif if ACCOUNTSSETTINGS builtin_modules += accounts_settings builtin_sources += plugins/accounts-settings.c builtin_cflags += @SYSTEMD_CFLAGS@ builtin_libadd += @SYSTEMD_LIBS@ endif sbin_PROGRAMS = src/ofonod src_ofonod_SOURCES = $(builtin_sources) src/ofono.ver \ 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 \ src/network.c src/voicecall.c src/ussd.c src/sms.c \ src/call-settings.c src/call-forwarding.c \ src/call-meter.c src/smsutil.h src/smsutil.c \ src/call-barring.c src/sim.c src/stk.c \ src/phonebook.c src/history.c src/message-waiting.c \ src/simutil.h src/simutil.c src/storage.h \ src/storage.c src/cbs.c src/watch.c src/call-volume.c \ src/gprs.c src/idmap.h src/idmap.c \ src/radio-settings.c src/stkutil.h src/stkutil.c \ src/nettime.c src/stkagent.c src/stkagent.h \ src/simfs.c src/simfs.h src/audio-settings.c \ src/smsagent.c src/smsagent.h src/ctm.c \ src/cdma-voicecall.c src/sim-auth.c \ src/message.h src/message.c src/gprs-provision.c \ src/emulator.c src/location-reporting.c \ src/cdma-connman.c src/gnss.c \ src/gnssagent.c src/gnssagent.h \ src/cdma-smsutil.h src/cdma-smsutil.c \ src/cdma-sms.c src/private-network.c src/cdma-netreg.c \ src/cdma-provision.c src/handsfree.c \ src/handsfree-audio.c src/bluetooth.h \ src/hfp.h src/siri.c \ src/sim-mnclength.c src/spn-table.c \ src/dns-client.c src/wakelock.c \ src/system-settings.c src_ofonod_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \ @GLIB_LIBS@ @DBUS_LIBS@ -ldl src_ofonod_LDFLAGS = -Wl,--export-dynamic \ -Wl,--version-script=$(srcdir)/src/ofono.ver BUILT_SOURCES = $(local_headers) src/builtin.h CLEANFILES = $(BUILT_SOURCES) $(rules_DATA) plugindir = $(pkglibdir)/plugins if MAINTAINER_MODE build_plugindir = $(abs_top_srcdir)/plugins/.libs else build_plugindir = $(plugindir) endif AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ $(builtin_cflags) \ -DOFONO_PLUGIN_BUILTIN \ -DPLUGINDIR=\""$(build_plugindir)"\" 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 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 \ doc/voicecallmanager-api.txt doc/voicecall-api.txt \ doc/call-forwarding-api.txt doc/call-settings-api.txt \ doc/call-meter-api.txt doc/call-barring-api.txt \ doc/supplementaryservices-api.txt \ doc/connman-api.txt doc/features.txt \ doc/pushnotification-api.txt \ doc/smartmessaging-api.txt \ doc/call-volume-api.txt doc/cell-broadcast-api.txt \ doc/messagemanager-api.txt doc/message-waiting-api.txt \ doc/phonebook-api.txt doc/radio-settings-api.txt \ doc/sim-api.txt doc/stk-api.txt \ doc/audio-settings-api.txt doc/text-telephony-api.txt \ doc/calypso-modem.txt doc/message-api.txt \ doc/location-reporting-api.txt \ doc/certification.txt doc/siri-api.txt \ doc/telit-modem.txt test_scripts = test/backtrace \ test/create-internet-context \ test/create-mms-context \ test/activate-context \ test/deactivate-context \ test/deactivate-all \ test/dial-number \ test/list-calls \ test/answer-calls \ test/reject-calls \ test/create-multiparty \ test/private-chat \ test/disable-modem \ test/enable-modem \ test/enter-pin \ test/reset-pin \ test/hangup-all \ test/hangup-active \ test/set-mms-details \ test/set-roaming-allowed \ test/list-contexts \ test/list-modems \ test/list-operators \ test/scan-for-operators \ test/get-operators\ test/monitor-ofono \ test/process-context-settings \ test/receive-sms \ test/remove-contexts \ test/send-sms \ test/set-mic-volume \ test/set-speaker-volume \ test/test-stk-menu \ test/test-advice-of-charge \ test/test-call-barring \ test/test-call-forwarding \ test/test-call-settings \ test/test-modem \ test/test-network-registration \ test/test-phonebook \ test/test-cbs \ test/test-ss \ test/test-ss-control-cb \ test/test-ss-control-cf \ test/test-ss-control-cs \ test/send-ussd \ test/cancel-ussd \ test/initiate-ussd \ test/offline-modem \ test/online-modem \ test/get-tech-preference \ test/set-tech-preference \ test/set-use-sms-reports \ test/set-cbs-topics \ test/enable-cbs \ test/lock-pin \ test/unlock-pin \ test/change-pin \ test/enable-gprs \ test/disable-gprs \ test/get-icon \ test/set-fast-dormancy \ test/test-push-notification \ test/test-smart-messaging \ test/send-vcard \ test/send-vcal \ test/set-tty \ test/set-gsm-band \ test/set-umts-band \ test/lockdown-modem \ test/set-call-forwarding \ test/cdma-list-call \ test/cdma-dial-number \ test/cdma-hangup \ test/cdma-set-credentials \ test/disable-call-forwarding \ test/list-messages \ test/test-sms \ test/test-message-waiting \ test/cdma-connman-disable \ test/cdma-connman-enable \ test/set-context-property \ test/test-gnss \ test/swap-calls \ test/release-and-answer \ test/release-and-swap \ test/hold-and-answer \ test/hangup-multiparty \ test/hangup-call \ test/display-icon \ test/set-msisdn \ test/rilmodem/test-hangup \ test/rilmodem/test-modem-offline \ test/rilmodem/test-sim-online \ test/set-ddr if TEST testdir = $(pkglibdir)/test test_SCRIPTS = $(test_scripts) endif EXTRA_DIST = src/genbuiltin $(rules_files) $(doc_files) $(test_scripts) $(doc_files) $(test_scripts) dist_man_MANS = doc/ofonod.8 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-grilrequest \ unit/test-grilreply \ unit/test-grilunsol \ unit/test-mnclength \ unit/test-mtkrequest \ unit/test-mtkreply \ unit/test-mtkunsol \ unit/test-rilmodem-cs \ unit/test-rilmodem-sms \ unit/test-rilmodem-cb 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_LDADD = @GLIB_LIBS@ unit_objects += $(unit_test_common_OBJECTS) unit_test_util_SOURCES = unit/test-util.c src/util.c 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_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_LDADD = @GLIB_LIBS@ unit_objects += $(unit_test_simutil_OBJECTS) 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_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_LDADD = @GLIB_LIBS@ unit_objects += $(unit_test_sms_OBJECTS) unit_test_cdmasms_SOURCES = unit/test-cdmasms.c src/cdma-smsutil.c 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_LDADD = @GLIB_LIBS@ unit_objects += $(unit_test_sms_root_OBJECTS) unit_test_mux_SOURCES = unit/test-mux.c $(gatchat_sources) unit_test_mux_LDADD = @GLIB_LIBS@ 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_LDADD = @GLIB_LIBS@ unit_objects += $(unit_test_caif_OBJECTS) unit_test_grilrequest_SOURCES = unit/test-grilrequest.c $(gril_sources) \ src/log.c src/util.c src/simutil.c \ src/common.c gatchat/ringbuffer.c unit_test_grilrequest_LDADD = @GLIB_LIBS@ -ldl unit_objects += $(unit_test_grilrequest_OBJECTS) unit_test_grilreply_SOURCES = unit/test-grilreply.c $(gril_sources) \ src/log.c src/util.c src/simutil.c \ src/common.c gatchat/ringbuffer.c unit_test_grilreply_LDADD = @GLIB_LIBS@ -ldl unit_objects += $(unit_test_grilreply_OBJECTS) unit_test_grilunsol_SOURCES = unit/test-grilunsol.c $(gril_sources) \ src/log.c src/util.c src/simutil.c \ src/common.c gatchat/ringbuffer.c unit_test_grilunsol_LDADD = @GLIB_LIBS@ -ldl unit_objects += $(unit_test_grilunsol_OBJECTS) unit_test_mtkrequest_SOURCES = unit/test-mtkrequest.c $(gril_sources) \ drivers/mtkmodem/mtkrequest.c \ src/log.c src/util.c src/simutil.c \ src/common.c gatchat/ringbuffer.c unit_test_mtkrequest_LDADD = @GLIB_LIBS@ -ldl unit_objects += $(unit_test_mtkrequest_OBJECTS) unit_test_mtkreply_SOURCES = unit/test-mtkreply.c $(gril_sources) \ drivers/mtkmodem/mtkreply.c \ src/log.c src/util.c src/simutil.c \ src/common.c gatchat/ringbuffer.c unit_test_mtkreply_LDADD = @GLIB_LIBS@ -ldl unit_objects += $(unit_test_mtkreply_OBJECTS) unit_test_mtkunsol_SOURCES = unit/test-mtkunsol.c $(gril_sources) \ drivers/mtkmodem/mtkunsol.c \ src/log.c src/util.c src/simutil.c \ src/common.c gatchat/ringbuffer.c unit_test_mtkunsol_LDADD = @GLIB_LIBS@ -ldl unit_objects += $(unit_test_mtkunsol_OBJECTS) unit_test_mnclength_SOURCES = unit/test-mnclength.c plugins/mnclength.c \ src/log.c src/sim-mnclength.c unit_test_mnclength_LDADD = @GLIB_LIBS@ -ldl unit_objects += $(unit_test_mnclength_OBJECTS) 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_test_rilmodem_cs_SOURCES = $(test_rilmodem_sources) \ unit/test-rilmodem-cs.c \ drivers/rilmodem/call-settings.c unit_test_rilmodem_cs_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \ @GLIB_LIBS@ @DBUS_LIBS@ -ldl unit_objects += $(unit_test_rilmodem_cs_OBJECTS) unit_test_rilmodem_sms_SOURCES = $(test_rilmodem_sources) \ unit/test-rilmodem-sms.c \ drivers/rilmodem/sms.c unit_test_rilmodem_sms_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \ @GLIB_LIBS@ @DBUS_LIBS@ -ldl unit_objects += $(unit_test_rilmodem_sms_OBJECTS) unit_test_rilmodem_cb_SOURCES = $(test_rilmodem_sources) \ unit/test-rilmodem-cb.c \ drivers/rilmodem/call-barring.c unit_test_rilmodem_cb_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \ @GLIB_LIBS@ @DBUS_LIBS@ -ldl unit_objects += $(unit_test_rilmodem_cb_OBJECTS) TESTS = $(unit_tests) if TOOLS noinst_PROGRAMS += tools/huawei-audio tools/auto-enable \ tools/get-location tools/lookup-apn \ tools/lookup-provider-name tools/tty-redirector tools_huawei_audio_SOURCES = tools/huawei-audio.c tools_huawei_audio_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ tools_auto_enable_SOURCES = tools/auto-enable.c tools_auto_enable_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ tools_get_location_SOURCES = tools/get-location.c tools_get_location_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ tools_lookup_apn_SOURCES = plugins/mbpi.c plugins/mbpi.h tools/lookup-apn.c tools_lookup_apn_LDADD = @GLIB_LIBS@ tools_lookup_provider_name_SOURCES = plugins/mbpi.c plugins/mbpi.h \ tools/lookup-provider-name.c 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 tools_stktest_SOURCES = $(gatchat_sources) tools/stktest.c \ unit/stk-test-data.h tools_stktest_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ endif endif if BLUETOOTH if DUNDEE sbin_PROGRAMS += dundee/dundee dundee_common_sources = $(gatchat_sources) \ src/log.c src/dbus.c dundee/dundee.h dundee/main.c \ dundee/dbus.c dundee/manager.c dundee/device.c dundee_dundee_LDADD = $(builtin_libadd) gdbus/libgdbus-internal.la \ @GLIB_LIBS@ @DBUS_LIBS@ -ldl if DATAFILES dist_dbusconf_DATA += dundee/dundee.conf if SYSTEMD systemdunit_DATA += dundee/dundee.service endif endif if BLUEZ4 dundee_dundee_SOURCES = $(dundee_common_sources) $(btio_sources) \ plugins/bluez4.c dundee/bluez4.c else dundee_dundee_SOURCES = $(dundee_common_sources) plugins/bluez5.c \ dundee/bluez5.c endif endif endif noinst_PROGRAMS += gatchat/gsmdial gatchat/test-server gatchat/test-qcdm gatchat_gsmdial_SOURCES = gatchat/gsmdial.c $(gatchat_sources) gatchat_gsmdial_LDADD = @GLIB_LIBS@ gatchat_test_server_SOURCES = gatchat/test-server.c $(gatchat_sources) gatchat_test_server_LDADD = @GLIB_LIBS@ -lutil gatchat_test_qcdm_SOURCES = gatchat/test-qcdm.c $(gatchat_sources) gatchat_test_qcdm_LDADD = @GLIB_LIBS@ DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles \ --enable-dundee --enable-tools MAINTAINERCLEANFILES = Makefile.in \ aclocal.m4 configure config.h.in config.sub config.guess \ ltmain.sh depcomp compile missing install-sh mkinstalldirs src/builtin.h: src/genbuiltin config.status $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@ plugins/%.rules: $(AM_V_GEN)cp $(srcdir)/$(subst 97-,,$@) $@ include/ofono/version.h: include/version.h $(AM_V_at)$(MKDIR_P) include/ofono $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@ include/ofono/%.h: $(abs_top_srcdir)/include/%.h $(AM_V_at)$(MKDIR_P) include/ofono $(AM_V_GEN)$(LN_S) $< $@ clean-local: @$(RM) -rf include/ofono ofono-1.17.bzr6912+16.04.20160314.3/tools/0000755000015600001650000000000012671500304017540 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/tools/auto-enable.c0000644000015600001650000003100012671500024022071 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #define OFONO_SERVICE "org.ofono" #define OFONO_MANAGER_INTERFACE OFONO_SERVICE ".Manager" #define OFONO_MODEM_INTERFACE OFONO_SERVICE ".Modem" #define OFONO_SIM_INTERFACE OFONO_SERVICE ".SimManager" struct modem_data { char *path; DBusConnection *conn; guint sim_changed_watch; dbus_bool_t has_powered; dbus_bool_t has_online; dbus_bool_t has_sim; }; static GHashTable *modem_list; static gboolean option_online = FALSE; static void set_property_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { g_printerr("%s: %s\n", err.name, err.message); dbus_error_free(&err); } dbus_message_unref(reply); } static int set_property(struct modem_data *modem, const char *key, int type, const void *val) { DBusConnection *conn = modem->conn; DBusMessage *msg; DBusMessageIter iter, value; DBusPendingCall *call; const char *signature; msg = dbus_message_new_method_call(OFONO_SERVICE, modem->path, OFONO_MODEM_INTERFACE, "SetProperty"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key); switch (type) { case DBUS_TYPE_BOOLEAN: signature = DBUS_TYPE_BOOLEAN_AS_STRING; break; default: dbus_message_unref(msg); return -EINVAL; } dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, signature, &value); dbus_message_iter_append_basic(&value, type, val); dbus_message_iter_close_container(&iter, &value); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, set_property_reply, modem, NULL); dbus_pending_call_unref(call); return 0; } static gboolean sim_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct modem_data *modem = user_data; DBusMessageIter iter, value; const char *key; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); if (g_str_equal(key, "SubscriberIdentity") == FALSE) return TRUE; if (modem->has_online == FALSE) { dbus_bool_t online = TRUE; set_property(modem, "Online", DBUS_TYPE_BOOLEAN, &online); } return TRUE; } static void check_interfaces(struct modem_data *modem, DBusMessageIter *iter) { DBusMessageIter entry; dbus_bool_t has_sim = FALSE; dbus_message_iter_recurse(iter, &entry); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *interface; dbus_message_iter_get_basic(&entry, &interface); if (g_str_equal(interface, OFONO_SIM_INTERFACE) == TRUE) has_sim = TRUE; dbus_message_iter_next(&entry); } if (modem->has_sim == has_sim) return; modem->has_sim = has_sim; } static void check_property(struct modem_data *modem, const char *key, DBusMessageIter *value) { if (g_str_equal(key, "Interfaces") == TRUE) { check_interfaces(modem, value); return; } if (g_str_equal(key, "Powered") == TRUE) { dbus_bool_t powered; dbus_message_iter_get_basic(value, &powered); if (powered == TRUE) { g_print("modem enabled (%s)\n", modem->path); modem->has_powered = TRUE; } else { g_print("modem disabled (%s)\n", modem->path); if (modem->has_powered == FALSE) { powered = TRUE; set_property(modem, "Powered", DBUS_TYPE_BOOLEAN, &powered); } } } else if (g_str_equal(key, "Online") == TRUE) { dbus_bool_t online; dbus_message_iter_get_basic(value, &online); if (online == TRUE) { g_print("modem online (%s)\n", modem->path); modem->has_online = TRUE; } else g_print("modem offline (%s)\n", modem->path); } else if (g_str_equal(key, "Lockdown") == TRUE) { dbus_bool_t lockdown; dbus_message_iter_get_basic(value, &lockdown); if (lockdown == TRUE) g_print("modem locked (%s)\n", modem->path); else g_print("modem unlocked (%s)\n", modem->path); } } static void destroy_modem(gpointer data) { struct modem_data *modem = data; g_print("modem removed (%s)\n", modem->path); g_dbus_remove_watch(modem->conn, modem->sim_changed_watch); dbus_connection_unref(modem->conn); g_free(modem->path); g_free(modem); } static void create_modem(DBusConnection *conn, const char *path, DBusMessageIter *iter) { struct modem_data *modem; DBusMessageIter dict; modem = g_try_new0(struct modem_data, 1); if (modem == NULL) return; modem->path = g_strdup(path); modem->conn = dbus_connection_ref(conn); modem->sim_changed_watch = g_dbus_add_signal_watch(conn, OFONO_SERVICE, NULL, OFONO_SIM_INTERFACE, "PropertyChanged", sim_changed, modem, NULL); g_hash_table_replace(modem_list, modem->path, modem); g_print("modem added (%s)\n", modem->path); dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); check_property(modem, key, &value); dbus_message_iter_next(&dict); } } static gboolean modem_added(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessageIter iter, dict; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &dict); create_modem(conn, path, &iter); return TRUE; } static gboolean modem_removed(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessageIter iter; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &path); g_hash_table_remove(modem_list, path); return TRUE; } static gboolean modem_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct modem_data *modem; DBusMessageIter iter, value; const char *path, *key; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; path = dbus_message_get_path(msg); modem = g_hash_table_lookup(modem_list, path); if (modem == NULL) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); check_property(modem, key, &value); return TRUE; } static void get_modems_reply(DBusPendingCall *call, void *user_data) { DBusConnection *conn = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter, list; DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { g_printerr("%s: %s\n", err.name, err.message); dbus_error_free(&err); goto done; } if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE) goto done; if (dbus_message_iter_init(reply, &iter) == FALSE) goto done; dbus_message_iter_recurse(&iter, &list); while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) { DBusMessageIter entry, dict; const char *path; dbus_message_iter_recurse(&list, &entry); dbus_message_iter_get_basic(&entry, &path); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &dict); create_modem(conn, path, &entry); dbus_message_iter_next(&list); } done: dbus_message_unref(reply); } static int get_modems(DBusConnection *conn) { DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(OFONO_SERVICE, "/", OFONO_MANAGER_INTERFACE, "GetModems"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); g_print("getting modems\n"); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, get_modems_reply, conn, NULL); dbus_pending_call_unref(call); return 0; } static gboolean ofono_running = FALSE; static guint modem_added_watch; static guint modem_removed_watch; static guint modem_changed_watch; static void ofono_connect(DBusConnection *conn, void *user_data) { g_print("starting telephony interface\n"); ofono_running = TRUE; modem_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, destroy_modem); modem_added_watch = g_dbus_add_signal_watch(conn, OFONO_SERVICE, NULL, OFONO_MANAGER_INTERFACE, "ModemAdded", modem_added, NULL, NULL); modem_removed_watch = g_dbus_add_signal_watch(conn, OFONO_SERVICE, NULL, OFONO_MANAGER_INTERFACE, "ModemRemoved", modem_removed, NULL, NULL); modem_changed_watch = g_dbus_add_signal_watch(conn, OFONO_SERVICE, NULL, OFONO_MODEM_INTERFACE, "PropertyChanged", modem_changed, NULL, NULL); get_modems(conn); } static void ofono_disconnect(DBusConnection *conn, void *user_data) { g_print("stopping telephony interface\n"); ofono_running = FALSE; g_dbus_remove_watch(conn, modem_added_watch); modem_added_watch = 0; g_dbus_remove_watch(conn, modem_removed_watch); modem_removed_watch = 0; g_dbus_remove_watch(conn, modem_changed_watch); modem_changed_watch = 0; g_hash_table_destroy(modem_list); modem_list = NULL; } static GMainLoop *main_loop = NULL; static volatile sig_atomic_t __terminated = 0; static void sig_term(int sig) { if (__terminated > 0) return; __terminated = 1; g_print("Terminating\n"); g_main_loop_quit(main_loop); } static void disconnect_callback(DBusConnection *conn, void *user_data) { g_printerr("D-Bus disconnect\n"); g_main_loop_quit(main_loop); } static gboolean option_version = FALSE; static GOptionEntry options[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { "online", 'o', 0, G_OPTION_ARG_NONE, &option_online, "Bring device online if possible" }, { NULL }, }; int main(int argc, char **argv) { GOptionContext *context; GError *error = NULL; DBusConnection *conn; DBusError err; guint watch; struct sigaction sa; #ifdef NEED_THREADS if (g_thread_supported() == FALSE) g_thread_init(NULL); #endif context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (option_version == TRUE) { printf("%s\n", VERSION); exit(0); } main_loop = g_main_loop_new(NULL, FALSE); #ifdef NEED_THREADS if (dbus_threads_init_default() == FALSE) { fprintf(stderr, "Can't init usage of threads\n"); exit(1); } #endif dbus_error_init(&err); conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err); if (conn == NULL) { if (dbus_error_is_set(&err) == TRUE) { fprintf(stderr, "%s\n", err.message); dbus_error_free(&err); } else fprintf(stderr, "Can't register with system bus\n"); exit(1); } g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL); memset(&sa, 0, sizeof(sa)); sa.sa_handler = sig_term; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); watch = g_dbus_add_service_watch(conn, OFONO_SERVICE, ofono_connect, ofono_disconnect, NULL, NULL); g_main_loop_run(main_loop); g_dbus_remove_watch(conn, watch); if (ofono_running == TRUE) ofono_disconnect(conn, NULL); dbus_connection_unref(conn); g_main_loop_unref(main_loop); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/tools/qmi.c0000644000015600001650000000155612671500024020500 0ustar pbuserpbgroup00000000000000/* * * 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 #endif int main(int argc, char **argv) { return 0; } ofono-1.17.bzr6912+16.04.20160314.3/tools/get-location.c0000644000015600001650000001347212671500024022277 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 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 * */ #define OFONO_SERVICE "org.ofono" #define MANAGER_PATH "/" #define MANAGER_INTERFACE OFONO_SERVICE ".Manager" #define LOCATION_REPORTING_INTERFACE OFONO_SERVICE ".LocationReporting" #include #include #include #include #include #include #include #include #include #include #ifndef DBUS_TYPE_UNIX_FD #define DBUS_TYPE_UNIX_FD -1 #endif static GMainLoop *event_loop; static char *get_first_modem_path(DBusConnection *conn) { DBusMessage *msg, *reply; DBusMessageIter iter, array, entry; DBusError error; int arg_type; const char *path; msg = dbus_message_new_method_call(OFONO_SERVICE, MANAGER_PATH, MANAGER_INTERFACE, "GetModems"); dbus_error_init(&error); reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &error); dbus_message_unref(msg); if (!reply) { if (dbus_error_is_set(&error)) { fprintf(stderr, "%s\n", error.message); dbus_error_free(&error); } else { fprintf(stderr, "GetModems failed"); } return NULL; } dbus_message_iter_init(reply, &iter); dbus_message_iter_recurse(&iter, &array); dbus_message_iter_recurse(&array, &entry); arg_type = dbus_message_iter_get_arg_type(&entry); while (arg_type != DBUS_TYPE_INVALID && arg_type != DBUS_TYPE_OBJECT_PATH) { dbus_message_iter_next(&entry); arg_type = dbus_message_iter_get_arg_type(&entry); } if (arg_type != DBUS_TYPE_OBJECT_PATH) { fprintf(stderr, "modem not found\n"); return NULL; } dbus_message_iter_get_basic(&entry, &path); fprintf(stderr, "Using modem: %s\n", path); return strdup(path); } static gboolean data_read_cb(GIOChannel *channel, GIOCondition cond, gpointer data) { int fd = GPOINTER_TO_INT(data); char buf[128]; int ret; while ((ret = read(fd, buf, sizeof(buf) - 1)) >= 0) { buf[ret] = '\0'; printf("%s", buf); } if (errno != EAGAIN && errno != EWOULDBLOCK) fprintf(stderr, "Error reading fd"); return TRUE; } static int setup_data_channel(DBusConnection *conn, const char *path) { DBusMessage *msg, *reply; DBusError error; int fd, fd_source; GIOChannel *channel; msg = dbus_message_new_method_call(OFONO_SERVICE, path, LOCATION_REPORTING_INTERFACE, "Request"); dbus_error_init(&error); reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &error); dbus_message_unref(msg); printf("Requesting location-reporting...\n"); if (!reply) { if (dbus_error_is_set(&error)) { fprintf(stderr, "%s\n", error.message); dbus_error_free(&error); } else { fprintf(stderr, "Request() failed"); } return -1; } dbus_error_init(&error); if (dbus_message_get_args(reply, &error, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID) == FALSE) { fprintf(stderr, "%s\n", error.message); dbus_error_free(&error); return -1; } printf("Using fd=%d\n", fd); fcntl(fd, F_SETFL, O_NONBLOCK); channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); fd_source = g_io_add_watch(channel, G_IO_IN, data_read_cb, GINT_TO_POINTER(fd)); g_io_channel_unref(channel); return fd_source; } static gboolean signal_cb(GIOChannel *channel, GIOCondition cond, gpointer data) { int signal_fd = GPOINTER_TO_INT(data); struct signalfd_siginfo si; ssize_t len; len = read(signal_fd, &si, sizeof(si)); if (len < 0) return TRUE; g_main_loop_quit(event_loop); return TRUE; } static int setup_signals(void) { sigset_t mask; int signal_fd, signal_source; GIOChannel *signal_io; sigemptyset(&mask); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGINT); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { fprintf(stderr, "Can't set signal mask - %m"); return -1; } signal_fd = signalfd(-1, &mask, 0); if (signal_fd < 0) { fprintf(stderr, "Can't create signal filedescriptor - %m"); return -1; } signal_io = g_io_channel_unix_new(signal_fd); g_io_channel_set_close_on_unref(signal_io, TRUE); signal_source = g_io_add_watch(signal_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_cb, GINT_TO_POINTER(signal_fd)); g_io_channel_unref(signal_io); return signal_source; } int main(int argc, char *argv[]) { DBusConnection *conn; char *modem_path; int signal_source; int data_source; int ret; if (DBUS_TYPE_UNIX_FD < 0) { fprintf(stderr, "File-descriptor passing not supported\n"); exit(1); } conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); if (!conn) { fprintf(stderr, "Can't get on system bus\n"); exit(1); } if (argc > 1) modem_path = strdup(argv[1]); else modem_path = get_first_modem_path(conn); if (modem_path == NULL) { ret = 1; goto out; } signal_source = setup_signals(); if (signal_source < 0) goto out; data_source = setup_data_channel(conn, modem_path); if (data_source < 0) { g_source_remove(signal_source); goto out; } event_loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(event_loop); ret = 0; g_source_remove(signal_source); g_source_remove(data_source); g_main_loop_unref(event_loop); out: if (modem_path) free(modem_path); dbus_connection_unref(conn); return ret; } ofono-1.17.bzr6912+16.04.20160314.3/tools/huawei-audio.c0000644000015600001650000004525112671500024022273 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #define OFONO_SERVICE "org.ofono" #define OFONO_MANAGER_INTERFACE OFONO_SERVICE ".Manager" #define OFONO_MODEM_INTERFACE OFONO_SERVICE ".Modem" #define OFONO_CALLMANAGER_INTERFACE OFONO_SERVICE ".VoiceCallManager" #define OFONO_CALL_INTERFACE OFONO_SERVICE ".VoiceCall" #define OFONO_AUDIO_INTERFACE OFONO_SERVICE ".AudioSettings" struct modem_data { char *path; GHashTable *call_list; DBusConnection *conn; guint call_added_watch; guint call_removed_watch; guint call_changed_watch; guint audio_changed_watch; gboolean has_callmanager; gboolean has_audiosettings; gboolean is_huawei; gint audio_users; guint audio_watch; int format; int channels; int speed; int dsp_out; }; struct call_data { char *path; struct modem_data *modem; }; static GHashTable *modem_list; static gboolean audio_receive(GIOChannel *channel, GIOCondition condition, gpointer user_data) { struct modem_data *modem = user_data; char buf[512]; ssize_t rlen, wlen; int fd; if (condition & (G_IO_NVAL | G_IO_ERR)) { modem->audio_watch = 0; return FALSE; } fd = g_io_channel_unix_get_fd(channel); rlen = read(fd, buf, sizeof(buf)); if (rlen < 0) return TRUE; wlen = write(modem->dsp_out, buf, rlen); if (wlen < 0) { modem->audio_watch = 0; return FALSE; } return TRUE; } static void open_audio(struct modem_data *modem) { GIOChannel *channel; struct termios ti; int fd; if (modem->is_huawei == FALSE) return; if (modem->audio_users > 0) return; g_print("enabling audio\n"); modem->dsp_out = open("/dev/dsp", O_WRONLY, 0); if (modem->dsp_out < 0) { g_printerr("Failed to open DSP device\n"); return; } if (ioctl(modem->dsp_out, SNDCTL_DSP_SETFMT, &modem->format) < 0) g_printerr("Failed to set DSP format\n"); if (ioctl(modem->dsp_out, SNDCTL_DSP_CHANNELS, &modem->channels) < 0) g_printerr("Failed to set DSP channels\n"); if (ioctl(modem->dsp_out, SNDCTL_DSP_SPEED, &modem->speed) < 0) g_printerr("Failed to set DSP speed\n"); fd = open("/dev/ttyUSB1", O_RDWR | O_NOCTTY); if (fd < 0) { g_printerr("Failed to open audio port\n"); close(modem->dsp_out); modem->dsp_out = -1; return; } /* Switch TTY to raw mode */ memset(&ti, 0, sizeof(ti)); cfmakeraw(&ti); tcflush(fd, TCIOFLUSH); tcsetattr(fd, TCSANOW, &ti); channel = g_io_channel_unix_new(fd); if (channel == NULL) { g_printerr("Failed to create IO channel\n"); close(modem->dsp_out); modem->dsp_out = -1; close(fd); return; } g_io_channel_set_close_on_unref(channel, TRUE); modem->audio_watch = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, audio_receive, modem); g_io_channel_unref(channel); modem->audio_users++; } static void close_audio(struct modem_data *modem) { if (modem->is_huawei == FALSE) return; modem->audio_users--; if (modem->audio_users > 0) return; g_print("disabling audio\n"); if (modem->audio_watch > 0) { g_source_remove(modem->audio_watch); modem->audio_watch = 0; } close(modem->dsp_out); } static void audio_set(struct modem_data *modem, const char *key, DBusMessageIter *iter) { const char *str = NULL; if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) dbus_message_iter_get_basic(iter, &str); if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BOOLEAN) { dbus_bool_t val; dbus_message_iter_get_basic(iter, &val); str = (val == TRUE) ? "yes" : "no"; } g_print("updating audio (%s) [ %s = %s ]\n", modem->path, key, str ? str : "..."); } static void call_set(struct call_data *call, const char *key, DBusMessageIter *iter) { const char *str = NULL; if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) dbus_message_iter_get_basic(iter, &str); if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BOOLEAN) { dbus_bool_t val; dbus_message_iter_get_basic(iter, &val); str = (val == TRUE) ? "yes" : "no"; } g_print("updating call (%s) [ %s = %s ]\n", call->path, key, str ? str : "..."); } static void destroy_call(gpointer data) { struct call_data *call = data; g_print("call removed (%s)\n", call->path); close_audio(call->modem); g_free(call->path); g_free(call); } static void create_call(struct modem_data *modem, const char *path, DBusMessageIter *iter) { struct call_data *call; DBusMessageIter dict; call = g_try_new0(struct call_data, 1); if (call == NULL) return; call->path = g_strdup(path); g_hash_table_replace(modem->call_list, call->path, call); g_print("call added (%s)\n", call->path); call->modem = modem; open_audio(modem); dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); call_set(call, key, &value); dbus_message_iter_next(&dict); } } static gboolean call_added(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct modem_data *modem = user_data; DBusMessageIter iter, dict; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &dict); create_call(modem, path, &iter); return TRUE; } static gboolean call_removed(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct modem_data *modem = user_data; DBusMessageIter iter; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &path); g_hash_table_remove(modem->call_list, path); return TRUE; } static gboolean call_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct modem_data *modem = user_data; struct call_data *call; DBusMessageIter iter, value; const char *path, *key; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; path = dbus_message_get_path(msg); call = g_hash_table_lookup(modem->call_list, path); if (call == NULL) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); call_set(call, key, &value); return TRUE; } static gboolean audio_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct modem_data *modem = user_data; DBusMessageIter iter, value; const char *key; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); audio_set(modem, key, &value); return TRUE; } static void get_calls_reply(DBusPendingCall *call, void *user_data) { struct modem_data *modem = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter, list; DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { g_printerr("%s: %s\n", err.name, err.message); dbus_error_free(&err); goto done; } if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE) goto done; if (dbus_message_iter_init(reply, &iter) == FALSE) goto done; dbus_message_iter_recurse(&iter, &list); while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) { DBusMessageIter entry, dict; const char *path; dbus_message_iter_recurse(&list, &entry); dbus_message_iter_get_basic(&entry, &path); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &dict); create_call(modem, path, &dict); dbus_message_iter_next(&list); } done: dbus_message_unref(reply); } static int get_calls(struct modem_data *modem) { DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(OFONO_SERVICE, modem->path, OFONO_CALLMANAGER_INTERFACE, "GetCalls"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); g_print("getting calls (%s)\n", modem->path); if (dbus_connection_send_with_reply(modem->conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, get_calls_reply, modem, NULL); dbus_pending_call_unref(call); return 0; } static void check_interfaces(struct modem_data *modem, DBusMessageIter *iter) { DBusMessageIter entry; gboolean has_callmanager = FALSE; gboolean has_audiosettings = FALSE; dbus_message_iter_recurse(iter, &entry); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *interface; dbus_message_iter_get_basic(&entry, &interface); if (g_str_equal(interface, OFONO_CALLMANAGER_INTERFACE) == TRUE) has_callmanager = TRUE; if (g_str_equal(interface, OFONO_AUDIO_INTERFACE) == TRUE) has_audiosettings = TRUE; dbus_message_iter_next(&entry); } modem->has_audiosettings = has_audiosettings; if (modem->has_callmanager == has_callmanager) return; modem->has_callmanager = has_callmanager; if (modem->has_callmanager == TRUE) get_calls(modem); } static void check_manufacturer(struct modem_data *modem, DBusMessageIter *iter) { const char *manufacturer; dbus_message_iter_get_basic(iter, &manufacturer); if (g_str_equal(manufacturer, "huawei") == TRUE) { g_print("found Huawei modem\n"); modem->is_huawei = TRUE; } } static void destroy_modem(gpointer data) { struct modem_data *modem = data; g_dbus_remove_watch(modem->conn, modem->call_added_watch); g_dbus_remove_watch(modem->conn, modem->call_removed_watch); g_dbus_remove_watch(modem->conn, modem->call_changed_watch); g_dbus_remove_watch(modem->conn, modem->audio_changed_watch); g_hash_table_destroy(modem->call_list); g_print("modem removed (%s)\n", modem->path); g_free(modem->path); g_free(modem); } static void create_modem(DBusConnection *conn, const char *path, DBusMessageIter *iter) { struct modem_data *modem; DBusMessageIter dict; modem = g_try_new0(struct modem_data, 1); if (modem == NULL) return; modem->path = g_strdup(path); modem->format = AFMT_S16_LE; modem->channels = 1; modem->speed = 8000; modem->dsp_out = -1; modem->call_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, destroy_call); modem->conn = conn; modem->call_added_watch = g_dbus_add_signal_watch(conn, OFONO_SERVICE, modem->path, OFONO_CALLMANAGER_INTERFACE, "CallAdded", call_added, modem, NULL); modem->call_removed_watch = g_dbus_add_signal_watch(conn, OFONO_SERVICE, modem->path, OFONO_CALLMANAGER_INTERFACE, "CallRemoved", call_removed, modem, NULL); modem->call_changed_watch = g_dbus_add_signal_watch(conn, OFONO_SERVICE, NULL, OFONO_CALL_INTERFACE, "PropertyChanged", call_changed, modem, NULL); modem->audio_changed_watch = g_dbus_add_signal_watch(conn, OFONO_SERVICE, NULL, OFONO_AUDIO_INTERFACE, "PropertyChanged", audio_changed, modem, NULL); g_hash_table_replace(modem_list, modem->path, modem); g_print("modem added (%s)\n", modem->path); dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (g_str_equal(key, "Interfaces") == TRUE) check_interfaces(modem, &value); else if (g_str_equal(key, "Manufacturer") == TRUE) check_manufacturer(modem, &value); dbus_message_iter_next(&dict); } } static gboolean modem_added(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessageIter iter, dict; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &dict); create_modem(conn, path, &iter); return TRUE; } static gboolean modem_removed(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessageIter iter; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &path); g_hash_table_remove(modem_list, path); return TRUE; } static gboolean modem_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct modem_data *modem; DBusMessageIter iter, value; const char *path, *key; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; path = dbus_message_get_path(msg); modem = g_hash_table_lookup(modem_list, path); if (modem == NULL) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); if (g_str_equal(key, "Interfaces") == TRUE) check_interfaces(modem, &value); else if (g_str_equal(key, "Manufacturer") == TRUE) check_manufacturer(modem, &value); return TRUE; } static void get_modems_reply(DBusPendingCall *call, void *user_data) { DBusConnection *conn = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter, list; DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { g_printerr("%s: %s\n", err.name, err.message); dbus_error_free(&err); goto done; } if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE) goto done; if (dbus_message_iter_init(reply, &iter) == FALSE) goto done; dbus_message_iter_recurse(&iter, &list); while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) { DBusMessageIter entry, dict; const char *path; dbus_message_iter_recurse(&list, &entry); dbus_message_iter_get_basic(&entry, &path); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &dict); create_modem(conn, path, &entry); dbus_message_iter_next(&list); } done: dbus_message_unref(reply); } static int get_modems(DBusConnection *conn) { DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(OFONO_SERVICE, "/", OFONO_MANAGER_INTERFACE, "GetModems"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); g_print("getting modems\n"); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, get_modems_reply, conn, NULL); dbus_pending_call_unref(call); return 0; } static gboolean ofono_running = FALSE; static guint modem_added_watch; static guint modem_removed_watch; static guint modem_changed_watch; static void ofono_connect(DBusConnection *conn, void *user_data) { g_print("starting telephony interface\n"); ofono_running = TRUE; modem_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, destroy_modem); modem_added_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_MANAGER_INTERFACE, "ModemAdded", modem_added, NULL, NULL); modem_removed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_MANAGER_INTERFACE, "ModemRemoved", modem_removed, NULL, NULL); modem_changed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, OFONO_MODEM_INTERFACE, "PropertyChanged", modem_changed, NULL, NULL); get_modems(conn); } static void ofono_disconnect(DBusConnection *conn, void *user_data) { g_print("stopping telephony interface\n"); ofono_running = FALSE; g_dbus_remove_watch(conn, modem_added_watch); modem_added_watch = 0; g_dbus_remove_watch(conn, modem_removed_watch); modem_removed_watch = 0; g_dbus_remove_watch(conn, modem_changed_watch); modem_changed_watch = 0; g_hash_table_destroy(modem_list); modem_list = NULL; } static GMainLoop *main_loop = NULL; static volatile sig_atomic_t __terminated = 0; static void sig_term(int sig) { if (__terminated > 0) return; __terminated = 1; g_print("Terminating\n"); g_main_loop_quit(main_loop); } static void disconnect_callback(DBusConnection *conn, void *user_data) { g_printerr("D-Bus disconnect\n"); g_main_loop_quit(main_loop); } static gboolean option_version = FALSE; static GOptionEntry options[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { NULL }, }; int main(int argc, char **argv) { GOptionContext *context; GError *error = NULL; DBusConnection *conn; DBusError err; guint watch; struct sigaction sa; #ifdef NEED_THREADS if (g_thread_supported() == FALSE) g_thread_init(NULL); #endif context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (option_version == TRUE) { printf("%s\n", VERSION); exit(0); } main_loop = g_main_loop_new(NULL, FALSE); #ifdef NEED_THREADS if (dbus_threads_init_default() == FALSE) { fprintf(stderr, "Can't init usage of threads\n"); exit(1); } #endif dbus_error_init(&err); conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err); if (conn == NULL) { if (dbus_error_is_set(&err) == TRUE) { fprintf(stderr, "%s\n", err.message); dbus_error_free(&err); } else fprintf(stderr, "Can't register with system bus\n"); exit(1); } g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL); memset(&sa, 0, sizeof(sa)); sa.sa_handler = sig_term; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); watch = g_dbus_add_service_watch(conn, OFONO_SERVICE, ofono_connect, ofono_disconnect, NULL, NULL); g_main_loop_run(main_loop); g_dbus_remove_watch(conn, watch); if (ofono_running == TRUE) ofono_disconnect(conn, NULL); dbus_connection_unref(conn); g_main_loop_unref(main_loop); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/tools/stktest.c0000644000015600001650000037547612671500024021432 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include "unit/stk-test-data.h" #define OFONO_SERVICE "org.ofono" #define STKTEST_PATH "/stktest" #define STKTEST_ERROR "org.ofono.stktest.Error" #define OFONO_ERROR "org.ofono.Error" #define OFONO_MANAGER_INTERFACE OFONO_SERVICE ".Manager" #define OFONO_MODEM_INTERFACE OFONO_SERVICE ".Modem" #define OFONO_STK_INTERFACE OFONO_SERVICE ".SimToolkit" #define OFONO_STKAGENT_INTERFACE OFONO_SERVICE ".SimToolkitAgent" #define LISTEN_PORT 12765 #define CYRILLIC "ЗДРАВСТВУЙТЕ" enum test_state { TEST_STATE_POWERING_UP = 1, TEST_STATE_REGISTERING_AGENT, TEST_STATE_RUNNING, TEST_STATE_POWERING_DOWN, }; enum test_result { TEST_RESULT_NOT_RUN = 0, TEST_RESULT_PASSED, TEST_RESULT_FAILED }; typedef DBusMessage *(*display_text_cb_t)(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent); typedef DBusMessage *(*get_inkey_cb_t)(DBusMessage *msg, const char *alpha, unsigned char icon_id); typedef DBusMessage *(*get_input_cb_t)(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min_chars, unsigned char max_chars, gboolean hide_typing); typedef DBusMessage *(*play_tone_cb_t)(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id); typedef void (*terminal_response_func)(const unsigned char *pdu, unsigned int len); struct test { char *name; char *method; unsigned char *req_pdu; unsigned int req_len; unsigned char *rsp_pdu; unsigned int rsp_len; void *agent_func; terminal_response_func tr_func; enum test_result result; gdouble min_time; gdouble max_time; }; static GMainLoop *main_loop = NULL; static volatile sig_atomic_t __terminated = 0; static GList *tests = NULL; static GList *cur_test = NULL; static GTimer *timer = NULL; /* DBus related */ static DBusConnection *conn; static gboolean ofono_running = FALSE; static guint modem_changed_watch; static enum test_state state; static DBusMessage *pending = NULL; /* Emulator setup */ static guint server_watch; static GAtServer *emulator; /* Emulated modem state variables */ static int modem_mode = 0; static void __stktest_test_next(); static void __stktest_test_finish(gboolean successful); static gboolean create_tcp(void); #define STKTEST_AGENT_ASSERT(expr) \ do { \ if (!(expr)) { \ g_printerr("Assertion Failed %s:%d %s\n", \ __FILE__, __LINE__, #expr); \ __stktest_test_finish(FALSE); \ return stktest_error_failed(msg); \ } \ } while (0) #define STKTEST_RESPONSE_ASSERT(expect_pdu, expect_pdu_len, \ got_pdu, got_pdu_len) \ do { \ if ((expect_pdu_len) != (got_pdu_len)) { \ g_printerr("Assertion Failed %s:%d" \ " Wrong response len" \ " want: %d, got: %d\n", \ __FILE__, __LINE__, \ expect_pdu_len, got_pdu_len); \ __stktest_test_finish(FALSE); \ return; \ } \ \ if (memcmp(expect_pdu, got_pdu, expect_pdu_len) != 0) { \ g_printerr("Assertion Failed %s:%d" \ "Wrong response\n", \ __FILE__, __LINE__); \ __stktest_test_finish(FALSE); \ return; \ } \ } while (0) static const char *to_hex(const unsigned char *data, unsigned int len) { static char buf[512+1]; unsigned int i; for (i = 0; i < len; i++) sprintf(buf + i * 2, "%02hhX", data[i]); buf[i*2] = '\0'; return buf; } static void send_proactive_command(const unsigned char *pdu, unsigned int len) { char buf[1024]; sprintf(buf, "+CUSATP: %s", to_hex(pdu, len)); g_at_server_send_unsolicited(emulator, buf); } static DBusMessage *stktest_error_invalid_args(DBusMessage *msg) { return g_dbus_create_error(msg, STKTEST_ERROR ".InvalidArguments", "Invalid arguments provided"); } static DBusMessage *stktest_error_failed(DBusMessage *msg) { return g_dbus_create_error(msg, STKTEST_ERROR ".Failed", "Operation failed"); } static DBusMessage *stktest_error_end_session(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR ".EndSession", "End Session Request"); } static DBusMessage *stktest_error_go_back(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR ".GoBack", "Go Back Request"); } static DBusMessage *stktest_error_busy(DBusMessage *msg) { return g_dbus_create_error(msg, OFONO_ERROR ".Busy", "UI Busy"); } static DBusMessage *agent_release(DBusConnection *conn, DBusMessage *msg, void *data) { if (pending) { dbus_message_unref(pending); pending = NULL; } return dbus_message_new_method_return(msg); } static DBusMessage *agent_cancel(DBusConnection *conn, DBusMessage *msg, void *data) { if (pending) { dbus_message_unref(pending); pending = NULL; } return NULL; } static DBusMessage *agent_display_text(DBusConnection *conn, DBusMessage *msg, void *data) { const char *text; unsigned char icon_id; dbus_bool_t urgent; struct test *test; display_text_cb_t func; DBusMessage *reply; if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &text, DBUS_TYPE_BYTE, &icon_id, DBUS_TYPE_BOOLEAN, &urgent, DBUS_TYPE_INVALID) == FALSE) return stktest_error_invalid_args(msg); if (cur_test == NULL) return stktest_error_failed(msg); test = cur_test->data; func = test->agent_func; if (strcmp(test->method, "DisplayText")) { g_printerr("Wrong method called!\n"); __stktest_test_finish(FALSE); return stktest_error_failed(msg); } if (func == NULL) { g_printerr("DisplayText not expected to be called"); __stktest_test_finish(FALSE); return stktest_error_failed(msg); } reply = func(msg, text, icon_id, urgent); if (reply == NULL) pending = dbus_message_ref(msg); return reply; } #define GET_INKEY_TEMPLATE(func, method_name) \ static DBusMessage *func(DBusConnection *conn, DBusMessage *msg, \ void *data) \ { \ const char *alpha; \ unsigned char icon_id; \ struct test *test; \ get_inkey_cb_t func; \ DBusMessage *reply; \ \ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &alpha, \ DBUS_TYPE_BYTE, &icon_id, \ DBUS_TYPE_INVALID) == FALSE) \ return stktest_error_invalid_args(msg); \ \ if (cur_test == NULL) \ return stktest_error_failed(msg); \ \ test = cur_test->data; \ func = test->agent_func; \ \ if (strcmp(test->method, method_name)) { \ g_printerr("Wrong method called!" \ " Expected: %s, Got: %s\n", \ test->method, method_name); \ __stktest_test_finish(FALSE); \ return stktest_error_failed(msg); \ } \ \ if (func == NULL) { \ g_printerr(method_name " not expected to be called"); \ __stktest_test_finish(FALSE); \ return stktest_error_failed(msg); \ } \ \ reply = func(msg, alpha, icon_id); \ if (reply == NULL) \ pending = dbus_message_ref(msg); \ \ return reply; \ } \ GET_INKEY_TEMPLATE(agent_request_key, "RequestKey") GET_INKEY_TEMPLATE(agent_request_digit, "RequestDigit") GET_INKEY_TEMPLATE(agent_request_confirmation, "RequestConfirmation") #define GET_INPUT_TEMPLATE(func, method_name) \ static DBusMessage *func(DBusConnection *conn, DBusMessage *msg, \ void *data) \ { \ const char *alpha; \ const char *def_input; \ unsigned char icon_id; \ unsigned char min_chars; \ unsigned char max_chars; \ gboolean hide_typing; \ struct test *test; \ get_input_cb_t func; \ DBusMessage *reply; \ \ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &alpha, \ DBUS_TYPE_BYTE, &icon_id, \ DBUS_TYPE_STRING, &def_input, \ DBUS_TYPE_BYTE, &min_chars, \ DBUS_TYPE_BYTE, &max_chars, \ DBUS_TYPE_BOOLEAN, \ &hide_typing, \ DBUS_TYPE_INVALID) == FALSE) \ return stktest_error_invalid_args(msg); \ \ if (cur_test == NULL) \ return stktest_error_failed(msg); \ \ test = cur_test->data; \ func = test->agent_func; \ \ if (strcmp(test->method, method_name)) { \ g_printerr("Wrong method called!" \ " Expected: %s, Got: %s\n", \ test->method, method_name); \ __stktest_test_finish(FALSE); \ return stktest_error_failed(msg); \ } \ \ if (func == NULL) { \ g_printerr(method_name " not expected to be called"); \ __stktest_test_finish(FALSE); \ return stktest_error_failed(msg); \ } \ \ reply = func(msg, alpha, icon_id, def_input, \ min_chars, max_chars, hide_typing); \ if (reply == NULL) \ pending = dbus_message_ref(msg); \ \ return reply; \ } \ GET_INPUT_TEMPLATE(agent_request_input, "RequestInput") GET_INPUT_TEMPLATE(agent_request_digits, "RequestDigits") #define PLAY_TONE_TEMPLATE(func, method_name) \ static DBusMessage *func(DBusConnection *conn, DBusMessage *msg, \ void *data) \ { \ const char *tone; \ const char *text; \ unsigned char icon_id; \ struct test *test; \ play_tone_cb_t func; \ DBusMessage *reply; \ \ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &tone, \ DBUS_TYPE_STRING, &text, \ DBUS_TYPE_BYTE, &icon_id, \ DBUS_TYPE_INVALID) == FALSE) \ return stktest_error_invalid_args(msg); \ \ if (cur_test == NULL) \ return stktest_error_failed(msg); \ \ test = cur_test->data; \ func = test->agent_func; \ \ if (strcmp(test->method, method_name)) { \ g_printerr("Wrong method called!" \ " Expected: %s, Got: %s\n", \ test->method, method_name); \ __stktest_test_finish(FALSE); \ return stktest_error_failed(msg); \ } \ \ if (func == NULL) { \ g_printerr(method_name " not expected to be called"); \ __stktest_test_finish(FALSE); \ return stktest_error_failed(msg); \ } \ \ reply = func(msg, tone, text, icon_id); \ if (reply == NULL) \ pending = dbus_message_ref(msg); \ \ return reply; \ } \ PLAY_TONE_TEMPLATE(agent_play_tone, "PlayTone") PLAY_TONE_TEMPLATE(agent_loop_tone, "LoopTone") static void server_debug(const char *str, void *data) { g_print("%s: %s\n", (char *) data, str); } static void cgmi_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: g_at_server_send_info(server, "oFono", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); }; } static void cgmm_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: g_at_server_send_info(server, "oFono pre-1.0", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); }; } static void cgmr_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[256]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: sprintf(buf, "oFono pre-1.0 version: %s", VERSION); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); }; } static void cgsn_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: g_at_server_send_info(server, "123456789", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); }; } static gboolean send_ok(gpointer user) { GAtServer *server = user; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); return FALSE; } static void cfun_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[12]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_info(server, "+CFUN: (0-1,4)", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: snprintf(buf, sizeof(buf), "+CFUN: %d", modem_mode); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SET: { GAtResultIter iter; int mode; g_at_result_iter_init(&iter, cmd); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &mode) == FALSE) goto error; if (mode != 0 && mode != 1) goto error; if (modem_mode == mode) { g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; } modem_mode = mode; g_timeout_add_seconds(1, send_ok, server); break; } default: goto error; }; return; error: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); } static void cusatt_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_ext_final(server, "+CME ERROR: 4"); break; case G_AT_SERVER_REQUEST_TYPE_SET: { GAtResultIter iter; const unsigned char *pdu; int len; struct test *test; terminal_response_func func; g_at_result_iter_init(&iter, cmd); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_hexstring(&iter, &pdu, &len) == FALSE) goto error; if (cur_test == NULL) goto error; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); test = cur_test->data; func = test->tr_func; func(pdu, len); break; } default: goto error; }; return; error: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); } static void listen_again(gpointer user_data) { g_at_server_unref(emulator); emulator = NULL; if (create_tcp() == TRUE) return; g_print("Error listening to socket\n"); g_main_loop_quit(main_loop); } static void setup_emulator(GAtServer *server) { if (getenv("OFONO_AT_DEBUG")) g_at_server_set_debug(server, server_debug, "Server"); g_at_server_register(server, "+CGMI", cgmi_cb, NULL, NULL); g_at_server_register(server, "+CGMM", cgmm_cb, NULL, NULL); g_at_server_register(server, "+CGMR", cgmr_cb, NULL, NULL); g_at_server_register(server, "+CGSN", cgsn_cb, NULL, NULL); g_at_server_register(server, "+CFUN", cfun_cb, NULL, NULL); g_at_server_register(server, "+CUSATT", cusatt_cb, NULL, NULL); g_at_server_set_disconnect_function(server, listen_again, NULL); } static gboolean on_socket_connected(GIOChannel *chan, GIOCondition cond, gpointer user) { struct sockaddr saddr; unsigned int len = sizeof(saddr); int fd; GIOChannel *client_io = NULL; if (cond != G_IO_IN) goto error; fd = accept(g_io_channel_unix_get_fd(chan), &saddr, &len); if (fd == -1) goto error; client_io = g_io_channel_unix_new(fd); emulator = g_at_server_new(client_io); g_at_server_set_echo(emulator, FALSE); g_io_channel_unref(client_io); if (emulator == NULL) goto error; setup_emulator(emulator); error: server_watch = 0; return FALSE; } static gboolean create_tcp(void) { struct sockaddr_in addr; int sk; int reuseaddr = 1; GIOChannel *server_io; sk = socket(PF_INET, SOCK_STREAM, 0); if (sk < 0) { g_print("Can't create tcp/ip socket: %s (%d)\n", strerror(errno), errno); return FALSE; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(LISTEN_PORT); setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)); if (bind(sk, (struct sockaddr *) &addr, sizeof(struct sockaddr)) < 0) { g_print("Can't bind socket: %s (%d)", strerror(errno), errno); close(sk); return FALSE; } if (listen(sk, 1) < 0) { g_print("Can't listen on socket: %s (%d)", strerror(errno), errno); close(sk); return FALSE; } server_io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(server_io, TRUE); server_watch = g_io_add_watch_full(server_io, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, on_socket_connected, NULL, NULL); g_io_channel_unref(server_io); return TRUE; } static gboolean has_stk_interface(DBusMessageIter *iter) { DBusMessageIter entry; dbus_message_iter_recurse(iter, &entry); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *interface; dbus_message_iter_get_basic(&entry, &interface); if (g_str_equal(interface, OFONO_STK_INTERFACE) == TRUE) return TRUE; dbus_message_iter_next(&entry); } return FALSE; } static int send_with_reply(const char *path, const char *interface, const char *method, DBusPendingCall **call, DBusPendingCallNotifyFunction cb, void *user_data, DBusFreeFunction free_func, int timeout, int type, ...) { DBusMessage *msg; DBusPendingCall *c; va_list args; int err; msg = dbus_message_new_method_call(OFONO_SERVICE, path, interface, method); if (msg == NULL) { g_printerr("Unable to allocate new D-Bus %s message\n", method); err = -ENOMEM; goto fail; } va_start(args, type); if (!dbus_message_append_args_valist(msg, type, args)) { va_end(args); err = -EIO; goto fail; } va_end(args); if (timeout > 0) timeout *= 1000; if (!dbus_connection_send_with_reply(conn, msg, &c, timeout)) { g_printerr("Sending %s failed\n", method); err = -EIO; goto fail; } if (call != NULL) *call = c; dbus_pending_call_set_notify(c, cb, user_data, free_func); dbus_pending_call_unref(c); dbus_message_unref(msg); return 0; fail: if (free_func && user_data) free_func(user_data); if (msg) dbus_message_unref(msg); return err; } static void set_property_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { g_printerr("%s: %s\n", err.name, err.message); dbus_error_free(&err); } dbus_message_unref(reply); } static int set_property(const char *path, const char *interface, const char *key, int type, const void *val, DBusPendingCallNotifyFunction notify, gpointer user_data, DBusFreeFunction destroy) { DBusMessage *msg; DBusMessageIter iter, value; DBusPendingCall *call; const char *signature; msg = dbus_message_new_method_call(OFONO_SERVICE, path, interface, "SetProperty"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key); switch (type) { case DBUS_TYPE_BOOLEAN: signature = DBUS_TYPE_BOOLEAN_AS_STRING; break; default: dbus_message_unref(msg); return -EINVAL; } dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, signature, &value); dbus_message_iter_append_basic(&value, type, val); dbus_message_iter_close_container(&iter, &value); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, notify, user_data, destroy); dbus_pending_call_unref(call); return 0; } static void register_agent_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; struct test *test; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { g_printerr("%s: %s\n", err.name, err.message); dbus_error_free(&err); } dbus_message_unref(reply); state = TEST_STATE_RUNNING; test = cur_test->data; send_proactive_command(test->req_pdu, test->req_len); if (test->min_time != 0.0 || test->max_time != 0.0) g_timer_start(timer); } static void register_agent() { const char *path = "/default"; int status; status = send_with_reply(STKTEST_PATH, OFONO_STK_INTERFACE, "RegisterAgent", NULL, register_agent_reply, NULL, NULL, 1, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID); if (status < 0) { g_printerr("Unable to register agent with oFono\n"); g_main_loop_quit(main_loop); return; } state = TEST_STATE_REGISTERING_AGENT; } static gboolean modem_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBusMessageIter iter, value; const char *path, *key; gboolean has_stk; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; path = dbus_message_get_path(msg); if (g_str_equal(STKTEST_PATH, path) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &key); dbus_message_iter_next(&iter); dbus_message_iter_recurse(&iter, &value); if (g_str_equal(key, "Interfaces") == FALSE) return TRUE; has_stk = has_stk_interface(&value); switch (state) { case TEST_STATE_POWERING_UP: if (has_stk) register_agent(); break; case TEST_STATE_REGISTERING_AGENT: case TEST_STATE_RUNNING: if (has_stk == FALSE) g_printerr("Unexpectedly lost STK interface\n"); /* Fall through */ case TEST_STATE_POWERING_DOWN: break; }; return TRUE; } static void powerup(void) { dbus_bool_t powered = TRUE; state = TEST_STATE_POWERING_UP; set_property(STKTEST_PATH, OFONO_MODEM_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &powered, set_property_reply, NULL, NULL); } static void get_modems_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter, list; DBusError err; gboolean found = FALSE; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply) == TRUE) { g_printerr("%s: %s\n", err.name, err.message); dbus_error_free(&err); goto done; } if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE) goto done; if (dbus_message_iter_init(reply, &iter) == FALSE) goto done; dbus_message_iter_recurse(&iter, &list); while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) { DBusMessageIter entry; const char *path; dbus_message_iter_recurse(&list, &entry); dbus_message_iter_get_basic(&entry, &path); if (g_str_equal(path, STKTEST_PATH)) found = TRUE; dbus_message_iter_next(&list); } done: dbus_message_unref(reply); if (found == FALSE) { g_printerr("STK Test modem not found\n"); g_main_loop_quit(main_loop); return; } g_print("Test modem found\n"); modem_changed_watch = g_dbus_add_signal_watch(conn, OFONO_SERVICE, STKTEST_PATH, OFONO_MODEM_INTERFACE, "PropertyChanged", modem_changed, NULL, NULL); if (create_tcp() == FALSE) { g_printerr("Unable to listen on modem emulator socket\n"); g_main_loop_quit(main_loop); } __stktest_test_next(); } static int get_modems(DBusConnection *conn) { DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(OFONO_SERVICE, "/", OFONO_MANAGER_INTERFACE, "GetModems"); if (msg == NULL) return -ENOMEM; dbus_message_set_auto_start(msg, FALSE); g_print("getting modems\n"); if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return -EIO; } dbus_message_unref(msg); if (call == NULL) return -EINVAL; dbus_pending_call_set_notify(call, get_modems_reply, conn, NULL); dbus_pending_call_unref(call); return 0; } static const GDBusMethodTable agent_methods[] = { { GDBUS_METHOD("Release", NULL, NULL, agent_release) }, { GDBUS_ASYNC_METHOD("DisplayText", GDBUS_ARGS({ "text", "s" }, { "icon_id", "y" }, { "urgent", "b" }), NULL, agent_display_text) }, { GDBUS_ASYNC_METHOD("RequestDigit", GDBUS_ARGS({ "alpha", "s" }, { "icon_id", "y" }), GDBUS_ARGS({ "digit", "s" }), agent_request_digit) }, { GDBUS_ASYNC_METHOD("RequestKey", GDBUS_ARGS({ "alpha", "s" }, { "icon_id", "y" }), GDBUS_ARGS({ "key", "s" }), agent_request_key) }, { GDBUS_ASYNC_METHOD("RequestConfirmation", GDBUS_ARGS({ "alpha", "s" }, { "icon_id", "y" }), GDBUS_ARGS({ "confirmation", "b" }), agent_request_confirmation) }, { GDBUS_ASYNC_METHOD("RequestInput", GDBUS_ARGS({ "alpha", "s" }, { "icon_id", "y" }, { "default", "s" }, { "min_chars", "y" }, { "max_chars", "y" }, { "hide_typing", "b" }), GDBUS_ARGS({ "input", "s" }), agent_request_input) }, { GDBUS_ASYNC_METHOD("RequestDigits", GDBUS_ARGS({ "alpha", "s" }, { "icon_id", "y" }, { "default", "s" }, { "min_chars", "y" }, { "max_chars", "y" }, { "hide_typing", "b" }), GDBUS_ARGS({ "digits", "s" }), agent_request_digits) }, { GDBUS_ASYNC_METHOD("PlayTone", GDBUS_ARGS({ "tone", "s" }, { "text", "s" }, { "icon_id", "y" }), NULL, agent_play_tone) }, { GDBUS_ASYNC_METHOD("LoopTone", GDBUS_ARGS({ "tone", "s" }, { "text", "s" }, { "icon_id", "y" }), NULL, agent_loop_tone) }, { GDBUS_NOREPLY_METHOD("Cancel", NULL, NULL, agent_cancel) }, { }, }; static void ofono_connect(DBusConnection *conn, void *user_data) { g_print("starting telephony interface\n"); if (!g_dbus_register_interface(conn, "/default", OFONO_STKAGENT_INTERFACE, agent_methods, NULL, NULL, NULL, NULL)) { g_printerr("Unable to register local agent"); g_main_loop_quit(main_loop); } ofono_running = TRUE; get_modems(conn); } static void ofono_disconnect(DBusConnection *conn, void *user_data) { g_print("stopping telephony interface\n"); g_dbus_unregister_interface(conn, "/default", OFONO_STKAGENT_INTERFACE); ofono_running = FALSE; g_dbus_remove_watch(conn, modem_changed_watch); modem_changed_watch = 0; if (server_watch) { g_source_remove(server_watch); server_watch = 0; } g_at_server_unref(emulator); emulator = NULL; } static void sig_term(int sig) { if (__terminated > 0) return; __terminated = 1; g_print("Terminating\n"); g_main_loop_quit(main_loop); } static void disconnect_callback(DBusConnection *conn, void *user_data) { g_printerr("D-Bus disconnect\n"); g_main_loop_quit(main_loop); } static gboolean end_session_and_finish(gpointer user_data) { g_at_server_send_unsolicited(emulator, "+CUSATEND"); __stktest_test_finish(TRUE); return FALSE; } static void expect_response_and_finish(const unsigned char *pdu, unsigned int len) { struct test *test = cur_test->data; STKTEST_RESPONSE_ASSERT(test->rsp_pdu, test->rsp_len, pdu, len); if (test->min_time != 0.0 || test->max_time != 0.0) { gdouble elapsed = g_timer_elapsed(timer, NULL); if (elapsed < test->min_time) { g_printerr("Response received too soon, elapsed:%.2f," " expected: %.2f\n", elapsed, test->min_time); __stktest_test_finish(FALSE); return; } if (elapsed > test->max_time) { g_printerr("Response received too late, elapsed: %.2f," " expected: %.2f\n", elapsed, test->max_time); __stktest_test_finish(FALSE); return; } } g_idle_add(end_session_and_finish, NULL); } static void expect_response(const unsigned char *pdu, unsigned int len) { struct test *test = cur_test->data; STKTEST_RESPONSE_ASSERT(test->rsp_pdu, test->rsp_len, pdu, len); } static gboolean poweroff_not_canceled_after_3(gpointer user_data) { __stktest_test_finish(pending != NULL); return FALSE; } static gboolean end_session_and_not_canceled_after_3(gpointer user_data) { g_at_server_send_unsolicited(emulator, "+CUSATEND"); g_timeout_add_seconds(3, poweroff_not_canceled_after_3, NULL); return FALSE; } static void expect_response_and_not_canceled_after_3(const unsigned char *pdu, unsigned int len) { struct test *test = cur_test->data; STKTEST_RESPONSE_ASSERT(test->rsp_pdu, test->rsp_len, pdu, len); g_idle_add(end_session_and_not_canceled_after_3, NULL); } static gboolean poweroff_and_canceled_after_21(gpointer user_data) { __stktest_test_finish(pending == NULL); return FALSE; } static gboolean end_session_and_canceled_after_21(gpointer user_data) { g_at_server_send_unsolicited(emulator, "+CUSATEND"); g_timeout_add_seconds(21, poweroff_and_canceled_after_21, NULL); return FALSE; } static void expect_response_and_canceled_after_21(const unsigned char *pdu, unsigned int len) { struct test *test = cur_test->data; STKTEST_RESPONSE_ASSERT(test->rsp_pdu, test->rsp_len, pdu, len); g_idle_add(end_session_and_canceled_after_21, NULL); } static DBusMessage *test_display_text_11(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Toolkit Test 1")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_12(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Toolkit Test 1")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return stktest_error_busy(msg); } static DBusMessage *test_display_text_13(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Toolkit Test 2")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == TRUE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_14(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Toolkit Test 3")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_15(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Toolkit Test 4")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return NULL; } static DBusMessage *test_display_text_16(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "This command instructs the ME" " to display a text message. " "It allows the SIM to define " "the priority of that message, " "and the text string format. " "Two types of prio")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_17(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { /* oFono gives rich text formatting in HTML */ STKTEST_AGENT_ASSERT(g_str_equal(text, "<GO-BACKWARDS>")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return stktest_error_go_back(msg); } static DBusMessage *test_display_text_18(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { /* oFono gives rich text formatting in HTML */ STKTEST_AGENT_ASSERT(g_str_equal(text, "<ABORT>")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return stktest_error_end_session(msg); } static DBusMessage *test_display_text_21(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "<TIME-OUT>")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return NULL; } static DBusMessage *test_display_text_31(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { static const char *expected = "This command instructs the ME to display" " a text message, and/or an icon " "(see 6.5.4). It allows the " "SIM to define the priority of that " "message, and the text string format. " "Two types of priority are defined:- " "display normal priority text and/"; STKTEST_AGENT_ASSERT(g_str_equal(text, expected)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_41(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Toolkit Test 1")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return NULL; } static DBusMessage *test_display_text_42(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Toolkit Test 2")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return NULL; } static gboolean user_response(gpointer user_data) { if (pending == NULL) { __stktest_test_finish(FALSE); return FALSE; } g_dbus_send_reply(conn, pending, DBUS_TYPE_INVALID); dbus_message_unref(pending); pending = NULL; __stktest_test_finish(TRUE); return FALSE; } static DBusMessage *test_display_text_43(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Toolkit Test 3")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); g_timeout_add_seconds(3, user_response, NULL); return NULL; } static DBusMessage *test_display_text_51(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Basic Icon")); STKTEST_AGENT_ASSERT(icon_id == 1); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_52(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Colour Icon")); STKTEST_AGENT_ASSERT(icon_id == 2); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_53(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "Basic Icon")); STKTEST_AGENT_ASSERT(icon_id == 1); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_61(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, CYRILLIC)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_71(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { STKTEST_AGENT_ASSERT(g_str_equal(text, "10 Second")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return NULL; } static DBusMessage *test_display_text_81(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "
Text Attribute 1" "
"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_82(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "
Text Attribute 1" "
"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_83(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "
Text Attribute 1" "
"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_84(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "
" "Text Attribute 1
"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_85(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "
" "Text Attribute 1
"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_86(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "
" "Text Attribute 1
"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_87(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "
" "Text Attribute 1
"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_88(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "
Text Attribute 1
"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_89(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "
Text Attribute 1
"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_810(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "
Text Attribute 1" "
"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_91(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "你好"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_display_text_101(DBusMessage *msg, const char *text, unsigned char icon_id, gboolean urgent) { const char *expect = "80ル"; STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(urgent == FALSE); return dbus_message_new_method_return(msg); } static DBusMessage *test_get_inkey_11(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter \"+\"")); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_12(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "0"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter \"0\"")); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_13(DBusMessage *msg, const char *alpha, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<GO-BACKWARDS>")); STKTEST_AGENT_ASSERT(icon_id == 0); return stktest_error_go_back(msg); } static DBusMessage *test_get_inkey_14(DBusMessage *msg, const char *alpha, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<ABORT>")); STKTEST_AGENT_ASSERT(icon_id == 0); return stktest_error_end_session(msg); } static DBusMessage *test_get_inkey_15(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "q"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter \"q\"")); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_16(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "x"; const char *expected = "Enter \"x\". This command instructs the ME to display text, " "and to expect the user to enter a single character. Any " "response entered by the user shall be passed t"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expected)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_21(DBusMessage *msg, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(text, "<TIME-OUT>")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_get_inkey_31(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, CYRILLIC)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_32(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙ"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_41(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "Д"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter")); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_51a(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; dbus_bool_t ret = 1; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter YES")); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_51b(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; dbus_bool_t ret = 0; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter NO")); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_61(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<NO-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 1); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_62(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<BASIC-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 1); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_63(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<NO-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 2); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_64(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<COLOUR-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 2); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_81(DBusMessage *msg, const char *alpha, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter \"+\"")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_get_inkey_91(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "
Enter \"+\"
"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_92(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "
Enter \"+\"" "
"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_93(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "
Enter \"+\"" "
"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_94(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "
Enter \"+\"" "
"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_95(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "
" "Enter \"+\"
"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_96(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "
Enter \"+\"" "
"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_97(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "
" "Enter \"+\"
"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_98(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "
Enter \"+\"
"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_99(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "
Enter \"+\"
"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_910(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "
Enter \"+\"
"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_101(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "你好"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_102(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_111(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "好"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter")); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_121(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "ル"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_122(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "+"; const char *expect = "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルル"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_inkey_131(DBusMessage *msg, const char *alpha, unsigned char icon_id) { DBusMessage *reply; const char *ret = "ル"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter")); STKTEST_AGENT_ASSERT(icon_id == 0); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_11(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "12345"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter 12345")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 5); STKTEST_AGENT_ASSERT(max == 5); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_12(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "67*#+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter 67*#+")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 5); STKTEST_AGENT_ASSERT(max == 5); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_13(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "AbCdE"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter AbCdE")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 5); STKTEST_AGENT_ASSERT(max == 5); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_14(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "2345678"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Password 1<SEND>2345678")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 4); STKTEST_AGENT_ASSERT(max == 8); STKTEST_AGENT_ASSERT(hide_typing == TRUE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_15(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "12345678901234567890"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter 1..9,0..9,0(1)")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 1); STKTEST_AGENT_ASSERT(max == 20); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_16(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<GO-BACKWARDS>")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 0); STKTEST_AGENT_ASSERT(max == 8); STKTEST_AGENT_ASSERT(hide_typing == FALSE); return stktest_error_go_back(msg); } static DBusMessage *test_get_input_17(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<ABORT>")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 0); STKTEST_AGENT_ASSERT(max == 8); STKTEST_AGENT_ASSERT(hide_typing == FALSE); return stktest_error_end_session(msg); } static DBusMessage *test_get_input_18(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *expect = "***1111111111###***2222222222###***3333333333###***4444444444###***" "5555555555###***6666666666###***7777777777###***8888888888###***9999" "999999###***0000000000###"; const char *ret = "***1111111111###***2222222222###***" "3333333333###***4444444444###" "***5555555555###***6666666666###" "***7777777777###***8888888888###" "***9999999999###***0000000000###"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 160); STKTEST_AGENT_ASSERT(max == 160); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_19(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = ""; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<SEND>")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 0); STKTEST_AGENT_ASSERT(max == 1); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_110(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "12345"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 1); STKTEST_AGENT_ASSERT(max == 5); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_21(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<TIME-OUT>")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 0); STKTEST_AGENT_ASSERT(max == 10); STKTEST_AGENT_ASSERT(hide_typing == FALSE); return NULL; } static DBusMessage *test_get_input_31(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *expect = CYRILLIC; const char *ret = "HELLO"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 5); STKTEST_AGENT_ASSERT(max == 5); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_32(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *expect = "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙ"; const char *ret = "HELLO"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 5); STKTEST_AGENT_ASSERT(max == 5); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_41(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = CYRILLIC; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter Hello")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 12); STKTEST_AGENT_ASSERT(max == 12); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_42(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙ"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter Hello")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 5); STKTEST_AGENT_ASSERT(max == 255); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_51(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter 12345")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "12345")); STKTEST_AGENT_ASSERT(min == 5); STKTEST_AGENT_ASSERT(max == 5); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &def_input, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_52(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *def_expect = "***1111111111###***2222222222###***3333333333###***4444444444###***" "5555555555###***6666666666###***7777777777###***8888888888###***9999" "999999###***0000000000###"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter:")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, def_expect)); STKTEST_AGENT_ASSERT(min == 160); STKTEST_AGENT_ASSERT(max == 160); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &def_input, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_61(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<NO-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 1); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 0); STKTEST_AGENT_ASSERT(max == 10); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_62(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<BASIC-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 1); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 0); STKTEST_AGENT_ASSERT(max == 10); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_63(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<NO-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 2); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 0); STKTEST_AGENT_ASSERT(max == 10); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_64(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "+"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "<COLOUR-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 2); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 0); STKTEST_AGENT_ASSERT(max == 10); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } #define GET_INPUT_8X_TEMPLATE(seq, expect) \ static DBusMessage *test_get_input_8##seq(DBusMessage *msg, \ const char *alpha, \ unsigned char icon_id, \ const char *def_input, \ unsigned char min, \ unsigned char max, \ gboolean hide_typing) \ { \ DBusMessage *reply; \ const char *ret = "12345"; \ \ STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); \ STKTEST_AGENT_ASSERT(icon_id == 0); \ STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); \ STKTEST_AGENT_ASSERT(min == 5); \ STKTEST_AGENT_ASSERT(max == 5); \ STKTEST_AGENT_ASSERT(hide_typing == FALSE); \ \ reply = dbus_message_new_method_return(msg); \ dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, \ DBUS_TYPE_INVALID); \ \ return reply; \ } GET_INPUT_8X_TEMPLATE(1, "
Enter 12345
") GET_INPUT_8X_TEMPLATE(2, "
Enter 12345" "
") GET_INPUT_8X_TEMPLATE(3, "
Enter 12345" "
") GET_INPUT_8X_TEMPLATE(4, "
Enter 12345" "
") GET_INPUT_8X_TEMPLATE(5, "
Enter " "12345
") GET_INPUT_8X_TEMPLATE(6, "
Enter " "12345
") GET_INPUT_8X_TEMPLATE(7, "
Enter " "12345
") GET_INPUT_8X_TEMPLATE(8, "
Enter 12345
") GET_INPUT_8X_TEMPLATE(9, "
Enter 12345
") GET_INPUT_8X_TEMPLATE(10, "
Enter 12345
") #define GET_INPUT_9X_11X_TEMPLATE(seq, expect) \ static DBusMessage *test_get_input_##seq(DBusMessage *msg, \ const char *alpha, \ unsigned char icon_id, \ const char *def_input, \ unsigned char min, \ unsigned char max, \ gboolean hide_typing) \ { \ DBusMessage *reply; \ const char *ret = "HELLO"; \ \ STKTEST_AGENT_ASSERT(g_str_equal(alpha, expect)); \ STKTEST_AGENT_ASSERT(icon_id == 0); \ STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); \ STKTEST_AGENT_ASSERT(min == 5); \ STKTEST_AGENT_ASSERT(max == 5); \ STKTEST_AGENT_ASSERT(hide_typing == FALSE); \ \ reply = dbus_message_new_method_return(msg); \ dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, \ DBUS_TYPE_INVALID); \ \ return reply; \ } GET_INPUT_9X_11X_TEMPLATE(91, "你好") GET_INPUT_9X_11X_TEMPLATE(92, "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好") static DBusMessage *test_get_input_101(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "你好"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter Hello")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 2); STKTEST_AGENT_ASSERT(max == 2); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_102(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "你好你好你好你好你好你好" "你好你好你好你好你好你好" "你好你好你好你好你好你好" "你好你好你好你好你好你好" "你好你好你好你好你好你好" "你好你好你好你好你好"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter Hello")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 5); STKTEST_AGENT_ASSERT(max == 255); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } GET_INPUT_9X_11X_TEMPLATE(111,"ル") GET_INPUT_9X_11X_TEMPLATE(112, "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルル") static DBusMessage *test_get_input_121(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "ルル"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter Hello")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 2); STKTEST_AGENT_ASSERT(max == 2); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_get_input_122(DBusMessage *msg, const char *alpha, unsigned char icon_id, const char *def_input, unsigned char min, unsigned char max, gboolean hide_typing) { DBusMessage *reply; const char *ret = "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルル"; STKTEST_AGENT_ASSERT(g_str_equal(alpha, "Enter Hello")); STKTEST_AGENT_ASSERT(icon_id == 0); STKTEST_AGENT_ASSERT(g_str_equal(def_input, "")); STKTEST_AGENT_ASSERT(min == 5); STKTEST_AGENT_ASSERT(max == 255); STKTEST_AGENT_ASSERT(hide_typing == FALSE); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID); return reply; } static DBusMessage *test_play_tone_11a(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "dial-tone")); STKTEST_AGENT_ASSERT(g_str_equal(text, "Dial Tone")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_play_tone_11b(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "busy")); STKTEST_AGENT_ASSERT(g_str_equal(text, "Sub. Busy")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_play_tone_11c(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "congestion")); STKTEST_AGENT_ASSERT(g_str_equal(text, "Congestion")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_play_tone_11d(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "radio-path-acknowledge")); STKTEST_AGENT_ASSERT(g_str_equal(text, "RP Ack")); STKTEST_AGENT_ASSERT(icon_id == 0); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_11e(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "radio-path-not-available")); STKTEST_AGENT_ASSERT(g_str_equal(text, "No RP")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_play_tone_11f(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "error")); STKTEST_AGENT_ASSERT(g_str_equal(text, "Spec Info")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_play_tone_11g(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "call-waiting")); STKTEST_AGENT_ASSERT(g_str_equal(text, "Call Wait")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_play_tone_11h(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "ringing-tone")); STKTEST_AGENT_ASSERT(g_str_equal(text, "Ring Tone")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_play_tone_11i(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { const char *expect_text = "This command instructs the ME to play an audio tone. " "Upon receiving this command, the ME shall check " "if it is currently in, or in the process of setting " "up (SET-UP message sent to the network, see " "GSM\"04.08\"(8)), a speech call. - If the ME I"; STKTEST_AGENT_ASSERT(g_str_equal(tone, "general-beep")); STKTEST_AGENT_ASSERT(g_str_equal(text, expect_text)); STKTEST_AGENT_ASSERT(icon_id == 0); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_11j(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "general-beep")); STKTEST_AGENT_ASSERT(g_str_equal(text, "Beep")); STKTEST_AGENT_ASSERT(icon_id == 0); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_11k(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "positive-acknowledgement")); STKTEST_AGENT_ASSERT(g_str_equal(text, "Positive")); STKTEST_AGENT_ASSERT(icon_id == 0); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_11l(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "negative-acknowledgement")); STKTEST_AGENT_ASSERT(g_str_equal(text, "Negative")); STKTEST_AGENT_ASSERT(icon_id == 0); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_11m(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "general-beep")); STKTEST_AGENT_ASSERT(g_str_equal(text, "Quick")); STKTEST_AGENT_ASSERT(icon_id == 0); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_11n(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "error")); STKTEST_AGENT_ASSERT(g_str_equal(text, "<ABORT>")); STKTEST_AGENT_ASSERT(icon_id == 0); return stktest_error_end_session(msg); } static DBusMessage *test_play_tone_11o(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "general-beep")); STKTEST_AGENT_ASSERT(g_str_equal(text, "")); STKTEST_AGENT_ASSERT(icon_id == 0); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_21(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "positive-acknowledgement")); STKTEST_AGENT_ASSERT(g_str_equal(text, CYRILLIC)); STKTEST_AGENT_ASSERT(icon_id == 0); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_31(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "positive-acknowledgement")); STKTEST_AGENT_ASSERT(g_str_equal(text, "<BASIC-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 1); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_32(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "positive-acknowledgement")); STKTEST_AGENT_ASSERT(g_str_equal(text, "<BASIC-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 1); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_33(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "positive-acknowledgement")); STKTEST_AGENT_ASSERT(g_str_equal(text, "<COLOUR-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 2); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_34(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "positive-acknowledgement")); STKTEST_AGENT_ASSERT(g_str_equal(text, "<COLOUR-ICON>")); STKTEST_AGENT_ASSERT(icon_id == 2); return dbus_message_new_method_return(msg); } #define PLAY_TONE_4X_TEMPLATE(seq, expect) \ static DBusMessage *test_play_tone_4##seq(DBusMessage *msg, \ const char *tone, \ const char *text, \ unsigned char icon_id) \ { \ g_print("%s\n", text); \ STKTEST_AGENT_ASSERT(g_str_equal(tone, \ "positive-acknowledgement")); \ STKTEST_AGENT_ASSERT(g_str_equal(text, expect)); \ STKTEST_AGENT_ASSERT(icon_id == 0); \ \ return dbus_message_new_method_return(msg); \ } \ PLAY_TONE_4X_TEMPLATE(1a, "
Text Attribute 1" "
") PLAY_TONE_4X_TEMPLATE(1b, "Text Attribute 2") PLAY_TONE_4X_TEMPLATE(2a, "
Text Attribute 1" "
") PLAY_TONE_4X_TEMPLATE(2b, "Text Attribute 2") PLAY_TONE_4X_TEMPLATE(3a, "
Text Attribute 1" "
") PLAY_TONE_4X_TEMPLATE(3b, "Text Attribute 2") PLAY_TONE_4X_TEMPLATE(4a, "
" "Text Attribute 1
") PLAY_TONE_4X_TEMPLATE(4b, "
Text Attribute 2" "
") PLAY_TONE_4X_TEMPLATE(4c, "Text Attribute 3") PLAY_TONE_4X_TEMPLATE(5a, "
" "Text Attribute 1
") PLAY_TONE_4X_TEMPLATE(5b, "
Text Attribute 2" "
") PLAY_TONE_4X_TEMPLATE(5c, "Text Attribute 3") PLAY_TONE_4X_TEMPLATE(6a, "
" "Text Attribute
1") PLAY_TONE_4X_TEMPLATE(6b, "
Text Attribute 2" "
") PLAY_TONE_4X_TEMPLATE(6c, "Text Attribute 3") PLAY_TONE_4X_TEMPLATE(7a, "
" "Text Attribute
1") PLAY_TONE_4X_TEMPLATE(7b, "
Text Attribute 2" "
") PLAY_TONE_4X_TEMPLATE(7c, "Text Attribute 3") PLAY_TONE_4X_TEMPLATE(8a, "
Text Attribute 1
") PLAY_TONE_4X_TEMPLATE(8b, "
Text Attribute 2" "
") PLAY_TONE_4X_TEMPLATE(8c, "Text Attribute 3") PLAY_TONE_4X_TEMPLATE(9a, "
Text Attribute 1
") PLAY_TONE_4X_TEMPLATE(9b, "
Text Attribute 2" "
") PLAY_TONE_4X_TEMPLATE(9c, "Text Attribute 3") PLAY_TONE_4X_TEMPLATE(10a, "
Text Attribute 1" "
") PLAY_TONE_4X_TEMPLATE(10b, "Text Attribute 2") static DBusMessage *test_play_tone_51(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "positive-acknowledgement")); STKTEST_AGENT_ASSERT(g_str_equal(text, "中一")); STKTEST_AGENT_ASSERT(icon_id == 0); return dbus_message_new_method_return(msg); } static DBusMessage *test_play_tone_61a(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "dial-tone")); STKTEST_AGENT_ASSERT(g_str_equal(text, "80ル0")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_play_tone_61b(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "dial-tone")); STKTEST_AGENT_ASSERT(g_str_equal(text, "81ル1")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static DBusMessage *test_play_tone_61c(DBusMessage *msg, const char *tone, const char *text, unsigned char icon_id) { STKTEST_AGENT_ASSERT(g_str_equal(tone, "dial-tone")); STKTEST_AGENT_ASSERT(g_str_equal(text, "82ル2")); STKTEST_AGENT_ASSERT(icon_id == 0); return NULL; } static void power_down_reply(DBusPendingCall *call, void *user_data) { __stktest_test_next(); } static void __stktest_test_finish(gboolean successful) { struct test *test = cur_test->data; dbus_bool_t powered = FALSE; test->result = successful ? TEST_RESULT_PASSED : TEST_RESULT_FAILED; state = TEST_STATE_POWERING_DOWN; set_property(STKTEST_PATH, OFONO_MODEM_INTERFACE, "Powered", DBUS_TYPE_BOOLEAN, &powered, power_down_reply, NULL, NULL); } static void __stktest_test_next() { if (cur_test == NULL) cur_test = tests; else cur_test = cur_test->next; if (cur_test == NULL) { g_main_loop_quit(main_loop); return; } powerup(); } static void stktest_add_test(const char *name, const char *method, const unsigned char *req, unsigned int req_len, const unsigned char *rsp, unsigned int rsp_len, void *agent_func, terminal_response_func tr_func) { struct test *test = g_new0(struct test, 1); test->name = g_strdup(name); test->method = g_strdup(method); test->req_pdu = g_memdup(req, req_len); test->req_len = req_len; test->rsp_pdu = g_memdup(rsp, rsp_len); test->rsp_len = rsp_len; test->agent_func = agent_func; test->tr_func = tr_func; tests = g_list_append(tests, test); } static void stktest_add_timed_test(const char *name, const char *method, const unsigned char *req, unsigned int req_len, const unsigned char *rsp, unsigned int rsp_len, void *agent_func, terminal_response_func tr_func, gdouble expected_min_time, gdouble expected_max_time) { GList *last; struct test *test; stktest_add_test(name, method, req, req_len, rsp, rsp_len, agent_func, tr_func); last = g_list_last(tests); test = last->data; test->min_time = expected_min_time; test->max_time = expected_max_time; } static void __stktest_test_init(void) { stktest_add_test("Display Text 1.1", "DisplayText", display_text_111, sizeof(display_text_111), display_text_response_111, sizeof(display_text_response_111), test_display_text_11, expect_response_and_finish); stktest_add_test("Display Text 1.2", "DisplayText", display_text_111, sizeof(display_text_111), display_text_response_121, sizeof(display_text_response_121), test_display_text_12, expect_response_and_finish); stktest_add_test("Display Text 1.3", "DisplayText", display_text_131, sizeof(display_text_131), display_text_response_131, sizeof(display_text_response_131), test_display_text_13, expect_response_and_finish); stktest_add_test("Display Text 1.4", "DisplayText", display_text_141, sizeof(display_text_141), display_text_response_141, sizeof(display_text_response_141), test_display_text_14, expect_response_and_finish); stktest_add_test("Display Text 1.5", "DisplayText", display_text_151, sizeof(display_text_151), display_text_response_151, sizeof(display_text_response_151), test_display_text_15, expect_response_and_finish); stktest_add_test("Display Text 1.6", "DisplayText", display_text_161, sizeof(display_text_161), display_text_response_161, sizeof(display_text_response_161), test_display_text_16, expect_response_and_finish); stktest_add_test("Display Text 1.7", "DisplayText", display_text_171, sizeof(display_text_171), display_text_response_171, sizeof(display_text_response_171), test_display_text_17, expect_response_and_finish); stktest_add_test("Display Text 1.8", "DisplayText", display_text_181, sizeof(display_text_181), display_text_response_181, sizeof(display_text_response_181), test_display_text_18, expect_response_and_finish); stktest_add_test("Display Text 1.9", "DisplayText", display_text_191, sizeof(display_text_191), display_text_response_191, sizeof(display_text_response_191), NULL, expect_response_and_finish); stktest_add_test("Display Text 2.1", "DisplayText", display_text_211, sizeof(display_text_211), display_text_response_211, sizeof(display_text_response_211), test_display_text_21, expect_response_and_finish); stktest_add_test("Display Text 3.1", "DisplayText", display_text_311, sizeof(display_text_311), display_text_response_311, sizeof(display_text_response_311), test_display_text_31, expect_response_and_finish); stktest_add_test("Display Text 4.1", "DisplayText", display_text_411, sizeof(display_text_411), display_text_response_411, sizeof(display_text_response_411), test_display_text_41, expect_response_and_not_canceled_after_3); stktest_add_test("Display Text 4.2", "DisplayText", display_text_421, sizeof(display_text_421), display_text_response_421, sizeof(display_text_response_421), test_display_text_42, expect_response_and_canceled_after_21); stktest_add_test("Display Text 4.3", "DisplayText", display_text_431, sizeof(display_text_431), display_text_response_431, sizeof(display_text_response_431), test_display_text_43, expect_response); stktest_add_test("Display Text 5.1A", "DisplayText", display_text_511, sizeof(display_text_511), display_text_response_511a, sizeof(display_text_response_511a), test_display_text_51, expect_response_and_finish); stktest_add_test("Display Text 5.2A", "DisplayText", display_text_521, sizeof(display_text_521), display_text_response_521a, sizeof(display_text_response_521a), test_display_text_52, expect_response_and_finish); stktest_add_test("Display Text 5.3A", "DisplayText", display_text_531, sizeof(display_text_531), display_text_response_531a, sizeof(display_text_response_531a), test_display_text_53, expect_response_and_finish); stktest_add_test("Display Text 6.1", "DisplayText", display_text_611, sizeof(display_text_611), display_text_response_611, sizeof(display_text_response_611), test_display_text_61, expect_response_and_finish); stktest_add_test("Display Text 7.1", "DisplayText", display_text_711, sizeof(display_text_711), display_text_response_711, sizeof(display_text_response_711), test_display_text_71, expect_response_and_finish); /* * We skip parts where the UI is asked to display simple text to ensure * that the alignment, font is set up correctly and not 'remembered' * from a previous state. oFono does not keep any state of the * previous commands */ stktest_add_test("Display Text 8.1", "DisplayText", display_text_811, sizeof(display_text_811), display_text_response_811, sizeof(display_text_response_811), test_display_text_81, expect_response_and_finish); stktest_add_test("Display Text 8.2", "DisplayText", display_text_821, sizeof(display_text_821), display_text_response_821, sizeof(display_text_response_821), test_display_text_82, expect_response_and_finish); stktest_add_test("Display Text 8.3", "DisplayText", display_text_831, sizeof(display_text_831), display_text_response_831, sizeof(display_text_response_831), test_display_text_83, expect_response_and_finish); stktest_add_test("Display Text 8.4", "DisplayText", display_text_841, sizeof(display_text_841), display_text_response_841, sizeof(display_text_response_841), test_display_text_84, expect_response_and_finish); stktest_add_test("Display Text 8.5", "DisplayText", display_text_851, sizeof(display_text_851), display_text_response_851, sizeof(display_text_response_851), test_display_text_85, expect_response_and_finish); stktest_add_test("Display Text 8.6", "DisplayText", display_text_861, sizeof(display_text_861), display_text_response_861, sizeof(display_text_response_861), test_display_text_86, expect_response_and_finish); stktest_add_test("Display Text 8.7", "DisplayText", display_text_871, sizeof(display_text_871), display_text_response_871, sizeof(display_text_response_871), test_display_text_87, expect_response_and_finish); stktest_add_test("Display Text 8.8", "DisplayText", display_text_881, sizeof(display_text_881), display_text_response_881, sizeof(display_text_response_881), test_display_text_88, expect_response_and_finish); stktest_add_test("Display Text 8.9", "DisplayText", display_text_891, sizeof(display_text_891), display_text_response_891, sizeof(display_text_response_891), test_display_text_89, expect_response_and_finish); stktest_add_test("Display Text 8.10", "DisplayText", display_text_8101, sizeof(display_text_8101), display_text_response_8101, sizeof(display_text_response_8101), test_display_text_810, expect_response_and_finish); stktest_add_test("Display Text 9.1", "DisplayText", display_text_911, sizeof(display_text_911), display_text_response_911, sizeof(display_text_response_911), test_display_text_91, expect_response_and_finish); stktest_add_test("Display Text 10.1", "DisplayText", display_text_1011, sizeof(display_text_1011), display_text_response_1011, sizeof(display_text_response_1011), test_display_text_101, expect_response_and_finish); stktest_add_test("Get Inkey 1.1", "RequestDigit", get_inkey_111, sizeof(get_inkey_111), get_inkey_response_111, sizeof(get_inkey_response_111), test_get_inkey_11, expect_response_and_finish); stktest_add_test("Get Inkey 1.2", "RequestDigit", get_inkey_121, sizeof(get_inkey_121), get_inkey_response_121, sizeof(get_inkey_response_121), test_get_inkey_12, expect_response_and_finish); stktest_add_test("Get Inkey 1.3", "RequestDigit", get_inkey_131, sizeof(get_inkey_131), get_inkey_response_131, sizeof(get_inkey_response_131), test_get_inkey_13, expect_response_and_finish); stktest_add_test("Get Inkey 1.4", "RequestDigit", get_inkey_141, sizeof(get_inkey_141), get_inkey_response_141, sizeof(get_inkey_response_141), test_get_inkey_14, expect_response_and_finish); stktest_add_test("Get Inkey 1.5", "RequestKey", get_inkey_151, sizeof(get_inkey_151), get_inkey_response_151, sizeof(get_inkey_response_151), test_get_inkey_15, expect_response_and_finish); stktest_add_test("Get Inkey 1.6", "RequestKey", get_inkey_161, sizeof(get_inkey_161), get_inkey_response_161, sizeof(get_inkey_response_161), test_get_inkey_16, expect_response_and_finish); stktest_add_test("Get Inkey 2.1", "RequestDigit", get_inkey_211, sizeof(get_inkey_211), get_inkey_response_211, sizeof(get_inkey_response_211), test_get_inkey_21, expect_response_and_finish); stktest_add_test("Get Inkey 3.1", "RequestDigit", get_inkey_311, sizeof(get_inkey_311), get_inkey_response_311, sizeof(get_inkey_response_311), test_get_inkey_31, expect_response_and_finish); stktest_add_test("Get Inkey 3.2", "RequestDigit", get_inkey_321, sizeof(get_inkey_321), get_inkey_response_321, sizeof(get_inkey_response_321), test_get_inkey_32, expect_response_and_finish); stktest_add_test("Get Inkey 4.1", "RequestKey", get_inkey_411, sizeof(get_inkey_411), get_inkey_response_411, sizeof(get_inkey_response_411), test_get_inkey_41, expect_response_and_finish); stktest_add_test("Get Inkey 5.1a", "RequestConfirmation", get_inkey_511, sizeof(get_inkey_511), get_inkey_response_511, sizeof(get_inkey_response_511), test_get_inkey_51a, expect_response_and_finish); stktest_add_test("Get Inkey 5.1b", "RequestConfirmation", get_inkey_512, sizeof(get_inkey_512), get_inkey_response_512, sizeof(get_inkey_response_512), test_get_inkey_51b, expect_response_and_finish); stktest_add_test("Get Inkey 6.1", "RequestDigit", get_inkey_611, sizeof(get_inkey_611), get_inkey_response_611, sizeof(get_inkey_response_611), test_get_inkey_61, expect_response_and_finish); stktest_add_test("Get Inkey 6.2", "RequestDigit", get_inkey_621, sizeof(get_inkey_621), get_inkey_response_621, sizeof(get_inkey_response_621), test_get_inkey_62, expect_response_and_finish); stktest_add_test("Get Inkey 6.3", "RequestDigit", get_inkey_631, sizeof(get_inkey_631), get_inkey_response_631, sizeof(get_inkey_response_631), test_get_inkey_63, expect_response_and_finish); stktest_add_test("Get Inkey 6.4", "RequestDigit", get_inkey_641, sizeof(get_inkey_641), get_inkey_response_641, sizeof(get_inkey_response_641), test_get_inkey_64, expect_response_and_finish); /* Test Sequence for GetInkey 7.1 skipped, we do not support help */ stktest_add_test("Get Inkey 8.1", "RequestDigit", get_inkey_811, sizeof(get_inkey_811), get_inkey_response_811, sizeof(get_inkey_response_811), test_get_inkey_81, expect_response_and_finish); stktest_add_test("Get Inkey 9.1", "RequestDigit", get_inkey_911, sizeof(get_inkey_911), get_inkey_response_911, sizeof(get_inkey_response_911), test_get_inkey_91, expect_response_and_finish); stktest_add_test("Get Inkey 9.2", "RequestDigit", get_inkey_921, sizeof(get_inkey_921), get_inkey_response_921, sizeof(get_inkey_response_921), test_get_inkey_92, expect_response_and_finish); stktest_add_test("Get Inkey 9.3", "RequestDigit", get_inkey_931, sizeof(get_inkey_931), get_inkey_response_931, sizeof(get_inkey_response_931), test_get_inkey_93, expect_response_and_finish); stktest_add_test("Get Inkey 9.4", "RequestDigit", get_inkey_941, sizeof(get_inkey_941), get_inkey_response_941, sizeof(get_inkey_response_941), test_get_inkey_94, expect_response_and_finish); stktest_add_test("Get Inkey 9.5", "RequestDigit", get_inkey_951, sizeof(get_inkey_951), get_inkey_response_951, sizeof(get_inkey_response_951), test_get_inkey_95, expect_response_and_finish); stktest_add_test("Get Inkey 9.6", "RequestDigit", get_inkey_961, sizeof(get_inkey_961), get_inkey_response_961, sizeof(get_inkey_response_961), test_get_inkey_96, expect_response_and_finish); stktest_add_test("Get Inkey 9.7", "RequestDigit", get_inkey_971, sizeof(get_inkey_971), get_inkey_response_971, sizeof(get_inkey_response_971), test_get_inkey_97, expect_response_and_finish); stktest_add_test("Get Inkey 9.8", "RequestDigit", get_inkey_981, sizeof(get_inkey_981), get_inkey_response_981, sizeof(get_inkey_response_981), test_get_inkey_98, expect_response_and_finish); stktest_add_test("Get Inkey 9.9", "RequestDigit", get_inkey_991, sizeof(get_inkey_991), get_inkey_response_991, sizeof(get_inkey_response_991), test_get_inkey_99, expect_response_and_finish); stktest_add_test("Get Inkey 9.10", "RequestDigit", get_inkey_9101, sizeof(get_inkey_9101), get_inkey_response_9101, sizeof(get_inkey_response_9101), test_get_inkey_910, expect_response_and_finish); stktest_add_test("Get Inkey 10.1", "RequestDigit", get_inkey_1011, sizeof(get_inkey_1011), get_inkey_response_1011, sizeof(get_inkey_response_1011), test_get_inkey_101, expect_response_and_finish); stktest_add_test("Get Inkey 10.2", "RequestDigit", get_inkey_1021, sizeof(get_inkey_1021), get_inkey_response_1021, sizeof(get_inkey_response_1021), test_get_inkey_102, expect_response_and_finish); stktest_add_test("Get Inkey 11.1", "RequestKey", get_inkey_1111, sizeof(get_inkey_1111), get_inkey_response_1111, sizeof(get_inkey_response_1111), test_get_inkey_111, expect_response_and_finish); stktest_add_test("Get Inkey 12.1", "RequestDigit", get_inkey_1211, sizeof(get_inkey_1211), get_inkey_response_1211, sizeof(get_inkey_response_1211), test_get_inkey_121, expect_response_and_finish); stktest_add_test("Get Inkey 12.2", "RequestDigit", get_inkey_1221, sizeof(get_inkey_1221), get_inkey_response_1221, sizeof(get_inkey_response_1221), test_get_inkey_122, expect_response_and_finish); stktest_add_test("Get Inkey 13.1", "RequestKey", get_inkey_1311, sizeof(get_inkey_1311), get_inkey_response_1311, sizeof(get_inkey_response_1311), test_get_inkey_131, expect_response_and_finish); stktest_add_test("Get Input 1.1", "RequestDigits", get_input_111, sizeof(get_input_111), get_input_response_111, sizeof(get_input_response_111), test_get_input_11, expect_response_and_finish); stktest_add_test("Get Input 1.2", "RequestDigits", get_input_121, sizeof(get_input_121), get_input_response_121, sizeof(get_input_response_121), test_get_input_12, expect_response_and_finish); stktest_add_test("Get Input 1.3", "RequestInput", get_input_131, sizeof(get_input_131), get_input_response_131, sizeof(get_input_response_131), test_get_input_13, expect_response_and_finish); stktest_add_test("Get Input 1.4", "RequestDigits", get_input_141, sizeof(get_input_141), get_input_response_141, sizeof(get_input_response_141), test_get_input_14, expect_response_and_finish); stktest_add_test("Get Input 1.5", "RequestDigits", get_input_151, sizeof(get_input_151), get_input_response_151, sizeof(get_input_response_151), test_get_input_15, expect_response_and_finish); stktest_add_test("Get Input 1.6", "RequestDigits", get_input_161, sizeof(get_input_161), get_input_response_161, sizeof(get_input_response_161), test_get_input_16, expect_response_and_finish); stktest_add_test("Get Input 1.7", "RequestDigits", get_input_171, sizeof(get_input_171), get_input_response_171, sizeof(get_input_response_171), test_get_input_17, expect_response_and_finish); stktest_add_test("Get Input 1.8", "RequestDigits", get_input_181, sizeof(get_input_181), get_input_response_181, sizeof(get_input_response_181), test_get_input_18, expect_response_and_finish); stktest_add_test("Get Input 1.9", "RequestDigits", get_input_191, sizeof(get_input_191), get_input_response_191a, sizeof(get_input_response_191a), test_get_input_19, expect_response_and_finish); stktest_add_test("Get Input 1.10", "RequestDigits", get_input_1101, sizeof(get_input_1101), get_input_response_1101, sizeof(get_input_response_1101), test_get_input_110, expect_response_and_finish); stktest_add_test("Get Input 2.1", "RequestDigits", get_input_211, sizeof(get_input_211), get_input_response_211, sizeof(get_input_response_211), test_get_input_21, expect_response_and_finish); stktest_add_test("Get Input 3.1", "RequestInput", get_input_311, sizeof(get_input_311), get_input_response_311, sizeof(get_input_response_311), test_get_input_31, expect_response_and_finish); stktest_add_test("Get Input 3.2", "RequestInput", get_input_321, sizeof(get_input_321), get_input_response_321, sizeof(get_input_response_321), test_get_input_32, expect_response_and_finish); stktest_add_test("Get Input 4.1", "RequestInput", get_input_411, sizeof(get_input_411), get_input_response_411, sizeof(get_input_response_411), test_get_input_41, expect_response_and_finish); stktest_add_test("Get Input 4.2", "RequestInput", get_input_421, sizeof(get_input_421), get_input_response_421, sizeof(get_input_response_421), test_get_input_42, expect_response_and_finish); stktest_add_test("Get Input 5.1", "RequestDigits", get_input_511, sizeof(get_input_511), get_input_response_511, sizeof(get_input_response_511), test_get_input_51, expect_response_and_finish); stktest_add_test("Get Input 5.2", "RequestDigits", get_input_521, sizeof(get_input_521), get_input_response_521, sizeof(get_input_response_521), test_get_input_52, expect_response_and_finish); stktest_add_test("Get Input 6.1", "RequestDigits", get_input_611, sizeof(get_input_611), get_input_response_611a, sizeof(get_input_response_611a), test_get_input_61, expect_response_and_finish); stktest_add_test("Get Input 6.2", "RequestDigits", get_input_621, sizeof(get_input_621), get_input_response_621a, sizeof(get_input_response_621a), test_get_input_62, expect_response_and_finish); stktest_add_test("Get Input 6.3", "RequestDigits", get_input_631, sizeof(get_input_631), get_input_response_631a, sizeof(get_input_response_631a), test_get_input_63, expect_response_and_finish); stktest_add_test("Get Input 6.4", "RequestDigits", get_input_641, sizeof(get_input_641), get_input_response_641a, sizeof(get_input_response_641a), test_get_input_64, expect_response_and_finish); /* GetInput 7.1 skipped, Help not supported */ stktest_add_test("Get Input 8.1", "RequestDigits", get_input_811, sizeof(get_input_811), get_input_response_811, sizeof(get_input_response_811), test_get_input_81, expect_response_and_finish); stktest_add_test("Get Input 8.2", "RequestDigits", get_input_821, sizeof(get_input_821), get_input_response_821, sizeof(get_input_response_821), test_get_input_82, expect_response_and_finish); stktest_add_test("Get Input 8.3", "RequestDigits", get_input_831, sizeof(get_input_831), get_input_response_831, sizeof(get_input_response_831), test_get_input_83, expect_response_and_finish); stktest_add_test("Get Input 8.4", "RequestDigits", get_input_841, sizeof(get_input_841), get_input_response_841, sizeof(get_input_response_841), test_get_input_84, expect_response_and_finish); stktest_add_test("Get Input 8.5", "RequestDigits", get_input_851, sizeof(get_input_851), get_input_response_851, sizeof(get_input_response_851), test_get_input_85, expect_response_and_finish); stktest_add_test("Get Input 8.6", "RequestDigits", get_input_861, sizeof(get_input_861), get_input_response_861, sizeof(get_input_response_861), test_get_input_86, expect_response_and_finish); stktest_add_test("Get Input 8.7", "RequestDigits", get_input_871, sizeof(get_input_871), get_input_response_871, sizeof(get_input_response_871), test_get_input_87, expect_response_and_finish); stktest_add_test("Get Input 8.8", "RequestDigits", get_input_881, sizeof(get_input_881), get_input_response_881, sizeof(get_input_response_881), test_get_input_88, expect_response_and_finish); stktest_add_test("Get Input 8.9", "RequestDigits", get_input_891, sizeof(get_input_891), get_input_response_891, sizeof(get_input_response_891), test_get_input_89, expect_response_and_finish); stktest_add_test("Get Input 8.10", "RequestDigits", get_input_8101, sizeof(get_input_8101), get_input_response_8101, sizeof(get_input_response_8101), test_get_input_810, expect_response_and_finish); stktest_add_test("Get Input 9.1", "RequestInput", get_input_911, sizeof(get_input_911), get_input_response_911, sizeof(get_input_response_911), test_get_input_91, expect_response_and_finish); stktest_add_test("Get Input 9.2", "RequestInput", get_input_921, sizeof(get_input_921), get_input_response_921, sizeof(get_input_response_921), test_get_input_92, expect_response_and_finish); stktest_add_test("Get Input 10.1", "RequestInput", get_input_1011, sizeof(get_input_1011), get_input_response_1011, sizeof(get_input_response_1011), test_get_input_101, expect_response_and_finish); stktest_add_test("Get Input 10.2", "RequestInput", get_input_1021, sizeof(get_input_1021), get_input_response_1021, sizeof(get_input_response_1021), test_get_input_102, expect_response_and_finish); stktest_add_test("Get Input 11.1", "RequestInput", get_input_1111, sizeof(get_input_1111), get_input_response_1111, sizeof(get_input_response_1111), test_get_input_111, expect_response_and_finish); stktest_add_test("Get Input 11.2", "RequestInput", get_input_1121, sizeof(get_input_1121), get_input_response_1121, sizeof(get_input_response_1121), test_get_input_112, expect_response_and_finish); stktest_add_test("Get Input 12.1", "RequestInput", get_input_1211, sizeof(get_input_1211), get_input_response_1211, sizeof(get_input_response_1211), test_get_input_121, expect_response_and_finish); stktest_add_test("Get Input 12.2", "RequestInput", get_input_1221, sizeof(get_input_1221), get_input_response_1221, sizeof(get_input_response_1221), test_get_input_122, expect_response_and_finish); stktest_add_test("More Time 1.1", NULL, more_time_111, sizeof(more_time_111), more_time_response_111, sizeof(more_time_response_111), NULL, expect_response_and_finish); stktest_add_timed_test("Play Tone 1.1a", "LoopTone", play_tone_111, sizeof(play_tone_111), play_tone_response_111, sizeof(play_tone_response_111), test_play_tone_11a, expect_response_and_finish, 5.0, 6.0); stktest_add_timed_test("Play Tone 1.1b", "LoopTone", play_tone_112, sizeof(play_tone_112), play_tone_response_112, sizeof(play_tone_response_112), test_play_tone_11b, expect_response_and_finish, 5.0, 6.0); stktest_add_timed_test("Play Tone 1.1c", "LoopTone", play_tone_113, sizeof(play_tone_113), play_tone_response_113, sizeof(play_tone_response_113), test_play_tone_11c, expect_response_and_finish, 5.0, 6.0); stktest_add_test("Play Tone 1.1d", "PlayTone", play_tone_114, sizeof(play_tone_114), play_tone_response_114, sizeof(play_tone_response_114), test_play_tone_11d, expect_response_and_finish); stktest_add_timed_test("Play Tone 1.1e", "LoopTone", play_tone_115, sizeof(play_tone_115), play_tone_response_115, sizeof(play_tone_response_115), test_play_tone_11e, expect_response_and_finish, 5.0, 6.0); stktest_add_timed_test("Play Tone 1.1f", "LoopTone", play_tone_116, sizeof(play_tone_116), play_tone_response_116, sizeof(play_tone_response_116), test_play_tone_11f, expect_response_and_finish, 5.0, 6.0); stktest_add_timed_test("Play Tone 1.1g", "LoopTone", play_tone_117, sizeof(play_tone_117), play_tone_response_117, sizeof(play_tone_response_117), test_play_tone_11g, expect_response_and_finish, 5.0, 6.0); stktest_add_timed_test("Play Tone 1.1h", "LoopTone", play_tone_118, sizeof(play_tone_118), play_tone_response_118, sizeof(play_tone_response_118), test_play_tone_11h, expect_response_and_finish, 5.0, 6.0); stktest_add_test("Play Tone 1.1i", "PlayTone", play_tone_119, sizeof(play_tone_119), play_tone_response_119, sizeof(play_tone_response_119), test_play_tone_11i, expect_response_and_finish); stktest_add_test("Play Tone 1.1j", "PlayTone", play_tone_1110, sizeof(play_tone_1110), play_tone_response_1110, sizeof(play_tone_response_1110), test_play_tone_11j, expect_response_and_finish); stktest_add_test("Play Tone 1.1k", "PlayTone", play_tone_1111, sizeof(play_tone_1111), play_tone_response_1111, sizeof(play_tone_response_1111), test_play_tone_11k, expect_response_and_finish); stktest_add_test("Play Tone 1.1l", "PlayTone", play_tone_1112, sizeof(play_tone_1112), play_tone_response_1112, sizeof(play_tone_response_1112), test_play_tone_11l, expect_response_and_finish); stktest_add_test("Play Tone 1.1m", "PlayTone", play_tone_1113, sizeof(play_tone_1113), play_tone_response_1113, sizeof(play_tone_response_1113), test_play_tone_11m, expect_response_and_finish); stktest_add_test("Play Tone 1.1n", "LoopTone", play_tone_1114, sizeof(play_tone_1114), play_tone_response_1114, sizeof(play_tone_response_1114), test_play_tone_11n, expect_response_and_finish); stktest_add_test("Play Tone 1.1o", "PlayTone", play_tone_1115, sizeof(play_tone_1115), play_tone_response_1115, sizeof(play_tone_response_1115), test_play_tone_11o, expect_response_and_finish); stktest_add_test("Play Tone 2.1a", "PlayTone", play_tone_211, sizeof(play_tone_211), play_tone_response_211, sizeof(play_tone_response_211), test_play_tone_21, expect_response_and_finish); stktest_add_test("Play Tone 2.1b", "PlayTone", play_tone_212, sizeof(play_tone_212), play_tone_response_212, sizeof(play_tone_response_212), test_play_tone_21, expect_response_and_finish); stktest_add_test("Play Tone 2.1c", "PlayTone", play_tone_213, sizeof(play_tone_213), play_tone_response_213, sizeof(play_tone_response_213), test_play_tone_21, expect_response_and_finish); stktest_add_test("Play Tone 3.1", "PlayTone", play_tone_311, sizeof(play_tone_311), play_tone_response_311, sizeof(play_tone_response_311), test_play_tone_31, expect_response_and_finish); stktest_add_test("Play Tone 3.2", "PlayTone", play_tone_321, sizeof(play_tone_321), play_tone_response_321, sizeof(play_tone_response_321), test_play_tone_32, expect_response_and_finish); stktest_add_test("Play Tone 3.3", "PlayTone", play_tone_331, sizeof(play_tone_331), play_tone_response_331, sizeof(play_tone_response_331), test_play_tone_33, expect_response_and_finish); stktest_add_test("Play Tone 3.4", "PlayTone", play_tone_341, sizeof(play_tone_341), play_tone_response_341, sizeof(play_tone_response_341), test_play_tone_34, expect_response_and_finish); stktest_add_test("Play Tone 4.1a", "PlayTone", play_tone_411, sizeof(play_tone_411), play_tone_response_411, sizeof(play_tone_response_411), test_play_tone_41a, expect_response_and_finish); stktest_add_test("Play Tone 4.1b", "PlayTone", play_tone_412, sizeof(play_tone_412), play_tone_response_412, sizeof(play_tone_response_412), test_play_tone_41b, expect_response_and_finish); stktest_add_test("Play Tone 4.2a", "PlayTone", play_tone_421, sizeof(play_tone_421), play_tone_response_421, sizeof(play_tone_response_421), test_play_tone_42a, expect_response_and_finish); stktest_add_test("Play Tone 4.2b", "PlayTone", play_tone_422, sizeof(play_tone_422), play_tone_response_422, sizeof(play_tone_response_422), test_play_tone_42b, expect_response_and_finish); stktest_add_test("Play Tone 4.3a", "PlayTone", play_tone_431, sizeof(play_tone_431), play_tone_response_431, sizeof(play_tone_response_431), test_play_tone_43a, expect_response_and_finish); stktest_add_test("Play Tone 4.3b", "PlayTone", play_tone_432, sizeof(play_tone_432), play_tone_response_432, sizeof(play_tone_response_432), test_play_tone_43b, expect_response_and_finish); stktest_add_test("Play Tone 4.4a", "PlayTone", play_tone_441, sizeof(play_tone_441), play_tone_response_441, sizeof(play_tone_response_441), test_play_tone_44a, expect_response_and_finish); stktest_add_test("Play Tone 4.4b", "PlayTone", play_tone_442, sizeof(play_tone_442), play_tone_response_442, sizeof(play_tone_response_442), test_play_tone_44b, expect_response_and_finish); stktest_add_test("Play Tone 4.4c", "PlayTone", play_tone_443, sizeof(play_tone_443), play_tone_response_443, sizeof(play_tone_response_443), test_play_tone_44c, expect_response_and_finish); stktest_add_test("Play Tone 4.5a", "PlayTone", play_tone_451, sizeof(play_tone_451), play_tone_response_451, sizeof(play_tone_response_451), test_play_tone_45a, expect_response_and_finish); stktest_add_test("Play Tone 4.5b", "PlayTone", play_tone_452, sizeof(play_tone_452), play_tone_response_452, sizeof(play_tone_response_452), test_play_tone_45b, expect_response_and_finish); stktest_add_test("Play Tone 4.5c", "PlayTone", play_tone_453, sizeof(play_tone_453), play_tone_response_453, sizeof(play_tone_response_453), test_play_tone_45c, expect_response_and_finish); stktest_add_test("Play Tone 4.6a", "PlayTone", play_tone_461, sizeof(play_tone_461), play_tone_response_461, sizeof(play_tone_response_461), test_play_tone_46a, expect_response_and_finish); stktest_add_test("Play Tone 4.6b", "PlayTone", play_tone_462, sizeof(play_tone_462), play_tone_response_462, sizeof(play_tone_response_462), test_play_tone_46b, expect_response_and_finish); stktest_add_test("Play Tone 4.6c", "PlayTone", play_tone_463, sizeof(play_tone_463), play_tone_response_463, sizeof(play_tone_response_463), test_play_tone_46c, expect_response_and_finish); stktest_add_test("Play Tone 4.7a", "PlayTone", play_tone_471, sizeof(play_tone_471), play_tone_response_471, sizeof(play_tone_response_471), test_play_tone_47a, expect_response_and_finish); stktest_add_test("Play Tone 4.7b", "PlayTone", play_tone_472, sizeof(play_tone_472), play_tone_response_472, sizeof(play_tone_response_472), test_play_tone_47b, expect_response_and_finish); stktest_add_test("Play Tone 4.7c", "PlayTone", play_tone_473, sizeof(play_tone_473), play_tone_response_473, sizeof(play_tone_response_473), test_play_tone_47c, expect_response_and_finish); stktest_add_test("Play Tone 4.8a", "PlayTone", play_tone_481, sizeof(play_tone_481), play_tone_response_481, sizeof(play_tone_response_481), test_play_tone_48a, expect_response_and_finish); stktest_add_test("Play Tone 4.8b", "PlayTone", play_tone_482, sizeof(play_tone_482), play_tone_response_482, sizeof(play_tone_response_482), test_play_tone_48b, expect_response_and_finish); stktest_add_test("Play Tone 4.8c", "PlayTone", play_tone_483, sizeof(play_tone_483), play_tone_response_483, sizeof(play_tone_response_483), test_play_tone_48c, expect_response_and_finish); stktest_add_test("Play Tone 4.9a", "PlayTone", play_tone_491, sizeof(play_tone_491), play_tone_response_491, sizeof(play_tone_response_491), test_play_tone_49a, expect_response_and_finish); stktest_add_test("Play Tone 4.9b", "PlayTone", play_tone_492, sizeof(play_tone_492), play_tone_response_492, sizeof(play_tone_response_492), test_play_tone_49b, expect_response_and_finish); stktest_add_test("Play Tone 4.9c", "PlayTone", play_tone_493, sizeof(play_tone_493), play_tone_response_493, sizeof(play_tone_response_493), test_play_tone_49c, expect_response_and_finish); stktest_add_test("Play Tone 4.10a", "PlayTone", play_tone_4101, sizeof(play_tone_4101), play_tone_response_4101, sizeof(play_tone_response_4101), test_play_tone_410a, expect_response_and_finish); stktest_add_test("Play Tone 4.10b", "PlayTone", play_tone_4102, sizeof(play_tone_4102), play_tone_response_4102, sizeof(play_tone_response_4102), test_play_tone_410b, expect_response_and_finish); stktest_add_test("Play Tone 5.1a", "PlayTone", play_tone_511, sizeof(play_tone_511), play_tone_response_511, sizeof(play_tone_response_511), test_play_tone_51, expect_response_and_finish); stktest_add_test("Play Tone 5.1b", "PlayTone", play_tone_512, sizeof(play_tone_512), play_tone_response_512, sizeof(play_tone_response_512), test_play_tone_51, expect_response_and_finish); stktest_add_test("Play Tone 5.1c", "PlayTone", play_tone_513, sizeof(play_tone_513), play_tone_response_513, sizeof(play_tone_response_513), test_play_tone_51, expect_response_and_finish); stktest_add_timed_test("Play Tone 6.1a", "LoopTone", play_tone_611, sizeof(play_tone_611), play_tone_response_611, sizeof(play_tone_response_611), test_play_tone_61a, expect_response_and_finish, 5.0, 6.0); stktest_add_timed_test("Play Tone 6.1b", "LoopTone", play_tone_612, sizeof(play_tone_612), play_tone_response_612, sizeof(play_tone_response_612), test_play_tone_61b, expect_response_and_finish, 5.0, 6.0); stktest_add_timed_test("Play Tone 6.1c", "LoopTone", play_tone_613, sizeof(play_tone_613), play_tone_response_613, sizeof(play_tone_response_613), test_play_tone_61c, expect_response_and_finish, 5.0, 6.0); stktest_add_test("Poll Interval 1.1", NULL, poll_interval_111, sizeof(poll_interval_111), poll_interval_response_111, sizeof(poll_interval_response_111), NULL, expect_response_and_finish); } static void test_destroy(gpointer user_data) { struct test *test = user_data; g_free(test->name); g_free(test->method); g_free(test->req_pdu); g_free(test->rsp_pdu); g_free(test); } static void __stktest_test_summarize(void) { GList *l; unsigned int not_run = 0; unsigned int passed = 0; unsigned int failed = 0; g_print("\n\nTest Summary\n"); g_print("============\n"); for (l = tests; l; l = l->next) { struct test *test = l->data; g_print("%-60s", test->name); switch (test->result) { case TEST_RESULT_NOT_RUN: g_print("Not Run\n"); not_run += 1; break; case TEST_RESULT_PASSED: g_print("Passed\n"); passed += 1; break; case TEST_RESULT_FAILED: g_print("Failed\n"); failed += 1; break; } } g_print("\nTotal: %d, Passed: %d(%.1f%%), Failed: %d, NotRun: %d\n", not_run + passed + failed, passed, (float) passed * 100 / (not_run + passed + failed), failed, not_run); } static void __stktest_test_cleanup(void) { g_list_free_full(tests, test_destroy); tests = NULL; cur_test = NULL; } static gboolean option_version = FALSE; static GOptionEntry options[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { NULL }, }; int main(int argc, char **argv) { GOptionContext *context; GError *error = NULL; DBusError err; guint watch; struct sigaction sa; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (option_version == TRUE) { printf("%s\n", VERSION); exit(0); } __stktest_test_init(); main_loop = g_main_loop_new(NULL, FALSE); dbus_error_init(&err); conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err); if (conn == NULL) { if (dbus_error_is_set(&err) == TRUE) { fprintf(stderr, "%s\n", err.message); dbus_error_free(&err); } else fprintf(stderr, "Can't register with system bus\n"); exit(1); } g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL); memset(&sa, 0, sizeof(sa)); sa.sa_handler = sig_term; sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); watch = g_dbus_add_service_watch(conn, OFONO_SERVICE, ofono_connect, ofono_disconnect, NULL, NULL); timer = g_timer_new(); g_main_loop_run(main_loop); g_timer_destroy(timer); g_dbus_remove_watch(conn, watch); if (ofono_running == TRUE) ofono_disconnect(conn, NULL); dbus_connection_unref(conn); g_main_loop_unref(main_loop); __stktest_test_summarize(); __stktest_test_cleanup(); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/tools/lookup-apn.c0000644000015600001650000000541112671500024021771 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "plugins/mbpi.h" static void lookup_apn(const char *match_mcc, const char *match_mnc, gboolean allow_duplicates) { GSList *l; GSList *apns; GError *error = NULL; g_print("Searching for info for network: %s%s\n", match_mcc, match_mnc); apns = mbpi_lookup_apn(match_mcc, match_mnc, OFONO_GPRS_CONTEXT_TYPE_ANY, allow_duplicates, &error); if (apns == NULL) { if (error != NULL) { g_printerr("Lookup failed: %s\n", error->message); g_error_free(error); } return; } for (l = apns; l; l = l->next) { struct ofono_gprs_provision_data *ap = l->data; g_print("\n"); g_print("Name: %s\n", ap->name); g_print("APN: %s\n", ap->apn); g_print("Type: %s\n", mbpi_ap_type(ap->type)); g_print("Username: %s\n", ap->username); g_print("Password: %s\n", ap->password); mbpi_ap_free(ap); } g_slist_free(apns); } static gboolean option_version = FALSE; static gboolean option_duplicates = FALSE; static GOptionEntry options[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { "allow-duplicates", 0, 0, G_OPTION_ARG_NONE, &option_duplicates, "Allow duplicate access point types" }, { NULL }, }; int main(int argc, char **argv) { GOptionContext *context; GError *error = NULL; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (option_version == TRUE) { g_print("%s\n", VERSION); exit(0); } if (argc < 2) { g_printerr("Missing parameters\n"); exit(1); } lookup_apn(argv[1], argv[2], option_duplicates); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/tools/lookup-provider-name.c0000644000015600001650000000437312671500024023771 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include "plugins/mbpi.h" static void lookup_cdma_provider_name(const char *match_sid) { GError *error = NULL; char *name; g_print("Searching for serving network name with SID: %s\n", match_sid); name = mbpi_lookup_cdma_provider_name(match_sid, &error); if (name == NULL) { if (error != NULL) { g_printerr("Lookup failed: %s\n", error->message); g_error_free(error); } else g_printerr("Not found\n"); return; } g_print("CDMA provider name: %s\n", name); g_free(name); } static gboolean option_version = FALSE; static GOptionEntry options[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { NULL }, }; int main(int argc, char **argv) { GOptionContext *context; GError *error = NULL; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (option_version == TRUE) { g_print("%s\n", VERSION); exit(0); } if (argc < 1) { g_printerr("Missing parameters\n"); exit(1); } lookup_cdma_provider_name(argv[1]); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/tools/tty-redirector.c0000644000015600001650000001716012671500024022670 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011-2012 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #define IFX_RESET_PATH "/sys/module/hsi_ffl_tty/parameters/reset_modem" static gchar *option_device = NULL; static gboolean option_ifx = FALSE; static GMainLoop *main_loop; static bool main_terminated; static int device_fd = -1; static int client_fd = -1; static guint device_watch = 0; static guint client_watch = 0; static gboolean shutdown_timeout(gpointer user_data) { g_main_loop_quit(main_loop); return FALSE; } static void do_terminate(void) { if (main_terminated) return; main_terminated = true; g_timeout_add_seconds(1, shutdown_timeout, NULL); } static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct signalfd_siginfo si; ssize_t result; int fd; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; fd = g_io_channel_unix_get_fd(channel); result = read(fd, &si, sizeof(si)); if (result != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGINT: case SIGTERM: do_terminate(); break; } return TRUE; } static guint create_watch(int fd, GIOFunc func) { GIOChannel *channel; guint source; channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, func, NULL); g_io_channel_unref(channel); return source; } static guint setup_signalfd(void) { sigset_t mask; int fd; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { perror("Failed to set signal mask"); return 0; } fd = signalfd(-1, &mask, 0); if (fd < 0) { perror("Failed to create signal descriptor"); return 0; } return create_watch(fd, signal_handler); } static int write_file(const char *path, const char *value) { ssize_t written; int fd; fd = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC); if (fd < 0) { perror("Failed to open file"); return -1; } written = write(fd, value, strlen(value)); if (written < 0) { perror("Failed to write value"); return -1; } return 0; } static int open_device(const char *path) { struct termios ti; int fd; /* Switch TTY to raw mode */ memset(&ti, 0, sizeof(ti)); cfmakeraw(&ti); fd = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC); if (fd < 0) { perror("Failed to open device"); return -1; } tcflush(fd, TCIOFLUSH); tcsetattr(fd, TCSANOW, &ti); return fd; } static gboolean forward_data(GIOCondition cond, int input_fd, int output_fd) { unsigned char buf[1024]; ssize_t bytes_read, bytes_written; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; bytes_read = read(input_fd, buf, sizeof(buf)); if (bytes_read < 0) return FALSE; bytes_written = write(output_fd, buf, bytes_read); if (bytes_written != bytes_read) return FALSE; return TRUE; } static gboolean device_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { if (forward_data(cond, device_fd, client_fd) == FALSE) { g_printerr("Closing device descriptor\n"); if (client_watch > 0) { g_source_remove(client_watch); client_watch = 0; } device_watch = 0; return FALSE; } return TRUE; } static gboolean client_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { if (forward_data(cond, client_fd, device_fd) == FALSE) { g_printerr("Closing client connection\n"); if (device_watch > 0) { g_source_remove(device_watch); device_watch = 0; } client_watch = 0; return FALSE; } return TRUE; } static gboolean accept_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct sockaddr_in addr; socklen_t addrlen; int fd, nfd; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; fd = g_io_channel_unix_get_fd(channel); memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); nfd = accept4(fd, (struct sockaddr *) &addr, &addrlen, SOCK_CLOEXEC); if (nfd < 0) return TRUE; if (device_watch > 0) { g_printerr("Closing previous descriptors\n"); g_source_remove(device_watch); device_watch = 0; if (client_watch > 0) { g_source_remove(client_watch); client_watch = 0; } } if (option_ifx == TRUE) { write_file(IFX_RESET_PATH, "1"); sleep(1); write_file(IFX_RESET_PATH, "0"); sleep(1); } device_fd = open_device(option_device); if (device_fd < 0) { close(nfd); return TRUE; } device_watch = create_watch(device_fd, device_handler); if (device_watch == 0) { close(nfd); return TRUE; } client_watch = create_watch(nfd, client_handler); if (client_watch == 0) { g_source_remove(device_watch); device_watch = 0; close(nfd); return TRUE; } client_fd = nfd; return TRUE; } static guint setup_server(void) { struct sockaddr_in addr; int fd, opt = 1; fd = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) { perror("Failed to open server socket"); return 0; } setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(12345); if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("Failed to bind server socket"); close(fd); return 0; } if (listen(fd, 1) < 0) { perror("Failed to listen server socket"); close(fd); return 0; } return create_watch(fd, accept_handler); } static GOptionEntry options[] = { { "device", 0, 0, G_OPTION_ARG_STRING, &option_device, "Specify device to use", "DEVNODE" }, { "ifx", 0, 0, G_OPTION_ARG_NONE, &option_ifx, "Use Infineon reset handling" }, { NULL }, }; int main(int argc, char **argv) { GOptionContext *context; GError *error = NULL; guint signal_watch; guint server_watch; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { if (error != NULL) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); return EXIT_FAILURE; } g_option_context_free(context); if (option_device == NULL) { if (option_ifx == TRUE) { option_device = g_strdup("/dev/ttyIFX0"); } else { g_printerr("No valid device specified\n"); return EXIT_FAILURE; } } main_loop = g_main_loop_new(NULL, FALSE); signal_watch = setup_signalfd(); server_watch = setup_server(); g_main_loop_run(main_loop); g_source_remove(server_watch); g_source_remove(signal_watch); g_main_loop_unref(main_loop); g_free(option_device); return EXIT_SUCCESS; } ofono-1.17.bzr6912+16.04.20160314.3/COPYING0000644000015600001650000004313312671500024017436 0ustar pbuserpbgroup00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ofono-1.17.bzr6912+16.04.20160314.3/AUTHORS0000644000015600001650000001065112671500024017452 0ustar pbuserpbgroup00000000000000Denis Kenzior Marcel Holtmann Andrzej Zaborowski Minjun Li Rémi Denis-Courmont Aki Niemi Yang Gu Shane Bryan Santtu Lakkala Andres Salomon Alexander Kanavin Ismo Puustinen Zhenhua Zhang Jukka Saunamäki Pekka Pessi Marko Saukko Olivier Le Thanh Duong Ryan Raasch Gustavo Padovan Martin Xu Zhigang Li Anders Gustafsson Jussi Kukkonen Sjur Brændeland João Paulo Rechi Vita Vinicius Costa Gomes Inaky Perez-Gonzalez Kristen Carlson Accardi Matthias Günther Daniel Wagner Kalle Valo Pasi Miettinen Florian Steinel Arun Ravindran Thadeu Lima de Souza Cascardo Petteri Tikander Jeevaka Badrappan Frank Gau Kai Vehmanen Mika Liljeberg Marit Henriksen Guillaume Lucas George Matveev Antti Paila Rafael Ignacio Zurita Helen Clemson Jessica Nilsson Oleg Zhurakivskyy Lasse Kunnasluoto John Mathew Benoît Monin Dara Spieker-Doyle Neil Jerram Lei Yu Oskari Timperi Faiyaz Baxamusa Jussi Kangas Guillaume Zajac Olivier Guiter Amit Mendapara Frédéric Danis Frédéric Dalleau Paavo Leinonen Jan Luebbe Antoine Reversat Patrick Porlan Miia Leinonen Jarko Poutiainen Bertrand Aygon Christian Lam Philippe Nunes Nicolas Bertrand Caiwen Zhang Bernhard Guillon Michael Schloh von Bennewitz Luiz Augusto von Dentz Mikel Astiz Christopher Vogl Syam Sidhardhan Renat Zaripov Michael Brudevold Pablo Neira Ayuso August Mayer Holger Hans Peter Freyther Cedric Jehasse Mingli Wu Forest Bond Claudio Takahasi Paulo Borges Anthony Viallard Jesper Larsen Slava Monich Andrew Earl Krzysztof Wilk Tony Espy Martin Pitt Alfonso Sanchez-Beato Jussi Pakkanen Sergio Checa Blanco Philip Paeps Kuba Pawlak Tommi Kenakkala Alex J Lennon Sergey Alirzaev Marko Sulejic Johannes 'josch' Schauer ofono-1.17.bzr6912+16.04.20160314.3/gril/0000755000015600001650000000000012671500304017335 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/gril/grilutil.h0000644000015600001650000000355112671500024021344 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical 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. * * 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 * */ #ifndef __GRILUTIL_H #define __GRILUTIL_H #ifdef __cplusplus extern "C" { #endif #include "gfunc.h" #include "parcel.h" #include "gril.h" const char *ril_ofono_protocol_to_ril_string(guint protocol); int ril_protocol_string_to_ofono_protocol(gchar *protocol_str); const char *ril_appstate_to_string(int app_state); const char *ril_apptype_to_string(int app_type); const char *ril_authtype_to_string(int auth_type); const char *ril_cardstate_to_string(int card_state); const char *ril_error_to_string(int error); const char *ril_pinstate_to_string(int pin_state); const char *ril_radio_state_to_string(int radio_state); const char *ril_radio_tech_to_string(int radio_tech); const char *ril_request_id_to_string(int req); const char *ril_unsol_request_to_string(int request); const char *ril_pdp_fail_to_string(int status); void g_ril_util_debug_hexdump(gboolean in, const unsigned char *buf, gsize len, GRilDebugFunc debugf, gpointer user_data); gboolean g_ril_util_setup_io(GIOChannel *io, GIOFlags flags); #ifdef __cplusplus } #endif #endif /* __GRILUTIL_H */ ofono-1.17.bzr6912+16.04.20160314.3/gril/grilrequest.h0000644000015600001650000001551712671500024022064 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2014 Canonical Ltd. * Copyright (C) 2015 Ratchanan Srirattanamet. * * 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 * */ #ifndef __GRILREQUEST_H #define __GRILREQUEST_H #include #include #include #include "gril.h" #ifdef __cplusplus extern "C" { #endif struct req_call_fwd { int action; int type; int cls; const struct ofono_phone_number *number; int time; }; struct req_deactivate_data_call { gint cid; guint reason; }; struct req_setup_data_call { guint tech; guint data_profile; gchar *apn; gchar *username; gchar *password; guint auth_type; guint protocol; unsigned req_cid; }; struct req_sim_read_info { guint app_type; gchar *aid_str; int fileid; const unsigned char *path; unsigned int path_len; }; struct req_sim_read_binary { guint app_type; gchar *aid_str; int fileid; const unsigned char *path; unsigned int path_len; int start; int length; }; struct req_sim_read_record { guint app_type; gchar *aid_str; int fileid; const unsigned char *path; unsigned int path_len; int record; int length; }; struct req_sim_write_binary { guint app_type; gchar *aid_str; int fileid; const unsigned char *path; unsigned int path_len; int start; int length; const unsigned char *data; }; enum req_record_access_mode { GRIL_REC_ACCESS_MODE_CURRENT, GRIL_REC_ACCESS_MODE_ABSOLUTE, GRIL_REC_ACCESS_MODE_NEXT, GRIL_REC_ACCESS_MODE_PREVIOUS, }; struct req_sim_write_record { guint app_type; gchar *aid_str; int fileid; const unsigned char *path; unsigned int path_len; enum req_record_access_mode mode; int record; int length; const unsigned char *data; }; struct req_pin_change_state { const gchar *aid_str; enum ofono_sim_password_type passwd_type; int enable; const char *passwd; }; struct req_sms_cmgs { const unsigned char *pdu; int pdu_len; int tpdu_len; }; gboolean g_ril_request_deactivate_data_call(GRil *gril, const struct req_deactivate_data_call *req, struct parcel *rilp, struct ofono_error *error); void g_ril_request_power(GRil *gril, gboolean power, struct parcel *rilp); void g_ril_request_set_net_select_manual(GRil *gril, const char *mccmnc, struct parcel *rilp); gboolean g_ril_request_setup_data_call(GRil *gril, const struct req_setup_data_call *req, struct parcel *rilp, struct ofono_error *error); gboolean g_ril_request_sim_read_info(GRil *gril, const struct req_sim_read_info *req, struct parcel *rilp); gboolean g_ril_request_sim_read_binary(GRil *gril, const struct req_sim_read_binary *req, struct parcel *rilp); gboolean g_ril_request_sim_read_record(GRil *gril, const struct req_sim_read_record *req, struct parcel *rilp); gboolean g_ril_request_sim_write_binary(GRil *gril, const struct req_sim_write_binary *req, struct parcel *rilp); gboolean g_ril_request_sim_write_record(GRil *gril, const struct req_sim_write_record *req, struct parcel *rilp); void g_ril_request_read_imsi(GRil *gril, const gchar *aid_str, struct parcel *rilp); void g_ril_request_pin_send(GRil *gril, const char *passwd, const gchar *aid_str, struct parcel *rilp); gboolean g_ril_request_pin_change_state(GRil *gril, const struct req_pin_change_state *req, struct parcel *rilp); void g_ril_request_pin_send_puk(GRil *gril, const char *puk, const char *passwd, const gchar *aid_str, struct parcel *rilp); void g_ril_request_change_passwd(GRil *gril, const char *old_passwd, const char *new_passwd, const gchar *aid_str, struct parcel *rilp); void g_ril_request_sms_cmgs(GRil *gril, const struct req_sms_cmgs *req, struct parcel *rilp); void g_ril_request_sms_acknowledge(GRil *gril, struct parcel *rilp); void g_ril_request_set_smsc_address(GRil *gril, const struct ofono_phone_number *sca, struct parcel *rilp); void g_ril_request_dial(GRil *gril, const struct ofono_phone_number *ph, enum ofono_clir_option clir, struct parcel *rilp); void g_ril_request_hangup(GRil *gril, unsigned call_id, struct parcel *rilp); void g_ril_request_dtmf(GRil *gril, char dtmf_char, struct parcel *rilp); void g_ril_request_separate_conn(GRil *gril, int call_id, struct parcel *rilp); void g_ril_request_set_supp_svc_notif(GRil *gril, struct parcel *rilp); void g_ril_request_set_mute(GRil *gril, int muted, struct parcel *rilp); void g_ril_request_send_ussd(GRil *gril, const char *ussd, struct parcel *rilp); void g_ril_request_set_call_waiting(GRil *gril, int enabled, int serviceclass, struct parcel *rilp); void g_ril_request_query_call_waiting(GRil *gril, int serviceclass, struct parcel *rilp); void g_ril_request_set_clir(GRil *gril, int mode, struct parcel *rilp); void g_ril_request_screen_state(GRil *gril, int state, struct parcel *rilp); void g_ril_request_call_fwd(GRil *gril, const struct req_call_fwd *req, struct parcel *rilp); void g_ril_request_set_preferred_network_type(GRil *gril, int net_type, struct parcel *rilp); void g_ril_request_query_facility_lock(GRil *gril, const char *facility, const char *password, int services, struct parcel *rilp); void g_ril_request_set_facility_lock(GRil *gril, const char *facility, int enable, const char *passwd, int services, struct parcel *rilp); void g_ril_request_change_barring_password(GRil *gril, const char *facility, const char *old_passwd, const char *new_passwd, struct parcel *rilp); void g_ril_request_oem_hook_raw(GRil *gril, const void *payload, size_t length, struct parcel *rilp); void g_ril_request_oem_hook_strings(GRil *gril, const char **strs, int num_str, struct parcel *rilp); void g_ril_request_set_initial_attach_apn(GRil *gril, const char *apn, int proto, const char *user, const char *passwd, const char *mccmnc, struct parcel *rilp); void g_ril_request_set_uicc_subscription(GRil *gril, int slot_id, int app_index, int sub_id, int sub_status, struct parcel *rilp); #ifdef __cplusplus } #endif #endif /* __GRILREQUEST_H */ ofono-1.17.bzr6912+16.04.20160314.3/gril/parcel.h0000644000015600001650000000320712671500024020755 0ustar pbuserpbgroup00000000000000/* * Copyright © 2011 Joel Armstrong * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License (`GPL') as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Based on parcel implementation from https://bitbucket.org/floren/inferno * */ #ifndef __PARCEL_H #define __PARCEL_H #include struct parcel { char *data; size_t offset; size_t capacity; size_t size; int malformed; }; struct parcel_str_array { int num_str; char *str[]; }; void parcel_init(struct parcel *p); void parcel_grow(struct parcel *p, size_t size); void parcel_free(struct parcel *p); int32_t parcel_r_int32(struct parcel *p); int parcel_w_int32(struct parcel *p, int32_t val); int parcel_w_string(struct parcel *p, const char *str); char *parcel_r_string(struct parcel *p); int parcel_w_raw(struct parcel *p, const void *data, size_t len); void *parcel_r_raw(struct parcel *p, int *len); size_t parcel_data_avail(struct parcel *p); struct parcel_str_array *parcel_r_str_array(struct parcel *p); void parcel_free_str_array(struct parcel_str_array *str_arr); #endif ofono-1.17.bzr6912+16.04.20160314.3/gril/gril.h0000644000015600001650000001203212671500073020444 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical 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. * * 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 * */ #ifndef __GRIL_H #define __GRIL_H #ifdef __cplusplus extern "C" { #endif #include "grilio.h" #include "grilutil.h" #include "parcel.h" #include "ril_constants.h" #include "drivers/rilmodem/vendor.h" #define RIL_MAX_NUM_ACTIVE_DATA_CALLS 2 struct _GRil; typedef struct _GRil GRil; /* * This struct represents an entire RIL message read * from the command socket. It can hold responses or * unsolicited requests from RILD. */ struct ril_msg { gchar *buf; gsize buf_len; gboolean unsolicited; int req; int serial_no; int error; }; typedef void (*GRilResponseFunc)(struct ril_msg *message, gpointer user_data); typedef void (*GRilNotifyFunc)(struct ril_msg *message, gpointer user_data); typedef const char *(*GRilMsgIdToStrFunc)(int msg_id); /** * TRACE: * @fmt: format string * @arg...: list of arguments * * Simple macro around ofono_debug() used for tracing RIL messages * name it is called in. */ #define G_RIL_TRACE(gril, fmt, arg...) do { \ if (gril && g_ril_get_trace(gril)) \ ofono_debug(fmt, ## arg); \ } while (0) extern char print_buf[]; #define g_ril_print_request(gril, token, req) \ G_RIL_TRACE(gril, "[%d,%04d]> %s %s", \ g_ril_get_slot(gril), token, \ g_ril_request_id_to_string(gril, req), print_buf) #define g_ril_print_request_no_args(gril, token, req) \ G_RIL_TRACE(gril, "[%d,%04d]> %s", \ g_ril_get_slot(gril), token, \ g_ril_request_id_to_string(gril, req)) #define g_ril_print_response(gril, message) \ G_RIL_TRACE(gril, "[%d,%04d]< %s %s", \ g_ril_get_slot(gril), \ message->serial_no, \ g_ril_request_id_to_string(gril, message->req), \ print_buf) #define g_ril_print_response_no_args(gril, message) \ G_RIL_TRACE(gril, "[%d,%04d]< %s", \ g_ril_get_slot(gril), message->serial_no, \ g_ril_request_id_to_string(gril, message->req)) #define g_ril_append_print_buf(gril, x...) do { \ if (gril && g_ril_get_trace(gril)) \ sprintf(print_buf, x); \ } while (0) #define g_ril_print_unsol(gril, message) \ G_RIL_TRACE(gril, "[%d,UNSOL]< %s %s", \ g_ril_get_slot(gril), \ g_ril_unsol_request_to_string(gril, \ message->req), \ print_buf) #define g_ril_print_unsol_no_args(gril, message) \ G_RIL_TRACE(gril, "[%d,UNSOL]< %s", g_ril_get_slot(gril), \ g_ril_unsol_request_to_string(gril, message->req)) void g_ril_init_parcel(const struct ril_msg *message, struct parcel *rilp); GRil *g_ril_new(const char *sock_path, enum ofono_ril_vendor vendor); GIOChannel *g_ril_get_channel(GRil *ril); GRilIO *g_ril_get_io(GRil *ril); GRil *g_ril_ref(GRil *ril); void g_ril_unref(GRil *ril); GRil *g_ril_clone(GRil *ril); void g_ril_set_disconnect_function(GRil *ril, GRilDisconnectFunc disconnect, gpointer user_data); gboolean g_ril_get_trace(GRil *ril); gboolean g_ril_set_trace(GRil *ril, gboolean trace); int g_ril_get_slot(GRil *ril); gboolean g_ril_set_slot(GRil *ril, int slot); int g_ril_get_version(GRil *ril); gboolean g_ril_set_version(GRil *ril, int version); /*! * If the function is not NULL, then on every read/write from the GIOChannel * provided to GRil the logging function will be called with the * input/output string and user data */ gboolean g_ril_set_debugf(GRil *ril, GRilDebugFunc func, gpointer user_data); gboolean g_ril_set_vendor_print_msg_id_funcs(GRil *ril, GRilMsgIdToStrFunc req_to_string, GRilMsgIdToStrFunc unsol_to_string); /*! * Queue an RIL request for execution. The request contents are given * in data. Once the command executes, the callback function given by * func is called with user provided data in user_data. * * Returns an id of the queued command which can be canceled using * g_ril_cancel. If an error occurred, an id of 0 is returned. * */ gint g_ril_send(GRil *ril, const gint reqid, struct parcel *rilp, GRilResponseFunc func, gpointer user_data, GDestroyNotify notify); guint g_ril_register(GRil *ril, const int req, GRilNotifyFunc func, gpointer user_data); gboolean g_ril_unregister(GRil *ril, guint id); gboolean g_ril_unregister_all(GRil *ril); enum ofono_ril_vendor g_ril_vendor(GRil *ril); const char *g_ril_request_id_to_string(GRil *ril, int req); const char *g_ril_unsol_request_to_string(GRil *ril, int req); #ifdef __cplusplus } #endif #endif /* __GRIL_H */ ofono-1.17.bzr6912+16.04.20160314.3/gril/grilio.h0000644000015600001650000000377712671500024021010 0ustar pbuserpbgroup00000000000000/* * * RIL chat library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical 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. * * 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 * */ #ifndef __GRILIO_H #define __GRILIO_H #ifdef __cplusplus extern "C" { #endif #include "gfunc.h" #define GRIL_BUFFER_SIZE 8192 struct _GRilIO; typedef struct _GRilIO GRilIO; struct ring_buffer; typedef void (*GRilIOReadFunc)(struct ring_buffer *buffer, gpointer user_data); typedef gboolean (*GRilIOWriteFunc)(gpointer user_data); GRilIO *g_ril_io_new(GIOChannel *channel); GRilIO *g_ril_io_new_blocking(GIOChannel *channel); GIOChannel *g_ril_io_get_channel(GRilIO *io); GRilIO *g_ril_io_ref(GRilIO *io); void g_ril_io_unref(GRilIO *io); gboolean g_ril_io_set_read_handler(GRilIO *io, GRilIOReadFunc read_handler, gpointer user_data); gboolean g_ril_io_set_write_handler(GRilIO *io, GRilIOWriteFunc write_handler, gpointer user_data); void g_ril_io_set_write_done(GRilIO *io, GRilDisconnectFunc func, gpointer user_data); void g_ril_io_drain_ring_buffer(GRilIO *io, guint len); gsize g_ril_io_write(GRilIO *io, const gchar *data, gsize count); gboolean g_ril_io_set_disconnect_function(GRilIO *io, GRilDisconnectFunc disconnect, gpointer user_data); gboolean g_ril_io_set_debug(GRilIO *io, GRilDebugFunc func, gpointer user_data); #ifdef __cplusplus } #endif #endif /* __GRILIO_H */ ofono-1.17.bzr6912+16.04.20160314.3/gril/gril.c0000644000015600001650000006442712671500073020456 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2013 Canonical Ltd. * Copyright (C) 2013 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. * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "ringbuffer.h" #include "gril.h" #include "grilutil.h" #define RIL_TRACE(ril, fmt, arg...) do { \ if (ril->trace == TRUE) \ ofono_debug(fmt, ## arg); \ } while (0) #define COMMAND_FLAG_EXPECT_PDU 0x1 #define COMMAND_FLAG_EXPECT_SHORT_PROMPT 0x2 #define RADIO_GID 1001 #define RADIO_UID 1001 struct ril_request { gchar *data; guint data_len; gint req; gint id; guint gid; GRilResponseFunc callback; gpointer user_data; GDestroyNotify notify; }; struct ril_notify_node { guint id; guint gid; GRilNotifyFunc callback; gpointer user_data; gboolean destroyed; }; typedef gboolean (*node_remove_func)(struct ril_notify_node *node, gpointer user_data); struct ril_notify { GSList *nodes; }; struct ril_s { gint ref_count; /* Ref count */ gint next_cmd_id; /* Next command id */ guint next_notify_id; /* Next notify id */ guint next_gid; /* Next group id */ GRilIO *io; /* GRil IO */ GQueue *command_queue; /* Command queue */ GQueue *out_queue; /* Commands sent/been sent */ guint req_bytes_written; /* bytes written from req */ GHashTable *notify_list; /* List of notification reg */ GRilDisconnectFunc user_disconnect; /* user disconnect func */ gpointer user_disconnect_data; /* user disconnect data */ guint read_so_far; /* Number of bytes processed */ gboolean suspended; /* Are we suspended? */ gboolean debug; gboolean trace; gint timeout_source; gboolean destroyed; /* Re-entrancy guard */ gboolean in_read_handler; /* Re-entrancy guard */ gboolean in_notify; enum ofono_ril_vendor vendor; int slot; GRilMsgIdToStrFunc req_to_string; GRilMsgIdToStrFunc unsol_to_string; int version; }; struct _GRil { gint ref_count; struct ril_s *parent; guint group; }; struct req_hdr { /* Warning: length is stored in network order */ uint32_t length; uint32_t reqid; uint32_t serial; }; #define RIL_PRINT_BUF_SIZE 8096 char print_buf[RIL_PRINT_BUF_SIZE] __attribute__((used)); static void ril_wakeup_writer(struct ril_s *ril); static const char *request_id_to_string(struct ril_s *ril, int req) { const char *str = NULL; if (ril->req_to_string) str = ril->req_to_string(req); if (str == NULL) str = ril_request_id_to_string(req); return str; } static const char *unsol_request_to_string(struct ril_s *ril, int req) { const char *str = NULL; if (ril->unsol_to_string) str = ril->unsol_to_string(req); if (str == NULL) str = ril_unsol_request_to_string(req); return str; } static void ril_notify_node_destroy(gpointer data, gpointer user_data) { struct ril_notify_node *node = data; g_free(node); } static void ril_notify_destroy(gpointer user_data) { struct ril_notify *notify = user_data; g_slist_foreach(notify->nodes, ril_notify_node_destroy, NULL); g_slist_free(notify->nodes); g_free(notify); } static gint ril_notify_node_compare_by_id(gconstpointer a, gconstpointer b) { const struct ril_notify_node *node = a; guint id = GPOINTER_TO_UINT(b); if (node->id < id) return -1; if (node->id > id) return 1; return 0; } static gboolean ril_unregister_all(struct ril_s *ril, gboolean mark_only, node_remove_func func, gpointer userdata) { GHashTableIter iter; struct ril_notify *notify; struct ril_notify_node *node; gpointer key, value; GSList *p; GSList *c; GSList *t; if (ril->notify_list == NULL) return FALSE; g_hash_table_iter_init(&iter, ril->notify_list); while (g_hash_table_iter_next(&iter, &key, &value)) { notify = value; p = NULL; c = notify->nodes; while (c) { node = c->data; if (func(node, userdata) != TRUE) { p = c; c = c->next; continue; } if (mark_only) { node->destroyed = TRUE; p = c; c = c->next; continue; } if (p) p->next = c->next; else notify->nodes = c->next; ril_notify_node_destroy(node, NULL); t = c; c = c->next; g_slist_free_1(t); } if (notify->nodes == NULL) g_hash_table_iter_remove(&iter); } return TRUE; } /* * This function creates a RIL request. For a good reference on * the layout of RIL requests, responses, and unsolicited requests * see: * * https://wiki.mozilla.org/B2G/RIL */ static struct ril_request *ril_request_create(struct ril_s *ril, guint gid, const gint req, const gint id, struct parcel *rilp, GRilResponseFunc func, gpointer user_data, GDestroyNotify notify, gboolean wakeup) { struct ril_request *r; struct req_hdr header; guint data_len = 0; if (rilp != NULL) data_len = rilp->size; r = g_try_new0(struct ril_request, 1); if (r == NULL) { ofono_error("%s Out of memory", __func__); return NULL; } /* Full request size: header size plus buffer length */ r->data_len = data_len + sizeof(header); r->data = g_try_new(char, r->data_len); if (r->data == NULL) { ofono_error("ril_request: can't allocate new request."); g_free(r); return NULL; } /* Length does not include the length field. Network order. */ header.length = htonl(r->data_len - sizeof(header.length)); header.reqid = req; header.serial = id; /* copy header */ memcpy(r->data, &header, sizeof(header)); /* copy request data */ if (data_len) memcpy(r->data + sizeof(header), rilp->data, data_len); r->req = req; r->gid = gid; r->id = id; r->callback = func; r->user_data = user_data; r->notify = notify; return r; } static void ril_request_destroy(struct ril_request *req) { if (req->notify) req->notify(req->user_data); g_free(req->data); g_free(req); } static void ril_cleanup(struct ril_s *p) { /* Cleanup pending commands */ if (p->command_queue) { g_queue_free(p->command_queue); p->command_queue = NULL; } if (p->out_queue) { g_queue_free(p->out_queue); p->out_queue = NULL; } /* Cleanup registered notifications */ if (p->notify_list) { g_hash_table_destroy(p->notify_list); p->notify_list = NULL; } if (p->timeout_source) { g_source_remove(p->timeout_source); p->timeout_source = 0; } } void g_ril_set_disconnect_function(GRil *ril, GRilDisconnectFunc disconnect, gpointer user_data) { ril->parent->user_disconnect = disconnect; ril->parent->user_disconnect_data = user_data; } static void io_disconnect(gpointer user_data) { struct ril_s *ril = user_data; ofono_error("%s: disconnected from rild", __func__); ril_cleanup(ril); g_ril_io_unref(ril->io); ril->io = NULL; if (ril->user_disconnect) ril->user_disconnect(ril->user_disconnect_data); } static void handle_response(struct ril_s *p, struct ril_msg *message) { guint count = g_queue_get_length(p->command_queue); struct ril_request *req; gboolean found = FALSE; guint i, len; gint id; for (i = 0; i < count; i++) { req = g_queue_peek_nth(p->command_queue, i); if (req->id == message->serial_no) { found = TRUE; message->req = req->req; if (message->error != RIL_E_SUCCESS) RIL_TRACE(p, "[%d,%04d]< %s failed %s", p->slot, message->serial_no, request_id_to_string(p, message->req), ril_error_to_string(message->error)); req = g_queue_pop_nth(p->command_queue, i); if (req->callback) req->callback(message, req->user_data); len = g_queue_get_length(p->out_queue); for (i = 0; i < len; i++) { id = GPOINTER_TO_INT(g_queue_peek_nth( p->out_queue, i)); if (id == req->id) { g_queue_pop_nth(p->out_queue, i); break; } } ril_request_destroy(req); if (g_queue_peek_head(p->command_queue)) ril_wakeup_writer(p); break; } } if (found == FALSE) ofono_error("No matching request for reply: %s serial_no: %d!", request_id_to_string(p, message->req), message->serial_no); } static gboolean node_check_destroyed(struct ril_notify_node *node, gpointer userdata) { gboolean val = GPOINTER_TO_UINT(userdata); if (node->destroyed == val) return TRUE; return FALSE; } static void handle_unsol_req(struct ril_s *p, struct ril_msg *message) { struct ril_notify *notify; if (p->notify_list == NULL) return; p->in_notify = TRUE; notify = g_hash_table_lookup(p->notify_list, &message->req); if (notify != NULL) { GSList *list_item; for (list_item = notify->nodes; list_item; list_item = g_slist_next(list_item)) { struct ril_notify_node *node = list_item->data; if (node->destroyed) continue; node->callback(message, node->user_data); } } else { /* Only log events not being listended for... */ DBG("RIL Event slot %d: %s\n", p->slot, unsol_request_to_string(p, message->req)); } p->in_notify = FALSE; /* Now destroy nodes possibly removed by callbacks */ if (notify != NULL) ril_unregister_all(p, FALSE, node_check_destroyed, GUINT_TO_POINTER(TRUE)); } static void dispatch(struct ril_s *p, struct ril_msg *message) { int32_t *unsolicited_field, *id_num_field; gchar *bufp = message->buf; gchar *datap; gsize data_len; /* This could be done with a struct/union... */ unsolicited_field = (int32_t *) (void *) bufp; if (*unsolicited_field) message->unsolicited = TRUE; else message->unsolicited = FALSE; bufp += 4; id_num_field = (int32_t *) (void *) bufp; if (message->unsolicited) { message->req = (int) *id_num_field; /* * A RIL Unsolicited Event is two UINT32 fields ( unsolicited, * and req/ev ), so subtract the length of the header from the * overall length to calculate the length of the Event Data. */ data_len = message->buf_len - 8; } else { message->serial_no = (int) *id_num_field; bufp += 4; message->error = *((int32_t *) (void *) bufp); /* * A RIL Solicited Response is three UINT32 fields ( unsolicied, * serial_no and error ), so subtract the length of the header * from the overall length to calculate the length of the Event * Data. */ data_len = message->buf_len - 12; } /* advance to start of data.. */ bufp += 4; /* * Now, use buffer for event data if present */ if (data_len) { datap = g_try_malloc(data_len); if (datap == NULL) goto error; /* Copy event bytes from message->buf into new buffer */ memcpy(datap, bufp, data_len); /* Free buffer that includes header */ g_free(message->buf); /* ...and replace with new buffer */ message->buf = datap; message->buf_len = data_len; } else { /* Free buffer that includes header */ g_free(message->buf); /* To know if there was no data when parsing */ message->buf = NULL; message->buf_len = 0; } if (message->unsolicited == TRUE) handle_unsol_req(p, message); else handle_response(p, message); error: g_free(message->buf); g_free(message); } static struct ril_msg *read_fixed_record(struct ril_s *p, const guchar *bytes, gsize *len) { struct ril_msg *message; unsigned message_len, plen; /* First four bytes are length in TCP byte order (Big Endian) */ plen = ntohl(*((uint32_t *) (void *) bytes)); bytes += 4; /* * TODO: Verify that 8k is the max message size from rild. * * This condition shouldn't happen. If it does * there are three options: * * 1) Exit; ofono will restart via DBus (this is what we do now) * 2) Consume the bytes & continue * 3) force a disconnect */ if (plen > GRIL_BUFFER_SIZE - 4) { ofono_error("ERROR RIL parcel bigger than buffer (%u), exiting", plen); exit(1); } /* * If we don't have the whole fixed record in the ringbuffer * then return NULL & leave ringbuffer as is. */ message_len = *len - 4; if (message_len < plen) return NULL; message = g_malloc(sizeof(struct ril_msg)); /* allocate ril_msg->buffer */ message->buf_len = plen; message->buf = g_malloc(plen); /* Copy bytes into message buffer */ memmove(message->buf, (const void *) bytes, plen); /* Indicate to caller size of record we extracted */ *len = plen + 4; return message; } static void new_bytes(struct ring_buffer *rbuf, gpointer user_data) { struct ril_msg *message; struct ril_s *p = user_data; unsigned int len = ring_buffer_len(rbuf); unsigned int wrap = ring_buffer_len_no_wrap(rbuf); guchar *buf = ring_buffer_read_ptr(rbuf, p->read_so_far); p->in_read_handler = TRUE; while (p->suspended == FALSE && (p->read_so_far < len)) { gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far); if (rbytes < 4) { DBG("Not enough bytes for header length: len: %d", len); return; } /* * This function attempts to read the next full length * fixed message from the stream. if not all bytes are * available, it returns NULL. otherwise it allocates * and returns a ril_message with the copied bytes, and * drains those bytes from the ring_buffer */ message = read_fixed_record(p, buf, &rbytes); /* wait for the rest of the record... */ if (message == NULL) break; buf += rbytes; p->read_so_far += rbytes; /* TODO: need to better understand how wrap works! */ if (p->read_so_far == wrap) { buf = ring_buffer_read_ptr(rbuf, p->read_so_far); wrap = len; } dispatch(p, message); ring_buffer_drain(rbuf, p->read_so_far); len -= p->read_so_far; wrap -= p->read_so_far; p->read_so_far = 0; } p->in_read_handler = FALSE; if (p->destroyed) g_free(p); } /* * This function is a GIOFunc and may be called directly or via an IO watch. * The return value controls whether the watch stays active ( TRUE ), or is * removed ( FALSE ). */ static gboolean can_write_data(gpointer data) { struct ril_s *ril = data; struct ril_request *req; gsize bytes_written, towrite, len; guint qlen, oqlen; gint id; gboolean written = TRUE; guint i, j; qlen = g_queue_get_length(ril->command_queue); if (qlen < 1) return FALSE; /* if the whole request was not written */ if (ril->req_bytes_written != 0) { for (i = 0; i < qlen; i++) { req = g_queue_peek_nth(ril->command_queue, i); if (req) { id = GPOINTER_TO_INT(g_queue_peek_head( ril->out_queue)); if (req->id == id) goto out; } else { return FALSE; } } } /* if no requests already sent */ oqlen = g_queue_get_length(ril->out_queue); if (oqlen < 1) { req = g_queue_peek_head(ril->command_queue); if (req == NULL) return FALSE; g_queue_push_head(ril->out_queue, GINT_TO_POINTER(req->id)); goto out; } for (i = 0; i < qlen; i++) { req = g_queue_peek_nth(ril->command_queue, i); if (req == NULL) return FALSE; for (j = 0; j < oqlen; j++) { id = GPOINTER_TO_INT( g_queue_peek_nth(ril->out_queue, j)); if (req->id == id) { written = TRUE; break; } else { written = FALSE; } } if (written == FALSE) break; } /* watcher fired though requests already written */ if (written == TRUE) return FALSE; g_queue_push_head(ril->out_queue, GINT_TO_POINTER(req->id)); out: len = req->data_len; towrite = len - ril->req_bytes_written; #ifdef WRITE_SCHEDULER_DEBUG if (towrite > 5) towrite = 5; #endif bytes_written = g_ril_io_write(ril->io, req->data + ril->req_bytes_written, towrite); if (bytes_written == 0) return FALSE; ril->req_bytes_written += bytes_written; if (bytes_written < towrite) return TRUE; else ril->req_bytes_written = 0; return FALSE; } static void ril_wakeup_writer(struct ril_s *ril) { g_ril_io_set_write_handler(ril->io, can_write_data, ril); } static void ril_suspend(struct ril_s *ril) { ril->suspended = TRUE; g_ril_io_set_write_handler(ril->io, NULL, NULL); g_ril_io_set_read_handler(ril->io, NULL, NULL); g_ril_io_set_debug(ril->io, NULL, NULL); } static gboolean ril_set_debug(struct ril_s *ril, GRilDebugFunc func, gpointer user_data) { if (ril->io == NULL) return FALSE; g_ril_io_set_debug(ril->io, func, user_data); return TRUE; } static void ril_unref(struct ril_s *ril) { gboolean is_zero; is_zero = g_atomic_int_dec_and_test(&ril->ref_count); if (is_zero == FALSE) return; if (ril->io) { ril_suspend(ril); g_ril_io_unref(ril->io); ril->io = NULL; ril_cleanup(ril); } if (ril->in_read_handler) ril->destroyed = TRUE; else g_free(ril); } static gboolean node_compare_by_group(struct ril_notify_node *node, gpointer userdata) { guint group = GPOINTER_TO_UINT(userdata); if (node->gid == group) return TRUE; return FALSE; } static void set_process_id(gid_t gid, uid_t uid) { if (setegid(gid) < 0) ofono_error("%s: setegid(%d) failed: %s (%d)", __func__, gid, strerror(errno), errno); if (seteuid(uid) < 0) ofono_error("%s: seteuid(%d) failed: %s (%d)", __func__, uid, strerror(errno), errno); } static struct ril_s *create_ril(const char *sock_path) { struct ril_s *ril; struct sockaddr_un addr; int sk; GIOChannel *io; ril = g_try_new0(struct ril_s, 1); if (ril == NULL) return ril; ril->ref_count = 1; ril->next_cmd_id = 1; ril->next_notify_id = 1; ril->next_gid = 0; ril->req_bytes_written = 0; ril->trace = FALSE; /* sock_path is allowed to be NULL for unit tests */ if (sock_path == NULL) return ril; sk = socket(AF_UNIX, SOCK_STREAM, 0); if (sk < 0) { ofono_error("create_ril: can't create unix socket: %s (%d)\n", strerror(errno), errno); goto error; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1); /* RIL expects user radio to connect to the socket */ set_process_id(RADIO_GID, RADIO_UID); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { ofono_error("create_ril: can't connect to RILD: %s (%d)\n", strerror(errno), errno); /* Switch back to root */ set_process_id(0, 0); goto error; } /* Switch back to root */ set_process_id(0, 0); io = g_io_channel_unix_new(sk); if (io == NULL) { ofono_error("create_ril: can't open RILD io channel: %s (%d)\n", strerror(errno), errno); goto error; } g_io_channel_set_close_on_unref(io, TRUE); g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); ril->io = g_ril_io_new(io); g_io_channel_unref(io); if (ril->io == NULL) { ofono_error("create_ril: can't create ril->io"); goto error; } g_ril_io_set_disconnect_function(ril->io, io_disconnect, ril); ril->command_queue = g_queue_new(); if (ril->command_queue == NULL) { ofono_error("create_ril: Couldn't create command_queue."); goto error; } ril->out_queue = g_queue_new(); if (ril->out_queue == NULL) { ofono_error("create_ril: Couldn't create out_queue."); goto error; } ril->notify_list = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, ril_notify_destroy); g_ril_io_set_read_handler(ril->io, new_bytes, ril); return ril; error: ril_unref(ril); return NULL; } static struct ril_notify *ril_notify_create(struct ril_s *ril, const int req) { struct ril_notify *notify; int *key; notify = g_try_new0(struct ril_notify, 1); if (notify == NULL) return 0; key = g_try_new0(int, 1); if (key == NULL) return 0; *key = req; g_hash_table_insert(ril->notify_list, key, notify); return notify; } static void ril_cancel_group(struct ril_s *ril, guint group) { int n = 0; guint len, i; struct ril_request *req; gboolean sent; if (ril->command_queue == NULL) return; while ((req = g_queue_peek_nth(ril->command_queue, n)) != NULL) { if (req->id == 0 || req->gid != group) { n += 1; continue; } req->callback = NULL; sent = FALSE; len = g_queue_get_length(ril->out_queue); for (i = 0; i < len; i++) { if (GPOINTER_TO_INT( g_queue_peek_nth(ril->out_queue, i)) == req->id) { n += 1; sent = TRUE; break; } } if (sent) continue; g_queue_remove(ril->command_queue, req); ril_request_destroy(req); } } static guint ril_register(struct ril_s *ril, guint group, const int req, GRilNotifyFunc func, gpointer user_data) { struct ril_notify *notify; struct ril_notify_node *node; if (ril->notify_list == NULL) return 0; if (func == NULL) return 0; notify = g_hash_table_lookup(ril->notify_list, &req); if (notify == NULL) notify = ril_notify_create(ril, req); if (notify == NULL) return 0; node = g_try_new0(struct ril_notify_node, 1); if (node == NULL) return 0; node->id = ril->next_notify_id++; node->gid = group; node->callback = func; node->user_data = user_data; notify->nodes = g_slist_prepend(notify->nodes, node); return node->id; } static gboolean ril_unregister(struct ril_s *ril, gboolean mark_only, guint group, guint id) { GHashTableIter iter; struct ril_notify *notify; struct ril_notify_node *node; gpointer key, value; GSList *l; if (ril->notify_list == NULL) return FALSE; g_hash_table_iter_init(&iter, ril->notify_list); while (g_hash_table_iter_next(&iter, &key, &value)) { notify = value; l = g_slist_find_custom(notify->nodes, GUINT_TO_POINTER(id), ril_notify_node_compare_by_id); if (l == NULL) continue; node = l->data; if (node->gid != group) return FALSE; if (mark_only) { node->destroyed = TRUE; return TRUE; } ril_notify_node_destroy(node, NULL); notify->nodes = g_slist_remove(notify->nodes, node); if (notify->nodes == NULL) g_hash_table_iter_remove(&iter); return TRUE; } return FALSE; } void g_ril_init_parcel(const struct ril_msg *message, struct parcel *rilp) { /* Set up Parcel struct for proper parsing */ rilp->data = message->buf; rilp->size = message->buf_len; rilp->capacity = message->buf_len; rilp->offset = 0; rilp->malformed = 0; } GRil *g_ril_new(const char *sock_path, enum ofono_ril_vendor vendor) { GRil *ril; ril = g_try_new0(GRil, 1); if (ril == NULL) return NULL; ril->parent = create_ril(sock_path); if (ril->parent == NULL) { g_free(ril); return NULL; } ril->group = ril->parent->next_gid++; ril->ref_count = 1; ril->parent->vendor = vendor; ril->parent->version = RIL_VERSION_UNSPECIFIED; return ril; } GRil *g_ril_clone(GRil *clone) { GRil *ril; if (clone == NULL) return NULL; ril = g_try_new0(GRil, 1); if (ril == NULL) return NULL; ril->parent = clone->parent; ril->group = ril->parent->next_gid++; ril->ref_count = 1; g_atomic_int_inc(&ril->parent->ref_count); return ril; } GIOChannel *g_ril_get_channel(GRil *ril) { if (ril == NULL || ril->parent->io == NULL) return NULL; return g_ril_io_get_channel(ril->parent->io); } GRilIO *g_ril_get_io(GRil *ril) { if (ril == NULL) return NULL; return ril->parent->io; } GRil *g_ril_ref(GRil *ril) { if (ril == NULL) return NULL; g_atomic_int_inc(&ril->ref_count); return ril; } gint g_ril_send(GRil *ril, const gint reqid, struct parcel *rilp, GRilResponseFunc func, gpointer user_data, GDestroyNotify notify) { struct ril_request *r; struct ril_s *p; if (ril == NULL || ril->parent == NULL || ril->parent->command_queue == NULL) return 0; p = ril->parent; r = ril_request_create(p, ril->group, reqid, p->next_cmd_id, rilp, func, user_data, notify, FALSE); if (rilp != NULL) parcel_free(rilp); if (r == NULL) return 0; p->next_cmd_id++; g_queue_push_tail(p->command_queue, r); ril_wakeup_writer(p); if (rilp == NULL) g_ril_print_request_no_args(ril, r->id, reqid); else g_ril_print_request(ril, r->id, reqid); return r->id; } void g_ril_unref(GRil *ril) { gboolean is_zero; if (ril == NULL) return; is_zero = g_atomic_int_dec_and_test(&ril->ref_count); if (is_zero == FALSE) return; ril_cancel_group(ril->parent, ril->group); g_ril_unregister_all(ril); ril_unref(ril->parent); g_free(ril); } gboolean g_ril_get_trace(GRil *ril) { if (ril == NULL || ril->parent == NULL) return FALSE; return ril->parent->trace; } gboolean g_ril_set_trace(GRil *ril, gboolean trace) { if (ril == NULL || ril->parent == NULL) return FALSE; return ril->parent->trace = trace; } gboolean g_ril_set_slot(GRil *ril, int slot) { if (ril == NULL || ril->parent == NULL) return FALSE; ril->parent->slot = slot; return TRUE; } int g_ril_get_slot(GRil *ril) { if (ril == NULL) return 0; return ril->parent->slot; } gboolean g_ril_set_version(GRil *ril, int version) { if (ril == NULL || ril->parent == NULL || ril->parent->version != RIL_VERSION_UNSPECIFIED) return FALSE; ril->parent->version = version; return TRUE; } int g_ril_get_version(GRil *ril) { if (ril == NULL) return RIL_VERSION_UNSPECIFIED; return ril->parent->version; } gboolean g_ril_set_debugf(GRil *ril, GRilDebugFunc func, gpointer user_data) { if (ril == NULL || ril->group != 0) return FALSE; return ril_set_debug(ril->parent, func, user_data); } gboolean g_ril_set_vendor_print_msg_id_funcs(GRil *ril, GRilMsgIdToStrFunc req_to_string, GRilMsgIdToStrFunc unsol_to_string) { if (ril == NULL || ril->parent == NULL) return FALSE; ril->parent->req_to_string = req_to_string; ril->parent->unsol_to_string = unsol_to_string; return TRUE; } guint g_ril_register(GRil *ril, const int req, GRilNotifyFunc func, gpointer user_data) { if (ril == NULL) return 0; return ril_register(ril->parent, ril->group, req, func, user_data); } gboolean g_ril_unregister(GRil *ril, guint id) { if (ril == NULL) return FALSE; return ril_unregister(ril->parent, ril->parent->in_notify, ril->group, id); } gboolean g_ril_unregister_all(GRil *ril) { if (ril == NULL) return FALSE; return ril_unregister_all(ril->parent, ril->parent->in_notify, node_compare_by_group, GUINT_TO_POINTER(ril->group)); } enum ofono_ril_vendor g_ril_vendor(GRil *ril) { if (ril == NULL) return OFONO_RIL_VENDOR_AOSP; return ril->parent->vendor; } const char *g_ril_request_id_to_string(GRil *ril, int req) { return request_id_to_string(ril->parent, req); } const char *g_ril_unsol_request_to_string(GRil *ril, int req) { return unsol_request_to_string(ril->parent, req); } ofono-1.17.bzr6912+16.04.20160314.3/gril/grilreply.h0000644000015600001650000001115412671500024021520 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2014 Canonical 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. * * 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 * */ #ifndef __GRILREPLY_H #define __GRILREPLY_H #include #include #include "gril.h" #ifdef __cplusplus extern "C" { #endif struct reply_operator { char *lalpha; char *salpha; char *numeric; char *status; int tech; }; struct reply_avail_ops { guint num_ops; GSList *list; }; struct reply_reg_state { int status; int lac; int ci; int tech; }; struct reply_data_reg_state { struct reply_reg_state reg_state; unsigned int max_cids; }; struct reply_sim_io { int sw1; int sw2; int hex_len; unsigned char *hex_response; }; #define MAX_UICC_APPS 16 struct reply_sim_app { guint app_type; guint app_state; guint perso_substate; char *aid_str; char *app_str; guint pin_replaced; guint pin1_state; guint pin2_state; }; struct reply_sim_status { guint card_state; guint pin_state; guint gsm_umts_index; guint cdma_index; guint ims_index; guint num_apps; struct reply_sim_app *apps[MAX_UICC_APPS]; }; struct reply_clir { int status; int provisioned; }; struct reply_oem_hook { int length; void *data; }; void g_ril_reply_free_avail_ops(struct reply_avail_ops *reply); struct reply_avail_ops *g_ril_reply_parse_avail_ops(GRil *gril, const struct ril_msg *message); void g_ril_reply_free_operator(struct reply_operator *reply); struct reply_operator *g_ril_reply_parse_operator(GRil *gril, const struct ril_msg *message); void g_ril_reply_free_sim_io(struct reply_sim_io *reply); struct reply_sim_io *g_ril_reply_parse_sim_io(GRil *gril, const struct ril_msg *message); gchar *g_ril_reply_parse_imsi(GRil *gril, const struct ril_msg *message); struct reply_reg_state *g_ril_reply_parse_voice_reg_state(GRil *gril, const struct ril_msg *message); struct reply_data_reg_state *g_ril_reply_parse_data_reg_state(GRil *gril, const struct ril_msg *message); void g_ril_reply_free_sim_status(struct reply_sim_status *status); struct reply_sim_status *g_ril_reply_parse_sim_status(GRil *gril, const struct ril_msg *message); struct ofono_phone_number *g_ril_reply_parse_get_smsc_address( GRil *gril, const struct ril_msg *message); int g_ril_reply_parse_sms_response(GRil *gril, const struct ril_msg *message); GSList *g_ril_reply_parse_get_calls(GRil *gril, const struct ril_msg *message); enum ofono_disconnect_reason g_ril_reply_parse_call_fail_cause( GRil *gril, const struct ril_msg *message); int g_ril_reply_parse_get_mute(GRil *gril, const struct ril_msg *message); char *g_ril_reply_parse_baseband_version(GRil *gril, const struct ril_msg *message); char *g_ril_reply_parse_get_imei(GRil *gril, const struct ril_msg *message); int g_ril_reply_parse_query_call_waiting(GRil *gril, const struct ril_msg *message); int g_ril_reply_parse_query_clip(GRil *gril, const struct ril_msg *message); void g_ril_reply_free_get_clir(struct reply_clir *rclir); struct reply_clir *g_ril_reply_parse_get_clir(GRil *gril, const struct ril_msg *message); struct ofono_call_forwarding_condition *g_ril_reply_parse_query_call_fwd(GRil *gril, const struct ril_msg *message, unsigned int *list_size); int g_ril_reply_parse_get_preferred_network_type(GRil *gril, const struct ril_msg *message); int g_ril_reply_parse_query_facility_lock(GRil *gril, const struct ril_msg *message); int g_ril_reply_parse_set_facility_lock(GRil *gril, const struct ril_msg *message); int *g_ril_reply_parse_retries(GRil *gril, const struct ril_msg *message, enum ofono_sim_password_type passwd_type); void g_ril_reply_free_oem_hook(struct reply_oem_hook *oem_hook); struct reply_oem_hook *g_ril_reply_oem_hook_raw(GRil *gril, const struct ril_msg *message); struct parcel_str_array *g_ril_reply_oem_hook_strings(GRil *gril, const struct ril_msg *message); #ifdef __cplusplus } #endif #endif /* __GRILREPLY_H */ ofono-1.17.bzr6912+16.04.20160314.3/gril/gfunc.h0000644000015600001650000000234212671500024020610 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical 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. * * 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 * */ #ifndef __GFUNC_H #define __GFUNC_H #include #ifdef __cplusplus extern "C" { #endif typedef void (*GRilDisconnectFunc)(gpointer user_data); typedef void (*GRilReceiveFunc)(const unsigned char *data, gsize size, gpointer user_data); typedef void (*GRilDebugFunc)(const char *str, gpointer user_data); typedef void (*GRilSuspendFunc)(gpointer user_data); #ifdef __cplusplus } #endif #endif /* __GFUNC_H */ ofono-1.17.bzr6912+16.04.20160314.3/gril/parcel.c0000644000015600001650000001334712671500024020756 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Joel Armstrong * Copyright (C) 2012 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License (`GPL') as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Based on parcel implementation from https://bitbucket.org/floren/inferno * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include /* Parcel-handling code */ #include #include #include #include #include #include "parcel.h" #define PAD_SIZE(s) (((s)+3)&~3) typedef uint16_t char16_t; void parcel_init(struct parcel *p) { p->data = g_malloc0(sizeof(int32_t)); p->size = 0; p->capacity = sizeof(int32_t); p->offset = 0; p->malformed = 0; } void parcel_grow(struct parcel *p, size_t size) { char *new = g_realloc(p->data, p->capacity + size); p->data = new; p->capacity += size; } void parcel_free(struct parcel *p) { g_free(p->data); p->size = 0; p->capacity = 0; p->offset = 0; } int32_t parcel_r_int32(struct parcel *p) { int32_t ret; if (p->malformed) return 0; if (p->offset + sizeof(int32_t) > p->size) { ofono_error("%s: parcel is too small", __func__); p->malformed = 1; return 0; } ret = *((int32_t *) (void *) (p->data + p->offset)); p->offset += sizeof(int32_t); return ret; } int parcel_w_int32(struct parcel *p, int32_t val) { for (;;) { if (p->offset + sizeof(int32_t) < p->capacity) { /* There's enough space */ *((int32_t *) (void *) (p->data + p->offset)) = val; p->offset += sizeof(int32_t); p->size += sizeof(int32_t); break; } else { /* Grow data and retry */ parcel_grow(p, sizeof(int32_t)); } } return 0; } int parcel_w_string(struct parcel *p, const char *str) { gunichar2 *gs16; glong gs16_len; size_t len; size_t gs16_size; if (str == NULL) { parcel_w_int32(p, -1); return 0; } gs16 = g_utf8_to_utf16(str, -1, NULL, &gs16_len, NULL); if (parcel_w_int32(p, gs16_len) == -1) return -1; gs16_size = gs16_len * sizeof(char16_t); len = gs16_size + sizeof(char16_t); for (;;) { size_t padded = PAD_SIZE(len); if (p->offset + len < p->capacity) { /* There's enough space */ memcpy(p->data + p->offset, gs16, gs16_size); *((char16_t *) (void *) (p->data + p->offset + gs16_size)) = 0; p->offset += padded; p->size += padded; if (padded != len) { #if BYTE_ORDER == BIG_ENDIAN static const uint32_t mask[4] = { 0x00000000, 0xffffff00, 0xffff0000, 0xff000000 }; #endif #if BYTE_ORDER == LITTLE_ENDIAN static const uint32_t mask[4] = { 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff }; #endif *((uint32_t *) (void *) (p->data + p->offset - 4)) &= mask[padded - len]; } break; } else { /* Grow data and retry */ parcel_grow(p, padded); } } g_free(gs16); return 0; } char *parcel_r_string(struct parcel *p) { char *ret; int len16 = parcel_r_int32(p); int strbytes; if (p->malformed) return NULL; /* This is how a null string is sent */ if (len16 < 0) return NULL; strbytes = PAD_SIZE((len16 + 1) * sizeof(char16_t)); if (p->offset + strbytes > p->size) { ofono_error("%s: parcel is too small", __func__); p->malformed = 1; return NULL; } ret = g_utf16_to_utf8((gunichar2 *) (void *) (p->data + p->offset), len16, NULL, NULL, NULL); if (ret == NULL) { ofono_error("%s: wrong UTF16 coding", __func__); p->malformed = 1; return NULL; } p->offset += strbytes; return ret; } int parcel_w_raw(struct parcel *p, const void *data, size_t len) { if (data == NULL) { parcel_w_int32(p, -1); return 0; } parcel_w_int32(p, len); for (;;) { if (p->offset + len < p->capacity) { /* There's enough space */ memcpy(p->data + p->offset, data, len); p->offset += len; p->size += len; break; } else { /* Grow data and retry */ parcel_grow(p, len); } } return 0; } void *parcel_r_raw(struct parcel *p, int *len) { char *ret; *len = parcel_r_int32(p); if (p->malformed || *len <= 0) return NULL; if (p->offset + *len > p->size) { ofono_error("%s: parcel is too small", __func__); p->malformed = 1; return NULL; } ret = g_try_malloc0(*len); if (ret == NULL) { ofono_error("%s: out of memory (%d bytes)", __func__, *len); return NULL; } memcpy(ret, p->data + p->offset, *len); p->offset += *len; return ret; } size_t parcel_data_avail(struct parcel *p) { return p->size - p->offset; } struct parcel_str_array *parcel_r_str_array(struct parcel *p) { int i; struct parcel_str_array *str_arr; int num_str = parcel_r_int32(p); if (p->malformed || num_str <= 0) return NULL; str_arr = g_try_malloc0(sizeof(*str_arr) + num_str * sizeof(char *)); if (str_arr == NULL) return NULL; str_arr->num_str = num_str; for (i = 0; i < num_str; ++i) str_arr->str[i] = parcel_r_string(p); if (p->malformed) { parcel_free_str_array(str_arr); return NULL; } return str_arr; } void parcel_free_str_array(struct parcel_str_array *str_arr) { if (str_arr) { int i; for (i = 0; i < str_arr->num_str; ++i) g_free(str_arr->str[i]); g_free(str_arr); } } ofono-1.17.bzr6912+16.04.20160314.3/gril/grilunsol.h0000644000015600001650000000466012671500073021535 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2013 Canonical 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. * * 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 * */ #ifndef __GRILUNSOL_H #define __GRILUNSOL_H #include #include "gril.h" #ifdef __cplusplus extern "C" { #endif struct ril_data_call { guint status; gint cid; guint active; guint protocol; char *ifname; gchar *ip_addr; gchar **dns_addrs; gchar **gateways; }; struct ril_data_call_list { guint version; GSList *calls; }; struct unsol_sms_data { long length; unsigned char *data; }; struct unsol_supp_svc_notif { int notif_type; int code; int index; struct ofono_phone_number number; }; struct unsol_ussd { int type; char *message; }; int g_ril_unsol_parse_connected(GRil *gril, const struct ril_msg *message); void g_ril_unsol_free_data_call_list(struct ril_data_call_list *data_call_list); struct ril_data_call_list *g_ril_unsol_parse_data_call_list(GRil *gril, const struct ril_msg *message); char *g_ril_unsol_parse_nitz(GRil *gril, const struct ril_msg *message); void g_ril_unsol_free_sms_data(struct unsol_sms_data *unsol); struct unsol_sms_data *g_ril_unsol_parse_new_sms(GRil *gril, const struct ril_msg *message); int g_ril_unsol_parse_radio_state_changed(GRil *gril, const struct ril_msg *message); int g_ril_unsol_parse_signal_strength(GRil *gril, const struct ril_msg *message, int ril_tech); void g_ril_unsol_free_supp_svc_notif(struct unsol_supp_svc_notif *unsol); struct unsol_supp_svc_notif *g_ril_unsol_parse_supp_svc_notif(GRil *gril, struct ril_msg *message); void g_ril_unsol_free_ussd(struct unsol_ussd *unsol); struct unsol_ussd *g_ril_unsol_parse_ussd(GRil *gril, struct ril_msg *message); #ifdef __cplusplus } #endif #endif /* __GRILUNSOL_H */ ofono-1.17.bzr6912+16.04.20160314.3/gril/grilutil.c0000644000015600001650000005404212671500024021340 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include "grilutil.h" #include "ril_constants.h" /* Constants used by CALL_LIST, and SETUP_DATA_CALL RIL requests */ #define PROTO_IP_STR "IP" #define PROTO_IPV6_STR "IPV6" #define PROTO_IPV4V6_STR "IPV4V6" static char temp_str[32]; const char *ril_ofono_protocol_to_ril_string(guint protocol) { char *result; switch (protocol) { case OFONO_GPRS_PROTO_IPV6: result = PROTO_IPV6_STR; break; case OFONO_GPRS_PROTO_IPV4V6: result = PROTO_IPV4V6_STR; break; case OFONO_GPRS_PROTO_IP: result = PROTO_IP_STR; break; default: result = NULL; } return result; } int ril_protocol_string_to_ofono_protocol(gchar *protocol_str) { int result; if (g_strcmp0(protocol_str, PROTO_IPV6_STR) == 0) result = OFONO_GPRS_PROTO_IPV6; else if (g_strcmp0(protocol_str, PROTO_IPV4V6_STR) == 0) result = OFONO_GPRS_PROTO_IPV4V6; else if (g_strcmp0(protocol_str, PROTO_IP_STR) == 0) result = OFONO_GPRS_PROTO_IP; else result = -1; return result; } const char *ril_appstate_to_string(int app_state) { switch (app_state) { case RIL_APPSTATE_UNKNOWN: return "UNKNOWN"; case RIL_APPSTATE_DETECTED: return "DETECTED"; case RIL_APPSTATE_PIN: return "PIN"; case RIL_APPSTATE_PUK: return "PUK"; case RIL_APPSTATE_SUBSCRIPTION_PERSO: return ""; case RIL_APPSTATE_READY: return "READY"; default: return ""; } } const char *ril_apptype_to_string(int app_type) { switch (app_type) { case RIL_APPTYPE_UNKNOWN: return "UNKNOWN"; case RIL_APPTYPE_SIM: return "SIM"; case RIL_APPTYPE_USIM: return "USIM"; case RIL_APPTYPE_RUIM: return "RUIM"; case RIL_APPTYPE_CSIM: return "CSIM"; case RIL_APPTYPE_ISIM: return "ISIM"; default: return ""; } } const char *ril_authtype_to_string(int auth_type) { switch (auth_type) { case RIL_AUTH_NONE: return "NONE"; case RIL_AUTH_PAP: return "PAP"; case RIL_AUTH_CHAP: return "CHAP"; case RIL_AUTH_BOTH: return "BOTH"; case RIL_AUTH_ANY: return "ANY"; default: return ""; } } const char *ril_cardstate_to_string(int card_state) { switch (card_state) { case RIL_CARDSTATE_ABSENT: return "ABSENT"; case RIL_CARDSTATE_PRESENT: return "PRESENT"; case RIL_CARDSTATE_ERROR: return "ERROR"; default: return ""; } } const char *ril_error_to_string(int error) { switch (error) { case RIL_E_SUCCESS: return "SUCCESS"; case RIL_E_RADIO_NOT_AVAILABLE: return "RADIO_NOT_AVAILABLE"; case RIL_E_GENERIC_FAILURE: return "GENERIC_FAILURE"; case RIL_E_PASSWORD_INCORRECT: return "PASSWORD_INCORRECT"; case RIL_E_SIM_PIN2: return "SIM_PIN2"; case RIL_E_SIM_PUK2: return "SIM_PUK2"; case RIL_E_REQUEST_NOT_SUPPORTED: return "REQUEST_NOT_SUPPORTED"; case RIL_E_CANCELLED: return "CANCELLED"; case RIL_E_OP_NOT_ALLOWED_DURING_VOICE_CALL: return "OP_NOT_ALLOWED_DURING_VOICE_CALL"; case RIL_E_OP_NOT_ALLOWED_BEFORE_REG_TO_NW: return "OP_NOT_ALLOWED_BEFORE_REG_TO_NW"; case RIL_E_SMS_SEND_FAIL_RETRY: return "SMS_SEND_FAIL_RETRY"; case RIL_E_SIM_ABSENT: return "SIM_ABSENT"; case RIL_E_SUBSCRIPTION_NOT_AVAILABLE: return "SUBSCRIPTION_NOT_AVAILABLE"; case RIL_E_MODE_NOT_SUPPORTED: return "MODE_NOT_SUPPORTED"; case RIL_E_FDN_CHECK_FAILURE: return "FDN_CHECK_FAILURE"; case RIL_E_ILLEGAL_SIM_OR_ME: return "ILLEGAL_SIM_OR_ME"; case RIL_E_DIAL_MODIFIED_TO_USSD: return "DIAL_MODIFIED_TO_USSD"; case RIL_E_DIAL_MODIFIED_TO_SS: return "DIAL_MODIFIED_TO_SS"; case RIL_E_DIAL_MODIFIED_TO_DIAL: return "DIAL_MODIFIED_TO_DIAL"; case RIL_E_USSD_MODIFIED_TO_DIAL: return "USSD_MODIFIED_TO_DIAL"; case RIL_E_USSD_MODIFIED_TO_SS: return "USSD_MODIFIED_TO_SS"; case RIL_E_USSD_MODIFIED_TO_USSD: return "USSD_MODIFIED_TO_USSD"; case RIL_E_SS_MODIFIED_TO_DIAL: return "SS_MODIFIED_TO_DIAL"; case RIL_E_SS_MODIFIED_TO_USSD: return "SS_MODIFIED_TO_USSD"; case RIL_E_SS_MODIFIED_TO_SS: return "SS_MODIFIED_TO_SS"; case RIL_E_SUBSCRIPTION_NOT_SUPPORTED: return "SUBSCRIPTION_NOT_SUPPORTED"; default: return ""; } } const char *ril_pinstate_to_string(int pin_state) { switch (pin_state) { case RIL_PINSTATE_UNKNOWN: return "UNKNOWN"; case RIL_PINSTATE_ENABLED_NOT_VERIFIED: return "ENABLED_NOT_VERIFIED"; case RIL_PINSTATE_ENABLED_VERIFIED: return "ENABLED_VERIFIED"; case RIL_PINSTATE_DISABLED: return "DISABLED"; case RIL_PINSTATE_ENABLED_BLOCKED: return "ENABLED_BLOCKED"; case RIL_PINSTATE_ENABLED_PERM_BLOCKED: return "ENABLED_PERM_BLOCKED"; default: return ""; } } const char *ril_radio_state_to_string(int radio_state) { switch (radio_state) { case RADIO_STATE_OFF: return "OFF"; case RADIO_STATE_UNAVAILABLE: return "UNAVAILABLE"; case RADIO_STATE_SIM_NOT_READY: return "SIM_NOT_READY"; case RADIO_STATE_SIM_LOCKED_OR_ABSENT: return "SIM_LOCKED_OR_ABSENT"; case RADIO_STATE_SIM_READY: return "SIM_READY"; case RADIO_STATE_ON: return "ON"; default: return ""; } } const char *ril_radio_tech_to_string(int radio_tech) { switch (radio_tech) { case RADIO_TECH_UNKNOWN: return "UNKNOWN"; case RADIO_TECH_GPRS: return "GPRS"; case RADIO_TECH_EDGE: return "EDGE"; case RADIO_TECH_UMTS: return "UMTS"; case RADIO_TECH_IS95A: return "IS95A"; case RADIO_TECH_IS95B: return "IS95B"; case RADIO_TECH_1xRTT: return "1xRTT"; case RADIO_TECH_EVDO_0: return "EVDO_0"; case RADIO_TECH_EVDO_A: return "EVDO_A"; case RADIO_TECH_HSDPA: return "HSDPA"; case RADIO_TECH_HSUPA: return "HSUPA"; case RADIO_TECH_HSPA: return "HSPA"; case RADIO_TECH_EVDO_B: return "EVDO_B"; case RADIO_TECH_EHRPD: return "EHRPD"; case RADIO_TECH_LTE: return "LTE"; case RADIO_TECH_HSPAP: return "HSPAP"; case RADIO_TECH_GSM: return "GSM"; case MTK_RADIO_TECH_HSDPAP: return "MTK_HSDPAP"; case MTK_RADIO_TECH_HSDPAP_UPA: return "MTK_HSDPAP_UPA"; case MTK_RADIO_TECH_HSUPAP: return "MTK_HSUPAP"; case MTK_RADIO_TECH_HSUPAP_DPA: return "MTK_HSUPAP_DPA"; case MTK_RADIO_TECH_DC_DPA: return "MTK_DC_DPA"; case MTK_RADIO_TECH_DC_UPA: return "MTK_DC_UPA"; case MTK_RADIO_TECH_DC_HSDPAP: return "MTK_DC_HSDPAP"; case MTK_RADIO_TECH_DC_HSDPAP_UPA: return "MTK_DC_HSDPAP_UPA"; case MTK_RADIO_TECH_DC_HSDPAP_DPA: return "MTK_DC_HSDPAP_DPA"; case MTK_RADIO_TECH_DC_HSPAP: return "MTK_DC_HSPAP"; default: if (g_snprintf(temp_str, sizeof(temp_str), "", radio_tech)) return temp_str; else return ""; } } const char *ril_request_id_to_string(int req) { switch (req) { case RIL_REQUEST_GET_SIM_STATUS: return "RIL_REQUEST_GET_SIM_STATUS"; case RIL_REQUEST_ENTER_SIM_PIN: return "RIL_REQUEST_ENTER_SIM_PIN"; case RIL_REQUEST_ENTER_SIM_PUK: return "RIL_REQUEST_ENTER_SIM_PUK"; case RIL_REQUEST_ENTER_SIM_PIN2: return "RIL_REQUEST_ENTER_SIM_PIN2"; case RIL_REQUEST_ENTER_SIM_PUK2: return "RIL_REQUEST_ENTER_SIM_PUK2"; case RIL_REQUEST_CHANGE_SIM_PIN: return "RIL_REQUEST_CHANGE_SIM_PIN"; case RIL_REQUEST_CHANGE_SIM_PIN2: return "RIL_REQUEST_CHANGE_SIM_PIN2"; case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: return "RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION"; case RIL_REQUEST_GET_CURRENT_CALLS: return "RIL_REQUEST_GET_CURRENT_CALLS"; case RIL_REQUEST_DIAL: return "RIL_REQUEST_DIAL"; case RIL_REQUEST_GET_IMSI: return "RIL_REQUEST_GET_IMSI"; case RIL_REQUEST_HANGUP: return "RIL_REQUEST_HANGUP"; case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: return "RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND"; case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: return "RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND"; case RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE: return "RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE"; case RIL_REQUEST_CONFERENCE: return "RIL_REQUEST_CONFERENCE"; case RIL_REQUEST_UDUB: return "RIL_REQUEST_UDUB"; case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: return "RIL_REQUEST_LAST_CALL_FAIL_CAUSE"; case RIL_REQUEST_SIGNAL_STRENGTH: return "RIL_REQUEST_SIGNAL_STRENGTH"; case RIL_REQUEST_VOICE_REGISTRATION_STATE: return "RIL_REQUEST_VOICE_REGISTRATION_STATE"; case RIL_REQUEST_DATA_REGISTRATION_STATE: return "RIL_REQUEST_DATA_REGISTRATION_STATE"; case RIL_REQUEST_OPERATOR: return "RIL_REQUEST_OPERATOR"; case RIL_REQUEST_RADIO_POWER: return "RIL_REQUEST_RADIO_POWER"; case RIL_REQUEST_DTMF: return "RIL_REQUEST_DTMF"; case RIL_REQUEST_SEND_SMS: return "RIL_REQUEST_SEND_SMS"; case RIL_REQUEST_SEND_SMS_EXPECT_MORE: return "RIL_REQUEST_SEND_SMS_EXPECT_MORE"; case RIL_REQUEST_SETUP_DATA_CALL: return "RIL_REQUEST_SETUP_DATA_CALL"; case RIL_REQUEST_SIM_IO: return "RIL_REQUEST_SIM_IO"; case RIL_REQUEST_SEND_USSD: return "RIL_REQUEST_SEND_USSD"; case RIL_REQUEST_CANCEL_USSD: return "RIL_REQUEST_CANCEL_USSD"; case RIL_REQUEST_GET_CLIR: return "RIL_REQUEST_GET_CLIR"; case RIL_REQUEST_SET_CLIR: return "RIL_REQUEST_SET_CLIR"; case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: return "RIL_REQUEST_QUERY_CALL_FORWARD_STATUS"; case RIL_REQUEST_SET_CALL_FORWARD: return "RIL_REQUEST_SET_CALL_FORWARD"; case RIL_REQUEST_QUERY_CALL_WAITING: return "RIL_REQUEST_QUERY_CALL_WAITING"; case RIL_REQUEST_SET_CALL_WAITING: return "RIL_REQUEST_SET_CALL_WAITING"; case RIL_REQUEST_SMS_ACKNOWLEDGE: return "RIL_REQUEST_SMS_ACKNOWLEDGE "; case RIL_REQUEST_GET_IMEI: return "RIL_REQUEST_GET_IMEI"; case RIL_REQUEST_GET_IMEISV: return "RIL_REQUEST_GET_IMEISV"; case RIL_REQUEST_ANSWER: return "RIL_REQUEST_ANSWER"; case RIL_REQUEST_DEACTIVATE_DATA_CALL: return "RIL_REQUEST_DEACTIVATE_DATA_CALL"; case RIL_REQUEST_QUERY_FACILITY_LOCK: return "RIL_REQUEST_QUERY_FACILITY_LOCK"; case RIL_REQUEST_SET_FACILITY_LOCK: return "RIL_REQUEST_SET_FACILITY_LOCK"; case RIL_REQUEST_CHANGE_BARRING_PASSWORD: return "RIL_REQUEST_CHANGE_BARRING_PASSWORD"; case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: return "RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE"; case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: return "RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC"; case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: return "RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL"; case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS: return "RIL_REQUEST_QUERY_AVAILABLE_NETWORKS"; case RIL_REQUEST_DTMF_START: return "RIL_REQUEST_DTMF_START"; case RIL_REQUEST_DTMF_STOP: return "RIL_REQUEST_DTMF_STOP"; case RIL_REQUEST_BASEBAND_VERSION: return "RIL_REQUEST_BASEBAND_VERSION"; case RIL_REQUEST_SEPARATE_CONNECTION: return "RIL_REQUEST_SEPARATE_CONNECTION"; case RIL_REQUEST_SET_MUTE: return "RIL_REQUEST_SET_MUTE"; case RIL_REQUEST_GET_MUTE: return "RIL_REQUEST_GET_MUTE"; case RIL_REQUEST_QUERY_CLIP: return "RIL_REQUEST_QUERY_CLIP"; case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: return "RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE"; case RIL_REQUEST_DATA_CALL_LIST: return "RIL_REQUEST_DATA_CALL_LIST"; case RIL_REQUEST_RESET_RADIO: return "RIL_REQUEST_RESET_RADIO"; case RIL_REQUEST_OEM_HOOK_RAW: return "RIL_REQUEST_OEM_HOOK_RAW"; case RIL_REQUEST_OEM_HOOK_STRINGS: return "RIL_REQUEST_OEM_HOOK_STRINGS"; case RIL_REQUEST_SCREEN_STATE: return "RIL_REQUEST_SCREEN_STATE"; case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION: return "RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION"; case RIL_REQUEST_WRITE_SMS_TO_SIM: return "RIL_REQUEST_WRITE_SMS_TO_SIM"; case RIL_REQUEST_DELETE_SMS_ON_SIM: return "RIL_REQUEST_DELETE_SMS_ON_SIM"; case RIL_REQUEST_SET_BAND_MODE: return "RIL_REQUEST_SET_BAND_MODE"; case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: return "RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE"; case RIL_REQUEST_STK_GET_PROFILE: return "RIL_REQUEST_STK_GET_PROFILE"; case RIL_REQUEST_STK_SET_PROFILE: return "RIL_REQUEST_STK_SET_PROFILE"; case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: return "RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND"; case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: return "RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE"; case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: return "RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM"; case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: return "RIL_REQUEST_EXPLICIT_CALL_TRANSFER"; case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: return "RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE"; case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: return "RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE"; case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: return "RIL_REQUEST_GET_NEIGHBORING_CELL_IDS"; case RIL_REQUEST_SET_LOCATION_UPDATES: return "RIL_REQUEST_SET_LOCATION_UPDATES"; case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: return "RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE"; case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: return "RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE"; case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: return "RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE"; case RIL_REQUEST_SET_TTY_MODE: return "RIL_REQUEST_SET_TTY_MODE"; case RIL_REQUEST_QUERY_TTY_MODE: return "RIL_REQUEST_QUERY_TTY_MODE"; case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE: return "RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE"; case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE: return "RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE"; case RIL_REQUEST_CDMA_FLASH: return "RIL_REQUEST_CDMA_FLASH"; case RIL_REQUEST_CDMA_BURST_DTMF: return "RIL_REQUEST_CDMA_BURST_DTMF"; case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY: return "RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY"; case RIL_REQUEST_CDMA_SEND_SMS: return "RIL_REQUEST_CDMA_SEND_SMS"; case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE: return "RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE"; case RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG: return "RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG"; case RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG: return "RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG"; case RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION: return "RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION"; case RIL_REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG: return "RIL_REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG"; case RIL_REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG: return "RIL_REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG"; case RIL_REQUEST_CDMA_SMS_BROADCAST_ACTIVATION: return "RIL_REQUEST_CDMA_SMS_BROADCAST_ACTIVATION"; case RIL_REQUEST_CDMA_SUBSCRIPTION: return "RIL_REQUEST_CDMA_SUBSCRIPTION"; case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM: return "RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM"; case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM: return "RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM"; case RIL_REQUEST_DEVICE_IDENTITY: return "RIL_REQUEST_DEVICE_IDENTITY"; case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: return "RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE"; case RIL_REQUEST_GET_SMSC_ADDRESS: return "RIL_REQUEST_GET_SMSC_ADDRESS"; case RIL_REQUEST_SET_SMSC_ADDRESS: return "RIL_REQUEST_SET_SMSC_ADDRESS"; case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: return "RIL_REQUEST_REPORT_SMS_MEMORY_STATUS"; case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING: return "RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING"; case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE: return "RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE"; case RIL_REQUEST_ISIM_AUTHENTICATION: return "RIL_REQUEST_ISIM_AUTHENTICATION"; case RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU: return "RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU"; case RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS: return "RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS"; case RIL_REQUEST_SET_INITIAL_ATTACH_APN: return "RIL_REQUEST_SET_INITIAL_ATTACH_APN"; default: return ""; } } const char *ril_unsol_request_to_string(int request) { switch (request) { case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: return "UNSOL_RESPONSE_RADIO_STATE_CHANGED"; case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: return "UNSOL_RESPONSE_CALL_STATE_CHANGED"; case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: return "UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED"; case RIL_UNSOL_RESPONSE_NEW_SMS: return "UNSOL_RESPONSE_NEW_SMS"; case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: return "UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT"; case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: return "UNSOL_RESPONSE_NEW_SMS_ON_SIM"; case RIL_UNSOL_ON_USSD: return "UNSOL_ON_USSD"; case RIL_UNSOL_ON_USSD_REQUEST: return "UNSOL_ON_USSD_REQUEST(obsolete)"; case RIL_UNSOL_NITZ_TIME_RECEIVED: return "UNSOL_NITZ_TIME_RECEIVED"; case RIL_UNSOL_SIGNAL_STRENGTH: return "UNSOL_SIGNAL_STRENGTH"; case RIL_UNSOL_SUPP_SVC_NOTIFICATION: return "UNSOL_SUPP_SVC_NOTIFICATION"; case RIL_UNSOL_STK_SESSION_END: return "UNSOL_STK_SESSION_END"; case RIL_UNSOL_STK_PROACTIVE_COMMAND: return "UNSOL_STK_PROACTIVE_COMMAND"; case RIL_UNSOL_STK_EVENT_NOTIFY: return "UNSOL_STK_EVENT_NOTIFY"; case RIL_UNSOL_STK_CALL_SETUP: return "UNSOL_STK_CALL_SETUP"; case RIL_UNSOL_SIM_SMS_STORAGE_FULL: return "UNSOL_SIM_SMS_STORAGE_FUL"; case RIL_UNSOL_SIM_REFRESH: return "UNSOL_SIM_REFRESH"; case RIL_UNSOL_DATA_CALL_LIST_CHANGED: return "UNSOL_DATA_CALL_LIST_CHANGED"; case RIL_UNSOL_CALL_RING: return "UNSOL_CALL_RING"; case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: return "UNSOL_RESPONSE_SIM_STATUS_CHANGED"; case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: return "UNSOL_NEW_CDMA_SMS"; case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: return "UNSOL_NEW_BROADCAST_SMS"; case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: return "UNSOL_CDMA_RUIM_SMS_STORAGE_FULL"; case RIL_UNSOL_RESTRICTED_STATE_CHANGED: return "UNSOL_RESTRICTED_STATE_CHANGED"; case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: return "UNSOL_ENTER_EMERGENCY_CALLBACK_MODE"; case RIL_UNSOL_CDMA_CALL_WAITING: return "UNSOL_CDMA_CALL_WAITING"; case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: return "UNSOL_CDMA_OTA_PROVISION_STATUS"; case RIL_UNSOL_CDMA_INFO_REC: return "UNSOL_CDMA_INFO_REC"; case RIL_UNSOL_OEM_HOOK_RAW: return "UNSOL_OEM_HOOK_RAW"; case RIL_UNSOL_RINGBACK_TONE: return "UNSOL_RINGBACK_TONE"; case RIL_UNSOL_RESEND_INCALL_MUTE: return "UNSOL_RESEND_INCALL_MUTE"; case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: return "UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED"; case RIL_UNSOL_CDMA_PRL_CHANGED: return "UNSOL_CDMA_PRL_CHANGED"; case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE"; case RIL_UNSOL_RIL_CONNECTED: return "UNSOL_RIL_CONNECTED"; default: return ""; } } const char *ril_pdp_fail_to_string(int status) { switch (status) { case PDP_FAIL_NONE: return "NONE"; case PDP_FAIL_OPERATOR_BARRED: return "OPERATOR_BARRED"; case PDP_FAIL_INSUFFICIENT_RESOURCES: return "INSUFFICIENT_RESOURCES"; case PDP_FAIL_MISSING_UKNOWN_APN: return "MISSING_UKNOWN_APN"; case PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE: return "UNKNOWN_PDP_ADDRESS_TYPE"; case PDP_FAIL_USER_AUTHENTICATION: return "USER_AUTHENTICATION"; case PDP_FAIL_ACTIVATION_REJECT_GGSN: return "ACTIVATION_REJECT_GGSN"; case PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED: return "ACTIVATION_REJECT_UNSPECIFIED"; case PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED: return "SERVICE_OPTION_NOT_SUPPORTED"; case PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED: return "SERVICE_OPTION_NOT_SUBSCRIBED"; case PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER: return "SERVICE_OPTION_OUT_OF_ORDER"; case PDP_FAIL_NSAPI_IN_USE: return "NSAPI_IN_USE"; case PDP_FAIL_REGULAR_DEACTIVATION: return "REGULAR_DEACTIVATION"; case PDP_FAIL_ONLY_IPV4_ALLOWED: return "ONLY_IPV4_ALLOWED"; case PDP_FAIL_ONLY_IPV6_ALLOWED: return "ONLY_IPV6_ALLOWED"; case PDP_FAIL_ONLY_SINGLE_BEARER_ALLOWED: return "ONLY_SINGLE_BEARER_ALLOWED"; case PDP_FAIL_PROTOCOL_ERRORS: return "PROTOCOL_ERRORS"; case PDP_FAIL_VOICE_REGISTRATION_FAIL: return "VOICE_REGISTRATION_FAIL"; case PDP_FAIL_DATA_REGISTRATION_FAIL: return "DATA_REGISTRATION_FAIL"; case PDP_FAIL_SIGNAL_LOST: return "SIGNAL_LOST"; case PDP_FAIL_PREF_RADIO_TECH_CHANGED: return "PREF_RADIO_TECH_CHANGED"; case PDP_FAIL_RADIO_POWER_OFF: return "RADIO_POWER_OFF"; case PDP_FAIL_TETHERED_CALL_ACTIVE: return "TETHERED_CALL_ACTIVE"; case PDP_FAIL_ERROR_UNSPECIFIED: return "ERROR_UNSPECIFIED"; default: if (g_snprintf(temp_str, sizeof(temp_str), "", status)) return temp_str; else return ""; } } void g_ril_util_debug_hexdump(gboolean in, const unsigned char *buf, gsize len, GRilDebugFunc debugf, gpointer user_data) { static const char hexdigits[] = "0123456789abcdef"; char str[68]; gsize i; if (debugf == NULL || !len) return; str[0] = in ? '<' : '>'; for (i = 0; i < len; i++) { str[((i % 16) * 3) + 1] = ' '; str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4]; str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf]; str[(i % 16) + 51] = g_ascii_isprint(buf[i]) ? buf[i] : '.'; if ((i + 1) % 16 == 0) { str[49] = ' '; str[50] = ' '; str[67] = '\0'; debugf(str, user_data); str[0] = ' '; } } if (i % 16 > 0) { gsize j; for (j = (i % 16); j < 16; j++) { str[(j * 3) + 1] = ' '; str[(j * 3) + 2] = ' '; str[(j * 3) + 3] = ' '; str[j + 51] = ' '; } str[49] = ' '; str[50] = ' '; str[67] = '\0'; debugf(str, user_data); } } gboolean g_ril_util_setup_io(GIOChannel *io, GIOFlags flags) { GIOFlags io_flags; if (g_io_channel_set_encoding(io, NULL, NULL) != G_IO_STATUS_NORMAL) return FALSE; g_io_channel_set_buffered(io, FALSE); if (flags & G_IO_FLAG_SET_MASK) { io_flags = g_io_channel_get_flags(io); io_flags |= (flags & G_IO_FLAG_SET_MASK); if (g_io_channel_set_flags(io, io_flags, NULL) != G_IO_STATUS_NORMAL) return FALSE; } g_io_channel_set_close_on_unref(io, TRUE); return TRUE; } ofono-1.17.bzr6912+16.04.20160314.3/gril/grilreply.c0000644000015600001650000007750212671500073021530 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2013 Jolla Ltd * Contact: Jussi Kangas * Copyright (C) 2012-2014 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include #include "common.h" #include "util.h" #include "grilreply.h" #include "grilutil.h" #define OPERATOR_NUM_PARAMS 3 /* Indexes for registration state replies */ #define RST_IX_STATE 0 #define RST_IX_LAC 1 #define RST_IX_CID 2 #define RST_IX_RAT 3 #define RDST_IX_MAXDC 5 #define MTK_MODEM_MAX_CIDS 3 static void ril_reply_free_operator(gpointer data) { struct reply_operator *reply = data; if (reply) { g_free(reply->lalpha); g_free(reply->salpha); g_free(reply->numeric); g_free(reply->status); g_free(reply); } } void g_ril_reply_free_avail_ops(struct reply_avail_ops *reply) { if (reply) { g_slist_free_full(reply->list, ril_reply_free_operator); g_free(reply); } } struct reply_avail_ops *g_ril_reply_parse_avail_ops(GRil *gril, const struct ril_msg *message) { struct parcel rilp; struct reply_operator *operator; struct reply_avail_ops *reply = NULL; unsigned int num_ops, num_strings; unsigned int i; int strings_per_opt; if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) strings_per_opt = 5; else strings_per_opt = 4; /* * Minimum message length is 4: * - array size */ if (message->buf_len < 4) { ofono_error("%s: invalid QUERY_AVAIL_NETWORKS reply: " "size too small (< 4): %d ", __func__, (int) message->buf_len); goto error; } g_ril_init_parcel(message, &rilp); g_ril_append_print_buf(gril, "{"); /* Number of operators at the list */ num_strings = (unsigned int) parcel_r_int32(&rilp); if (num_strings % strings_per_opt) { ofono_error("%s: invalid QUERY_AVAIL_NETWORKS reply: " "num_strings (%d) MOD %d != 0", __func__, num_strings, strings_per_opt); goto error; } num_ops = num_strings / strings_per_opt; DBG("noperators = %d", num_ops); reply = g_try_new0(struct reply_avail_ops, 1); if (reply == NULL) { ofono_error("%s: can't allocate reply struct", __func__); goto error; } reply->num_ops = num_ops; for (i = 0; i < num_ops; i++) { operator = g_try_new0(struct reply_operator, 1); if (operator == NULL) { ofono_error("%s: can't allocate reply struct", __func__); goto error; } operator->lalpha = parcel_r_string(&rilp); operator->salpha = parcel_r_string(&rilp); operator->numeric = parcel_r_string(&rilp); operator->status = parcel_r_string(&rilp); /* * MTK: additional string with technology: 2G/3G are the only * valid values currently. */ if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) { char *tech = parcel_r_string(&rilp); if (strcmp(tech, "3G") == 0) operator->tech = RADIO_TECH_UMTS; else operator->tech = RADIO_TECH_GSM; g_free(tech); } else { operator->tech = RADIO_TECH_GSM; } if (operator->lalpha == NULL && operator->salpha == NULL) { ofono_error("%s: operator (%s) doesn't specify names", operator->numeric, __func__); g_ril_reply_free_operator(operator); continue; } if (operator->numeric == NULL) { ofono_error("%s: operator (%s/%s) " "doesn't specify numeric", operator->lalpha, operator->salpha, __func__); g_ril_reply_free_operator(operator); continue; } if (operator->status == NULL) { ofono_error("%s: operator (%s/%s) " "doesn't specify status", operator->lalpha, operator->salpha, __func__); g_ril_reply_free_operator(operator); continue; } reply->list = g_slist_append(reply->list, operator); g_ril_append_print_buf(gril, "%s [lalpha=%s, salpha=%s, " " numeric=%s status=%s tech=%s]", print_buf, operator->lalpha, operator->salpha, operator->numeric, operator->status, ril_radio_tech_to_string(operator->tech)); } g_ril_append_print_buf(gril, "%s}", print_buf); g_ril_print_response(gril, message); return reply; error: if (reply) g_ril_reply_free_avail_ops(reply); return NULL; } void g_ril_reply_free_operator(struct reply_operator *reply) { ril_reply_free_operator(reply); } struct reply_operator *g_ril_reply_parse_operator(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int num_params; struct reply_operator *reply = NULL; /* * Minimum message length is 16: * - array size * - 3 NULL strings */ if (message->buf_len < 16) { ofono_error("%s: invalid OPERATOR reply: " "size too small (< 16): %d ", __func__, (int) message->buf_len); goto error; } g_ril_init_parcel(message, &rilp); num_params = parcel_r_int32(&rilp); if (num_params != OPERATOR_NUM_PARAMS) { ofono_error("%s: invalid OPERATOR reply: " "number of params is %d; should be 3.", __func__, num_params); goto error; } reply = g_new0(struct reply_operator, 1); reply->lalpha = parcel_r_string(&rilp); reply->salpha = parcel_r_string(&rilp); reply->numeric = parcel_r_string(&rilp); if (reply->lalpha == NULL && reply->salpha == NULL) { ofono_error("%s: invalid OPERATOR reply: " " no names returned.", __func__); goto error; } if (reply->numeric == NULL) { ofono_error("%s: invalid OPERATOR reply: " " no numeric returned.", __func__); goto error; } g_ril_append_print_buf(gril, "(lalpha=%s, salpha=%s, numeric=%s)", reply->lalpha, reply->salpha, reply->numeric); g_ril_print_response(gril, message); return reply; error: if (reply) g_ril_reply_free_operator(reply); return NULL; } static void set_reg_state(GRil *gril, struct reply_reg_state *reply, int i, const char *str) { int val; char *endp; int base; const char *strstate; if (str == NULL || *str == '\0') goto no_val; if (i == RST_IX_LAC || i == RST_IX_CID) base = 16; else base = 10; val = (int) strtol(str, &endp, base); if (*endp != '\0') goto no_val; switch (i) { case RST_IX_STATE: switch (val) { case RIL_REG_STATE_NOT_REGISTERED: case RIL_REG_STATE_REGISTERED: case RIL_REG_STATE_SEARCHING: case RIL_REG_STATE_DENIED: case RIL_REG_STATE_UNKNOWN: case RIL_REG_STATE_ROAMING: /* Only valid values for ofono */ strstate = registration_status_to_string(val); break; case RIL_REG_STATE_EMERGENCY_NOT_REGISTERED: case RIL_REG_STATE_EMERGENCY_SEARCHING: case RIL_REG_STATE_EMERGENCY_DENIED: case RIL_REG_STATE_EMERGENCY_UNKNOWN: /* Map to states valid for ofono core */ val -= RIL_REG_STATE_EMERGENCY_NOT_REGISTERED; strstate = str; break; default: val = NETWORK_REGISTRATION_STATUS_UNKNOWN; strstate = str; } reply->status = val; g_ril_append_print_buf(gril, "%s%s", print_buf, strstate); break; case RST_IX_LAC: reply->lac = val; g_ril_append_print_buf(gril, "%s0x%x", print_buf, val); break; case RST_IX_CID: reply->ci = val; g_ril_append_print_buf(gril, "%s0x%x", print_buf, val); break; case RST_IX_RAT: g_ril_append_print_buf(gril, "%s%s", print_buf, ril_radio_tech_to_string(val)); if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) { switch (val) { case MTK_RADIO_TECH_HSDPAP: case MTK_RADIO_TECH_HSDPAP_UPA: case MTK_RADIO_TECH_HSUPAP: case MTK_RADIO_TECH_HSUPAP_DPA: val = RADIO_TECH_HSPAP; break; case MTK_RADIO_TECH_DC_DPA: val = RADIO_TECH_HSDPA; break; case MTK_RADIO_TECH_DC_UPA: val = RADIO_TECH_HSUPA; break; case MTK_RADIO_TECH_DC_HSDPAP: case MTK_RADIO_TECH_DC_HSDPAP_UPA: case MTK_RADIO_TECH_DC_HSDPAP_DPA: case MTK_RADIO_TECH_DC_HSPAP: val = RADIO_TECH_HSPAP; break; } } reply->tech = val; break; default: goto no_val; } return; no_val: g_ril_append_print_buf(gril, "%s%s", print_buf, str ? str : "(null)"); } struct reply_reg_state *g_ril_reply_parse_voice_reg_state(GRil *gril, const struct ril_msg *message) { struct parcel rilp; struct parcel_str_array *str_arr; struct reply_reg_state *reply = NULL; int i; g_ril_init_parcel(message, &rilp); str_arr = parcel_r_str_array(&rilp); if (str_arr == NULL) { ofono_error("%s: parse error for %s", __func__, ril_request_id_to_string(message->req)); goto out; } reply = g_try_malloc0(sizeof(*reply)); if (reply == NULL) { ofono_error("%s: out of memory", __func__); goto out; } reply->status = -1; reply->lac = -1; reply->ci = -1; g_ril_append_print_buf(gril, "{"); for (i = 0; i < str_arr->num_str; ++i) { char *str = str_arr->str[i]; if (i > 0) g_ril_append_print_buf(gril, "%s,", print_buf); switch (i) { case RST_IX_STATE: case RST_IX_LAC: case RST_IX_CID: case RST_IX_RAT: set_reg_state(gril, reply, i, str); break; default: g_ril_append_print_buf(gril, "%s%s", print_buf, str ? str : "(null)"); } } g_ril_append_print_buf(gril, "%s}", print_buf); g_ril_print_response(gril, message); /* As a minimum we require a valid status string */ if (reply->status == -1) { ofono_error("%s: invalid status", __func__); g_free(reply); reply = NULL; } out: parcel_free_str_array(str_arr); return reply; } static void set_data_reg_state(GRil *gril, struct reply_data_reg_state *reply, int i, const char *str) { unsigned val; char *endp; if (str == NULL || *str == '\0') goto no_val; val = (unsigned) strtoul(str, &endp, 10); if (*endp != '\0') goto no_val; switch (i) { case RDST_IX_MAXDC: /* * MTK modem does not return max_cids, string for this index * actually contains the maximum data bearer capability. */ if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) reply->max_cids = MTK_MODEM_MAX_CIDS; else reply->max_cids = val; g_ril_append_print_buf(gril, "%s%u", print_buf, val); break; default: goto no_val; } return; no_val: g_ril_append_print_buf(gril, "%s%s", print_buf, str ? str : "(null)"); } struct reply_data_reg_state *g_ril_reply_parse_data_reg_state(GRil *gril, const struct ril_msg *message) { struct parcel rilp; struct parcel_str_array *str_arr; struct reply_data_reg_state *reply = NULL; int i; g_ril_init_parcel(message, &rilp); str_arr = parcel_r_str_array(&rilp); if (str_arr == NULL) { ofono_error("%s: parse error for %s", __func__, ril_request_id_to_string(message->req)); goto out; } reply = g_try_malloc0(sizeof(*reply)); if (reply == NULL) { ofono_error("%s: out of memory", __func__); goto out; } reply->reg_state.status = -1; reply->reg_state.lac = -1; reply->reg_state.ci = -1; g_ril_append_print_buf(gril, "{"); for (i = 0; i < str_arr->num_str; ++i) { char *str = str_arr->str[i]; if (i > 0) g_ril_append_print_buf(gril, "%s,", print_buf); switch (i) { case RST_IX_STATE: case RST_IX_LAC: case RST_IX_CID: case RST_IX_RAT: set_reg_state(gril, &reply->reg_state, i, str); break; case RDST_IX_MAXDC: set_data_reg_state(gril, reply, i, str); break; default: g_ril_append_print_buf(gril, "%s%s", print_buf, str ? str : "(null)"); } } g_ril_append_print_buf(gril, "%s}", print_buf); g_ril_print_response(gril, message); /* As a minimum we require a valid status string */ if (reply->reg_state.status == -1) { ofono_error("%s: invalid status", __func__); g_free(reply); reply = NULL; } out: parcel_free_str_array(str_arr); return reply; } void g_ril_reply_free_sim_io(struct reply_sim_io *reply) { if (reply) { g_free(reply->hex_response); g_free(reply); } } struct reply_sim_io *g_ril_reply_parse_sim_io(GRil *gril, const struct ril_msg *message) { struct parcel rilp; char *response = NULL; struct reply_sim_io *reply; /* * Minimum length of SIM_IO_Response is 12: * sw1 (int32) * sw2 (int32) * simResponse (string) */ if (message->buf_len < 12) { ofono_error("Invalid SIM IO reply: size too small (< 12): %d ", (int) message->buf_len); return NULL; } reply = g_new0(struct reply_sim_io, 1); g_ril_init_parcel(message, &rilp); reply->sw1 = parcel_r_int32(&rilp); reply->sw2 = parcel_r_int32(&rilp); response = parcel_r_string(&rilp); g_ril_append_print_buf(gril, "(sw1=0x%.2X,sw2=0x%.2X,%s)", reply->sw1, reply->sw2, response); g_ril_print_response(gril, message); if (rilp.malformed) goto error; if (response != NULL) { reply->hex_response = decode_hex(response, strlen(response), (long *) &reply->hex_len, -1); g_free(response); if (reply->hex_response == NULL) goto error; } return reply; error: g_free(reply); return NULL; } gchar *g_ril_reply_parse_imsi(GRil *gril, const struct ril_msg *message) { struct parcel rilp; gchar *imsi; g_ril_init_parcel(message, &rilp); imsi = parcel_r_string(&rilp); g_ril_append_print_buf(gril, "{%s}", imsi ? imsi : "NULL"); g_ril_print_response(gril, message); return imsi; } void g_ril_reply_free_sim_status(struct reply_sim_status *status) { if (status) { guint i; for (i = 0; i < status->num_apps; i++) { if (status->apps[i] != NULL) { g_free(status->apps[i]->aid_str); g_free(status->apps[i]->app_str); g_free(status->apps[i]); } } g_free(status); } } struct reply_sim_status *g_ril_reply_parse_sim_status(GRil *gril, const struct ril_msg *message) { struct parcel rilp; unsigned int i; struct reply_sim_status *status; g_ril_append_print_buf(gril, "[%d,%04d]< %s", g_ril_get_slot(gril), message->serial_no, ril_request_id_to_string(message->req)); g_ril_init_parcel(message, &rilp); status = g_new0(struct reply_sim_status, 1); status->card_state = parcel_r_int32(&rilp); /* * NOTE: * * The global pin_status is used for multi-application * UICC cards. For example, there are SIM cards that * can be used in both GSM and CDMA phones. Instead * of managed PINs for both applications, a global PIN * is set instead. It's not clear at this point if * such SIM cards are supported by ofono or RILD. */ status->pin_state = parcel_r_int32(&rilp); status->gsm_umts_index = parcel_r_int32(&rilp); status->cdma_index = parcel_r_int32(&rilp); status->ims_index = parcel_r_int32(&rilp); status->num_apps = parcel_r_int32(&rilp); if (rilp.malformed) goto error; g_ril_append_print_buf(gril, "(card_state=%d,universal_pin_state=%d," "gsm_umts_index=%d,cdma_index=%d," "ims_index=%d, ", status->card_state, status->pin_state, status->gsm_umts_index, status->cdma_index, status->ims_index); if (status->card_state != RIL_CARDSTATE_PRESENT) goto done; if (status->num_apps > MAX_UICC_APPS) { ofono_error("SIM error; too many apps: %d", status->num_apps); status->num_apps = MAX_UICC_APPS; } for (i = 0; i < status->num_apps; i++) { struct reply_sim_app *app; DBG("processing app[%d]", i); status->apps[i] = g_try_new0(struct reply_sim_app, 1); app = status->apps[i]; if (app == NULL) { ofono_error("Can't allocate app_data"); goto error; } app->app_type = parcel_r_int32(&rilp); app->app_state = parcel_r_int32(&rilp); app->perso_substate = parcel_r_int32(&rilp); /* * TODO: we need a way to instruct parcel to skip * a string, without allocating memory... */ /* application ID (AID) */ app->aid_str = parcel_r_string(&rilp); /* application label */ app->app_str = parcel_r_string(&rilp); app->pin_replaced = parcel_r_int32(&rilp); app->pin1_state = parcel_r_int32(&rilp); app->pin2_state = parcel_r_int32(&rilp); g_ril_append_print_buf(gril, "%s[app_type=%d,app_state=%d," "perso_substate=%d,aid_ptr=%s," "app_label_ptr=%s,pin1_replaced=%d," "pin1=%d,pin2=%d],", print_buf, app->app_type, app->app_state, app->perso_substate, app->aid_str ? app->aid_str : "NULL", app->app_str ? app->app_str : "NULL", app->pin_replaced, app->pin1_state, app->pin2_state); } if (rilp.malformed) goto error; done: g_ril_append_print_buf(gril, "%s}", print_buf); g_ril_print_response(gril, message); return status; error: g_ril_reply_free_sim_status(status); return NULL; } struct ofono_phone_number *g_ril_reply_parse_get_smsc_address( GRil *gril, const struct ril_msg *message) { struct ofono_phone_number *sca; struct parcel rilp; char *number, *temp_buf; sca = g_new0(struct ofono_phone_number, 1); if (sca == NULL) { ofono_error("%s Out of memory", __func__); goto err_alloc; } g_ril_init_parcel(message, &rilp); temp_buf = parcel_r_string(&rilp); if (temp_buf == NULL) { ofono_error("%s Cannot read SMSC address", __func__); goto err_readsca; } /* RIL gives address in quotes */ number = strtok(temp_buf, "\""); if (number == NULL || *number == '\0') { ofono_error("%s Invalid SMSC address", __func__); goto err_scaformat; } if (number[0] == '+') { number = number + 1; sca->type = OFONO_NUMBER_TYPE_INTERNATIONAL; } else { sca->type = OFONO_NUMBER_TYPE_UNKNOWN; } strncpy(sca->number, number, OFONO_MAX_PHONE_NUMBER_LENGTH); sca->number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0'; g_ril_append_print_buf(gril, "{type=%d,number=%s}", sca->type, sca->number); g_ril_print_response(gril, message); g_free(temp_buf); return sca; err_scaformat: g_free(temp_buf); err_readsca: g_free(sca); err_alloc: return NULL; } int g_ril_reply_parse_sms_response(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int error, mr; char *ack_pdu; /* Set up Parcel struct for proper parsing */ g_ril_init_parcel(message, &rilp); /* * TP-Message-Reference for GSM/ * BearerData MessageId for CDMA */ mr = parcel_r_int32(&rilp); ack_pdu = parcel_r_string(&rilp); error = parcel_r_int32(&rilp); g_ril_append_print_buf(gril, "{%d,%s,%d}", mr, ack_pdu, error); g_ril_print_response(gril, message); g_free(ack_pdu); return mr; } static gint g_ril_call_compare(gconstpointer a, gconstpointer b) { const struct ofono_call *ca = a; const struct ofono_call *cb = b; if (ca->id < cb->id) return -1; if (ca->id > cb->id) return 1; return 0; } GSList *g_ril_reply_parse_get_calls(GRil *gril, const struct ril_msg *message) { struct ofono_call *call; struct parcel rilp; GSList *l = NULL; int num, i; gchar *number, *name; g_ril_init_parcel(message, &rilp); g_ril_append_print_buf(gril, "{"); /* maguro signals no calls with empty event data */ if (rilp.size < sizeof(int32_t)) goto no_calls; /* Number of RIL_Call structs */ num = parcel_r_int32(&rilp); for (i = 0; i < num; i++) { call = g_try_new(struct ofono_call, 1); if (call == NULL) break; ofono_call_init(call); call->status = parcel_r_int32(&rilp); call->id = parcel_r_int32(&rilp); call->phone_number.type = parcel_r_int32(&rilp); parcel_r_int32(&rilp); /* isMpty */ parcel_r_int32(&rilp); /* isMT */ parcel_r_int32(&rilp); /* als */ call->type = parcel_r_int32(&rilp); /* isVoice */ parcel_r_int32(&rilp); /* isVoicePrivacy */ number = parcel_r_string(&rilp); if (number) { strncpy(call->phone_number.number, number, OFONO_MAX_PHONE_NUMBER_LENGTH); g_free(number); } parcel_r_int32(&rilp); /* numberPresentation */ name = parcel_r_string(&rilp); if (name) { strncpy(call->name, name, OFONO_MAX_CALLER_NAME_LENGTH); g_free(name); } parcel_r_int32(&rilp); /* namePresentation */ parcel_r_int32(&rilp); /* uusInfo */ if (strlen(call->phone_number.number) > 0) call->clip_validity = 0; else call->clip_validity = 2; g_ril_append_print_buf(gril, "%s [id=%d,status=%d,type=%d," "number=%s,name=%s]", print_buf, call->id, call->status, call->type, call->phone_number.number, call->name); l = g_slist_insert_sorted(l, call, g_ril_call_compare); } no_calls: g_ril_append_print_buf(gril, "%s}", print_buf); g_ril_print_response(gril, message); return l; } enum ofono_disconnect_reason g_ril_reply_parse_call_fail_cause( GRil *gril, const struct ril_msg *message) { enum ofono_disconnect_reason reason = OFONO_DISCONNECT_REASON_ERROR; int last_cause = CALL_FAIL_ERROR_UNSPECIFIED; struct parcel rilp; g_ril_init_parcel(message, &rilp); if (rilp.size < sizeof(int32_t)) ofono_error("%s: Parcel is too small", __func__); else if (parcel_r_int32(&rilp) > 0) last_cause = parcel_r_int32(&rilp); if (last_cause == CALL_FAIL_NORMAL || last_cause == CALL_FAIL_BUSY) reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP; g_ril_append_print_buf(gril, "{%d}", last_cause); g_ril_print_response(gril, message); return reason; } int g_ril_reply_parse_get_mute(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int muted; g_ril_init_parcel(message, &rilp); /* skip length of int[] */ parcel_r_int32(&rilp); muted = parcel_r_int32(&rilp); g_ril_append_print_buf(gril, "{%d}", muted); g_ril_print_response(gril, message); return muted; } char *g_ril_reply_parse_baseband_version(GRil *gril, const struct ril_msg *message) { struct parcel rilp; char *version; g_ril_init_parcel(message, &rilp); version = parcel_r_string(&rilp); g_ril_append_print_buf(gril, "{%s}", version); g_ril_print_response(gril, message); return version; } char *g_ril_reply_parse_get_imei(GRil *gril, const struct ril_msg *message) { struct parcel rilp; char *imei; g_ril_init_parcel(message, &rilp); imei = parcel_r_string(&rilp); g_ril_append_print_buf(gril, "{%s}", imei); g_ril_print_response(gril, message); return imei; } int g_ril_reply_parse_query_call_waiting(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int numint, enabled, cls; g_ril_init_parcel(message, &rilp); numint = parcel_r_int32(&rilp); if (numint < 1) { ofono_error("%s Wrong format", __func__); goto error; } enabled = parcel_r_int32(&rilp); if (enabled > 0) cls = parcel_r_int32(&rilp); else cls = 0; g_ril_append_print_buf(gril, "{%d,0x%x}", enabled, cls); g_ril_print_response(gril, message); return cls; error: return -1; } int g_ril_reply_parse_query_clip(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int clip_status, numint; g_ril_init_parcel(message, &rilp); numint = parcel_r_int32(&rilp); if (numint != 1) { ofono_error("%s Wrong format", __func__); goto error; } clip_status = parcel_r_int32(&rilp); g_ril_append_print_buf(gril, "{%d}", clip_status); g_ril_print_response(gril, message); return clip_status; error: return -1; } void g_ril_reply_free_get_clir(struct reply_clir *rclir) { g_free(rclir); } struct reply_clir *g_ril_reply_parse_get_clir(GRil *gril, const struct ril_msg *message) { struct parcel rilp; struct reply_clir *rclir; int numint; rclir = g_try_malloc0(sizeof(*rclir)); if (rclir == NULL) { ofono_error("%s Out of memory", __func__); goto error; } g_ril_init_parcel(message, &rilp); /* Length */ numint = parcel_r_int32(&rilp); if (numint != 2) { ofono_error("%s Wrong format", __func__); goto error; } /* Set HideCallerId property from network */ rclir->status = parcel_r_int32(&rilp); /* State of the CLIR supplementary service in the network */ rclir->provisioned = parcel_r_int32(&rilp); g_ril_append_print_buf(gril, "{%d,%d}", rclir->status, rclir->provisioned); g_ril_print_response(gril, message); return rclir; error: g_free(rclir); return NULL; } struct ofono_call_forwarding_condition *g_ril_reply_parse_query_call_fwd(GRil *gril, const struct ril_msg *message, unsigned int *list_size) { struct ofono_call_forwarding_condition *list; struct parcel rilp; unsigned int i; if (list_size == NULL) { ofono_error("%s: list_size is NULL!", __func__); goto error; } g_ril_init_parcel(message, &rilp); if (rilp.size < sizeof(int32_t)) { ofono_error("%s: malformed parcel, can't read num params", __func__); goto error; } *list_size = parcel_r_int32(&rilp); if (*list_size == 0) { /* not really an error; handled in caller */ goto error; } list = g_try_new0(struct ofono_call_forwarding_condition, *list_size); if (list == NULL) { ofono_error("%s: Out of memory", __func__); goto error; } g_ril_append_print_buf(gril, "{"); for (i = 0; i < *list_size; i++) { char *str; list[i].status = parcel_r_int32(&rilp); parcel_r_int32(&rilp); /* skip reason */ list[i].cls = parcel_r_int32(&rilp); list[i].phone_number.type = parcel_r_int32(&rilp); str = parcel_r_string(&rilp); if (str != NULL) { strncpy(list[i].phone_number.number, str, OFONO_MAX_PHONE_NUMBER_LENGTH); g_free(str); list[i].phone_number.number[ OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0'; } list[i].time = parcel_r_int32(&rilp); if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); g_free(list); goto error; } g_ril_append_print_buf(gril, "%s [%d,%d,%d,%s,%d]", print_buf, list[i].status, list[i].cls, list[i].phone_number.type, list[i].phone_number.number, list[i].time); } g_ril_append_print_buf(gril, "%s}", print_buf); g_ril_print_response(gril, message); return list; error: return NULL; } int g_ril_reply_parse_get_preferred_network_type(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int numint, parcel_net_type, net_type; g_ril_init_parcel(message, &rilp); numint = parcel_r_int32(&rilp); if (numint != 1) { ofono_error("%s: Wrong format", __func__); goto error; } parcel_net_type = parcel_r_int32(&rilp); net_type = parcel_net_type; /* Try to translate special MTK settings */ if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) { switch (net_type) { /* 4G preferred */ case MTK_PREF_NET_TYPE_LTE_GSM_WCDMA: case MTK_PREF_NET_TYPE_LTE_GSM_WCDMA_MMDC: case MTK_PREF_NET_TYPE_LTE_GSM_TYPE: case MTK_PREF_NET_TYPE_LTE_GSM_MMDC_TYPE: net_type = PREF_NET_TYPE_LTE_GSM_WCDMA; break; /* 3G or 2G preferred over LTE */ case MTK_PREF_NET_TYPE_GSM_WCDMA_LTE: case MTK_PREF_NET_TYPE_GSM_WCDMA_LTE_MMDC: net_type = PREF_NET_TYPE_GSM_WCDMA; break; } } if (net_type < 0 || net_type > PREF_NET_TYPE_LTE_ONLY) { ofono_error("%s: unknown network type", __func__); goto error; } if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); goto error; } g_ril_append_print_buf(gril, "{%d}", parcel_net_type); g_ril_print_response(gril, message); return net_type; error: return -1; } int g_ril_reply_parse_query_facility_lock(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int status, numint; g_ril_init_parcel(message, &rilp); /* infineon returns two integers */ numint = parcel_r_int32(&rilp); if (numint < 1) { ofono_error("%s: wrong format", __func__); goto error; } status = parcel_r_int32(&rilp); if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); goto error; } g_ril_append_print_buf(gril, "{%d}", status); g_ril_print_response(gril, message); return status; error: return -1; } int g_ril_reply_parse_set_facility_lock(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int retries = -1, numint; g_ril_init_parcel(message, &rilp); /* mako reply has no payload for call barring */ if (parcel_data_avail(&rilp) == 0) { retries = 0; goto done; } numint = parcel_r_int32(&rilp); if (numint != 1) { ofono_error("%s: wrong format", __func__); goto end; } retries = parcel_r_int32(&rilp); if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); retries = -1; goto end; } done: g_ril_append_print_buf(gril, "{%d}", retries); g_ril_print_response(gril, message); /* -1 indicates unknown; reset to 0 so as to not trigger failure */ if (retries == -1) retries = 0; end: return retries; } int *g_ril_reply_parse_retries(GRil *gril, const struct ril_msg *message, enum ofono_sim_password_type passwd_type) { struct parcel rilp; int i, numint; int *retries = g_try_malloc0(sizeof(int) * OFONO_SIM_PASSWORD_INVALID); if (retries == NULL) { ofono_error("%s: out of memory", __func__); goto no_data; } for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; ++i) retries[i] = -1; g_ril_init_parcel(message, &rilp); /* maguro/infineon: no data is returned */ if (parcel_data_avail(&rilp) == 0) goto no_data; numint = parcel_r_int32(&rilp); switch (g_ril_vendor(gril)) { case OFONO_RIL_VENDOR_AOSP: case OFONO_RIL_VENDOR_QCOM_MSIM: /* * The number of retries is valid only when a wrong password has * been introduced in Nexus 4. TODO: check Nexus 5 behaviour. */ if (message->error == RIL_E_PASSWORD_INCORRECT) retries[passwd_type] = parcel_r_int32(&rilp); g_ril_append_print_buf(gril, "{%d}", retries[passwd_type]); break; case OFONO_RIL_VENDOR_MTK: /* * Some versions of MTK modem return just the retries for the * password just entered while others return the retries for all * passwords. */ if (numint == 1) { retries[passwd_type] = parcel_r_int32(&rilp); g_ril_append_print_buf(gril, "{%d}", retries[passwd_type]); } else if (numint == 4) { retries[OFONO_SIM_PASSWORD_SIM_PIN] = parcel_r_int32(&rilp); retries[OFONO_SIM_PASSWORD_SIM_PIN2] = parcel_r_int32(&rilp); retries[OFONO_SIM_PASSWORD_SIM_PUK] = parcel_r_int32(&rilp); retries[OFONO_SIM_PASSWORD_SIM_PUK2] = parcel_r_int32(&rilp); g_ril_append_print_buf(gril, "{pin %d, pin2 %d, puk %d, puk2 %d}", retries[OFONO_SIM_PASSWORD_SIM_PIN], retries[OFONO_SIM_PASSWORD_SIM_PIN2], retries[OFONO_SIM_PASSWORD_SIM_PUK], retries[OFONO_SIM_PASSWORD_SIM_PUK2]); } else { ofono_error("%s: wrong format", __func__); goto no_data; } break; case OFONO_RIL_VENDOR_INFINEON: ofono_error("%s: infineon type should not arrive here", __func__); g_assert(FALSE); break; } if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); goto no_data; } g_ril_print_response(gril, message); return retries; no_data: g_free(retries); return NULL; } void g_ril_reply_free_oem_hook(struct reply_oem_hook *oem_hook) { if (oem_hook) { g_free(oem_hook->data); g_free(oem_hook); } } struct reply_oem_hook *g_ril_reply_oem_hook_raw(GRil *gril, const struct ril_msg *message) { struct reply_oem_hook *reply = NULL; struct parcel rilp; reply = g_try_malloc0(sizeof(*reply)); if (reply == NULL) { ofono_error("%s: out of memory", __func__); goto end; } g_ril_init_parcel(message, &rilp); reply->data = parcel_r_raw(&rilp, &(reply->length)); if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); g_ril_reply_free_oem_hook(reply); reply = NULL; goto end; } g_ril_append_print_buf(gril, "{%d", reply->length); if (reply->data != NULL) { char *hex_dump; hex_dump = encode_hex(reply->data, reply->length, '\0'); g_ril_append_print_buf(gril, "%s,%s", print_buf, hex_dump); g_free(hex_dump); } g_ril_append_print_buf(gril, "%s}", print_buf); g_ril_print_response(gril, message); end: return reply; } struct parcel_str_array *g_ril_reply_oem_hook_strings(GRil *gril, const struct ril_msg *message) { struct parcel rilp; struct parcel_str_array *str_arr; int i; g_ril_init_parcel(message, &rilp); str_arr = parcel_r_str_array(&rilp); if (str_arr == NULL) { ofono_error("%s: no strings", __func__); goto out; } g_ril_append_print_buf(gril, "{"); for (i = 0; i < str_arr->num_str; ++i) { if (i + 1 == str_arr->num_str) g_ril_append_print_buf(gril, "%s%s}", print_buf, str_arr->str[i]); else g_ril_append_print_buf(gril, "%s%s, ", print_buf, str_arr->str[i]); } g_ril_print_response(gril, message); out: return str_arr; } ofono-1.17.bzr6912+16.04.20160314.3/gril/ril_constants.h0000644000015600001650000004002012671500073022367 0ustar pbuserpbgroup00000000000000/* * * RIL constants adopted from AOSP's header: * * /hardware/ril/reference_ril/ril.h * * Copyright (C) 2013 Canonical 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. * * 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 * */ #ifndef __RIL_CONSTANTS_H #define __RIL_CONSTANTS_H 1 #define RIL_VERSION_UNSPECIFIED 0 /* 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 /* * Following error codes are actually Qualcomm-specific, but as they are used by * our reference platform, we consider them valid for vendor * OFONO_RIL_VENDOR_AOSP. The definition comes from cyanogenmod ril.h, which in * turn copied it from codeaurora. */ #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 /* Preferred network types */ #define PREF_NET_TYPE_GSM_WCDMA 0 #define PREF_NET_TYPE_GSM_ONLY 1 #define PREF_NET_TYPE_WCDMA 2 #define PREF_NET_TYPE_GSM_WCDMA_AUTO 3 #define PREF_NET_TYPE_CDMA_EVDO_AUTO 4 #define PREF_NET_TYPE_CDMA_ONLY 5 #define PREF_NET_TYPE_EVDO_ONLY 6 #define PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO 7 #define PREF_NET_TYPE_LTE_CDMA_EVDO 8 #define PREF_NET_TYPE_LTE_GSM_WCDMA 9 #define PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA 10 #define PREF_NET_TYPE_LTE_ONLY 11 #define PREF_NET_TYPE_LTE_WCDMA 12 /* MTK specific network types */ #define MTK_PREF_NET_TYPE_BASE 30 #define MTK_PREF_NET_TYPE_LTE_GSM_WCDMA (MTK_PREF_NET_TYPE_BASE + 1) #define MTK_PREF_NET_TYPE_LTE_GSM_WCDMA_MMDC (MTK_PREF_NET_TYPE_BASE + 2) #define MTK_PREF_NET_TYPE_GSM_WCDMA_LTE (MTK_PREF_NET_TYPE_BASE + 3) #define MTK_PREF_NET_TYPE_GSM_WCDMA_LTE_MMDC (MTK_PREF_NET_TYPE_BASE + 4) #define MTK_PREF_NET_TYPE_LTE_GSM_TYPE (MTK_PREF_NET_TYPE_BASE + 5) #define MTK_PREF_NET_TYPE_LTE_GSM_MMDC_TYPE (MTK_PREF_NET_TYPE_BASE + 6) /* * Data Call Failure causes ( see TS 24.008 ) * section 6.1.3.1.3 or TS 24.301 Release 8+ Annex B. */ #define PDP_FAIL_NONE 0 #define PDP_FAIL_OPERATOR_BARRED 0x08 #define PDP_FAIL_INSUFFICIENT_RESOURCES 0x1A #define PDP_FAIL_MISSING_UKNOWN_APN 0x1B #define PDP_FAIL_UNKNOWN_PDP_ADDRESS_TYPE 0x1C #define PDP_FAIL_USER_AUTHENTICATION 0x1D #define PDP_FAIL_ACTIVATION_REJECT_GGSN 0x1E #define PDP_FAIL_ACTIVATION_REJECT_UNSPECIFIED 0x1F #define PDP_FAIL_SERVICE_OPTION_NOT_SUPPORTED 0x20 #define PDP_FAIL_SERVICE_OPTION_NOT_SUBSCRIBED 0x21 #define PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER 0x22 #define PDP_FAIL_NSAPI_IN_USE 0x23 #define PDP_FAIL_REGULAR_DEACTIVATION 0x24 /* restart radio */ #define PDP_FAIL_ONLY_IPV4_ALLOWED 0x32 #define PDP_FAIL_ONLY_IPV6_ALLOWED 0x33 #define PDP_FAIL_ONLY_SINGLE_BEARER_ALLOWED 0x34 #define PDP_FAIL_PROTOCOL_ERRORS 0x6F #define PDP_FAIL_VOICE_REGISTRATION_FAIL -1 #define PDP_FAIL_DATA_REGISTRATION_FAIL -2 #define PDP_FAIL_SIGNAL_LOST -3 #define PDP_FAIL_PREF_RADIO_TECH_CHANGED -4 #define PDP_FAIL_RADIO_POWER_OFF -5 #define PDP_FAIL_TETHERED_CALL_ACTIVE -6 #define PDP_FAIL_ERROR_UNSPECIFIED 0xffff /* Radio States */ #define RADIO_STATE_OFF 0 #define RADIO_STATE_UNAVAILABLE 1 #define RADIO_STATE_ON 10 /* Deprecated, but still used by some modems */ #define RADIO_STATE_SIM_NOT_READY 2 #define RADIO_STATE_SIM_LOCKED_OR_ABSENT 3 #define RADIO_STATE_SIM_READY 4 /* Radio technologies */ #define RADIO_TECH_UNKNOWN 0 #define RADIO_TECH_GPRS 1 #define RADIO_TECH_EDGE 2 #define RADIO_TECH_UMTS 3 #define RADIO_TECH_IS95A 4 #define RADIO_TECH_IS95B 5 #define RADIO_TECH_1xRTT 6 #define RADIO_TECH_EVDO_0 7 #define RADIO_TECH_EVDO_A 8 #define RADIO_TECH_HSDPA 9 #define RADIO_TECH_HSUPA 10 #define RADIO_TECH_HSPA 11 #define RADIO_TECH_EVDO_B 12 #define RADIO_TECH_EHRPD 13 #define RADIO_TECH_LTE 14 #define RADIO_TECH_HSPAP 15 #define RADIO_TECH_GSM 16 /* MTK specific values for radio technologies */ #define MTK_RADIO_TECH_BASE 128 #define MTK_RADIO_TECH_HSDPAP (MTK_RADIO_TECH_BASE + 1) #define MTK_RADIO_TECH_HSDPAP_UPA (MTK_RADIO_TECH_BASE + 2) #define MTK_RADIO_TECH_HSUPAP (MTK_RADIO_TECH_BASE + 3) #define MTK_RADIO_TECH_HSUPAP_DPA (MTK_RADIO_TECH_BASE + 4) #define MTK_RADIO_TECH_DC_DPA (MTK_RADIO_TECH_BASE + 5) #define MTK_RADIO_TECH_DC_UPA (MTK_RADIO_TECH_BASE + 6) #define MTK_RADIO_TECH_DC_HSDPAP (MTK_RADIO_TECH_BASE + 7) #define MTK_RADIO_TECH_DC_HSDPAP_UPA (MTK_RADIO_TECH_BASE + 8) #define MTK_RADIO_TECH_DC_HSDPAP_DPA (MTK_RADIO_TECH_BASE + 9) #define MTK_RADIO_TECH_DC_HSPAP (MTK_RADIO_TECH_BASE + 10) /* 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 /* see RIL_REQUEST_DEACTIVATE_DATA_CALL parameter*/ #define RIL_DEACTIVATE_DATA_CALL_NO_REASON 0 #define RIL_DEACTIVATE_DATA_CALL_RADIO_SHUTDOWN 1 /* See RIL_REQUEST_SETUP_DATA_CALL */ #define RIL_DATA_PROFILE_DEFAULT 0 #define RIL_DATA_PROFILE_TETHERED 1 #define RIL_DATA_PROFILE_IMS 2 #define RIL_DATA_PROFILE_FOTA 3 /* FOTA = Firmware Over the Air */ #define RIL_DATA_PROFILE_CBS 4 #define RIL_DATA_PROFILE_OEM_BASE 1000 /* Start of OEM-specific profiles */ /* MTK specific profile for MMS */ #define RIL_DATA_PROFILE_MTK_MMS (RIL_DATA_PROFILE_OEM_BASE + 1) /* * auth type -1 seems to mean 0 (RIL_AUTH_NONE) if no user/password is * specified or 3 (RIL_AUTH_BOTH) otherwise. See $ANDROID/packages/ * providers/TelephonyProvider/src/com/android/providers/telephony/ * TelephonyProvider.java. */ #define RIL_AUTH_ANY -1 #define RIL_AUTH_NONE 0 #define RIL_AUTH_PAP 1 #define RIL_AUTH_CHAP 2 #define RIL_AUTH_BOTH 3 /* SIM card states */ #define RIL_CARDSTATE_ABSENT 0 #define RIL_CARDSTATE_PRESENT 1 #define RIL_CARDSTATE_ERROR 2 /* SIM - App states */ #define RIL_APPSTATE_UNKNOWN 0 #define RIL_APPSTATE_DETECTED 1 #define RIL_APPSTATE_PIN 2 #define RIL_APPSTATE_PUK 3 #define RIL_APPSTATE_SUBSCRIPTION_PERSO 4 #define RIL_APPSTATE_READY 5 /* SIM - PIN states */ #define RIL_PINSTATE_UNKNOWN 0 #define RIL_PINSTATE_ENABLED_NOT_VERIFIED 1 #define RIL_PINSTATE_ENABLED_VERIFIED 2 #define RIL_PINSTATE_DISABLED 3 #define RIL_PINSTATE_ENABLED_BLOCKED 4 #define RIL_PINSTATE_ENABLED_PERM_BLOCKED 5 /* SIM - App types */ #define RIL_APPTYPE_UNKNOWN 0 #define RIL_APPTYPE_SIM 1 #define RIL_APPTYPE_USIM 2 #define RIL_APPTYPE_RUIM 3 #define RIL_APPTYPE_CSIM 4 #define RIL_APPTYPE_ISIM 5 /* SIM - PersoSubstate */ #define RIL_PERSOSUBSTATE_UNKNOWN 0 #define RIL_PERSOSUBSTATE_IN_PROGRESS 1 #define RIL_PERSOSUBSTATE_READY 2 #define RIL_PERSOSUBSTATE_SIM_NETWORK 3 #define RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET 4 #define RIL_PERSOSUBSTATE_SIM_CORPORATE 5 #define RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER 6 #define RIL_PERSOSUBSTATE_SIM_SIM 7 #define RIL_PERSOSUBSTATE_SIM_NETWORK_PUK 8 #define RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK 9 #define RIL_PERSOSUBSTATE_SIM_CORPORATE_PUK 10 #define RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK 11 #define RIL_PERSOSUBSTATE_SIM_SIM_PUK 12 #define RIL_PERSOSUBSTATE_RUIM_NETWORK1 13 #define RIL_PERSOSUBSTATE_RUIM_NETWORK2 14 #define RIL_PERSOSUBSTATE_RUIM_HRPD 15 #define RIL_PERSOSUBSTATE_RUIM_CORPORATE 16 #define RIL_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER 17 #define RIL_PERSOSUBSTATE_RUIM_RUIM 18 #define RIL_PERSOSUBSTATE_RUIM_NETWORK1_PUK 19 #define RIL_PERSOSUBSTATE_RUIM_NETWORK2_PUK 20 #define RIL_PERSOSUBSTATE_RUIM_HRPD_PUK 21 #define RIL_PERSOSUBSTATE_RUIM_CORPORATE_PUK 22 #define RIL_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK 23 #define RIL_PERSOSUBSTATE_RUIM_RUIM_PUK 24 /* 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_WAITING_OR_HOLDING_AND_ACTIVE 15 #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_SET_INITIAL_ATTACH_APN 111 /* 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 /* Suplementary services Service class*/ #define SERVICE_CLASS_NONE 0 /* Network registration states */ #define RIL_REG_STATE_NOT_REGISTERED 0 #define RIL_REG_STATE_REGISTERED 1 #define RIL_REG_STATE_SEARCHING 2 #define RIL_REG_STATE_DENIED 3 #define RIL_REG_STATE_UNKNOWN 4 #define RIL_REG_STATE_ROAMING 5 #define RIL_REG_STATE_EMERGENCY_NOT_REGISTERED 10 #define RIL_REG_STATE_EMERGENCY_SEARCHING 12 #define RIL_REG_STATE_EMERGENCY_DENIED 13 #define RIL_REG_STATE_EMERGENCY_UNKNOWN 14 #endif /*__RIL_CONSTANTS_H*/ ofono-1.17.bzr6912+16.04.20160314.3/gril/grilunsol.c0000644000015600001650000003646212671500073021535 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2013 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include "util.h" #include "common.h" #include "grilunsol.h" /* Minimum size is two int32s version/number of calls */ #define MIN_DATA_CALL_LIST_SIZE 8 /* * Minimum NITZ is: 'yy/mm/dd,hh:mm:ss' * TZ '(+/-)tz,dt' are optional */ #define MIN_NITZ_SIZE 17 int g_ril_unsol_parse_connected(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int size; int version; DBG(""); g_ril_init_parcel(message, &rilp); size = parcel_r_int32(&rilp); version = parcel_r_int32(&rilp); if (rilp.malformed) { ofono_error("%s: malformed parcel", __func__); version = RIL_VERSION_UNSPECIFIED; } g_ril_append_print_buf(gril, "{size:%d, [%d, ...]}", size, version); g_ril_print_unsol(gril, message); return version; } static gint data_call_compare(gconstpointer a, gconstpointer b) { const struct ril_data_call *ca = a; const struct ril_data_call *cb = b; if (ca->cid < cb->cid) return -1; if (ca->cid > cb->cid) return 1; return 0; } static void free_data_call(gpointer data, gpointer user_data) { struct ril_data_call *call = data; if (call) { g_free(call->ifname); g_free(call->ip_addr); g_free(call->dns_addrs); g_free(call->gateways); g_free(call); } } void g_ril_unsol_free_data_call_list(struct ril_data_call_list *call_list) { if (call_list) { g_slist_foreach(call_list->calls, (GFunc) free_data_call, NULL); g_slist_free(call_list->calls); g_free(call_list); } } static gboolean handle_settings(struct ril_data_call *call, char *type, char *ifname, char *raw_ip_addrs, char *raw_dns, char *raw_gws) { gboolean result = FALSE; int protocol; char **dns_addrs = NULL, **gateways = NULL; char **ip_addrs = NULL, **split_ip_addr = NULL; protocol = ril_protocol_string_to_ofono_protocol(type); if (protocol < 0) { ofono_error("%s: invalid type(protocol) specified: %s", __func__, type); goto done; } if (ifname == NULL || strlen(ifname) == 0) { ofono_error("%s: no interface specified: %s", __func__, ifname); goto done; } /* Split DNS addresses */ if (raw_dns) dns_addrs = g_strsplit(raw_dns, " ", 3); /* * RILD can return multiple addresses; oFono only supports * setting a single IPv4 gateway. */ if (raw_gws) gateways = g_strsplit(raw_gws, " ", 3); if (gateways == NULL || g_strv_length(gateways) == 0) { ofono_error("%s: no gateways: %s", __func__, raw_gws); goto done; } /* TODO: * RILD can return multiple addresses; oFono only supports * setting a single IPv4 address. At this time, we only * use the first address. It's possible that a RIL may * just specify the end-points of the point-to-point * connection, in which case this code will need to * changed to handle such a device. * * For now split into a maximum of three, and only use * the first address for the remaining operations. */ if (raw_ip_addrs) ip_addrs = g_strsplit(raw_ip_addrs, " ", 3); if (ip_addrs == NULL || g_strv_length(ip_addrs) == 0) { ofono_error("%s: no IP address: %s", __func__, raw_ip_addrs); goto done; } DBG("num ip addrs is: %d", g_strv_length(ip_addrs)); if (g_strv_length(ip_addrs) > 1) ofono_warn("%s: more than one IP addr returned: %s", __func__, raw_ip_addrs); /* * Note - the address may optionally include a prefix size * ( Eg. "/30" ). As this confuses NetworkManager, we * explicitly strip any prefix after calculating the netmask. */ split_ip_addr = g_strsplit(ip_addrs[0], "/", 2); if (split_ip_addr == NULL || g_strv_length(split_ip_addr) == 0) { ofono_error("%s: invalid IP address field returned: %s", __func__, ip_addrs[0]); goto done; } call->protocol = protocol; call->ifname = g_strdup(ifname); call->ip_addr = g_strdup(split_ip_addr[0]); call->dns_addrs = g_strdupv(dns_addrs); call->gateways = g_strdupv(gateways); result = TRUE; done: if (dns_addrs) g_strfreev(dns_addrs); if (gateways) g_strfreev(gateways); if (ip_addrs) g_strfreev(ip_addrs); if (split_ip_addr) g_strfreev(split_ip_addr); return result; } /* * This function handles RIL_UNSOL_DATA_CALL_LIST_CHANGED messages, * as well as RIL_REQUEST_DATA_CALL_LIST/SETUP_DATA_CALL replies, as * all have the same payload. */ struct ril_data_call_list *g_ril_unsol_parse_data_call_list(GRil *gril, const struct ril_msg *message) { struct ril_data_call *call; struct parcel rilp; struct ril_data_call_list *reply = NULL; unsigned int active, cid, i, num_calls, retry, status; char *type = NULL, *ifname = NULL, *raw_addrs = NULL; char *raw_dns = NULL, *raw_gws = NULL; DBG(""); /* Can happen for RIL_REQUEST_DATA_CALL_LIST replies */ if (message->buf_len < MIN_DATA_CALL_LIST_SIZE) { if (message->req == RIL_REQUEST_SETUP_DATA_CALL) { ofono_error("%s: message too small: %d", __func__, (int) message->buf_len); goto error; } else { g_ril_append_print_buf(gril, "{"); goto done; } } reply = g_try_new0(struct ril_data_call_list, 1); if (reply == NULL) { ofono_error("%s: out of memory", __func__); goto error; } g_ril_init_parcel(message, &rilp); /* * ril.h documents the reply to a RIL_REQUEST_DATA_CALL_LIST * as being an array of RIL_Data_Call_Response_v6 structs, * however in reality, the response also includes a version * to start. */ reply->version = parcel_r_int32(&rilp); num_calls = parcel_r_int32(&rilp); g_ril_append_print_buf(gril, "{version=%d,num=%d", reply->version, num_calls); for (i = 0; i < num_calls; i++) { status = parcel_r_int32(&rilp); retry = parcel_r_int32(&rilp); /* ignore */ cid = parcel_r_int32(&rilp); active = parcel_r_int32(&rilp); type = parcel_r_string(&rilp); ifname = parcel_r_string(&rilp); raw_addrs = parcel_r_string(&rilp); raw_dns = parcel_r_string(&rilp); raw_gws = parcel_r_string(&rilp); /* malformed check */ if (rilp.malformed) { ofono_error("%s: malformed parcel received", __func__); goto error; } g_ril_append_print_buf(gril, "%s [status=%d,retry=%d,cid=%d," "active=%d,type=%s,ifname=%s," "address=%s,dns=%s,gateways=%s]", print_buf, status, retry, cid, active, type, ifname, raw_addrs, raw_dns, raw_gws); call = g_try_new0(struct ril_data_call, 1); if (call == NULL) { ofono_error("%s: out of memory", __func__); goto error; } call->status = status; call->cid = cid; call->active = active; if (message->req == RIL_REQUEST_SETUP_DATA_CALL && status == PDP_FAIL_NONE && handle_settings(call, type, ifname, raw_addrs, raw_dns, raw_gws) == FALSE) goto error; g_free(type); g_free(ifname); g_free(raw_addrs); g_free(raw_dns); g_free(raw_gws); reply->calls = g_slist_insert_sorted(reply->calls, call, data_call_compare); } done: g_ril_append_print_buf(gril, "%s}", print_buf); if (message->unsolicited) g_ril_print_unsol(gril, message); else g_ril_print_response(gril, message); return reply; error: g_free(type); g_free(ifname); g_free(raw_addrs); g_free(raw_dns); g_free(raw_gws); g_ril_unsol_free_data_call_list(reply); return NULL; } char *g_ril_unsol_parse_nitz(GRil *gril, const struct ril_msg *message) { struct parcel rilp; gchar *nitz = NULL; DBG(""); if (message->buf_len < MIN_NITZ_SIZE) { ofono_error("%s: NITZ too small: %d", __func__, (int) message->buf_len); goto error; } g_ril_init_parcel(message, &rilp); nitz = parcel_r_string(&rilp); g_ril_append_print_buf(gril, "(%s)", nitz); g_ril_print_unsol(gril, message); error: return nitz; } void g_ril_unsol_free_sms_data(struct unsol_sms_data *unsol) { if (unsol != NULL) { g_free(unsol->data); g_free(unsol); } } struct unsol_sms_data *g_ril_unsol_parse_new_sms(GRil *gril, const struct ril_msg *message) { struct parcel rilp; char *ril_pdu; size_t ril_pdu_len; struct unsol_sms_data *sms_data; sms_data = g_new0(struct unsol_sms_data, 1); if (sms_data == NULL) { ofono_error("%s out of memory", __func__); goto error; } g_ril_init_parcel(message, &rilp); ril_pdu = parcel_r_string(&rilp); if (ril_pdu == NULL) { ofono_error("%s Unable to parse notification", __func__); goto error; } ril_pdu_len = strlen(ril_pdu); sms_data->data = decode_hex(ril_pdu, ril_pdu_len, &sms_data->length, -1); if (sms_data->data == NULL) { ofono_error("%s Unable to decode notification", __func__); goto error_dec; } g_ril_append_print_buf(gril, "{%s}", ril_pdu); g_ril_print_unsol(gril, message); g_free(ril_pdu); return sms_data; error_dec: g_free(ril_pdu); error: g_ril_unsol_free_sms_data(sms_data); return NULL; } int g_ril_unsol_parse_radio_state_changed(GRil *gril, const struct ril_msg *message) { struct parcel rilp; int radio_state; g_ril_init_parcel(message, &rilp); radio_state = parcel_r_int32(&rilp); if (rilp.malformed) { ofono_error("%s: malformed parcel received", __func__); radio_state = -1; } g_ril_append_print_buf(gril, "(state: %s)", ril_radio_state_to_string(radio_state)); g_ril_print_unsol(gril, message); return radio_state; } /* * This function makes a similar processing to was is done by validateInput() * and getLteLevel() in $AOSP/frameworks/base/telephony/java/android/telephony/ * SignalStrength.java. The main difference is that we linearly transform the * ranges to ofono's one, while AOSP gives number of bars in a non-linear way * (bins for each bar have different size). We rely on the indicator to obtain * a translation to bars that makes sense for humans. */ static int get_lte_strength(int signal, int rsrp, int rssnr) { int s_rsrp = -1, s_rssnr = -1, s_signal = -1; /* * The range of signal is specified to be [0, 31] by ril.h, but the code * in SignalStrength.java contradicts this: valid values are (0-63, 99) * as defined in TS 36.331 for E-UTRA rssi. */ signal = (signal >= 0 && signal <= 63) ? signal : INT_MAX; rsrp = (rsrp >= 44 && rsrp <= 140) ? -rsrp : INT_MAX; rssnr = (rssnr >= -200 && rssnr <= 300) ? rssnr : INT_MAX; /* Linearly transform [-140, -44] to [0, 100] */ if (rsrp != INT_MAX) s_rsrp = (25 * rsrp + 3500) / 24; /* Linearly transform [-200, 300] to [0, 100] */ if (rssnr != INT_MAX) s_rssnr = (rssnr + 200) / 5; if (s_rsrp != -1 && s_rssnr != -1) return s_rsrp < s_rssnr ? s_rsrp : s_rssnr; if (s_rssnr != -1) return s_rssnr; if (s_rsrp != -1) return s_rsrp; /* Linearly transform [0, 63] to [0, 100] */ if (signal != INT_MAX) s_signal = (100 * signal) / 63; return s_signal; } /* * Comments to get_lte_strength() apply here also, changing getLteLevel() with * getGsmLevel(). The atmodem driver does exactly the same transformation with * the rssi from AT+CSQ command. */ static int get_gsm_strength(int signal) { /* Checking the range contemplates also the case signal=99 (invalid) */ if (signal >= 0 && signal <= 31) return (signal * 100) / 31; else return -1; } int g_ril_unsol_parse_signal_strength(GRil *gril, const struct ril_msg *message, int ril_tech) { struct parcel rilp; int gw_sigstr, gw_signal, cdma_dbm, evdo_dbm; int lte_sigstr = -1, lte_rsrp = -1, lte_rssnr = -1; int lte_signal; int signal; g_ril_init_parcel(message, &rilp); /* RIL_SignalStrength_v5 */ /* GW_SignalStrength */ gw_sigstr = parcel_r_int32(&rilp); gw_signal = get_gsm_strength(gw_sigstr); parcel_r_int32(&rilp); /* bitErrorRate */ /* * CDMA/EVDO values are not processed as CDMA is not supported */ /* CDMA_SignalStrength */ cdma_dbm = parcel_r_int32(&rilp); parcel_r_int32(&rilp); /* ecio */ /* EVDO_SignalStrength */ evdo_dbm = parcel_r_int32(&rilp); parcel_r_int32(&rilp); /* ecio */ parcel_r_int32(&rilp); /* signalNoiseRatio */ /* Present only for RIL_SignalStrength_v6 or newer */ if (parcel_data_avail(&rilp) > 0) { /* LTE_SignalStrength */ lte_sigstr = parcel_r_int32(&rilp); lte_rsrp = parcel_r_int32(&rilp); parcel_r_int32(&rilp); /* rsrq */ lte_rssnr = parcel_r_int32(&rilp); parcel_r_int32(&rilp); /* cqi */ lte_signal = get_lte_strength(lte_sigstr, lte_rsrp, lte_rssnr); } else { lte_signal = -1; } g_ril_append_print_buf(gril, "{gw: %d, cdma: %d, evdo: %d, lte: %d %d %d}", gw_sigstr, cdma_dbm, evdo_dbm, lte_sigstr, lte_rsrp, lte_rssnr); if (message->unsolicited) g_ril_print_unsol(gril, message); else g_ril_print_response(gril, message); /* Return the first valid one */ if (gw_signal != -1 && lte_signal != -1) if (ril_tech == RADIO_TECH_LTE) signal = lte_signal; else signal = gw_signal; else if (gw_signal != -1) signal = gw_signal; else if (lte_signal != -1) signal = lte_signal; else signal = -1; return signal; } void g_ril_unsol_free_supp_svc_notif(struct unsol_supp_svc_notif *unsol) { g_free(unsol); } struct unsol_supp_svc_notif *g_ril_unsol_parse_supp_svc_notif(GRil *gril, struct ril_msg *message) { struct parcel rilp; char *tmp_number; int type; struct unsol_supp_svc_notif *unsol = g_new0(struct unsol_supp_svc_notif, 1); g_ril_init_parcel(message, &rilp); unsol->notif_type = parcel_r_int32(&rilp); unsol->code = parcel_r_int32(&rilp); unsol->index = parcel_r_int32(&rilp); type = parcel_r_int32(&rilp); tmp_number = parcel_r_string(&rilp); if (tmp_number != NULL) { strncpy(unsol->number.number, tmp_number, OFONO_MAX_PHONE_NUMBER_LENGTH); unsol->number.type = type; g_free(tmp_number); } g_ril_append_print_buf(gril, "{%d,%d,%d,%d,%s}", unsol->notif_type, unsol->code, unsol->index, type, tmp_number); g_ril_print_unsol(gril, message); return unsol; } void g_ril_unsol_free_ussd(struct unsol_ussd *unsol) { if (unsol != NULL) { g_free(unsol->message); g_free(unsol); } } struct unsol_ussd *g_ril_unsol_parse_ussd(GRil *gril, struct ril_msg *message) { struct parcel rilp; struct unsol_ussd *ussd; char *typestr = NULL; int numstr; ussd = g_try_malloc0(sizeof(*ussd)); if (ussd == NULL) { ofono_error("%s out of memory", __func__); goto error; } g_ril_init_parcel(message, &rilp); numstr = parcel_r_int32(&rilp); if (numstr < 1) { ofono_error("%s malformed parcel", __func__); goto error; } typestr = parcel_r_string(&rilp); if (typestr == NULL || *typestr == '\0') { ofono_error("%s wrong type", __func__); goto error; } ussd->type = *typestr - '0'; g_free(typestr); if (numstr > 1) ussd->message = parcel_r_string(&rilp); g_ril_append_print_buf(gril, "{%d,%s}", ussd->type, ussd->message); g_ril_print_unsol(gril, message); return ussd; error: g_free(typestr); g_free(ussd); return NULL; } ofono-1.17.bzr6912+16.04.20160314.3/gril/grilio.c0000644000015600001650000002051012671500024020763 0ustar pbuserpbgroup00000000000000/* * * RIL chat library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include "ringbuffer.h" #include "grilio.h" #include "grilutil.h" struct _GRilIO { gint ref_count; /* Ref count */ guint read_watch; /* GSource read id, 0 if no */ guint write_watch; /* GSource write id, 0 if no */ GIOChannel *channel; /* comms channel */ GRilDisconnectFunc user_disconnect; /* user disconnect func */ gpointer user_disconnect_data; /* user disconnect data */ struct ring_buffer *buf; /* Current read buffer */ guint max_read_attempts; /* max reads / select */ GRilIOReadFunc read_handler; /* Read callback */ gpointer read_data; /* Read callback userdata */ gboolean use_write_watch; /* Use write select */ GRilIOWriteFunc write_handler; /* Write callback */ gpointer write_data; /* Write callback userdata */ GRilDebugFunc debugf; /* debugging output function */ gpointer debug_data; /* Data to pass to debug func */ GRilDisconnectFunc write_done_func; /* tx empty notifier */ gpointer write_done_data; /* tx empty data */ gboolean destroyed; /* Re-entrancy guard */ }; static void read_watcher_destroy_notify(gpointer user_data) { GRilIO *io = user_data; ring_buffer_free(io->buf); io->buf = NULL; io->debugf = NULL; io->debug_data = NULL; io->read_watch = 0; io->read_handler = NULL; io->read_data = NULL; io->channel = NULL; if (io->destroyed) g_free(io); else if (io->user_disconnect) io->user_disconnect(io->user_disconnect_data); } static gboolean received_data(GIOChannel *channel, GIOCondition cond, gpointer data) { unsigned char *buf; GRilIO *io = data; GIOStatus status; gsize rbytes; gsize toread; gsize total_read = 0; guint read_count = 0; if (cond & G_IO_NVAL) return FALSE; /* Regardless of condition, try to read all the data available */ do { toread = ring_buffer_avail_no_wrap(io->buf); if (toread == 0) break; rbytes = 0; buf = ring_buffer_write_ptr(io->buf, 0); status = g_io_channel_read_chars(channel, (char *) buf, toread, &rbytes, NULL); g_ril_util_debug_hexdump(TRUE, (guchar *) buf, rbytes, io->debugf, io->debug_data); read_count++; total_read += rbytes; if (rbytes > 0) ring_buffer_write_advance(io->buf, rbytes); } while (status == G_IO_STATUS_NORMAL && rbytes > 0 && read_count < io->max_read_attempts); if (total_read > 0 && io->read_handler) io->read_handler(io->buf, io->read_data); if (cond & (G_IO_HUP | G_IO_ERR)) return FALSE; if (read_count > 0 && rbytes == 0 && status != G_IO_STATUS_AGAIN) return FALSE; /* We're overflowing the buffer, shutdown the socket */ if (ring_buffer_avail(io->buf) == 0) return FALSE; return TRUE; } gsize g_ril_io_write(GRilIO *io, const gchar *data, gsize count) { GIOStatus status; gsize bytes_written; status = g_io_channel_write_chars(io->channel, data, count, &bytes_written, NULL); if (status != G_IO_STATUS_NORMAL) { g_source_remove(io->read_watch); return 0; } g_ril_util_debug_hexdump(FALSE, (guchar *) data, bytes_written, io->debugf, io->debug_data); return bytes_written; } static void write_watcher_destroy_notify(gpointer user_data) { GRilIO *io = user_data; io->write_watch = 0; io->write_handler = NULL; io->write_data = NULL; if (io->write_done_func) { io->write_done_func(io->write_done_data); io->write_done_func = NULL; io->write_done_data = NULL; } } static gboolean can_write_data(GIOChannel *channel, GIOCondition cond, gpointer data) { GRilIO *io = data; if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) return FALSE; if (io->write_handler == NULL) return FALSE; return io->write_handler(io->write_data); } static GRilIO *create_io(GIOChannel *channel, GIOFlags flags) { GRilIO *io; if (channel == NULL) return NULL; io = g_try_new0(GRilIO, 1); if (io == NULL) return io; io->ref_count = 1; io->debugf = NULL; if (flags & G_IO_FLAG_NONBLOCK) { io->max_read_attempts = 3; io->use_write_watch = TRUE; } else { io->max_read_attempts = 1; io->use_write_watch = FALSE; } io->buf = ring_buffer_new(GRIL_BUFFER_SIZE); if (!io->buf) goto error; if (!g_ril_util_setup_io(channel, flags)) goto error; io->channel = channel; io->read_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, received_data, io, read_watcher_destroy_notify); return io; error: if (io->buf) ring_buffer_free(io->buf); g_free(io); return NULL; } GRilIO *g_ril_io_new(GIOChannel *channel) { return create_io(channel, G_IO_FLAG_NONBLOCK); } GRilIO *g_ril_io_new_blocking(GIOChannel *channel) { return create_io(channel, 0); } GIOChannel *g_ril_io_get_channel(GRilIO *io) { if (io == NULL) return NULL; return io->channel; } gboolean g_ril_io_set_read_handler(GRilIO *io, GRilIOReadFunc read_handler, gpointer user_data) { if (io == NULL) return FALSE; io->read_handler = read_handler; io->read_data = user_data; if (read_handler && ring_buffer_len(io->buf) > 0) read_handler(io->buf, user_data); return TRUE; } static gboolean call_blocking_read(gpointer user_data) { GRilIO *io = user_data; while (can_write_data(io->channel, G_IO_OUT, io) == TRUE) ; write_watcher_destroy_notify(io); return FALSE; } gboolean g_ril_io_set_write_handler(GRilIO *io, GRilIOWriteFunc write_handler, gpointer user_data) { if (io == NULL) return FALSE; if (io->write_watch > 0) { if (write_handler == NULL) { g_source_remove(io->write_watch); return TRUE; } return FALSE; } if (write_handler == NULL) return FALSE; io->write_handler = write_handler; io->write_data = user_data; if (io->use_write_watch == TRUE) io->write_watch = g_io_add_watch_full(io->channel, G_PRIORITY_HIGH, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, can_write_data, io, write_watcher_destroy_notify); else io->write_watch = g_idle_add(call_blocking_read, io); return TRUE; } GRilIO *g_ril_io_ref(GRilIO *io) { if (io == NULL) return NULL; g_atomic_int_inc(&io->ref_count); return io; } static gboolean io_shutdown(GRilIO *io) { /* Don't trigger user disconnect on shutdown */ io->user_disconnect = NULL; io->user_disconnect_data = NULL; if (io->read_watch > 0) g_source_remove(io->read_watch); if (io->write_watch > 0) g_source_remove(io->write_watch); return TRUE; } void g_ril_io_unref(GRilIO *io) { gboolean is_zero; if (io == NULL) return; is_zero = g_atomic_int_dec_and_test(&io->ref_count); if (is_zero == FALSE) return; io_shutdown(io); /* glib delays the destruction of the watcher until it exits, this * means we can't free the data just yet, even though we've been * destroyed already. We have to wait until the read_watcher * destroy function gets called */ if (io->read_watch > 0) io->destroyed = TRUE; else g_free(io); } gboolean g_ril_io_set_disconnect_function(GRilIO *io, GRilDisconnectFunc disconnect, gpointer user_data) { if (io == NULL) return FALSE; io->user_disconnect = disconnect; io->user_disconnect_data = user_data; return TRUE; } gboolean g_ril_io_set_debug(GRilIO *io, GRilDebugFunc func, gpointer user_data) { if (io == NULL) return FALSE; io->debugf = func; io->debug_data = user_data; return TRUE; } void g_ril_io_set_write_done(GRilIO *io, GRilDisconnectFunc func, gpointer user_data) { if (io == NULL) return; io->write_done_func = func; io->write_done_data = user_data; } void g_ril_io_drain_ring_buffer(GRilIO *io, guint len) { ring_buffer_drain(io->buf, len); } ofono-1.17.bzr6912+16.04.20160314.3/gril/grilrequest.c0000644000015600001650000006761212671500024022062 0ustar pbuserpbgroup00000000000000/* * * RIL library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2012-2014 Canonical Ltd. * Copyright (C) 2015 Ratchanan Srirattanamet. * * 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 #endif #include #include #include #include #include #include #include #include #include "grilrequest.h" #include "simutil.h" #include "util.h" #include "common.h" /* DEACTIVATE_DATA_CALL request parameters */ #define DEACTIVATE_DATA_CALL_NUM_PARAMS 2 /* POWER request parameters */ #define POWER_PARAMS 1 /* SETUP_DATA_CALL_PARAMS request parameters */ #define SETUP_DATA_CALL_PARAMS 7 #define DATA_PROFILE_DEFAULT_STR "0" #define DATA_PROFILE_TETHERED_STR "1" #define DATA_PROFILE_IMS_STR "2" #define DATA_PROFILE_FOTA_STR "3" #define DATA_PROFILE_CBS_STR "4" #define DATA_PROFILE_OEM_BASE_STR "1000" #define DATA_PROFILE_MTK_MMS_STR "1001" /* SETUP_DATA_CALL_PARAMS reply parameters */ #define MIN_DATA_CALL_REPLY_SIZE 36 /* Commands defined for TS 27.007 +CRSM */ #define CMD_READ_BINARY 176 /* 0xB0 */ #define CMD_READ_RECORD 178 /* 0xB2 */ #define CMD_GET_RESPONSE 192 /* 0xC0 */ #define CMD_UPDATE_BINARY 214 /* 0xD6 */ #define CMD_UPDATE_RECORD 220 /* 0xDC */ #define CMD_STATUS 242 /* 0xF2 */ #define CMD_RETRIEVE_DATA 203 /* 0xCB */ #define CMD_SET_DATA 219 /* 0xDB */ /* FID/path of SIM/USIM root directory */ #define ROOTMF ((char[]) {'\x3F', '\x00'}) #define ROOTMF_SZ sizeof(ROOTMF) /* RIL_Request* parameter counts */ #define GET_IMSI_NUM_PARAMS 1 #define ENTER_SIM_PIN_PARAMS 2 #define SET_FACILITY_LOCK_PARAMS 5 #define ENTER_SIM_PUK_PARAMS 3 #define CHANGE_SIM_PIN_PARAMS 3 /* RIL_FACILITY_LOCK parameters */ #define RIL_FACILITY_UNLOCK "0" #define RIL_FACILITY_LOCK "1" /* Call ID should not really be a big number */ #define MAX_CID_DIGITS 3 /* * TODO: * * A potential future change here is to create a driver * abstraction for each request/reply/event method, and a * corresponding method to allow new per-message implementations * to be registered. This would allow PES to easily add code * to quirk a particular RIL implementation. * * struct g_ril_messages_driver { * const char *name; * }; * */ static gboolean set_path(GRil *ril, guint app_type, struct parcel *rilp, const int fileid, const guchar *path, const guint path_len) { unsigned char db_path[6] = { 0x00 }; unsigned char *comm_path = db_path; char *hex_path = NULL; int len = 0; if (path_len > 0 && path_len < 7) { memcpy(db_path, path, path_len); len = path_len; } else if (app_type == RIL_APPTYPE_USIM) { len = sim_ef_db_get_path_3g(fileid, db_path); } else if (app_type == RIL_APPTYPE_SIM) { len = sim_ef_db_get_path_2g(fileid, db_path); } else { ofono_error("Unsupported app_type: 0%x", app_type); return FALSE; } /* * db_path contains the ID of the MF, but MediaTek modems return an * error if we do not remove it. Other devices work the other way * around: they need the MF in the path. In fact MTK behaviour seem to * be the right one: to have the MF in the file is forbidden following * ETSI TS 102 221, section 8.4.2 (we are accessing the card in mode * "select by path from MF", see 3gpp 27.007, +CRSM). */ if (g_ril_vendor(ril) == OFONO_RIL_VENDOR_MTK && len >= (int) ROOTMF_SZ && memcmp(db_path, ROOTMF, ROOTMF_SZ) == 0) { comm_path = db_path + ROOTMF_SZ; len -= ROOTMF_SZ; } if (len > 0) { hex_path = encode_hex(comm_path, len, 0); parcel_w_string(rilp, hex_path); g_ril_append_print_buf(ril, "%spath=%s,", print_buf, hex_path); g_free(hex_path); } else { /* * The only known case of this is EFPHASE_FILED (0x6FAE). * The ef_db table ( see /src/simutil.c ) entry for * EFPHASE contains a value of 0x0000 for it's * 'parent3g' member. This causes a NULL path to * be returned. * (EF_PHASE does not exist for USIM) */ parcel_w_string(rilp, NULL); g_ril_append_print_buf(ril, "%spath=(null),", print_buf); } return TRUE; } gboolean g_ril_request_deactivate_data_call(GRil *gril, const struct req_deactivate_data_call *req, struct parcel *rilp, struct ofono_error *error) { gchar *cid_str = NULL; gchar *reason_str = NULL; if (req->reason != RIL_DEACTIVATE_DATA_CALL_NO_REASON && req->reason != RIL_DEACTIVATE_DATA_CALL_RADIO_SHUTDOWN) { goto error; } parcel_init(rilp); parcel_w_int32(rilp, DEACTIVATE_DATA_CALL_NUM_PARAMS); cid_str = g_strdup_printf("%d", req->cid); parcel_w_string(rilp, cid_str); /* * TODO: airplane-mode; change reason to '1', * which means "radio power off". */ reason_str = g_strdup_printf("%d", req->reason); parcel_w_string(rilp, reason_str); g_ril_append_print_buf(gril, "(%s,%s)", cid_str, reason_str); g_free(cid_str); g_free(reason_str); OFONO_NO_ERROR(error); return TRUE; error: OFONO_EINVAL(error); return FALSE; } void g_ril_request_power(GRil *gril, const gboolean power, struct parcel *rilp) { DBG(""); parcel_init(rilp); parcel_w_int32(rilp, POWER_PARAMS); parcel_w_int32(rilp, (int32_t) power); g_ril_append_print_buf(gril, "(%d)", power); } void g_ril_request_set_net_select_manual(GRil *gril, const char *mccmnc, struct parcel *rilp) { DBG(""); parcel_init(rilp); parcel_w_string(rilp, mccmnc); g_ril_append_print_buf(gril, "(%s)", mccmnc); } gboolean g_ril_request_setup_data_call(GRil *gril, const struct req_setup_data_call *req, struct parcel *rilp, struct ofono_error *error) { const gchar *protocol_str; gchar *tech_str; gchar *auth_str; gchar *profile_str; int num_param = SETUP_DATA_CALL_PARAMS; DBG(""); if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) num_param = SETUP_DATA_CALL_PARAMS + 1; /* * Radio technology to use: 0-CDMA, 1-GSM/UMTS, 2... * values > 2 are (RADIO_TECH + 2) */ if (req->tech < 1 || req->tech > (RADIO_TECH_GSM + 2)) { ofono_error("%s: Invalid tech value: %d", __func__, req->tech); goto error; } /* * TODO(OEM): This code doesn't currently support * OEM data profiles. If a use case exist, then * this code will need to be modified. */ switch (req->data_profile) { case RIL_DATA_PROFILE_DEFAULT: profile_str = DATA_PROFILE_DEFAULT_STR; break; case RIL_DATA_PROFILE_TETHERED: profile_str = DATA_PROFILE_TETHERED_STR; break; case RIL_DATA_PROFILE_IMS: profile_str = DATA_PROFILE_IMS_STR; break; case RIL_DATA_PROFILE_FOTA: profile_str = DATA_PROFILE_FOTA_STR; break; case RIL_DATA_PROFILE_CBS: profile_str = DATA_PROFILE_CBS_STR; break; case RIL_DATA_PROFILE_MTK_MMS: if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) { profile_str = DATA_PROFILE_MTK_MMS_STR; break; } default: ofono_error("%s, invalid data_profile value: %d", __func__, req->data_profile); goto error; } if (req->apn == NULL) goto error; if (req->auth_type > RIL_AUTH_BOTH) { ofono_error("%s: Invalid auth type: %d", __func__, req->auth_type); goto error; } protocol_str = ril_ofono_protocol_to_ril_string(req->protocol); if (protocol_str == NULL) { ofono_error("%s: Invalid protocol: %d", __func__, req->protocol); goto error; } parcel_init(rilp); parcel_w_int32(rilp, num_param); tech_str = g_strdup_printf("%d", req->tech); parcel_w_string(rilp, tech_str); parcel_w_string(rilp, profile_str); parcel_w_string(rilp, req->apn); parcel_w_string(rilp, req->username); parcel_w_string(rilp, req->password); auth_str = g_strdup_printf("%d", req->auth_type); parcel_w_string(rilp, auth_str); parcel_w_string(rilp, protocol_str); g_ril_append_print_buf(gril, "(%s,%s,%s,%s,%s,%s,%s", tech_str, profile_str, req->apn, req->username, req->password, auth_str, protocol_str); if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) { /* MTK request_cid parameter */ char cid_str[MAX_CID_DIGITS + 1]; snprintf(cid_str, sizeof(cid_str), "%u", req->req_cid); parcel_w_string(rilp, cid_str); g_ril_append_print_buf(gril, "%s,%s", print_buf, cid_str); } g_ril_append_print_buf(gril, "%s)", print_buf); g_free(tech_str); g_free(auth_str); OFONO_NO_ERROR(error); return TRUE; error: OFONO_EINVAL(error); return FALSE; } gboolean g_ril_request_sim_read_info(GRil *gril, const struct req_sim_read_info *req, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, CMD_GET_RESPONSE); parcel_w_int32(rilp, req->fileid); g_ril_append_print_buf(gril, "(cmd=0x%.2X,efid=0x%.4X,", CMD_GET_RESPONSE, req->fileid); if (set_path(gril, req->app_type, rilp, req->fileid, req->path, req->path_len) == FALSE) goto error; parcel_w_int32(rilp, 0); /* P1 */ parcel_w_int32(rilp, 0); /* P2 */ /* * TODO: review parameters values used by Android. * The values of P1-P3 in this code were based on * values used by the atmodem driver impl. * * NOTE: * GET_RESPONSE_EF_SIZE_BYTES == 15; !255 */ parcel_w_int32(rilp, 15); /* P3 - max length */ parcel_w_string(rilp, NULL); /* data; only req'd for writes */ parcel_w_string(rilp, NULL); /* pin2; only req'd for writes */ parcel_w_string(rilp, req->aid_str); /* AID (Application ID) */ /* * sessionId, specific to latest MTK modems (harmless for older ones). * It looks like this field selects one or another SIM application, but * we use only one at a time so using zero here seems safe. */ if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) parcel_w_int32(rilp, 0); return TRUE; error: return FALSE; } gboolean g_ril_request_sim_read_binary(GRil *gril, const struct req_sim_read_binary *req, struct parcel *rilp) { g_ril_append_print_buf(gril, "(cmd=0x%.2X,efid=0x%.4X,", CMD_READ_BINARY, req->fileid); parcel_init(rilp); parcel_w_int32(rilp, CMD_READ_BINARY); parcel_w_int32(rilp, req->fileid); if (set_path(gril, req->app_type, rilp, req->fileid, req->path, req->path_len) == FALSE) goto error; parcel_w_int32(rilp, (req->start >> 8)); /* P1 */ parcel_w_int32(rilp, (req->start & 0xff)); /* P2 */ parcel_w_int32(rilp, req->length); /* P3 */ parcel_w_string(rilp, NULL); /* data; only req'd for writes */ parcel_w_string(rilp, NULL); /* pin2; only req'd for writes */ parcel_w_string(rilp, req->aid_str); /* sessionId, specific to latest MTK modems (harmless for older ones) */ if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) parcel_w_int32(rilp, 0); return TRUE; error: return FALSE; } gboolean g_ril_request_sim_read_record(GRil *gril, const struct req_sim_read_record *req, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, CMD_READ_RECORD); parcel_w_int32(rilp, req->fileid); g_ril_append_print_buf(gril, "(cmd=0x%.2X,efid=0x%.4X,", CMD_READ_RECORD, req->fileid); if (set_path(gril, req->app_type, rilp, req->fileid, req->path, req->path_len) == FALSE) goto error; parcel_w_int32(rilp, req->record); /* P1 */ parcel_w_int32(rilp, 4); /* P2 */ parcel_w_int32(rilp, req->length); /* P3 */ parcel_w_string(rilp, NULL); /* data; only req'd for writes */ parcel_w_string(rilp, NULL); /* pin2; only req'd for writes */ parcel_w_string(rilp, req->aid_str); /* AID (Application ID) */ /* sessionId, specific to latest MTK modems (harmless for older ones) */ if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) parcel_w_int32(rilp, 0); return TRUE; error: return FALSE; } gboolean g_ril_request_sim_write_binary(GRil *gril, const struct req_sim_write_binary *req, struct parcel *rilp) { char *hex_data; int p1, p2; parcel_init(rilp); parcel_w_int32(rilp, CMD_UPDATE_BINARY); parcel_w_int32(rilp, req->fileid); g_ril_append_print_buf(gril, "(cmd=0x%02X,efid=0x%04X,", CMD_UPDATE_BINARY, req->fileid); if (set_path(gril, req->app_type, rilp, req->fileid, req->path, req->path_len) == FALSE) goto error; p1 = req->start >> 8; p2 = req->start & 0xff; hex_data = encode_hex(req->data, req->length, 0); parcel_w_int32(rilp, p1); /* P1 */ parcel_w_int32(rilp, p2); /* P2 */ parcel_w_int32(rilp, req->length); /* P3 (Lc) */ parcel_w_string(rilp, hex_data); /* data */ parcel_w_string(rilp, NULL); /* pin2; only for FDN/BDN */ parcel_w_string(rilp, req->aid_str); /* AID (Application ID) */ /* sessionId, specific to latest MTK modems (harmless for older ones) */ if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) parcel_w_int32(rilp, 0); g_ril_append_print_buf(gril, "%s%d,%d,%d,%s,pin2=(null),aid=%s)", print_buf, p1, p2, req->length, hex_data, req->aid_str); g_free(hex_data); return TRUE; error: return FALSE; } static int get_sim_record_access_p2(enum req_record_access_mode mode) { switch (mode) { case GRIL_REC_ACCESS_MODE_CURRENT: return 4; case GRIL_REC_ACCESS_MODE_ABSOLUTE: return 4; case GRIL_REC_ACCESS_MODE_NEXT: return 2; case GRIL_REC_ACCESS_MODE_PREVIOUS: return 3; } return -1; } gboolean g_ril_request_sim_write_record(GRil *gril, const struct req_sim_write_record *req, struct parcel *rilp) { char *hex_data; int p2; parcel_init(rilp); parcel_w_int32(rilp, CMD_UPDATE_RECORD); parcel_w_int32(rilp, req->fileid); g_ril_append_print_buf(gril, "(cmd=0x%02X,efid=0x%04X,", CMD_UPDATE_RECORD, req->fileid); if (set_path(gril, req->app_type, rilp, req->fileid, req->path, req->path_len) == FALSE) goto error; p2 = get_sim_record_access_p2(req->mode); hex_data = encode_hex(req->data, req->length, 0); parcel_w_int32(rilp, req->record); /* P1 */ parcel_w_int32(rilp, p2); /* P2 (access mode) */ parcel_w_int32(rilp, req->length); /* P3 (Lc) */ parcel_w_string(rilp, hex_data); /* data */ parcel_w_string(rilp, NULL); /* pin2; only for FDN/BDN */ parcel_w_string(rilp, req->aid_str); /* AID (Application ID) */ /* sessionId, specific to latest MTK modems (harmless for older ones) */ if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) parcel_w_int32(rilp, 0); g_ril_append_print_buf(gril, "%s%d,%d,%d,%s,pin2=(null),aid=%s)", print_buf, req->record, p2, req->length, hex_data, req->aid_str); g_free(hex_data); return TRUE; error: return FALSE; } void g_ril_request_read_imsi(GRil *gril, const gchar *aid_str, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, GET_IMSI_NUM_PARAMS); parcel_w_string(rilp, aid_str); g_ril_append_print_buf(gril, "(%d,%s)", GET_IMSI_NUM_PARAMS, aid_str); } void g_ril_request_pin_send(GRil *gril, const char *passwd, const gchar *aid_str, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, ENTER_SIM_PIN_PARAMS); parcel_w_string(rilp, passwd); parcel_w_string(rilp, aid_str); g_ril_append_print_buf(gril, "(%s,aid=%s)", passwd, aid_str); } gboolean g_ril_request_pin_change_state(GRil *gril, const struct req_pin_change_state *req, struct parcel *rilp) { const char *lock_type; /* * TODO: clean up the use of string literals & * the multiple g_ril_append_print_buf() calls * by using a table lookup as does the core sim code */ switch (req->passwd_type) { case OFONO_SIM_PASSWORD_SIM_PIN: g_ril_append_print_buf(gril, "(SC,"); lock_type = "SC"; break; case OFONO_SIM_PASSWORD_PHSIM_PIN: g_ril_append_print_buf(gril, "(PS,"); lock_type = "PS"; break; case OFONO_SIM_PASSWORD_PHFSIM_PIN: g_ril_append_print_buf(gril, "(PF,"); lock_type = "PF"; break; case OFONO_SIM_PASSWORD_SIM_PIN2: g_ril_append_print_buf(gril, "(P2,"); lock_type = "P2"; break; case OFONO_SIM_PASSWORD_PHNET_PIN: g_ril_append_print_buf(gril, "(PN,"); lock_type = "PN"; break; case OFONO_SIM_PASSWORD_PHNETSUB_PIN: g_ril_append_print_buf(gril, "(PU,"); lock_type = "PU"; break; case OFONO_SIM_PASSWORD_PHSP_PIN: g_ril_append_print_buf(gril, "(PP,"); lock_type = "PP"; break; case OFONO_SIM_PASSWORD_PHCORP_PIN: g_ril_append_print_buf(gril, "(PC,"); lock_type = "PC"; break; default: ofono_error("%s: Invalid password type: %d", __func__, req->passwd_type); goto error; } parcel_init(rilp); parcel_w_int32(rilp, SET_FACILITY_LOCK_PARAMS); parcel_w_string(rilp, lock_type); if (req->enable) parcel_w_string(rilp, RIL_FACILITY_LOCK); else parcel_w_string(rilp, RIL_FACILITY_UNLOCK); parcel_w_string(rilp, req->passwd); /* TODO: make this a constant... */ parcel_w_string(rilp, "0"); /* class */ parcel_w_string(rilp, req->aid_str); g_ril_append_print_buf(gril, "(%s,%d,%s,0,aid=%s)", print_buf, req->enable, req->passwd, req->aid_str); return TRUE; error: return FALSE; } void g_ril_request_pin_send_puk(GRil *gril, const char *puk, const char *passwd, const gchar *aid_str, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, ENTER_SIM_PUK_PARAMS); parcel_w_string(rilp, puk); parcel_w_string(rilp, passwd); parcel_w_string(rilp, aid_str); g_ril_append_print_buf(gril, "(puk=%s,pin=%s,aid=%s)", puk, passwd, aid_str); } void g_ril_request_change_passwd(GRil *gril, const char *old_passwd, const char *new_passwd, const gchar *aid_str, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, CHANGE_SIM_PIN_PARAMS); parcel_w_string(rilp, old_passwd); parcel_w_string(rilp, new_passwd); parcel_w_string(rilp, aid_str); g_ril_append_print_buf(gril, "(old=%s,new=%s,aid=%s)", old_passwd, new_passwd, aid_str); } void g_ril_request_sms_cmgs(GRil *gril, const struct req_sms_cmgs *req, struct parcel *rilp) { int smsc_len; char *tpdu; parcel_init(rilp); parcel_w_int32(rilp, 2); /* Number of strings */ /* * SMSC address: * * smsc_len == 1, then zero-length SMSC was spec'd * RILD expects a NULL string in this case instead * of a zero-length string. */ smsc_len = req->pdu_len - req->tpdu_len; /* TODO: encode SMSC & write to parcel */ if (smsc_len > 1) ofono_error("SMSC address specified (smsc_len %d); " "NOT-IMPLEMENTED", smsc_len); parcel_w_string(rilp, NULL); /* SMSC address; NULL == default */ /* * TPDU: * * 'pdu' is a raw hexadecimal string * encode_hex() turns it into an ASCII/hex UTF8 buffer * parcel_w_string() encodes utf8 -> utf16 */ tpdu = encode_hex(req->pdu + smsc_len, req->tpdu_len, 0); parcel_w_string(rilp, tpdu); g_ril_append_print_buf(gril, "(%s)", tpdu); g_free(tpdu); } void g_ril_request_sms_acknowledge(GRil *gril, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 2); /* Number of int32 values in array */ parcel_w_int32(rilp, 1); /* Successful receipt */ parcel_w_int32(rilp, 0); /* error code */ g_ril_append_print_buf(gril, "(1,0)"); } void g_ril_request_set_smsc_address(GRil *gril, const struct ofono_phone_number *sca, struct parcel *rilp) { char number[OFONO_MAX_PHONE_NUMBER_LENGTH + 4]; if (sca->type == OFONO_NUMBER_TYPE_INTERNATIONAL) snprintf(number, sizeof(number), "\"+%s\"", sca->number); else snprintf(number, sizeof(number), "\"%s\"", sca->number); parcel_init(rilp); parcel_w_string(rilp, number); g_ril_append_print_buf(gril, "(%s)", number); } void g_ril_request_dial(GRil *gril, const struct ofono_phone_number *ph, enum ofono_clir_option clir, struct parcel *rilp) { parcel_init(rilp); /* Number to dial */ parcel_w_string(rilp, phone_number_to_string(ph)); /* CLIR mode */ parcel_w_int32(rilp, clir); /* USS, empty string */ /* TODO: Deal with USS properly */ parcel_w_int32(rilp, 0); parcel_w_int32(rilp, 0); g_ril_append_print_buf(gril, "(%s,%d,0,0)", phone_number_to_string(ph), clir); } void g_ril_request_hangup(GRil *gril, unsigned call_id, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); /* Always 1 - AT+CHLD=1x */ parcel_w_int32(rilp, call_id); g_ril_append_print_buf(gril, "(%u)", call_id); } void g_ril_request_dtmf(GRil *gril, char dtmf_char, struct parcel *rilp) { char ril_dtmf[2]; parcel_init(rilp); /* Ril wants just one character, but we need to send as string */ ril_dtmf[0] = dtmf_char; ril_dtmf[1] = '\0'; parcel_w_string(rilp, ril_dtmf); g_ril_append_print_buf(gril, "(%s)", ril_dtmf); } void g_ril_request_separate_conn(GRil *gril, int call_id, struct parcel *rilp) { parcel_init(rilp); /* Payload is an array that holds just one element */ parcel_w_int32(rilp, 1); parcel_w_int32(rilp, call_id); g_ril_append_print_buf(gril, "(%d)", call_id); } void g_ril_request_set_supp_svc_notif(GRil *gril, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); /* size of array */ parcel_w_int32(rilp, 1); /* notifications enabled */ g_ril_append_print_buf(gril, "(1)"); } void g_ril_request_set_mute(GRil *gril, int muted, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); parcel_w_int32(rilp, muted); g_ril_append_print_buf(gril, "(%d)", muted); } void g_ril_request_send_ussd(GRil *gril, const char *ussd, struct parcel *rilp) { parcel_init(rilp); parcel_w_string(rilp, ussd); g_ril_append_print_buf(gril, "(%s)", ussd); } void g_ril_request_set_call_waiting(GRil *gril, int enabled, int serviceclass, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 2); /* Number of params */ parcel_w_int32(rilp, enabled); /* on/off */ /* * Modem seems to respond with error to all queries * or settings made with bearer class * BEARER_CLASS_DEFAULT. Design decision: If given * class is BEARER_CLASS_DEFAULT let's map it to * SERVICE_CLASS_VOICE effectively making it the * default bearer. */ if (serviceclass == BEARER_CLASS_DEFAULT) serviceclass = BEARER_CLASS_VOICE; parcel_w_int32(rilp, serviceclass); /* Service class */ g_ril_append_print_buf(gril, "(%d, 0x%x)", enabled, serviceclass); } void g_ril_request_query_call_waiting(GRil *gril, int serviceclass, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); /* Number of params */ /* * RILD expects service class to be 0 as certain carriers can reject the * query with specific service class */ parcel_w_int32(rilp, 0); g_ril_append_print_buf(gril, "(0)"); } void g_ril_request_set_clir(GRil *gril, int mode, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); /* Number of params */ parcel_w_int32(rilp, mode); g_ril_append_print_buf(gril, "(%d)", mode); } void g_ril_request_screen_state(GRil *gril, int state, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); /* Number of params */ parcel_w_int32(rilp, state); g_ril_append_print_buf(gril, "(%d)", state); } void g_ril_request_call_fwd(GRil *gril, const struct req_call_fwd *req, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, req->action); parcel_w_int32(rilp, req->type); parcel_w_int32(rilp, req->cls); g_ril_append_print_buf(gril, "(type: %d cls: %d ", req->type, req->cls); if (req->number != NULL) { parcel_w_int32(rilp, req->number->type); parcel_w_string(rilp, (char *) req->number->number); g_ril_append_print_buf(gril, "%s number type: %d number: " "%s time: %d) ", print_buf, req->number->type, req->number->number, req->time); } else { /* * The following values have no real meaning for * activation/deactivation/erasure actions, but * apparently rild expects them, so fields need to * be filled. Otherwise there is no response. */ parcel_w_int32(rilp, 0x81); /* TOA unknown */ parcel_w_string(rilp, "1234567890"); g_ril_append_print_buf(gril, "%s number type: %d number: " "%s time: %d) ", print_buf, 0x81, "1234567890", req->time); } parcel_w_int32(rilp, req->time); } void g_ril_request_set_preferred_network_type(GRil *gril, int net_type, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 1); /* Number of params */ parcel_w_int32(rilp, net_type); g_ril_append_print_buf(gril, "(%d)", net_type); } void g_ril_request_query_facility_lock(GRil *gril, const char *facility, const char *password, int services, struct parcel *rilp) { char svcs_str[4]; parcel_init(rilp); parcel_w_int32(rilp, 4); /* # of strings */ parcel_w_string(rilp, facility); parcel_w_string(rilp, password); snprintf(svcs_str, sizeof(svcs_str), "%d", services); parcel_w_string(rilp, svcs_str); parcel_w_string(rilp, NULL); /* AID (for FDN, not yet supported) */ g_ril_append_print_buf(gril, "(%s,%s,%s,(null))", facility, password, svcs_str); } void g_ril_request_set_facility_lock(GRil *gril, const char *facility, int enable, const char *passwd, int services, struct parcel *rilp) { char svcs_str[4]; const char *enable_str; parcel_init(rilp); parcel_w_int32(rilp, 5); /* # of strings */ parcel_w_string(rilp, facility); if (enable) enable_str = "1"; else enable_str = "0"; parcel_w_string(rilp, enable_str); parcel_w_string(rilp, passwd); snprintf(svcs_str, sizeof(svcs_str), "%d", services); parcel_w_string(rilp, svcs_str); parcel_w_string(rilp, NULL); /* AID (for FDN, not yet supported) */ g_ril_append_print_buf(gril, "(%s,%s,%s,%s,(null))", facility, enable_str, passwd, svcs_str); } void g_ril_request_change_barring_password(GRil *gril, const char *facility, const char *old_passwd, const char *new_passwd, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, 3); /* # of strings */ parcel_w_string(rilp, facility); parcel_w_string(rilp, old_passwd); parcel_w_string(rilp, new_passwd); g_ril_append_print_buf(gril, "(%s,%s,%s)", facility, old_passwd, new_passwd); } void g_ril_request_oem_hook_raw(GRil *gril, const void *payload, size_t length, struct parcel *rilp) { char *hex_dump = NULL; parcel_init(rilp); parcel_w_raw(rilp, payload, length); if (payload != NULL) hex_dump = encode_hex(payload, length, '\0'); g_ril_append_print_buf(gril, "(%s)", hex_dump ? hex_dump : "(null)"); g_free(hex_dump); } void g_ril_request_oem_hook_strings(GRil *gril, const char **strs, int num_str, struct parcel *rilp) { int i; parcel_init(rilp); parcel_w_int32(rilp, num_str); g_ril_append_print_buf(gril, "("); for (i = 0; i < num_str; ++i) { parcel_w_string(rilp, strs[i]); if (i == num_str - 1) g_ril_append_print_buf(gril, "%s%s)", print_buf, strs[i]); else g_ril_append_print_buf(gril, "%s%s, ", print_buf, strs[i]); } } void g_ril_request_set_initial_attach_apn(GRil *gril, const char *apn, int proto, const char *user, const char *passwd, const char *mccmnc, struct parcel *rilp) { const char *proto_str; const int auth_type = RIL_AUTH_ANY; parcel_init(rilp); parcel_w_string(rilp, apn); proto_str = ril_ofono_protocol_to_ril_string(proto); parcel_w_string(rilp, proto_str); parcel_w_int32(rilp, auth_type); parcel_w_string(rilp, user); parcel_w_string(rilp, passwd); g_ril_append_print_buf(gril, "(%s,%s,%s,%s,%s", apn, proto_str, ril_authtype_to_string(auth_type), user, passwd); if (g_ril_vendor(gril) == OFONO_RIL_VENDOR_MTK) { parcel_w_string(rilp, mccmnc); g_ril_append_print_buf(gril, "%s,%s)", print_buf, mccmnc); } else { g_ril_append_print_buf(gril, "%s)", print_buf); } } void g_ril_request_set_uicc_subscription(GRil *gril, int slot_id, int app_index, int sub_id, int sub_status, struct parcel *rilp) { parcel_init(rilp); parcel_w_int32(rilp, slot_id); parcel_w_int32(rilp, app_index); parcel_w_int32(rilp, sub_id); parcel_w_int32(rilp, sub_status); g_ril_append_print_buf(gril, "(%d, %d, %d, %d(%s))", slot_id, app_index, sub_id, sub_status, sub_status ? "ACTIVATE" : "DEACTIVATE"); } ofono-1.17.bzr6912+16.04.20160314.3/test/0000755000015600001650000000000012671500304017357 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/test/initiate-ussd0000755000015600001650000000211012671500024022060 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus if (len(sys.argv) < 2): print("Usage: %s [modem] " % (sys.argv[0])) sys.exit(1) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() if (len(sys.argv) == 2): path = modems[0][0] ussdstring = sys.argv[1] else: path = sys.argv[1] ussdstring = sys.argv[2] ussd = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SupplementaryServices') properties = ussd.GetProperties() state = properties["State"] print("State: %s" % (state)) if state != "idle": sys.exit(1); result = ussd.Initiate(ussdstring, timeout=100) properties = ussd.GetProperties() state = properties["State"] print(result[0] + ": " + result[1]) if state == "idle": sys.exit(0) print("State: %s" % (state)) while state == "user-response": response = input("Enter response: ") result = ussd.Respond(response, timeout=100) properties = ussd.GetProperties() state = properties["State"] print(result) if state != "idle": print("State: %s" % (state)) ofono-1.17.bzr6912+16.04.20160314.3/test/monitor-ofono0000755000015600001650000001054612671500024022117 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import dbus import dbus.mainloop.glib _dbus2py = { dbus.String : str, dbus.UInt32 : int, dbus.Int32 : int, dbus.Int16 : int, dbus.UInt16 : int, dbus.UInt64 : int, dbus.Int64 : int, dbus.Byte : int, dbus.Boolean : bool, dbus.ByteArray : str, dbus.ObjectPath : str } def dbus2py(d): t = type(d) if t in _dbus2py: return _dbus2py[t](d) if t is dbus.Dictionary: return dict([(dbus2py(k), dbus2py(v)) for k, v in d.items()]) if t is dbus.Array and d.signature == "y": return "".join([chr(b) for b in d]) if t is dbus.Array or t is list: return [dbus2py(v) for v in d] if t is dbus.Struct or t is tuple: return tuple([dbus2py(v) for v in d]) return d def pretty(d): d = dbus2py(d) t = type(d) if t in (dict, tuple, list) and len(d) > 0: if t is dict: d = ", ".join(["%s = %s" % (k, pretty(v)) for k, v in d.items()]) return "{ %s }" % d d = " ".join([pretty(e) for e in d]) if t is tuple: return "( %s )" % d if t is str: return "%s" % d return str(d) def property_changed(name, value, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s = %s" % (iface, path, name, pretty(value))) def added(name, value, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s %s" % (iface, member, name, pretty(value))) def removed(name, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s" % (iface, name, member)) def event(member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s" % (iface, path, member)) def message(msg, args, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s %s (%s)" % (iface, path, member, msg, pretty(args))) def network_time_changed(time, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s %s" % (iface, path, member, pretty(time))) def ussd(msg, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s %s" % (iface, path, member, msg)) def value(value, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s %s" % (iface, path, member, str(value))) if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() bus.add_signal_receiver(property_changed, bus_name="org.ofono", signal_name = "PropertyChanged", path_keyword="path", interface_keyword="interface") for member in ["IncomingBarringInEffect", "OutgoingBarringInEffect", "NearMaximumWarning"]: bus.add_signal_receiver(event, bus_name="org.ofono", signal_name = member, member_keyword="member", path_keyword="path", interface_keyword="interface") for member in ["ModemAdded", "ContextAdded", "CallAdded", "MessageAdded"]: bus.add_signal_receiver(added, bus_name="org.ofono", signal_name = member, member_keyword="member", path_keyword="path", interface_keyword="interface") for member in ["ModemRemoved", "ContextRemoved", "CallRemoved", "MessageRemoved"]: bus.add_signal_receiver(removed, bus_name="org.ofono", signal_name = member, member_keyword="member", path_keyword="path", interface_keyword="interface") for member in ["DisconnectReason", "Forwarded", "BarringActive"]: bus.add_signal_receiver(value, bus_name="org.ofono", signal_name = member, member_keyword="member", path_keyword="path", interface_keyword="interface") for member in ["IncomingBroadcast", "EmergencyBroadcast", "IncomingMessage", "ImmediateMessage", "StatusReport"]: bus.add_signal_receiver(message, bus_name="org.ofono", signal_name = member, member_keyword="member", path_keyword="path", interface_keyword="interface") for member in ["NotificationReceived", "RequestReceived"]: bus.add_signal_receiver(ussd, bus_name="org.ofono", signal_name = member, member_keyword="member", path_keyword="path", interface_keyword="interface") for member in ["NetworkTimeChanged"]: bus.add_signal_receiver(network_time_changed, bus_name="org.ofono", signal_name = member, member_keyword="member", path_keyword="path", interface_keyword="interface") mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/test-phonebook0000755000015600001650000000102212671500024022240 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus, sys if __name__ == "__main__": bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() phonebook = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Phonebook') print(phonebook.Import(timeout=100)) ofono-1.17.bzr6912+16.04.20160314.3/test/test-call-forwarding0000755000015600001650000000777612671500024023355 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import argparse import dbus import dbus.mainloop.glib def property_changed(property, value): print("CallForwarding property %s changed to %s" % (property, value)) def print_properties(cf): properties = cf.GetProperties() for p in properties: if len(properties[p].__str__()) > 0: print("%s call forwarding rule is: %s" % (p, properties[p])) else: print("%s call forwarding rule disabled" % (p)) def parse_arguments(): """Parses command-line arguments. """ parser = argparse.ArgumentParser(description="test-call-forwarding script") parser.add_argument("-m", "--modem", dest="modem", help="""Specifies the modem to run the call-forwarding tests against""", default="" ) parser.add_argument("--noreply-num", dest="noreply_num", help="""Specify a forwarding number for the no-reply condition""", default="+134444", ) parser.add_argument("--uncond-num", dest="uncond_num", help="""Specify an unconditional forwarding number""", default="+155555", ) parser.add_argument("--noreply-timeout", dest="noreply_timeout", help="""Specify a no-reply timeout""", default=-1, ) return parser.parse_args() def main(args): path = "" dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() assert len(modems) >= 1 for modem in modems: if modem[0] == args.modem: path = args.modem if path == "": path = modems[0][0] if args.noreply_timeout == -1: noreply_timeout = dbus.UInt16(30) else: noreply_timeout = dbus.UInt16(args.noreply_timeout) cf = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.CallForwarding') cf.connect_to_signal("PropertyChanged", property_changed) print_properties(cf) try: cf.SetProperty("FoobarNoReplyTimeout", dbus.UInt16(19)) except dbus.DBusException as e: print("Unable to set timeout - Good") try: cf.SetProperty("VoiceNotReachableTimeout", dbus.UInt16(19)) except dbus.DBusException as e: print("Unable to set timeout - Good") try: cf.SetProperty("VoiceNoReplyTimeout", dbus.UInt16(19)) except dbus.DBusException as e: print("Unable to set timeout - Good") try: cf.SetProperty("DataNoReplyTimeout", dbus.UInt16(19)) except dbus.DBusException as e: print("Unable to set timeout - Good") try: cf.SetProperty("FaxNoReplyTimeout", dbus.UInt16(19)) except dbus.DBusException as e: print("Unable to set timeout - Good") try: cf.SetProperty("SmsNoReplyTimeout", dbus.UInt16(19)) except dbus.DBusException as e: print("Unable to set timeout - Good") try: cf.SetProperty("VoiceNoReply", "") except dbus.DBusException as e: print("Unable to erase voice no reply rule - Bad") try: cf.SetProperty("VoiceNoReply", args.noreply_num) except dbus.DBusException as e: print("Unable to register voice no reply rule - Bad") try: cf.SetProperty("VoiceNoReplyTimeout", noreply_timeout) except dbus.DBusException as e: print("Unable to set voice no reply timeout - Bad") properties = cf.GetProperties() print(properties["VoiceNoReply"]) print(properties["VoiceNoReplyTimeout"]) try: cf.SetProperty("VoiceUnconditional", args.uncond_num) except dbus.DBusException as e: print("Unable to set Voice Unconditional - Bad") properties = cf.GetProperties() print(properties["VoiceUnconditional"]) try: cf.DisableAll("foobar") except dbus.DBusException as e: print("Unable to delete invalids - Good") try: cf.DisableAll("conditional") except dbus.DBusException as e: print("Unable to delete all conditional - Bad") properties = cf.GetProperties() print(properties["VoiceNoReply"]) print(properties["VoiceNoReplyTimeout"]) try: cf.DisableAll("all") except dbus.DBusException as e: print("Unable to delete all conditional - Bad") print(properties["VoiceUnconditional"]) mainloop = GLib.MainLoop() mainloop.run() if __name__ == "__main__": args = parse_arguments() main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/test-ss-control-cs0000755000015600001650000000504212671500024022770 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import dbus import dbus.mainloop.glib def property_changed(property, value): print("CallSettings property %s changed to %s" % (property, value)) def print_properties(cs): properties = cs.GetProperties() for p in properties: print("property %s, value: %s" % (p, properties[p])) if __name__ == "__main__": dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() cs = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.CallSettings') cs.connect_to_signal("PropertyChanged", property_changed) ss = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.SupplementaryServices') print_properties(cs) print("Trying invalid SS request for CLIR") try: print(ss.Initiate("*31#456666")) except dbus.DBusException as e: print("Failed with %s - Good" % e) print("Trying invalid SS request for CLIR") try: print(ss.Initiate("*31*455*4#")) except dbus.DBusException as e: print("Failed with %s - Good" % e) print("Trying invalid SS request for CLIR") try: print(ss.Initiate("*31**44435#")) except dbus.DBusException as e: print("Failed with %s - Good" % e) print("Query CLIP") print(ss.Initiate("*#30#")) print("Query CNAP") print(ss.Initiate("*#300#")) print("Query COLP") print(ss.Initiate("*#76#")) print("Query CLIR") print(ss.Initiate("*#31#")) print("Activate CLIR") print(ss.Initiate("*31#")) print_properties(cs) print("Deactivate CLIR") print(ss.Initiate("#31#")) print_properties(cs) print("Trying invalid SS request for CW") try: print(ss.Initiate("*43#456666")) except dbus.DBusException as e: print("Failed with %s - Good" % e) print("Trying invalid SS request for CW") try: print(ss.Initiate("*43*455*4#")) except dbus.DBusException as e: print("Failed with %s - Good" % e) print("Trying invalid SS request for CW") try: print(ss.Initiate("*43**44435#")) except dbus.DBusException as e: print("Failed with %s - Good" % e) print("Query CW") print(ss.Initiate("*#43#")) print("Query CW, only Voice") print(ss.Initiate("*#43*11#")) print("Query CW, only Fax") print(ss.Initiate("*#43*13#")) print("Disable CW for everything") print(ss.Initiate("#43#")); print_properties(cs) print("Enable CW for Voice") print(ss.Initiate("*43*11#")) print_properties(cs) mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/dundee-disconnect0000755000015600001650000000067612671500024022710 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono.dundee', '/'), 'org.ofono.dundee.Manager') devices = manager.GetDevices() path = devices[0][0] print("Disconnect device %s..." % path) device = dbus.Interface(bus.get_object('org.ofono.dundee', path), 'org.ofono.dundee.Device') device.SetProperty("Active", False) ofono-1.17.bzr6912+16.04.20160314.3/test/hangup-call0000755000015600001650000000040712671500024021500 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() if (len(sys.argv) < 2): print("Usage: %s [ Call Path ]" % (sys.argv[0])) sys.exit(1) call = dbus.Interface(bus.get_object('org.ofono', sys.argv[1]), 'org.ofono.VoiceCall') call.Hangup() ofono-1.17.bzr6912+16.04.20160314.3/test/cdma-list-call0000755000015600001650000000104112671500024022066 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: print("[ %s ]" % (path)) if "org.ofono.cdma.VoiceCallManager" not in properties["Interfaces"]: continue mgr = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.cdma.VoiceCallManager') properties = mgr.GetProperties() for key in properties.keys(): val = str(properties[key]) print(" %s = %s" % (key, val)) ofono-1.17.bzr6912+16.04.20160314.3/test/create-mms-context0000755000015600001650000000215212671500024023023 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if "org.ofono.ConnectionManager" not in properties["Interfaces"]: continue connman = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') contexts = connman.GetContexts() path = ""; for i, properties in contexts: if properties["Type"] == "mms": path = i break if path == "": path = connman.AddContext("mms") print("Created new context %s" % (path)) else: print("Found context %s" % (path)) context = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionContext') if len(sys.argv) > 1: context.SetProperty("AccessPointName", sys.argv[1]) print("Setting APN to %s" % (sys.argv[1])) if len(sys.argv) > 2: context.SetProperty("Username", sys.argv[2]) print("Setting username to %s" % (sys.argv[2])) if len(sys.argv) > 3: context.SetProperty("Password", sys.argv[3]) print("Setting password to %s" % (sys.argv[3])) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/0000755000015600001650000000000012671500304021167 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/test-modem-offline0000755000015600001650000000316712671500024024621 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is offline. import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: print("[ %s ]" % (path)) keys = list(properties.keys()) assert keys.index('Online') != "ValueError" assert keys.index("Powered") != "ValueError" assert keys.index("Emergency") != "ValueError" assert keys.index("Lockdown") != "ValueError" assert keys.index("Serial") != "ValueError" assert keys.index("Revision") != "ValueError" assert keys.index("Interfaces") != "ValueError" assert properties['Online'] == 0 assert properties['Powered'] == 1 assert properties['Emergency'] == 0 assert properties["Lockdown"] == 0 assert properties["Serial"].isalnum() assert properties["Revision"] print("OK\n") ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/test-hangup0000755000015600001650000000574012671500024023361 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. # # *** REQUIRED SETUP *** # # 1. /etc/host: # This script uses telnetlib to access the emulator console. In # order for this to work, the emulator needs to be configured so # that it can access 'localhost' on the host machine. This is # done by modifying /etc/hosts in the emulator to define 'localhost' # as 10.0.2.2, which is a special alias to the host loopback interface. # Without this change, it's impossible for this script to access the # emulator console. # # 2. Modem online # This script must be run *after* the modem has been brought online, # otherwise it will fail. This is currently done by telepathy-ofono, # which isn't being started in the current touch emulator, however it # will soon be the responsibility of urfkill. # # *** TODO *** # # 1. This test calls HangupAll() which is an asynchronous operation. # There's no getting around that fact that a timer needs to be used # to delay querying the calls after the hangup. The code currently # uses a time.sleep(5), however it could be changed to listen for a # CallRemoved signal. A timer would still be required to catch the # scenario where the signal was never received. import dbus import sys import time import telnetlib def create_incoming_call(number, port): tn = telnetlib.Telnet("localhost", 5554) tn.read_until(b"OK") tn.write(bytes("gsm call " + number + "\n", 'utf-8')) print("incoming call created") tn.read_until(b"OK") tn.write(b"exit") if __name__ == "__main__": if len(sys.argv) == 2: port = sys.argv[1] else: port = 5554 create_incoming_call("6663334444", port) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: print("[ %s ]" % (path)) assert "org.ofono.VoiceCallManager" in properties["Interfaces"] mgr = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') calls = mgr.GetCalls() assert len(calls) > 0, "No incoming call present!" mgr.HangupAll() time.sleep(5) calls = mgr.GetCalls() assert len(calls) == 0, "HangupAll() failed, calls still exist!" ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/0000755000015600001650000000000012671500304021757 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/test-sims-offline0000755000015600001650000000570612671500024025264 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """Tests SIM(s) and modem(s) offline. This module contains a functional test which checks a running ofono/rilmodem/mtkmodem instance to ensure that the correct DBus properties are exported when one or more SIMs (unlocked) are inserted AND the corresponding modem(s) are offline. As such it validates the properties of the followinh interfaces: * org.ofono.Modem * org.ofono.SimManager ( on supported hw ) * org.ofono.VoiceCallManager ( EmergencyNumbers ) * org.ofono.CallVolume NOTE - this test by default verifies all modems present a device. If the device is multi-SIM and not all slots have unlocked SIMs, then please use the -m to verify a specific modem. SETUP: * Ensure that at least one unlocked SIM is inserted in the phone * Ensure FlightMode from System Settings ( or net-indicator ) * Run this script * Disable FlightMode ToDo: * If run on the emulator, make this script use console commands to configure the modem(s) for the required conditions ( ie. no SIM(s), online ) """ import argparse import simtestutil from simtestutil import * def parse_args(): parser = argparse.ArgumentParser() parser.add_argument("--mnc", dest="mnc", help="""Specify a MNC (mobile-network-code) to match against""", default="", ) parser.add_argument("--mcc", dest="mcc", help="""Specify a MCC (mobile-country-code) to match against""", default="", ) parser.add_argument("--subscriber", dest="subscriber", help="""Specify a SubscriberIdentity to match against""", default="", ) return simtestutil.parse_args(parser) class TestSimsOffline(SimTestCase): def validate_modem(self, path): self.validate_modem_properties(path, False, True) # krillin: no SIM access when modem offline if (self.product != "krillin" and self.product != "arale"): self.validate_sim_properties(path, self.args.mcc, self.args.mnc, self.args.subscriber) self.validate_emergency_numbers(path) self.validate_call_volume_properties(path) def test_main(self): self.main(args) if __name__ == "__main__": args = parse_args() sim_unittest_main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/test-sim-enable-pin-lock0000755000015600001650000000553112671500024026413 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """Tests enabling a SIM for PIN locking. This module contains a functional test which checks an ofono/rilmodem/mtkmodem instance to ensure that the correct things happen when SIM PIN locking is enabled. NOTE - this test by default enables locking for a single SIM. If the device is multi-SIM, the first modem will be used by default. The -m argument can be used to specify the second modem if needed. Requirements: * an unlocked SIM card * the current PIN code for the SIM SETUP: * Ensure that at least one unlocked SIM is inserted in the phone * Ensure that FlightMode is NOT enabled * Run this script * Reboot the phone and re-run this script specifying --continue ( DON'T enter the PIN on the UI after rebooting!!! ) ToDo: * If run on the emulator, make this script use console commands to configure the modem(s) for the required conditions ( ie. no SIM(s), online ) """ import simtestutil from simtestutil import * class TestSimEnablePinLock(SimTestCase): def validate_pin_props(self, path): simmanager = self.get_simmanager(path) properties = simmanager.GetProperties() self.assertTrue(len(properties["LockedPins"]) == 1) self.assertTrue(properties["PinRequired"] == "pin") def enable_pin_lock(self, path): if self.args.debug: print("enable_pin_lock called, pin=%s" % self.args.pin) simmanager = self.get_simmanager(path) properties = simmanager.GetProperties() self.assertTrue(len(properties["LockedPins"]) == 0) simmanager.LockPin("pin", self.args.pin) properties = simmanager.GetProperties() locked_pins = properties["LockedPins"] self.assertTrue(len(locked_pins) == 1) self.assertTrue(locked_pins[0] == "pin") def validate_modem(self, path): if self.args.cont == False: self.validate_modem_properties(path, True, True) self.enable_pin_lock(path) else: self.validate_pin_props(path) def test_main(self): self.main(args) if __name__ == "__main__": args = simtestutil.parse_lock_test_args() sim_unittest_main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/test-sim-disable-pin-lock0000755000015600001650000000577412671500024026601 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """Tests disabling PIN locking for a SIM. This module contains a functional test which checks an ofono/rilmodem/mtkmodem instance to ensure that the correct things happen when SIM PIN locking is disabled. NOTE - this test by default disables locking for a single SIM. If the device is multi-SIM, the first modem will be used by default. The -m argument can be used to specify the second modem if needed. Requirements: * a locked SIM card * the current PIN code for the SIM SETUP: * Ensure that at least one locked SIM is inserted in the phone * Ensure that FlightMode is NOT enabled * [Optional] Some phones may require the SIM to be entered before PIN locking can be disabled. * Run this script * Reboot the phone and re-run this script specifying --continue ( DON'T enter the PIN on the UI after rebooting!!! ) ToDo: * If run on the emulator, make this script use console commands to configure the modem(s) for the required conditions ( ie. no SIM(s), online ) """ import simtestutil from simtestutil import * class TestSimDisablePinLock(SimTestCase): def validate_pin_props(self, path): simmanager = self.get_simmanager(path) properties = simmanager.GetProperties() self.assertTrue(len(properties["LockedPins"]) == 0) self.assertTrue(properties["PinRequired"] == "none") def disable_pin_lock(self, path): simmanager = self.get_simmanager(path) properties = simmanager.GetProperties() if self.args.debug: print("disable_pin_lock called, LockedPins: %s" % properties["LockedPins"]) print("pin=%s" % self.args.pin) locked_pins = properties["LockedPins"] self.assertTrue(len(locked_pins) == 1) self.assertTrue(locked_pins[0] == "pin") simmanager.UnlockPin("pin", self.args.pin) properties = simmanager.GetProperties() self.assertTrue(len(properties["LockedPins"]) == 0) def validate_modem(self, path): if self.args.cont == False: self.validate_modem_properties(path, True, True) self.disable_pin_lock(path) else: self.validate_pin_props(path) def test_main(self): self.main(args) if __name__ == "__main__": args = simtestutil.parse_lock_test_args() sim_unittest_main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/test-sim-puk-unlock0000755000015600001650000001301312671500024025535 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """Tests the abilty to unlock a PUK-locked SIM. Requirements: * a SIM with PIN-locking enabled * the current PUK code for the SIM Setup: * Ensure that FlightMode is NOT enabled * Ensure that at least one SIM with PIN-locking enabled is inserted in the phone AND STILL LOCKED! ( ie. the PIN hasn't been entered yet ) * Run this script Notes: This script will actually cause the SIM to become PUK-locked by entering a hard-coded SIM three times in a row. This is done to verify that the 'Retries' property is properly updated on incorrect PIN attempts. Likewise, once PUK-locked this test will also attempt on incorrect PUK reset to again check 'Retries'. It will then reset the SIM to the given new PIN. ToDo: * If run on the emulator, make this script use console commands to configure the modem(s) for the required conditions ( ie. no SIM(s), online ) """ import dbus.mainloop.glib import simtestutil from gi.repository import GLib from simtestutil import * def parse_test_args(): parser = argparse.ArgumentParser() parser.add_argument("--puk", dest="puk", help="""Specify the SIM PUK code""", required="yes", ) parser.add_argument("--new-pin", dest="new_pin", help="""Spicy the new SIM PIN code""", required="yes" ) parser.add_argument("-t", "--timeout", dest="timeout", help="""Specify a timeout which causes the script to exit""", default=10, ) return parse_args(parser) class TestSimUnlockPuk(SimTestCase): def setUp(self): self.args = args self.product = get_product() self.pin_retries = 3 self.puk_retries = 10 self.pin_retry_failure = False self.puk_retry_failure = False self.timeout_failure = False self.bad_puk_sent = False self.mainloop = GLib.MainLoop() dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) SimTestCase.setUp(self) interval_ms = 1000 * int(self.args.timeout) GLib.timeout_add(interval_ms, self.timeout_cb) def enter_bad_pin(self, path, simmanager=None): if simmanager == None: simmanager = self.get_simmanager(path) try: simmanager.EnterPin("pin", "0000") except dbus.DBusException as e: if self.args.debug: print("EnterPin failed") def enter_puk(self, path, puk, pin, simmanager=None): if simmanager == None: simmanager = self.get_simmanager(path) try: simmanager.ResetPin("puk", puk, pin) except dbus.DBusException as e: if self.args.debug: print("ResetPin failed") # now do a valid PUK unlock if self.bad_puk_sent == False: self.bad_puk_sent = True self.enter_puk(path, self.args.puk, self.args.new_pin) def retry_listener(self, name, value, path): if self.args.debug: print("modem: {} SIM property: {} changed " "to {}".format(path, name, str(value))) if name == "Retries" and len(value) > 0: # bug: lp: #14028 - Retries not updated for # bad PUK attempts! If it's possible to fix # this bug, then code should be added here to # validate puk retries decreasing... if self.bad_puk_sent == False: if "pin" in value: retries = int(value["pin"]) else: retries = 0 if retries != (self.pin_retries - 1): self.pin_retry_failure = True self.mainloop.quit() else: self.pin_retries = retries if retries > 0: if self.args.debug: print("enter another bad pin") self.enter_bad_pin(path) elif name == "PinRequired": if value == "none": if self.args.debug: print("Pin reset; all done!"); self.mainloop.quit() elif value == "puk": # trigger an invalid PUK unlock self.enter_puk(path, "0000000000", "0000") def timeout_cb(self): if self.args.debug: print("ALL DONE - timer fired!!!") self.timeout_failure = True self.mainloop.quit() def puk_unlock(self, path): if self.args.debug: print("puk_unlock called, puk= {} " "new_pin: {}".format(self.args.puk, self.args.new_pin)) simmanager = self.get_simmanager(path) properties = simmanager.GetProperties() # verify SIM is PIN-locked locked_pins = properties["LockedPins"] self.assertTrue(len(locked_pins) == 1) self.assertTrue(locked_pins[0] == "pin") self.assertTrue(properties["PinRequired"] == "pin") # enter wrong PIN x3 simmanager.connect_to_signal("PropertyChanged", self.retry_listener, path_keyword="path") self.enter_bad_pin(path, simmanager) def validate_modem(self, path): self.puk_unlock(path) self.mainloop.run() self.assertFalse(self.timeout_failure) self.assertFalse(self.pin_retry_failure) self.assertFalse(self.puk_retry_failure) def test_main(self): self.main(args) if __name__ == "__main__": args = parse_test_args() sim_unittest_main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/test-no-sims-online0000755000015600001650000000427312671500024025536 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """Tests no SIM(s) and modem(s) online. This module contains a functional test which checks a running ofono/rilmodem/mtkmodem instance to ensure that the correct DBus properties are exported when no SIM(s) are inserted AND the corresponding modem(s) are online. As such it validates the properties of the following interfaces: * org.ofono.Modem * org.ofono.SimManager * org.ofono.VoiceCallManager ( EmergencyNumbers ) * org.ofono.CallVolume NOTE - this test by default verifies all modems present a device. If the device is multi-SIM and not all slots have unlocked SIMs, then please use the -m to verify a specific modem. SETUP: * Power off the phone * Remove any SIMs * Power on the phone * Run this script * [Optional] Reboot the phone and re-run the script ToDo: * If run on the emulator, make this script use console commands to configure the modem(s) for the required conditions ( ie. no SIM(s), online ) """ from simtestutil import * class TestNoSimsOnline(SimTestCase): def validate_modem(self, path): self.validate_modem_properties(path, True, False) self.assertTrue(self.check_no_sim_present(path)) self.validate_emergency_numbers(path) self.validate_call_volume_properties(path) def test_main(self): self.main(args) if __name__ == "__main__": args = parse_args() sim_unittest_main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/test-sim-pin-unlock0000755000015600001650000000714712671500024025537 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """Tests the abilty to unlock a locked SIM for use. This module contains a functional test which checks an ofono/rilmodem/mtkmodem instance to ensure that the a SIM PIN can be entered and thus unlock the SIM for use. NOTE - this test by default tries to unlock the PIN of a single SIM. If the device is multi-SIM, the first modem will be used by default. The -m argument can be used to specify the second modem if needed. Requirements: * a SIM with PIN-locking enabled * the current PIN code for the SIM Setup: * Ensure that FlightMode is NOT enabled * Ensure that at least one SIM with PIN-locking enabled is inserted in the phone AND STILL LOCKED! ( ie. the PIN hasn't been entered yet ) * Run this script ToDo: * If run on the emulator, make this script use console commands to configure the modem(s) for the required conditions ( ie. no SIM(s), online ) """ import dbus.mainloop.glib import simtestutil from gi.repository import GLib from simtestutil import * def parse_test_args(): parser = argparse.ArgumentParser() parser.add_argument("--pin", dest="pin", help="""Specify the SIM PIN code""", required="yes", ) parser.add_argument("-t", "--timeout", dest="timeout", help="""Specify a timeout which causes the script to exit""", default=10, ) return parse_args(parser) class TestSimPukUnlock(SimTestCase): def setUp(self): self.args = args self.product = get_product() self.timer = False self.mainloop = GLib.MainLoop() dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) SimTestCase.setUp(self) interval_ms = 1000 * int(self.args.timeout) GLib.timeout_add(interval_ms, self.timeout_cb) def pin_unlock(self, path): if self.args.debug: print("pin_unlock called," "pin: {}".format(self.args.pin)) simmanager = self.get_simmanager(path) simmanager.connect_to_signal("PropertyChanged", self.sim_listener) properties = simmanager.GetProperties() locked_pins = properties["LockedPins"] self.assertTrue(len(locked_pins) == 1) self.assertTrue(locked_pins[0] == "pin") self.assertTrue(properties["PinRequired"] == "pin") simmanager.EnterPin("pin", self.args.pin) def sim_listener(self, name, value): if self.args.debug: print("SIM property: {} changed " "to {}".format(name, str(value))) if name == "PinRequired": self.assertTrue(value == "none") self.mainloop.quit() def timeout_cb(self): if self.args.debug: print("ALL DONE - timer fired!!!") self.timer = True self.mainloop.quit() def validate_modem(self, path): self.pin_unlock(path) self.mainloop.run() self.assertTrue(self.timer == False) def test_main(self): self.main(args) if __name__ == "__main__": args = parse_test_args() sim_unittest_main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/test-sim-change-pin0000755000015600001650000000642712671500024025471 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """Tests the abilty to change a SIM PIN. This module contains a functional test which checks an ofono/rilmodem/mtkmodem instance to ensure that the a SIM PIN can be changed. NOTE - this test by default tries to change the PIN of a single SIM. If the device is multi-SIM, the first modem will be used by default. The -m argument can be used to specify the second modem if needed. Requirements: * a SIM with PIN-locking enabled * the current PIN code for the SIM Setup: * Ensure that FlightMode is NOT enabled * Ensure that at least one SIM with PIN-locking enabled is inserted in the phone AND STILL LOCKED! ( ie. the PIN hasn't been entered yet ) * Run this script Notes: On some phones, this script will enter the SIM PIN to unlock it before changing the PIN. ToDo: * If run on the emulator, make this script use console commands to configure the modem(s) for the required conditions ( ie. no SIM(s), online ) """ import simtestutil from simtestutil import * def parse_test_args(): parser = argparse.ArgumentParser() parser.add_argument("--old-pin", dest="old_pin", help="""Specify the SIM PIN code""", required="yes", ) parser.add_argument("--new-pin", dest="new_pin", help="""Specify the new SIM PIN code""", required="yes" ) return parse_args(parser) class TestSimChangePin(SimTestCase): def change_pin_lock(self, path): if self.args.debug: print("change_pin_lock called, " "pin= {} " "new_pin: {}".format(self.args.old_pin, self.args.new_pin)) simmanager = self.get_simmanager(path) properties = simmanager.GetProperties() locked_pins = properties["LockedPins"] self.assertTrue(len(locked_pins) == 1) self.assertTrue(locked_pins[0] == "pin") if self.product == "krillin" and properties["PinRequired"] == "pin": simmanager.EnterPin("pin", self.args.old_pin) simmanager.ChangePin("pin", self.args.old_pin, self.args.new_pin) simmanager.UnlockPin("pin", self.args.new_pin) simmanager.LockPin("pin", self.args.new_pin) properties = simmanager.GetProperties() locked_pins = properties["LockedPins"] self.assertTrue(len(locked_pins) == 1) self.assertTrue(locked_pins[0] == "pin") self.assertTrue(properties["PinRequired"] == "none") def validate_modem(self, path): self.change_pin_lock(path) def test_main(self): self.main(args) if __name__ == "__main__": args = parse_test_args() sim_unittest_main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/test-no-sims-offline0000755000015600001650000000463312671500024025674 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """Tests no SIM(s) and modem(s) offline. This module contains a functional test which checks a running ofono/rilmodem/mtkmodem instance to ensure that the correct DBus properties are exported when no SIM(s) are inserted AND the corresponding modem(s) are offline. As such it validates the properties of the following interfaces: * org.ofono.Modem * org.ofono.SimManager ( on supported hw ) * org.ofono.VoiceCallManager ( EmergencyNumbers ) * org.ofono.CallVolume NOTE - this test by default verifies all modems present a device. If the device is multi-SIM and not all slots have unlocked SIMs, then please use the -m to verify a specific modem. SETUP: * Power off the phone * Remove any SIMs * Power on the phone * Enable FlightMode from System Setttings ( or net indicator ) * Run this script * [Optional] Reboot the phone and re-run script * Disable FlightMode ToDo: * If run on the emulator, make this script use console commands to configure the modem(s) for the required conditions ( ie. no SIM(s), online ) """ from simtestutil import * class TestNoSimsOffline(SimTestCase): def validate_modem(self, path): self.validate_modem_properties(path, False, False) # krillin doesn't expose SimManager when offline if (self.product != "krillin" and self.product != "arale"): self.assertTrue(self.check_no_sim_present(path)) self.validate_emergency_numbers(path) self.validate_call_volume_properties(path) def test_main(self): self.main(args) if __name__ == "__main__": args = parse_args() sim_unittest_main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/test-sim-removal0000755000015600001650000001003512671500024025113 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """Tests SIM removal detection/notification This module contains a functional test which checks a running ofono/rilmodem/mtkmodem instance to ensure that the correct DBus signals are generated and the correct properties updated when a SIM is removed from a running device. The script by default will wait for 60 seconds for the SIM to be removed after which it will exit. NOTE - this test by default verifies the removal of a SIM from a single modem. If the device is multi-SIM, the first modem will be used by default. The -m argument can be used to specify the second modem if needed. SETUP: * Run this script * Remove the SIM card Options: * -t / --timer - specify a timeout after which the script will exit. ToDo: * If run on the emulator, make this script use console commands to configure the modem(s) for the required conditions ( ie. no SIM(s), online ) """ import dbus.mainloop.glib import simtestutil from gi.repository import GLib from simtestutil import * def parse_args(): parser = argparse.ArgumentParser() parser.add_argument("-t", "--timeout", dest="timeout", help="""Specify a timeout which causes the script to exit""", default=60, ) return simtestutil.parse_args(parser) class TestSimRemovalNotification(SimTestCase): def modem_listener(self, name, value, path=None): if self.args.debug: print("Modem property: '%s' changed to '%s'" % (name, str(value))) if name == "Interfaces": if "org.ofono.SimManager" in value: if (self.check_no_sim_present(path) != True): self.sim_present_failure = True self.mainloop.quit() def sim_listener(self, name, value): if self.args.debug: print("SIM property: '%s' changed to '%s'" % (name, str(value))) if name == "Present": if value != 0: self.sim_present_signal_failure = True self.mainloop.quit() def setUp(self): self.args = args self.product = get_product() self.sim_present_failure = False self.sim_present_signal_failure = False self.timeout_failure = False self.mainloop = GLib.MainLoop() dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) SimTestCase.setUp(self) interval_ms = 1000 * int(self.args.timeout) GLib.timeout_add(interval_ms, self.timeout_cb) def timeout_cb(self): if self.args.debug: print("ALL DONE - timer fired!!!") self.timeout_failure = True self.mainloop.quit() def validate_modem(self, path): modem = self.validate_modem_properties(path, True, True) if self.if_supports_sim_offline() == True: # valid SimManager properties simmanager = self.get_simmanager(path) simmanager.connect_to_signal("PropertyChanged", self.sim_listener) else: modem.connect_to_signal("PropertyChanged", self.modem_listener, path_keyword="path") def test_main(self): if args.debug: print ("ro.build.product: %s" % self.product) if len(args.modem) > 0: self.validate_modem(args.modem) else: self.validate_modem(self.modems[0][0]) self.mainloop.run() self.assertFalse(self.timeout_failure) self.assertFalse(self.sim_present_failure) self.assertFalse(self.sim_present_signal_failure) if __name__ == "__main__": args = parse_args() sim_unittest_main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/simtestutil.py0000644000015600001650000002505012671500024024720 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """SIM Test utility functions. This module contains functions used by the SIM functional test scripts. ToDo: * Add check for optional SimManager 'PreferredLanguage' property """ import argparse import dbus import subprocess import sys import unittest emergency_numbers = ["08", "000", "999", "110", "112", "911", "118", "119"] # Note - rilmodem doesn't remove 'rat' when modem is offlined; # as this is different behavior than when booted offline, "rat" # is not present in either of the following offline lists no_sim_offline_features = [ "sim" ] sim_offline_features = [ "sim", "gprs", "sms" ] no_sim_online_features = [ "rat", "sim" ] sim_online_features = [ "gprs", "ussd", "net", "sms", "rat", "sim" ] no_sim_offline_ifaces = [ "org.ofono.CallVolume", "org.ofono.VoiceCallManager" ] # LP: #1399756; RadioSettings isn't properly removed from # 'Interfaces' ( and correspondingly "rat" from 'Features' ) # when rilmodem is set offline. Neither are present if the # device boots with modems offline. That's why it's not # included in this list. sim_offline_ifaces = [ "org.ofono.ConnectionManager", "org.ofono.Phonebook", "org.ofono.CallForwarding", "org.ofono.SmartMessaging", "org.ofono.PushNotification", "org.ofono.MessageManager", "org.ofono.NetworkTime", "org.ofono.MessageWaiting", "org.ofono.SimManager", "org.ofono.CallVolume", "org.ofono.VoiceCallManager" ] no_sim_online_ifaces = [ "org.ofono.RadioSettings", "org.ofono.SimManager", "org.ofono.CallVolume", "org.ofono.VoiceCallManager" ] sim_online_ifaces = [ "org.ofono.ConnectionManager", "org.ofono.CallBarring", "org.ofono.CallSettings", "org.ofono.SupplementaryServices", "org.ofono.NetworkRegistration", "org.ofono.Phonebook", "org.ofono.CallForwarding", "org.ofono.SmartMessaging", "org.ofono.PushNotification", "org.ofono.MessageManager", "org.ofono.NetworkTime", "org.ofono.MessageWaiting", "org.ofono.RadioSettings", "org.ofono.SimManager", "org.ofono.CallVolume", "org.ofono.VoiceCallManager" ] modem_properties = [ "Revision", "Serial", "Model", "Features", "Online", "Type", "Interfaces", "Emergency", "Manufacturer", "Powered", "Lockdown" ] sim_properties = [ "Present", "FixedDialing", "BarredDialing", "SubscriberNumbers", "LockedPins", "PinRequired", "Retries" ] def get_product(): product_bytes = subprocess.check_output(["getprop", "ro.build.product"]) return product_bytes.decode('utf-8').strip('\n') def sim_unittest_main(args): if args.debug: print(args) print(sys.argv[0]) if len(args.unittest_args) == 0: args.unittest_args.append("sys.argv[0]") unittest.main(argv=args.unittest_args) else: unittest.main() def parse_args(parser=None): if parser is None: parser = argparse.ArgumentParser() parser.add_argument("-d", "--debug", dest="debug", action="store_true", help="""Enables debug verbosity""", ) parser.add_argument("-m", "--modem", dest="modem", action="store", help="""Specifies modem path""", ) parser.add_argument("unittest_args", nargs="*") parser.set_defaults(modem="") args = parser.parse_args() return args def parse_lock_test_args(): parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group(required=True) group.add_argument("--pin", dest="pin", help="""Specify the SIM PIN code""", metavar="", ) group.add_argument("--continue", dest="cont", action="store_true", help="""Continue verification after a reboot""", ) return parse_args(parser) class SimTestCase(unittest.TestCase): def setUp(self): self.bus = dbus.SystemBus() self.manager = dbus.Interface(self.bus.get_object('org.ofono', '/'), 'org.ofono.Manager') self.modems = self.manager.GetModems() def get_simmanager(self, path): return dbus.Interface(self.bus.get_object('org.ofono', path), 'org.ofono.SimManager') def if_supports_sim_offline(self): if self.product != "krillin" and self.product != "arale": return True else: return False def check_no_sim_present(self, path): properties = self.get_simmanager(path).GetProperties() return (properties["Present"] != 1) def validate_modem_properties(self, path, test_online, test_sims): modem = dbus.Interface(self.bus.get_object('org.ofono', path), 'org.ofono.Modem') properties = modem.GetProperties() if self.args.debug == True: print("[ %s ]" % path) for property in modem_properties: self.assertTrue(property in properties) self.assertTrue(properties["Revision"] != "ValueError") self.assertTrue(properties["Type"] == "hardware") self.assertTrue(properties["Manufacturer"] == "Fake Manufacturer") self.assertTrue(properties["Model"] == "Fake Modem Model") self.assertTrue(properties["Powered"] == 1) self.assertTrue(properties["Emergency"] == 0) self.assertTrue(properties["Lockdown"] == 0) # emulator == "goldfish" # if self.product == "goldfish": self.assertTrue(properties["Serial"] == "000000000000000") else: self.assertTrue(properties["Serial"] != "") self.assertTrue(properties["Online"] == test_online) # test features features = properties["Features"] if test_online: if test_sims: check_features = sim_online_features[:] else: check_features = no_sim_online_features[:] else: if self.product == "krillin" or self.product == "arale": check_features = [] else: if test_sims: check_features = sim_offline_features[:] else: check_features = no_sim_offline_features[:] for feature in check_features: if self.args.debug: print(feature) self.assertTrue(feature in features) # test interfaces ifaces = properties["Interfaces"] if test_online: if test_sims: check_ifaces = sim_online_ifaces[:] if self.product == "krillin": check_ifaces.append( "org.ofono.MtkSettings") else: check_ifaces = no_sim_online_ifaces[:] # LP: #1399746; NetworkTime is only # available on krillin in this state, # and shouldn't be without a SIM present if self.product == "krillin": check_ifaces.append( "org.ofono.NetworkTime") check_ifaces.append( "org.ofono.MtkSettings") else: # krillin no diff between sim/no-SIM when offline if self.product == "krillin" or self.product == "arale": check_ifaces = no_sim_offline_ifaces[:] check_ifaces.append("org.ofono.NetworkTime") else: if test_sims: check_ifaces = sim_offline_ifaces[:] else: check_ifaces = no_sim_offline_ifaces[:] check_ifaces.append("org.ofono.SimManager") for iface in check_ifaces: if self.args.debug: print(iface) self.assertTrue(iface in ifaces) return modem def validate_call_volume_properties(self, path): call_volume = dbus.Interface(self.bus.get_object('org.ofono', path), 'org.ofono.CallVolume') properties = call_volume.GetProperties() keys = list(properties.keys()) self.assertTrue(properties["MicrophoneVolume"] == 0) self.assertTrue(properties["SpeakerVolume"] == 0) # The value of 'Muted' differs on mako/krillin: see # bug for details: # # https://bugs.launchpad.net/ubuntu/+source/ofono/+bug/1396317 if self.product == "krillin" or self.product == "arale": self.assertTrue(properties["Muted"] == 0) else: self.assertTrue(properties["Muted"] == 1) self.assertTrue(keys.index("Muted") != "ValueError") def validate_emergency_numbers(self, path): # valid VoiceCallManager properties voice = dbus.Interface(self.bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') properties = voice.GetProperties() keys = list(properties.keys()) self.assertTrue(keys.index("EmergencyNumbers") != "ValueError") numbers = properties["EmergencyNumbers"] if self.args.debug: print("%s" % numbers) for number in numbers: self.assertTrue(number in emergency_numbers) def validate_sim_properties(self, path, mnc="", mcc="", subs=""): # valid SimManager properties properties = self.get_simmanager(path).GetProperties() keys = list(properties.keys()) for property in sim_properties: self.assertTrue(property in keys) self.assertTrue(properties["Present"] == 1) # Don't validate "Retries"; it's only populated # after failed PIN/PUK unlock attempts self.assertTrue(properties["BarredDialing"] == 0) self.assertTrue(properties["FixedDialing"] == 0) # emulator == "goldfish" if self.product == "goldfish": self.assertTrue(properties["SubscriberNumbers"][0] == "15555215554") self.assertTrue(properties["PinRequired"] == "none") # validate optional properties if "CardIdentifier" in keys and self.product == "goldfish": self.assertTrue(properties["CardIdentifier"] == "89014103211118510720") if "MobileCountryCode" in keys: if self.product == "goldfish": self.assertTrue(properties["MobileCountryCode"] == "310") elif mcc != "": self.assertTrue(properties["MobileCountryCode"] == mcc) if "MobileNetworkCode" in keys: if self.product == "goldfish": self.assertTrue(properties["MobileNetworkCode"] == "260") elif mnc != "": self.assertTrue(properties["MobileNetworkCode"] == mnc) if "SubscriberIdentity" in keys: if self.product == "goldfish": self.assertTrue(properties["SubscriberIdentity"] == "310260000000000") elif subs != "": self.assertTrue(properties["SubscriberIdentity"] == subs) def main(self, args): self.args = args self.product = get_product() if args.debug: print ("ro.build.product: %s" % self.product) if len(args.modem) > 0: self.validate_modem(args.modem) else: for modem in self.modems: self.validate_modem(modem[0]) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/sim/test-sims-online0000755000015600001650000000542512671500024025124 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. """Tests SIM(s) and modem(s) online. This module contains a functional test which checks a running ofono/rilmodem/mtkmodem instance to ensure that the correct DBus properties are exported when one or more SIMs are inserted AND the corresponding modem(s) are online. NOTE - this test by default verifies all modems present a device. If the device is multi-SIM and not all slots have unlocked SIMs, then please use the -m to verify a specific modem. SETUP: * Ensure that at least one unlocked SIM is inserted in the phone * Ensure that FlightMode is NOT enabled * Run this script Options: * --mnc - specify a mobile network code to match against * --mcc - specify a mobile country code to match against * --subscriber - specify a subscriber identity to match against ToDo: * If run on the emulator, make this script use console commands to configure the modem(s) for the required conditions ( ie. no SIM(s), online ) """ import argparse import simtestutil from simtestutil import * def parse_args(): parser = argparse.ArgumentParser() parser.add_argument("--mnc", dest="mnc", help="""Specify a MNC (mobile-network-code) to match against""", default="", ) parser.add_argument("--mcc", dest="mcc", help="""Specify a MCC (mobile-country-code) to match against""", default="", ) parser.add_argument("--subscriber", dest="subscriber", help="""Specify a SubscriberIdentity to match against""", default="", ) return simtestutil.parse_args(parser) class TestSimsOnline(SimTestCase): def validate_modem(self, path): self.validate_modem_properties(path, True, True) self.validate_sim_properties(path, self.args.mcc, self.args.mnc, self.args.subscriber) self.validate_emergency_numbers(path) self.validate_call_volume_properties(path) def test_main(self): self.main(args) if __name__ == "__main__": args = parse_args() sim_unittest_main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/rilmodem/test-sim-online0000755000015600001650000001635412671500024024154 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # # oFono - Open Source Telephony - RIL Modem test # # Copyright (C) 2014 Canonical 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. # # 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 # # This test ensures that basic modem information is available # when the modem is online and has a valid, unlocked SIM present. import argparse import dbus import time def is_modem_online(properties): """Returns the modem 'Online' property. """ return properties['Online'] def set_modem_online(modem, path): """Sets the modem 'Online' property. """ print("Setting modem %s online..." % path) modem.SetProperty("Online", dbus.Boolean(1), timeout = 120) def validate_emergency_numbers(): # valid VoiceCallManager properties voice = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') properties = voice.GetProperties() keys = list(properties.keys()) assert keys.index("EmergencyNumbers") != "ValueError" numbers = properties["EmergencyNumbers"] print("%s" % numbers) for number in numbers: assert number in ["08", "000", "999", "110", "112", "911", "118", "119"] print("OK\n") def validate_netreg(args, path): """Validates NetworkRegistration properties. """ # valid NetworkRegistration properties netreg = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.NetworkRegistration') properties = netreg.GetProperties() keys = list(properties.keys()) assert keys.index("Status") != "ValueError" assert keys.index("Strength") != "ValueError" assert keys.index("Name") != "ValueError" assert keys.index("LocationAreaCode") != "ValueError" assert keys.index("Mode") != "ValueError" assert keys.index("MobileCountryCode") != "ValueError" # TODO: no Technology reported by emulator # assert keys.index("Technology") != "ValueError" assert keys.index("CellId") != "ValueError" assert keys.index("MobileNetworkCode") != "ValueError" assert properties["Status"] == "registered" assert properties["Name"] == "Android (Android)" assert properties["LocationAreaCode"] == 65535 # TODO: emulator reports "Mode" as "auto" ! "auto-only" # assert properties["Mode"] == "auto-only" assert properties["MobileCountryCode"] == "310" # TODO: no Technology reported by emulator # assert properties["Technology"] == "umts" assert properties["MobileNetworkCode"] == "260" def parse_arguments(): """Parses command-line arguments. """ parser = argparse.ArgumentParser(description="test-sim-online script") parser.add_argument("-e", "--emulator", dest="emulator", action="store_true", help="""Indicates that this script is to run in the Touch emulator""", default=False ) parser.add_argument("-o", "--online", dest="online", action="store_true", help="""Set the modem online if required""", default=False, ) parser.add_argument("--mnc", dest="mnc", help="""Specify a MNC (mobile-network-code) to match against""", default="", ) parser.add_argument("--mcc", dest="mcc", help="""Specify a MCC (mobile-country-code) to match against""", default="", ) parser.add_argument("--subscriber", dest="subscriber", help="""Specify a SubscriberIdentity to match against""", default="", ) parser.add_argument("--netreg", dest="netreg", action="store_true", help="""Validate Netreg interface""", default=False, ) return parser.parse_args() def main(args): bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] modem = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Modem') properties = modem.GetProperties() print("[ %s ]" % path) if is_modem_online(properties) == 0 and args.online == True: set_modem_online(modem, path) time.sleep(5) keys = list(properties.keys()) assert keys.index("Features") != "ValueError" assert keys.index("Emergency") != "ValueError" assert keys.index("Powered") != "ValueError" assert keys.index("Lockdown") != "ValueError" assert keys.index("Interfaces") != "ValueError" assert keys.index("Online") != "ValueError" assert keys.index("Model") != "ValueError" assert keys.index("Revision") != "ValueError" assert keys.index("Type") != "ValueError" assert keys.index("Serial") != "ValueError" assert keys.index("Manufacturer") != "ValueError" # validate Features = net sms gprs sim assert properties["Emergency"] == 0 assert properties["Powered"] == 1 assert properties["Lockdown"] == 0 # TODO: validate Interfaces: NetworkReg, CallVolume, MessageManager, # ConnectionManager, NetworkTime, SimManager, VoiceCallManager assert properties["Online"] == 1 assert properties["Model"] == "Fake Modem Model" assert properties["Type"] == "hardware" if args.emulator: assert properties["Serial"] == "000000000000000" assert properties["Manufacturer"] == "Fake Manufacturer" # valid SimManager properties simmanager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SimManager') properties = simmanager.GetProperties() keys = list(properties.keys()) assert keys.index("Retries") != "ValueError" assert keys.index("FixedDialing") != "ValueError" assert keys.index("MobileCountryCode") != "ValueError" assert keys.index("SubscriberNumbers") != "ValueError" assert keys.index("BarredDialing") != "ValueError" assert keys.index("CardIdentifier") != "ValueError" assert keys.index("LockedPins") != "ValueError" assert keys.index("MobileNetworkCode") != "ValueError" assert keys.index("SubscriberIdentity") != "ValueError" assert keys.index("Present") != "ValueError" assert keys.index("PinRequired") != "ValueError" # TODO: Retries isn't currently populated ( lp: ) # assert properties["Retries"] == assert properties["FixedDialing"] == 0 # TODO: make these optional arguments if args.emulator: assert properties["MobileCountryCode"] == "310" elif args.mcc != "": assert properties["MobileCountryCode"] == args.mcc if args.emulator: assert properties["SubscriberNumbers"][0] == "15555215554" assert properties["BarredDialing"] == 0 if args.emulator: assert properties["CardIdentifier"] == "89014103211118510720" assert len(properties["LockedPins"]) == 0 if args.emulator: assert properties["MobileNetworkCode"] == "260" elif args.mnc != "": assert properties["MobileNetworkCode"] == args.mnc if args.emulator: assert properties["SubscriberIdentity"] == "310260000000000" elif args.subscriber != "": assert properties["SubscriberIdentity"] == args.subscriber assert properties["Present"] == 1 assert properties["PinRequired"] == "none" if args.netreg: validate_netreg(args, path) # TODO: add support for emergency numbers if __name__ == "__main__": args = parse_arguments() main(args) ofono-1.17.bzr6912+16.04.20160314.3/test/set-3g-slot0000755000015600001650000000063012671500024021364 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Activate 3G for %s..." % path) mtks = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.MtkSettings') mtks.SetProperty("Has3G", True) ofono-1.17.bzr6912+16.04.20160314.3/test/receive-sms0000755000015600001650000000140012671500024021521 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import dbus import dbus.mainloop.glib def incoming_message(message, details, path, interface): print("%s" % (message.encode('utf-8'))) for key in details: val = details[key] print(" %s = %s" % (key, val)) if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() bus.add_signal_receiver(incoming_message, bus_name="org.ofono", signal_name = "ImmediateMessage", path_keyword="path", interface_keyword="interface") bus.add_signal_receiver(incoming_message, bus_name="org.ofono", signal_name = "IncomingMessage", path_keyword="path", interface_keyword="interface") mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/list-messages0000755000015600001650000000115612671500024022067 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: print("[ %s ]" % (path)) if "org.ofono.MessageManager" not in properties["Interfaces"]: continue connman = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.MessageManager') contexts = connman.GetMessages() for path, properties in contexts: print(" [ %s ]" % (path)) for key in properties.keys(): val = str(properties[key]) print(" %s = %s" % (key, val)) print('') ofono-1.17.bzr6912+16.04.20160314.3/test/send-vcal0000755000015600001650000000125712671500024021165 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus if len(sys.argv) < 3: print("Usage: %s [modem] " % (sys.argv[0])) sys.exit(1) bus = dbus.SystemBus() if len(sys.argv) == 4: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Send vcal using modem %s ..." % path) sm = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SmartMessaging') if len(sys.argv) == 4: vcal = file(sys.argv[3]).read() path = sm.SendAppointment(sys.argv[2], vcal) else: vcal = file(sys.argv[2]).read() path = sm.SendAppointment(sys.argv[1], vcal) print(path) ofono-1.17.bzr6912+16.04.20160314.3/test/cdma-connman-enable0000755000015600001650000000071112671500024023062 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Connecting CDMA Packet Data Service on modem %s..." % path) cm = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.cdma.ConnectionManager') cm.SetProperty("Powered", dbus.Boolean(1)) ofono-1.17.bzr6912+16.04.20160314.3/test/private-chat0000755000015600001650000000074112671500024021675 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus if (len(sys.argv) != 2): print("Usage: %s [voicecall_path]" % (sys.argv[0])) exit(1) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') call = sys.argv[1] sep = call.find("/", 1) path = call[0:sep] manager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') mpty = manager.PrivateChat(call, timeout=100) for path in mpty: print(path) ofono-1.17.bzr6912+16.04.20160314.3/test/set-fast-dormancy0000755000015600001650000000113512671500024022644 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 3: path = sys.argv[1] enable = int(sys.argv[2]) elif len(sys.argv) == 2: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] enable = int(sys.argv[1]) else: print("%s [PATH] {0|1}" % (sys.argv[0])) exit(1) print("Setting fast dormancy for modem %s..." % path) radiosettings = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.RadioSettings') radiosettings.SetProperty("FastDormancy", dbus.Boolean(enable)); ofono-1.17.bzr6912+16.04.20160314.3/test/cdma-set-credentials0000755000015600001650000000127712671500024023303 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if "org.ofono.cdma.ConnectionManager" not in properties["Interfaces"]: continue cm = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.cdma.ConnectionManager') print("Connecting CDMA Packet Data Service on modem %s..." % path) if len(sys.argv) > 1: cm.SetProperty("Username", (sys.argv[1])) print("Setting Username to %s" % (sys.argv[1])) if len(sys.argv) > 2: cm.SetProperty("Password", (sys.argv[2])) print("Setting Password to %s" % (sys.argv[2])) ofono-1.17.bzr6912+16.04.20160314.3/test/disable-modem0000755000015600001650000000067012671500024022011 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Disconnecting modem %s..." % path) modem = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Modem') modem.SetProperty("Powered", dbus.Boolean(0), timeout = 120) ofono-1.17.bzr6912+16.04.20160314.3/test/answer-calls0000755000015600001650000000124112671500024021675 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: print("[ %s ]" % (path)) if "org.ofono.VoiceCallManager" not in properties["Interfaces"]: continue mgr = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') calls = mgr.GetCalls() for path, properties in calls: state = properties["State"] print("[ %s ] %s" % (path, state)) if state != "incoming": continue call = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCall') call.Answer() ofono-1.17.bzr6912+16.04.20160314.3/test/set-tty0000755000015600001650000000111612671500024020714 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 3: path = sys.argv[1] enable = int(sys.argv[2]) elif len(sys.argv) == 2: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] enable = int(sys.argv[1]) else: print("%s [PATH] {0|1}" % (sys.argv[0])) exit(1) print("Setting TTY for modem %s..." % path) texttelephony = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.TextTelephony') texttelephony.SetProperty("Enabled", dbus.Boolean(enable)); ofono-1.17.bzr6912+16.04.20160314.3/test/set-mms-details0000755000015600001650000000170512671500024022317 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if "org.ofono.ConnectionManager" not in properties["Interfaces"]: continue connman = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') contexts = connman.GetContexts() path = ""; for i, properties in contexts: if properties["Type"] == "mms": path = i break if path == "": print("No MMS context") exit(1) context = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionContext') if len(sys.argv) < 3: print("Usage: %s
" % (sys.argv[0])) exit(1) context.SetProperty("MessageProxy", sys.argv[1]) print("Setting MMS Proxy to %s" % (sys.argv[1])) context.SetProperty("MessageCenter", sys.argv[2]) print("Setting MMSC to %s" % (sys.argv[2])) ofono-1.17.bzr6912+16.04.20160314.3/test/test-ss-control-cb0000755000015600001650000000432212671500024022747 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import dbus import dbus.mainloop.glib def property_changed(property, value): print("CallBarring property %s changed to %s" % (property, value)) def print_properties(cb): properties = cb.GetProperties() for p in properties: print("property %s, value: %s" % (p, properties[p])) if __name__ == "__main__": dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() cb = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.CallBarring') cb.connect_to_signal("PropertyChanged", property_changed) ss = dbus.Interface(bus.get_object('org.ofono', modems[0]), 'org.ofono.SupplementaryServices') print_properties(cb) print("Trying invalid SS request for CB") try: print(ss.Initiate("*33#456666")) except dbus.DBusException as e: print("Failed with %s - Good" % e) print("Trying invalid SS request for CB") try: print(ss.Initiate("*33*ABC#")) except dbus.DBusException as e: print("Failed with %s - Good" % e) print("Trying invalid SS request for CB") try: print(ss.Initiate("*33**ABC#")) except dbus.DBusException as e: print("Failed with %s - Good" % e) print("Trying invalid SS request for CB") try: print(ss.Initiate("*33***12#")) except dbus.DBusException as e: print("Failed with %s - Good" % e) print("Query Outgoing All") print(ss.Initiate("*#33#")) print("Query Outgoing International") print(ss.Initiate("*#331#")) print("Query Outgoing except home country") print(ss.Initiate("*#332#")) print("Query Incoming All") print(ss.Initiate("*#35#")) print("Query Incoming while Roaming") print(ss.Initiate("*#351#")) print("Query All Outgoing") print(ss.Initiate("*#333#")) print("Query All Incoming") print(ss.Initiate("*#353#")) print("Query All") print(ss.Initiate("*#330#")) print("Enable Barring for Outgoing International calls for Voice") print(ss.Initiate("*33*3579*11#")) print_properties(cb) print("Disable All Barrings") print(ss.Initiate("#330*3579#")) mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/lockdown-modem0000755000015600001650000000105712671500024022226 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Locking and disconnecting modem %s..." % path) modem = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Modem') modem.SetProperty("Lockdown", dbus.Boolean(1)) print("press ENTER to unlock the modem %s" % path) sys.stdin.readline() modem.SetProperty("Lockdown", dbus.Boolean(0)) ofono-1.17.bzr6912+16.04.20160314.3/test/test-ss0000755000015600001650000000230312671500024020704 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() modem = modems[0][0] if (len(sys.argv) == 2): ss_code = sys.argv[1] else: modem = sys.argv[1] ss_code = sys.argv[2] ss = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.SupplementaryServices') try: ss_type, properties = ss.Initiate(ss_code, timeout=100) except dbus.DBusException as e: print("Unable to perform operation: %s" % e) sys.exit(1); if (ss_type == "CallBarring"): print("%s : Operation [ %s ] Service Type [ %s ]" % (ss_type, properties[0], properties[1])) for key in properties[2]: print("%s : %s" % (key, properties[2][key])) elif (ss_type == "CallForwarding"): print("%s : Operation [ %s ] Service Type [ %s ]" % (ss_type, properties[0], properties[1])) for key in properties[2]: print("%s : %s" % (key, properties[2][key])) elif (ss_type == "CallWaiting"): print("%s : Operation [ %s ]" % (ss_type, properties[0])) for key in properties[1]: print("%s : %s" % (key, properties[1][key])) else: print("%s : Operation [ %s ] Status [ %s ]" % (ss_type, properties[0], properties[1])) ofono-1.17.bzr6912+16.04.20160314.3/test/send-sms0000755000015600001650000000143112671500024021034 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus if len(sys.argv) < 4: print("Usage: %s [modem] " %\ (sys.argv[0])) sys.exit(1) bus = dbus.SystemBus() if len(sys.argv) == 5: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Send message using modem %s ..." % path) mm = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.MessageManager') if len(sys.argv) == 5: mm.SetProperty("UseDeliveryReports", dbus.Boolean(int(sys.argv[4]))) path = mm.SendMessage(sys.argv[2], sys.argv[3]) else: mm.SetProperty("UseDeliveryReports", dbus.Boolean(int(sys.argv[3]))) path = mm.SendMessage(sys.argv[1], sys.argv[2]) print(path) ofono-1.17.bzr6912+16.04.20160314.3/test/create-ia-context0000755000015600001650000000215012671500024022616 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if "org.ofono.ConnectionManager" not in properties["Interfaces"]: continue connman = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') contexts = connman.GetContexts() path = ""; for i, properties in contexts: if properties["Type"] == "ia": path = i break if path == "": path = connman.AddContext("ia") print("Created new context %s" % (path)) else: print("Found context %s" % (path)) context = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionContext') if len(sys.argv) > 1: context.SetProperty("AccessPointName", sys.argv[1]) print("Setting APN to %s" % (sys.argv[1])) if len(sys.argv) > 2: context.SetProperty("Username", sys.argv[2]) print("Setting username to %s" % (sys.argv[2])) if len(sys.argv) > 3: context.SetProperty("Password", sys.argv[3]) print("Setting password to %s" % (sys.argv[3])) ofono-1.17.bzr6912+16.04.20160314.3/test/set-speaker-volume0000755000015600001650000000054012671500024023033 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] cv = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.CallVolume') cv.SetProperty("SpeakerVolume", dbus.Byte(int(sys.argv[1]))) ofono-1.17.bzr6912+16.04.20160314.3/test/test-call-settings0000755000015600001650000000431012671500024023030 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import dbus import dbus.mainloop.glib import sys def property_changed(name, value): print("CallSettings property: '%s' changed to '%s'" % (name, value)) if canexit: mainloop.quit(); if __name__ == "__main__": if len(sys.argv) < 3: print("Usage: %s [modem] " % (sys.argv[0])) print("Properties can be: VoiceCallWaiting,") print(" ConnectedLineRestriction, CallingLineRestriction,") print(" CallingLinePresentation, CalledLinePresentation,") print(" ConnectedLinePresentation, HideCallerId") sys.exit(1) canexit = False dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() modem = modems[0][0] if (len(sys.argv) == 4): modem = sys.argv[1] property = sys.argv[2] newvalue = sys.argv[3] else: property = sys.argv[1] newvalue = sys.argv[2] print("Using modem %s" % modem) cs = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.CallSettings') cs.connect_to_signal("PropertyChanged", property_changed) properties = cs.GetProperties() print("Current Property values:") print("Network Status of Call Waiting - Voice: %s" %\ (properties['VoiceCallWaiting'])) print("Network Status of Connected Line Restriction: %s" %\ (properties['ConnectedLineRestriction'])) print("Network Status of Calling Line Restriction: %s" %\ (properties['CallingLineRestriction'])) print("Network Status of Calling Line Presentation: %s" %\ (properties['CallingLinePresentation'])) print("Network Status of Called Line Presentation: %s" %\ (properties['CalledLinePresentation'])) print("Network Status of Connected Line Presentation: %s" %\ (properties['ConnectedLinePresentation'])) print("Hide my Caller Id: %s" % (properties['HideCallerId'])) try: cs.SetProperty(property, newvalue) except dbus.DBusException as e: print("Unable to set property: %s" % e) sys.exit(1); print("Setting successful") if (properties[property] == newvalue): print("Setting was already set to this value") sys.exit(1); canexit = True mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/remove-contexts0000755000015600001650000000100712671500024022444 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if "org.ofono.ConnectionManager" not in properties["Interfaces"]: continue connman = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') contexts = connman.GetContexts() for path, properties in contexts: connman.RemoveContext(path) print("Removed: [ %s ]" % (path)) ofono-1.17.bzr6912+16.04.20160314.3/test/test-network-registration0000755000015600001650000000322712671500024024466 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import sys import dbus import dbus.mainloop.glib def network_property_changed(name, value): print("Network Registration property '%s' changed to '%s'" %\ (name, value)) if name == 'Name' and canexit: mainloop.quit() if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: %s [modem] - Register to PLMN on " %\ (sys.argv[0])) print("Usage: %s [modem] default - Register to default PLMN" %\ (sys.argv[0])) sys.exit(1) canexit = False dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if len(sys.argv) == 3: path = sys.argv[1] plmn = sys.argv[2] else: modems = manager.GetModems() path = modems[0][0] plmn = sys.argv[1] netreg = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.NetworkRegistration') netreg.connect_to_signal("PropertyChanged", network_property_changed) props = netreg.GetProperties() print("Status is: '%s', Operator Name is: '%s'" %\ (props['Status'], props['Name'])) if 'LocationAreaCode' in props and 'CellId' in props: print("Location: '%d', Cell: '%d'" %\ (props['LocationAreaCode'], props['CellId'])) if 'Technology' in props: print("Technology: '%s'" % (props['Technology'])) try: if plmn == 'default': netreg.Register() else: obj = bus.get_object('org.ofono', plmn); op = dbus.Interface(obj, 'org.ofono.NetworkOperator') op.Register() except dbus.DBusException as e: print("Unable to register: %s" % e) sys.exit(1) canexit = True mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/dundee-connect0000755000015600001650000000067212671500024022204 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono.dundee', '/'), 'org.ofono.dundee.Manager') devices = manager.GetDevices() path = devices[0][0] print("Connect device %s..." % path) device = dbus.Interface(bus.get_object('org.ofono.dundee', path), 'org.ofono.dundee.Device') device.SetProperty("Active", True) ofono-1.17.bzr6912+16.04.20160314.3/test/backtrace0000755000015600001650000000214312671500024021223 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import os import re import sys import subprocess if (len(sys.argv) < 3): print("Usage: %s [binary] [log]" % (sys.argv[0])) sys.exit(1) binary = sys.argv[1] count = 0 frames = [] addrs = [] log_file = open(sys.argv[2], 'r') # Extract addresses for line in log_file: matchobj = re.compile(r'\[(0x[0-9a-f]+)\]$').search(line) if matchobj: addrs.append(matchobj.group(1)) log_file.close() # Feed into addr2line command = ['addr2line', '--demangle', '--functions', '--basename', '-e', binary] command.extend(addrs) p = subprocess.Popen(command, shell=False, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True) (child_stdin, child_stdout) = (p.stdin, p.stdout) child_stdin.close() # Backtrace display for line in child_stdout: if line.startswith("??"): continue line = line.strip() frames.append(line) child_stdout.close() frame_count = len(frames); count = 0 print("-------- backtrace --------") while count < frame_count: print("[%d]: %s() [%s]" % (count/2, frames[count], frames[count + 1])) count = count + 2 print("---------------------------") ofono-1.17.bzr6912+16.04.20160314.3/test/send-vcard0000755000015600001650000000126712671500024021340 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus if len(sys.argv) < 3: print("Usage: %s [modem] " % (sys.argv[0])) sys.exit(1) bus = dbus.SystemBus() if len(sys.argv) == 4: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Send vcard using modem %s ..." % path) sm = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SmartMessaging') if len(sys.argv) == 4: vcard = file(sys.argv[3]).read() path = sm.SendBusinessCard(sys.argv[2], vcard) else: vcard = file(sys.argv[2]).read() path = sm.SendBusinessCard(sys.argv[1], vcard) print(path) ofono-1.17.bzr6912+16.04.20160314.3/test/send-ussd0000755000015600001650000000221112671500024021205 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus if (len(sys.argv) < 2): print("Usage: %s [modem] " % (sys.argv[0])) sys.exit(1) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() if (len(sys.argv) == 2): path = modems[0][0] ussdstring = sys.argv[1] else: path = sys.argv[1] ussdstring = sys.argv[2] ussd = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SupplementaryServices') properties = ussd.GetProperties() state = properties["State"] print("State: %s" % (state)) if state == "idle": result = ussd.Initiate(ussdstring, timeout=100) print(result[0] + ": " + result[1]) elif state == "user-response": print(ussd.Respond(ussdstring, timeout=100)) else: sys.exit(1); properties = ussd.GetProperties() state = properties["State"] if state == "idle": sys.exit(0) print("State: %s" % (state)) while state == "user-response": response = input("Enter response: ") print(ussd.Respond(response, timeout=100)) properties = ussd.GetProperties() state = properties["State"] if state != "idle": print("State: %s" % (state)) ofono-1.17.bzr6912+16.04.20160314.3/test/create-multiparty0000755000015600001650000000077112671500024022764 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if (len(sys.argv) == 1): modems = manager.GetModems() path = modems[0][0] elif (len(sys.argv) == 2): path = sys.argv[1] else: print("Usage: %s [modem]" % (sys.argv[0])) sys.exit(1) manager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') mpty = manager.CreateMultiparty() for path in mpty: print(path) ofono-1.17.bzr6912+16.04.20160314.3/test/set-context-property0000755000015600001650000000342312671500024023445 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if (len(sys.argv) == 3): modems = manager.GetModems() modem = modems[0][0] context_idx = 0 property = sys.argv[1] value = sys.argv[2] if property.startswith("/"): print("name can't start with '/'") exit(1) elif (len(sys.argv) == 4): modems = manager.GetModems() modem = modems[0][0] context_idx = int(sys.argv[1]) - 1 property = sys.argv[2] value = sys.argv[3] elif (len(sys.argv) == 5): modem = sys.argv[1] context_idx = int(sys.argv[2]) - 1 property = sys.argv[3] value = sys.argv[4] else: print("Usage: set-context-property [modem] [context_number] ") sys.exit(1) if property == "Active" or property == "Preferred": if value == "false" or value == "0": value = dbus.Boolean(False, variant_level=1) elif value == "true" or value == "1": value = dbus.Boolean(True, variant_level=1) else: print("Must specify 0|1 or false|true for property %s" % property) sys.exit(1) modemapi = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.Modem') properties = modemapi.GetProperties() if "org.ofono.ConnectionManager" not in properties["Interfaces"]: print("org.ofono.ConnectionManager not found") exit(2) connman = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.ConnectionManager') contexts = connman.GetContexts() if (len(contexts) == 0): print("No context available") sys.exit(1) path = contexts[context_idx][0] context = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionContext') try: context.SetProperty(property, value) except dbus.DBusException as e: print("Error setting context %s property %s: %s" %\ (path, property, str(e))) exit(2) ofono-1.17.bzr6912+16.04.20160314.3/test/list-contexts0000755000015600001650000000177612671500024022137 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: print("[ %s ]" % (path)) if "org.ofono.ConnectionManager" not in properties["Interfaces"]: continue connman = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') contexts = connman.GetContexts() for path, properties in contexts: print(" [ %s ]" % (path)) for key in properties.keys(): if key in ["Settings"] or key in ["IPv6.Settings"]: val = "{" for i in properties[key].keys(): val += " " + i + "=" if i in ["DomainNameServers"]: for n in properties[key][i]: val += n + "," elif i in ["PrefixLength", "ProxyPort"]: p = int(properties[key][i]) val += str(p) else: val += properties[key][i] val += " }" else: val = str(properties[key]) print(" %s = %s" % (key, val)) print('') ofono-1.17.bzr6912+16.04.20160314.3/test/hangup-multiparty0000755000015600001650000000057212671500024023002 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() modem = modems[0][0] if (len(sys.argv) == 2): modem = sys.argv[1] manager = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.VoiceCallManager') manager.HangupMultiparty() ofono-1.17.bzr6912+16.04.20160314.3/test/test-message-waiting0000755000015600001650000000200512671500024023342 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import sys import dbus import dbus.mainloop.glib def mw_property_changed(name, value): if name == 'VoicemailMessageCount': print("MessageWaiting property: '%s' changed to '%d'" %\ (name,value)) else: print("MessageWaiting property: '%s' changed to '%s'" %\ (name,value)) if __name__ == "__main__": dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() mw = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.MessageWaiting') mw.connect_to_signal("PropertyChanged", mw_property_changed) properties = mw.GetProperties() print("Voicemail waiting: %s" % (properties['VoicemailWaiting'])) print("Voicemail message count: %d" %\ (properties['VoicemailMessageCount'])) print("Voicemail mailbox number: %s" %\ (properties['VoicemailMailboxNumber'])) mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/list-operators0000755000015600001650000000150212671500024022271 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: print("[ %s ]" % (path)) if "org.ofono.NetworkRegistration" not in properties["Interfaces"]: continue netreg = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.NetworkRegistration') if len(sys.argv) == 2 and sys.argv[1] == 'scan': operators = netreg.Scan() else: operators = netreg.GetOperators() for path, properties in operators: print(" [ %s ]" % (path)) for key in properties.keys(): if key in ["Technologies"]: val = "" for i in properties[key]: val += i + " " else: val = str(properties[key]) print(" %s = %s" % (key, val)) print('') ofono-1.17.bzr6912+16.04.20160314.3/test/reject-calls0000755000015600001650000000124112671500024021652 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: print("[ %s ]" % (path)) if "org.ofono.VoiceCallManager" not in properties["Interfaces"]: continue mgr = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') calls = mgr.GetCalls() for path, properties in calls: state = properties["State"] print("[ %s ] %s" % (path, state)) if state != "incoming": continue call = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCall') call.Hangup() ofono-1.17.bzr6912+16.04.20160314.3/test/test-modem0000755000015600001650000000314112671500024021361 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import dbus import dbus.mainloop.glib def property_changed(name, value): print("Modem property %s changed to %s" % (name, value)) if __name__ == "__main__": dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() modem = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.Modem') modem.connect_to_signal("PropertyChanged", property_changed) properties = modem.GetProperties() if 'Name' in properties: print("Name: %s" % (properties['Name'])) if 'Manufacturer' in properties: print("Manufacturer: %s" % (properties['Manufacturer'])) if 'Model' in properties: print("Model: %s" % (properties['Model'])) if 'Revision' in properties: print("Revision: %s" % (properties['Revision'])) if 'Serial' in properties: print("Serial: %s" % (properties['Serial'])) if 'Powered' in properties: print("Powered: %s" % (properties['Powered'])) if 'Online' in properties: print("Online: %s" % (properties['Online'])) if 'Lockdown' in properties: print("Lockdown: %s" % (properties['Lockdown'])) if 'Emergency' in properties: print("Emergency: %s" % (properties['Emergency'])) if 'Features' in properties: print("Features:") for feature in properties["Features"]: print(" [ %s ]" % (feature)) if 'Interfaces' in properties: print("Interfaces:") for interface in properties["Interfaces"]: print(" [ %s ]" % (interface)) mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/set-use-sms-reports0000755000015600001650000000124312671500024023165 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 3: path = sys.argv[1] enabled = sys.argv[2] if sys.argv[2] == "off": enabled = 0 elif len(sys.argv) == 2: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] enabled = sys.argv[1] if sys.argv[1] == "off": enabled = 0 else: print("%s [PATH] on/off" % (sys.argv[0])) sys.exit(1) print("Setting delivery report use for modem %s..." % path) sms = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.MessageManager') sms.SetProperty("UseDeliveryReports", dbus.Boolean(enabled)); ofono-1.17.bzr6912+16.04.20160314.3/test/set-umts-band0000755000015600001650000000106612671500024021772 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 3: path = sys.argv[1] band = sys.argv[2] elif len(sys.argv) == 2: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] band = sys.argv[1] else: print("%s [PATH] band" % (sys.argv[0])) exit(1) print("Setting umts band for modem %s..." % path) radiosettings = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.RadioSettings') radiosettings.SetProperty("UmtsBand", band); ofono-1.17.bzr6912+16.04.20160314.3/test/disable-gprs0000755000015600001650000000066312671500024021665 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Disconnecting GPRS on modem %s..." % path) cm = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') cm.SetProperty("Powered", dbus.Boolean(0)) ofono-1.17.bzr6912+16.04.20160314.3/test/lock-pin0000755000015600001650000000114612671500024021022 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 4: path = sys.argv[1] pin_type = sys.argv[2] pin = sys.argv[3] elif len(sys.argv) == 3: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] pin_type = sys.argv[1] pin = sys.argv[2] else: print("%s [PATH] pin_type pin" % (sys.argv[0])) sys.exit(0) print("Lock %s %s for modem %s..." % (pin_type, pin, path)) simmanager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SimManager') simmanager.LockPin(pin_type, pin) ofono-1.17.bzr6912+16.04.20160314.3/test/test-call-barring0000755000015600001650000000376512671500024022631 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import sys import dbus import dbus.mainloop.glib def property_changed(name, value): print("CallBarring property: '%s' changed to '%s'" % (name, str(value))) if canexit: mainloop.quit() def print_useage(s): print("Usage: %s " % (s)) print("Usage: %s disableall " % (s)) print("Usage: %s passwd " % (s)) sys.exit(1); if __name__ == "__main__": if len(sys.argv) != 3 and len(sys.argv) != 4: print_useage(sys.argv[0]) if (sys.argv[1] == 'disableall'): pin = sys.argv[2] elif (sys.argv[1] == 'passwd'): old_password = sys.argv[2] new_password = sys.argv[3] else: if (len(sys.argv) != 4): print_useage(sys.argv[0]) property = sys.argv[1] newvalue = sys.argv[2] pin = sys.argv[3] canexit = False dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() cb = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.CallBarring') cb.connect_to_signal("PropertyChanged", property_changed) properties = cb.GetProperties() print("Barring settings for Incoming Voice calls: %s" %\ (properties['VoiceIncoming'])) print("Barring settings for Outgoing Calls: %s" %\ (properties['VoiceOutgoing'])) if (sys.argv[1] == 'disableall'): print("Disabling all barrings") try: cb.DisableAll(pin) except dbus.DBusException as e: print("Unable to Disable All barrings: ", e) sys.exit(1) elif (sys.argv[1] == 'passwd'): try: cb.ChangePassword(old_password, new_password) except dbus.DBusException as e: print("Unable to change password: ", e) sys.exit(1) print("Password changed") sys.exit(0) else: try: cb.SetProperty(property, newvalue, pin) except dbus.DBusException as e: print("Unable to set property: ", e) sys.exit(1) canexit = True mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/set-tech-preference0000755000015600001650000000111312671500024023130 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 3: path = sys.argv[1] tech = sys.argv[2] elif len(sys.argv) == 2: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] tech = sys.argv[1] else: print("%s [PATH] technology" % (sys.argv[0])) print("Setting technology preference for modem %s..." % path) radiosettings = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.RadioSettings') radiosettings.SetProperty("TechnologyPreference", tech); ofono-1.17.bzr6912+16.04.20160314.3/test/hangup-active0000755000015600001650000000130412671500024022035 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if (len(sys.argv) == 1): modems = manager.GetModems() path = modems[0][0] elif (len(sys.argv) == 2): path = sys.argv[1] else: print("Usage: %s [modem]" % (sys.argv[0])) sys.exit(1) manager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') calls = manager.GetCalls() for path, properties in calls: state = properties["State"] print("[ %s ] %s" % (path, state)) if state != "active": continue call = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCall') call.Hangup() ofono-1.17.bzr6912+16.04.20160314.3/test/test-sms0000755000015600001650000001501212671500024021062 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 # -*- coding: utf-8 -*- from gi.repository import GLib import sys import os import dbus import dbus.mainloop.glib SCA = "" lock = "off" def clear_screen(numlines=100): import os if os.name == "posix": os.system('clear') elif os.name in ("nt", "dos", "ce"): os.system('CLS') else: print('\n' * numlines) def print_menu(): print("Select test case") print("-----------------------------------------------------------") print("[0] Send SMS") print("[1] Enable delivery reports") print("[2] Disable delivery reports") print("[3] Set Service Center Address") print("[4] Set PS-only Bearer") print("[5] Set CS-only Bearer") print("[6] Set PS-preferred Bearer") print("[7] Set CS-preferred Bearer") print("[x] Exit") print("-----------------------------------------------------------") def print_send_sms_menu(): print("Select SMS type") print("-----------------------------------------------------------") print("[1] Default SMS") print("[2] Long SMS > 160 characters") print("[3] SMS with national characters") print("-----------------------------------------------------------") def message_delivery_report(sms, value): try: sms.SetProperty("UseDeliveryReports", dbus.Boolean(value)) except dbus.DBusException as e: if value == 1: print("Unable to activate Delivery Reports - FAIL") if value == 0: print("Unable to deactivate Delivery Reports - FAIL") def message_service_center_address(sms, value): try: sms.SetProperty("ServiceCenterAddress",dbus.String(value)) except dbus.DBusException as e: print("Unable to set correct Service Center Address - FAIL") def message_bearer(sms, value): try: sms.SetProperty("Bearer", dbus.String(value)) except dbus.DBusException as e: print("Unable to set Bearer[%s] - FAIL" % (value)) def message_send(sms, number, value): sms.SendMessage(dbus.String(number), value) def property_changed(property, value): print("[1]:Message Manager property %s changed to %s" %\ (property, value)) print("[1]:press ENTER") def immediate_message(property, value): print("[2]:Message Manager immediate message") print("[2]:Text::%s" % (property)) for key in value.keys(): val = str(value[key]) print("[2]:%s = %s" % (key, val)) print("[2]:press ENTER") def incoming_message(property, value): print("[3]:Message Manager incoming message") print("[3]:Text::%s" % (property)) for key in value.keys(): val = str(value[key]) print("[3]:%s = %s" % (key, val)) print("[3]:press ENTER") def message_added(property, value): print("[4]:Message Manager[Added]") print("[4]:%s"% (property)) for key in value.keys(): val = str(value[key]) print("[4]:%s = %s" % (key, val)) print("[4]:press ENTER") def message_removed(property): print("[5]:Message Manager [removed]") print("[5]: %s" % (property)) print("[5]:press ENTER") def print_sms_properties(sms): global SCA properties = sms.GetProperties() print("---------------------PROPERTIES----------------------------") for p in properties: if len(properties[p].__str__()) > 0: print("%s Message Manager rule is: %s" %\ (p, properties[p])) print("------------------------------------------" \ "-----------------") if p == "ServiceCenterAddress": SCA = properties[p] else: print("%s Message Manager rule disabled" % (p)) def stdin_handler(channel, condition, sms, value, number): global lock in_key = os.read(channel.unix_get_fd(), 160).rstrip().decode('UTF-8') if lock == "off": lock = "on" if in_key == '0': print_send_sms_menu() sms_type = input('Select SMS type: ') if sms_type == '1': message_send(sms, number, value) elif sms_type == '2': val = "abcde12345" for i in range(30): value = value + val message_send(sms, number, value) elif sms_type == '3': value = "ÖÄÅöäåµʒ×cvcvbŋ" message_send(sms, number, value) elif in_key == '1': message_delivery_report(sms, 1) send_msg = input('Send test message[y/n]?: ') if send_msg == 'y': message_send(sms, number, ("(1)" + value + ": UseDeliveryReports[TRUE]")) elif in_key == '2': message_delivery_report(sms, 0) send_msg = input('Send test message[y/n]?: ') if send_msg == 'y': message_send(sms, number, ("(2) " + value + ": UseDeliveryReports[FALSE]")) elif in_key == '3': message_service_center_address(sms, SCA) send_msg = input('Send test message[y/n]?: ') if send_msg == 'y': message_send(sms, number, ("(3) " + value + ": ServiceCenterAddress")) elif in_key == '4': message_bearer(sms, "ps-only") send_msg = input('Send test message[y/n]?: ') if send_msg == 'y': message_send(sms, number, ("(4) " + value + ": Bearer[ps-only]")) elif in_key == '5': message_bearer(sms, "cs-only") send_msg = input('Send test message[y/n]?: ') if send_msg == 'y': message_send(sms, number, ("(5) " + value + ": Bearer[cs-only]")) elif in_key == '6': message_bearer(sms, "ps-preferred") send_msg = input('Send test message[y/n]?: ') if send_msg == 'y': message_send(sms, number, ("(6) " + value + ": Bearer[ps-preferred]")) elif in_key == '7': message_bearer(sms, "cs-preferred") send_msg = input('Send test message[y/n]?: ') if send_msg == 'y': message_send(sms,number, ("(7) " + value + ": Bearer[cs-preferred]")) elif in_key == 'x': sys.exit(1) clear_screen() print_sms_properties(sms) print_menu() lock = "off" return True if __name__ == "__main__": if (len(sys.argv) < 3): print("Usage: %s [modem] " % (sys.argv[0])) sys.exit(1) dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if (len(sys.argv) == 4): path = sys.argv[1] number = sys.argv[2] value = sys.argv[3] else: modems = manager.GetModems() path = modems[0][0] number = sys.argv[1] value = sys.argv[2] sms = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.MessageManager') sms.connect_to_signal("PropertyChanged", property_changed) sms.connect_to_signal("ImmediateMessage", immediate_message) sms.connect_to_signal("IncomingMessage", incoming_message) sms.connect_to_signal("MessageAdded", message_added) sms.connect_to_signal("MessageRemoved", message_removed) clear_screen() print_sms_properties(sms) print_menu() GLib.io_add_watch(GLib.IOChannel(filedes=sys.stdin.fileno()), GLib.PRIORITY_DEFAULT, GLib.IOCondition.IN, stdin_handler, sms, value, number) mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/get-icon0000755000015600001650000000107512671500024021014 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: id = sys.argv[1] else: print("%s " % (sys.argv[0])) sys.exit(0) manager = dbus.Interface(bus.get_object("org.ofono", "/"), "org.ofono.Manager") modems = manager.GetModems() for path, properties in modems: if "org.ofono.SimManager" in properties["Interfaces"]: break sim = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SimManager') icon = sim.GetIcon(dbus.Byte(int(sys.argv[1]))) xpm = "" for byte in icon: xpm += str(byte) print(xpm) ofono-1.17.bzr6912+16.04.20160314.3/test/cancel-ussd0000755000015600001650000000074412671500024021512 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() if (len(sys.argv) == 2): path = sys.argv[1] else: path = modems[0][0] ussd = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SupplementaryServices') properties = ussd.GetProperties() state = properties["State"] print("State: %s" % (state)) if state != "idle": ussd.Cancel() ofono-1.17.bzr6912+16.04.20160314.3/test/set-call-forwarding0000755000015600001650000000273212671500024023154 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys from gi.repository import GLib import dbus import dbus.mainloop.glib def property_changed(property, value): if len(value.__str__()) > 0: print("CF property %s changed to %s" % (property, value)) else: print("CF property %s changed to disabled" % (property)) if canexit: mainloop.quit(); if __name__ == "__main__": if len(sys.argv) < 3: print("Usage: %s " % (sys.argv[0])) print("Properties can be: VoiceUnconditional, VoiceBusy,") print(" VoiceNoReply, VoiceNoReplyTimeout, VoiceNotReachable") print("Value: number to or the timeout") sys.exit(1) property = sys.argv[1] value = sys.argv[2] canexit = False dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() cf = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.CallForwarding') cf.connect_to_signal("PropertyChanged", property_changed) if (property == "VoiceNoReplyTimeout"): try: cf.SetProperty(property, dbus.UInt16(value), timeout = 100) except dbus.DBusException as e: print("Unable SetProperty %s" % e) sys.exit(1); else: try: cf.SetProperty(property, value, timeout = 100) except dbus.DBusException as e: print("Unable SetProperty %s" % e) sys.exit(1); print("Set Property successful") canexit = True mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/list-modems0000755000015600001650000000604512671500024021546 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import argparse import dbus import re def parse_arguments(): """Parses command-line arguments. """ parser = argparse.ArgumentParser(description="list-modems script") parser.add_argument("-p", "--private", dest="priv", help="""Specifies that properties considered private should be output obfuscated vs. cleartext (default).""", action="store_true", default=False ) return parser.parse_args() args = parse_arguments() bus = dbus.SystemBus() try: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') except dbus.exceptions.DBusException as e: print("Service org.ofono not found on DBus: {}".format(e)) exit(1) modems = manager.GetModems() if args.priv: p = re.compile("."); for path, properties in modems: print("[ %s ]" % (path)) for key in properties.keys(): if key in ["Interfaces", "Features"]: val = "" for i in properties[key]: val += i + " " elif key in ["Serial"] and len(properties[key]) and args.priv: val = p.sub("X", properties[key]) else: val = properties[key] print(" %s = %s" % (key, val)) for interface in properties["Interfaces"]: object = dbus.Interface(bus.get_object('org.ofono', path), interface) print(" [ %s ]" % (interface)) try: properties = object.GetProperties() except: continue for key in properties.keys(): if key in ["Calls", "MultipartyCalls", "EmergencyNumbers", "PreferredLanguages", "PrimaryContexts", "LockedPins", "Features", "AvailableTechnologies"]: val = "" for i in properties[key]: val += i + " " elif key in ["SubscriberNumbers"]: val = "" for i in properties[key]: if args.priv: val += p.sub("X", i) + " " else: val += i + " " elif key in ["ServiceNumbers"]: val = "" for i in properties[key]: val += "[" + i + "] = '" val += properties[key][i] + "' " elif key in ["MobileNetworkCodeLength", "VoicemailMessageCount", "MicrophoneVolume", "SpeakerVolume", "Strength", "DataStrength", "BatteryChargeLevel"]: val = int(properties[key]) elif key in ["MainMenu"]: val = ", ".join([ text + " (" + str(int(icon)) + ")" for text, icon in properties[key] ]) elif key in ["Retries"]: val = "" for i in properties[key]: val += "[" + i + " = " val += str(int(properties[key][i])) + "] " elif key in ["Settings"]: val = "{" for i in properties[key].keys(): val += " " + i + "=" if i in ["DomainNameServers"]: for n in properties[key][i]: val += n + "," else: val += properties[key][i] val += " }" elif (key in ["CardIdentifier", "CellId", "LocationAreaCode", "SubscriberIdentity", "VoiceUnconditional", "VoiceBusy", "VoiceNoReply", "VoiceNotReachable"] and len(str(properties[key])) and args.priv): val = p.sub("X", str(properties[key])) else: val = properties[key] print(" %s = %s" % (key, val)) print('') ofono-1.17.bzr6912+16.04.20160314.3/test/activate-context0000755000015600001650000000257212671500024022574 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if (len(sys.argv) == 3): modem = sys.argv[1] context_idx = int(sys.argv[2]) - 1 else: modem = None modems = manager.GetModems() for path, properties in modems: if "org.ofono.ConnectionManager" in properties["Interfaces"]: modem = path break if (modem is None): exit(2) if (len(sys.argv) == 1): context_idx = 0 elif (len(sys.argv) == 2): context_idx = int(sys.argv[1]) - 1 else: print("Usage: %s [modem] [context_number]" % (sys.argv[0])) exit(1) modemapi = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.Modem') properties = modemapi.GetProperties() if "org.ofono.ConnectionManager" not in properties["Interfaces"]: print("org.ofono.ConnectionManager not found") exit(2) connman = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.ConnectionManager') contexts = connman.GetContexts() if (len(contexts) == 0): print("No context available") exit(1) connman.SetProperty("Powered", dbus.Boolean(1)) path = contexts[context_idx][0] context = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionContext') try: context.SetProperty("Active", dbus.Boolean(1), timeout = 100) except dbus.DBusException as e: print("Error activating %s: %s" % (path, str(e))) exit(2) ofono-1.17.bzr6912+16.04.20160314.3/test/dial-number0000755000015600001650000000155312671500024021507 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus if (len(sys.argv) < 2): print("Usage: %s [modem] [hide_callerid]" % (sys.argv[0])) sys.exit(1) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() modem = modems[0][0] hide_callerid = "default" if (len(sys.argv) == 2): number = sys.argv[1] elif (len(sys.argv) == 3): if (sys.argv[2] == "default") or (sys.argv[2] == "enabled") or \ (sys.argv[2] == "disabled"): number = sys.argv[1] hide_callerid = sys.argv[2] else: modem = sys.argv[1] number = sys.argv[2] else: modem = sys.argv[1] number = sys.argv[2] hide_callerid = sys.argv[3] print("Using modem %s" % modem) vcm = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.VoiceCallManager') path = vcm.Dial(number, hide_callerid) print(path) ofono-1.17.bzr6912+16.04.20160314.3/test/enter-pin0000755000015600001650000000112512671500024021204 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 4: path = sys.argv[1] pin_type = sys.argv[2] pin = sys.argv[3] elif len(sys.argv) == 3: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] pin_type = sys.argv[1] pin = sys.argv[2] else: print("%s [PATH] pin_type pin" % (sys.argv[0])) sys.exit(0) print("Enter Pin for modem %s..." % path) simmanager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SimManager') simmanager.EnterPin(pin_type, pin) ofono-1.17.bzr6912+16.04.20160314.3/test/test-gnss0000755000015600001650000000474012671500024021240 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import sys import os import dbus import dbus.service import dbus.mainloop.glib GNSS_INTERFACE = "org.ofono.AssistedSatelliteNavigation" AGENT_INTERFACE = "org.ofono.PositioningRequestAgent" class PositioningAgent(dbus.service.Object): @dbus.service.method(AGENT_INTERFACE, in_signature="", out_signature="") def Release(self): print("Release") mainloop.quit() @dbus.service.method(AGENT_INTERFACE, in_signature="s", out_signature="") def Request(self, xml): print("positioning data: %s" % (xml)) @dbus.service.method(AGENT_INTERFACE, in_signature="", out_signature="") def ResetAssistanceData(self): print("Reset Assistance Data request received") def print_menu(): print("Select test case") print("-----------------------------------------------------------") print("[0] SendPositioningElement") print("[1] RegisterPositioningRequestAgent") print("[2] UnregisterPositioningRequestAgent") print("[x] Exit") print("-----------------------------------------------------------") def stdin_handler(channel, condition, gnss, path): in_key = os.read(channel.unix_get_fd(), 160).rstrip().decode('UTF-8') if in_key == '0': xml = input('type the element and press enter: ') try: gnss.SendPositioningElement(dbus.String(xml)) print("ok") except dbus.DBusException as e: print("Unable to send positioning element") elif in_key == '1': try: gnss.RegisterPositioningRequestAgent("/test/posagent") print("ok") except dbus.DBusException as e: print("Unable to register positioning agent") elif in_key == '2': try: gnss.UnregisterPositioningRequestAgent(path) print("ok") except dbus.DBusException as e: print("Unable to unregister positioning agent") elif in_key == 'x': sys.exit(1) return True if __name__ == "__main__": if len(sys.argv) < 1: sys.exit(1) dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if GNSS_INTERFACE not in properties["Interfaces"]: continue gnss = dbus.Interface(bus.get_object('org.ofono', path), GNSS_INTERFACE) path = "/test/posagent" agent = PositioningAgent(bus, path) print_menu() GLib.io_add_watch(GLib.IOChannel(filedes=sys.stdin.fileno()), GLib.PRIORITY_DEFAULT, GLib.IO_IN, stdin_handler, gnss, path) mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/test-advice-of-charge0000755000015600001650000000442212671500024023347 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import sys import dbus import dbus.mainloop.glib def cm_property_changed(name, value): print("CallMeter property: '%s' changed to '%s'" % (name, str(value))) if canexit: mainloop.quit() def cm_maximum_reached(): print("Only 30 seconds call time remains, recharge.") def print_useage(s): print("Usage: %s " % (s)) print("Usage: %s reset " % (s)) sys.exit(1); if __name__ == "__main__": if len(sys.argv) != 3 and len(sys.argv) != 4: print_useage(sys.argv[0]) if (sys.argv[1] == 'reset'): pin = sys.argv[2] else: if (len(sys.argv) != 4): print_useage(sys.argv[0]) property = sys.argv[1] newvalue = sys.argv[2] pin = sys.argv[3] canexit = False dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() cm = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.CallMeter') cm.connect_to_signal("PropertyChanged", cm_property_changed) cm.connect_to_signal("NearMaximumWarning", cm_maximum_reached) properties = cm.GetProperties() print("Currency: %s" % (properties['Currency'])) print("PricePerUnit %s" % (properties['PricePerUnit'])) print("Call meter for the current call: %s" % (properties['CallMeter'])) print("Call meter for current and previous calls: %s" %\ properties['AccumulatedCallMeter']) print("Call meter maximum, once reached calls are not possible: %s" %\ properties['AccumulatedCallMeterMaximum']) total = properties['PricePerUnit'] * properties['AccumulatedCallMeter'] print("Accumulated Meter in Currency: %s %s" %\ (total, properties['Currency'])) if (sys.argv[1] == 'reset'): print("Resetting Accumulated Call Meter") try: cm.Reset(pin) except dbus.DBusException as e: print("Unable to reset ACM: %s" % e) sys.exit(1) else: try: if property == 'AccumulatedCallMeterMaximum': newvalue = dbus.UInt32(newvalue) elif property == 'PricePerUnit': newvalue = float(newvalue) cm.SetProperty(property, newvalue, pin) except dbus.DBusException as e: print("Unable to set property: %s" % e) sys.exit(1) canexit = True mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/test-ss-control-cf0000755000015600001650000000350312671500024022753 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import dbus import dbus.mainloop.glib def property_changed(property, value): print("CallForwarding property %s changed to %s" % (property, value)) def print_properties(cf): properties = cf.GetProperties() for p in properties: if len(properties[p].__str__()) > 0: value = properties[p] else: value = "disabled" print("%s call forwarding rule: %s" % (p, value)) if __name__ == "__main__": dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() cf = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.CallForwarding') cf.connect_to_signal("PropertyChanged", property_changed) ss = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.SupplementaryServices') # Clear everything ss.Initiate("##002#") print_properties(cf) # Busy To +155542, for Voice print("Setting Busy Voice rule to +155542") print(ss.Initiate("*67*+155542*11#")) print_properties(cf) # Not Reachable to +155543, Voice print("Setting Voice Not Reachable rule to +155543") print(ss.Initiate("**62*+155543*11#")) # Not Reachable to +155544, Voice service print("Setting Voice No Reply rule to +155544, timeout=30") print(ss.Initiate("**61*+155544*11*30#")) # Unconditional to +155547, Voice print("Setting Unconditional for Voice to +155545") print(ss.Initiate("*21*+155545*10#")) print_properties(cf) print("Query all voice forwardings") print(ss.Initiate("*#002**11#")) print("Query no reply voice forwardings") print(ss.Initiate("*#61**11#")) # Deactivate everything print("Deactivating everything") print(ss.Initiate("##002#")) print_properties(cf) mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/set-cbs-topics0000755000015600001650000000105212671500024022141 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 3: path = sys.argv[1] topics = sys.argv[2] elif len(sys.argv) == 2: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] topics = sys.argv[1] else: print("%s [PATH] topics" % (sys.argv[0])) print("Setting cell broadcast topics for modem %s..." % path) cbs = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.CellBroadcast') cbs.SetProperty("Topics", topics); ofono-1.17.bzr6912+16.04.20160314.3/test/test-smart-messaging0000755000015600001650000000317012671500024023363 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import sys import dbus import dbus.service import dbus.mainloop.glib class SmartMessagingAgent(dbus.service.Object): @dbus.service.method("org.ofono.SmartMessagingAgent", in_signature="", out_signature="") def Release(self): print("Release") mainloop.quit() @dbus.service.method("org.ofono.SmartMessagingAgent", in_signature="aya{sv}", out_signature="") def ReceiveBusinessCard(self, data, props): for key in props.keys(): print("Key: %s, Value: %s" % (key, props[key])) string = "" for byte in data: string += str(byte) print("Received Business Card:") print(string) @dbus.service.method("org.ofono.SmartMessagingAgent", in_signature="aya{sv}", out_signature="") def ReceiveAppointment(self, data, props): for key in props.keys(): print("Key: %s, Value: %s" % (key, props[key])) string = "" for byte in data: string += str(byte) print("Received Appointment:") print(string) if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object("org.ofono", "/"), "org.ofono.Manager") modems = manager.GetModems() for path, properties in modems: if "org.ofono.SmartMessaging" not in properties["Interfaces"]: continue pn = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SmartMessaging') path = "/test/agent" agent = SmartMessagingAgent(bus, path) pn.RegisterAgent(path) print("Agent registered") mainloop = GLib.MainLoop() try: mainloop.run() except KeyboardInterrupt: pn.UnregisterAgent(path) mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/set-gsm-band0000755000015600001650000000106412671500024021566 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 3: path = sys.argv[1] band = sys.argv[2] elif len(sys.argv) == 2: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] band = sys.argv[1] else: print("%s [PATH] band" % (sys.argv[0])) exit(1) print("Setting gsm band for modem %s..." % path) radiosettings = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.RadioSettings') radiosettings.SetProperty("GsmBand", band); ofono-1.17.bzr6912+16.04.20160314.3/test/get-tech-preference0000755000015600001650000000072412671500024023123 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus, sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] radiosettings = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.RadioSettings') properties = radiosettings.GetProperties() print("Technology preference: %s" % (properties["TechnologyPreference"])) ofono-1.17.bzr6912+16.04.20160314.3/test/reset-contexts0000755000015600001650000000064312671500024022276 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Resetting contexts for SIM on modem %s..." % path) cm = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') cm.ResetContexts() ofono-1.17.bzr6912+16.04.20160314.3/test/test-stk-menu0000755000015600001650000002551212671500024022031 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import sys import dbus import dbus.service import dbus.mainloop.glib import signal def handler(signum, frame): raise Exception("\nSingle tone is finished!") class GoBack(dbus.DBusException): _dbus_error_name = "org.ofono.Error.GoBack" class EndSession(dbus.DBusException): _dbus_error_name = "org.ofono.Error.EndSession" class Busy(dbus.DBusException): _dbus_error_name = "org.ofono.Error.Busy" class StkAgent(dbus.service.Object): exit_on_release = True timeout_id = 0 timeout_reply_handler = None def set_exit_on_release(self, exit_on_release): self.exit_on_release = exit_on_release def timeout_callback(self): self.timeout_id = 0 self.timeout_reply_handler() return False def call_added(self, path, properties): print("call added %s" % (path)) if (self.timeout_id > 0): GLib.source_remove(self.timeout_id) self.timeout_callback() @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="", out_signature="") def Release(self): print("Release") if self.exit_on_release: mainloop.quit() @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sya(sy)n", out_signature="y") def RequestSelection(self, title, icon, items, default): print("Title: (%s)" % (title)) print("Icon: (%d)" % (int(icon))) index = 0 for item in items: print("%d. %s (icon: %d)" % (index, item[0], int(item[1]))) index += 1 print("\nDefault: %d" % (default)) select = input("Enter Selection (t, b):") if select == 'b': raise GoBack("User wishes to go back") elif select == 't': raise EndSession("User wishes to terminate session") else: return int(select) @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="syb", out_signature="", async_callbacks=("reply_func", "error_func")) def DisplayText(self, title, icon, urgent, reply_func, error_func): print("DisplayText (%s)" % (title)) print("Icon: (%d)" % (int(icon))) print("Urgent: (%d)" % (urgent)) key = input("Press return to clear ('t' terminates, " "'b' goes back, 'n' busy, " "'w' return and wait):") if key == 'w': seconds = 60 else: seconds = 0 if key == 'b': raise GoBack("User wishes to go back") elif key == 't': raise EndSession("User wishes to terminate session") elif key == 'n': raise Busy("User wishes to simulate busy screen") if (seconds > 0): print("Waiting for %d seconds" % (seconds)) self.timeout_reply_handler = reply_func self.timeout_id = GLib.timeout_add_seconds(seconds, self.timeout_callback) @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sysyyb", out_signature="s") def RequestInput(self, title, icon, default, min_chars, max_chars, hide_typing): print("Title: (%s)" % (title)) print("Icon: (%d)" % (int(icon))) print("Default: (%s)" % (default)) print("Hide typing: (%s)" % (hide_typing)) print("Enter characters, min: %d, max: %d:" % (min_chars, max_chars)) userin = input("") return userin @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sysyyb", out_signature="s") def RequestDigits(self, title, icon, default, min_chars, max_chars, hide_typing): print("Title: (%s)" % (title)) print("Icon: (%d)" % (int(icon))) print("Default: (%s)" % (default)) print("Hide typing: (%s)" % (hide_typing)) print("Enter digits, min: %d, max: %d:" % (min_chars, max_chars)) userin = input("'t' terminates, 'b' goes back:") if userin == 'b': raise GoBack("User wishes to go back") elif userin == 't': raise EndSession("User wishes to terminate session") else: return userin @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sy", out_signature="s") def RequestKey(self, title, icon): print("Title: (%s)" % (title)) print("Icon: (%d)" % (int(icon))) key = input("Enter Key (t, b):") if key == 'b': raise GoBack("User wishes to go back") elif key == 't': raise EndSession("User wishes to terminate session") else: return key @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sy", out_signature="s") def RequestDigit(self, title, icon): print("Title: (%s)" % (title)) print("Icon: (%d)" % (int(icon))) key = input("Enter Digit (t, b):") if key == 'b': raise GoBack("User wishes to go back") elif key == 't': raise EndSession("User wishes to terminate session") else: return key @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sy", out_signature="s") def RequestQuickDigit(self, title, icon): print("Title: (%s)" % (title)) print("Icon: (%d)" % (int(icon))) key = input("Quick digit (0-9, *, #, t, b):") if key == 'b': raise GoBack("User wishes to go back") elif key == 't': raise EndSession("User wishes to terminate session") else: return key @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sy", out_signature="b") def RequestConfirmation(self, title, icon): print("Title: (%s)" % (title)) print("Icon: (%d)" % (int(icon))) key = input("Enter Confirmation (t, b, y, n):") if key == 'b': raise GoBack("User wishes to go back") elif key == 't': raise EndSession("User wishes to terminate session") elif key == 'y': return True else: return False @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sy", out_signature="b") def ConfirmCallSetup(self, info, icon): print("Information: (%s)" % (info)) print("Icon: (%d)" % (int(icon))) key = input("Enter Confirmation (t, y, n):") if key == 't': raise EndSession("User wishes to terminate session") elif key == 'y': return True else: return False @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sys", out_signature="b") def ConfirmLaunchBrowser(self, info, icon, url): print("Information: (%s)" % (info)) print("Icon: (%d)" % (int(icon))) print("URL (%s)" % (url)) key = input("Enter Confirmation (y, n):") if key == 'y': return True else: return False @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="", out_signature="") def Cancel(self): print("Cancel") @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="ssy", out_signature="") def PlayTone(self, tone, text, icon): print("PlayTone: %s" % (tone)) print("Text: %s" % (text)) print("Icon: %d" % (int(icon))) signal.signal(signal.SIGALRM, handler) signal.alarm(5) try: key = input("Press return to end before end of" " single tone (t):") signal.alarm(0) if key == 't': raise EndSession("User wishes to terminate" " session") except Exception as exc: print(exc) @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="ssy", out_signature="", async_callbacks=("reply_func", "error_func")) def LoopTone(self, tone, text, icon, reply_func, error_func): print("LoopTone: %s" % (tone)) print("Text: %s" % (text)) print("Icon: %d" % (int(icon))) key = input("Press return to end before timeout " "('t' terminates, 'w' return and wait):") if key == 'w': seconds = 60 else: seconds = 0 if key == 't': raise EndSession("User wishes to terminate session") if (seconds > 0): print("Waiting for %d seconds" % (seconds)) self.timeout_reply_handler = reply_func self.timeout_id = GLib.timeout_add_seconds(seconds, self.timeout_callback) @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sy", out_signature="") def DisplayActionInformation(self, text, icon): print("Text: %s" % (text)) print("Icon: %d" % (int(icon))) @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sy", out_signature="") def DisplayAction(self, text, icon): print("Text: (%s)" % (text)) print("Icon: (%d)" % (int(icon))) key = input("Press 't' to terminate the session ") if key == 't': raise EndSession("User wishes to terminate session") @dbus.service.method("org.ofono.SimToolkitAgent", in_signature="sy", out_signature="b") def ConfirmOpenChannel(self, info, icon): print("Open channel confirmation: (%s)" % (info)) print("Icon: (%d)" % (int(icon))) key = input("Enter Confirmation (t, y, n):") if key == 't': raise EndSession("User wishes to terminate session") elif key == 'y': return True else: return False _dbus2py = { dbus.String : str, dbus.UInt32 : int, dbus.Int32 : int, dbus.Int16 : int, dbus.UInt16 : int, dbus.UInt64 : int, dbus.Int64 : int, dbus.Byte : int, dbus.Boolean : bool, dbus.ByteArray : str, dbus.ObjectPath : str } def dbus2py(d): t = type(d) if t in _dbus2py: return _dbus2py[t](d) if t is dbus.Dictionary: return dict([(dbus2py(k), dbus2py(v)) for k, v in d.items()]) if t is dbus.Array and d.signature == "y": return "".join([chr(b) for b in d]) if t is dbus.Array or t is list: return [dbus2py(v) for v in d] if t is dbus.Struct or t is tuple: return tuple([dbus2py(v) for v in d]) return d def pretty(d): d = dbus2py(d) t = type(d) if t in (dict, tuple, list) and len(d) > 0: if t is dict: d = ", ".join(["%s = %s" % (k, pretty(v)) for k, v in d.items()]) return "{ %s }" % d d = " ".join([pretty(e) for e in d]) if t is tuple: return "( %s )" % d if t is str: return "%s" % d return str(d) def property_changed(name, value): print("SimToolKit property: %s changed to '%s'" % (name, pretty(value))) if __name__ == '__main__': if len(sys.argv) == 2: mode = sys.argv[1] else: mode = 'menu' dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object("org.ofono", "/"), "org.ofono.Manager") modems = manager.GetModems() for path, properties in modems: if "org.ofono.SimToolkit" in properties["Interfaces"]: stk = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SimToolkit') if "org.ofono.VoiceCallManager" in properties["Interfaces"]: vcm = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') stk.connect_to_signal("PropertyChanged", property_changed) properties = stk.GetProperties() if mode == 'menu': if "MainMenuTitle" in properties: print("Main Menu:") print("%s" % (properties["MainMenuTitle"])) print("\n") if "MainMenu" in properties: print("Items:") index = 0 for item in properties["MainMenu"]: print("%d. %s" % (index, item[0])) index += 1 path = "/test/agent" agent = StkAgent(bus, path) try: vcm.connect_to_signal("CallAdded", agent.call_added) except: pass select = int(input("Enter Selection: ")) stk.SelectItem(select, path) elif mode == 'agent': path = "/test/agent" agent = StkAgent(bus, path) try: vcm.connect_to_signal("CallAdded", agent.call_added) except: pass stk.RegisterAgent(path) print("Default Agent registered - Waiting for STK command...") else: print("%s [menu|agent]" % (sys.argv[0])) exit(0) mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/swap-calls0000755000015600001650000000057612671500024021362 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() modem = modems[0][0] if (len(sys.argv) == 2): modem = sys.argv[1] manager = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.VoiceCallManager') manager.SwapCalls(timeout=100) ofono-1.17.bzr6912+16.04.20160314.3/test/change-pin0000755000015600001650000000126112671500024021315 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 5: path = sys.argv[1] pin_type = sys.argv[2] old_pin = sys.argv[3] new_pin = sys.argv[4] elif len(sys.argv) == 3: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] pin_type = sys.argv[1] old_pin = sys.argv[2] new_pin = sys.argv[3] else: print("%s [PATH] pin_type old_pin new_pin" % (sys.argv[0])) sys.exit(0) print("Change %s for modem %s..." % (pin_type, path)) simmanager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SimManager') simmanager.ChangePin(pin_type, old_pin, new_pin) ofono-1.17.bzr6912+16.04.20160314.3/test/set-ddr0000755000015600001650000000072512671500024020652 0ustar pbuserpbgroup00000000000000#!/usr/bin/python import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if "org.ofono.Handsfree" not in properties["Interfaces"]: continue handsfree = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Handsfree') handsfree.SetProperty("DistractedDrivingReduction",\ dbus.Boolean(int(sys.argv[1]))) ofono-1.17.bzr6912+16.04.20160314.3/test/test-push-notification0000755000015600001650000000240412671500024023724 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import sys import dbus import dbus.service import dbus.mainloop.glib class PushNotificationAgent(dbus.service.Object): @dbus.service.method("org.ofono.PushNotificationAgent", in_signature="", out_signature="") def Release(self): print("Release") mainloop.quit() @dbus.service.method("org.ofono.PushNotificationAgent", in_signature="aya{sv}", out_signature="") def ReceiveNotification(self, data, props): for key in props.keys(): print("Key: %s, Value: %s" % (key, props[key])) print("Received notification of size: %d" % len(data)) if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object("org.ofono", "/"), "org.ofono.Manager") modems = manager.GetModems() for path, properties in modems: if "org.ofono.PushNotification" not in properties["Interfaces"]: continue pn = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.PushNotification') path = "/test/agent" agent = PushNotificationAgent(bus, path) pn.RegisterAgent(path) print("Agent registered") mainloop = GLib.MainLoop() try: mainloop.run() except KeyboardInterrupt: pn.UnregisterAgent(path) mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/get-network-time0000755000015600001650000000266612671500024022520 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys _dbus2py = { dbus.String : str, dbus.UInt32 : int, dbus.Int32 : int, dbus.Int16 : int, dbus.UInt16 : int, dbus.UInt64 : int, dbus.Int64 : int, dbus.Byte : int, dbus.Boolean : bool, dbus.ByteArray : str, dbus.ObjectPath : str } def dbus2py(d): t = type(d) if t in _dbus2py: return _dbus2py[t](d) if t is dbus.Dictionary: return dict([(dbus2py(k), dbus2py(v)) for k, v in d.items()]) if t is dbus.Array and d.signature == "y": return "".join([chr(b) for b in d]) if t is dbus.Array or t is list: return [dbus2py(v) for v in d] if t is dbus.Struct or t is tuple: return tuple([dbus2py(v) for v in d]) return d def pretty(d): d = dbus2py(d) t = type(d) if t in (dict, tuple, list) and len(d) > 0: if t is dict: d = ", ".join(["%s = %s" % (k, pretty(v)) for k, v in d.items()]) return "{ %s }" % d d = " ".join([pretty(e) for e in d]) if t is tuple: return "( %s )" % d if t is str: return "%s" % d return str(d) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object("org.ofono", "/"), "org.ofono.Manager") if (len(sys.argv) == 1): modems = manager.GetModems() path = modems[0][0] elif (len(sys.argv) == 2): path = sys.argv[1] else: print("Usage: %s [modem]" % (sys.argv[0])) sys.exit(1) net_time = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.NetworkTime') time = net_time.GetNetworkTime() print(pretty(time)) ofono-1.17.bzr6912+16.04.20160314.3/test/set-msisdn0000755000015600001650000000105312671500024021371 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 3: path = sys.argv[1] number = sys.argv[2] elif len(sys.argv) == 2: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] number = sys.argv[1] else: print("%s [PATH] " % (sys.argv[0])) exit(1) print("Setting MSISDN for modem %s..." % path) sim = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SimManager') sim.SetProperty("SubscriberNumbers", [number]); ofono-1.17.bzr6912+16.04.20160314.3/test/list-calls0000755000015600001650000000125212671500024021353 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: print("[ %s ]" % (path)) if "org.ofono.VoiceCallManager" not in properties["Interfaces"]: continue mgr = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') calls = mgr.GetCalls() for path, properties in calls: print(" [ %s ]" % (path)) for key in properties.keys(): if key == 'Icon': print(" %s = %d" % (key, properties[key])) else: val = str(properties[key]) print(" %s = %s" % (key, val)) ofono-1.17.bzr6912+16.04.20160314.3/test/process-context-settings0000755000015600001650000000234712671500024024310 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import os import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if "org.ofono.ConnectionManager" not in properties["Interfaces"]: continue connman = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') contexts = connman.GetContexts() for path, properties in contexts: if properties["Active"] == dbus.Boolean(0): continue print("Configuring %s" % (path)) settings = properties["Settings"] interface = settings["Interface"] address = settings["Address"] try: gateway = settings["Gateway"] except: gateway = "0.0.0.0"; if settings["Method"] == "dhcp": print(" Run DHCP on interface %s" % (interface)) else: print(" Interface is %s" % (interface)) print(" IP address is %s" % (address)) print(" Gateway is %s" % (gateway)) cmd = "ifconfig " + interface + " " + address cmd += " netmask 255.255.255.255" os.system(cmd); for i in settings["DomainNameServers"]: print(" Nameserver is %s" % (i)) cmd = "route add -host " + i cmd +=" dev " + interface os.system(cmd); print('') ofono-1.17.bzr6912+16.04.20160314.3/test/create-internet-context0000755000015600001650000000216412671500024024062 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if "org.ofono.ConnectionManager" not in properties["Interfaces"]: continue connman = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') contexts = connman.GetContexts() path = ""; for i, properties in contexts: if properties["Type"] == "internet": path = i break if path == "": path = connman.AddContext("internet") print("Created new context %s" % (path)) else: print("Found context %s" % (path)) context = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionContext') if len(sys.argv) > 1: context.SetProperty("AccessPointName", sys.argv[1]) print("Setting APN to %s" % (sys.argv[1])) if len(sys.argv) > 2: context.SetProperty("Username", sys.argv[2]) print("Setting username to %s" % (sys.argv[2])) if len(sys.argv) > 3: context.SetProperty("Password", sys.argv[3]) print("Setting password to %s" % (sys.argv[3])) ofono-1.17.bzr6912+16.04.20160314.3/test/enable-modem0000755000015600001650000000124412671500024021632 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: try: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') except dbus.exceptions.DBusException as e: print ("Can't access org.ofono on DBus: {}".format(e)) exit(1) modems = manager.GetModems() path = modems[0][0] print("Connecting modem %s..." % path) modem = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Modem') try: modem.SetProperty("Powered", dbus.Boolean(1), timeout = 120) except dbus.exceptions.DBusException as e: print("Can't set modem 'Powered': {}".format(e)) exit(1) ofono-1.17.bzr6912+16.04.20160314.3/test/set-mic-volume0000755000015600001650000000054312671500024022154 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] cv = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.CallVolume') cv.SetProperty("MicrophoneVolume", dbus.Byte(int(sys.argv[1]))) ofono-1.17.bzr6912+16.04.20160314.3/test/monitor-dundee0000755000015600001650000000542212671500024022240 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 from gi.repository import GLib import dbus import dbus.mainloop.glib _dbus2py = { dbus.String : str, dbus.UInt32 : int, dbus.Int32 : int, dbus.Int16 : int, dbus.UInt16 : int, dbus.UInt64 : int, dbus.Int64 : int, dbus.Byte : int, dbus.Boolean : bool, dbus.ByteArray : str, dbus.ObjectPath : str } def dbus2py(d): t = type(d) if t in _dbus2py: return _dbus2py[t](d) if t is dbus.Dictionary: return dict([(dbus2py(k), dbus2py(v)) for k, v in d.items()]) if t is dbus.Array and d.signature == "y": return "".join([chr(b) for b in d]) if t is dbus.Array or t is list: return [dbus2py(v) for v in d] if t is dbus.Struct or t is tuple: return tuple([dbus2py(v) for v in d]) return d def pretty(d): d = dbus2py(d) t = type(d) if t in (dict, tuple, list) and len(d) > 0: if t is dict: d = ", ".join(["%s = %s" % (k, pretty(v)) for k, v in d.items()]) return "{ %s }" % d d = " ".join([pretty(e) for e in d]) if t is tuple: return "( %s )" % d return str(d) def property_changed(name, value, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s = %s" % (iface, path, name, pretty(value))) def added(name, value, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s %s" % (iface, member, name, pretty(value))) def removed(name, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s" % (iface, member, name)) def event(member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s" % (iface, path, member)) def message(msg, args, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s %s (%s)" % (iface, path, member, str(msg), pretty(args))) def ussd(msg, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s %s" % (iface, path, member, str(msg))) def value(value, member, path, interface): iface = interface[interface.rfind(".") + 1:] print("{%s} [%s] %s %s" % (iface, path, member, str(value))) if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() bus.add_signal_receiver(property_changed, bus_name="org.ofono.dundee", signal_name = "PropertyChanged", path_keyword="path", interface_keyword="interface") bus.add_signal_receiver(added, bus_name="org.ofono.dundee", signal_name = "DeviceAdded", member_keyword="member", path_keyword="path", interface_keyword="interface") bus.add_signal_receiver(removed, bus_name="org.ofono.dundee", signal_name = "DeviceRemoved", member_keyword="member", path_keyword="path", interface_keyword="interface") mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/cdma-connman-disable0000755000015600001650000000071412671500024023242 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Disconnecting CDMA Packet Data Service on modem %s..." % path) cm = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.cdma.ConnectionManager') cm.SetProperty("Powered", dbus.Boolean(0)) ofono-1.17.bzr6912+16.04.20160314.3/test/enable-gprs0000755000015600001650000000065012671500024021504 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Connecting modem %s..." % path) cm = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') cm.SetProperty("Powered", dbus.Boolean(1)) ofono-1.17.bzr6912+16.04.20160314.3/test/hangup-all0000755000015600001650000000056312671500024021340 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() modem = modems[0][0] if (len(sys.argv) == 2): modem = sys.argv[1] manager = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.VoiceCallManager') manager.HangupAll() ofono-1.17.bzr6912+16.04.20160314.3/test/hold-and-answer0000755000015600001650000000060212671500024022265 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() modem = modems[0][0] if (len(sys.argv) == 2): modem = sys.argv[1] manager = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.VoiceCallManager') manager.HoldAndAnswer(timeout=100) ofono-1.17.bzr6912+16.04.20160314.3/test/release-and-answer0000755000015600001650000000154112671500024022762 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if (len(sys.argv) == 1): path = None modems = manager.GetModems() for path_i, properties in modems: if "org.ofono.VoiceCallManager" in properties["Interfaces"]: path = path_i break if (path is None): exit(2) elif (len(sys.argv) == 2): path = sys.argv[1] else: print("Usage: %s [modem]" % (sys.argv[0])) exit(1) modemapi = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Modem') properties = modemapi.GetProperties() if "org.ofono.VoiceCallManager" not in properties["Interfaces"]: print("org.ofono.VoiceCallManager not found") exit(2) print("[ %s ]" % (path)) mgr = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.VoiceCallManager') mgr.ReleaseAndAnswer() ofono-1.17.bzr6912+16.04.20160314.3/test/online-modem0000755000015600001650000000065312671500024021673 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus, sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Setting modem %s online..." % path) modem = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Modem') modem.SetProperty("Online", dbus.Boolean(1), timeout = 120) ofono-1.17.bzr6912+16.04.20160314.3/test/test-cbs0000755000015600001650000001201512671500024021027 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import dbus.mainloop.glib import sys from gi.repository import GLib import os def print_menu(): print("Select test case") print("----------------------------------------------------------------") print("[0] Activate cbs") print("[1] Deactivate cbs") print("[2] Get cbs properties") print("[3] Set/Register topics") print(" If several - give topics separated with comma. \ \n E.g. 20,50-51,60") print("[4] Clear/Unregister topics") print("[5] NetReg Base Station - Get current serving cell") print("[x] Exit") print("----------------------------------------------------------------") def property_changed(property, value): if value == "" and property == "Topics": print("User selected Topics have been cleared. \ \nRegistered for emergency topics only.") else: print("Cell Broadcast property %s is changed to %s" % (property, value)) print("\nPress ENTER to continue") def incoming_broadcast(text, topic): print("Broadcast msg: %s \n Topic channel: %s" % (text, topic)) print("\nPress ENTER to continue") def emergency_broadcast(text, properties): emergType = properties["EmergencyType"] emergAlert = properties["EmergencyAlert"] print("Broadcast msg: %s \n\t Type: %s \n\t Alert: %s \n\t Popup: %s" \ % (text, emergType, emergAlert, popup)) if properties["Popup"] == True: print("Popup required.") print("\nPress ENTER to continue") def set_cbs_state(cbs, state): if state == True: print("Activating cell broadcast...") cbs.SetProperty("Powered", dbus.Boolean(1)) else: print("Deactivating cell broadcast...") cbs.SetProperty("Powered", dbus.Boolean(0)) print("-----------------------------------------------------------") def print_cbs_properties(cbs): properties = cbs.GetProperties() print("---------------------PROPERTIES----------------------------") for p in properties: if len(properties[p].__str__()) > 0: if p == "Powered": if properties[p] == True: print("Cell Broadcast is Activated.") else: print("Cell Broadcast is Deactivated.") elif p == "Topics": print("Currently set CBS %s are: %s" \ % (p, properties[p])) topics_available = True else: print("Cell Broadcast %s value empty" % (p)) print("-----------------------------------------------------------") def set_topics(cbs): print_cbs_properties(cbs) topicTemp = "" invalidData = False; index = 0 topics = input('Enter the topic ID(s) you want to register to: ') while index < len(topics): if topics[index] == ',' or topics[index] == '-': topicTemp = "" elif topics[index] >= '0' and topics[index] <= '9': topicTemp = topicTemp + topics[index] else: print("Invalid char. \"%s\" entered. Topic not set." \ % (topics[index])) invalidData = True break if topicTemp: if int(topicTemp) > 999: invalidData = True print("Invalid Topic ID %s (range 0-999). \ \nCould not register." % topicTemp) index = index + 1 if invalidData == False: try: print("Setting Cell Broadcast topics...") cbs.SetProperty("Topics", topics); except dbus.DBusException as e: print("Unable to set topic: %s" % e) print("-----------------------------------------------------------") def get_serving_cell_name(netReg): wasFound = False; properties = netReg.GetProperties() for p in properties: if p == "BaseStation": if len(properties[p].__str__()) > 0: print("Current serving cell name: %s" \ % (properties["BaseStation"])) wasFound = True; else: print("Current Serving cell name empty. \ Base Station CBS not available.") if wasFound == False: print("Base Station parameter not found. \ \nBase Station CBS not available.") print("-----------------------------------------------------------") def stdin_handler(channel, condition, cbs, netReg): in_key = os.read(channel.unix_get_fd(), 160).rstrip().decode('UTF-8') if in_key == '0': set_cbs_state(cbs, True) elif in_key == '1': set_cbs_state(cbs, False) elif in_key == '2': print_cbs_properties(cbs) elif in_key == '3': set_topics(cbs) elif in_key == '4': cbs.SetProperty("Topics", "") elif in_key == '5': get_serving_cell_name(netReg) elif in_key == 'x': sys.exit(1) print('\n' * 2) print_menu() return True if __name__ == "__main__": dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] cbs = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.CellBroadcast') netReg = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.NetworkRegistration') cbs.connect_to_signal("PropertyChanged", property_changed) cbs.connect_to_signal("IncomingBroadcast", incoming_broadcast) cbs.connect_to_signal("EmergencyBroadcast", emergency_broadcast) print('\n' * 2) print_menu() GLib.io_add_watch(GLib.IOChannel(filedes=sys.stdin.fileno()), GLib.PRIORITY_DEFAULT, GLib.IO_IN, stdin_handler, cbs, \ netReg) mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/cdma-dial-number0000755000015600001650000000071612671500024022411 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if len(sys.argv) > 2: path = sys.argv[1] number = sys.argv[2] else: modems = manager.GetModems() path, properties = modems[0] number = sys.argv[1] print("Using modem %s" % path) manager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.cdma.VoiceCallManager') manager.Dial(number) ofono-1.17.bzr6912+16.04.20160314.3/test/cdma-hangup0000755000015600001650000000057612671500024021500 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if len(sys.argv) > 2: path = sys.argv[1] else: modems = manager.GetModems() path, properties = modems[0] manager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.cdma.VoiceCallManager') manager.Hangup() ofono-1.17.bzr6912+16.04.20160314.3/test/offline-modem0000755000015600001650000000065412671500024022032 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus, sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Setting modem %s offline..." % path) modem = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.Modem') modem.SetProperty("Online", dbus.Boolean(0), timeout = 120) ofono-1.17.bzr6912+16.04.20160314.3/test/deactivate-context0000755000015600001650000000250012671500024023074 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if (len(sys.argv) == 3): modem = sys.argv[1] context_idx = int(sys.argv[2]) - 1 else: modem = None modems = manager.GetModems() for path, properties in modems: if "org.ofono.ConnectionManager" in properties["Interfaces"]: modem = path break if (modem is None): exit(2) if (len(sys.argv) == 1): context_idx = 0 elif (len(sys.argv) == 2): context_idx = int(sys.argv[1]) - 1 else: print("Usage: %s [modem] [context_number]" % (sys.argv[0])) exit(1) modemapi = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.Modem') properties = modemapi.GetProperties() if "org.ofono.ConnectionManager" not in properties["Interfaces"]: print("org.ofono.ConnectionManager not found") exit(2) connman = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.ConnectionManager') contexts = connman.GetContexts() if (len(contexts) == 0): print("No context available") sys.exit(1) path = contexts[context_idx][0] context = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionContext') try: context.SetProperty("Active", dbus.Boolean(0)) except dbus.DBusException as e: print("Error deactivating %s: %s" % (path, str(e))) exit(2) ofono-1.17.bzr6912+16.04.20160314.3/test/get-operators0000755000015600001650000000125512671500024022102 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] netreg = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.NetworkRegistration') operators = netreg.GetOperators() for entry in operators: path = entry[0] properties = entry[1] print("[ %s ]" % (path)) for key in properties.keys(): if key in ["Technologies"]: val = "" for i in properties[key]: val += i + " " else: val = str(properties[key]) print(" %s = %s" % (key, val)) print('') ofono-1.17.bzr6912+16.04.20160314.3/test/reset-pin0000755000015600001650000000106312671500024021212 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 5: path, puk_type, puk, pin = sys.argv[1:] elif len(sys.argv) == 4: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] puk_type, puk, pin = sys.argv[1:] else: print("%s [PATH] puk_type puk pin" % (sys.argv[0])) print("Reset pin for modem %s..." % path) simmanager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SimManager') simmanager.ResetPin(puk_type, puk, pin) ofono-1.17.bzr6912+16.04.20160314.3/test/display-icon0000755000015600001650000000123612671500024021701 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus if (len(sys.argv) < 2): print("Usage: %s [modem] icon_id" % (sys.argv[0])) sys.exit(1) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() modem = modems[0][0] if (len(sys.argv) == 2): icon = sys.argv[1] elif (len(sys.argv) == 3): modem = sys.argv[1] icon = sys.argv[2] print("Using modem %s" % modem) sim = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.SimManager') icon = sim.GetIcon(dbus.Byte(int(icon))) xpm = "" for byte in icon: xpm += str(byte) f = open("/tmp/icon.xpm", 'w') f.write(xpm) f.close() ofono-1.17.bzr6912+16.04.20160314.3/test/unlock-pin0000755000015600001650000000115212671500024021362 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 4: path = sys.argv[1] pin_type = sys.argv[2] pin = sys.argv[3] elif len(sys.argv) == 3: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] pin_type = sys.argv[1] pin = sys.argv[2] else: print("%s [PATH] pin_type pin" % (sys.argv[0])) sys.exit(0) print("Unlock %s %s for modem %s..." % (pin_type, pin, path)) simmanager = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.SimManager') simmanager.UnlockPin(pin_type, pin) ofono-1.17.bzr6912+16.04.20160314.3/test/disable-call-forwarding0000755000015600001650000000211712671500024023761 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys from gi.repository import GLib import dbus import dbus.mainloop.glib def property_changed(property, value): if len(value.__str__()) > 0: print("CF property %s changed to %s" % (property, value)) else: print("CF property %s changed to disabled" % (property)) if canexit: mainloop.quit(); if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: %s " % (sys.argv[0])) print("Type can be: all, conditional") sys.exit(1) canexit = False type = sys.argv[1] dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() cf = dbus.Interface(bus.get_object('org.ofono', modems[0][0]), 'org.ofono.CallForwarding') cf.connect_to_signal("PropertyChanged", property_changed) try: cf.DisableAll(type, timeout = 100) except dbus.DBusException as e: print("Unable to DisableAll %s" % e) sys.exit(1); print("DisableAll successful") canexit = True mainloop = GLib.MainLoop() mainloop.run() ofono-1.17.bzr6912+16.04.20160314.3/test/scan-for-operators0000755000015600001650000000134312671500024023031 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Scanning operators on modem %s..." % path) netreg = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.NetworkRegistration') operators = netreg.Scan(timeout=100); for entry in operators: path = entry[0] properties = entry[1] print("[ %s ]" % (path)) for key in properties.keys(): if key in ["Technologies"]: val = "" for i in properties[key]: val += i + " " else: val = str(properties[key]) print(" %s = %s" % (key, val)) print('') ofono-1.17.bzr6912+16.04.20160314.3/test/deactivate-all0000755000015600001650000000064312671500024022166 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if "org.ofono.ConnectionManager" not in properties["Interfaces"]: continue connman = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') connman.DeactivateAll() ofono-1.17.bzr6912+16.04.20160314.3/test/enable-cbs0000755000015600001650000000067412671500024021306 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import dbus import sys bus = dbus.SystemBus() if len(sys.argv) == 2: path = sys.argv[1] else: manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() path = modems[0][0] print("Enabling cell broadcast on modem %s..." % path) cbs = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.CellBroadcast') cbs.SetProperty("Powered", dbus.Boolean(1)) ofono-1.17.bzr6912+16.04.20160314.3/test/release-and-swap0000755000015600001650000000154212671500024022436 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') if (len(sys.argv) == 1): modem = None modems = manager.GetModems() for path, properties in modems: if "org.ofono.VoiceCallManager" in properties["Interfaces"]: modem = path break if (modem is None): exit(2) elif (len(sys.argv) == 2): modem = sys.argv[1] else: print("Usage: %s [modem]" % (sys.argv[0])) exit(1) modemapi = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.Modem') properties = modemapi.GetProperties() if "org.ofono.VoiceCallManager" not in properties["Interfaces"]: print("org.ofono.VoiceCallManager not found") exit(2) print("[ %s ]" % (modem)) mgr = dbus.Interface(bus.get_object('org.ofono', modem), 'org.ofono.VoiceCallManager') mgr.ReleaseAndSwap() ofono-1.17.bzr6912+16.04.20160314.3/test/set-roaming-allowed0000755000015600001650000000113512671500024023156 0ustar pbuserpbgroup00000000000000#!/usr/bin/python3 import sys import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.ofono', '/'), 'org.ofono.Manager') modems = manager.GetModems() for path, properties in modems: if "org.ofono.ConnectionManager" not in properties["Interfaces"]: continue connman = dbus.Interface(bus.get_object('org.ofono', path), 'org.ofono.ConnectionManager') if len(sys.argv) > 1: allowed = dbus.Boolean(int(sys.argv[1])) else: allowed = dbus.Boolean(1) connman.SetProperty("RoamingAllowed", allowed) print("Setting %s to RoamingAllowed=%d" % (path, allowed)) ofono-1.17.bzr6912+16.04.20160314.3/.mailmap0000644000015600001650000000073112671500024020021 0ustar pbuserpbgroup00000000000000Luiz Augusto von Dentz Zhenhua Zhang Pekka Pessi Pekka Pessi Lasse Kunnasluoto Syam Sidhardhan Michael Dietrich ofono-1.17.bzr6912+16.04.20160314.3/HACKING0000644000015600001650000000764412671500024017401 0ustar pbuserpbgroup00000000000000Hacking on oFono **************** Build tools requirements ======================== When building and testing directly from the repository it is important to have at least automake version 1.10 or later installed. All modern distributions should default to the latest version, but it seems that Debian's default is still an earlier version: Check version # dpkg -l '*automake*' Install new version # apt-get install automake1.10 # update-alternatives --config automake Working with the source code repository ======================================= The repository contains two extra scripts that accomplish the bootstrap process. One is called "bootstrap" which is the basic scripts that uses the autotools scripts to create the needed files for building and installing. It makes sure to call the right programs depending on the usage of shared or static libraries or translations etc. The second program is called "bootstrap-configure". This program will make sure to properly clean the repository, call the "bootstrap" script and then call configure with proper settings for development. It will use the best options and pass them over to configure. These options normally include the enabling the maintainer mode and the debugging features. So while in a normal source project the call "./configure ..." is used to configure the project with its settings like prefix and extra options. In case of bare repositories call "./bootstrap-configure" and it will bootstrap the repository and calls configure with all the correct options to make development easier. In case of preparing for a release with "make distcheck", don't use bootstrap-configure since it could export development specific settings. So the normal steps to checkout, build and install such a repository is like this: Checkout repository # git clone git://git.kernel.org/pub/scm/network/ofono/ofono.git # cd ofono Configure and build # ./bootstrap-configure # make Check installation # make install DESTDIR=$PWD/x # find x # rm -rf x Check distribution # make distcheck Final installation # sudo make install Remove autogenerated files # make maintainer-clean Running from within the source code repository ============================================== When using "./configure --enable-maintainer-mode" the automake scripts will use the plugins directly from within the repository. This removes the need to use "make install" when testing "ofonod". The "bootstrap-configure" automatically includes this option. Copy configuration file which specifies the required security policies # sudo cp ./src/ofono.conf /etc/dbus-1/system.d/ Run daemon in foreground with debugging # sudo ./src/ofonod -n -d 'plugins/*' For production installations or distribution packaging it is important that the "--enable-maintainer-mode" option is NOT used. Note multiple arguments to -d can be specified, colon, comma or space separated. The arguments are relative source code filenames for which debugging output should be enabled; output shell-style globs are accepted (e.g.: 'plugins/*:src/main.c'). Other debugging settings that can be toggled: - Environment variable OFONO_AT_DEBUG (set to 1): enable AT commands debugging Submitting patches ================== If you fixed a bug or you want to add support for something, patches are welcome! In order to ease the inclusion of your patch, it's important to follow some rules, otherwise it will likely be rejected by maintainers: 1) Do *not* add "Signed-off-by" lines in your commit messages. oFono does not use them, so including them is actually an error. 2) Be sure to follow the coding style rules of oFono. They are listed in doc/coding-style.txt. 3) Split your patch according to the top-level directories. E.g.: if you added a feature that touches files under 'include/', 'src/' and 'drivers/' directories, split in three separated patches, taking care not to break compilation. ofono-1.17.bzr6912+16.04.20160314.3/gisi/0000755000015600001650000000000012671500304017333 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/gisi/socket.h0000644000015600001650000000174312671500024021000 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ GIOChannel *g_isi_phonet_new(unsigned int ifindex); size_t g_isi_phonet_peek_length(GIOChannel *io); ssize_t g_isi_phonet_read(GIOChannel *io, void *restrict buf, size_t len, struct sockaddr_pn *addr); ofono-1.17.bzr6912+16.04.20160314.3/gisi/server.h0000644000015600001650000000317112671500024021013 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __GISI_SERVER_H #define __GISI_SERVER_H #ifdef __cplusplus extern "C" { #endif #include #include #include "message.h" #include "modem.h" struct _GIsiServer; typedef struct _GIsiServer GIsiServer; GIsiServer *g_isi_server_create(GIsiModem *modem, uint8_t resource, GIsiVersion *version); uint8_t g_isi_server_resource(GIsiServer *server); GIsiModem *g_isi_server_modem(GIsiServer *server); void g_isi_server_destroy(GIsiServer *server); int g_isi_server_send(GIsiServer *server, const GIsiMessage *req, const void *__restrict data, size_t len); int g_isi_server_vsend(GIsiServer *server, const GIsiMessage *req, const struct iovec *iov, size_t iovlen); GIsiPending *g_isi_server_handle(GIsiServer *server, uint8_t type, GIsiNotifyFunc notify, void *data); #ifdef __cplusplus } #endif #endif /* __GISI_SERVER_H */ ofono-1.17.bzr6912+16.04.20160314.3/gisi/client.h0000644000015600001650000000463012671500024020764 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __GISI_CLIENT_H #define __GISI_CLIENT_H #ifdef __cplusplus extern "C" { #endif #include #include "modem.h" #define G_ISI_CLIENT_DEFAULT_TIMEOUT (5) struct _GIsiClient; typedef struct _GIsiClient GIsiClient; GIsiClient *g_isi_client_create(GIsiModem *modem, uint8_t resource); GIsiModem *g_isi_client_modem(GIsiClient *client); uint8_t g_isi_client_resource(GIsiClient *client); void g_isi_client_reset(GIsiClient *client); void g_isi_client_destroy(GIsiClient *client); void g_isi_client_set_timeout(GIsiClient *client, unsigned timeout); gboolean g_isi_client_send(GIsiClient *client, const void *__restrict msg, size_t len, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); gboolean g_isi_client_vsend(GIsiClient *client, const struct iovec *iov, size_t iovlen, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); gboolean g_isi_client_send_with_timeout(GIsiClient *client, const void *__restrict msg, size_t len, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); gboolean g_isi_client_vsend_with_timeout(GIsiClient *client, const struct iovec *iov, size_t iovlen, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); gboolean g_isi_client_ind_subscribe(GIsiClient *client, uint8_t type, GIsiNotifyFunc notify, void *data); gboolean g_isi_client_ntf_subscribe(GIsiClient *client, uint8_t type, GIsiNotifyFunc notify, void *data); gboolean g_isi_client_verify(GIsiClient *client, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); #ifdef __cplusplus } #endif #endif /* __GISI_CLIENT_H */ ofono-1.17.bzr6912+16.04.20160314.3/gisi/modem.c0000644000015600001650000006304312671500024020605 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "message.h" #include "common.h" #include "modem.h" #include "socket.h" #define ISIDBG(m, fmt, ...) \ if ((m) != NULL && (m)->debug != NULL) \ m->debug("gisi: "fmt, ##__VA_ARGS__); struct _GIsiServiceMux { GIsiModem *modem; GSList *pending; GIsiVersion version; uint8_t resource; uint8_t last_utid; uint16_t object; unsigned subscriptions; unsigned registrations; gboolean reachable; gboolean version_pending; }; typedef struct _GIsiServiceMux GIsiServiceMux; struct _GIsiModem { unsigned index; uint8_t device; GHashTable *services; gboolean subs_source; int req_fd; int ind_fd; guint req_watch; guint ind_watch; GIsiDebugFunc debug; GIsiNotifyFunc trace; void *opaque; unsigned long flags; }; struct _GIsiPending { enum GIsiMessageType type; GIsiServiceMux *service; gpointer owner; guint timeout; GIsiNotifyFunc notify; GDestroyNotify destroy; void *data; uint8_t utid; uint8_t msgid; }; static GIsiServiceMux *service_get(GIsiModem *modem, uint8_t resource) { GIsiServiceMux *mux; int key = resource; mux = g_hash_table_lookup(modem->services, GINT_TO_POINTER(key)); if (mux != NULL) return mux; mux = g_try_new0(GIsiServiceMux, 1); if (mux == NULL) return NULL; g_hash_table_insert(modem->services, GINT_TO_POINTER(key), mux); mux->modem = modem; mux->resource = resource; mux->version.major = -1; mux->version.minor = -1; mux->reachable = FALSE; mux->version_pending = FALSE; return mux; } static gint utid_equal(gconstpointer a, gconstpointer b) { const GIsiPending *pa = a; const GIsiPending *pb = b; return pa->utid - pb->utid; } static const char *pend_type_to_str(enum GIsiMessageType type) { switch (type) { case GISI_MESSAGE_TYPE_REQ: return "REQ"; case GISI_MESSAGE_TYPE_IND: return "IND"; case GISI_MESSAGE_TYPE_NTF: return "NTF"; case GISI_MESSAGE_TYPE_RESP: return "RESP"; case GISI_MESSAGE_TYPE_COMMON: return "COMMON"; } return "UNKNOWN"; } static void pending_dispatch(GIsiPending *pend, GIsiMessage *msg) { GIsiModem *modem; if (pend->notify == NULL) return; modem = pend->service->modem; ISIDBG(modem, "%s %s to %p [res=0x%02X, id=0x%02X, utid=0x%02X]", g_isi_msg_strerror(msg), pend_type_to_str(pend->type), pend, g_isi_msg_resource(msg), g_isi_msg_id(msg), g_isi_msg_utid(msg)); pend->notify(msg, pend->data); } static void pending_remove_and_dispatch(GIsiPending *op, GIsiMessage *msg) { GIsiModem *modem; op->service->pending = g_slist_remove(op->service->pending, op); if (op->notify == NULL || msg == NULL) goto destroy; modem = op->service->modem; ISIDBG(modem, "%s %s to %p [res=0x%02X, id=0x%02X, utid=0x%02X]", g_isi_msg_error(msg) ? g_isi_msg_strerror(msg) : "normal", pend_type_to_str(op->type), op, g_isi_msg_resource(msg), g_isi_msg_id(msg), g_isi_msg_utid(msg)); op->notify(msg, op->data); destroy: if (op->timeout > 0) g_source_remove(op->timeout); if (op->destroy != NULL) op->destroy(op->data); g_free(op); } static void service_dispatch(GIsiServiceMux *mux, GIsiMessage *msg, gboolean is_indication) { uint8_t msgid = g_isi_msg_id(msg); uint8_t utid = g_isi_msg_utid(msg); GSList *l = mux->pending; while (l != NULL) { GSList *next = l->next; GIsiPending *pend = l->data; /* * REQs, NTFs and INDs are dispatched on message ID. While * INDs have the unique transaction ID set to zero, NTFs * typically mirror the UTID of the request that set up the * session, and REQs can naturally have any transaction ID. * * RESPs are dispatched on unique transaction ID, explicitly * ignoring the msgid. A RESP also completes a transaction, * so it needs to be removed after being notified of. * * Version query responses are dispatched in a similar fashion * as RESPs, but based on the pending type and the message ID. * Some of these may be synthesized, but nevertheless need to * be removed. */ if (pend->type < GISI_MESSAGE_TYPE_RESP && pend->msgid == msgid) { pending_dispatch(pend, msg); } else if (pend->type == GISI_MESSAGE_TYPE_RESP && !is_indication && pend->utid == utid) { pending_remove_and_dispatch(pend, msg); break; } else if (pend->type == GISI_MESSAGE_TYPE_COMMON && msgid == COMMON_MESSAGE && pend->msgid == COMM_ISI_VERSION_GET_REQ) { pending_remove_and_dispatch(pend, msg); } l = next; } } static void common_message_decode(GIsiServiceMux *mux, GIsiMessage *msg) { uint8_t code; uint8_t major; uint8_t minor; if (!g_isi_msg_data_get_byte(msg, 0, &code)) return; switch (code) { case COMM_ISA_ENTITY_NOT_REACHABLE_RESP: mux->reachable = FALSE; msg->error = ENOENT; break; case COMM_ISI_VERSION_GET_RESP: if (g_isi_msg_data_get_byte(msg, 1, &major) && g_isi_msg_data_get_byte(msg, 2, &minor)) { mux->version.major = major; mux->version.minor = minor; } /* fall through */ default: /* * PN_SIM doesn't support ISI version, but sends a * garbage message as a response. Work around this * modem wart. */ mux->object = g_isi_msg_object(msg); mux->version_pending = FALSE; mux->reachable = TRUE; break; } msg->version = &mux->version; } static void firewall_notify_handle(GIsiModem *modem, GIsiMessage *msg) { uint8_t id; if (!g_isi_msg_data_get_byte(msg, 0, &id)) return; ISIDBG(modem, "firewall blocked message 0x%02X", id); } static gboolean isi_callback(GIOChannel *channel, GIOCondition cond, gpointer data) { GIsiModem *modem = data; int len; int fd; if (cond & (G_IO_NVAL|G_IO_HUP)) { ISIDBG(modem, "Unexpected event on PhoNet channel %p", channel); return FALSE; } fd = g_io_channel_unix_get_fd(channel); len = g_isi_phonet_peek_length(channel); if (len > 0) { struct sockaddr_pn addr; uint32_t buf[(len + 3) / 4]; GIsiServiceMux *mux; GIsiMessage msg; unsigned key; len = g_isi_phonet_read(channel, buf, len, &addr); if (len < 2) return TRUE; msg.addr = &addr; msg.error = 0; msg.data = buf; msg.len = len; if (modem->trace != NULL) modem->trace(&msg, NULL); key = addr.spn_resource; mux = g_hash_table_lookup(modem->services, GINT_TO_POINTER(key)); if (mux == NULL) { /* * Unfortunately, the FW report has the wrong * resource ID in the N900 modem. */ if (key == PN_FIREWALL) firewall_notify_handle(modem, &msg); return TRUE; } msg.version = &mux->version; if (g_isi_msg_id(&msg) == COMMON_MESSAGE) common_message_decode(mux, &msg); service_dispatch(mux, &msg, fd == modem->ind_fd); } return TRUE; } static gboolean modem_subs_update(gpointer data) { GHashTableIter iter; gpointer keyptr, value; GIsiModem *modem = data; gboolean legacy = modem->flags & GISI_MODEM_FLAG_USE_LEGACY_SUBSCRIBE; struct sockaddr_pn commgr = { .spn_family = AF_PHONET, .spn_resource = PN_COMMGR, .spn_dev = modem->device, }; uint8_t msg[4 + 1024] = { 0, /* UTID */ legacy ? PNS_SUBSCRIBED_RESOURCES_IND : PNS_SUBSCRIBED_RESOURCES_EXTEND_IND, 0, /* Count */ 0, /* Filler */ }; uint8_t count = 0; size_t len; modem->subs_source = 0; g_hash_table_iter_init(&iter, modem->services); while (g_hash_table_iter_next(&iter, &keyptr, &value)) { GIsiServiceMux *mux = value; if (mux->subscriptions == 0) continue; if (legacy) msg[3 + count] = mux->resource; else /* Resource field is 32bit and Little-endian */ msg[4 + count * 4 + 3] = mux->resource; count++; } len = legacy ? 3 + count : 4 + count * 4; msg[2] = count; sendto(modem->ind_fd, msg, len, MSG_NOSIGNAL, (void *) &commgr, sizeof(commgr)); return FALSE; } static void modem_subs_update_when_idle(GIsiModem *modem) { if (modem->subs_source > 0) return; modem->subs_source = g_idle_add(modem_subs_update, modem); } static void service_name_register(GIsiServiceMux *mux) { struct sockaddr_pn namesrv = { .spn_family = AF_PHONET, .spn_resource = PN_NAMESERVICE, .spn_dev = mux->modem->device, }; uint8_t msg[] = { 0, PNS_NAME_ADD_REQ, 0, 0, 0, 0, 0, mux->resource, /* 32-bit Big-Endian name */ 0, 0, /* device/object */ 0, 0, /* filler */ }; uint16_t object = 0; if (ioctl(mux->modem->req_fd, SIOCPNGETOBJECT, &object) < 0) { ISIDBG(mux->modem, "ioctl(SIOCPNGETOBJECT): %s", strerror(errno)); return; } /* Fill in the object ID */ msg[8] = object >> 8; msg[9] = object & 0xFF; sendto(mux->modem->req_fd, msg, sizeof(msg), MSG_NOSIGNAL, (void *) &namesrv, sizeof(namesrv)); } static void service_name_deregister(GIsiServiceMux *mux) { struct sockaddr_pn namesrv = { .spn_family = AF_PHONET, .spn_resource = PN_NAMESERVICE, .spn_dev = mux->modem->device, }; const uint8_t msg[] = { 0, PNS_NAME_REMOVE_REQ, 0, 0, 0, 0, 0, mux->resource, }; sendto(mux->modem->req_fd, msg, sizeof(msg), MSG_NOSIGNAL, (void *) &namesrv, sizeof(namesrv)); } static void pending_destroy(gpointer value, gpointer user) { GIsiPending *op = value; if (op == NULL) return; if (op->timeout > 0) g_source_remove(op->timeout); if (op->destroy != NULL) op->destroy(op->data); g_free(op); } static void service_finalize(gpointer value) { GIsiServiceMux *mux = value; GIsiModem *modem = mux->modem; if (mux->subscriptions > 0) modem_subs_update_when_idle(modem); if (mux->registrations > 0) service_name_deregister(mux); g_slist_foreach(mux->pending, pending_destroy, NULL); g_slist_free(mux->pending); g_free(mux); } GIsiModem *g_isi_modem_create(unsigned index) { GIsiModem *modem; GIOChannel *inds; GIOChannel *reqs; if (index == 0) { errno = ENODEV; return NULL; } modem = g_try_new0(GIsiModem, 1); if (modem == NULL) { errno = ENOMEM; return NULL; } inds = g_isi_phonet_new(index); reqs = g_isi_phonet_new(index); if (inds == NULL || reqs == NULL) { g_free(modem); return NULL; } modem->req_fd = g_io_channel_unix_get_fd(reqs); modem->req_watch = g_io_add_watch(reqs, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, isi_callback, modem); modem->ind_fd = g_io_channel_unix_get_fd(inds); modem->ind_watch = g_io_add_watch(inds, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, isi_callback, modem); g_io_channel_unref(reqs); g_io_channel_unref(inds); modem->index = index; modem->services = g_hash_table_new_full(g_direct_hash, NULL, NULL, service_finalize); return modem; } GIsiModem *g_isi_modem_create_by_name(const char *name) { return g_isi_modem_create(if_nametoindex(name)); } void *g_isi_modem_set_userdata(GIsiModem *modem, void *data) { void *old; if (modem == NULL) return NULL; old = modem->opaque; modem->opaque = data; return old; } void *g_isi_modem_get_userdata(GIsiModem *modem) { if (modem == NULL) return NULL; return modem->opaque; } unsigned long g_isi_modem_flags(GIsiModem *modem) { if (modem == NULL) return 0; return modem->flags; } void g_isi_modem_set_flags(GIsiModem *modem, unsigned long flags) { if (modem == NULL) return; modem->flags = flags; } uint8_t g_isi_modem_device(GIsiModem *modem) { if (modem == NULL) return 0; return modem->device; } int g_isi_modem_set_device(GIsiModem *modem, uint8_t remote) { if (modem == NULL) return -EINVAL; if (remote != PN_DEV_HOST && remote != PN_DEV_MODEM) return -EINVAL; modem->device = remote; return 0; } static uint8_t service_next_utid(GIsiServiceMux *mux) { if (mux->last_utid == 0x00 || mux->last_utid == 0xFF) return 1; return mux->last_utid + 1; } static void service_subs_incr(GIsiServiceMux *mux) { GIsiModem *modem = mux->modem; mux->subscriptions++; if (mux->subscriptions == 1) modem_subs_update_when_idle(modem); } static void service_subs_decr(GIsiServiceMux *mux) { GIsiModem *modem = mux->modem; if (mux->subscriptions == 0) return; mux->subscriptions--; if (mux->subscriptions == 0) modem_subs_update_when_idle(modem); } static void service_regs_incr(GIsiServiceMux *mux) { mux->registrations++; if (mux->registrations == 1) service_name_register(mux); } static void service_regs_decr(GIsiServiceMux *mux) { if (mux->registrations == 0) return; mux->registrations--; if (mux->registrations == 0) service_name_deregister(mux); } void g_isi_modem_destroy(GIsiModem *modem) { if (modem == NULL) return; g_hash_table_remove_all(modem->services); if (modem->subs_source > 0) { g_source_remove(modem->subs_source); modem_subs_update(modem); } g_hash_table_unref(modem->services); if (modem->ind_watch > 0) g_source_remove(modem->ind_watch); if (modem->req_watch > 0) g_source_remove(modem->req_watch); g_free(modem); } unsigned g_isi_modem_index(GIsiModem *modem) { return modem != NULL ? modem->index : 0; } GIsiPending *g_isi_request_send(GIsiModem *modem, uint8_t resource, const void *__restrict buf, size_t len, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { struct sockaddr_pn dst = { .spn_family = AF_PHONET, .spn_resource = resource, .spn_dev = modem->device, }; return g_isi_request_sendto(modem, &dst, buf, len, timeout, notify, data, destroy); }; GIsiPending *g_isi_request_vsend(GIsiModem *modem, uint8_t resource, const struct iovec *__restrict iov, size_t iovlen, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { struct sockaddr_pn dst = { .spn_family = AF_PHONET, .spn_resource = resource, .spn_dev = modem->device, }; return g_isi_request_vsendto(modem, &dst, iov, iovlen, timeout, notify, data, destroy); } GIsiPending *g_isi_request_sendto(GIsiModem *modem, struct sockaddr_pn *dst, const void *__restrict buf, size_t len, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { const struct iovec iov = { .iov_base = (void *)buf, .iov_len = len, }; return g_isi_request_vsendto(modem, dst, &iov, 1, timeout, notify, data, destroy); } static void vtrace(struct sockaddr_pn *dst, const struct iovec *__restrict iov, size_t iovlen, size_t total_len, GIsiNotifyFunc trace) { uint8_t buffer[total_len]; uint8_t *ptr = buffer; GIsiMessage msg = { .addr = dst, .data = (const void *)buffer, .len = total_len, }; size_t i; for (i = 0; i < iovlen; i++) { memcpy(ptr, iov[i].iov_base, iov[i].iov_len); ptr += iov[i].iov_len; } trace(&msg, NULL); } static gboolean resp_timeout(gpointer data) { GIsiPending *op = data; GIsiMessage msg = { .error = ETIMEDOUT, }; op->timeout = 0; pending_remove_and_dispatch(op, &msg); return FALSE; } GIsiPending *g_isi_request_vsendto(GIsiModem *modem, struct sockaddr_pn *dst, const struct iovec *__restrict iov, size_t iovlen, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { struct iovec _iov[1 + iovlen]; struct msghdr msg = { .msg_name = (void *)dst, .msg_namelen = sizeof(struct sockaddr_pn), .msg_iov = _iov, .msg_iovlen = 1 + iovlen, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0, }; ssize_t ret; size_t i, len; GIsiServiceMux *mux; GIsiPending *resp; if (modem == NULL) { errno = EINVAL; return NULL; } mux = service_get(modem, dst->spn_resource); if (mux == NULL) { errno = ENOMEM; return NULL; } resp = g_try_new0(GIsiPending, 1); if (resp == NULL) { errno = ENOMEM; return NULL; } resp->type = GISI_MESSAGE_TYPE_RESP; resp->utid = service_next_utid(mux); resp->service = mux; resp->notify = notify; resp->destroy = destroy; resp->data = data; if (g_slist_find_custom(mux->pending, resp, utid_equal)) { /* * FIXME: perhaps retry with randomized access after * initial miss. Although if the rate at which * requests are sent is so high that the unique * transaction ID wraps, it's likely there is * something wrong and we might as well fail here. */ ISIDBG(modem, "ERROR: UTID wrapped, modem busy"); errno = EBUSY; goto error; } _iov[0].iov_base = &resp->utid; _iov[0].iov_len = 1; for (i = 0, len = 1; i < iovlen; i++) { _iov[1 + i] = iov[i]; len += iov[i].iov_len; } if (modem->trace != NULL) vtrace(dst, _iov, 1 + iovlen, len, modem->trace); ret = sendmsg(modem->req_fd, &msg, MSG_NOSIGNAL); if (ret == -1) goto error; if (ret != (ssize_t)len) { errno = EMSGSIZE; goto error; } mux->pending = g_slist_prepend(mux->pending, resp); if (timeout > 0) resp->timeout = g_timeout_add_seconds(timeout, resp_timeout, resp); mux->last_utid = resp->utid; return resp; error: g_free(resp); return NULL; } uint8_t g_isi_request_utid(GIsiPending *resp) { return resp != NULL ? resp->utid : 0; } void g_isi_pending_remove(GIsiPending *op) { if (op == NULL) return; if (op->type == GISI_MESSAGE_TYPE_IND) service_subs_decr(op->service); if (op->type == GISI_MESSAGE_TYPE_REQ) service_regs_decr(op->service); if (op->type == GISI_MESSAGE_TYPE_RESP && op->notify != NULL) { GIsiMessage msg = { .error = ESHUTDOWN, }; pending_remove_and_dispatch(op, &msg); return; } op->service->pending = g_slist_remove(op->service->pending, op); pending_destroy(op, NULL); } static void foreach_destroy(GIsiPending *op) { if (op->type == GISI_MESSAGE_TYPE_IND) service_subs_decr(op->service); if (op->type == GISI_MESSAGE_TYPE_REQ) service_regs_decr(op->service); if (op->type == GISI_MESSAGE_TYPE_RESP && op->notify != NULL) { GIsiMessage msg = { .error = ESHUTDOWN, }; pending_dispatch(op, &msg); } pending_destroy(op, NULL); } void g_isi_pending_set_owner(GIsiPending *op, gpointer owner) { if (op == NULL) return; op->owner = owner; } void g_isi_remove_pending_by_owner(GIsiModem *modem, uint8_t resource, gpointer owner) { GIsiServiceMux *mux; GSList *l; GSList *next; GIsiPending *op; GSList *owned = NULL; mux = service_get(modem, resource); if (mux == NULL) return; for (l = mux->pending; l != NULL; l = next) { next = l->next; op = l->data; if (op->owner != owner) continue; mux->pending = g_slist_remove_link(mux->pending, l); l->next = owned; owned = l; } for (l = owned; l != NULL; l = l->next) { op = l->data; foreach_destroy(op); } g_slist_free(owned); } GIsiPending *g_isi_ntf_subscribe(GIsiModem *modem, uint8_t resource, uint8_t msgid, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { GIsiServiceMux *mux; GIsiPending *ntf; mux = service_get(modem, resource); if (mux == NULL) { errno = ENOMEM; return NULL; } ntf = g_try_new0(GIsiPending, 1); if (ntf == NULL) { errno = ENOMEM; return NULL; } ntf->type = GISI_MESSAGE_TYPE_NTF; ntf->service = mux; ntf->notify = notify; ntf->data = data; ntf->destroy = destroy; ntf->msgid = msgid; mux->pending = g_slist_append(mux->pending, ntf); ISIDBG(modem, "Subscribed to %s (%p) [res=0x%02X, id=0x%02X]", pend_type_to_str(ntf->type), ntf, resource, msgid); return ntf; } GIsiPending *g_isi_service_bind(GIsiModem *modem, uint8_t resource, uint8_t msgid, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { GIsiServiceMux *mux; GIsiPending *srv; mux = service_get(modem, resource); if (mux == NULL) { errno = ENOMEM; return NULL; } srv = g_try_new0(GIsiPending, 1); if (srv == NULL) { errno = ENOMEM; return NULL; } srv->type = GISI_MESSAGE_TYPE_REQ; srv->service = mux; srv->notify = notify; srv->data = data; srv->destroy = destroy; srv->msgid = msgid; mux->pending = g_slist_append(mux->pending, srv); ISIDBG(modem, "Bound service for %s (%p) [res=0x%02X, id=0x%02X]", pend_type_to_str(srv->type), srv, resource, msgid); service_regs_incr(mux); return srv; } GIsiPending *g_isi_ind_subscribe(GIsiModem *modem, uint8_t resource, uint8_t msgid, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { GIsiServiceMux *mux; GIsiPending *ind; mux = service_get(modem, resource); if (mux == NULL) { errno = ENOMEM; return NULL; } ind = g_try_new0(GIsiPending, 1); if (ind == NULL) { errno = ENOMEM; return NULL; } ind->type = GISI_MESSAGE_TYPE_IND; ind->service = mux; ind->notify = notify; ind->data = data; ind->destroy = destroy; ind->msgid = msgid; mux->pending = g_slist_append(mux->pending, ind); ISIDBG(modem, "Subscribed for %s (%p) [res=0x%02X, id=0x%02X]", pend_type_to_str(ind->type), ind, resource, msgid); service_subs_incr(mux); return ind; } int g_isi_response_send(GIsiModem *modem, const GIsiMessage *req, const void *__restrict buf, size_t len) { const struct iovec iov = { .iov_base = (void *)buf, .iov_len = len, }; return g_isi_response_vsend(modem, req, &iov, 1); } int g_isi_response_vsend(GIsiModem *modem, const GIsiMessage *req, const struct iovec *__restrict iov, size_t iovlen) { struct iovec _iov[1 + iovlen]; uint8_t utid; size_t i; utid = g_isi_msg_utid(req); _iov[0].iov_base = &utid; _iov[0].iov_len = 1; for (i = 0; i < iovlen; i++) _iov[1 + i] = iov[i]; return g_isi_modem_vsendto(modem, req->addr, _iov, 1 + iovlen); } int g_isi_modem_send(GIsiModem *modem, uint8_t resource, const void *__restrict buf, size_t len) { struct sockaddr_pn dst = { .spn_family = AF_PHONET, .spn_resource = resource, .spn_dev = modem->device, }; return g_isi_modem_sendto(modem, &dst, buf, len); } int g_isi_modem_vsend(GIsiModem *modem, uint8_t resource, const struct iovec *__restrict iov, size_t iovlen) { struct sockaddr_pn dst = { .spn_family = AF_PHONET, .spn_resource = resource, .spn_dev = modem->device, }; return g_isi_modem_vsendto(modem, &dst, iov, iovlen); } int g_isi_modem_sendto(GIsiModem *modem, struct sockaddr_pn *dst, const void *__restrict buf, size_t len) { const struct iovec iov = { .iov_base = (void *)buf, .iov_len = len, }; return g_isi_modem_vsendto(modem, dst, &iov, 1); } int g_isi_modem_vsendto(GIsiModem *modem, struct sockaddr_pn *dst, const struct iovec *__restrict iov, size_t iovlen) { struct msghdr msg = { .msg_name = (void *)dst, .msg_namelen = sizeof(struct sockaddr_pn), .msg_iov = (struct iovec *)iov, .msg_iovlen = iovlen, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0, }; ssize_t ret; size_t i, len; GIsiServiceMux *mux; if (modem == NULL) return -EINVAL; mux = service_get(modem, dst->spn_resource); if (mux == NULL) return -ENOMEM; for (i = 0, len = 0; i < iovlen; i++) len += iov[i].iov_len; if (modem->trace != NULL) vtrace(dst, iov, iovlen, len, modem->trace); ret = sendmsg(modem->req_fd, &msg, MSG_NOSIGNAL); if (ret == -1) return -errno; if (ret != (ssize_t)len) return -EMSGSIZE; return 0; } void g_isi_modem_set_trace(GIsiModem *modem, GIsiNotifyFunc trace) { if (modem == NULL) return; modem->trace = trace; } void g_isi_modem_set_debug(GIsiModem *modem, GIsiDebugFunc debug) { if (modem == NULL) return; modem->debug = debug; } static int version_get_send(GIsiModem *modem, GIsiPending *ping) { GIsiServiceMux *mux = ping->service; struct sockaddr_pn dst = { .spn_family = AF_PHONET, .spn_resource = mux->resource, .spn_dev = modem->device, }; uint8_t msg[] = { ping->utid, /* UTID */ COMMON_MESSAGE, COMM_ISI_VERSION_GET_REQ, 0, /* Filler */ }; ssize_t ret; if (g_slist_find_custom(mux->pending, ping, utid_equal)) return -EBUSY; ret = sendto(modem->req_fd, msg, sizeof(msg), MSG_NOSIGNAL, (void *)&dst, sizeof(dst)); if (ret == -1) return -errno; if (ret != (ssize_t)sizeof(msg)) return -EMSGSIZE; mux->last_utid = ping->utid; mux->version_pending = TRUE; return 0; } static gboolean reachable_notify(gpointer data) { GIsiPending *pong = data; GIsiServiceMux *mux = pong->service; struct sockaddr_pn addr = { .spn_resource = mux->resource, .spn_dev = mux->object >> 8, .spn_obj = mux->object & 0xff, }; GIsiMessage msg = { .version = &mux->version, .addr = &addr, }; pending_remove_and_dispatch(pong, &msg); return FALSE; } GIsiPending *g_isi_resource_ping(GIsiModem *modem, uint8_t resource, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { GIsiServiceMux *mux; GIsiPending *ping; int ret; mux = service_get(modem, resource); if (mux == NULL) { errno = ENOMEM; return NULL; } ping = g_try_new0(GIsiPending, 1); if (ping == NULL) { errno = ENOMEM; return NULL; } ping->type = GISI_MESSAGE_TYPE_COMMON; ping->utid = service_next_utid(mux); ping->service = mux; ping->notify = notify; ping->data = data; ping->destroy = destroy; ping->msgid = COMM_ISI_VERSION_GET_REQ; if (mux->reachable) { g_idle_add(reachable_notify, ping); return ping; } if (!mux->version_pending) { ret = version_get_send(modem, ping); if (ret < 0) { g_free(ping); errno = ret; return NULL; } mux->last_utid = ping->utid; } ping->timeout = g_timeout_add_seconds(COMMON_TIMEOUT, resp_timeout, ping); mux->pending = g_slist_prepend(mux->pending, ping); mux->version_pending = TRUE; ISIDBG(modem, "Ping sent %s (%p) [res=0x%02X]", pend_type_to_str(ping->type), ping, resource); return ping; } ofono-1.17.bzr6912+16.04.20160314.3/gisi/netlink.h0000644000015600001650000000310012671500024021141 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #include #include #ifndef __GISI_PN_NETLINK_H #define __GISI_PN_NETLINK_H #ifdef __cplusplus extern "C" { #endif struct _GIsiPhonetNetlink; typedef struct _GIsiPhonetNetlink GIsiPhonetNetlink; enum GIsiPhonetLinkState { PN_LINK_REMOVED, PN_LINK_DOWN, PN_LINK_UP, }; typedef void (*GIsiPhonetNetlinkFunc)(GIsiModem *modem, enum GIsiPhonetLinkState st, const char *iface, void *data); GIsiPhonetNetlink *g_isi_pn_netlink_by_modem(GIsiModem *modem); GIsiPhonetNetlink *g_isi_pn_netlink_start(GIsiModem *idx, GIsiPhonetNetlinkFunc cb, void *data); void g_isi_pn_netlink_stop(GIsiPhonetNetlink *self); int g_isi_pn_netlink_set_address(GIsiModem *modem, uint8_t local); #ifdef __cplusplus } #endif #endif /* __GISI_PN_NETLINK_H */ ofono-1.17.bzr6912+16.04.20160314.3/gisi/common.h0000644000015600001650000000333312671500024020775 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __GISI_COMMON_H #define __GISI_COMMON_H #ifdef __cplusplus extern "C" { #endif #define PN_COMMGR 0x10 #define PN_NAMESERVICE 0xDB #define PN_FIREWALL 0x43 #define COMMON_TIMEOUT 5 enum message_id { PNS_NAME_ADD_REQ = 0x05, PNS_NAME_REMOVE_REQ = 0x07, PNS_SUBSCRIBED_RESOURCES_IND = 0x10, PNS_SUBSCRIBED_RESOURCES_EXTEND_IND = 0x12, COMM_ISI_VERSION_GET_REQ = 0x12, COMM_ISI_VERSION_GET_RESP = 0x13, COMM_ISA_ENTITY_NOT_REACHABLE_RESP = 0x14, COMM_SERVICE_NOT_AUTHENTICATED_RESP = 0x17, COMMON_MESSAGE = 0xF0, }; enum GIsiPhonetDevice { PN_DEV_PC = 0x10, /* PC Suite */ PN_DEV_HOST = 0x00, /* Host modem */ PN_DEV_MODEM = 0x60, /* Modem */ PN_DEV_SOS = 0x6C, /* Symbian or Linux */ }; enum GIsiMessageType { GISI_MESSAGE_TYPE_REQ, GISI_MESSAGE_TYPE_IND, GISI_MESSAGE_TYPE_NTF, GISI_MESSAGE_TYPE_RESP, GISI_MESSAGE_TYPE_COMMON, /* ISI version, namely */ }; #ifdef __cplusplus } #endif #endif /* __GISI_COMMON_H */ ofono-1.17.bzr6912+16.04.20160314.3/gisi/pipe.c0000644000015600001650000002061712671500024020441 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include "client.h" #include "pipe.h" #define PN_PIPE 0xD9 #define PN_PIPE_INVALID_HANDLE 0xFF struct isi_pipe_create_req { uint8_t cmd; uint8_t state_after; uint8_t priority; uint8_t device1; uint8_t object1; uint8_t type1; uint8_t pad; uint8_t device2; uint8_t object2; uint8_t type2; uint8_t n_sb; }; struct isi_pipe_enable_req { uint8_t cmd; uint8_t pipe_handle; uint8_t pad; }; struct isi_pipe_reset_req { uint8_t cmd; uint8_t pipe_handle; uint8_t state_after; }; struct isi_pipe_remove_req { uint8_t cmd; uint8_t pipe_handle; }; struct isi_pipe_resp { uint8_t pipe_handle; uint8_t error_code; uint8_t error1; uint8_t error2; }; enum isi_pipe_message_id { PNS_PIPE_CREATE_REQ, PNS_PIPE_CREATE_RESP, PNS_PIPE_REMOVE_REQ, PNS_PIPE_REMOVE_RESP, PNS_PIPE_RESET_REQ, PNS_PIPE_RESET_RESP, PNS_PIPE_ENABLE_REQ, PNS_PIPE_ENABLE_RESP, PNS_PIPE_REDIRECT_REQ, PNS_PIPE_REDIRECT_RESP, PNS_PIPE_DISABLE_REQ, PNS_PIPE_DISABLE_RESP, }; enum pn_pipe_error { /* error codes */ PN_PIPE_ERR_NO_ERROR, PN_PIPE_ERR_INVALID_PARAM, PN_PIPE_ERR_INVALID_HANDLE, PN_PIPE_ERR_INVALID_CTRL_ID, PN_PIPE_ERR_NOT_ALLOWED, PN_PIPE_ERR_PEP_IN_USE, PN_PIPE_ERR_OVERLOAD, PN_PIPE_ERR_DEV_DISCONNECTED, PN_PIPE_ERR_TIMEOUT, PN_PIPE_ERR_ALL_PIPES_IN_USE, PN_PIPE_ERR_GENERAL, PN_PIPE_ERR_NOT_SUPPORTED, }; enum pn_pipe_state { /* initial pipe state */ PN_PIPE_DISABLE, PN_PIPE_ENABLE, }; enum pn_msg_priority { PN_MSG_PRIORITY_LOW = 1, PN_MSG_PRIORITY_HIGH, }; struct _GIsiPipe { GIsiClient *client; GIsiPipeHandler handler; GIsiPipeErrorHandler error_handler; void *opaque; int error; uint8_t handle; gboolean enabled; gboolean enabling; }; static int g_isi_pipe_error(enum pn_pipe_error code) { switch (code) { case PN_PIPE_ERR_NO_ERROR: return 0; case PN_PIPE_ERR_INVALID_PARAM: return -EINVAL; case PN_PIPE_ERR_INVALID_HANDLE: return -EBADF; case PN_PIPE_ERR_INVALID_CTRL_ID: return -ENOTSUP; case PN_PIPE_ERR_NOT_ALLOWED: return -EPERM; case PN_PIPE_ERR_PEP_IN_USE: return -EBUSY; case PN_PIPE_ERR_OVERLOAD: return -ENOBUFS; case PN_PIPE_ERR_DEV_DISCONNECTED: return -ENETDOWN; case PN_PIPE_ERR_TIMEOUT: return -ETIMEDOUT; case PN_PIPE_ERR_ALL_PIPES_IN_USE: return -ENFILE; case PN_PIPE_ERR_GENERAL: return -EAGAIN; case PN_PIPE_ERR_NOT_SUPPORTED: return -ENOSYS; } return -EBADMSG; } static void g_isi_pipe_handle_error(GIsiPipe *pipe, uint8_t code) { int err = g_isi_pipe_error(code); if (err == 0) return; pipe->error = err; if (pipe->error_handler) pipe->error_handler(pipe); } static void g_isi_pipe_created(const GIsiMessage *msg, void *data) { struct isi_pipe_resp *resp; size_t len = sizeof(struct isi_pipe_resp); GIsiPipe *pipe = data; if (g_isi_msg_error(msg) < 0) { g_isi_pipe_handle_error(pipe, PN_PIPE_ERR_TIMEOUT); return; } if (g_isi_msg_id(msg) != PNS_PIPE_CREATE_RESP) return; if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &resp, len)) return; if (resp->pipe_handle == PN_PIPE_INVALID_HANDLE) { g_isi_pipe_handle_error(pipe, resp->error_code); return; } pipe->handle = resp->pipe_handle; if (pipe->enabling) g_isi_pipe_start(pipe); if (pipe->handler) pipe->handler(pipe); } /** * Create a Phonet pipe in disabled state and with low priority. * @param modem ISI modem to create a pipe with * @param created optional callback for created event * @param obj1 Object handle of the first end point * @param obj2 Object handle of the second end point * @param type1 Type of the first end point * @param type2 Type of the second end point * @return a pipe object on success, NULL on error. */ GIsiPipe *g_isi_pipe_create(GIsiModem *modem, GIsiPipeHandler cb, uint16_t obj1, uint16_t obj2, uint8_t type1, uint8_t type2) { struct isi_pipe_create_req msg = { .cmd = PNS_PIPE_CREATE_REQ, .state_after = PN_PIPE_DISABLE, .priority = PN_MSG_PRIORITY_LOW, .device1 = obj1 >> 8, .object1 = obj1 & 0xff, .type1 = type1, .device2 = obj2 >> 8, .object2 = obj2 & 0xff, .type2 = type2, .n_sb = 0, }; size_t len = sizeof(msg); GIsiPipe *pipe; pipe = g_try_new0(GIsiPipe, 1); if (pipe == NULL) { errno = ENOMEM; return NULL; } pipe->client = g_isi_client_create(modem, PN_PIPE); if (pipe->client == NULL) { errno = ENOMEM; g_free(pipe); return NULL; } pipe->handler = cb; pipe->error_handler = NULL; pipe->error = 0; pipe->enabling = FALSE; pipe->enabled = FALSE; pipe->handle = PN_PIPE_INVALID_HANDLE; if (g_isi_client_send(pipe->client, &msg, len, g_isi_pipe_created, pipe, NULL)) return pipe; g_isi_client_destroy(pipe->client); g_free(pipe); return NULL; } static void g_isi_pipe_enabled(const GIsiMessage *msg, void *data) { GIsiPipe *pipe = data; const struct isi_pipe_resp *resp; size_t len = sizeof(struct isi_pipe_resp); if (g_isi_msg_error(msg) < 0) { g_isi_pipe_handle_error(pipe, PN_PIPE_ERR_TIMEOUT); return; } if (g_isi_msg_id(msg) != PNS_PIPE_ENABLE_RESP) return; if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &resp, len)) return; if (pipe->handle != resp->pipe_handle) return; g_isi_pipe_handle_error(pipe, resp->error_code); pipe->enabling = FALSE; if (!pipe->error) pipe->enabled = TRUE; } static void g_isi_pipe_enable(GIsiPipe *pipe) { struct isi_pipe_enable_req msg = { .cmd = PNS_PIPE_ENABLE_REQ, .pipe_handle = pipe->handle, }; size_t len = sizeof(msg); g_isi_client_send(pipe->client, &msg, len, g_isi_pipe_enabled, pipe, NULL); } /** * Enable a pipe, i.e. turn on data transfer between the two end points. * @param pipe pipe as returned from g_isi_pipe_create() * @return 0 on success or an error code */ int g_isi_pipe_start(GIsiPipe *pipe) { if (pipe->error) return pipe->error; if (pipe->enabling || pipe->enabled) return 0; if (pipe->handle != PN_PIPE_INVALID_HANDLE) g_isi_pipe_enable(pipe); else pipe->enabling = TRUE; return 0; } /* Not very useful, it will never have time to trigger */ static void g_isi_pipe_removed(const GIsiMessage *msg, void *data) { GIsiPipe *pipe = data; struct isi_pipe_resp *resp; size_t len = sizeof(struct isi_pipe_resp); if (g_isi_msg_error(msg) < 0) { g_isi_pipe_handle_error(pipe, PN_PIPE_ERR_TIMEOUT); return; } if (g_isi_msg_id(msg) != PNS_PIPE_REMOVE_RESP) return; if (!g_isi_msg_data_get_struct(msg, 0, (const void **) &resp, len)) return; if (pipe->handle != resp->pipe_handle) return; pipe->handle = PN_PIPE_INVALID_HANDLE; pipe->error = -EPIPE; } static void g_isi_pipe_remove(GIsiPipe *pipe) { struct isi_pipe_remove_req msg = { .cmd = PNS_PIPE_REMOVE_REQ, .pipe_handle = pipe->handle, }; size_t len = sizeof(msg); g_isi_client_send(pipe->client, &msg, len, g_isi_pipe_removed, pipe, NULL); } /** * Destroy a pipe. If it was connected, it is removed. * @param pipe pipe as returned from g_isi_pipe_create() */ void g_isi_pipe_destroy(GIsiPipe *pipe) { if (!pipe->error) g_isi_pipe_remove(pipe); g_isi_client_destroy(pipe->client); g_free(pipe); } void g_isi_pipe_set_error_handler(GIsiPipe *pipe, GIsiPipeErrorHandler cb) { pipe->error_handler = cb; } int g_isi_pipe_get_error(const GIsiPipe *pipe) { return pipe->error; } void *g_isi_pipe_set_userdata(GIsiPipe *pipe, void *opaque) { void *old = pipe->opaque; pipe->opaque = opaque; return old; } void *g_isi_pipe_get_userdata(GIsiPipe *pipe) { return pipe->opaque; } /** * Return a pipe handle. * @param pipe a ready-made pipe with handler data present. Available * after the pipe creation callback is called. * @return uint8_t handle. */ uint8_t g_isi_pipe_get_handle(GIsiPipe *pipe) { return pipe->handle; } ofono-1.17.bzr6912+16.04.20160314.3/gisi/phonet.h0000644000015600001650000000315012671500024020777 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef NETPHONET_PHONET_H #define NETPHONET_PHONET_H #include #include #ifndef AF_PHONET #define AF_PHONET 35 #define PF_PHONET AF_PHONET #endif #define PN_PROTO_TRANSPORT 0 #define PN_PROTO_PHONET 1 #define PN_PROTO_PIPE 2 #define SOL_PNPIPE 275 #define PNPIPE_ENCAP 1 #define PNPIPE_IFINDEX 2 #define PNPIPE_ENCAP_NONE 0 #define PNPIPE_ENCAP_IP 1 #define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0) #define SIOCPNADDRESOURCE (SIOCPROTOPRIVATE + 14) #define SIOCPNDELRESOURCE (SIOCPROTOPRIVATE + 15) struct sockaddr_pn { sa_family_t spn_family; uint8_t spn_obj; uint8_t spn_dev; uint8_t spn_resource; uint8_t __pad[sizeof(struct sockaddr) - (sizeof(sa_family_t) + 3)]; } __attribute__ ((packed)); #include #ifndef RTNLGRP_PHONET_IFADDR #define RTNLGRP_PHONET_IFADDR 21 #endif #endif ofono-1.17.bzr6912+16.04.20160314.3/gisi/pep.h0000644000015600001650000000245312671500024020273 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __GISI_PEP_H #define __GISI_PEP_H #include "modem.h" #ifdef __cplusplus extern "C" { #endif typedef struct _GIsiPEP GIsiPEP; typedef void (*GIsiPEPCallback)(GIsiPEP *pep, void *opaque); GIsiPEP *g_isi_pep_create(GIsiModem *modem, GIsiPEPCallback cb, void *data); void g_isi_pep_destroy(GIsiPEP *pep); uint16_t g_isi_pep_get_object(const GIsiPEP *pep); unsigned g_isi_pep_get_ifindex(const GIsiPEP *pep); char *g_isi_pep_get_ifname(const GIsiPEP *pep, char *ifname); #ifdef __cplusplus } #endif #endif /* __GISI_PEP_H */ ofono-1.17.bzr6912+16.04.20160314.3/gisi/iter.h0000644000015600001650000000634712671500024020460 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __GISI_ITER_H #define __GISI_ITER_H #ifdef __cplusplus extern "C" { #endif #include #include "message.h" struct _GIsiSubBlockIter { uint8_t *start; uint8_t *end; gboolean longhdr; uint16_t cursor; uint16_t sub_blocks; }; typedef struct _GIsiSubBlockIter GIsiSubBlockIter; void g_isi_sb_iter_init(GIsiSubBlockIter *iter, const GIsiMessage *msg, size_t used); void g_isi_sb_iter_init_full(GIsiSubBlockIter *iter, const GIsiMessage *msg, size_t used, gboolean longhdr, uint16_t sub_blocks); void g_isi_sb_subiter_init(GIsiSubBlockIter *outer, GIsiSubBlockIter *inner, size_t used); void g_isi_sb_subiter_init_full(GIsiSubBlockIter *out, GIsiSubBlockIter *in, size_t used, gboolean longhdr, uint16_t sub_blocks); gboolean g_isi_sb_iter_is_valid(const GIsiSubBlockIter *iter); gboolean g_isi_sb_iter_next(GIsiSubBlockIter *iter); int g_isi_sb_iter_get_id(const GIsiSubBlockIter *iter); size_t g_isi_sb_iter_get_len(const GIsiSubBlockIter *iter); gboolean g_isi_sb_iter_get_data(const GIsiSubBlockIter *restrict iter, void **data, unsigned pos); gboolean g_isi_sb_iter_get_byte(const GIsiSubBlockIter *restrict iter, uint8_t *byte, unsigned pos); gboolean g_isi_sb_iter_get_word(const GIsiSubBlockIter *restrict iter, uint16_t *word, unsigned pos); gboolean g_isi_sb_iter_get_dword(const GIsiSubBlockIter *restrict iter, uint32_t *dword, unsigned pos); gboolean g_isi_sb_iter_eat_byte(GIsiSubBlockIter *restrict iter, uint8_t *byte); gboolean g_isi_sb_iter_eat_word(GIsiSubBlockIter *restrict iter, uint16_t *word); gboolean g_isi_sb_iter_eat_dword(GIsiSubBlockIter *restrict iter, uint32_t *dword); gboolean g_isi_sb_iter_get_oper_code(const GIsiSubBlockIter *restrict iter, char *mcc, char *mnc, unsigned pos); gboolean g_isi_sb_iter_eat_oper_code(GIsiSubBlockIter *restrict iter, char *mcc, char *mnc); gboolean g_isi_sb_iter_get_alpha_tag(const GIsiSubBlockIter *restrict iter, char **utf8, size_t len, unsigned pos); gboolean g_isi_sb_iter_eat_alpha_tag(GIsiSubBlockIter *restrict iter, char **utf8, size_t len); gboolean g_isi_sb_iter_get_latin_tag(const GIsiSubBlockIter *restrict iter, char **ascii, size_t len, unsigned pos); gboolean g_isi_sb_iter_eat_latin_tag(GIsiSubBlockIter *restrict iter, char **ascii, size_t len); gboolean g_isi_sb_iter_get_struct(const GIsiSubBlockIter *restrict iter, void **ptr, size_t len, unsigned pos); #ifdef __cplusplus } #endif #endif /* __GISI_ITER_H */ ofono-1.17.bzr6912+16.04.20160314.3/gisi/modem.h0000644000015600001650000001044312671500024020606 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __GISI_MODEM_H #define __GISI_MODEM_H #include #include "phonet.h" #include "message.h" #ifdef __cplusplus extern "C" { #endif enum GIsiModemFlags { GISI_MODEM_FLAG_USE_LEGACY_SUBSCRIBE = 1, }; struct _GIsiModem; typedef struct _GIsiModem GIsiModem; struct _GIsiPending; typedef struct _GIsiPending GIsiPending; typedef void (*GIsiNotifyFunc)(const GIsiMessage *msg, void *opaque); typedef void (*GIsiDebugFunc)(const char *fmt, ...); GIsiModem *g_isi_modem_create(unsigned index); GIsiModem *g_isi_modem_create_by_name(const char *name); void g_isi_modem_destroy(GIsiModem *modem); unsigned g_isi_modem_index(GIsiModem *modem); uint8_t g_isi_modem_device(GIsiModem *modem); int g_isi_modem_set_device(GIsiModem *modem, uint8_t dev); void g_isi_modem_set_trace(GIsiModem *modem, GIsiNotifyFunc notify); void g_isi_modem_set_debug(GIsiModem *modem, GIsiDebugFunc debug); void *g_isi_modem_set_userdata(GIsiModem *modem, void *data); void *g_isi_modem_get_userdata(GIsiModem *modem); unsigned long g_isi_modem_flags(GIsiModem *modem); void g_isi_modem_set_flags(GIsiModem *modem, unsigned long flags); GIsiPending *g_isi_request_send(GIsiModem *modem, uint8_t resource, const void *__restrict buf, size_t len, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); GIsiPending *g_isi_request_vsend(GIsiModem *modem, uint8_t resource, const struct iovec *__restrict iov, size_t iovlen, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); GIsiPending *g_isi_request_sendto(GIsiModem *modem, struct sockaddr_pn *dst, const void *__restrict buf, size_t len, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); GIsiPending *g_isi_request_vsendto(GIsiModem *modem, struct sockaddr_pn *dst, const struct iovec *__restrict iov, size_t iovlen, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); int g_isi_modem_send(GIsiModem *modem, uint8_t resource, const void *__restrict buf, size_t len); int g_isi_modem_vsend(GIsiModem *modem, uint8_t resource, const struct iovec *__restrict iov, size_t iovlen); int g_isi_modem_sendto(GIsiModem *modem, struct sockaddr_pn *dst, const void *__restrict buf, size_t len); int g_isi_modem_vsendto(GIsiModem *modem, struct sockaddr_pn *dst, const struct iovec *__restrict iov, size_t iovlen); uint8_t g_isi_request_utid(GIsiPending *resp); GIsiPending *g_isi_ind_subscribe(GIsiModem *modem, uint8_t resource, uint8_t type, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); GIsiPending *g_isi_ntf_subscribe(GIsiModem *modem, uint8_t resource, uint8_t type, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); GIsiPending *g_isi_service_bind(GIsiModem *modem, uint8_t resource, uint8_t type, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); int g_isi_response_send(GIsiModem *modem, const GIsiMessage *req, const void *__restrict buf, size_t len); int g_isi_response_vsend(GIsiModem *modem, const GIsiMessage *req, const struct iovec *__restrict iov, size_t iovlen); void g_isi_pending_remove(GIsiPending *op); void g_isi_pending_set_owner(GIsiPending *op, gpointer owner); void g_isi_remove_pending_by_owner(GIsiModem *modem, uint8_t resource, gpointer owner); GIsiPending *g_isi_resource_ping(GIsiModem *modem, uint8_t resource, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy); #ifdef __cplusplus } #endif #endif /* __GISI_MODEM_H */ ofono-1.17.bzr6912+16.04.20160314.3/gisi/message.c0000644000015600001650000000623412671500024021127 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include "message.h" int g_isi_msg_version_major(const GIsiMessage *msg) { if (msg == NULL || msg->version == NULL) return -1; return msg->version->major; } int g_isi_msg_version_minor(const GIsiMessage *msg) { if (msg == NULL || msg->version == NULL) return -1; return msg->version->minor; } int g_isi_msg_error(const GIsiMessage *msg) { return msg != NULL ? -msg->error : -EINVAL; } const char *g_isi_msg_strerror(const GIsiMessage *msg) { return strerror(-g_isi_msg_error(msg)); } uint8_t g_isi_msg_resource(const GIsiMessage *msg) { if (msg == NULL || msg->addr == NULL) return 0; return msg->addr->spn_resource; } uint16_t g_isi_msg_object(const GIsiMessage *msg) { if (msg == NULL || msg->addr == NULL) return 0; return (msg->addr->spn_dev << 8) | msg->addr->spn_obj; } uint8_t g_isi_msg_id(const GIsiMessage *msg) { const uint8_t *buf; if (msg == NULL || msg->data == NULL || msg->len < 2) return 0; buf = msg->data; return buf[1]; } uint8_t g_isi_msg_utid(const GIsiMessage *msg) { const uint8_t *buf; if (msg == NULL || msg->data == NULL || msg->len < 2) return 0; buf = msg->data; return buf[0]; } size_t g_isi_msg_data_len(const GIsiMessage *msg) { if (msg == NULL || msg->data == NULL) return 0; return msg->len - 2; } const void *g_isi_msg_data(const GIsiMessage *msg) { if (msg == NULL || msg->data == NULL) return NULL; return (void *)msg->data + 2; } gboolean g_isi_msg_data_get_byte(const GIsiMessage *msg, unsigned offset, uint8_t *byte) { const uint8_t *buf = g_isi_msg_data(msg); if (buf == NULL || g_isi_msg_data_len(msg) < offset) return FALSE; if (byte != NULL) *byte = buf[offset]; return TRUE; } gboolean g_isi_msg_data_get_word(const GIsiMessage *msg, unsigned offset, uint16_t *word) { const uint8_t *buf = g_isi_msg_data(msg); uint16_t val; if (buf == NULL || g_isi_msg_data_len(msg) < offset + 1) return FALSE; memcpy(&val, buf + offset, sizeof(uint16_t)); if (word != NULL) *word = ntohs(val); return TRUE; } gboolean g_isi_msg_data_get_struct(const GIsiMessage *msg, unsigned offset, const void **type, size_t len) { if (g_isi_msg_data_len(msg) < offset + len) return FALSE; if (type != NULL) *type = g_isi_msg_data(msg) + offset; return TRUE; } ofono-1.17.bzr6912+16.04.20160314.3/gisi/server.c0000644000015600001650000000501112671500024021001 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include "phonet.h" #include "server.h" struct _GIsiServer { GIsiModem *modem; GIsiVersion version; uint8_t resource; }; uint8_t g_isi_server_resource(GIsiServer *server) { return server != NULL ? server->resource : 0; } GIsiModem *g_isi_server_modem(GIsiServer *server) { return server != NULL ? server->modem : 0; } GIsiServer *g_isi_server_create(GIsiModem *modem, uint8_t resource, GIsiVersion *version) { GIsiServer *server; if (modem == NULL) { errno = EINVAL; return NULL; } server = g_try_new0(GIsiServer, 1); if (server == NULL) { errno = ENOMEM; return NULL; } if (version != NULL) memcpy(&server->version, version, sizeof(GIsiVersion)); server->resource = resource; server->modem = modem; return server; } void g_isi_server_destroy(GIsiServer *server) { if (server == NULL) return; g_isi_remove_pending_by_owner(server->modem, server->resource, server); g_free(server); } int g_isi_server_send(GIsiServer *server, const GIsiMessage *req, const void *__restrict buf, size_t len) { if (server == NULL) return -EINVAL; return g_isi_response_send(server->modem, req, buf, len); } int g_isi_server_vsend(GIsiServer *server, const GIsiMessage *req, const struct iovec *iov, size_t iovlen) { if (server == NULL) return -EINVAL; return g_isi_response_vsend(server->modem, req, iov, iovlen); } GIsiPending *g_isi_server_handle(GIsiServer *server, uint8_t type, GIsiNotifyFunc notify, void *data) { GIsiPending *op; op = g_isi_service_bind(server->modem, server->resource, type, notify, data, NULL); g_isi_pending_set_owner(op, server); return op; } ofono-1.17.bzr6912+16.04.20160314.3/gisi/pep.c0000644000015600001650000000647112671500024020272 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include "phonet.h" #include "socket.h" #include "pep.h" struct _GIsiPEP { GIsiPEPCallback ready; void *opaque; int gprs_fd; guint source; uint16_t handle; }; static gboolean g_isi_pep_callback(GIOChannel *channel, GIOCondition cond, gpointer data) { GIsiPEP *pep = data; int fd = g_io_channel_unix_get_fd(channel); int encap = PNPIPE_ENCAP_IP; if (cond & (G_IO_HUP|G_IO_NVAL)) return FALSE; fd = accept(fd, NULL, NULL); if (fd == -1) return TRUE; fcntl(fd, F_SETFD, FD_CLOEXEC); if (setsockopt(fd, SOL_PNPIPE, PNPIPE_ENCAP, &encap, sizeof(encap))) { close(fd); return TRUE; } pep->gprs_fd = fd; if (pep->ready != NULL) pep->ready(pep, pep->opaque); return FALSE; } GIsiPEP *g_isi_pep_create(GIsiModem *modem, GIsiPEPCallback cb, void *opaque) { unsigned ifi = g_isi_modem_index(modem); GIsiPEP *pep = NULL; GIOChannel *channel; int fd; char buf[IF_NAMESIZE]; fd = socket(PF_PHONET, SOCK_SEQPACKET, 0); if (fd == -1) return NULL; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK|fcntl(fd, F_GETFL)); if (if_indextoname(ifi, buf) == NULL) goto error; if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, buf, IF_NAMESIZE) != 0) goto error; pep = g_try_malloc(sizeof(GIsiPEP)); if (pep == NULL) goto error; pep->ready = cb; pep->opaque = opaque; pep->gprs_fd = -1; pep->handle = 0; if (listen(fd, 1) || ioctl(fd, SIOCPNGETOBJECT, &pep->handle)) goto error; channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); pep->source = g_io_add_watch(channel, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, g_isi_pep_callback, pep); g_io_channel_unref(channel); return pep; error: close(fd); g_free(pep); return NULL; } uint16_t g_isi_pep_get_object(const GIsiPEP *pep) { return pep->handle; } void g_isi_pep_destroy(GIsiPEP *pep) { if (pep->gprs_fd != -1) close(pep->gprs_fd); else g_source_remove(pep->source); g_free(pep); } unsigned g_isi_pep_get_ifindex(const GIsiPEP *pep) { unsigned ifi; socklen_t len = sizeof(ifi); g_assert(pep->gprs_fd != -1); getsockopt(pep->gprs_fd, SOL_PNPIPE, PNPIPE_IFINDEX, &ifi, &len); return ifi; } char *g_isi_pep_get_ifname(const GIsiPEP *pep, char *ifname) { if (pep->gprs_fd == -1) return NULL; return if_indextoname(g_isi_pep_get_ifindex(pep), ifname); } ofono-1.17.bzr6912+16.04.20160314.3/gisi/socket.c0000644000015600001650000000442312671500024020771 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include #include "phonet.h" #include "socket.h" GIOChannel *g_isi_phonet_new(unsigned ifindex) { GIOChannel *channel; struct sockaddr_pn addr = { .spn_family = AF_PHONET, }; char buf[IF_NAMESIZE]; int fd = socket(PF_PHONET, SOCK_DGRAM, 0); if (fd == -1) return NULL; fcntl(fd, F_SETFD, FD_CLOEXEC); /* Use blocking mode on purpose. */ if (ifindex == 0) g_warning("Unspecified modem interface index"); else if (if_indextoname(ifindex, buf) == NULL || setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, buf, IF_NAMESIZE)) goto error; if (bind(fd, (void *)&addr, sizeof(addr))) goto error; channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); return channel; error: close(fd); return NULL; } size_t g_isi_phonet_peek_length(GIOChannel *channel) { int len; int fd = g_io_channel_unix_get_fd(channel); return ioctl(fd, FIONREAD, &len) ? 0 : len; } ssize_t g_isi_phonet_read(GIOChannel *channel, void *restrict buf, size_t len, struct sockaddr_pn *addr) { socklen_t addrlen = sizeof(struct sockaddr_pn); ssize_t ret; ret = recvfrom(g_io_channel_unix_get_fd(channel), buf, len, MSG_DONTWAIT, (void *)addr, &addrlen); if (ret == -1) return -1; return ret; } ofono-1.17.bzr6912+16.04.20160314.3/gisi/iter.c0000644000015600001650000001706512671500024020452 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include "iter.h" static inline void bcd_to_mccmnc(const uint8_t *restrict bcd, char *mcc, char *mnc) { mcc[0] = '0' + (bcd[0] & 0x0F); mcc[1] = '0' + ((bcd[0] & 0xF0) >> 4); mcc[2] = '0' + (bcd[1] & 0x0F); mcc[3] = '\0'; mnc[0] = '0' + (bcd[2] & 0x0F); mnc[1] = '0' + ((bcd[2] & 0xF0) >> 4); mnc[2] = (bcd[1] & 0xF0) == 0xF0 ? '\0' : '0' + (bcd[1] & 0xF0); mnc[3] = '\0'; } void g_isi_sb_iter_init_full(GIsiSubBlockIter *iter, const GIsiMessage *msg, size_t used, gboolean longhdr, uint16_t sub_blocks) { const uint8_t *data = g_isi_msg_data(msg); size_t len = g_isi_msg_data_len(msg); if (data == NULL) len = used = 0; iter->cursor = longhdr ? 4 : 2; iter->start = (uint8_t *) data + used; iter->end = iter->start + len; iter->longhdr = longhdr; iter->sub_blocks = len > used ? sub_blocks : 0; } void g_isi_sb_iter_init(GIsiSubBlockIter *iter, const GIsiMessage *msg, size_t used) { const uint8_t *data = g_isi_msg_data(msg); size_t len = g_isi_msg_data_len(msg); if (data == NULL) len = used = 0; iter->cursor = 2; iter->start = (uint8_t *) data + used; iter->end = iter->start + len; iter->longhdr = FALSE; iter->sub_blocks = len > used ? iter->start[-1] : 0; } void g_isi_sb_subiter_init(GIsiSubBlockIter *outer, GIsiSubBlockIter *inner, size_t used) { size_t len = g_isi_sb_iter_get_len(outer); if (outer->start + len > outer->end || outer->start + used > outer->end) len = used = 0; inner->cursor = 2; inner->start = outer->start + used; inner->end = inner->start + len; inner->longhdr = FALSE; inner->sub_blocks = len > used ? inner->start[-1] : 0; } void g_isi_sb_subiter_init_full(GIsiSubBlockIter *outer, GIsiSubBlockIter *inner, size_t used, gboolean longhdr, uint16_t sub_blocks) { size_t len = g_isi_sb_iter_get_len(outer); if (outer->start + len > outer->end || outer->start + used > outer->end) len = used = 0; inner->cursor = longhdr ? 4 : 2; inner->start = outer->start + used; inner->end = inner->start + len; inner->longhdr = longhdr; inner->sub_blocks = len > used ? sub_blocks : 0; } gboolean g_isi_sb_iter_is_valid(const GIsiSubBlockIter *iter) { if (iter == NULL) return FALSE; if (iter->sub_blocks == 0) return FALSE; if (iter->start + (iter->longhdr ? 4 : 2) > iter->end) return FALSE; if (iter->start + g_isi_sb_iter_get_len(iter) > iter->end) return FALSE; return TRUE; } int g_isi_sb_iter_get_id(const GIsiSubBlockIter *iter) { if (iter->longhdr) return (iter->start[0] << 8) | iter->start[1]; return iter->start[0]; } size_t g_isi_sb_iter_get_len(const GIsiSubBlockIter *iter) { if (iter->longhdr) return (iter->start[2] << 8) | iter->start[3]; return iter->start[1]; } gboolean g_isi_sb_iter_get_data(const GIsiSubBlockIter *restrict iter, void **data, unsigned pos) { if ((size_t) pos > g_isi_sb_iter_get_len(iter) || iter->start + pos > iter->end) return FALSE; *data = (void *) iter->start + pos; return TRUE; } gboolean g_isi_sb_iter_get_byte(const GIsiSubBlockIter *restrict iter, uint8_t *byte, unsigned pos) { if ((size_t) pos > g_isi_sb_iter_get_len(iter) || iter->start + pos > iter->end) return FALSE; *byte = iter->start[pos]; return TRUE; } gboolean g_isi_sb_iter_get_word(const GIsiSubBlockIter *restrict iter, uint16_t *word, unsigned pos) { uint16_t val; if (pos + 1 > g_isi_sb_iter_get_len(iter)) return FALSE; memcpy(&val, iter->start + pos, sizeof(uint16_t)); *word = ntohs(val); return TRUE; } gboolean g_isi_sb_iter_get_dword(const GIsiSubBlockIter *restrict iter, uint32_t *dword, unsigned pos) { uint32_t val; if (pos + 3 > g_isi_sb_iter_get_len(iter)) return FALSE; memcpy(&val, iter->start + pos, sizeof(uint32_t)); *dword = ntohl(val); return TRUE; } gboolean g_isi_sb_iter_eat_byte(GIsiSubBlockIter *restrict iter, uint8_t *byte) { if (!g_isi_sb_iter_get_byte(iter, byte, iter->cursor)) return FALSE; iter->cursor += 1; return TRUE; } gboolean g_isi_sb_iter_eat_word(GIsiSubBlockIter *restrict iter, uint16_t *word) { if (!g_isi_sb_iter_get_word(iter, word, iter->cursor)) return FALSE; iter->cursor += 2; return TRUE; } gboolean g_isi_sb_iter_eat_dword(GIsiSubBlockIter *restrict iter, uint32_t *dword) { if (!g_isi_sb_iter_get_dword(iter, dword, iter->cursor)) return FALSE; iter->cursor += 4; return TRUE; } gboolean g_isi_sb_iter_get_oper_code(const GIsiSubBlockIter *restrict iter, char *mcc, char *mnc, unsigned pos) { if (pos + 2 > g_isi_sb_iter_get_len(iter)) return FALSE; bcd_to_mccmnc(iter->start + pos, mcc, mnc); return TRUE; } gboolean g_isi_sb_iter_eat_oper_code(GIsiSubBlockIter *restrict iter, char *mcc, char *mnc) { if (!g_isi_sb_iter_get_oper_code(iter, mcc, mnc, iter->cursor)) return FALSE; iter->cursor += 3; return TRUE; } gboolean g_isi_sb_iter_get_alpha_tag(const GIsiSubBlockIter *restrict iter, char **utf8, size_t len, unsigned pos) { uint8_t *ucs2 = NULL; if (pos > g_isi_sb_iter_get_len(iter)) return FALSE; if (utf8 == NULL || len == 0 || pos + len > g_isi_sb_iter_get_len(iter)) return FALSE; ucs2 = iter->start + pos; if (ucs2 + len > iter->end) return FALSE; *utf8 = g_convert((const char *) ucs2, len, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); return *utf8 != NULL; } gboolean g_isi_sb_iter_eat_alpha_tag(GIsiSubBlockIter *restrict iter, char **utf8, size_t len) { if (!g_isi_sb_iter_get_alpha_tag(iter, utf8, len, iter->cursor)) return FALSE; iter->cursor += len; return TRUE; } gboolean g_isi_sb_iter_get_latin_tag(const GIsiSubBlockIter *restrict iter, char **latin, size_t len, unsigned pos) { uint8_t *str = NULL; if (pos > g_isi_sb_iter_get_len(iter)) return FALSE; if (latin == NULL || len == 0) return FALSE; if (pos + len > g_isi_sb_iter_get_len(iter)) return FALSE; str = iter->start + pos; if (str + len > iter->end) return FALSE; *latin = g_strndup((char *) str, len); return *latin != NULL; } gboolean g_isi_sb_iter_eat_latin_tag(GIsiSubBlockIter *restrict iter, char **latin, size_t len) { if (!g_isi_sb_iter_get_latin_tag(iter, latin, len, iter->cursor)) return FALSE; iter->cursor += len; return TRUE; } gboolean g_isi_sb_iter_next(GIsiSubBlockIter *iter) { uint8_t len = g_isi_sb_iter_get_len(iter); if (len == 0) len = iter->longhdr ? 4 : 2; if (iter->sub_blocks == 0) return FALSE; if (iter->start + len > iter->end) return FALSE; iter->cursor = iter->longhdr ? 4 : 2; iter->start += len; iter->sub_blocks--; return TRUE; } gboolean g_isi_sb_iter_get_struct(const GIsiSubBlockIter *restrict iter, void **type, size_t len, unsigned pos) { if (iter->start + pos + len > iter->end) return FALSE; return g_isi_sb_iter_get_data(iter, type, pos); } ofono-1.17.bzr6912+16.04.20160314.3/gisi/netlink.c0000644000015600001650000002102212671500024021137 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #ifndef SOL_NETLINK #define SOL_NETLINK 270 /* libc!? */ #endif #include "phonet.h" #include #include #include #include #include #include "netlink.h" #ifndef ARPHRD_PHONET #define ARPHRD_PHONET (820) #endif /* * GCC -Wcast-align does not like rtlink alignment macros, * fixed macros by Andrzej Zaborowski . */ #undef IFA_RTA #define IFA_RTA(r) ((struct rtattr *)(void *)(((char *)(r)) \ + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) #undef IFLA_RTA #define IFLA_RTA(r) ((struct rtattr *)(void *)(((char *)(r)) \ + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) #undef NLMSG_NEXT #define NLMSG_NEXT(nlh, len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ (struct nlmsghdr *)(void *)(((char *)(nlh)) \ + NLMSG_ALIGN((nlh)->nlmsg_len))) #undef RTA_NEXT #define RTA_NEXT(rta, attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ (struct rtattr *)(void *)(((char *)(rta)) \ + RTA_ALIGN((rta)->rta_len))) #define SIZE_NLMSG (16384) struct _GIsiPhonetNetlink { GIsiModem *modem; GIsiPhonetNetlinkFunc callback; void *opaque; guint watch; }; static GSList *netlink_list; static void bring_up(unsigned ifindex) { struct ifreq req = { .ifr_ifindex = ifindex, }; int fd = socket(PF_LOCAL, SOCK_DGRAM, 0); if (ioctl(fd, SIOCGIFNAME, &req) || ioctl(fd, SIOCGIFFLAGS, &req)) goto error; req.ifr_flags |= IFF_UP | IFF_RUNNING; ioctl(fd, SIOCSIFFLAGS, &req); error: close(fd); } static int pn_netlink_socket(void) { int fd; int bufsize = SIZE_NLMSG; fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (fd == -1) return -1; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize))) { int error = errno; close(fd), fd = -1; errno = error; } return fd; } static void pn_netlink_link(GIsiPhonetNetlink *self, struct nlmsghdr *nlh) { const struct ifinfomsg *ifi; const struct rtattr *rta; int len; const char *ifname = NULL; enum GIsiPhonetLinkState st; unsigned interface; ifi = NLMSG_DATA(nlh); len = IFA_PAYLOAD(nlh); if (ifi->ifi_type != ARPHRD_PHONET) return; interface = g_isi_modem_index(self->modem); if (interface != 0 && interface != (unsigned)ifi->ifi_index) return; #define UP (IFF_UP | IFF_LOWER_UP | IFF_RUNNING) if (nlh->nlmsg_type == RTM_DELLINK) st = PN_LINK_REMOVED; else if ((ifi->ifi_flags & UP) != UP) st = PN_LINK_DOWN; else st = PN_LINK_UP; for (rta = IFLA_RTA(ifi); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { if (rta->rta_type == IFLA_IFNAME) ifname = RTA_DATA(rta); } if (ifname && self->modem) self->callback(self->modem, st, ifname, self->opaque); #undef UP } /* Parser Netlink messages */ static gboolean pn_netlink_process(GIOChannel *channel, GIOCondition cond, gpointer data) { struct { struct nlmsghdr nlh; char buf[SIZE_NLMSG]; } resp; struct iovec iov = { &resp, sizeof(resp), }; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, }; ssize_t ret; struct nlmsghdr *nlh; int fd = g_io_channel_unix_get_fd(channel); GIsiPhonetNetlink *self = data; if (cond & (G_IO_NVAL|G_IO_HUP)) return FALSE; ret = recvmsg(fd, &msg, 0); if (ret == -1) return TRUE; if (msg.msg_flags & MSG_TRUNC) { g_printerr("Netlink message of %zu bytes truncated at %zu\n", ret, sizeof(resp)); return TRUE; } for (nlh = &resp.nlh; NLMSG_OK(nlh, (size_t)ret); nlh = NLMSG_NEXT(nlh, ret)) { if (nlh->nlmsg_type == NLMSG_DONE) break; switch (nlh->nlmsg_type) { case NLMSG_ERROR: { struct nlmsgerr *err = NLMSG_DATA(nlh); if (err->error) g_printerr("Netlink error: %s", strerror(-err->error)); return TRUE; } case RTM_NEWLINK: case RTM_DELLINK: pn_netlink_link(self, nlh); break; } } return TRUE; } /* Dump current links */ static int pn_netlink_getlink(int fd) { struct { struct nlmsghdr nlh; struct ifinfomsg ifi; } req = { .nlh = { .nlmsg_type = RTM_GETLINK, .nlmsg_len = sizeof(req), .nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_MATCH, .nlmsg_pid = getpid(), }, .ifi = { .ifi_family = AF_UNSPEC, .ifi_type = ARPHRD_PHONET, .ifi_change = 0xffFFffFF, } }; struct sockaddr_nl addr = { .nl_family = AF_NETLINK, }; return sendto(fd, &req, sizeof(req), 0, (struct sockaddr *)&addr, sizeof(addr)); } GIsiPhonetNetlink *g_isi_pn_netlink_by_modem(GIsiModem *modem) { GSList *m; for (m = netlink_list; m; m = m->next) { GIsiPhonetNetlink *self = m->data; if (g_isi_modem_index(modem) == g_isi_modem_index(self->modem)) return self; } return NULL; } GIsiPhonetNetlink *g_isi_pn_netlink_start(GIsiModem *modem, GIsiPhonetNetlinkFunc cb, void *data) { GIOChannel *chan; GIsiPhonetNetlink *self; int fd; unsigned group = RTNLGRP_LINK; unsigned interface; fd = pn_netlink_socket(); if (fd == -1) return NULL; self = g_try_new0(GIsiPhonetNetlink, 1); if (self == NULL) goto error; fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL)); if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group))) goto error; interface = g_isi_modem_index(modem); if (interface) bring_up(interface); pn_netlink_getlink(fd); chan = g_io_channel_unix_new(fd); if (chan == NULL) goto error; g_io_channel_set_close_on_unref(chan, TRUE); g_io_channel_set_encoding(chan, NULL, NULL); g_io_channel_set_buffered(chan, FALSE); self->callback = cb; self->opaque = data; self->modem = modem; self->watch = g_io_add_watch(chan, G_IO_IN|G_IO_ERR|G_IO_HUP, pn_netlink_process, self); g_io_channel_unref(chan); netlink_list = g_slist_prepend(netlink_list, self); return self; error: close(fd); free(self); return NULL; } void g_isi_pn_netlink_stop(GIsiPhonetNetlink *self) { if (self == NULL) return; netlink_list = g_slist_remove(netlink_list, self); g_source_remove(self->watch); g_free(self); } static int pn_netlink_getack(int fd) { struct { struct nlmsghdr nlh; char buf[SIZE_NLMSG]; } resp; struct iovec iov = { &resp, sizeof(resp), }; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, }; ssize_t ret; struct nlmsghdr *nlh = &resp.nlh; ret = recvmsg(fd, &msg, 0); if (ret == -1) return -errno; if (msg.msg_flags & MSG_TRUNC) return -EIO; for (; NLMSG_OK(nlh, (size_t)ret); nlh = NLMSG_NEXT(nlh, ret)) { if (nlh->nlmsg_type == NLMSG_DONE) return 0; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = NLMSG_DATA(nlh); return err->error; } } return -EIO; } /* Set local address */ static int pn_netlink_setaddr(uint32_t ifa_index, uint8_t ifa_local) { struct ifaddrmsg *ifa; struct rtattr *rta; uint32_t reqlen = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(*ifa)) + RTA_SPACE(1)); struct req { struct nlmsghdr nlh; char buf[512]; } req = { .nlh = { .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, .nlmsg_type = RTM_NEWADDR, .nlmsg_pid = getpid(), .nlmsg_len = reqlen, }, }; int fd; int error; struct sockaddr_nl addr = { .nl_family = AF_NETLINK, }; ifa = NLMSG_DATA(&req.nlh); ifa->ifa_family = AF_PHONET; ifa->ifa_prefixlen = 0; ifa->ifa_index = ifa_index; rta = IFA_RTA(ifa); rta->rta_type = IFA_LOCAL; rta->rta_len = RTA_LENGTH(1); *(uint8_t *)RTA_DATA(rta) = ifa_local; fd = pn_netlink_socket(); if (fd == -1) return -errno; if (sendto(fd, &req, reqlen, 0, (void *)&addr, sizeof(addr)) == -1) error = -errno; else error = pn_netlink_getack(fd); close(fd); return error; } int g_isi_pn_netlink_set_address(GIsiModem *modem, uint8_t local) { uint32_t ifindex = g_isi_modem_index(modem); if (ifindex == 0) return -ENODEV; if (local != PN_DEV_PC && local != PN_DEV_SOS) return -EINVAL; return pn_netlink_setaddr(ifindex, local); } ofono-1.17.bzr6912+16.04.20160314.3/gisi/message.h0000644000015600001650000000405412671500024021132 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __GISI_MESSAGE_H #define __GISI_MESSAGE_H #ifdef __cplusplus extern "C" { #endif #include #include #include "phonet.h" struct _GIsiVersion { int major; int minor; }; typedef struct _GIsiVersion GIsiVersion; struct _GIsiMessage { struct sockaddr_pn *addr; GIsiVersion *version; int error; const void *__restrict data; size_t len; void *private; }; typedef struct _GIsiMessage GIsiMessage; int g_isi_msg_version_major(const GIsiMessage *msg); int g_isi_msg_version_minor(const GIsiMessage *msg); int g_isi_msg_error(const GIsiMessage *msg); const char *g_isi_msg_strerror(const GIsiMessage *msg); uint8_t g_isi_msg_resource(const GIsiMessage *msg); uint16_t g_isi_msg_object(const GIsiMessage *msg); uint8_t g_isi_msg_id(const GIsiMessage *msg); uint8_t g_isi_msg_utid(const GIsiMessage *msg); size_t g_isi_msg_data_len(const GIsiMessage *msg); const void *g_isi_msg_data(const GIsiMessage *msg); gboolean g_isi_msg_data_get_byte(const GIsiMessage *msg, unsigned offset, uint8_t *byte); gboolean g_isi_msg_data_get_word(const GIsiMessage *msg, unsigned offset, uint16_t *word); gboolean g_isi_msg_data_get_struct(const GIsiMessage *msg, unsigned offset, const void **type, size_t len); #ifdef __cplusplus } #endif #endif /* __GISI_MESSAGE_H */ ofono-1.17.bzr6912+16.04.20160314.3/gisi/client.c0000644000015600001650000001041412671500024020754 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include "client.h" struct _GIsiClient { GIsiModem *modem; unsigned timeout; uint8_t resource; }; uint8_t g_isi_client_resource(GIsiClient *client) { return client != NULL ? client->resource : 0; } GIsiModem *g_isi_client_modem(GIsiClient *client) { return client != NULL ? client->modem : NULL; } GIsiClient *g_isi_client_create(GIsiModem *modem, uint8_t resource) { GIsiClient *client; if (modem == NULL) { errno = EINVAL; return NULL; } client = g_try_new0(GIsiClient, 1); if (client == NULL) { errno = ENOMEM; return NULL; } client->timeout = G_ISI_CLIENT_DEFAULT_TIMEOUT; client->resource = resource; client->modem = modem; return client; } void g_isi_client_reset(GIsiClient *client) { g_isi_remove_pending_by_owner(client->modem, client->resource, client); }; void g_isi_client_destroy(GIsiClient *client) { if (client == NULL) return; g_isi_client_reset(client); g_free(client); } void g_isi_client_set_timeout(GIsiClient *client, unsigned timeout) { if (client == NULL) return; client->timeout = timeout; } gboolean g_isi_client_send(GIsiClient *client, const void *__restrict msg, size_t len, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { GIsiPending *op; op = g_isi_request_send(client->modem, client->resource, msg, len, client->timeout, notify, data, destroy); g_isi_pending_set_owner(op, client); return op != NULL; } gboolean g_isi_client_send_with_timeout(GIsiClient *client, const void *__restrict buf, size_t len, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { GIsiPending *op; op = g_isi_request_send(client->modem, client->resource, buf, len, timeout, notify, data, destroy); g_isi_pending_set_owner(op, client); return op != NULL; } gboolean g_isi_client_vsend(GIsiClient *client, const struct iovec *iov, size_t iovlen, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { GIsiPending *op; op = g_isi_request_vsend(client->modem, client->resource, iov, iovlen, client->timeout, notify, data, destroy); g_isi_pending_set_owner(op, client); return op != NULL; } gboolean g_isi_client_vsend_with_timeout(GIsiClient *client, const struct iovec *__restrict iov, size_t iovlen, unsigned timeout, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { GIsiPending *op; op = g_isi_request_vsend(client->modem, client->resource, iov, iovlen, timeout, notify, data, destroy); g_isi_pending_set_owner(op, client); return op != NULL; } gboolean g_isi_client_ind_subscribe(GIsiClient *client, uint8_t type, GIsiNotifyFunc notify, void *data) { GIsiPending *op; op = g_isi_ind_subscribe(client->modem, client->resource, type, notify, data, NULL); g_isi_pending_set_owner(op, client); return op != NULL; } gboolean g_isi_client_ntf_subscribe(GIsiClient *client, uint8_t type, GIsiNotifyFunc notify, void *data) { GIsiPending *op; op = g_isi_ntf_subscribe(client->modem, client->resource, type, notify, data, NULL); g_isi_pending_set_owner(op, client); return op != NULL; } gboolean g_isi_client_verify(GIsiClient *client, GIsiNotifyFunc notify, void *data, GDestroyNotify destroy) { GIsiPending *op; op = g_isi_resource_ping(client->modem, client->resource, notify, data, destroy); g_isi_pending_set_owner(op, client); return op != NULL; } ofono-1.17.bzr6912+16.04.20160314.3/gisi/pipe.h0000644000015600001650000000305212671500024020440 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 * */ #ifndef __GISI_PIPE_H #define __GISI_PIPE_H #ifdef __cplusplus extern "C" { #endif struct _GIsiPipe; typedef struct _GIsiPipe GIsiPipe; typedef void (*GIsiPipeHandler)(GIsiPipe *pipe); typedef void (*GIsiPipeErrorHandler)(GIsiPipe *pipe); GIsiPipe *g_isi_pipe_create(GIsiModem *modem, GIsiPipeHandler cb, uint16_t obj1, uint16_t obj2, uint8_t type1, uint8_t type2); void g_isi_pipe_destroy(GIsiPipe *pipe); void g_isi_pipe_set_error_handler(GIsiPipe *pipe, GIsiPipeErrorHandler cb); int g_isi_pipe_get_error(const GIsiPipe *pipe); void *g_isi_pipe_set_userdata(GIsiPipe *pipe, void *data); void *g_isi_pipe_get_userdata(GIsiPipe *pipe); uint8_t g_isi_pipe_get_handle(GIsiPipe *pipe); int g_isi_pipe_start(GIsiPipe *pipe); #ifdef __cplusplus } #endif #endif /* __GISI_PIPE_H */ ofono-1.17.bzr6912+16.04.20160314.3/bootstrap0000755000015600001650000000022212671500024020336 0ustar pbuserpbgroup00000000000000#!/bin/sh aclocal && \ autoheader && \ libtoolize --automake --copy --force && \ autoconf && \ automake --add-missing --copy ofono-1.17.bzr6912+16.04.20160314.3/acinclude.m40000644000015600001650000000143112671500024020567 0ustar pbuserpbgroup00000000000000AC_DEFUN([AC_PROG_CC_PIE], [ AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [ echo 'void f(){}' > conftest.c if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then ac_cv_prog_cc_pie=yes else ac_cv_prog_cc_pie=no fi rm -rf conftest* ]) ]) AC_DEFUN([COMPILER_FLAGS], [ if (test "${CFLAGS}" = ""); then CFLAGS="-Wall -O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2" fi if (test "$USE_MAINTAINER_MODE" = "yes"); then CFLAGS="$CFLAGS -Werror -Wextra" CFLAGS="$CFLAGS -Wno-unused-parameter" CFLAGS="$CFLAGS -Wno-missing-field-initializers" CFLAGS="$CFLAGS -Wdeclaration-after-statement" CFLAGS="$CFLAGS -Wmissing-declarations" CFLAGS="$CFLAGS -Wredundant-decls" CFLAGS="$CFLAGS -Wcast-align" CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED" fi ]) ofono-1.17.bzr6912+16.04.20160314.3/TODO0000644000015600001650000004242112671500024017072 0ustar pbuserpbgroup00000000000000Background ========== - Priority scale: High, Medium and Low - Complexity scale: C1, C2, C4 and C8. The complexity scale is exponential, with complexity 1 being the lowest complexity. Complexity is a function of both task 'complexity' and task 'scope'. The general rule of thumb is that a complexity 1 task should take 1-2 weeks for a person very familiar with oFono codebase. Higher complexity tasks require more time and have higher uncertainty. Higher complexity tasks should be refined into several lower complexity tasks once the task is better understood. SMS === - Add support for Enhanced SMS (EMS) from 3GPP 23.040. These SMS messages support a richer set of formatting elements, including bold/italic and font size selection. Embedding images and sounds into the SMS is also supported. This task should add support for receiving such SMS messages. Proposed solution is to convert such messages to HTML and embed image / sound data as embedded MIME in the generated HTML stream. The EMS messages will have to be detected (by the presence of particular tags in the SMS) and signaled separately from simple SMS messages. Priority: Low Complexity: C8 - Asynchronously acknowledge SMS DELIVER messages sent by the SMS driver to core using ofono_sms_deliver_notify(). This may require the struct ofono_sms_driver to be extended with one more function pointer like: void (*deliver_ack)(unsigned char *pdu, int len, cb_t cb, void *data) because currently messages are automatically acknowledged by either the modem (this is the case of some AT modems) or the driver right after ofono_sms_deliver_notify() and a failure to deliver at an upper level is ignored. The PDU can be an RP-ACK or RP-ERROR message with optional TP-User-Content element, for example if returned from USIM Data Download. Priority: Low Complexity: C2 CBS === - Support UMTS format for CBS messages. This might be needed by some hardware which does not convert UMTS-formatted cell broadcasts to GSM-formatted cell broadcasts. The UMTS CBS format can be found in 3GPP 25.324 Section 11.1. Priority: Low Complexity: C2 SIM / SIM File system ===================== - SIM Call History plugin. New UICCs support four new SIM elementary files for storing call history information on the SIM: EFici, EFict, EFoci, EFoct. A plugin should be developed for oFono that will write to these files. Priority: Low Complexity: C2 - Add support for SIM 'ready' notifications from the driver to the core. Most modem manufacturers initialize the SIM (e.g. cache SIM file system, STK initialization, etc) internally before allowing the telephony stack to access these portions. When the PIN is locked, this can lead to oFono being too fast for the modem and asking it for things before the firmware is ready. The proposal is to introduce a new sim function: void ofono_sim_ready_notify(struct ofono_sim *sim); When oFono determines the SIM PIN is READY, it checks whether ofono_sim_ready_notify has been called. If it hasn't, then it stalls the initialization procedure (and calling post_sim) until ofono_sim_ready_notify is called. Priority: High Complexity: C2 - Support SIM authentication: SIM and AKA suites. Priority: Medium Complexity: C4 - Support SIM authentication: GBA_U suite. Priority: Low Complexity: C4 - ISIM support ISIM is the SIM application for IP Multimedia Subsystem, specified in 3GPP TS 31.103. The UICCs can support multiple ISIMs for different IMS identities. Priority: Low Complexity: C4 Modem Emulator ============== - Support CE4A extensions to HFP AG emulator. CE4A defines additional AT command extensions to the Bluetooth HFP AG standard. Refer to CE4A white paper: "AT-commands for Automotive Premium Phone Integration". Plugins can register additional command handlers appropriately to handle such commands. Complexity: C4 Priority: Low Depends: HFP AG emulator - Support HSP AG. Similar to HFP AG emulator but implements the much reduced Bluetooth HSP AG profile. Priority: Low Complexity: C1 - Support DUN networking over the USB transport. This might require extra AT commands to be implemented in order to comply with general USB DUN expectations as there is no standard for the same. Complexity: C4 Priority: Low - Support Bluetooth SPP profile. Complexity: C4 Priority: Medium - Support new HFP 1.6 AG commands allowing to publish, select and connect audio codecs (AT+BAC, AT+BCS, +BCS, AT+BCC). This will need to interact with audio framework. Complexity: C4 Priority: Low Depends: HFP AG emulator - Integrate HFP AG emulator as a BlueZ service. Replace direct access to Bluetooth library by usage of the BlueZ service architecture. Complexity: C2 Priority: Medium Depends: HFP AG emulator - Add audio management to HFP AG emulator. Integrate HFP AG emulator to BlueZ and Pulse Audio. Add audio related AT commands support: remote audio volume control and in-band ring tone management. Complexity: C4 Priority: Medium Depends: HFP AG emulator as BlueZ service PPP === - IPv6 CP support. To support IPv6 based GPRS contexts via PPP, GAtPPP needs to be updated to support IPv6CP from RFC 2472. Priority: Low Complexity: C4 Location Services ================= - Neighbor Cell Info. Add dedicated atom, D-Bus API and atom driver(s) for Neighbor Cell information. This feature is not discussed in 27.007, thus manufacturer specific commands are required. Complexity: C4 Priority: Medium Supplementary Services ====================== - Closed User Group (CUG) support. Priority: Low Complexity: C8 - Call Completion to Busy Subscriber (CCBS) support This feature is not discussed in 27.007, thus manufacturer specific commands are required. Priority: Low Complexity: C8 - User to User Signaling (UUS) support Priority: Low Complexity: C8 - Multiple Subscriber Profile (MSP) support Priority: Low Complexity: C2 - CPHS Support. This includes ALS and CPHS specific elementary files. Priority: Low Complexity: C8 - Call forwarding state handling change At the moment call forwarding states are not always correct. Any active conditional call forwarding should become quiescent while unconditional call forwarding is activate. If call forwarding unconditional is subsequently deactivated, all the quiescent forwardings should become operative again. I.e. No conditional call forwarding string should be returned while unconditional call forwarding is active even if they exist. If there is an successful attempt to activate/deactivate conditional call forwarding while unconditional call forwarding is active the conditional cache flag should cleared. Priority: High Complexity: C1 Owner: Nicolas Bertrand Voicecall ========= - Dial strings. Include CLIR prefixes and 2nd stage dial strings in the DialString call property. Add dialstring accessor method to C API. Priority: Medium Complexity: C4 - Provide feedback of sent DTMF tones. Emit SendingTones signal if modem can provide approximate starting and stopping times for DTMF tones. Signal argument contains a string of DTMF tones to be sent, or empty string when all tones has been sent. Priority: Medium Complexity: C2 - Blacklisting. According to 3GPP TS 22.001 annex E, the TE must provide automatic calling repeat call attempt restrictions. There should be a method to manually reset blacklisting. Priority: Medium Complexity: C1 Sim Toolkit =========== - Support of the BIP (Bearer Independent Protocol) proactive commands. The specification defines several bearer types. For now, only the packet data service bearer is considered. - OPEN CHANNEL: requests the terminal to open a data channel with parameters indicated in the command. A user confirmation may be requested by the SimToolkitAgent. - CLOSE CHANNEL:requests the terminal to close the specified data channel. - RECEIVE DATA:requests the terminal to return to the UICC data received on the specified channel. - SEND DATA:requests the terminal to send on the specified channel data provided by the UICC. - GET CHANNEL STATUS: requests the terminal to return the current status of all available data channels. Priority: Medium Complexity: C4 Owner: Philippe Nunes - Support Setup Event List proactive command. To fully support the class 'e', the following events -Data Available event -Channel status event shall be monitored by oFono if part of the current event list. This list is supplied by the last SETUP EVENT LIST command. Priority: Medium Complexity: C2 Owner: Philippe Nunes Miscellaneous ============= - PolicyKit support. Add support for PolicyKit checking of all oFono D-Bus interfaces. Complexity: C4 Priority: Low - Add Location Service API for providing basic E911 support. This will be based on the 27.007 defined AT commands using XML for transport of positioning request and responses. Priority: Medium Complexity: C2 CDMA Voicecall ============== - Add support for Mobile Originated and Mobile Terminated Voice Call over a CDMA network. This includes management of call state and providing appropriate values for the LineIdentification in each case. Priority: High Complexity: C2 - Add support for Three-Way Calling over a CDMA network. Three-Way Calling provides the subscriber with the capability to add a third party to an established two-party call, so that all three parties may communicate in a three-way call. In CDMA mode, the originating subscriber of a current conversation can request for a third party to be added to a conversation by sending a Flash With Information Message (FWIM) with dialed digits to the network. Upon setting up a two-way conversation with the added party, the originating subscriber can request to establish a three-way conversation by sending another Flash With Information Message. Upon receiving the second Flash With Information Message, the MSC reconnects the original party to the conversation thus completing the setup of a three-way conversation. CDMA Three-Way Calling is described by Figure B-5 in 3GPP2 C.S0005-E Version 2.0. Priority: High Complexity: C2 - Add support for Call Waiting over a CDMA network. Call Waiting (CW) provides notification of an incoming call to an originating subscriber, while the subscriber's call is in the 2-way state. Subsequently, the originating subscriber can either answer or ignore the incoming call. If the originating subscriber answers the second call, it may alternate between the two calls. In CDMA mode, the originating subscriber of a current conversation will receive either a Flash With Information Message or an Alert With Information Message from the network if there is an additional mobile terminated voice call incoming. The originating subscriber can change conversation parties by sending a Flash With Information Message to the network and the MSC will toggle the speech path between the two conversations. CDMA Call Waiting is described by Figure B-6 in 3GPP2 C.S0005-E Version 2.0. Priority: High Complexity: C2 - Support sending DTMF tones over CDMA network. Priority: High Complexity: C2 - Support optional network-based Plus Code Dialing for international calls over a CDMA network. An input key, e.g. the "+" key, or a functional equivalent can be used to replace the international access prefix when dialing. When received, transmitted or stored, an international indicator can be included with the address digits although it is the responsibility of the network to ignore the international indicator when attached to a national number. This is described in Section 2.7.1.3.2.4 of 3GPP2 C.S0005-E v2.0 and Section 1.2 of 3GPP2 N.S0027 v1.0. Priority: High Complexity: C2 CDMA SMS ============== - Support CDMA SMS stack in PDU mode. This includes basic support of SMS Point-to-Point Message, SMS Broadcast Message and SMS Acknowledge Message as per 3GPP2 C.S0015-B version 2.0. Priority: High Complexity: C4 - Support sending Wireless Messaging Teleservice (WMT) Submit Message and receiving WMT Deliver Message as defined 3GPP2 C.S0015-B version 2.0. Priority: High Complexity: C4 - Support Delivery Acknowledgment. oFono allows requesting of CDMA SMS Delivery Acknowledgment via the MessageManager's UseDeliveryAcknowledgement property. If enabled, oFono's CDMA SMS stack will encode the Reply Option subparameter in the Submit message and process incoming SMS Delivery Acknowledgment Message. oFono will notify UI either via DBus or history plugin API. Priority: Medium Complexity: C2 - Support receiving Voice Mail Notification (VMN) Teleservice Deliver message. CDMA network uses VMN Teleservice to deliver the number of messages stored at the Voice Mail System to the CDMA mobile subscriber. Priority: High Complexity: C4 - Support sending Wireless Enhanced Messaging Teleservice (WEMT) Submit Message and receiving WEMT Deliver Messsage as defined 3GPP2 C.S0015-B version 2.0. WMT does not support message fragmentation thus can not be used to for long message. WEMT is devised to support long message and Enhanced Messaging Service (EMS). The WEMT SMS message's CHARi field of the subparameter User Data encapsulate GSM-SMS TP-User Data as defined in Section 9.2.3.24 of 3GPP TS 23.040. Priority: Medium Complexity: C4 - Support sending Wireless Application Protocol (WAP) Teleservice Submit Message and receiving WAP Deliver Messsage as defined 3GPP2 C.S0015-B version 2.0. Priority: Medium Complexity: C4 - Support Call-Back Number. The Call-Back Number subparameter indicates the number to be dialed in reply to a received SMS message. In transmit direction, oFono allows setting of Call-Back Number. If the Call Back Number property is set, CDMA SMS stack will encode Call-Back Number subparameter in the Submit Message. In receiving direction, oFono will process the Call-Back Number subparameter in the incoming Deliver Message and notify UI of the Call-Back Number together with the newly received text message. Priority: Medium Complexity: C2 - Support immediately displayed message. oFono CDMA SMS stack will process the optional Message Display Mode subparameter in the incoming SMS message. If Message Display Mode subparameter indicates the message display mode is Immediate Display, oFono will send ImmediateMessage signal, otherwise oFono will send IncomingMessage signal. Priority: Medium Complexity: C2 CDMA CMAS ============== - Support Commercial Mobile Alert Service (CMAS) over CDMA systems. CMAS over CDMA system is defined in TIA-1149. The CMAS message is carried in the CHARi field of the User Data subparameter of CDMA SMS Broadcast message. Priority: Medium Complexity: C4 CDMA Network Acquisition ======================== - Support reporting of the pilot energy ratio (Ec/Io) measurement for the currently acquired CDMA network. Priority: Medium Complexity: C1 - Support of the signal to interference-plus-noise ratio (SINR) measurement for the currently acquired 1xEV-DO data network. Priority: Medium Complexity: C1 - Support reporting of the Enhanced Roaming Indicators (ERI) to indicate the current roaming condition of the CDMA mobile device. Each indicator maps to a unique display number within the Standard and Non-Standard service ranges, as described in Section 8 of 3GPP2 C.R1001-C v1.0. These numbers are stored on the device in the (Enhanced) Preferred Roaming List (PRL) and it is the responsibility of the modem to broadcast the relevant indicator for a currently acquired system. Further details of the system acquisition process are described in 3GPP2 C.S0016-B v1.0. Priority: Medium Complexity: C2 - Support reporting of identifiers of the currently acquired CDMA network, including the System Identifier (SID) and the Network Identifier (NID), It is the responsibility of the modem to broadcast the relevant identifiers for a currently acquired system, and these identifiers are provided by the network. This is described in 3GPP2 C.S0005-E v2.0. Priority: Medium Complexity: C2 - Support International Roaming, including support for reporting the Mobile Country Code (MCC) and the Mobile Network Code (MNC) for the currently acquired network. International Roaming is provided via enhancements to the PRL by encoding the MCC and the (two digit only) MNC in existing SID/NID fields, as described in the CDMA Development Group standards Document "IPRL Enhancements for International Roaming - CDG Doc #86". It is the responsibility of the modem to broadcast the values for the currently acquired system. Priority: Medium Complexity: C2 - Support reporting of the current registered operator name in long alphanumeric format. Based on the System Identifier (SID) broadcasted by the modem, the CDMA network name is retrieved from a look-up table (aka the 'mobile broadband provider info' database). Priority: Medium Complexity: C2 CDMA Connection Manager ======================= - Support Packet Data Service over CDMA (1xRTT and 1xEV-DO) systems. This includes Mobile Originated connection and disconnection features. Priority: Medium Complexity: C4 ofono-1.17.bzr6912+16.04.20160314.3/INSTALL0000644000015600001650000002240612671500024017434 0ustar pbuserpbgroup00000000000000Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PREFIX', the package will use PREFIX as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Here is a another example: /bin/bash ./configure CONFIG_SHELL=/bin/bash Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent configuration-related scripts to be executed by `/bin/bash'. `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. ofono-1.17.bzr6912+16.04.20160314.3/unit/0000755000015600001650000000000012671500304017357 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/unit/test-simutil.c0000644000015600001650000004044512671500024022174 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include "simutil.h" #include "util.h" /* Taken from 51.011 Appendix K.2 */ const unsigned char valid_mms_params[] = { 0xAB, 0x81, 0x88, 0x80, 0x01, 0x01, 0x81, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6D, 0x6D, 0x73, 0x2D, 0x6F, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6F, 0x72, 0x2E, 0x63, 0x6F, 0x6D, 0x82, 0x32, 0x10, 0xAA, 0x08, 0x2B, 0x34, 0x39, 0x35, 0x33, 0x34, 0x31, 0x39, 0x30, 0x36, 0x00, 0x09, 0x87, 0x25, 0xC5, 0x0A, 0x90, 0x0C, 0x9A, 0x0D, 0x64, 0x75, 0x6D, 0x6D, 0x79, 0x5F, 0x6E, 0x61, 0x6D, 0x65, 0x00, 0x0E, 0x64, 0x75, 0x6D, 0x6D, 0x79, 0x5F, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x00, 0x83, 0x36, 0x20, 0x31, 0x37, 0x30, 0x2E, 0x31, 0x38, 0x37, 0x2E, 0x35, 0x31, 0x2E, 0x33, 0x00, 0x21, 0x85, 0x23, 0x39, 0x32, 0x30, 0x33, 0x00, 0x24, 0xCB, 0x19, 0x9C, 0x1A, 0x64, 0x75, 0x6D, 0x6D, 0x79, 0x5F, 0x6E, 0x61, 0x6D, 0x65, 0x00, 0x1B, 0x64, 0x75, 0x6D, 0x6D, 0x79, 0x5F, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x00 }; static void test_buffer(const unsigned char *buf, size_t size) { struct ber_tlv_iter iter; struct ber_tlv_iter cont; ber_tlv_iter_init(&iter, buf, size); g_assert(ber_tlv_iter_next(&iter) == TRUE); g_assert(ber_tlv_iter_get_short_tag(&iter) == 0xAB); ber_tlv_iter_recurse(&iter, &cont); g_assert(ber_tlv_iter_next(&cont) == TRUE); g_assert(ber_tlv_iter_get_short_tag(&cont) == 0x80); g_assert(ber_tlv_iter_get_length(&cont) == 1); g_assert(ber_tlv_iter_next(&cont) == TRUE); g_assert(ber_tlv_iter_get_short_tag(&cont) == 0x81); g_assert(ber_tlv_iter_get_length(&cont) == 23); g_assert(ber_tlv_iter_next(&cont) == TRUE); g_assert(ber_tlv_iter_get_short_tag(&cont) == 0x82); g_assert(ber_tlv_iter_get_length(&cont) == 50); g_assert(ber_tlv_iter_next(&cont) == TRUE); g_assert(ber_tlv_iter_get_short_tag(&cont) == 0x83); g_assert(ber_tlv_iter_get_length(&cont) == 54); g_assert(ber_tlv_iter_next(&cont) == FALSE); g_assert(ber_tlv_iter_next(&iter) == FALSE); } static void test_ber_tlv_iter(void) { test_buffer(valid_mms_params, sizeof(valid_mms_params)); } static void test_ber_tlv_builder_mms(void) { struct ber_tlv_iter top_iter, nested_iter; struct ber_tlv_builder top_builder, nested_builder; unsigned char buf[512], *pdu; unsigned int pdulen; ber_tlv_iter_init(&top_iter, valid_mms_params, sizeof(valid_mms_params)); g_assert(ber_tlv_builder_init(&top_builder, buf, sizeof(buf))); /* Copy the structure */ while (ber_tlv_iter_next(&top_iter) == TRUE) { g_assert(ber_tlv_builder_next(&top_builder, ber_tlv_iter_get_class(&top_iter), ber_tlv_iter_get_encoding(&top_iter), ber_tlv_iter_get_tag(&top_iter))); ber_tlv_iter_recurse(&top_iter, &nested_iter); g_assert(ber_tlv_builder_recurse(&top_builder, &nested_builder)); while (ber_tlv_iter_next(&nested_iter) == TRUE) { g_assert(ber_tlv_builder_next(&nested_builder, ber_tlv_iter_get_class(&nested_iter), ber_tlv_iter_get_encoding(&nested_iter), ber_tlv_iter_get_tag(&nested_iter))); g_assert(ber_tlv_builder_set_length(&nested_builder, ber_tlv_iter_get_length(&nested_iter))); memcpy(ber_tlv_builder_get_data(&nested_builder), ber_tlv_iter_get_data(&nested_iter), ber_tlv_iter_get_length(&nested_iter)); } ber_tlv_builder_optimize(&nested_builder, NULL, NULL); } ber_tlv_builder_optimize(&top_builder, &pdu, &pdulen); test_buffer(pdu, pdulen); } static void test_ber_tlv_builder_efpnn(void) { struct sim_eons *eons_info; unsigned char efpnn0[64], efpnn1[64]; struct ber_tlv_builder builder; g_assert(ber_tlv_builder_init(&builder, efpnn0, sizeof(efpnn0))); g_assert(ber_tlv_builder_next(&builder, BER_TLV_DATA_TYPE_APPLICATION, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x03)); g_assert(ber_tlv_builder_set_length(&builder, 10)); ber_tlv_builder_get_data(&builder)[0] = 0x00; ber_tlv_builder_get_data(&builder)[1] = 0x54; ber_tlv_builder_get_data(&builder)[2] = 0x75; ber_tlv_builder_get_data(&builder)[3] = 0x78; ber_tlv_builder_get_data(&builder)[4] = 0x20; ber_tlv_builder_get_data(&builder)[5] = 0x43; ber_tlv_builder_get_data(&builder)[6] = 0x6f; ber_tlv_builder_get_data(&builder)[7] = 0x6d; ber_tlv_builder_get_data(&builder)[8] = 0x6d; ber_tlv_builder_get_data(&builder)[9] = 0xff; ber_tlv_builder_get_data(&builder)[10] = 0xff; ber_tlv_builder_optimize(&builder, NULL, NULL); g_assert(ber_tlv_builder_init(&builder, efpnn1, sizeof(efpnn1))); g_assert(ber_tlv_builder_next(&builder, BER_TLV_DATA_TYPE_APPLICATION, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x03)); g_assert(ber_tlv_builder_set_length(&builder, 3)); ber_tlv_builder_get_data(&builder)[0] = 0x00; ber_tlv_builder_get_data(&builder)[1] = 0x4c; ber_tlv_builder_get_data(&builder)[2] = 0x6f; ber_tlv_builder_get_data(&builder)[3] = 0x6e; ber_tlv_builder_get_data(&builder)[4] = 0x67; g_assert(ber_tlv_builder_next(&builder, BER_TLV_DATA_TYPE_APPLICATION, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x05)); g_assert(ber_tlv_builder_set_length(&builder, 6)); ber_tlv_builder_get_data(&builder)[0] = 0x00; ber_tlv_builder_get_data(&builder)[1] = 0x53; ber_tlv_builder_get_data(&builder)[2] = 0x68; ber_tlv_builder_get_data(&builder)[3] = 0x6f; ber_tlv_builder_get_data(&builder)[4] = 0x72; ber_tlv_builder_get_data(&builder)[5] = 0x74; ber_tlv_builder_optimize(&builder, NULL, NULL); eons_info = sim_eons_new(1); sim_eons_add_pnn_record(eons_info, 1, efpnn0, sizeof(efpnn0)); g_assert(!sim_eons_pnn_is_empty(eons_info)); sim_eons_free(eons_info); eons_info = sim_eons_new(1); sim_eons_add_pnn_record(eons_info, 1, efpnn1, sizeof(efpnn1)); g_assert(!sim_eons_pnn_is_empty(eons_info)); sim_eons_free(eons_info); } static void test_ber_tlv_builder_3g_status(void) { unsigned char buf[512]; struct ber_tlv_builder top_builder, nested_builder; unsigned char *response; unsigned int len; int flen, rlen, str; unsigned char access[3]; unsigned short efid; /* Build a binary EF status response */ g_assert(ber_tlv_builder_init(&top_builder, buf, sizeof(buf))); g_assert(ber_tlv_builder_next(&top_builder, BER_TLV_DATA_TYPE_APPLICATION, BER_TLV_DATA_ENCODING_TYPE_CONSTRUCTED, 0x02)); g_assert(ber_tlv_builder_recurse(&top_builder, &nested_builder)); g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x02)); g_assert(ber_tlv_builder_set_length(&nested_builder, 2)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x41; ber_tlv_builder_get_data(&nested_builder)[1] = 0x21; g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x03)); g_assert(ber_tlv_builder_set_length(&nested_builder, 2)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x2f; ber_tlv_builder_get_data(&nested_builder)[1] = 0x05; g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x0a)); g_assert(ber_tlv_builder_set_length(&nested_builder, 1)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x05; g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x0b)); g_assert(ber_tlv_builder_set_length(&nested_builder, 3)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x2f; ber_tlv_builder_get_data(&nested_builder)[1] = 0x06; ber_tlv_builder_get_data(&nested_builder)[2] = 0x0f; g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x00)); g_assert(ber_tlv_builder_set_length(&nested_builder, 2)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x00; ber_tlv_builder_get_data(&nested_builder)[1] = 0x0a; g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x08)); g_assert(ber_tlv_builder_set_length(&nested_builder, 1)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x28; ber_tlv_builder_optimize(&nested_builder, NULL, NULL); ber_tlv_builder_optimize(&top_builder, &response, &len); sim_parse_3g_get_response(response, len, &flen, &rlen, &str, access, &efid); g_assert(flen == 10); g_assert(rlen == 0); g_assert(str == 0); g_assert(access[0] == 0x01); g_assert(access[1] == 0xff); g_assert(access[2] == 0x44); g_assert(efid == 0x2F05); /* Build a record-based EF status response */ g_assert(ber_tlv_builder_init(&top_builder, buf, sizeof(buf))); g_assert(ber_tlv_builder_next(&top_builder, BER_TLV_DATA_TYPE_APPLICATION, BER_TLV_DATA_ENCODING_TYPE_CONSTRUCTED, 0x02)); g_assert(ber_tlv_builder_recurse(&top_builder, &nested_builder)); g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x02)); g_assert(ber_tlv_builder_set_length(&nested_builder, 5)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x42; ber_tlv_builder_get_data(&nested_builder)[1] = 0x21; ber_tlv_builder_get_data(&nested_builder)[2] = 0x00; ber_tlv_builder_get_data(&nested_builder)[3] = 0x20; ber_tlv_builder_get_data(&nested_builder)[4] = 0x04; g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x03)); g_assert(ber_tlv_builder_set_length(&nested_builder, 2)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x6f; ber_tlv_builder_get_data(&nested_builder)[1] = 0x40; g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x0a)); g_assert(ber_tlv_builder_set_length(&nested_builder, 1)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x05; g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x0b)); g_assert(ber_tlv_builder_set_length(&nested_builder, 3)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x2f; ber_tlv_builder_get_data(&nested_builder)[1] = 0x06; ber_tlv_builder_get_data(&nested_builder)[2] = 0x07; g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x00)); g_assert(ber_tlv_builder_set_length(&nested_builder, 2)); ber_tlv_builder_get_data(&nested_builder)[0] = 0x00; ber_tlv_builder_get_data(&nested_builder)[1] = 0x80; g_assert(ber_tlv_builder_next(&nested_builder, BER_TLV_DATA_TYPE_CONTEXT_SPECIFIC, BER_TLV_DATA_ENCODING_TYPE_PRIMITIVE, 0x08)); ber_tlv_builder_optimize(&nested_builder, NULL, NULL); ber_tlv_builder_optimize(&top_builder, &response, &len); sim_parse_3g_get_response(response, len, &flen, &rlen, &str, access, &efid); g_assert(flen == 0x80); g_assert(rlen == 0x20); g_assert(str == 1); g_assert(access[0] == 0x11); g_assert(access[1] == 0xff); g_assert(access[2] == 0x44); g_assert(efid == 0x6F40); } const unsigned char valid_efopl[] = { 0x42, 0xf6, 0x1d, 0x00, 0x00, 0xff, 0xfe, 0x01, }; const unsigned char valid_efpnn[][28] = { /* Solavei */ { 0x43, 0x08, 0x87, 0xD3, 0x37, 0x3B, 0x6C, 0x2F, 0xA7, 0x01 }, /* T-Mobile / T-Mobile */ { 0x43, 0x08, 0x80, 0xD4, 0x56, 0xF3, 0x2D, 0x4E, 0xB3, 0xCB, 0x45, 0x08, 0x80, 0xD4, 0x56, 0xF3, 0x2D, 0x4E, 0xB3, 0xCB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } }; static void test_eons(void) { const struct sim_eons_operator_info *op_info; struct sim_eons *eons_info; eons_info = sim_eons_new(2); g_assert(sim_eons_pnn_is_empty(eons_info)); sim_eons_add_pnn_record(eons_info, 1, valid_efpnn[0], sizeof(valid_efpnn[0])); g_assert(!sim_eons_pnn_is_empty(eons_info)); sim_eons_add_pnn_record(eons_info, 2, valid_efpnn[1], sizeof(valid_efpnn[1])); g_assert(!sim_eons_pnn_is_empty(eons_info)); sim_eons_add_opl_record(eons_info, valid_efopl, sizeof(valid_efopl)); sim_eons_optimize(eons_info); op_info = sim_eons_lookup(eons_info, "246", "82"); g_assert(op_info == NULL); op_info = sim_eons_lookup(eons_info, "246", "81"); g_assert(op_info); g_assert(!strcmp(op_info->longname, "Solavei")); g_assert(!op_info->shortname); g_assert(!op_info->info); sim_eons_free(eons_info); } static void test_ef_db(void) { struct sim_ef_info *info; info = sim_ef_db_lookup(0x6FAD); g_assert(info); info = sim_ef_db_lookup(0x6FB1); g_assert(info == NULL); info = sim_ef_db_lookup(0x2F05); g_assert(info); } static const char *binary_ef = "62178202412183022F058A01058B032F060F8002000A" "880128"; static const char *record_ef = "62198205422100200483026F408A01058B036F0607" "800200808800"; static void test_3g_status_data(void) { unsigned char *response; long len; int flen, rlen, str; unsigned char access[3]; unsigned short efid; response = decode_hex(binary_ef, -1, &len, 0); sim_parse_3g_get_response(response, len, &flen, &rlen, &str, access, &efid); g_assert(flen == 10); g_assert(rlen == 0); g_assert(str == 0); g_assert(access[0] == 0x01); g_assert(access[1] == 0xff); g_assert(access[2] == 0x44); g_assert(efid == 0x2F05); g_free(response); response = decode_hex(record_ef, -1, &len, 0); sim_parse_3g_get_response(response, len, &flen, &rlen, &str, access, &efid); g_assert(flen == 0x80); g_assert(rlen == 0x20); g_assert(str == 1); g_assert(access[0] == 0x11); g_assert(access[1] == 0xff); g_assert(access[2] == 0x44); g_assert(efid == 0x6F40); g_free(response); } static char *at_cuad_response = "611B4F10A0000000871002FFFFFFFF8905080000" "FFFFFFFFFFFFFFFFFFFFFFFFFF611F4F0CA000000063504B43532D" "313550094D49445066696C657351043F007F80"; static void test_application_entry_decode(void) { unsigned char *ef_dir; long len; GSList *entries; struct sim_app_record *app[2]; ef_dir = decode_hex(at_cuad_response, -1, &len, 0); entries = sim_parse_app_template_entries(ef_dir, len); g_assert(g_slist_length(entries) == 2); app[0] = entries->next->data; app[1] = entries->data; g_assert(app[0]->aid_len == 0x10); g_assert(!memcmp(app[0]->aid, &ef_dir[4], 0x10)); g_assert(app[0]->label == NULL); g_assert(app[1]->aid_len == 0x0c); g_assert(!memcmp(app[1]->aid, &ef_dir[37], 0x0c)); g_assert(app[1]->label != NULL); g_assert(!strcmp(app[1]->label, "MIDPfiles")); g_free(ef_dir); } static void test_get_3g_path(void) { unsigned char path[6]; unsigned int len; unsigned char path1[] = { 0x3F, 0x00, 0x7F, 0xFF }; len = sim_ef_db_get_path_3g(SIM_EFPNN_FILEID, path); g_assert(len == 4); g_assert(!memcmp(path, path1, len)); } static void test_get_2g_path(void) { unsigned char path[6]; unsigned int len; unsigned char path1[] = { 0x3F, 0x00, 0x7F, 0x20 }; len = sim_ef_db_get_path_2g(SIM_EFPNN_FILEID, path); g_assert(len == 4); g_assert(!memcmp(path, path1, len)); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/testsimutil/ber tlv iter", test_ber_tlv_iter); g_test_add_func("/testsimutil/ber tlv encode MMS", test_ber_tlv_builder_mms); g_test_add_func("/testsimutil/ber tlv encode EFpnn", test_ber_tlv_builder_efpnn); g_test_add_func("/testsimutil/ber tlv encode 3G Status response", test_ber_tlv_builder_3g_status); g_test_add_func("/testsimutil/EONS Handling", test_eons); g_test_add_func("/testsimutil/Elementary File DB", test_ef_db); g_test_add_func("/testsimutil/3G Status response", test_3g_status_data); g_test_add_func("/testsimutil/Application entries decoding", test_application_entry_decode); g_test_add_func("/testsimutil/3G path", test_get_3g_path); g_test_add_func("/testsimutil/2G path", test_get_2g_path); return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-mnclength.c0000644000015600001650000000775512671500024022474 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include #include extern struct ofono_plugin_desc __ofono_builtin_mnclength; struct get_mnclength_test_data { const char *imsi; int mnc_length; /* Expected length */ }; /* test data */ static const struct get_mnclength_test_data get_mnclength_0 = { .imsi = "214060240111837", .mnc_length = -ENOTSUP }; static const struct get_mnclength_test_data get_mnclength_1 = { .imsi = "214060240111837", .mnc_length = 2 }; static const struct get_mnclength_test_data get_mnclength_2 = { .imsi = "313001740111837", .mnc_length = 3 }; static const struct get_mnclength_test_data get_mnclength_3 = { .imsi = "352060240111837", .mnc_length = 3 }; static const struct get_mnclength_test_data get_mnclength_4 = { .imsi = "602060240111837", .mnc_length = 2 }; static const struct get_mnclength_test_data get_mnclength_5 = { .imsi = "4051000240111837", .mnc_length = 2 }; static const struct get_mnclength_test_data get_mnclength_6 = { .imsi = "405801240111837", .mnc_length = 3 }; static const struct get_mnclength_test_data get_mnclength_7 = { .imsi = "714020240111837", .mnc_length = 3 }; static const struct get_mnclength_test_data get_mnclength_8 = { .imsi = "000000000000000", .mnc_length = -ENOENT }; static const struct get_mnclength_test_data get_mnclength_9 = { .imsi = "0xx000000000000", .mnc_length = -EINVAL }; static const struct get_mnclength_test_data get_mnclength_10 = { .imsi = "111111000000000", .mnc_length = -ENOENT }; static void test_get_mnclength(gconstpointer data) { int mnc_length; const struct get_mnclength_test_data *testdata = data; mnc_length = __ofono_sim_mnclength_get_mnclength(testdata->imsi); g_assert(mnc_length == testdata->mnc_length); } static void test_mnclength_register(void) { g_assert(__ofono_builtin_mnclength.init() == 0); } int main(int argc, char **argv) { int res; g_test_init(&argc, &argv, NULL); g_test_add_data_func("/testmnclength: Test 0", &get_mnclength_0, test_get_mnclength); g_test_add_func("/testmnclength: Test register", test_mnclength_register); g_test_add_data_func("/testmnclength: Test 1", &get_mnclength_1, test_get_mnclength); g_test_add_data_func("/testmnclength: Test 2", &get_mnclength_2, test_get_mnclength); g_test_add_data_func("/testmnclength: Test 3", &get_mnclength_3, test_get_mnclength); g_test_add_data_func("/testmnclength: Test 4", &get_mnclength_4, test_get_mnclength); g_test_add_data_func("/testmnclength: Test 5", &get_mnclength_5, test_get_mnclength); g_test_add_data_func("/testmnclength: Test 6", &get_mnclength_6, test_get_mnclength); g_test_add_data_func("/testmnclength: Test 7", &get_mnclength_7, test_get_mnclength); g_test_add_data_func("/testmnclength: Test 8", &get_mnclength_8, test_get_mnclength); g_test_add_data_func("/testmnclength: Test 9", &get_mnclength_9, test_get_mnclength); g_test_add_data_func("/testmnclength: Test 10", &get_mnclength_10, test_get_mnclength); res = g_test_run(); __ofono_builtin_mnclength.exit(); return res; } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-mtkreply.c0000644000015600001650000001536212671500024022355 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include "common.h" #include "grilreply.h" #include "drivers/mtkmodem/mtk_constants.h" #include "drivers/mtkmodem/mtkrequest.h" #include "drivers/mtkmodem/mtkreply.h" /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN struct sim_password_test { int retries; enum ofono_sim_password_type passwd_type; const struct ril_msg msg; }; struct rep_3g_capability_test { int slot_3g; const struct ril_msg msg; }; struct rep_query_modem_type_test { int type; const struct ril_msg msg; }; /* * The following hexadecimal data contains the event data of a valid * MTK-specific RIL_REQUEST_AVAILABLE_NETWORKS with the following parameters: * * {lalpha=AT&T, salpha=, numeric=310410, status=available, tech=3G} */ static const guchar mtk_reply_avail_ops_valid_parcel1[] = { 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x54, 0x00, 0x26, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x31, 0x00, 0x30, 0x00, 0x34, 0x00, 0x31, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x61, 0x00, 0x76, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x33, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg mtk_reply_avail_ops_valid_1 = { .buf = (char *) &mtk_reply_avail_ops_valid_parcel1, .buf_len = sizeof(mtk_reply_avail_ops_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, .serial_no = 0, .error = 0, }; /* * The following structure contains test data for a valid * RIL_REQUEST_ENTER_SIM_PIN reply with parameters {3,3,10,10} */ static const guchar mtk_reply_enter_sim_pin_valid_parcel1[] = { 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00 }; static const struct sim_password_test mtk_reply_enter_sim_pin_valid_1 = { .retries = 3, .passwd_type = OFONO_SIM_PASSWORD_SIM_PIN, .msg = { .buf = (gchar *) mtk_reply_enter_sim_pin_valid_parcel1, .buf_len = sizeof(mtk_reply_enter_sim_pin_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_ENTER_SIM_PIN, .serial_no = 0, .error = RIL_E_SUCCESS, } }; /* * The following hexadecimal data contains the reply to a * MTK_RIL_REQUEST_GET_3G_CAPABILITY request with parameter {1} */ static const guchar mtk_reply_3g_capability_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct rep_3g_capability_test mtk_reply_3g_capability_valid_1 = { .slot_3g = 0, .msg = { .buf = (char *) &mtk_reply_3g_capability_valid_parcel1, .buf_len = sizeof(mtk_reply_3g_capability_valid_parcel1), .unsolicited = FALSE, .req = MTK_RIL_REQUEST_GET_3G_CAPABILITY, .serial_no = 0, .error = 0, } }; /* * The following hexadecimal data contains the reply to a * MTK_RIL_REQUEST_QUERY_MODEM_TYPE request with parameter {6} */ static const guchar mtk_reply_query_modem_type_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00 }; static const struct rep_query_modem_type_test mtk_reply_query_modem_type_valid_1 = { .type = MTK_MD_TYPE_LTG, .msg = { .buf = (char *) &mtk_reply_query_modem_type_valid_parcel1, .buf_len = sizeof(mtk_reply_query_modem_type_valid_parcel1), .unsolicited = FALSE, .req = MTK_RIL_REQUEST_QUERY_MODEM_TYPE, .serial_no = 0, .error = 0, } }; static void test_mtk_reply_avail_ops_valid(gconstpointer data) { struct reply_avail_ops *reply; GRil *gril = g_ril_new(NULL, OFONO_RIL_VENDOR_MTK); reply = g_ril_reply_parse_avail_ops(gril, data); g_assert(reply != NULL); g_ril_unref(gril); } static void test_mtk_reply_enter_sim_pin_valid(gconstpointer data) { GRil *gril = g_ril_new(NULL, OFONO_RIL_VENDOR_MTK); const struct sim_password_test *test = data; int *retries = g_ril_reply_parse_retries(gril, &test->msg, test->passwd_type); g_assert(retries != NULL); g_assert(retries[test->passwd_type] == test->retries); g_free(retries); g_ril_unref(gril); } static void test_mtk_reply_3g_capability_valid(gconstpointer data) { GRil *gril = g_ril_new(NULL, OFONO_RIL_VENDOR_MTK); const struct rep_3g_capability_test *test = data; int slot_3g = g_mtk_reply_parse_get_3g_capability(gril, &test->msg); g_assert(slot_3g == test->slot_3g); g_ril_unref(gril); } static void test_mtk_reply_query_modem_type_valid(gconstpointer data) { GRil *gril = g_ril_new(NULL, OFONO_RIL_VENDOR_MTK); const struct rep_query_modem_type_test *test = data; int type = g_mtk_reply_parse_query_modem_type(gril, &test->msg); g_assert(type == test->type); g_ril_unref(gril); } #endif /* LITTLE_ENDIAN */ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN g_test_add_data_func("/testmtkreply/netreg: " "valid QUERY_AVAIL_OPS Test 1", &mtk_reply_avail_ops_valid_1, test_mtk_reply_avail_ops_valid); g_test_add_data_func("/testmtkreply/sim: " "valid ENTER_SIM_PIN Test 1", &mtk_reply_enter_sim_pin_valid_1, test_mtk_reply_enter_sim_pin_valid); g_test_add_data_func("/testmtkreply/radio-settings: " "valid GET_3G_CAPABILITY Test 1", &mtk_reply_3g_capability_valid_1, test_mtk_reply_3g_capability_valid); g_test_add_data_func("/testmtkreply/modem-fw: " "valid QUERY_MODEM_TYPE Test 1", &mtk_reply_query_modem_type_valid_1, test_mtk_reply_query_modem_type_valid); #endif /* LITTLE_ENDIAN */ return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-mtkrequest.c0000644000015600001650000004415312671500024022712 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "grilrequest.h" #include "drivers/rilmodem/vendor.h" #include "drivers/mtkmodem/mtkrequest.h" /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN struct request_test_data { gconstpointer request; const guchar *parcel_data; gsize parcel_size; }; /* MTK specific sim_read_info tests */ static const guchar req_mtk_sim_read_info_parcel_valid_1[] = { 0xc0, 0x00, 0x00, 0x00, 0xb7, 0x6f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x37, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char sim_mtk_read_info_path_valid_1[] = {0x7F, 0xFF}; static const struct req_sim_read_info req_mtk_sim_read_info_valid_1 = { .app_type = RIL_APPTYPE_UNKNOWN, .aid_str = "", .fileid = 0x6FB7, .path = sim_mtk_read_info_path_valid_1, .path_len = sizeof(sim_mtk_read_info_path_valid_1), }; static const struct request_test_data mtk_sim_read_info_valid_test_1 = { .request = &req_mtk_sim_read_info_valid_1, .parcel_data = (guchar *) &req_mtk_sim_read_info_parcel_valid_1, .parcel_size = sizeof(req_mtk_sim_read_info_parcel_valid_1), }; /* MTK specific sim_read_binary tests */ static const guchar req_mtk_sim_read_binary_parcel_valid_1[] = { 0xb0, 0x00, 0x00, 0x00, 0xe2, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char sim_mtk_read_binary_path_valid_1[] = {0x3F, 0x00}; static const struct req_sim_read_binary req_mtk_sim_read_binary_valid_1 = { .app_type = RIL_APPTYPE_UNKNOWN, .aid_str = "", .fileid = 0x2FE2, .path = sim_mtk_read_binary_path_valid_1, .path_len = sizeof(sim_mtk_read_binary_path_valid_1), .start = 0, .length = 0x0A, }; static const struct request_test_data mtk_sim_read_binary_valid_test_1 = { .request = &req_mtk_sim_read_binary_valid_1, .parcel_data = (guchar *) &req_mtk_sim_read_binary_parcel_valid_1, .parcel_size = sizeof(req_mtk_sim_read_binary_parcel_valid_1), }; /* MTK specific sim_read_record tests */ static const guchar mtk_req_sim_read_record_parcel_valid_1[] = { 0xb2, 0x00, 0x00, 0x00, 0xe2, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char mtk_sim_read_record_path_valid_1[] = {0x3F, 0x00}; static const struct req_sim_read_record mtk_req_sim_read_record_valid_1 = { .app_type = RIL_APPTYPE_UNKNOWN, .aid_str = "", .fileid = 0x2FE2, .path = mtk_sim_read_record_path_valid_1, .path_len = sizeof(mtk_sim_read_record_path_valid_1), .record = 5, .length = 0x0A, }; static const struct request_test_data mtk_sim_read_record_valid_test_1 = { .request = &mtk_req_sim_read_record_valid_1, .parcel_data = (guchar *) &mtk_req_sim_read_record_parcel_valid_1, .parcel_size = sizeof(mtk_req_sim_read_record_parcel_valid_1), }; /* MTK: dual_sim_mode_switch tests */ const int dual_sim_mode_1 = MTK_SWITCH_MODE_SIM_1_ACTIVE; static const guchar req_dual_sim_mode_switch_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct request_test_data dual_sim_mode_switch_valid_test_1 = { .request = &dual_sim_mode_1, .parcel_data = req_dual_sim_mode_switch_parcel_valid_1, .parcel_size = sizeof(req_dual_sim_mode_switch_parcel_valid_1), }; /* MTK: set_gprs_connect_type tests */ const int set_gprs_connect_always = MTK_GPRS_CONNECT_ALWAYS; static const guchar req_set_gprs_connect_type_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct request_test_data set_gprs_connect_type_valid_test_1 = { .request = &set_gprs_connect_always, .parcel_data = req_set_gprs_connect_type_parcel_valid_1, .parcel_size = sizeof(req_set_gprs_connect_type_parcel_valid_1), }; /* MTK: set_gprs_transfer_type tests */ const int set_gprs_transfer_call_prefer = MTK_GPRS_TRANSFER_CALL_PREFER; static const guchar req_set_gprs_transfer_type_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct request_test_data set_gprs_transfer_type_valid_test_1 = { .request = &set_gprs_transfer_call_prefer, .parcel_data = req_set_gprs_transfer_type_parcel_valid_1, .parcel_size = sizeof(req_set_gprs_transfer_type_parcel_valid_1), }; /* MTK: set_call_indication tests */ struct request_test_set_call_indication_data { int mode; int call_id; int seq_number; const unsigned char *parcel_data; size_t parcel_size; }; static const guchar req_set_call_indication_parcel_valid_1[] = { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct request_test_set_call_indication_data set_call_indication_valid_test_1 = { .mode = MTK_CALL_INDIC_MODE_AVAIL, .call_id = 1, .seq_number = 1, .parcel_data = req_set_call_indication_parcel_valid_1, .parcel_size = sizeof(req_set_call_indication_parcel_valid_1), }; /* MTK: set_fd_mode tests */ struct request_test_set_fd_mode_data { int mode; int param1; int param2; const unsigned char *parcel_data; size_t parcel_size; }; static const guchar req_set_fd_mode_parcel_valid_1[] = { 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const struct request_test_set_fd_mode_data set_fd_mode_valid_test_1 = { .mode = MTK_FD_MODE_SCREEN_STATUS, .param1 = MTK_FD_PAR1_SCREEN_OFF, .param2 = -1, .parcel_data = req_set_fd_mode_parcel_valid_1, .parcel_size = sizeof(req_set_fd_mode_parcel_valid_1), }; static const guchar req_set_fd_mode_parcel_valid_2[] = { 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, }; static const struct request_test_set_fd_mode_data set_fd_mode_valid_test_2 = { .mode = MTK_FD_MODE_SCREEN_STATUS, .param1 = MTK_FD_PAR1_SCREEN_ON, .param2 = -1, .parcel_data = req_set_fd_mode_parcel_valid_2, .parcel_size = sizeof(req_set_fd_mode_parcel_valid_2), }; /* MTK: set_3g_capability tests */ static const guchar req_set_3g_capability_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, }; static const struct request_test_data set_3g_capability_valid_test_1 = { .request = NULL, .parcel_data = req_set_3g_capability_valid_1, .parcel_size = sizeof(req_set_3g_capability_valid_1), }; /* MTK: store_modem_type tests */ static const guchar req_store_modem_type_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, }; static int store_modem_type_md_type_1 = MTK_MD_TYPE_LWG; static const struct request_test_data store_modem_type_valid_test_1 = { .request = &store_modem_type_md_type_1, .parcel_data = req_store_modem_type_valid_1, .parcel_size = sizeof(req_store_modem_type_valid_1), }; /* MTK: set_trm tests */ static const guchar req_set_trm_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, }; static int set_trm_argument_1 = 0x0B; static const struct request_test_data set_trm_valid_test_1 = { .request = &set_trm_argument_1, .parcel_data = req_set_trm_valid_1, .parcel_size = sizeof(req_set_trm_valid_1), }; /* MTK: resume_registration tests */ static const guchar req_resume_registration_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, }; static int resume_registration_argument_1 = 1; static const struct request_test_data resume_registration_valid_test_1 = { .request = &resume_registration_argument_1, .parcel_data = req_resume_registration_valid_1, .parcel_size = sizeof(req_resume_registration_valid_1), }; /* MTK specific set_initial_attach_apn tests */ struct request_test_set_initial_attach_apn { const char *apn; const int proto; const char *user; const char *passwd; const char *mccmnc; const guchar *parcel_data; gsize parcel_size; }; static const guchar req_set_initial_attach_apn_valid_1[] = { 0x0c, 0x00, 0x00, 0x00, 0x61, 0x00, 0x69, 0x00, 0x72, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x77, 0x00, 0x61, 0x00, 0x70, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x77, 0x00, 0x61, 0x00, 0x70, 0x00, 0x40, 0x00, 0x77, 0x00, 0x61, 0x00, 0x70, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x77, 0x00, 0x61, 0x00, 0x70, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x32, 0x00, 0x31, 0x00, 0x34, 0x00, 0x30, 0x00, 0x31, 0x00, 0x00, 0x00 }; static const struct request_test_set_initial_attach_apn set_initial_attach_apn_valid_test_1 = { .apn = "airtelwap.es", .proto = OFONO_GPRS_PROTO_IP, .user = "wap@wap", .passwd = "wap125", .mccmnc = "21401", .parcel_data = req_set_initial_attach_apn_valid_1, .parcel_size = sizeof(req_set_initial_attach_apn_valid_1), }; /* Test functions */ static void test_mtk_req_sim_read_info_valid(gconstpointer data) { const struct request_test_data *test_data = data; const struct req_sim_read_info *req = test_data->request; gboolean result; struct parcel rilp; GRil *gril = g_ril_new(NULL, OFONO_RIL_VENDOR_MTK); result = g_ril_request_sim_read_info(gril, req, &rilp); g_assert(result == TRUE); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); g_ril_unref(gril); } static void test_mtk_req_sim_read_binary_valid(gconstpointer data) { const struct request_test_data *test_data = data; const struct req_sim_read_binary *req = test_data->request; struct parcel rilp; gboolean result; GRil *gril = g_ril_new(NULL, OFONO_RIL_VENDOR_MTK); g_assert(gril != NULL); result = g_ril_request_sim_read_binary(gril, req, &rilp); g_assert(result == TRUE); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); g_ril_unref(gril); } static void test_mtk_req_sim_read_record_valid(gconstpointer data) { const struct request_test_data *test_data = data; const struct req_sim_read_record *req = test_data->request; struct parcel rilp; gboolean result; GRil *gril = g_ril_new(NULL, OFONO_RIL_VENDOR_MTK); g_assert(gril != NULL); result = g_ril_request_sim_read_record(gril, req, &rilp); g_assert(result == TRUE); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); g_ril_unref(gril); } static void test_request_dual_sim_mode_switch(gconstpointer data) { const struct request_test_data *test_data = data; int mode = *(int *) test_data->request; struct parcel rilp; g_mtk_request_dual_sim_mode_switch(NULL, mode, &rilp); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); } static void test_request_set_gprs_connect_type(gconstpointer data) { const struct request_test_data *test_data = data; int connect_type = *(int *) test_data->request; struct parcel rilp; g_mtk_request_set_gprs_connect_type(NULL, connect_type, &rilp); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); } static void test_request_set_gprs_transfer_type(gconstpointer data) { const struct request_test_data *test_data = data; int transfer_type = *(int *) test_data->request; struct parcel rilp; g_mtk_request_set_gprs_transfer_type(NULL, transfer_type, &rilp); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); } static void test_request_set_call_indication(gconstpointer data) { const struct request_test_set_call_indication_data *test_data = data; struct parcel rilp; g_mtk_request_set_call_indication(NULL, test_data->mode, test_data->call_id, test_data->seq_number, &rilp); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); } static void test_request_set_fd_mode(gconstpointer data) { const struct request_test_set_fd_mode_data *test_data = data; struct parcel rilp; g_mtk_request_set_fd_mode(NULL, test_data->mode, test_data->param1, test_data->param2, &rilp); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); } static void test_request_set_3g_capability(gconstpointer data) { const struct request_test_data *test_data = data; struct parcel rilp; g_mtk_request_set_3g_capability(NULL, &rilp); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); } static void test_request_store_modem_type(gconstpointer data) { const struct request_test_data *test_data = data; const int *md_type = test_data->request; struct parcel rilp; g_mtk_request_store_modem_type(NULL, *md_type, &rilp); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); } static void test_request_set_trm(gconstpointer data) { const struct request_test_data *test_data = data; const int *trm = test_data->request; struct parcel rilp; g_mtk_request_set_trm(NULL, *trm, &rilp); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); } static void test_request_resume_registration(gconstpointer data) { const struct request_test_data *test_data = data; const int *resume = test_data->request; struct parcel rilp; g_mtk_request_resume_registration(NULL, *resume, &rilp); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); } static void test_request_set_initial_attach_apn(gconstpointer data) { const struct request_test_set_initial_attach_apn *test_data = data; struct parcel rilp; GRil *gril = g_ril_new(NULL, OFONO_RIL_VENDOR_MTK); g_assert(gril != NULL); g_ril_request_set_initial_attach_apn(gril, test_data->apn, test_data->proto, test_data->user, test_data->passwd, test_data->mccmnc, &rilp); g_assert(test_data->parcel_size == rilp.size); g_assert(!memcmp(rilp.data, test_data->parcel_data, rilp.size)); parcel_free(&rilp); } #endif /* LITTLE_ENDIAN */ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN g_test_add_data_func("/testmtkrequest/mtk: " "valid DUAL_SIM_MODE_SWITCH Test 1", &dual_sim_mode_switch_valid_test_1, test_request_dual_sim_mode_switch); g_test_add_data_func("/testmtkrequest/gprs: " "valid SET_GPRS_CONNECT_TYPE Test 1", &set_gprs_connect_type_valid_test_1, test_request_set_gprs_connect_type); g_test_add_data_func("/testmtkrequest/gprs: " "valid SET_GPRS_TRANSFER_TYPE Test 1", &set_gprs_transfer_type_valid_test_1, test_request_set_gprs_transfer_type); g_test_add_data_func("/testmtkrequest/voicecall: " "valid SET_CALL_INDICATION Test 1", &set_call_indication_valid_test_1, test_request_set_call_indication); g_test_add_data_func("/testmtkrequest/sim: " "valid SIM_READ_INFO Test 1", &mtk_sim_read_info_valid_test_1, test_mtk_req_sim_read_info_valid); g_test_add_data_func("/testmtkrequest/sim: " "valid SIM_READ_BINARY Test 1", &mtk_sim_read_binary_valid_test_1, test_mtk_req_sim_read_binary_valid); g_test_add_data_func("/testmtkrequest/sim: " "valid SIM_READ_RECORD Test 1", &mtk_sim_read_record_valid_test_1, test_mtk_req_sim_read_record_valid); g_test_add_data_func("/testmtkrequest/radio-settings: " "valid SET_FD_MODE Test 1", &set_fd_mode_valid_test_1, test_request_set_fd_mode); g_test_add_data_func("/testmtkrequest/radio-settings: " "valid SET_FD_MODE Test 2", &set_fd_mode_valid_test_2, test_request_set_fd_mode); g_test_add_data_func("/testmtkrequest/mtk-settings: " "valid SET_3G_CAPABILITY Test 1", &set_3g_capability_valid_test_1, test_request_set_3g_capability); g_test_add_data_func("/testmtkrequest/modem-fw: " "valid STORE_MODEM_TYPE Test 1", &store_modem_type_valid_test_1, test_request_store_modem_type); g_test_add_data_func("/testmtkrequest/modem-fw: " "valid SET_TRM Test 1", &set_trm_valid_test_1, test_request_set_trm); g_test_add_data_func("/testmtkrequest/network: " "valid RESUME_REGISTRATION Test 1", &resume_registration_valid_test_1, test_request_resume_registration); g_test_add_data_func("/testmtkrequest/set-ia-apn: " "valid SET_INITIAL_ATTACH_APN Test 1", &set_initial_attach_apn_valid_test_1, test_request_set_initial_attach_apn); #endif /* LITTLE_ENDIAN */ return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-cdmasms.c0000644000015600001650000000556412671500024022140 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include "cdma-smsutil.h" static inline void check_text(const char *decoded, const char *expected) { if (expected == NULL) { g_assert(decoded == NULL); return; } g_assert(decoded != NULL); g_assert(g_str_equal(decoded, expected)); } struct wmt_deliver_test { const guint8 *tpdu; guint8 tpdu_len; const char *text; const char *oaddr; }; guint8 wmt_deliver_1[] = { 0x00, 0x00, 0x02, 0x10, 0x02, 0x02, 0x05, 0x01, 0xC4, 0x8D, 0x15, 0x9C, 0x08, 0x0D, 0x00, 0x03, 0x1B, 0xEE, 0xF0, 0x01, 0x06, 0x10, 0x2C, 0x8C, 0xBB, 0x36, 0x6F }; guint8 wmt_deliver_2[] = { 0x00, 0x00, 0x02, 0x10, 0x02, 0x02, 0x07, 0x02, 0xA1, 0x62, 0x51, 0x55, 0xA6, 0x40, 0x08, 0x18, 0x00, 0x03, 0x10, 0x00, 0x40, 0x01, 0x06, 0x10, 0x25, 0x4C, 0xBC, 0xFA, 0x00, 0x03, 0x06, 0x03, 0x08, 0x20, 0x13, 0x43, 0x12, 0x0D, 0x01, 0x01 }; static struct wmt_deliver_test wmt_deliver_data_1 = { .tpdu = wmt_deliver_1, .tpdu_len = sizeof(wmt_deliver_1), .text = "Hello", .oaddr = "1234567" }; static struct wmt_deliver_test wmt_deliver_data_2 = { .tpdu = wmt_deliver_2, .tpdu_len = sizeof(wmt_deliver_2), .text = "Test", .oaddr = "8589455699" }; static void test_wmt_deliver(gconstpointer data) { const struct wmt_deliver_test *test = data; gboolean ret; struct cdma_sms s; const char *addr; char *message; memset(&s, 0, sizeof(struct cdma_sms)); ret = cdma_sms_decode(test->tpdu, test->tpdu_len, &s); g_assert(ret == TRUE); g_assert(s.type == CDMA_SMS_TP_MSG_TYPE_P2P); g_assert(s.p2p_msg.teleservice_id == CDMA_SMS_TELESERVICE_ID_WMT); addr = cdma_sms_address_to_string(&s.p2p_msg.oaddr); check_text(addr, test->oaddr); message = cdma_sms_decode_text(&s.p2p_msg.bd.wmt_deliver.ud); check_text(message, test->text); g_free(message); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_data_func("/test-cdmasms/WMT DELIVER 1", &wmt_deliver_data_1, test_wmt_deliver); g_test_add_data_func("/test-cdmasms/WMT DELIVER 2", &wmt_deliver_data_2, test_wmt_deliver); return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-common.c0000644000015600001650000000641412671500024021774 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include "common.h" static const char *invalid_strings[] = { "33", "*", "**", "***", "*****", "******", "#", "##", "###", "####", "#####", "*#", "**#", "****#", "*****#", "**#", "*#", "##", "*04*98*0000*00000*00000#", NULL }; static void test_invalid(void) { char *sc; char *sia; char *sib; char *sic; char *sid; char *dn; int type; char *str; int i; gboolean ret; for (i = 0; invalid_strings[i]; i++) { if (g_test_verbose()) g_print("%s...\n", invalid_strings[i]); str = strdup(invalid_strings[i]); ret = parse_ss_control_string(str, &type, &sc, &sia, &sib, &sic, &sid, &dn); if (ret == TRUE && strlen(sid)) ret = FALSE; g_assert(ret == FALSE); free(str); } } static const char *valid_strings[] = { "*31#", "*31#+55555", "#31#", "#31#+55555", "*21*+55555*10*20#", "*21*+55555*10#", "*21**20#", "*21*+55555#", "*21**10*20#", "*21**10#", "*21***20#", "*21#", "**21#", "*#21#", "#21#", "##21#", NULL }; static void test_valid(void) { char *sc; char *sia; char *sib; char *sic; char *sid; char *dn; int type; gboolean ret; char *str; int i; for (i = 0; valid_strings[i]; i++) { if (g_test_verbose()) g_print("%s...", valid_strings[i]); str = strdup(valid_strings[i]); ret = parse_ss_control_string(str, &type, &sc, &sia, &sib, &sic, &sid, &dn); if (strlen(sid)) ret = FALSE; g_assert(ret == TRUE); if (g_test_verbose()) g_print("parsed as: %d, %s, %s, %s, %s, %s\n", type, sc, sia, sib, sic, dn); free(str); } } static const char *valid_apns[] = { "wap.cingular", "vodafone.co.uk", "vodafone.com", "", NULL }; static const char *invalid_apns[] = { ".", "..", "f..f", "foo.bar.#", NULL }; static void test_apn(void) { int i; gboolean res; for (i = 0; valid_apns[i]; i++) { if (g_test_verbose()) g_print("Test Valid:%s\n", valid_apns[i]); res = is_valid_apn(valid_apns[i]); g_assert(res == TRUE); } for (i = 0; invalid_apns[i]; i++) { if (g_test_verbose()) g_print("Test Invalid:%s\n", invalid_apns[i]); res = is_valid_apn(invalid_apns[i]); g_assert(res == FALSE); } } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/testutil/Invalid", test_invalid); g_test_add_func("/testutil/Valid", test_valid); g_test_add_func("/testutil/APN", test_apn); return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-idmap.c0000644000015600001650000000370512671500024021576 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include "idmap.h" static void test_alloc(void) { struct idmap *idmap; unsigned int bit; idmap = idmap_new(2); g_assert(idmap); bit = idmap_alloc(idmap); g_assert(bit == 1); bit = idmap_alloc(idmap); g_assert(bit == 2); bit = idmap_alloc(idmap); g_assert(bit == 3); idmap_put(idmap, 3); bit = idmap_alloc(idmap); g_assert(bit == 3); idmap_put(idmap, 0); bit = idmap_alloc(idmap); g_assert(bit == 3); idmap_put(idmap, 1); bit = idmap_alloc(idmap); g_assert(bit == 1); idmap_put(idmap, 1); idmap_put(idmap, 2); bit = idmap_alloc(idmap); g_assert(bit == 1); idmap_free(idmap); } static void test_alloc_next(void) { struct idmap *idmap; unsigned int bit; idmap = idmap_new(256); g_assert(idmap); bit = idmap_alloc_next(idmap, 255); g_assert(bit == 256); bit = idmap_alloc_next(idmap, 255); g_assert(bit == 1); bit = idmap_alloc_next(idmap, 1); g_assert(bit == 2); idmap_free(idmap); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/testidmap/alloc", test_alloc); g_test_add_func("/testidmap/alloc_next", test_alloc_next); return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-sms.c0000644000015600001650000014077212671500024021314 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include "util.h" #include "smsutil.h" static const char *simple_deliver = "07911326040000F0" "040B911346610089F60000208062917314480CC8F71D14969741F977FD07"; static const char *alnum_sender = "0791447758100650" "040DD0F334FC1CA6970100008080312170224008D4F29CDE0EA7D9"; static const char *unicode_deliver = "04819999990414D0FBFD7EBFDFEFF77BFE1E001" "9512090801361807E00DC00FC00C400E400D600F600C500E500D800F800C" "600E600C700E700C900E900CA00EA00DF003100320033003400350036003" "7003800390030002000540068006900730020006D0065007300730061006" "7006500200069007300200036003300200075006E00690063006F0064006" "5002000630068006100720073002E"; static const char *simple_submit = "0011000B916407281553F80000AA" "0AE8329BFD4697D9EC37"; static const char *simple_mwi = "07913366002020F8040B913366600600F100C8318070" "6174148000"; static void print_scts(struct sms_scts *scts, const char *prefix) { time_t ts; struct tm remote; char buf[128]; g_print("%s: (YY-MM-DD) %02d-%02d-%02d\n", prefix, (int)scts->year, (int)scts->month, (int)scts->day); g_print("%s: (HH-MM-SS) %02d:%02d:%02d\n", prefix, (int)scts->hour, (int)scts->minute, (int)scts->second); g_print("%s: Timezone %d hours %d minutes\n", prefix, (int)scts->timezone / 4, (int)((abs(scts->timezone) % 4) * 15)); ts = sms_scts_to_time(scts, &remote); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&ts)); buf[127] = '\0'; g_print("local time: %s\n", buf); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", &remote); buf[127] = '\0'; g_print("remote time: %s\n", buf); } static void print_vpf(enum sms_validity_period_format vpf, struct sms_validity_period *vp) { g_print("Validity Period Format: %d\n", (int)vpf); switch (vpf) { case SMS_VALIDITY_PERIOD_FORMAT_ABSENT: g_print("Validity-Period: Absent\n"); break; case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE: g_print("Validity-Period: %d\n", (int)vp->relative); break; case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE: print_scts(&vp->absolute, "Validity-Period:"); break; case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED: g_print("Validity-Period: Enhanced"); break; } } static void dump_details(struct sms *sms) { if (sms->sc_addr.address[0] == '\0') g_print("SMSC Address absent, default will be used\n"); else g_print("SMSC Address number_type: %d, number_plan: %d, %s\n", (int)sms->sc_addr.number_type, (int)sms->sc_addr.numbering_plan, sms->sc_addr.address); switch (sms->type) { case SMS_TYPE_DELIVER: g_print("Type: Deliver\n"); g_print("Originator-Address: %d, %d, %s\n", (int)sms->deliver.oaddr.number_type, (int)sms->deliver.oaddr.numbering_plan, sms->deliver.oaddr.address); g_print("PID: %d\n", (int)sms->deliver.pid); g_print("DCS: %d\n", (int)sms->deliver.dcs); print_scts(&sms->deliver.scts, "Timestamp"); break; case SMS_TYPE_SUBMIT: g_print("Type: Submit\n"); g_print("Message Reference: %u\n", (int)sms->submit.mr); g_print("Destination-Address: %d, %d, %s\n", (int)sms->submit.daddr.number_type, (int)sms->submit.daddr.numbering_plan, sms->submit.daddr.address); g_print("PID: %d\n", (int)sms->submit.pid); g_print("DCS: %d\n", (int)sms->submit.dcs); print_vpf(sms->submit.vpf, &sms->submit.vp); break; case SMS_TYPE_STATUS_REPORT: break; case SMS_TYPE_COMMAND: case SMS_TYPE_DELIVER_REPORT_ACK: case SMS_TYPE_DELIVER_REPORT_ERROR: case SMS_TYPE_SUBMIT_REPORT_ACK: case SMS_TYPE_SUBMIT_REPORT_ERROR: break; } } static void test_simple_deliver(void) { struct sms sms; unsigned char *decoded_pdu; long pdu_len; gboolean ret; int data_len; unsigned char *unpacked; char *utf8; decoded_pdu = decode_hex(simple_deliver, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == (long)strlen(simple_deliver) / 2); ret = sms_decode(decoded_pdu, pdu_len, FALSE, 30, &sms); g_free(decoded_pdu); g_assert(ret); g_assert(sms.type == SMS_TYPE_DELIVER); if (g_test_verbose()) dump_details(&sms); g_assert(sms.sc_addr.number_type == SMS_NUMBER_TYPE_INTERNATIONAL); g_assert(sms.sc_addr.numbering_plan == SMS_NUMBERING_PLAN_ISDN); g_assert(strcmp(sms.sc_addr.address, "31624000000") == 0); g_assert(sms.deliver.oaddr.number_type == SMS_NUMBER_TYPE_INTERNATIONAL); g_assert(sms.deliver.oaddr.numbering_plan == SMS_NUMBERING_PLAN_ISDN); g_assert(strcmp(sms.deliver.oaddr.address, "31641600986") == 0); g_assert(sms.deliver.pid == 0); g_assert(sms.deliver.dcs == 0); g_assert(sms.deliver.scts.year == 2); g_assert(sms.deliver.scts.month == 8); g_assert(sms.deliver.scts.day == 26); g_assert(sms.deliver.scts.hour == 19); g_assert(sms.deliver.scts.minute == 37); g_assert(sms.deliver.scts.second == 41); g_assert(sms.deliver.scts.timezone == -4); g_assert(sms.deliver.udl == 12); data_len = sms_udl_in_bytes(sms.deliver.udl, sms.deliver.dcs); g_assert(data_len == 11); unpacked = unpack_7bit(sms.deliver.ud, data_len, 0, FALSE, sms.deliver.udl, NULL, 0xff); g_assert(unpacked); utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff); g_free(unpacked); g_assert(utf8); if (g_test_verbose()) g_print("Decoded user data is: %s\n", utf8); g_assert(strcmp(utf8, "How are you?") == 0); g_free(utf8); } static void test_alnum_sender(void) { struct sms sms; unsigned char *decoded_pdu; long pdu_len; gboolean ret; int data_len; unsigned char *unpacked; char *utf8; decoded_pdu = decode_hex(alnum_sender, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == (long)strlen(alnum_sender) / 2); ret = sms_decode(decoded_pdu, pdu_len, FALSE, 27, &sms); g_free(decoded_pdu); g_assert(ret); g_assert(sms.type == SMS_TYPE_DELIVER); if (g_test_verbose()) dump_details(&sms); g_assert(sms.sc_addr.number_type == SMS_NUMBER_TYPE_INTERNATIONAL); g_assert(sms.sc_addr.numbering_plan == SMS_NUMBERING_PLAN_ISDN); g_assert(strcmp(sms.sc_addr.address, "447785016005") == 0); g_assert(sms.deliver.oaddr.number_type == SMS_NUMBER_TYPE_ALPHANUMERIC); g_assert(sms.deliver.oaddr.numbering_plan == SMS_NUMBERING_PLAN_UNKNOWN); g_assert(strcmp(sms.deliver.oaddr.address, "sipgate") == 0); g_assert(sms.deliver.pid == 0); g_assert(sms.deliver.dcs == 0); g_assert(sms.deliver.scts.year == 8); g_assert(sms.deliver.scts.month == 8); g_assert(sms.deliver.scts.day == 13); g_assert(sms.deliver.scts.hour == 12); g_assert(sms.deliver.scts.minute == 07); g_assert(sms.deliver.scts.second == 22); g_assert(sms.deliver.scts.timezone == 4); g_assert(sms.deliver.udl == 8); data_len = sms_udl_in_bytes(sms.deliver.udl, sms.deliver.dcs); g_assert(data_len == 7); unpacked = unpack_7bit(sms.deliver.ud, data_len, 0, FALSE, sms.deliver.udl, NULL, 0xff); g_assert(unpacked); utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff); g_free(unpacked); g_assert(utf8); if (g_test_verbose()) g_print("Decoded user data is: %s\n", utf8); g_assert(strcmp(utf8, "Testmail") == 0); g_free(utf8); } static void test_deliver_encode(void) { struct sms sms; unsigned char *decoded_pdu; long pdu_len; gboolean ret; unsigned char pdu[176]; int encoded_pdu_len; int encoded_tpdu_len; char *encoded_pdu; decoded_pdu = decode_hex(simple_deliver, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == (long)strlen(simple_deliver) / 2); ret = sms_decode(decoded_pdu, pdu_len, FALSE, 30, &sms); g_free(decoded_pdu); g_assert(ret); g_assert(sms.type == SMS_TYPE_DELIVER); ret = sms_encode(&sms, &encoded_pdu_len, &encoded_tpdu_len, pdu); if (g_test_verbose()) { int i; for (i = 0; i < encoded_pdu_len; i++) g_print("%02X", pdu[i]); g_print("\n"); } g_assert(ret); g_assert(encoded_tpdu_len == 30); g_assert(encoded_pdu_len == pdu_len); encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0); g_assert(strcmp(simple_deliver, encoded_pdu) == 0); g_free(encoded_pdu); decoded_pdu = decode_hex(alnum_sender, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == (long)strlen(alnum_sender) / 2); ret = sms_decode(decoded_pdu, pdu_len, FALSE, 27, &sms); g_free(decoded_pdu); g_assert(ret); g_assert(sms.type == SMS_TYPE_DELIVER); ret = sms_encode(&sms, &encoded_pdu_len, &encoded_tpdu_len, pdu); if (g_test_verbose()) { int i; for (i = 0; i < encoded_pdu_len; i++) g_print("%02X", pdu[i]); g_print("\n"); } g_assert(ret); g_assert(encoded_tpdu_len == 27); g_assert(encoded_pdu_len == pdu_len); encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0); g_assert(strcmp(alnum_sender, encoded_pdu) == 0); g_free(encoded_pdu); /* test unicode_deliver*/ decoded_pdu = decode_hex(unicode_deliver, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == (long)strlen(unicode_deliver) / 2); ret = sms_decode(decoded_pdu, pdu_len, FALSE, 149, &sms); g_free(decoded_pdu); g_assert(ret); g_assert(sms.type == SMS_TYPE_DELIVER); ret = sms_encode(&sms, &encoded_pdu_len, &encoded_tpdu_len, pdu); if (g_test_verbose()) { int i; for (i = 0; i < encoded_pdu_len; i++) g_print("%02X", pdu[i]); g_print("\n"); } g_assert(ret); g_assert(encoded_tpdu_len == 149); g_assert(encoded_pdu_len == pdu_len); encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0); g_assert(strcmp(unicode_deliver, encoded_pdu) == 0); g_free(encoded_pdu); } static void test_simple_submit(void) { struct sms sms; unsigned char *decoded_pdu; long pdu_len; gboolean ret; int data_len; unsigned char *unpacked; char *utf8; decoded_pdu = decode_hex(simple_submit, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == (long)strlen(simple_submit) / 2); ret = sms_decode(decoded_pdu, pdu_len, TRUE, 23, &sms); g_free(decoded_pdu); g_assert(ret); g_assert(sms.type == SMS_TYPE_SUBMIT); if (g_test_verbose()) dump_details(&sms); g_assert(strlen(sms.sc_addr.address) == 0); g_assert(sms.submit.mr == 0); g_assert(sms.submit.daddr.number_type == SMS_NUMBER_TYPE_INTERNATIONAL); g_assert(sms.submit.daddr.numbering_plan == SMS_NUMBERING_PLAN_ISDN); g_assert(strcmp(sms.submit.daddr.address, "46708251358") == 0); g_assert(sms.submit.pid == 0); g_assert(sms.submit.dcs == 0); g_assert(sms.submit.vpf == SMS_VALIDITY_PERIOD_FORMAT_RELATIVE); g_assert(sms.submit.vp.relative == 0xAA); g_assert(sms.submit.udl == 10); data_len = sms_udl_in_bytes(sms.submit.udl, sms.submit.dcs); g_assert(data_len == 9); unpacked = unpack_7bit(sms.submit.ud, data_len, 0, FALSE, sms.submit.udl, NULL, 0xff); g_assert(unpacked); utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff); g_free(unpacked); g_assert(utf8); if (g_test_verbose()) g_print("Decoded user data is: %s\n", utf8); g_assert(strcmp(utf8, "hellohello") == 0); g_free(utf8); } static void test_submit_encode(void) { struct sms sms; unsigned char *decoded_pdu; long pdu_len; gboolean ret; unsigned char pdu[176]; int encoded_pdu_len; int encoded_tpdu_len; char *encoded_pdu; decoded_pdu = decode_hex(simple_submit, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == (long)strlen(simple_submit) / 2); ret = sms_decode(decoded_pdu, pdu_len, TRUE, 23, &sms); g_free(decoded_pdu); g_assert(ret); g_assert(sms.type == SMS_TYPE_SUBMIT); ret = sms_encode(&sms, &encoded_pdu_len, &encoded_tpdu_len, pdu); if (g_test_verbose()) { int i; for (i = 0; i < encoded_pdu_len; i++) g_print("%02X", pdu[i]); g_print("\n"); } g_assert(ret); g_assert(encoded_tpdu_len == 23); g_assert(encoded_pdu_len == pdu_len); encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0); g_assert(strcmp(simple_submit, encoded_pdu) == 0); g_free(encoded_pdu); } static void test_simple_mwi(void) { struct sms sms; unsigned char *decoded_pdu; long pdu_len; gboolean ret; enum sms_mwi_type type; gboolean active; gboolean discard; decoded_pdu = decode_hex(simple_mwi, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == (long)strlen(simple_mwi) / 2); ret = sms_decode(decoded_pdu, pdu_len, FALSE, 19, &sms); g_free(decoded_pdu); g_assert(ret); g_assert(sms.type == SMS_TYPE_DELIVER); if (g_test_verbose()) dump_details(&sms); g_assert(sms.sc_addr.number_type == SMS_NUMBER_TYPE_INTERNATIONAL); g_assert(sms.sc_addr.numbering_plan == SMS_NUMBERING_PLAN_ISDN); g_assert(strcmp(sms.sc_addr.address, "33660002028") == 0); g_assert(sms.deliver.oaddr.number_type == SMS_NUMBER_TYPE_INTERNATIONAL); g_assert(sms.deliver.oaddr.numbering_plan == SMS_NUMBERING_PLAN_ISDN); g_assert(strcmp(sms.deliver.oaddr.address, "33660660001") == 0); g_assert(sms.deliver.pid == 0); g_assert(sms.deliver.dcs == 200); g_assert(sms.deliver.scts.year == 13); g_assert(sms.deliver.scts.month == 8); g_assert(sms.deliver.scts.day == 7); g_assert(sms.deliver.scts.hour == 16); g_assert(sms.deliver.scts.minute == 47); g_assert(sms.deliver.scts.second == 41); g_assert(sms.deliver.scts.timezone == 8); g_assert(sms.deliver.udl == 0); if (sms.deliver.udhi) { struct sms_udh_iter iter; enum sms_iei iei; ret = sms_udh_iter_init(&sms, &iter); g_assert(ret); while ((iei = sms_udh_iter_get_ie_type(&iter)) != SMS_IEI_INVALID) { switch (iei) { case SMS_IEI_ENHANCED_VOICE_MAIL_INFORMATION: { unsigned char evm_iei[140]; sms_udh_iter_get_ie_data(&iter, evm_iei); sms_udh_iter_get_ie_length(&iter); if (g_test_verbose()) g_print("Enhanced Voicemail IEI\n"); break; } case SMS_IEI_SPECIAL_MESSAGE_INDICATION: { unsigned char special_iei[4]; sms_udh_iter_get_ie_data(&iter, special_iei); sms_udh_iter_get_ie_length(&iter); if (g_test_verbose()) g_print("Special Voicemail IEI\n"); break; } default: break; } sms_udh_iter_next(&iter); } } ret = sms_mwi_dcs_decode(sms.deliver.dcs, &type, NULL, &active, &discard); g_assert(ret); if (g_test_verbose()) { g_print("Type: %d, Active: %d, Discard: %d\n", type, active, discard); } } struct sms_charset_data { char *pdu; int data_len; enum gsm_dialect locking_lang; enum gsm_dialect single_lang; char expected_text[]; }; static struct sms_charset_data sms_charset_default = { .pdu = "0001000B91" "5310101010" "1000008080" "8060402818" "0E888462C1" "68381E9088" "6442A9582E" "988C06C4E9" "783EA09068" "442A994EA8" "946AC56AB9" "5EB0986C46" "ABD96EB89C" "6EC7EBF97E" "C0A070482C" "1A8FC8A472" "C96C3A9FD0" "A8744AAD5A" "AFD8AC76CB" "ED7ABFE0B0" "784C2E9BCF" "E8B47ACD6E" "BBDFF0B87C" "4EAFDBEFF8" "BC7ECFEFFB" "FF", .data_len = 112, .expected_text = { 0x40, 0xc2, 0xa3, 0x24, 0xc2, 0xa5, 0xc3, 0xa8, 0xc3, 0xa9, 0xc3, 0xb9, 0xc3, 0xac, 0xc3, 0xb2, 0xc3, 0x87, 0x0a, 0xc3, 0x98, 0xc3, 0xb8, 0x0d, 0xc3, 0x85, 0xc3, 0xa5, 0xce, 0x94, 0x5f, 0xce, 0xa6, 0xce, 0x93, 0xce, 0x9b, 0xce, 0xa9, 0xce, 0xa0, 0xce, 0xa8, 0xce, 0xa3, 0xce, 0x98, 0xce, 0x9e, 0x20, 0xc3, 0x86, 0xc3, 0xa6, 0xc3, 0x9f, 0xc3, 0x89, 0x20, 0x21, 0x22, 0x23, 0xc2, 0xa4, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0xc2, 0xa1, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xc3, 0x84, 0xc3, 0x96, 0xc3, 0x91, 0xc3, 0x9c, 0xc2, 0xa7, 0xc2, 0xbf, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xc3, 0xa4, 0xc3, 0xb6, 0xc3, 0xb1, 0xc3, 0xbc, 0xc3, 0xa0, 0x00 } }; static struct sms_charset_data sms_charset_default_ext = { .pdu = "0001000B91" "5310101010" "100000151B" "C58602DAA0" "36A9CD6BC3" "DBF436BE0D" "705306", .data_len = 19, .expected_text = { 0x0c, 0x5e, 0x20, 0x7b, 0x7d, 0x5c, 0x5b, 0x7e, 0x5d, 0x7c, 0xe2, 0x82, 0xac, 0x00 } }; static struct sms_charset_data sms_charset_turkey = { .pdu = "0001000B91" "5310101010" "1000008080" "8060402818" "0E888462C1" "68381E9088" "6442A9582E" "988C06C4E9" "783EA09068" "442A994EA8" "946AC56AB9" "5EB0986C46" "ABD96EB89C" "6EC7EBF97E" "C0A070482C" "1A8FC8A472" "C96C3A9FD0" "A8744AAD5A" "AFD8AC76CB" "ED7ABFE0B0" "784C2E9BCF" "E8B47ACD6E" "BBDFF0B87C" "4EAFDBEFF8" "BC7ECFEFFB" "FF", .data_len = 112, .locking_lang = GSM_DIALECT_TURKISH, .expected_text = { 0x40, 0xc2, 0xa3, 0x24, 0xc2, 0xa5, 0xe2, 0x82, 0xac, 0xc3, 0xa9, 0xc3, 0xb9, 0xc4, 0xb1, 0xc3, 0xb2, 0xc3, 0x87, 0x0a, 0xc4, 0x9e, 0xc4, 0x9f, 0x0d, 0xc3, 0x85, 0xc3, 0xa5, 0xce, 0x94, 0x5f, 0xce, 0xa6, 0xce, 0x93, 0xce, 0x9b, 0xce, 0xa9, 0xce, 0xa0, 0xce, 0xa8, 0xce, 0xa3, 0xce, 0x98, 0xce, 0x9e, 0x20, 0xc5, 0x9e, 0xc5, 0x9f, 0xc3, 0x9f, 0xc3, 0x89, 0x20, 0x21, 0x22, 0x23, 0xc2, 0xa4, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0xc4, 0xb0, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xc3, 0x84, 0xc3, 0x96, 0xc3, 0x91, 0xc3, 0x9c, 0xc2, 0xa7, 0xc3, 0xa7, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xc3, 0xa4, 0xc3, 0xb6, 0xc3, 0xb1, 0xc3, 0xbc, 0xc3, 0xa0, 0x00 } }; static struct sms_charset_data sms_charset_turkey_ext = { .pdu = "0001000B91" "5310101010" "1000001A1B" "C586B2416D" "529BD786B7" "E96D7C1BE0" "02C8011318" "870E", .data_len = 23, .locking_lang = GSM_DIALECT_TURKISH, .single_lang = GSM_DIALECT_TURKISH, .expected_text = { 0x0c, 0x5e, 0x7b, 0x7d, 0x5c, 0x5b, 0x7e, 0x5d, 0x7c, 0xc4, 0x9e, 0xc4, 0xb0, 0xc5, 0x9e, 0xc3, 0xa7, 0xe2, 0x82, 0xac, 0xc4, 0x9f, 0xc4, 0xb1, 0xc5, 0x9f, 0x00 } }; static struct sms_charset_data sms_charset_portugal = { .pdu = "0001000B91" "5310101010" "1000008080" "8060402818" "0E888462C1" "68381E9088" "6442A9582E" "988C06C4E9" "783EA09068" "442A994EA8" "946AC56AB9" "5EB0986C46" "ABD96EB89C" "6EC7EBF97E" "C0A070482C" "1A8FC8A472" "C96C3A9FD0" "A8744AAD5A" "AFD8AC76CB" "ED7ABFE0B0" "784C2E9BCF" "E8B47ACD6E" "BBDFF0B87C" "4EAFDBEFF8" "BC7ECFEFFB" "FF", .data_len = 112, .locking_lang = GSM_DIALECT_PORTUGUESE, .expected_text = { 0x40, 0xc2, 0xa3, 0x24, 0xc2, 0xa5, 0xc3, 0xaa, 0xc3, 0xa9, 0xc3, 0xba, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xa7, 0x0a, 0xc3, 0x94, 0xc3, 0xb4, 0x0d, 0xc3, 0x81, 0xc3, 0xa1, 0xce, 0x94, 0x5f, 0xc2, 0xaa, 0xc3, 0x87, 0xc3, 0x80, 0xe2, 0x88, 0x9e, 0x5e, 0x5c, 0xe2, 0x82, 0xac, 0xc3, 0x93, 0x7c, 0x20, 0xc3, 0x82, 0xc3, 0xa2, 0xc3, 0x8a, 0xc3, 0x89, 0x20, 0x21, 0x22, 0x23, 0xc2, 0xba, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0xc3, 0x8d, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xc3, 0x83, 0xc3, 0x95, 0xc3, 0x9a, 0xc3, 0x9c, 0xc2, 0xa7, 0x7e, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xc3, 0xa3, 0xc3, 0xb5, 0x60, 0xc3, 0xbc, 0xc3, 0xa0, 0x00 } }; static struct sms_charset_data sms_charset_portugal_ext = { .pdu = "0001000B91" "5310101010" "1000003184" "C446B16038" "1E1BC96662" "D9543696CD" "6583D9643C" "1BD42675D9" "F0C01B9F86" "02CC74B75C" "0EE68030EC" "F91D", .data_len = 43, .locking_lang = GSM_DIALECT_PORTUGUESE, .single_lang = GSM_DIALECT_PORTUGUESE, .expected_text = { 0xc3, 0xaa, 0xc3, 0xa7, 0x0c, 0xc3, 0x94, 0xc3, 0xb4, 0xc3, 0x81, 0xc3, 0xa1, 0xce, 0xa6, 0xce, 0x93, 0x5e, 0xce, 0xa9, 0xce, 0xa0, 0xce, 0xa8, 0xce, 0xa3, 0xce, 0x98, 0xc3, 0x8a, 0x7b, 0x7d, 0x5c, 0x5b, 0x7e, 0x5d, 0x7c, 0xc3, 0x80, 0xc3, 0x8d, 0xc3, 0x93, 0xc3, 0x9a, 0xc3, 0x83, 0xc3, 0x95, 0xc3, 0x82, 0xe2, 0x82, 0xac, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba, 0xc3, 0xa3, 0xc3, 0xb5, 0xc3, 0xa2, 0x00 } }; static struct sms_charset_data sms_charset_spain = { .pdu = "0001000B91" "5310101010" "100000269B" "C446B1A16C" "509BD4E6B5" "E16D7A1BDF" "06B8096E92" "9BE7A6BA09" "6FCA9BF4E6" "BDA903", .data_len = 34, .locking_lang = GSM_DIALECT_SPANISH, .single_lang = GSM_DIALECT_SPANISH, .expected_text = { 0xc3, 0xa7, 0x0c, 0x5e, 0x7b, 0x7d, 0x5c, 0x5b, 0x7e, 0x5d, 0x7c, 0xc3, 0x81, 0xc3, 0x8d, 0xc3, 0x93, 0xc3, 0x9a, 0xc3, 0xa1, 0xe2, 0x82, 0xac, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba, 0x00 } }; static void test_sms_charset(gconstpointer param) { gboolean ret; struct sms sms; unsigned char *pdu; unsigned char *unpacked; long pdu_len; int data_len; enum sms_charset sms_charset; gboolean sms_compressed; char *text; struct sms_charset_data *data = (struct sms_charset_data *)param; pdu = decode_hex(data->pdu, -1, &pdu_len, 0); g_assert(pdu); g_assert(pdu_len == (gint64)strlen(data->pdu) / 2); ret = sms_decode(pdu, pdu_len, FALSE, pdu_len, &sms); g_assert(ret); g_free(pdu); g_assert(sms.type == SMS_TYPE_DELIVER); ret = sms_dcs_decode(sms.deliver.dcs, NULL, &sms_charset, &sms_compressed, NULL); g_assert(ret); g_assert(sms_charset == SMS_CHARSET_7BIT); g_assert(sms_compressed == FALSE); data_len = sms_udl_in_bytes(sms.deliver.udl, sms.deliver.dcs); g_assert(data_len == data->data_len); unpacked = unpack_7bit(sms.deliver.ud, data_len, 0, FALSE, sms.deliver.udl, NULL, 0xff); g_assert(unpacked); text = convert_gsm_to_utf8_with_lang(unpacked, -1, NULL, NULL, 0xff, data->locking_lang, data->single_lang); g_assert(text); g_free(unpacked); g_assert(strcmp(data->expected_text, text) == 0); g_free(text); } struct text_format_header { unsigned char len; unsigned char start; unsigned char span; unsigned char format; unsigned char color; }; struct ems_udh_test { const char *pdu; unsigned int len; const char *expected; unsigned int udl; unsigned int udhl; unsigned int data_len; struct text_format_header formats[]; }; static struct ems_udh_test ems_udh_test_1 = { .pdu = "0041000B915121551532F40000631A0A031906200A032104100A03270504" "0A032E05080A043807002B8ACD29A85D9ECFC3E7F21C340EBB41E3B79B1" "E4EBB41697A989D1EB340E2379BCC02B1C3F27399059AB7C36C3628EC26" "83C66FF65B5E2683E8653C1D", .len = 100, .expected = "EMS messages can contain italic, bold, large, small and" " colored text", .formats = { { .len = 3, .start = 0x19, .span = 0x06, .format = 0x20, }, { .len = 3, .start = 0x21, .span = 0x04, .format = 0x10, }, { .len = 3, .start = 0x27, .span = 0x05, .format = 0x04, }, { .len = 3, .start = 0x2E, .span = 0x05, .format = 0x08, }, { .len = 4, .start = 0x38, .span = 0x07, .format = 0x00, .color = 0x2B, }, { .len = 0, } }, .udl = 99, .udhl = 26, .data_len = 87, }; static struct ems_udh_test ems_udh_test_2 = { .pdu = "079194712272303351030B915121340195F60000FF80230A030F07230A031" "806130A031E0A430A032E0D830A033D14020A035104F60A0355010600159" "D9E83D2735018442FCFE98A243DCC4E97C92C90F8CD26B3407537B92C67A" "7DD65320B1476934173BA3CBD2ED3D1F277FD8C76299CEF3B280C92A7CF6" "83A28CC4E9FDD6532E8FE96935D", .len = 126, .expected = "This is a test\nItalied, bold, underlined, and " "strikethrough.\nNow a right aligned word.", .formats = { { .len = 3, .start = 0x0f, .span = 0x07, .format = 0x23, }, { .len = 3, .start = 0x18, .span = 0x06, .format = 0x13, }, { .len = 3, .start = 0x1e, .span = 0x0a, .format = 0x43, }, { .len = 3, .start = 0x2e, .span = 0x0d, .format = 0x83, }, { .len = 3, .start = 0x3d, .span = 0x14, .format = 0x02, }, { .len = 3, .start = 0x51, .span = 0x04, .format = 0xf6, }, { .len = 3, .start = 0x55, .span = 0x01, .format = 0x06, }, }, .udl = 128, .udhl = 35, .data_len = 112, }; static void test_ems_udh(gconstpointer data) { const struct ems_udh_test *test = data; struct sms sms; unsigned char *decoded_pdu; long pdu_len; gboolean ret; unsigned int data_len; unsigned int udhl; struct sms_udh_iter iter; int max_chars; unsigned char *unpacked; char *utf8; int i; decoded_pdu = decode_hex(test->pdu, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == (long)strlen(test->pdu) / 2); ret = sms_decode(decoded_pdu, pdu_len, TRUE, test->len, &sms); g_free(decoded_pdu); g_assert(ret); g_assert(sms.type == SMS_TYPE_SUBMIT); if (g_test_verbose()) dump_details(&sms); udhl = sms.submit.ud[0]; g_assert(sms.submit.udl == test->udl); g_assert(udhl == test->udhl); ret = sms_udh_iter_init(&sms, &iter); g_assert(ret); for (i = 0; test->formats[i].len; i++) { if (g_test_verbose()) { int j; unsigned char ie_data[4]; sms_udh_iter_get_ie_data(&iter, ie_data); g_print("Header:\n"); for (j = 0; j < sms_udh_iter_get_ie_length(&iter); j++) g_print("0x%02x ", ie_data[j]); g_print("\n"); } g_assert(sms_udh_iter_get_ie_type(&iter) == SMS_IEI_TEXT_FORMAT); g_assert(sms_udh_iter_get_ie_length(&iter) == test->formats[i].len); if (test->formats[i+1].len) { g_assert(sms_udh_iter_has_next(&iter) == TRUE); g_assert(sms_udh_iter_next(&iter) == TRUE); } else { g_assert(sms_udh_iter_has_next(&iter) == FALSE); g_assert(sms_udh_iter_next(&iter) == FALSE); g_assert(sms_udh_iter_get_ie_type(&iter) == SMS_IEI_INVALID); } } data_len = sms_udl_in_bytes(sms.submit.udl, sms.submit.dcs); g_assert(data_len == test->data_len); max_chars = (data_len - (udhl + 1)) * 8 / 7; unpacked = unpack_7bit(sms.submit.ud + udhl + 1, data_len - (udhl + 1), udhl + 1, FALSE, max_chars, NULL, 0xff); g_assert(unpacked); utf8 = convert_gsm_to_utf8(unpacked, -1, NULL, NULL, 0xff); g_free(unpacked); g_assert(utf8); if (g_test_verbose()) g_print("Decoded user data is: %s\n", utf8); g_assert(strcmp(utf8, test->expected) == 0); g_free(utf8); } static const char *assembly_pdu1 = "038121F340048155550119906041001222048C0500" "031E0301041804420430043A002C002004100" "43B0435043A04410430043D04340440002000" "200441043B044304480430043B00200437043" "000200434043204350440044C044E00200020" "04380020002004320441043500200431043E0" "43B044C044804350020043F04400435043804" "41043F043E043B043D044F043B0441044F002" "000200433043D0435"; static int assembly_pdu_len1 = 155; static const char *assembly_pdu2 = "038121F340048155550119906041001222048C0500" "031E03020432043E043C002E000A041D04300" "43A043E043D04350446002C0020043D043500" "200432002004410438043B043004450020043" "40430043B043504350020044204350440043F" "04350442044C002C0020043E043D002004410" "44204400435043C043804420435043B044C04" "3D043E002004320431043504360430043B002" "004320020043A043E"; static int assembly_pdu_len2 = 155; static const char *assembly_pdu3 = "038121F340048155550119906041001222044A0500" "031E0303043C043D043004420443002C00200" "43F043E043704300431044B0432000A043404" "3004360435002C002004470442043E0020002" "00431044B043B0020043D04300433002E"; static int assembly_pdu_len3 = 89; static void test_assembly(void) { unsigned char pdu[176]; long pdu_len; struct sms sms; struct sms_assembly *assembly = sms_assembly_new(NULL); guint16 ref; guint8 max; guint8 seq; GSList *l; char *utf8; char *reencoded; decode_hex_own_buf(assembly_pdu1, -1, &pdu_len, 0, pdu); sms_decode(pdu, pdu_len, FALSE, assembly_pdu_len1, &sms); sms_extract_concatenation(&sms, &ref, &max, &seq); l = sms_assembly_add_fragment(assembly, &sms, time(NULL), &sms.deliver.oaddr, ref, max, seq); if (g_test_verbose()) { g_print("Ref: %u\n", ref); g_print("Max: %u\n", max); g_print("From: %s\n", sms_address_to_string(&sms.deliver.oaddr)); } g_assert(g_slist_length(assembly->assembly_list) == 1); g_assert(l == NULL); sms_assembly_expire(assembly, time(NULL) + 40); g_assert(g_slist_length(assembly->assembly_list) == 0); sms_extract_concatenation(&sms, &ref, &max, &seq); l = sms_assembly_add_fragment(assembly, &sms, time(NULL), &sms.deliver.oaddr, ref, max, seq); g_assert(g_slist_length(assembly->assembly_list) == 1); g_assert(l == NULL); decode_hex_own_buf(assembly_pdu2, -1, &pdu_len, 0, pdu); sms_decode(pdu, pdu_len, FALSE, assembly_pdu_len2, &sms); sms_extract_concatenation(&sms, &ref, &max, &seq); l = sms_assembly_add_fragment(assembly, &sms, time(NULL), &sms.deliver.oaddr, ref, max, seq); g_assert(l == NULL); decode_hex_own_buf(assembly_pdu3, -1, &pdu_len, 0, pdu); sms_decode(pdu, pdu_len, FALSE, assembly_pdu_len3, &sms); sms_extract_concatenation(&sms, &ref, &max, &seq); l = sms_assembly_add_fragment(assembly, &sms, time(NULL), &sms.deliver.oaddr, ref, max, seq); g_assert(l != NULL); utf8 = sms_decode_text(l); g_slist_foreach(l, (GFunc)g_free, NULL); g_slist_free(l); sms_assembly_free(assembly); if (g_test_verbose()) g_printf("Text:\n%s\n", utf8); l = sms_text_prepare("555", utf8, ref, TRUE, FALSE); g_assert(l); g_assert(g_slist_length(l) == 3); reencoded = sms_decode_text(l); if (g_test_verbose()) g_printf("ReEncoded:\n%s\n", reencoded); g_assert(strcmp(utf8, reencoded) == 0); g_free(utf8); g_free(reencoded); } static const char *test_no_fragmentation_7bit = "This is testing !"; static const char *expected_no_fragmentation_7bit = "079153485002020911000C915" "348870420140000A71154747A0E4ACF41F4F29C9E769F4121"; static const char *sc_addr = "+358405202090"; static const char *da_addr = "+358478400241"; static void test_prepare_7bit(void) { GSList *r; struct sms *sms; gboolean ret; unsigned char pdu[176]; int encoded_pdu_len; int encoded_tpdu_len; char *encoded_pdu; r = sms_text_prepare("555", test_no_fragmentation_7bit, 0, FALSE, FALSE); g_assert(r != NULL); sms = r->data; sms->sc_addr.number_type = SMS_NUMBER_TYPE_INTERNATIONAL; sms->sc_addr.numbering_plan = SMS_NUMBERING_PLAN_ISDN; strcpy(sms->sc_addr.address, sc_addr+1); if (g_test_verbose()) g_print("sc_addr: %s\n", sms_address_to_string(&sms->sc_addr)); g_assert(!strcmp(sc_addr, sms_address_to_string(&sms->sc_addr))); sms->submit.daddr.number_type = SMS_NUMBER_TYPE_INTERNATIONAL; sms->submit.daddr.numbering_plan = SMS_NUMBERING_PLAN_ISDN; strcpy(sms->submit.daddr.address, da_addr+1); if (g_test_verbose()) g_print("da_addr: %s\n", sms_address_to_string(&sms->submit.daddr)); g_assert(!strcmp(da_addr, sms_address_to_string(&sms->submit.daddr))); ret = sms_encode(sms, &encoded_pdu_len, &encoded_tpdu_len, pdu); g_assert(ret); if (g_test_verbose()) { int i; for (i = 0; i < encoded_pdu_len; i++) g_print("%02X", pdu[i]); g_print("\n"); } encoded_pdu = encode_hex(pdu, encoded_pdu_len, 0); g_assert(strcmp(expected_no_fragmentation_7bit, encoded_pdu) == 0); g_free(encoded_pdu); g_slist_foreach(r, (GFunc)g_free, NULL); g_slist_free(r); } struct sms_concat_data { const char *str; unsigned int segments; }; static struct sms_concat_data shakespeare_test = { .str = "Shakespeare divided his time between London and Str" "atford during his career. In 1596, the year before he bought New Plac" "e as his family home in Stratford, Shakespeare was living in the pari" "sh of St. Helen's, Bishopsgate, north of the River Thames.", .segments = 2, }; static struct sms_concat_data surr_pair_split_test = { .str = "😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱", .segments = 2, }; /* The string in this test should be padded at the end. This confuses some * decoders which do not use udl properly */ static void test_prepare_concat(gconstpointer data) { const struct sms_concat_data *test = data; GSList *r; GSList *l; char *decoded_str; GSList *pdus = NULL; unsigned char pdu[176]; struct sms *sms; struct sms decoded; int pdu_len, tpdu_len; struct sms_assembly *assembly = sms_assembly_new(NULL); guint16 ref; guint8 max; guint8 seq; if (g_test_verbose()) g_print("strlen: %zd\n", strlen(test->str)); r = sms_text_prepare("+15554449999", test->str, 0, TRUE, FALSE); g_assert(r); g_assert(g_slist_length(r) == test->segments); for (l = r; l; l = l->next) { char *strpdu; sms = l->data; sms_encode(sms, &pdu_len, &tpdu_len, pdu); g_assert(pdu_len == (tpdu_len + 1)); strpdu = encode_hex(pdu, pdu_len, 0); if (g_test_verbose()) g_printf("PDU: %s, len: %d, tlen: %d\n", strpdu, pdu_len, tpdu_len); pdus = g_slist_append(pdus, strpdu); } g_slist_foreach(r, (GFunc)g_free, NULL); g_slist_free(r); for (l = pdus; l; l = l->next) { long len; gboolean ok; decode_hex_own_buf((char *)l->data, -1, &len, 0, pdu); if (g_test_verbose()) g_print("PDU Len: %ld\n", len); ok = sms_decode(pdu, len, TRUE, len - 1, &decoded); g_assert(ok); if (g_test_verbose()) g_print("Pdu udl: %d\n", (int)decoded.submit.udl); sms_extract_concatenation(&decoded, &ref, &max, &seq); r = sms_assembly_add_fragment(assembly, &decoded, time(NULL), &decoded.submit.daddr, ref, max, seq); } g_assert(r); decoded_str = sms_decode_text(r); if (g_test_verbose()) g_printf("Decoded String: %s\n", decoded_str); g_assert(decoded_str); g_assert(strcmp(decoded_str, test->str) == 0); g_free(decoded_str); sms_assembly_free(assembly); } static void test_limit(gunichar uni, int target_size, gboolean use_16bit) { char *utf8; char *decoded; GSList *l; unsigned int i; char utf8_char[6]; unsigned int stride; stride = g_unichar_to_utf8(uni, utf8_char); utf8 = g_new0(char, (target_size + 2) * stride); for (i = 0; i < target_size * stride; i += stride) memcpy(utf8 + i, utf8_char, stride); utf8[i] = '\0'; l = sms_text_prepare("555", utf8, 0, use_16bit, FALSE); g_assert(l); g_assert(g_slist_length(l) == 255); decoded = sms_decode_text(l); g_assert(g_utf8_strlen(decoded, -1) == target_size); g_free(decoded); memcpy(utf8 + i, utf8_char, stride); utf8[i+stride] = '\0'; l = sms_text_prepare("555", utf8, 0, use_16bit, FALSE); g_assert(l == NULL); g_free(utf8); } static void test_prepare_limits(void) { gunichar ascii = 0x41; gunichar ucs2 = 0x416; unsigned int target_size; /* The limit for 16 bit headers is 255 * 152 for GSM7 */ target_size = 255 * 152; test_limit(ascii, target_size, TRUE); /* The limit for 8 bit headers is 255 * 153 for GSM7 */ target_size = 255 * 153; test_limit(ascii, target_size, FALSE); /* The limit for 16 bit headers is 255 * 66 for UCS2 */ target_size = 255 * 66; test_limit(ucs2, target_size, TRUE); /* The limit for 8 bit headers is 255 * 67 for UCS2 */ target_size = 255 * 67; test_limit(ucs2, target_size, FALSE); } static const char *cbs1 = "011000320111C2327BFC76BBCBEE46A3D168341A8D46A3D1683" "41A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D168" "341A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D100"; static const char *cbs2 = "0110003201114679785E96371A8D46A3D168341A8D46A3D1683" "41A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D168" "341A8D46A3D168341A8D46A3D168341A8D46A3D168341A8D46A3D100"; static const char *cbs3 = "001000000111E280604028180E888462C168381E90886442A95" "82E988C66C3E9783EA09068442A994EA8946AC56AB95EB0986C46ABD96EB89C6EC7EBF" "97EC0A070482C1A8FC8A472C96C3A9FD0A8744AAD5AAFD8AC76CB05"; static void test_cbs_encode_decode(void) { unsigned char *decoded_pdu; long pdu_len; gboolean ret; struct cbs cbs; unsigned char pdu[88]; int len; char *encoded_pdu; GSList *l; char iso639_lang[3]; char *utf8; decoded_pdu = decode_hex(cbs1, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == (long)strlen(cbs1) / 2); g_assert(pdu_len == 88); ret = cbs_decode(decoded_pdu, pdu_len, &cbs); g_free(decoded_pdu); g_assert(ret); g_assert(cbs.gs == CBS_GEO_SCOPE_CELL_IMMEDIATE); g_assert(cbs.message_code == 17); g_assert(cbs.update_number == 0); g_assert(cbs.message_identifier == 50); g_assert(cbs.dcs == 1); g_assert(cbs.max_pages == 1); g_assert(cbs.page == 1); l = g_slist_append(NULL, &cbs); utf8 = cbs_decode_text(l, iso639_lang); g_assert(utf8); if (g_test_verbose()) { g_printf("%s\n", utf8); if (iso639_lang[0] == '\0') g_printf("Lang: Unspecified\n"); else g_printf("Lang: %s\n", iso639_lang); } g_assert(strcmp(utf8, "Belconnen") == 0); g_assert(strcmp(iso639_lang, "en") == 0); g_free(utf8); g_slist_free(l); ret = cbs_encode(&cbs, &len, pdu); g_assert(ret); encoded_pdu = encode_hex(pdu, len, 0); g_assert(strcmp(cbs1, encoded_pdu) == 0); g_free(encoded_pdu); } static void test_cbs_assembly(void) { unsigned char *decoded_pdu; long pdu_len; struct cbs dec1; struct cbs dec2; struct cbs_assembly *assembly; char iso639_lang[3]; GSList *l; char *utf8; assembly = cbs_assembly_new(); g_assert(assembly); decoded_pdu = decode_hex(cbs1, -1, &pdu_len, 0); cbs_decode(decoded_pdu, pdu_len, &dec1); g_free(decoded_pdu); decoded_pdu = decode_hex(cbs2, -1, &pdu_len, 0); cbs_decode(decoded_pdu, pdu_len, &dec2); g_free(decoded_pdu); /* Add an initial page to the assembly */ l = cbs_assembly_add_page(assembly, &dec1); g_assert(l); g_assert(g_slist_length(assembly->recv_cell) == 1); g_slist_foreach(l, (GFunc)g_free, NULL); g_slist_free(l); /* Can we receive new updates ? */ dec1.update_number = 8; l = cbs_assembly_add_page(assembly, &dec1); g_assert(l); g_assert(g_slist_length(assembly->recv_cell) == 1); g_slist_foreach(l, (GFunc)g_free, NULL); g_slist_free(l); /* Do we ignore old pages ? */ l = cbs_assembly_add_page(assembly, &dec1); g_assert(l == NULL); /* Do we ignore older pages ? */ dec1.update_number = 5; l = cbs_assembly_add_page(assembly, &dec1); g_assert(l == NULL); cbs_assembly_location_changed(assembly, TRUE, TRUE, TRUE); g_assert(assembly->recv_cell == NULL); dec1.update_number = 9; dec1.page = 3; dec1.max_pages = 3; dec2.update_number = 9; dec2.page = 2; dec2.max_pages = 3; l = cbs_assembly_add_page(assembly, &dec2); g_assert(l == NULL); l = cbs_assembly_add_page(assembly, &dec1); g_assert(l == NULL); dec1.page = 1; l = cbs_assembly_add_page(assembly, &dec1); g_assert(l); utf8 = cbs_decode_text(l, iso639_lang); g_assert(utf8); if (g_test_verbose()) { g_printf("%s\n", utf8); if (iso639_lang[0] == '\0') g_printf("Lang: Unspecified\n"); else g_printf("Lang: %s\n", iso639_lang); } g_assert(strcmp(utf8, "BelconnenFraserBelconnen") == 0); g_free(utf8); g_slist_foreach(l, (GFunc)g_free, NULL); g_slist_free(l); cbs_assembly_free(assembly); } static void test_cbs_padding_character(void) { unsigned char *decoded_pdu; long pdu_len; gboolean ret; struct cbs cbs; GSList *l; char iso639_lang[3]; char *utf8; decoded_pdu = decode_hex(cbs3, -1, &pdu_len, 0); g_assert(decoded_pdu); g_assert(pdu_len == 88); ret = cbs_decode(decoded_pdu, pdu_len, &cbs); g_free(decoded_pdu); g_assert(ret); g_assert(cbs.gs == CBS_GEO_SCOPE_CELL_IMMEDIATE); g_assert(cbs.message_code == 1); g_assert(cbs.update_number == 0); g_assert(cbs.message_identifier == 0); g_assert(cbs.dcs == 1); g_assert(cbs.max_pages == 1); g_assert(cbs.page == 1); l = g_slist_append(NULL, &cbs); utf8 = cbs_decode_text(l, iso639_lang); g_assert(utf8); if (g_test_verbose()) { g_printf("%s\n", utf8); if (iso639_lang[0] == '\0') g_printf("Lang: Unspecified\n"); else g_printf("Lang: %s\n", iso639_lang); } g_assert(strcmp(utf8, "b£$¥èéùìòÇ\x0AØø\x0DÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤" "\x25&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLM" "NOPQRSTUVWXYZÄÖ") == 0); g_assert(strcmp(iso639_lang, "en") == 0); g_free(utf8); g_slist_free(l); } static const char *ranges[] = { "1-5, 2, 3, 600, 569-900, 999", "0-20, 33, 44, 50-60, 20-50, 1-5, 5, 3, 5", NULL }; static const char *inv_ranges[] = { "1-5, 3333", "1-5, afbcd", "1-5, 3-5,,", "1-5, 3-5, c", NULL }; static void test_range_minimizer(void) { int i = 0; while (inv_ranges[i]) { GSList *l = cbs_extract_topic_ranges(inv_ranges[i]); g_assert(l == NULL); i++; } i = 0; while (ranges[i]) { GSList *r = cbs_extract_topic_ranges(ranges[i]); char *rangestr; g_assert(r != NULL); i++; rangestr = cbs_topic_ranges_to_string(r); g_assert(rangestr); if (g_test_verbose()) g_print("range: %s\n", rangestr); g_free(rangestr); g_slist_foreach(r, (GFunc)g_free, NULL); g_slist_free(r); } } static void test_sr_assembly(void) { const char *sr_pdu1 = "06040D91945152991136F00160124130340A0160124130" "940A00"; const char *sr_pdu2 = "06050D91945152991136F00160124130640A0160124130" "450A00"; const char *sr_pdu3 = "0606098121436587F9019012413064A0019012413045A0" "00"; struct sms sr1; struct sms sr2; struct sms sr3; unsigned char pdu[176]; long pdu_len; struct status_report_assembly *sra; gboolean delivered; struct sms_address addr; unsigned char sha1[SMS_MSGID_LEN] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; unsigned char id[SMS_MSGID_LEN]; /* international address, mr 4 & mr 5 */ decode_hex_own_buf(sr_pdu1, -1, &pdu_len, 0, pdu); g_assert(sms_decode(pdu, pdu_len, FALSE, 26, &sr1) == TRUE); decode_hex_own_buf(sr_pdu2, -1, &pdu_len, 0, pdu); g_assert(sms_decode(pdu, pdu_len, FALSE, 26, &sr2) == TRUE); /* national address, mr 6 */ decode_hex_own_buf(sr_pdu3, -1, &pdu_len, 0, pdu); g_assert(sms_decode(pdu, pdu_len, FALSE, 24, &sr3) == TRUE); if (g_test_verbose()) { g_print("sr1 address: %s, mr: %d\n", sms_address_to_string(&sr1.status_report.raddr), sr1.status_report.mr); g_print("sr2 address: %s, mr: %d\n", sms_address_to_string(&sr2.status_report.raddr), sr2.status_report.mr); g_print("sr3 address: %s, mr: %d\n", sms_address_to_string(&sr3.status_report.raddr), sr3.status_report.mr); } sms_address_from_string(&addr, "+4915259911630"); sra = status_report_assembly_new(NULL); status_report_assembly_add_fragment(sra, sha1, &addr, 4, time(NULL), 2); status_report_assembly_add_fragment(sra, sha1, &addr, 5, time(NULL), 2); status_report_assembly_expire(sra, time(NULL) + 40); g_assert(g_hash_table_size(sra->assembly_table) == 0); status_report_assembly_add_fragment(sra, sha1, &addr, 4, time(NULL), 2); status_report_assembly_add_fragment(sra, sha1, &addr, 5, time(NULL), 2); g_assert(!status_report_assembly_report(sra, &sr1, id, &delivered)); g_assert(status_report_assembly_report(sra, &sr2, id, &delivered)); g_assert(memcmp(id, sha1, SMS_MSGID_LEN) == 0); g_assert(delivered == TRUE); /* * Send sms-message in the national address-format, * but receive in the international address-format. */ sms_address_from_string(&addr, "9911630"); status_report_assembly_add_fragment(sra, sha1, &addr, 4, time(NULL), 2); status_report_assembly_add_fragment(sra, sha1, &addr, 5, time(NULL), 2); g_assert(!status_report_assembly_report(sra, &sr1, id, &delivered)); g_assert(status_report_assembly_report(sra, &sr2, id, &delivered)); g_assert(memcmp(id, sha1, SMS_MSGID_LEN) == 0); g_assert(delivered == TRUE); g_assert(g_hash_table_size(sra->assembly_table) == 0); /* * Send sms-message in the international address-format, * but receive in the national address-format. */ sms_address_from_string(&addr, "+358123456789"); status_report_assembly_add_fragment(sra, sha1, &addr, 6, time(NULL), 1); g_assert(status_report_assembly_report(sra, &sr3, id, &delivered)); g_assert(memcmp(id, sha1, SMS_MSGID_LEN) == 0); g_assert(delivered == TRUE); g_assert(g_hash_table_size(sra->assembly_table) == 0); status_report_assembly_free(sra); } struct wap_push_data { const char *pdu; int len; }; static struct wap_push_data wap_push_1 = { .pdu = "0791947122725014440185F039F501801140311480720605040B8423F00106" "246170706C69636174696F6E2F766E642E7761702E6D6D732D6D657373616" "76500AF84B4868C82984F67514B4B42008D9089088045726F74696B009650" "696E2D557073008A808E0240008805810303F48083687474703A2F2F65707" "3332E64652F4F2F5A39495A4F00", .len = 128, }; static void test_wap_push(gconstpointer data) { const struct wap_push_data *test = data; struct sms sms; unsigned char *decoded_pdu; gboolean ret; long pdu_len; long data_len; enum sms_class cls; enum sms_charset charset; GSList *list; unsigned char *wap_push; int dst_port, src_port; gboolean is_8bit; decoded_pdu = decode_hex(test->pdu, -1, &pdu_len, 0); g_assert(decoded_pdu); ret = sms_decode(decoded_pdu, pdu_len, FALSE, test->len, &sms); g_free(decoded_pdu); g_assert(ret); g_assert(sms.type == SMS_TYPE_DELIVER); if (g_test_verbose()) dump_details(&sms); ret = sms_dcs_decode(sms.deliver.dcs, &cls, &charset, NULL, NULL); g_assert(ret == TRUE); g_assert(charset == SMS_CHARSET_8BIT); g_assert(sms_extract_app_port(&sms, &dst_port, &src_port, &is_8bit)); if (g_test_verbose()) { g_print("8bit: %d\n", is_8bit); g_print("src: %d, dst: %d\n", src_port, dst_port); } g_assert(is_8bit == FALSE); g_assert(dst_port == 2948); list = g_slist_append(NULL, &sms); wap_push = sms_decode_datagram(list, &data_len); if (g_test_verbose()) { int i; g_print("data_len: %ld\n", data_len); for (i = 0; i < data_len; i++) { g_print("%02x", wap_push[i]); if ((i % 16) == 15) g_print("\n"); } g_print("\n"); } g_assert(wap_push); g_free(wap_push); g_slist_free(list); } int main(int argc, char **argv) { char long_string[152*33 + 1]; struct sms_concat_data long_string_test; g_test_init(&argc, &argv, NULL); g_test_add_func("/testsms/Test Simple Deliver", test_simple_deliver); g_test_add_func("/testsms/Test Alnum Deliver", test_alnum_sender); g_test_add_func("/testsms/Test Deliver Encode", test_deliver_encode); g_test_add_func("/testsms/Test Simple Submit", test_simple_submit); g_test_add_func("/testsms/Test Submit Encode", test_submit_encode); g_test_add_func("/testsms/Test Simple MWI", test_simple_mwi); g_test_add_data_func("/testsms/Test " "GSM 7 bit Default Alphabet Decode", &sms_charset_default, test_sms_charset); g_test_add_data_func("/testsms/Test " "GSM 7 bit Default Alphabet Extension Table Decode", &sms_charset_default_ext, test_sms_charset); g_test_add_data_func("/testsms/Test " "Turkish National Language Locking Shift Table Decode", &sms_charset_turkey, test_sms_charset); g_test_add_data_func("/testsms/Test " "Turkish National Language Single Shift Table Decode", &sms_charset_turkey_ext, test_sms_charset); g_test_add_data_func("/testsms/Test " "Portuguese National Language Locking Shift Table Decode", &sms_charset_portugal, test_sms_charset); g_test_add_data_func("/testsms/Test " "Portuguese National Language Single Shift Table Decode", &sms_charset_portugal_ext, test_sms_charset); g_test_add_data_func("/testsms/Test " "Spanish National Language Single Shift Table Decode", &sms_charset_spain, test_sms_charset); g_test_add_data_func("/testsms/Test EMS UDH 1", &ems_udh_test_1, test_ems_udh); g_test_add_data_func("/testsms/Test EMS UDH 2", &ems_udh_test_2, test_ems_udh); g_test_add_func("/testsms/Test Assembly", test_assembly); g_test_add_func("/testsms/Test Prepare 7Bit", test_prepare_7bit); g_test_add_data_func("/testsms/Test Prepare Concat", &shakespeare_test, test_prepare_concat); g_test_add_data_func("/testsms/Test Prepare Concat UTF-16 surrog pairs", &surr_pair_split_test, test_prepare_concat); memset(long_string, 'a', 152*30); memset(long_string + 152*30, 'b', 152); memset(long_string + 152*31, 'c', 152); memset(long_string + 152*32, 'd', 152); long_string[152*33] = '\0'; long_string_test.str = long_string; long_string_test.segments = 33; g_test_add_data_func("/testsms/Test Prepare Concat 30+ segments", &long_string_test, test_prepare_concat); g_test_add_func("/testsms/Test Prepare Limits", test_prepare_limits); g_test_add_func("/testsms/Test CBS Encode / Decode", test_cbs_encode_decode); g_test_add_func("/testsms/Test CBS Assembly", test_cbs_assembly); g_test_add_func("/testsms/Test CBS Padding Character", test_cbs_padding_character); g_test_add_func("/testsms/Range minimizer", test_range_minimizer); g_test_add_func("/testsms/Status Report Assembly", test_sr_assembly); g_test_add_data_func("/testsms/Test WAP Push 1", &wap_push_1, test_wap_push); return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-caif.c0000644000015600001650000000740212671500024021404 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include #include #include #include #include static GMainLoop *mainloop; static int do_open(void) { int fd; fd = open("/dev/chnlat11", O_RDWR); if (fd < 0) { g_printerr("Open of chnlat11 failed (%d)\n", errno); return -EIO; } return fd; } static int do_connect(void) { struct sockaddr_caif addr; int sk, err; /* Create a CAIF socket for AT Service */ sk = socket(AF_CAIF, SOCK_SEQPACKET, CAIFPROTO_AT); if (sk < 0) { g_printerr("CAIF socket creation failed (%d)\n", errno); return -EIO; } memset(&addr, 0, sizeof(addr)); addr.family = AF_CAIF; addr.u.at.type = CAIF_ATTYPE_PLAIN; /* Connect to the AT Service at the modem */ err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { g_printerr("CAIF socket connect failed (%d)\n", errno); close(sk); return err; } return sk; } static void caif_debug(const char *str, void *data) { g_print("%s\n", str); } static void caif_init(gboolean ok, GAtResult *result, gpointer data) { GAtChat *chat = data; g_print("caif_init: %d\n", ok); if (ok == FALSE) { g_at_chat_unref(chat); g_main_loop_quit(mainloop); return; } g_at_chat_unref(chat); g_main_loop_quit(mainloop); } static void test_connect(gboolean use_socket) { GIOChannel *io; GAtChat *chat; GAtSyntax *syntax; int fd; if (use_socket == TRUE) fd = do_connect(); else fd = do_open(); if (fd < 0) return; io = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(io, TRUE); syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new_blocking(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); if (chat == NULL) { g_printerr("Chat creation failed\n"); return; } g_at_chat_set_debug(chat, caif_debug, NULL); g_at_chat_send(chat, "ATE0 +CMEE=1", NULL, caif_init, chat, NULL); mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); g_main_loop_unref(mainloop); } static void test_basic(void) { g_test_trap_subprocess("/testcaif/basic:subprocess", 60 * 1000 * 1000, 0); g_test_trap_assert_passed(); //g_test_trap_assert_stderr("failed"); } static void test_chnlat(void) { g_test_trap_subprocess("/testcaif/chnlat:subprocess", 60 * 1000 * 1000, 0); g_test_trap_assert_passed(); //g_test_trap_assert_stderr("failed"); } static void test_connect_sock(void) { test_connect(TRUE); } static void test_connect_no_sock(void) { test_connect(FALSE); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/testcaif/basic", test_basic); g_test_add_func("/testcaif/chnlat", test_chnlat); g_test_add_func("/testcaif/basic:subprocess", test_connect_sock); g_test_add_func("/testcaif/chnlat:subprocess", test_connect_no_sock); return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/stk-test-data.h0000644000015600001650000020116712671500024022223 0ustar pbuserpbgroup00000000000000static const unsigned char display_text_111[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F, 0x04, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x31 }; static const unsigned char display_text_response_111[] = { 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const unsigned char display_text_response_121[] = { 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x20, 0x01, }; static const unsigned char display_text_131[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x81, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F, 0x04, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x32 }; static const unsigned char display_text_response_131[] = { 0x81, 0x03, 0x01, 0x21, 0x81, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const unsigned char display_text_141[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0E, 0x00, 0xD4, 0xF7, 0x9B, 0xBD, 0x4E, 0xD3, 0x41, 0xD4, 0xF2, 0x9C, 0x0E, 0x9A, 0x01 }; #define display_text_response_141 display_text_response_111 static const unsigned char display_text_151[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x00, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F, 0x04, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x34 }; static const unsigned char display_text_response_151[] = { 0x81, 0x03, 0x01, 0x21, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const unsigned char display_text_161[] = { 0xD0, 0x81, 0xAD, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x81, 0xA1, 0x04, 0x54, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4D, 0x45, 0x20, 0x74, 0x6F, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x20, 0x61, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2E, 0x20, 0x49, 0x74, 0x20, 0x61, 0x6C, 0x6C, 0x6F, 0x77, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x49, 0x4D, 0x20, 0x74, 0x6F, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x69, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x2E, 0x20, 0x54, 0x77, 0x6F, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x6F, 0x66, 0x20, 0x70, 0x72, 0x69, 0x6F }; #define display_text_response_161 display_text_response_111 static const unsigned char display_text_171[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F, 0x04, 0x3C, 0x47, 0x4F, 0x2D, 0x42, 0x41, 0x43, 0x4B, 0x57, 0x41, 0x52, 0x44, 0x53, 0x3E }; static const unsigned char display_text_response_171[] = { 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x11, }; static const unsigned char display_text_181[] = { 0xD0, 0x13, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x08, 0x04, 0x3C, 0x41, 0x42, 0x4F, 0x52, 0x54, 0x3E }; static const unsigned char display_text_response_181[] = { 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x10, }; static const unsigned char display_text_191[] = { 0xD0, 0x0F, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x00, 0x9E, 0x02, 0x00, 0x01 }; static const unsigned char display_text_response_191[] = { 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x32, }; static const unsigned char display_text_211[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0B, 0x04, 0x3C, 0x54, 0x49, 0x4D, 0x45, 0x2D, 0x4F, 0x55, 0x54, 0x3E, }; static const unsigned char display_text_response_211[] = { 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x12, }; static const unsigned char display_text_311[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x81, 0xF1, 0x04, 0x54, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4D, 0x45, 0x20, 0x74, 0x6F, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x20, 0x61, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x2F, 0x6F, 0x72, 0x20, 0x61, 0x6E, 0x20, 0x69, 0x63, 0x6F, 0x6E, 0x20, 0x28, 0x73, 0x65, 0x65, 0x20, 0x36, 0x2E, 0x35, 0x2E, 0x34, 0x29, 0x2E, 0x20, 0x49, 0x74, 0x20, 0x61, 0x6C, 0x6C, 0x6F, 0x77, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x49, 0x4D, 0x20, 0x74, 0x6F, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x69, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x66, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x2E, 0x20, 0x54, 0x77, 0x6F, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x6F, 0x66, 0x20, 0x70, 0x72, 0x69, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x64, 0x3A, 0x2D, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x70, 0x72, 0x69, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x61, 0x6E, 0x64, 0x2F, }; #define display_text_response_311 display_text_response_111 static const unsigned char display_text_411[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F, 0x04, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x31, 0xAB, 0x00, }; #define display_text_response_411 display_text_response_111 static const unsigned char display_text_421[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x21, 0x00, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F, 0x04, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x32, 0xAB, 0x00, }; #define display_text_response_421 display_text_response_151 static const unsigned char display_text_431[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0F, 0x04, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x33, 0xAB, 0x00, }; #define display_text_response_431 display_text_response_111 static const unsigned char display_text_511[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0B, 0x04, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x9E, 0x02, 0x00, 0x01 }; #define display_text_response_511a display_text_response_111 static const unsigned char display_text_response_511b[] = { 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, }; static const unsigned char display_text_521[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0C, 0x04, 0x43, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x9E, 0x02, 0x00, 0x02 }; #define display_text_response_521a display_text_response_111 #define display_text_response_521b display_text_response_511b static const unsigned char display_text_531[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0B, 0x04, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x9E, 0x02, 0x01, 0x01 }; #define display_text_response_531a display_text_response_111 #define display_text_response_531b display_text_response_511b static const unsigned char display_text_611[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x19, 0x08, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15 }; #define display_text_response_611 display_text_response_111 static const unsigned char display_text_711[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x0A, 0x04, 0x31, 0x30, 0x20, 0x53, 0x65, 0x63, 0x6F, 0x6E, 0x64, 0x84, 0x02, 0x01, 0x0A }; #define display_text_response_711 display_text_response_211 static const unsigned char display_text_811[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static const unsigned char display_text_812[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, }; #define display_text_response_811 display_text_response_111 static const unsigned char display_text_821[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x01, 0xB4 }; #define display_text_response_821 display_text_response_111 static const unsigned char display_text_831[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x02, 0xB4 }; #define display_text_response_831 display_text_response_111 static const unsigned char display_text_841[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x04, 0xB4 }; #define display_text_response_841 display_text_response_111 static const unsigned char display_text_851[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x08, 0xB4 }; #define display_text_response_851 display_text_response_111 static const unsigned char display_text_861[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x10, 0xB4 }; #define display_text_response_861 display_text_response_111 static const unsigned char display_text_871[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x20, 0xB4 }; #define display_text_response_871 display_text_response_111 static const unsigned char display_text_881[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x40, 0xB4 }; #define display_text_response_881 display_text_response_111 static const unsigned char display_text_891[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x80, 0xB4 }; #define display_text_response_891 display_text_response_111 static const unsigned char display_text_8101[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x11, 0x04, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4, }; #define display_text_response_8101 display_text_response_111 static const unsigned char display_text_911[] = { 0xD0, 0x10, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x05, 0x08, 0x4F, 0x60, 0x59, 0x7D }; #define display_text_response_911 display_text_response_111 static const unsigned char display_text_1011[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x21, 0x80, 0x82, 0x02, 0x81, 0x02, 0x8D, 0x07, 0x08, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB }; #define display_text_response_1011 display_text_response_111 static const unsigned char get_inkey_111[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22 }; static const unsigned char get_inkey_response_111[] = { 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x2b, }; static const unsigned char get_inkey_121[] = { 0xD0, 0x14, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x09, 0x00, 0x45, 0x37, 0xBD, 0x2C, 0x07, 0x89, 0x60, 0x22 }; static const unsigned char get_inkey_response_121[] = { 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x30, }; static const unsigned char get_inkey_131[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0F, 0x04, 0x3C, 0x47, 0x4F, 0x2D, 0x42, 0x41, 0x43, 0x4B, 0x57, 0x41, 0x52, 0x44, 0x53, 0x3E }; static const unsigned char get_inkey_response_131[] = { 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x11, }; static const unsigned char get_inkey_141[] = { 0xD0, 0x13, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x08, 0x04, 0x3C, 0x41, 0x42, 0x4F, 0x52, 0x54, 0x3E }; static const unsigned char get_inkey_response_141[] = { 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x10, }; static const unsigned char get_inkey_151[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x01, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x71, 0x22, }; static const unsigned char get_inkey_response_151[] = { 0x81, 0x03, 0x01, 0x22, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x71, }; static const unsigned char get_inkey_161[] = { 0xD0, 0x81, 0xAD, 0x81, 0x03, 0x01, 0x22, 0x01, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x81, 0xA1, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x78, 0x22, 0x2E, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4D, 0x45, 0x20, 0x74, 0x6F, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6C, 0x61, 0x79, 0x20, 0x74, 0x65, 0x78, 0x74, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2E, 0x20, 0x41, 0x6E, 0x79, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x65, 0x20, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x73, 0x68, 0x61, 0x6C, 0x6C, 0x20, 0x62, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, }; static const unsigned char get_inkey_response_161[] = { 0x81, 0x03, 0x01, 0x22, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x78, }; static const unsigned char get_inkey_211[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0B, 0x04, 0x3C, 0x54, 0x49, 0x4D, 0x45, 0x2D, 0x4F, 0x55, 0x54, 0x3E, }; static const unsigned char get_inkey_response_211[] = { 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x12, }; static const unsigned char get_inkey_311[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x19, 0x08, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15 }; #define get_inkey_response_311 get_inkey_response_111 static const unsigned char get_inkey_321[] = { 0xD0, 0x81, 0x99, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x81, 0x8D, 0x08, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, }; #define get_inkey_response_321 get_inkey_response_111 static const unsigned char get_inkey_411[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x22, 0x03, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x06, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72 }; static const unsigned char get_inkey_response_411[] = { 0x81, 0x03, 0x01, 0x22, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x03, 0x08, 0x04, 0x14, }; static const unsigned char get_inkey_511[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x04, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x59, 0x45, 0x53 }; static const unsigned char get_inkey_response_511[] = { 0x81, 0x03, 0x01, 0x22, 0x04, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x01, }; static const unsigned char get_inkey_512[] = { 0xD0, 0x14, 0x81, 0x03, 0x01, 0x22, 0x04, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x09, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x4E, 0x4F }; static const unsigned char get_inkey_response_512[] = { 0x81, 0x03, 0x01, 0x22, 0x04, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x00, }; static const unsigned char get_inkey_611[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x3C, 0x4E, 0x4F, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x1E, 0x02, 0x00, 0x01 }; #define get_inkey_response_611 get_inkey_response_111 static const unsigned char get_inkey_621[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0D, 0x04, 0x3C, 0x42, 0x41, 0x53, 0x49, 0x43, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x1E, 0x02, 0x01, 0x01 }; #define get_inkey_response_621 get_inkey_response_111 static const unsigned char get_inkey_631[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x3C, 0x4E, 0x4F, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x1E, 0x02, 0x00, 0x02 }; #define get_inkey_response_631 get_inkey_response_111 static const unsigned char get_inkey_641[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0E, 0x04, 0x3C, 0x43, 0x4F, 0x4C, 0x4F, 0x55, 0x52, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x1E, 0x02, 0x01, 0x02 }; #define get_inkey_response_641 get_inkey_response_111 static const unsigned char get_inkey_811[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0x84, 0x02, 0x01, 0x0A }; static const unsigned char get_inkey_response_811[] = { 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x12, 0x04, 0x02, 0x01, 0x0B, }; static const unsigned char get_inkey_911[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x00, 0xB4 }; #define get_inkey_response_911 get_inkey_response_111 static const unsigned char get_inkey_921[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x01, 0xB4 }; #define get_inkey_response_921 get_inkey_response_111 static const unsigned char get_inkey_931[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x02, 0xB4 }; #define get_inkey_response_931 get_inkey_response_111 static const unsigned char get_inkey_941[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x04, 0xB4 }; #define get_inkey_response_941 get_inkey_response_111 static const unsigned char get_inkey_951[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x08, 0xB4 }; #define get_inkey_response_951 get_inkey_response_111 static const unsigned char get_inkey_961[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x10, 0xB4 }; #define get_inkey_response_961 get_inkey_response_111 static const unsigned char get_inkey_971[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x20, 0xB4 }; #define get_inkey_response_971 get_inkey_response_111 static const unsigned char get_inkey_981[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x40, 0xB4 }; #define get_inkey_response_981 get_inkey_response_111 static const unsigned char get_inkey_991[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x80, 0xB4 }; #define get_inkey_response_991 get_inkey_response_111 static const unsigned char get_inkey_9101[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x00, 0xB4 }; #define get_inkey_response_9101 get_inkey_response_111 static const unsigned char get_inkey_1011[] = { 0xD0, 0x10, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x05, 0x08, 0x4F, 0x60, 0x59, 0x7D }; #define get_inkey_response_1011 get_inkey_response_111 static const unsigned char get_inkey_1021[] = { 0xD0, 0x81, 0x99, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x81, 0x8D, 0x08, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, }; #define get_inkey_response_1021 get_inkey_response_111 static const unsigned char get_inkey_1111[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x22, 0x03, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x06, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72 }; static const unsigned char get_inkey_response_1111[] = { 0x81, 0x03, 0x01, 0x22, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x03, 0x08, 0x59, 0x7d, }; static const unsigned char get_inkey_1211[] = { 0xD0, 0x0E, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x03, 0x08, 0x30, 0xEB }; #define get_inkey_response_1211 get_inkey_response_111 static const unsigned char get_inkey_1221[] = { 0xD0, 0x81, 0x99, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x81, 0x8D, 0x08, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, }; #define get_inkey_response_1221 get_inkey_response_111 static const unsigned char get_inkey_1311[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x22, 0x03, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x06, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72 }; static const unsigned char get_inkey_response_1311[] = { 0x81, 0x03, 0x01, 0x22, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x03, 0x08, 0x30, 0xeb, }; static const unsigned char get_input_111[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05 }; static const unsigned char get_input_response_111[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x04, 0x31, 0x32, 0x33, 0x34, 0x35, }; static const unsigned char get_input_121[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x23, 0x08, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0B, 0x00, 0x45, 0x37, 0xBD, 0x2C, 0x07, 0xD9, 0x6E, 0xAA, 0xD1, 0x0A, 0x91, 0x02, 0x05, 0x05 }; static const unsigned char get_input_response_121[] = { 0x81, 0x03, 0x01, 0x23, 0x08, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x00, 0xb6, 0x9b, 0x6a, 0xb4, 0x02, }; static const unsigned char get_input_131[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x41, 0x62, 0x43, 0x64, 0x45, 0x91, 0x02, 0x05, 0x05 }; static const unsigned char get_input_response_131[] = { 0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x04, 0x41, 0x62, 0x43, 0x64, 0x45, }; static const unsigned char get_input_141[] = { 0xD0, 0x27, 0x81, 0x03, 0x01, 0x23, 0x04, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x18, 0x04, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x20, 0x31, 0x3C, 0x53, 0x45, 0x4E, 0x44, 0x3E, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x91, 0x02, 0x04, 0x08 }; static const unsigned char get_input_response_141[] = { 0x81, 0x03, 0x01, 0x23, 0x04, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x08, 0x04, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, }; static const unsigned char get_input_151[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x15, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x2E, 0x2E, 0x39, 0x2C, 0x30, 0x2E, 0x2E, 0x39, 0x2C, 0x30, 0x28, 0x31, 0x29, 0x91, 0x02, 0x01, 0x14, }; static const unsigned char get_input_response_151[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x15, 0x04, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, }; static const unsigned char get_input_161[] = { 0xD0, 0x1E, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0F, 0x04, 0x3C, 0x47, 0x4F, 0x2D, 0x42, 0x41, 0x43, 0x4B, 0x57, 0x41, 0x52, 0x44, 0x53, 0x3E, 0x91, 0x02, 0x00, 0x08 }; static const unsigned char get_input_response_161[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x11, }; static const unsigned char get_input_171[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x08, 0x04, 0x3C, 0x41, 0x42, 0x4F, 0x52, 0x54, 0x3E, 0x91, 0x02, 0x00, 0x08 }; static const unsigned char get_input_response_171[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x10, }; static const unsigned char get_input_181[] = { 0xD0, 0x81, 0xB1, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x81, 0xA1, 0x04, 0x2A, 0x2A, 0x2A, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x23, 0x23, 0x23, 0x91, 0x02, 0xA0, 0xA0 }; static const unsigned char get_input_response_181[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x81, 0xa1, 0x04, 0x2a, 0x2a, 0x2a, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x23, 0x23, 0x23, 0x2a, 0x2a, 0x2a, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x23, 0x23, 0x23, 0x2a, 0x2a, 0x2a, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x23, 0x23, 0x23, 0x2a, 0x2a, 0x2a, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x23, 0x23, 0x23, 0x2a, 0x2a, 0x2a, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x23, 0x23, 0x23, 0x2a, 0x2a, 0x2a, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x23, 0x23, 0x23, 0x2a, 0x2a, 0x2a, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x23, 0x23, 0x23, 0x2a, 0x2a, 0x2a, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x23, 0x23, 0x23, 0x2a, 0x2a, 0x2a, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x23, 0x23, 0x23, 0x2a, 0x2a, 0x2a, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x23, 0x23, 0x23, }; static const unsigned char get_input_191[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x07, 0x04, 0x3C, 0x53, 0x45, 0x4E, 0x44, 0x3E, 0x91, 0x02, 0x00, 0x01 }; static const unsigned char get_input_response_191a[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x01, 0x04, }; static const unsigned char get_input_1101[] = { 0xD0, 0x0F, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x00, 0x91, 0x02, 0x01, 0x05 }; #define get_input_response_1101 get_input_response_111 static const unsigned char get_input_211[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0B, 0x04, 0x3C, 0x54, 0x49, 0x4D, 0x45, 0x2D, 0x4F, 0x55, 0x54, 0x3E, 0x91, 0x02, 0x00, 0x0A }; static const unsigned char get_input_response_211[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x12, }; static const unsigned char get_input_311[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x19, 0x08, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x91, 0x02, 0x05, 0x05 }; static const unsigned char get_input_response_311[] = { 0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x04, 0x48, 0x45, 0x4c, 0x4c, 0x4f, }; static const unsigned char get_input_321[] = { 0xD0, 0x81, 0x9D, 0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x81, 0x8D, 0x08, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x91, 0x02, 0x05, 0x05 }; #define get_input_response_321 get_input_response_311 static const unsigned char get_input_411[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x91, 0x02, 0x0C, 0x0C }; static const unsigned char get_input_response_411[] = { 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x19, 0x08, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, }; static const unsigned char get_input_421[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x91, 0x02, 0x05, 0xFF }; static const unsigned char get_input_response_421[] = { 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x81, 0x8d, 0x08, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, }; static const unsigned char get_input_511[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0x17, 0x06, 0x04, 0x31, 0x32, 0x33, 0x34, 0x35 }; #define get_input_response_511 get_input_response_111 static const unsigned char get_input_521[] = { 0xD0, 0x81, 0xBA, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x07, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x3A, 0x91, 0x02, 0xA0, 0xA0, 0x17, 0x81, 0xA1, 0x04, 0x2A, 0x2A, 0x2A, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x23, 0x23, 0x23, 0x2A, 0x2A, 0x2A, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x23, 0x23, 0x23 }; #define get_input_response_521 get_input_response_181 static const unsigned char get_input_611[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82,0x8D, 0x0A, 0x04, 0x3C, 0x4E, 0x4F, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x91, 0x02, 0x00, 0x0A, 0x1E, 0x02, 0x00, 0x01 }; static const unsigned char get_input_response_611a[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x2b, }; static const unsigned char get_input_621[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0D, 0x04, 0x3C, 0x42, 0x41, 0x53, 0x49, 0x43, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x91, 0x02, 0x00, 0x0A, 0x1E, 0x02, 0x01, 0x01 }; #define get_input_response_621a get_input_response_611a static const unsigned char get_input_631[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x3C, 0x4E, 0x4F, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x91, 0x02, 0x00, 0x0A, 0x1E, 0x02, 0x00, 0x02 }; #define get_input_response_631a get_input_response_611a static const unsigned char get_input_641[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0E, 0x04, 0x3C, 0x43, 0x4F, 0x4C, 0x4F, 0x55, 0x52, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x91, 0x02, 0x00, 0x0A, 0x1E, 0x02, 0x01, 0x02 }; #define get_input_response_641a get_input_response_611a static const unsigned char get_input_811[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; #define get_input_response_811 get_input_response_111 static const unsigned char get_input_821[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x01, 0xB4 }; #define get_input_response_821 get_input_response_111 static const unsigned char get_input_831[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x02, 0xB4 }; #define get_input_response_831 get_input_response_111 static const unsigned char get_input_841[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x04, 0xB4 }; #define get_input_response_841 get_input_response_111 static const unsigned char get_input_851[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x08, 0xB4 }; #define get_input_response_851 get_input_response_111 static const unsigned char get_input_861[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x10, 0xB4 }; #define get_input_response_861 get_input_response_111 static const unsigned char get_input_871[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x20, 0xB4 }; #define get_input_response_871 get_input_response_111 static const unsigned char get_input_881[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x40, 0xB4 }; #define get_input_response_881 get_input_response_111 static const unsigned char get_input_891[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x80, 0xB4 }; #define get_input_response_891 get_input_response_111 static const unsigned char get_input_8101[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; #define get_input_response_8101 get_input_response_111 static const unsigned char get_input_911[] = { 0xD0, 0x14, 0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x05, 0x08, 0x4F, 0x60, 0x59, 0x7D, 0x91, 0x02, 0x05, 0x05 }; #define get_input_response_911 get_input_response_311 static const unsigned char get_input_921[] = { 0xD0, 0x81, 0x9D, 0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x81, 0x8D, 0x08, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x4F, 0x60, 0x59, 0x7D, 0x91, 0x02, 0x05, 0x05 }; #define get_input_response_921 get_input_response_311 static const unsigned char get_input_1011[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x91, 0x02, 0x02, 0x02 }; static const unsigned char get_input_response_1011[] = { 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x05, 0x08, 0x4f, 0x60, 0x59, 0x7d, }; static const unsigned char get_input_1021[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x91, 0x02, 0x05, 0xFF }; static const unsigned char get_input_response_1021[] = { 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x81, 0x8d, 0x08, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, 0x4f, 0x60, 0x59, 0x7d, }; static const unsigned char get_input_1111[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x03, 0x08, 0x30, 0xEB, 0x91, 0x02, 0x05, 0x05 }; #define get_input_response_1111 get_input_response_311 static const unsigned char get_input_1121[] = { 0xD0, 0x81, 0x9D, 0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x81, 0x8D, 0x08, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x30, 0xEB, 0x91, 0x02, 0x05, 0x05 }; #define get_input_response_1121 get_input_response_311 static const unsigned char get_input_1211[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x91, 0x02, 0x02, 0x02 }; static const unsigned char get_input_response_1211[] = { 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x05, 0x08, 0x30, 0xeb, 0x30, 0xeb, }; static const unsigned char get_input_1221[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x91, 0x02, 0x05, 0xFF }; static const unsigned char get_input_response_1221[] = { 0x81, 0x03, 0x01, 0x23, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x81, 0x8d, 0x08, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, 0x30, 0xeb, }; static const unsigned char more_time_111[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x02, 0x00, 0x82, 0x02, 0x81, 0x82 }; static const unsigned char more_time_response_111[] = { 0x81, 0x03, 0x01, 0x02, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const unsigned char play_tone_111[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x09, 0x44, 0x69, 0x61, 0x6C, 0x20, 0x54, 0x6F, 0x6E, 0x65, 0x8E, 0x01, 0x01, 0x84, 0x02, 0x01, 0x05 }; static const unsigned char play_tone_response_111[] = { 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const unsigned char play_tone_112[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x09, 0x53, 0x75, 0x62, 0x2E, 0x20, 0x42, 0x75, 0x73, 0x79, 0x8E, 0x01, 0x02, 0x84, 0x02, 0x01, 0x05 }; #define play_tone_response_112 play_tone_response_111 static const unsigned char play_tone_113[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x0A, 0x43, 0x6F, 0x6E, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6F, 0x6E, 0x8E, 0x01, 0x03, 0x84, 0x02, 0x01, 0x05 }; #define play_tone_response_113 play_tone_response_111 static const unsigned char play_tone_114[] = { 0xD0, 0x18, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x06, 0x52, 0x50, 0x20, 0x41, 0x63, 0x6B, 0x8E, 0x01, 0x04, 0x84, 0x02, 0x01, 0x05 }; #define play_tone_response_114 play_tone_response_111 static const unsigned char play_tone_115[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x05, 0x4E, 0x6F, 0x20, 0x52, 0x50, 0x8E, 0x01, 0x05, 0x84, 0x02, 0x01, 0x05 }; #define play_tone_response_115 play_tone_response_111 static const unsigned char play_tone_116[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x09, 0x53, 0x70, 0x65, 0x63, 0x20, 0x49, 0x6E, 0x66, 0x6F, 0x8E, 0x01, 0x06, 0x84, 0x02, 0x01, 0x05 }; #define play_tone_response_116 play_tone_response_111 static const unsigned char play_tone_117[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x09, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x57, 0x61, 0x69, 0x74, 0x8E, 0x01, 0x07, 0x84, 0x02, 0x01, 0x05 }; #define play_tone_response_117 play_tone_response_111 static const unsigned char play_tone_118[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x09, 0x52, 0x69, 0x6E, 0x67, 0x20, 0x54, 0x6F, 0x6E, 0x65, 0x8E, 0x01, 0x08, 0x84, 0x02, 0x01, 0x05 }; #define play_tone_response_118 play_tone_response_111 static const unsigned char play_tone_119[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x81, 0xF1, 0x54, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x69, 0x6E, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4D, 0x45, 0x20, 0x74, 0x6F, 0x20, 0x70, 0x6C, 0x61, 0x79, 0x20, 0x61, 0x6E, 0x20, 0x61, 0x75, 0x64, 0x69, 0x6F, 0x20, 0x74, 0x6F, 0x6E, 0x65, 0x2E, 0x20, 0x55, 0x70, 0x6F, 0x6E, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4D, 0x45, 0x20, 0x73, 0x68, 0x61, 0x6C, 0x6C, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x20, 0x69, 0x66, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x6C, 0x79, 0x20, 0x69, 0x6E, 0x2C, 0x20, 0x6F, 0x72, 0x20, 0x69, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x73, 0x73, 0x20, 0x6F, 0x66, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x75, 0x70, 0x20, 0x28, 0x53, 0x45, 0x54, 0x2D, 0x55, 0x50, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x73, 0x65, 0x6E, 0x74, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x2C, 0x20, 0x73, 0x65, 0x65, 0x20, 0x47, 0x53, 0x4D, 0x22, 0x30, 0x34, 0x2E, 0x30, 0x38, 0x22, 0x28, 0x38, 0x29, 0x29, 0x2C, 0x20, 0x61, 0x20, 0x73, 0x70, 0x65, 0x65, 0x63, 0x68, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x2E, 0x20, 0x2D, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4D, 0x45, 0x20, 0x49 }; #define play_tone_response_119 play_tone_response_111 static const unsigned char play_tone_1110[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x04, 0x42, 0x65, 0x65, 0x70, 0x8E, 0x01, 0x10, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_1110 play_tone_response_111 static const unsigned char play_tone_1111[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x08, 0x50, 0x6F, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_1111 play_tone_response_111 static const unsigned char play_tone_1112[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x08, 0x4E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x8E, 0x01, 0x12, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_1112 play_tone_response_111 static const unsigned char play_tone_1113[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x05, 0x51, 0x75, 0x69, 0x63, 0x6B, 0x8E, 0x01, 0x10, 0x84, 0x02, 0x02, 0x02 }; #define play_tone_response_1113 play_tone_response_111 static const unsigned char play_tone_1114[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x07, 0x3C, 0x41, 0x42, 0x4F, 0x52, 0x54, 0x3E, 0x8E, 0x01, 0x06, 0x84, 0x02, 0x00, 0x01 }; static const unsigned char play_tone_response_1114[] = { 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x10, }; static const unsigned char play_tone_1115[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03 }; #define play_tone_response_1115 play_tone_response_111 static const unsigned char play_tone_211[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_211 play_tone_response_111 static const unsigned char play_tone_212[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x0F, 0x81, 0x0C, 0x08, 0x97, 0x94, 0xA0, 0x90, 0x92, 0xA1, 0xA2, 0x92, 0xA3, 0x99, 0xA2, 0x95, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_212 play_tone_response_111 static const unsigned char play_tone_213[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x82, 0x0C, 0x04, 0x10, 0x87, 0x84, 0x90, 0x80, 0x82, 0x91, 0x92, 0x82, 0x93, 0x89, 0x92, 0x85, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_213 play_tone_response_111 static const unsigned char play_tone_311[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x0C, 0x3C, 0x42, 0x41, 0x53, 0x49, 0x43, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0x1E, 0x02, 0x00, 0x01 }; #define play_tone_response_311 play_tone_response_111 static const unsigned char play_tone_321[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x0C, 0x3C, 0x42, 0x41, 0x53, 0x49, 0x43, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0x1E, 0x02, 0x01, 0x01 }; #define play_tone_response_321 play_tone_response_111 static const unsigned char play_tone_331[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x0D, 0x3C, 0x43, 0x4F, 0x4C, 0x4F, 0x55, 0x52, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0x1E, 0x02, 0x00, 0x02 }; #define play_tone_response_331 play_tone_response_111 static const unsigned char play_tone_341[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x0D, 0x3C, 0x43, 0x4F, 0x4C, 0x4F, 0x55, 0x52, 0x2D, 0x49, 0x43, 0x4F, 0x4E, 0x3E, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0x1E, 0x02, 0x01, 0x02 }; #define play_tone_response_341 play_tone_response_111 static const unsigned char play_tone_411[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; #define play_tone_response_411 play_tone_response_111 static const unsigned char play_tone_412[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_412 play_tone_response_111 static const unsigned char play_tone_421[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x01, 0xB4 }; #define play_tone_response_421 play_tone_response_111 static const unsigned char play_tone_422[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_422 play_tone_response_111 static const unsigned char play_tone_431[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x02, 0xB4 }; #define play_tone_response_431 play_tone_response_111 static const unsigned char play_tone_432[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_432 play_tone_response_111 static const unsigned char play_tone_441[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x04, 0xB4 }; #define play_tone_response_441 play_tone_response_111 static const unsigned char play_tone_442[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; #define play_tone_response_442 play_tone_response_111 static const unsigned char play_tone_443[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_443 play_tone_response_111 static const unsigned char play_tone_451[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x08, 0xB4 }; #define play_tone_response_451 play_tone_response_111 static const unsigned char play_tone_452[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; #define play_tone_response_452 play_tone_response_111 static const unsigned char play_tone_453[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_453 play_tone_response_111 static const unsigned char play_tone_461[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x0E, 0x10, 0xB4 }; #define play_tone_response_461 play_tone_response_111 static const unsigned char play_tone_462[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; #define play_tone_response_462 play_tone_response_111 static const unsigned char play_tone_463[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_463 play_tone_response_111 static const unsigned char play_tone_471[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x0E, 0x20, 0xB4 }; #define play_tone_response_471 play_tone_response_111 static const unsigned char play_tone_472[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; #define play_tone_response_472 play_tone_response_111 static const unsigned char play_tone_473[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_473 play_tone_response_111 static const unsigned char play_tone_481[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x40, 0xB4 }; #define play_tone_response_481 play_tone_response_111 static const unsigned char play_tone_482[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; #define play_tone_response_482 play_tone_response_111 static const unsigned char play_tone_483[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_483 play_tone_response_111 static const unsigned char play_tone_491[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x80, 0xB4 }; #define play_tone_response_491 play_tone_response_111 static const unsigned char play_tone_492[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; #define play_tone_response_492 play_tone_response_111 static const unsigned char play_tone_493[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_493 play_tone_response_111 static const unsigned char play_tone_4101[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; #define play_tone_response_4101 play_tone_response_111 static const unsigned char play_tone_4102[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_4102 play_tone_response_111 static const unsigned char play_tone_511[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x05, 0x80, 0x4E, 0x2D, 0x4E, 0x00, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_511 play_tone_response_111 static const unsigned char play_tone_512[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x05, 0x81, 0x02, 0x9C, 0xAD, 0x80, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_512 play_tone_response_111 static const unsigned char play_tone_513[] = { 0xD0, 0x18, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x06, 0x82, 0x02, 0x4E, 0x00, 0xAD, 0x80, 0x8E, 0x01, 0x11, 0x84, 0x02, 0x01, 0x01 }; #define play_tone_response_513 play_tone_response_111 static const unsigned char play_tone_611[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x09, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x30, 0x8E, 0x01, 0x01, 0x84, 0x02, 0x01, 0x05 }; #define play_tone_response_611 play_tone_response_111 static const unsigned char play_tone_612[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x07, 0x81, 0x04, 0x61, 0x38, 0x31, 0xEB, 0x31, 0x8E, 0x01, 0x01, 0x84, 0x02, 0x01, 0x05 }; #define play_tone_response_612 play_tone_response_111 static const unsigned char play_tone_613[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x81, 0x03, 0x85, 0x08, 0x82, 0x04, 0x30, 0xA0, 0x38, 0x32, 0xCB, 0x32, 0x8E, 0x01, 0x01, 0x84, 0x02, 0x01, 0x05 }; #define play_tone_response_613 play_tone_response_111 static const unsigned char poll_interval_111[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x03, 0x00, 0x82, 0x02, 0x81, 0x82, 0x84, 0x02, 0x01, 0x14 }; static const unsigned char poll_interval_response_111[] = { 0x81, 0x03, 0x01, 0x03, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x84, 0x02, 0x01, 0x14, }; ofono-1.17.bzr6912+16.04.20160314.3/unit/test-stkutil.c0000644000015600001650000262265012671500024022213 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #include "smsutil.h" #include "stkutil.h" #include "util.h" #include "stk-test-data.h" #define MAX_ITEM 100 struct sms_submit_test { gboolean rd; enum sms_validity_period_format vpf; gboolean rp; gboolean udhi; gboolean srr; guint8 mr; struct sms_address daddr; guint8 pid; guint8 dcs; struct sms_validity_period vp; guint8 udl; guint8 ud[160]; }; struct sms_test { struct sms_address sc_addr; enum sms_type type; union { struct sms_deliver deliver; struct sms_deliver_ack_report deliver_ack_report; struct sms_deliver_err_report deliver_err_report; struct sms_submit_test submit; struct sms_submit_ack_report submit_ack_report; struct sms_submit_err_report submit_err_report; struct sms_command command; struct sms_status_report status_report; }; }; static gboolean g_mem_equal(const unsigned char *v1, const unsigned char *v2, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) if (v1[i] != v2[i]) return FALSE; return TRUE; } static inline void check_common_bool(const ofono_bool_t command, const ofono_bool_t test) { g_assert(command == test); } static inline void check_common_byte(const unsigned char command, const unsigned char test) { g_assert(command == test); } static inline void check_common_text(const char *command, const char *test) { if (test == NULL) { g_assert(command == NULL); return; } g_assert(command != NULL); g_assert(g_str_equal(command, test)); } static inline void check_common_byte_array( const struct stk_common_byte_array *command, const struct stk_common_byte_array *test) { if (test->len == 0) { g_assert(command->len == 0); return; } g_assert(command->len != 0); g_assert(command->len == test->len); g_assert(g_mem_equal(command->array, test->array, test->len)); } /* Defined in TS 102.223 Section 8.1 */ static inline void check_address(const struct stk_address *command, const struct stk_address *test) { g_assert(command->ton_npi == test->ton_npi); check_common_text(command->number, test->number); } /* Defined in TS 102.223 Section 8.2 */ static inline void check_alpha_id(const char *command, const char *test) { if (test != NULL && strlen(test) > 0) check_common_text(command, test); else g_assert(command == NULL); } /* Defined in TS 102.223 Section 8.3 */ static void check_subaddress(const struct stk_subaddress *command, const struct stk_subaddress *test) { if (test->len == 0) { g_assert(command->len == 0); return; } g_assert(command->len != 0); g_assert(g_mem_equal(command->subaddr, test->subaddr, test->len)); } /* Defined in TS 102.223 Section 8.4 */ static void check_ccp(const struct stk_ccp *command, const struct stk_ccp *test) { if (test->len == 0) { g_assert(command->len == 0); return; } g_assert(command->len != 0); g_assert(g_mem_equal(command->ccp, test->ccp, test->len)); } /* Defined in TS 102.223 Section 8.8 */ static void check_duration(const struct stk_duration *command, const struct stk_duration *test) { g_assert(command->unit == test->unit); g_assert(command->interval == test->interval); } /* Defined in TS 102.223 Section 8.9 */ static void check_item(const struct stk_item *command, const struct stk_item *test) { g_assert(command->id == test->id); check_common_text(command->text, test->text); } /* Defined in TS 102.223 Section 8.10 */ static inline void check_item_id(const unsigned char command, const unsigned char test) { check_common_byte(command, test); } static void check_items(GSList *command, const struct stk_item *test) { struct stk_item *si; GSList *l; unsigned int i = 0; for (l = command; l; l = l->next) { si = l->data; check_item(si, &test[i++]); } g_assert(test[i].id == 0); } /* Defined in TS 102.223 Section 8.11 */ static void check_response_length(const struct stk_response_length *command, const struct stk_response_length *test) { g_assert(command->min == test->min); g_assert(command->max == test->max); } /* Defined in TS 102.223 Section 8.13 */ static void check_gsm_sms(const struct sms *command, const struct sms_test *test) { g_assert(command->sc_addr.number_type == test->sc_addr.number_type); g_assert(command->sc_addr.numbering_plan == test->sc_addr.numbering_plan); g_assert(g_str_equal(command->sc_addr.address, test->sc_addr.address)); switch (test->type) { case SMS_TYPE_SUBMIT: { const struct sms_submit *cs = &command->submit; const struct sms_submit_test *ts = &test->submit; enum sms_charset charset; g_assert(cs->rd == ts->rd); g_assert(cs->vpf == ts->vpf); g_assert(cs->rp == ts->rp); g_assert(cs->udhi == ts->udhi); g_assert(cs->srr == ts->srr); g_assert(cs->mr == ts->mr); g_assert(cs->daddr.number_type == ts->daddr.number_type); g_assert(cs->daddr.numbering_plan == ts->daddr.numbering_plan); g_assert(g_str_equal(cs->daddr.address, ts->daddr.address)); g_assert(cs->pid == ts->pid); g_assert(cs->dcs == ts->dcs); switch (ts->vpf) { case SMS_VALIDITY_PERIOD_FORMAT_RELATIVE: g_assert(cs->vp.relative == ts->vp.relative); break; case SMS_VALIDITY_PERIOD_FORMAT_ABSOLUTE: { const struct sms_scts *ca = &cs->vp.absolute; const struct sms_scts *ta = &ts->vp.absolute; g_assert(ca->year == ta->year); g_assert(ca->month == ta->month); g_assert(ca->day == ta->day); g_assert(ca->hour == ta->hour); g_assert(ca->minute == ta->minute); g_assert(ca->second == ta->second); g_assert(ca->has_timezone == ta->has_timezone); if (ta->has_timezone) g_assert(ca->timezone == ta->timezone); break; } case SMS_VALIDITY_PERIOD_FORMAT_ENHANCED: g_assert(g_mem_equal(cs->vp.enhanced, ts->vp.enhanced, 7)); break; default: break; } g_assert(cs->udl == ts->udl); sms_dcs_decode(ts->dcs, NULL, &charset, NULL, NULL); if (charset == SMS_CHARSET_8BIT) g_assert(g_str_equal(cs->ud, ts->ud)); else { GSList *sms_list = NULL; char *message; sms_list = g_slist_prepend(sms_list, (void *)command); message = sms_decode_text(sms_list); g_assert(g_str_equal(message, ts->ud)); g_free(message); } break; } default: g_assert(FALSE); } } /* Defined in TS 102.223 Section 8.14 */ static inline void check_ss(const struct stk_ss *command, const struct stk_ss *test) { g_assert(command->ton_npi == test->ton_npi); check_common_text(command->ss, test->ss); } /* Defined in TS 102.223 Section 8.15 */ static inline void check_text(const char *command, const char *test) { check_common_text(command, test); } /* Defined in TS 102.223 Section 8.16 */ static inline void check_tone(const ofono_bool_t command, const ofono_bool_t test) { check_common_bool(command, test); } /* Defined in TS 102.223 Section 8.17 */ static inline void check_ussd(const struct stk_ussd_string *command, const char *test) { char *utf8 = ussd_decode(command->dcs, command->len, command->string); check_common_text(utf8, test); g_free(utf8); } /* Defined in TS 102.223 Section 8.18 */ static void check_file_list(GSList *command, const struct stk_file *test) { struct stk_file *sf; GSList *l; unsigned int i = 0; for (l = command; l; l = l->next) { sf = l->data; g_assert(sf->len == test[i].len); g_assert(g_mem_equal(sf->file, test[i++].file, sf->len)); } g_assert(test[i].len == 0); } /* Defined in TS 102.223 Section 8.23 */ static inline void check_default_text(const char *command, const char *test) { check_common_text(command, test); } /* Defined in TS 102.223 Section 8.24 */ static void check_items_next_action_indicator( const struct stk_items_next_action_indicator *command, const struct stk_items_next_action_indicator *test) { g_assert(command->len == test->len); g_assert(g_mem_equal(command->list, test->list, test->len)); } /* Defined in TS 102.223 Section 8.25 */ static void check_event_list(const struct stk_event_list *command, const struct stk_event_list *test) { g_assert(command->len == test->len); g_assert(g_mem_equal(command->list, test->list, test->len)); } /* Defined in TS 102.223 Section 8.31 */ static void check_icon_id(const struct stk_icon_id *command, const struct stk_icon_id *test) { g_assert(command->id == test->id); g_assert(command->qualifier == test->qualifier); } /* Defined in TS 102.223 Section 8.32 */ static void check_item_icon_id_list(const struct stk_item_icon_id_list *command, const struct stk_item_icon_id_list *test) { g_assert(command->qualifier == test->qualifier); g_assert(command->len == test->len); g_assert(g_mem_equal(command->list, test->list, test->len)); } /* Defined in TS 102.223 Section 8.35 */ static void check_c_apdu(const struct stk_c_apdu *command, const struct stk_c_apdu *test) { g_assert(command->cla == test->cla); g_assert(command->ins == test->ins); g_assert(command->p1 == test->p1); g_assert(command->p2 == test->p2); g_assert(command->lc == test->lc); g_assert(g_mem_equal(command->data, test->data, test->lc)); if (test->has_le) g_assert(command->le == test->le); } /* Defined in TS 102.223 Section 8.37 */ static inline void check_timer_id(const unsigned char command, const unsigned char test) { check_common_byte(command, test); } /* Defined in TS 102.223 Section 8.38 */ static inline void check_timer_value(const struct stk_timer_value *command, const struct stk_timer_value *test) { g_assert(command->hour == test->hour); g_assert(command->minute == test->minute); g_assert(command->second == test->second); } /* Defined in TS 102.223 Section 8.40 */ static inline void check_at_command(const char *command, const char *test) { check_common_text(command, test); } /* Defined in TS 102.223 Section 8.43 */ static inline void check_imm_resp(const unsigned char command, const unsigned char test) { check_common_byte(command, test); } /* Defined in TS 102.223 Section 8.44 */ static inline void check_dtmf_string(const char *command, const char *test) { check_common_text(command, test); } /* Defined in TS 102.223 Section 8.45 */ static inline void check_language(const char *command, const char *test) { check_common_text(command, test); } /* Defined in TS 102.223 Section 8.47 */ static inline void check_browser_id(const unsigned char command, const unsigned char test) { check_common_byte(command, test); } /* Defined in TS 102.223 Section 8.48 */ static inline void check_url(const char *command, const char *test) { check_common_text(command, test); } /* Defined in TS 102.223 Section 8.49 */ static inline void check_bearer(const struct stk_common_byte_array *command, const struct stk_common_byte_array *test) { check_common_byte_array(command, test); } /* Defined in TS 102.223 Section 8.50 */ static void check_provisioning_file_reference(const struct stk_file *command, const struct stk_file *test) { g_assert(command->len == test->len); g_assert(g_mem_equal(command->file, test->file, test->len)); } static void check_provisioning_file_references(GSList *command, const struct stk_file *test) { struct stk_file *sf; GSList *l; unsigned int i = 0; for (l = command; l; l = l->next) { sf = l->data; check_provisioning_file_reference(sf, &test[i++]); } g_assert(test[i].len == 0); } /* Defined in TS 102.223 Section 8.52 */ static void check_bearer_desc(const struct stk_bearer_description *command, const struct stk_bearer_description *test) { g_assert(command->type == test->type); if (test->type == STK_BEARER_TYPE_GPRS_UTRAN) { check_common_byte(command->gprs.precedence, test->gprs.precedence); check_common_byte(command->gprs.delay, test->gprs.delay); check_common_byte(command->gprs.reliability, test->gprs.reliability); check_common_byte(command->gprs.peak, test->gprs.peak); check_common_byte(command->gprs.mean, test->gprs.mean); check_common_byte(command->gprs.pdp_type, test->gprs.pdp_type); return; } } /* Defined in TS 102.223 Section 8.53 */ static inline void check_channel_data( const struct stk_common_byte_array *command, const struct stk_common_byte_array *test) { check_common_byte_array(command, test); } /* Defined in TS 102.223 Section 8.58 */ static inline void check_other_address( const struct stk_other_address *command, const struct stk_other_address *test) { check_common_byte(command->type, test->type); if (test->type == STK_ADDRESS_IPV4) g_assert(command->addr.ipv4 == test->addr.ipv4); else g_assert(g_mem_equal(command->addr.ipv6, test->addr.ipv6, 16)); } /* Defined in TS 102.223 Section 8.59 */ static void check_uicc_te_interface(const struct stk_uicc_te_interface *command, const struct stk_uicc_te_interface *test) { check_common_byte(command->protocol, test->protocol); g_assert(command->port == test->port); } /* Defined in TS 102.223 Section 8.60 */ static inline void check_aid(const struct stk_aid *command, const struct stk_aid *test) { g_assert(g_mem_equal(command->aid, test->aid, test->len)); } /* Defined in TS 102.223 Section 8.70 */ static inline void check_network_access_name(const char *command, const char *test) { check_common_text(command, test); } /* Defined in TS 102.223 Section 8.71 */ static inline void check_cdma_sms_tpdu( const struct stk_common_byte_array *command, const struct stk_common_byte_array *test) { check_common_byte_array(command, test); } static void check_text_attr_html(const struct stk_text_attribute *test, char *text, const char *expected_html) { char *html; unsigned short attrs[256]; int i; if (expected_html == NULL) return; for (i = 0; i < test->len; i += 4) { attrs[i] = test->attributes[i]; attrs[i + 1] = test->attributes[i + 1]; attrs[i + 2] = test->attributes[i + 2]; attrs[i + 3] = test->attributes[i + 3]; } html = stk_text_to_html(text, attrs, test->len / 4); g_assert(memcmp(html, expected_html, strlen(expected_html)) == 0); g_free(html); } /* Defined in TS 102.223 Section 8.72 */ static void check_text_attr(const struct stk_text_attribute *command, const struct stk_text_attribute *test) { g_assert(command->len == test->len); g_assert(g_mem_equal(command->attributes, test->attributes, test->len)); } /* Defined in TS 102.223 Section 8.73 */ static void check_item_text_attribute_list( const struct stk_item_text_attribute_list *command, const struct stk_item_text_attribute_list *test) { g_assert(command->len == test->len); g_assert(g_mem_equal(command->list, test->list, test->len)); } /* Defined in TS 102.223 Section 8.80 */ static void check_frame_id(const struct stk_frame_id *command, const struct stk_frame_id *test) { g_assert(command->has_id == test->has_id); if (test->has_id) g_assert(command->id == test->id); } struct display_text_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; const char *text; struct stk_icon_id icon_id; ofono_bool_t immediate_response; struct stk_duration duration; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; const char *html; }; static struct display_text_test display_text_data_111 = { .pdu = display_text_111, .pdu_len = sizeof(display_text_111), .qualifier = 0x80, .text = "Toolkit Test 1" }; static struct display_text_test display_text_data_131 = { .pdu = display_text_131, .pdu_len = sizeof(display_text_131), .qualifier = 0x81, .text = "Toolkit Test 2" }; static struct display_text_test display_text_data_141 = { .pdu = display_text_141, .pdu_len = sizeof(display_text_141), .qualifier = 0x80, .text = "Toolkit Test 3" }; static struct display_text_test display_text_data_151 = { .pdu = display_text_151, .pdu_len = sizeof(display_text_151), .qualifier = 0x00, .text = "Toolkit Test 4" }; static struct display_text_test display_text_data_161 = { .pdu = display_text_161, .pdu_len = sizeof(display_text_161), .qualifier = 0x80, .text = "This command instructs the ME to display a text message. " "It allows the SIM to define the priority of that " "message, and the text string format. Two types of " "prio" }; static struct display_text_test display_text_data_171 = { .pdu = display_text_171, .pdu_len = sizeof(display_text_171), .qualifier = 0x80, .text = "" }; static struct display_text_test display_text_data_511 = { .pdu = display_text_511, .pdu_len = sizeof(display_text_511), .qualifier = 0x80, .text = "Basic Icon", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct display_text_test display_text_data_521 = { .pdu = display_text_521, .pdu_len = sizeof(display_text_521), .qualifier = 0x80, .text = "Colour Icon", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x02 } }; static struct display_text_test display_text_data_531 = { .pdu = display_text_531, .pdu_len = sizeof(display_text_531), .qualifier = 0x80, .text = "Basic Icon", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct display_text_test display_text_data_611 = { .pdu = display_text_611, .pdu_len = sizeof(display_text_611), .qualifier = 0x80, .text = "ЗДРАВСТВУЙТЕ" }; static struct display_text_test display_text_data_711 = { .pdu = display_text_711, .pdu_len = sizeof(display_text_711), .qualifier = 0x80, .text = "10 Second", .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 10, } }; static struct display_text_test display_text_data_811 = { .pdu = display_text_811, .pdu_len = sizeof(display_text_811), .qualifier = 0x80, .text = "Text Attribute 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 }, }, .html = "
Text Attribute 1" "
", }; static struct display_text_test display_text_data_821 = { .pdu = display_text_821, .pdu_len = sizeof(display_text_821), .qualifier = 0x80, .text = "Text Attribute 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x01, 0xB4 }, }, .html = "
Text Attribute 1" "
", }; static struct display_text_test display_text_data_831 = { .pdu = display_text_831, .pdu_len = sizeof(display_text_831), .qualifier = 0x80, .text = "Text Attribute 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x02, 0xB4 }, }, .html = "
Text Attribute 1" "
", }; static struct display_text_test display_text_data_841 = { .pdu = display_text_841, .pdu_len = sizeof(display_text_841), .qualifier = 0x80, .text = "Text Attribute 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x04, 0xB4 }, }, .html = "
" "Text Attribute 1
", }; static struct display_text_test display_text_data_851 = { .pdu = display_text_851, .pdu_len = sizeof(display_text_851), .qualifier = 0x80, .text = "Text Attribute 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x08, 0xB4 }, }, .html = "
" "Text Attribute 1
", }; static struct display_text_test display_text_data_861 = { .pdu = display_text_861, .pdu_len = sizeof(display_text_861), .qualifier = 0x80, .text = "Text Attribute 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x10, 0xB4 }, }, .html = "
" "Text Attribute 1
", }; static struct display_text_test display_text_data_871 = { .pdu = display_text_871, .pdu_len = sizeof(display_text_871), .qualifier = 0x80, .text = "Text Attribute 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x20, 0xB4 }, }, .html = "
" "Text Attribute 1
", }; static struct display_text_test display_text_data_881 = { .pdu = display_text_881, .pdu_len = sizeof(display_text_881), .qualifier = 0x80, .text = "Text Attribute 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x40, 0xB4 }, }, .html = "
Text Attribute 1
", }; static struct display_text_test display_text_data_891 = { .pdu = display_text_891, .pdu_len = sizeof(display_text_891), .qualifier = 0x80, .text = "Text Attribute 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x80, 0xB4 }, }, .html = "
Text Attribute 1
", }; static struct display_text_test display_text_data_911 = { .pdu = display_text_911, .pdu_len = sizeof(display_text_911), .qualifier = 0x80, .text = "你好" }; static struct display_text_test display_text_data_1011 = { .pdu = display_text_1011, .pdu_len = sizeof(display_text_1011), .qualifier = 0x80, .text = "80ル" }; /* Defined in TS 102.384 Section 27.22.4.1 */ static void test_display_text(gconstpointer data) { const struct display_text_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_DISPLAY_TEXT); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_DISPLAY); g_assert(command->display_text.text); check_text(command->display_text.text, test->text); check_icon_id(&command->display_text.icon_id, &test->icon_id); check_imm_resp(command->display_text.immediate_response, test->immediate_response); check_duration(&command->display_text.duration, &test->duration); check_text_attr(&command->display_text.text_attr, &test->text_attr); check_text_attr_html(&command->display_text.text_attr, command->display_text.text, test->html); check_frame_id(&command->display_text.frame_id, &test->frame_id); stk_command_free(command); } struct get_inkey_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *text; struct stk_icon_id icon_id; struct stk_duration duration; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; char *html; }; static unsigned char get_inkey_711[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x80, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22 }; static unsigned char get_inkey_712[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x80, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x2B, 0x22 }; static unsigned char get_inkey_912[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static unsigned char get_inkey_922[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static unsigned char get_inkey_932[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static unsigned char get_inkey_942[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x00, 0xB4 }; static unsigned char get_inkey_943[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static unsigned char get_inkey_952[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x00, 0xB4 }; static unsigned char get_inkey_953[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static unsigned char get_inkey_962[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x00, 0xB4 }; static unsigned char get_inkey_963[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static unsigned char get_inkey_972[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x00, 0xB4 }; static unsigned char get_inkey_973[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static unsigned char get_inkey_982[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x00, 0xB4 }; static unsigned char get_inkey_983[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static unsigned char get_inkey_992a[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22, 0xD0, 0x04, 0x00, 0x09, 0x00, 0xB4 }; static unsigned char get_inkey_992b[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static unsigned char get_inkey_993[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static unsigned char get_inkey_9102[] = { 0xD0, 0x15, 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x22, 0x23, 0x22 }; static struct get_inkey_test get_inkey_data_111 = { .pdu = get_inkey_111, .pdu_len = sizeof(get_inkey_111), .qualifier = 0x00, .text = "Enter \"+\"" }; static struct get_inkey_test get_inkey_data_121 = { .pdu = get_inkey_121, .pdu_len = sizeof(get_inkey_121), .qualifier = 0x00, .text = "Enter \"0\"" }; static struct get_inkey_test get_inkey_data_131 = { .pdu = get_inkey_131, .pdu_len = sizeof(get_inkey_131), .qualifier = 0x00, .text = "" }; static struct get_inkey_test get_inkey_data_141 = { .pdu = get_inkey_141, .pdu_len = sizeof(get_inkey_141), .qualifier = 0x00, .text = "" }; static struct get_inkey_test get_inkey_data_151 = { .pdu = get_inkey_151, .pdu_len = sizeof(get_inkey_151), .qualifier = 0x01, .text = "Enter \"q\"" }; static struct get_inkey_test get_inkey_data_161 = { .pdu = get_inkey_161, .pdu_len = sizeof(get_inkey_161), .qualifier = 0x01, .text = "Enter \"x\". This command instructs the ME to display text, " "and to expect the user to enter a single character. Any " "response entered by the user shall be passed t" }; static struct get_inkey_test get_inkey_data_211 = { .pdu = get_inkey_211, .pdu_len = sizeof(get_inkey_211), .qualifier = 0x00, .text = "" }; static struct get_inkey_test get_inkey_data_311 = { .pdu = get_inkey_311, .pdu_len = sizeof(get_inkey_311), .qualifier = 0x00, .text = "ЗДРАВСТВУЙТЕ" }; static struct get_inkey_test get_inkey_data_321 = { .pdu = get_inkey_321, .pdu_len = sizeof(get_inkey_321), .qualifier = 0x00, .text = "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙ" }; static struct get_inkey_test get_inkey_data_411 = { .pdu = get_inkey_411, .pdu_len = sizeof(get_inkey_411), .qualifier = 0x03, .text = "Enter" }; static struct get_inkey_test get_inkey_data_511 = { .pdu = get_inkey_511, .pdu_len = sizeof(get_inkey_511), .qualifier = 0x04, .text = "Enter YES" }; static struct get_inkey_test get_inkey_data_512 = { .pdu = get_inkey_512, .pdu_len = sizeof(get_inkey_512), .qualifier = 0x04, .text = "Enter NO" }; static struct get_inkey_test get_inkey_data_611 = { .pdu = get_inkey_611, .pdu_len = sizeof(get_inkey_611), .qualifier = 0x00, .text = "", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct get_inkey_test get_inkey_data_621 = { .pdu = get_inkey_621, .pdu_len = sizeof(get_inkey_621), .qualifier = 0x00, .text = "", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct get_inkey_test get_inkey_data_631 = { .pdu = get_inkey_631, .pdu_len = sizeof(get_inkey_631), .qualifier = 0x00, .text = "", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x02 } }; static struct get_inkey_test get_inkey_data_641 = { .pdu = get_inkey_641, .pdu_len = sizeof(get_inkey_641), .qualifier = 0x00, .text = "", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x02 } }; static struct get_inkey_test get_inkey_data_711 = { .pdu = get_inkey_711, .pdu_len = sizeof(get_inkey_711), .qualifier = 0x80, .text = "Enter \"+\"" }; static struct get_inkey_test get_inkey_data_712 = { .pdu = get_inkey_712, .pdu_len = sizeof(get_inkey_712), .qualifier = 0x80, .text = "Enter \"+\"" }; static struct get_inkey_test get_inkey_data_811 = { .pdu = get_inkey_811, .pdu_len = sizeof(get_inkey_811), .qualifier = 0x00, .text = "Enter \"+\"", .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 10 } }; static struct get_inkey_test get_inkey_data_911 = { .pdu = get_inkey_911, .pdu_len = sizeof(get_inkey_911), .qualifier = 0x00, .text = "Enter \"+\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x00, 0xB4 } }, .html = "
Enter \"+\"
", }; static struct get_inkey_test get_inkey_data_912 = { .pdu = get_inkey_912, .pdu_len = sizeof(get_inkey_912), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_921 = { .pdu = get_inkey_921, .pdu_len = sizeof(get_inkey_921), .qualifier = 0x00, .text = "Enter \"+\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x01, 0xB4 } }, .html = "
Enter \"+\"" "
", }; static struct get_inkey_test get_inkey_data_922 = { .pdu = get_inkey_922, .pdu_len = sizeof(get_inkey_922), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_931 = { .pdu = get_inkey_931, .pdu_len = sizeof(get_inkey_931), .qualifier = 0x00, .text = "Enter \"+\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x02, 0xB4 } }, .html = "
Enter \"+\"" "
", }; static struct get_inkey_test get_inkey_data_932 = { .pdu = get_inkey_932, .pdu_len = sizeof(get_inkey_932), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_941 = { .pdu = get_inkey_941, .pdu_len = sizeof(get_inkey_941), .qualifier = 0x00, .text = "Enter \"+\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x04, 0xB4 } }, .html = "
Enter \"+\"" "
", }; static struct get_inkey_test get_inkey_data_942 = { .pdu = get_inkey_942, .pdu_len = sizeof(get_inkey_942), .qualifier = 0x00, .text = "Enter \"#\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x00, 0xB4 } }, .html = "
Enter \"#\"
", }; static struct get_inkey_test get_inkey_data_943 = { .pdu = get_inkey_943, .pdu_len = sizeof(get_inkey_943), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_951 = { .pdu = get_inkey_951, .pdu_len = sizeof(get_inkey_951), .qualifier = 0x00, .text = "Enter \"+\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x08, 0xB4 } }, .html = "
" "Enter \"+\"
", }; static struct get_inkey_test get_inkey_data_952 = { .pdu = get_inkey_952, .pdu_len = sizeof(get_inkey_952), .qualifier = 0x00, .text = "Enter \"#\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x00, 0xB4 } }, .html = "
Enter \"#\"
", }; static struct get_inkey_test get_inkey_data_953 = { .pdu = get_inkey_953, .pdu_len = sizeof(get_inkey_953), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_961 = { .pdu = get_inkey_961, .pdu_len = sizeof(get_inkey_961), .qualifier = 0x00, .text = "Enter \"+\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x10, 0xB4 } }, .html = "
Enter \"+\"" "
", }; static struct get_inkey_test get_inkey_data_962 = { .pdu = get_inkey_962, .pdu_len = sizeof(get_inkey_962), .qualifier = 0x00, .text = "Enter \"#\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x00, 0xB4 } }, .html = "
Enter \"#\"
", }; static struct get_inkey_test get_inkey_data_963 = { .pdu = get_inkey_963, .pdu_len = sizeof(get_inkey_963), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_971 = { .pdu = get_inkey_971, .pdu_len = sizeof(get_inkey_971), .qualifier = 0x00, .text = "Enter \"+\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x20, 0xB4 } }, .html = "
" "Enter \"+\"
", }; static struct get_inkey_test get_inkey_data_972 = { .pdu = get_inkey_972, .pdu_len = sizeof(get_inkey_972), .qualifier = 0x00, .text = "Enter \"#\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x00, 0xB4 } }, .html = "
Enter \"#\"
", }; static struct get_inkey_test get_inkey_data_973 = { .pdu = get_inkey_973, .pdu_len = sizeof(get_inkey_973), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_981 = { .pdu = get_inkey_981, .pdu_len = sizeof(get_inkey_981), .qualifier = 0x00, .text = "Enter \"+\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x40, 0xB4 } }, .html = "
Enter \"+\"
", }; static struct get_inkey_test get_inkey_data_982 = { .pdu = get_inkey_982, .pdu_len = sizeof(get_inkey_982), .qualifier = 0x00, .text = "Enter \"#\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x00, 0xB4 } }, .html = "
Enter \"#\"
", }; static struct get_inkey_test get_inkey_data_983 = { .pdu = get_inkey_983, .pdu_len = sizeof(get_inkey_983), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_991 = { .pdu = get_inkey_991, .pdu_len = sizeof(get_inkey_991), .qualifier = 0x00, .text = "Enter \"+\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x80, 0xB4 } }, .html = "
Enter \"+\"
", }; static struct get_inkey_test get_inkey_data_992a = { .pdu = get_inkey_992a, .pdu_len = sizeof(get_inkey_992a), .qualifier = 0x00, .text = "Enter \"#\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x00, 0xB4 } }, .html = "
Enter \"#\"
", }; static struct get_inkey_test get_inkey_data_992b = { .pdu = get_inkey_992b, .pdu_len = sizeof(get_inkey_992b), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_993 = { .pdu = get_inkey_993, .pdu_len = sizeof(get_inkey_993), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_9101 = { .pdu = get_inkey_9101, .pdu_len = sizeof(get_inkey_9101), .qualifier = 0x00, .text = "Enter \"+\"", .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x00, 0xB4 } }, .html = "
Enter \"+\"
", }; static struct get_inkey_test get_inkey_data_9102 = { .pdu = get_inkey_9102, .pdu_len = sizeof(get_inkey_9102), .qualifier = 0x00, .text = "Enter \"#\"" }; static struct get_inkey_test get_inkey_data_1011 = { .pdu = get_inkey_1011, .pdu_len = sizeof(get_inkey_1011), .qualifier = 0x00, .text = "你好" }; static struct get_inkey_test get_inkey_data_1021 = { .pdu = get_inkey_1021, .pdu_len = sizeof(get_inkey_1021), .qualifier = 0x00, .text = "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好" }; static struct get_inkey_test get_inkey_data_1111 = { .pdu = get_inkey_1111, .pdu_len = sizeof(get_inkey_1111), .qualifier = 0x03, .text = "Enter" }; static struct get_inkey_test get_inkey_data_1211 = { .pdu = get_inkey_1211, .pdu_len = sizeof(get_inkey_1211), .qualifier = 0x00, .text = "ル" }; static struct get_inkey_test get_inkey_data_1221 = { .pdu = get_inkey_1221, .pdu_len = sizeof(get_inkey_1221), .qualifier = 0x00, .text = "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルル" }; static struct get_inkey_test get_inkey_data_1311 = { .pdu = get_inkey_1311, .pdu_len = sizeof(get_inkey_1311), .qualifier = 0x03, .text = "Enter" }; /* Defined in TS 102.384 Section 27.22.4.2 */ static void test_get_inkey(gconstpointer data) { const struct get_inkey_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_GET_INKEY); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); g_assert(command->get_inkey.text); check_text(command->get_inkey.text, test->text); check_icon_id(&command->get_inkey.icon_id, &test->icon_id); check_duration(&command->get_inkey.duration, &test->duration); check_text_attr(&command->get_inkey.text_attr, &test->text_attr); check_text_attr_html(&command->get_inkey.text_attr, command->get_inkey.text, test->html); check_frame_id(&command->get_inkey.frame_id, &test->frame_id); stk_command_free(command); } struct get_input_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *text; struct stk_response_length resp_len; char *default_text; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; char *html; }; static unsigned char get_input_711[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x80, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x91, 0x02, 0x05, 0x05 }; static unsigned char get_input_812[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x91, 0x02, 0x05, 0x05 }; static unsigned char get_input_822[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x91, 0x02, 0x05, 0x05 }; static unsigned char get_input_832[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x91, 0x02, 0x05, 0x05 }; static unsigned char get_input_842[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char get_input_843[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x33, 0x33, 0x33, 0x33, 0x33, 0x91, 0x02, 0x05, 0x05 }; static unsigned char get_input_852[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char get_input_853[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x33, 0x33, 0x33, 0x33, 0x33, 0x91, 0x02, 0x05, 0x05 }; static unsigned char get_input_862[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char get_input_863[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x33, 0x33, 0x33, 0x33, 0x33, 0x91, 0x02, 0x05, 0x05 }; static unsigned char get_input_872[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char get_input_873[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x33, 0x33, 0x33, 0x33, 0x33, 0x91, 0x02, 0x05, 0x05 }; static unsigned char get_input_882[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char get_input_883[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x33, 0x33, 0x33, 0x33, 0x33, 0x91, 0x02, 0x05, 0x05 }; static unsigned char get_input_892[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x91, 0x02, 0x05, 0x05, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char get_input_893[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x33, 0x33, 0x33, 0x33, 0x33, 0x91, 0x02, 0x05, 0x05 }; static unsigned char get_input_8102[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0C, 0x04, 0x45, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x32, 0x32, 0x32, 0x32, 0x32, 0x91, 0x02, 0x05, 0x05 }; static struct get_input_test get_input_data_111 = { .pdu = get_input_111, .pdu_len = sizeof(get_input_111), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_121 = { .pdu = get_input_121, .pdu_len = sizeof(get_input_121), .qualifier = 0x08, .text = "Enter 67*#+", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_131 = { .pdu = get_input_131, .pdu_len = sizeof(get_input_131), .qualifier = 0x01, .text = "Enter AbCdE", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_141 = { .pdu = get_input_141, .pdu_len = sizeof(get_input_141), .qualifier = 0x04, .text = "Password 12345678", .resp_len = { .min = 4, .max = 8 } }; static struct get_input_test get_input_data_151 = { .pdu = get_input_151, .pdu_len = sizeof(get_input_151), .qualifier = 0x00, .text = "Enter 1..9,0..9,0(1)", .resp_len = { .min = 1, .max = 20 } }; static struct get_input_test get_input_data_161 = { .pdu = get_input_161, .pdu_len = sizeof(get_input_161), .qualifier = 0x00, .text = "", .resp_len = { .min = 0, .max = 8 } }; static struct get_input_test get_input_data_171 = { .pdu = get_input_171, .pdu_len = sizeof(get_input_171), .qualifier = 0x00, .text = "", .resp_len = { .min = 0, .max = 8 } }; static struct get_input_test get_input_data_181 = { .pdu = get_input_181, .pdu_len = sizeof(get_input_181), .qualifier = 0x00, .text = "***1111111111###***2222222222###***3333333333###" "***4444444444###***5555555555###***6666666666###" "***7777777777###***8888888888###***9999999999###" "***0000000000###", .resp_len = { .min = 160, .max = 160 } }; static struct get_input_test get_input_data_191 = { .pdu = get_input_191, .pdu_len = sizeof(get_input_191), .qualifier = 0x00, .text = "", .resp_len = { .min = 0, .max = 1 } }; static struct get_input_test get_input_data_1101 = { .pdu = get_input_1101, .pdu_len = sizeof(get_input_1101), .qualifier = 0x00, .text = "", .resp_len = { .min = 1, .max = 5 } }; static struct get_input_test get_input_data_211 = { .pdu = get_input_211, .pdu_len = sizeof(get_input_211), .qualifier = 0x00, .text = "", .resp_len = { .min = 0, .max = 10 } }; static struct get_input_test get_input_data_311 = { .pdu = get_input_311, .pdu_len = sizeof(get_input_311), .qualifier = 0x01, .text = "ЗДРАВСТВУЙТЕ", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_321 = { .pdu = get_input_321, .pdu_len = sizeof(get_input_321), .qualifier = 0x01, .text = "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙ", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_411 = { .pdu = get_input_411, .pdu_len = sizeof(get_input_411), .qualifier = 0x03, .text = "Enter Hello", .resp_len = { .min = 12, .max = 12 } }; static struct get_input_test get_input_data_421 = { .pdu = get_input_421, .pdu_len = sizeof(get_input_421), .qualifier = 0x03, .text = "Enter Hello", .resp_len = { .min = 5, .max = 0xFF } }; static struct get_input_test get_input_data_511 = { .pdu = get_input_511, .pdu_len = sizeof(get_input_511), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .default_text = "12345" }; static struct get_input_test get_input_data_521 = { .pdu = get_input_521, .pdu_len = sizeof(get_input_521), .qualifier = 0x00, .text = "Enter:", .resp_len = { .min = 160, .max = 160 }, .default_text = "***1111111111###***2222222222###***3333333333###" "***4444444444###***5555555555###***6666666666###" "***7777777777###***8888888888###***9999999999###" "***0000000000###" }; static struct get_input_test get_input_data_611 = { .pdu = get_input_611, .pdu_len = sizeof(get_input_611), .qualifier = 0x00, .text = "", .resp_len = { .min = 0, .max = 10 }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct get_input_test get_input_data_621 = { .pdu = get_input_621, .pdu_len = sizeof(get_input_621), .qualifier = 0x00, .text = "", .resp_len = { .min = 0, .max = 10 }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct get_input_test get_input_data_631 = { .pdu = get_input_631, .pdu_len = sizeof(get_input_631), .qualifier = 0x00, .text = "", .resp_len = { .min = 0, .max = 10 }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x02 } }; static struct get_input_test get_input_data_641 = { .pdu = get_input_641, .pdu_len = sizeof(get_input_641), .qualifier = 0x00, .text = "", .resp_len = { .min = 0, .max = 10 }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x02 } }; static struct get_input_test get_input_data_711 = { .pdu = get_input_711, .pdu_len = sizeof(get_input_711), .qualifier = 0x80, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_811 = { .pdu = get_input_811, .pdu_len = sizeof(get_input_811), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } }, .html = "
Enter 12345
" }; static struct get_input_test get_input_data_812 = { .pdu = get_input_812, .pdu_len = sizeof(get_input_812), .qualifier = 0x00, .text = "Enter 22222", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_821 = { .pdu = get_input_821, .pdu_len = sizeof(get_input_821), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x01, 0xB4 } }, .html = "
Enter 12345" "
", }; static struct get_input_test get_input_data_822 = { .pdu = get_input_822, .pdu_len = sizeof(get_input_822), .qualifier = 0x00, .text = "Enter 22222", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_831 = { .pdu = get_input_831, .pdu_len = sizeof(get_input_831), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x02, 0xB4 } }, .html = "
Enter 12345" "
", }; static struct get_input_test get_input_data_832 = { .pdu = get_input_832, .pdu_len = sizeof(get_input_832), .qualifier = 0x00, .text = "Enter 22222", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_841 = { .pdu = get_input_841, .pdu_len = sizeof(get_input_841), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x04, 0xB4 } }, .html = "
Enter 12345" "
", }; static struct get_input_test get_input_data_842 = { .pdu = get_input_842, .pdu_len = sizeof(get_input_842), .qualifier = 0x00, .text = "Enter 22222", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } }, .html = "
Enter 22222
" }; static struct get_input_test get_input_data_843 = { .pdu = get_input_843, .pdu_len = sizeof(get_input_843), .qualifier = 0x00, .text = "Enter 33333", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_851 = { .pdu = get_input_851, .pdu_len = sizeof(get_input_851), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x08, 0xB4 } }, .html = "
Enter " "12345
", }; static struct get_input_test get_input_data_852 = { .pdu = get_input_852, .pdu_len = sizeof(get_input_852), .qualifier = 0x00, .text = "Enter 22222", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } }, .html = "
Enter 22222
", }; static struct get_input_test get_input_data_853 = { .pdu = get_input_853, .pdu_len = sizeof(get_input_853), .qualifier = 0x00, .text = "Enter 33333", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_861 = { .pdu = get_input_861, .pdu_len = sizeof(get_input_861), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x10, 0xB4 } }, .html = "
Enter " "12345
" }; static struct get_input_test get_input_data_862 = { .pdu = get_input_862, .pdu_len = sizeof(get_input_862), .qualifier = 0x00, .text = "Enter 22222", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } }, .html = "
Enter 22222
", }; static struct get_input_test get_input_data_863 = { .pdu = get_input_863, .pdu_len = sizeof(get_input_863), .qualifier = 0x00, .text = "Enter 33333", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_871 = { .pdu = get_input_871, .pdu_len = sizeof(get_input_871), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x20, 0xB4 } }, .html = "
Enter " "12345
", }; static struct get_input_test get_input_data_872 = { .pdu = get_input_872, .pdu_len = sizeof(get_input_872), .qualifier = 0x00, .text = "Enter 22222", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } }, .html = "
Enter 22222
", }; static struct get_input_test get_input_data_873 = { .pdu = get_input_873, .pdu_len = sizeof(get_input_873), .qualifier = 0x00, .text = "Enter 33333", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_881 = { .pdu = get_input_881, .pdu_len = sizeof(get_input_881), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x40, 0xB4 } }, .html = "
Enter 12345
", }; static struct get_input_test get_input_data_882 = { .pdu = get_input_882, .pdu_len = sizeof(get_input_882), .qualifier = 0x00, .text = "Enter 22222", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } }, .html = "
Enter 22222
", }; static struct get_input_test get_input_data_883 = { .pdu = get_input_883, .pdu_len = sizeof(get_input_883), .qualifier = 0x00, .text = "Enter 33333", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_891 = { .pdu = get_input_891, .pdu_len = sizeof(get_input_891), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x80, 0xB4 } }, .html = "
Enter 12345
", }; static struct get_input_test get_input_data_892 = { .pdu = get_input_892, .pdu_len = sizeof(get_input_892), .qualifier = 0x00, .text = "Enter 22222", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } }, .html = "
Enter 22222
", }; static struct get_input_test get_input_data_893 = { .pdu = get_input_893, .pdu_len = sizeof(get_input_893), .qualifier = 0x00, .text = "Enter 33333", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_8101 = { .pdu = get_input_8101, .pdu_len = sizeof(get_input_8101), .qualifier = 0x00, .text = "Enter 12345", .resp_len = { .min = 5, .max = 5 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } }, .html = "
Enter 12345
", }; static struct get_input_test get_input_data_8102 = { .pdu = get_input_8102, .pdu_len = sizeof(get_input_8102), .qualifier = 0x00, .text = "Enter 22222", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_911 = { .pdu = get_input_911, .pdu_len = sizeof(get_input_911), .qualifier = 0x01, .text = "你好", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_921 = { .pdu = get_input_921, .pdu_len = sizeof(get_input_921), .qualifier = 0x01, .text = "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好你好你好你好你好你好" "你好你好你好你好你好", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_1011 = { .pdu = get_input_1011, .pdu_len = sizeof(get_input_1011), .qualifier = 0x03, .text = "Enter Hello", .resp_len = { .min = 2, .max = 2 } }; static struct get_input_test get_input_data_1021 = { .pdu = get_input_1021, .pdu_len = sizeof(get_input_1021), .qualifier = 0x03, .text = "Enter Hello", .resp_len = { .min = 5, .max = 0xFF } }; static struct get_input_test get_input_data_1111 = { .pdu = get_input_1111, .pdu_len = sizeof(get_input_1111), .qualifier = 0x01, .text = "ル", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_1121 = { .pdu = get_input_1121, .pdu_len = sizeof(get_input_1121), .qualifier = 0x01, .text = "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルルルルルルルルルルルル" "ルルルルルルルルルル", .resp_len = { .min = 5, .max = 5 } }; static struct get_input_test get_input_data_1211 = { .pdu = get_input_1211, .pdu_len = sizeof(get_input_1211), .qualifier = 0x03, .text = "Enter Hello", .resp_len = { .min = 2, .max = 2 } }; static struct get_input_test get_input_data_1221 = { .pdu = get_input_1221, .pdu_len = sizeof(get_input_1221), .qualifier = 0x03, .text = "Enter Hello", .resp_len = { .min = 5, .max = 0xFF } }; /* Defined in TS 102.384 Section 27.22.4.3 */ static void test_get_input(gconstpointer data) { const struct get_input_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_GET_INPUT); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); if (test->text) g_assert(command->get_input.text); check_text(command->get_input.text, test->text); check_response_length(&command->get_input.resp_len, &test->resp_len); check_default_text(command->get_input.default_text, test->default_text); check_icon_id(&command->get_input.icon_id, &test->icon_id); check_text_attr(&command->get_input.text_attr, &test->text_attr); check_text_attr_html(&command->get_input.text_attr, command->get_input.text, test->html); check_frame_id(&command->get_input.frame_id, &test->frame_id); stk_command_free(command); } struct more_time_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; }; static struct more_time_test more_time_data_111 = { .pdu = more_time_111, .pdu_len = sizeof(more_time_111), .qualifier = 0x00, }; /* Defined in TS 102.384 Section 27.22.4.4 */ static void test_more_time(gconstpointer data) { const struct get_input_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_MORE_TIME); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); stk_command_free(command); } struct play_tone_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *alpha_id; unsigned char tone; struct stk_duration duration; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; char *html; }; static struct play_tone_test play_tone_data_111 = { .pdu = play_tone_111, .pdu_len = sizeof(play_tone_111), .qualifier = 0x00, .alpha_id = "Dial Tone", .tone = STK_TONE_TYPE_DIAL_TONE, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; static struct play_tone_test play_tone_data_112 = { .pdu = play_tone_112, .pdu_len = sizeof(play_tone_112), .qualifier = 0x00, .alpha_id = "Sub. Busy", .tone = STK_TONE_TYPE_BUSY_TONE, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; static struct play_tone_test play_tone_data_113 = { .pdu = play_tone_113, .pdu_len = sizeof(play_tone_113), .qualifier = 0x00, .alpha_id = "Congestion", .tone = STK_TONE_TYPE_CONGESTION, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; static struct play_tone_test play_tone_data_114 = { .pdu = play_tone_114, .pdu_len = sizeof(play_tone_114), .qualifier = 0x00, .alpha_id = "RP Ack", .tone = STK_TONE_TYPE_RP_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; static struct play_tone_test play_tone_data_115 = { .pdu = play_tone_115, .pdu_len = sizeof(play_tone_115), .qualifier = 0x00, .alpha_id = "No RP", .tone = STK_TONE_TYPE_CALL_DROPPED, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; static struct play_tone_test play_tone_data_116 = { .pdu = play_tone_116, .pdu_len = sizeof(play_tone_116), .qualifier = 0x00, .alpha_id = "Spec Info", .tone = STK_TONE_TYPE_ERROR, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; static struct play_tone_test play_tone_data_117 = { .pdu = play_tone_117, .pdu_len = sizeof(play_tone_117), .qualifier = 0x00, .alpha_id = "Call Wait", .tone = STK_TONE_TYPE_CALL_WAITING, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; static struct play_tone_test play_tone_data_118 = { .pdu = play_tone_118, .pdu_len = sizeof(play_tone_118), .qualifier = 0x00, .alpha_id = "Ring Tone", .tone = STK_TONE_TYPE_RINGING, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; static struct play_tone_test play_tone_data_119 = { .pdu = play_tone_119, .pdu_len = sizeof(play_tone_119), .qualifier = 0x00, .alpha_id = "This command instructs the ME to play an audio tone. " "Upon receiving this command, the ME shall check " "if it is currently in, or in the process of setting " "up (SET-UP message sent to the network, see " "GSM\"04.08\"(8)), a speech call. - If the ME I" }; static struct play_tone_test play_tone_data_1110 = { .pdu = play_tone_1110, .pdu_len = sizeof(play_tone_1110), .qualifier = 0x00, .alpha_id = "Beep", .tone = STK_TONE_TYPE_GENERAL_BEEP, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_1111 = { .pdu = play_tone_1111, .pdu_len = sizeof(play_tone_1111), .qualifier = 0x00, .alpha_id = "Positive", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_1112 = { .pdu = play_tone_1112, .pdu_len = sizeof(play_tone_1112), .qualifier = 0x00, .alpha_id = "Negative", .tone = STK_TONE_TYPE_NEGATIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_1113 = { .pdu = play_tone_1113, .pdu_len = sizeof(play_tone_1113), .qualifier = 0x00, .alpha_id = "Quick", .tone = STK_TONE_TYPE_GENERAL_BEEP, .duration = { .unit = STK_DURATION_TYPE_SECOND_TENTHS, .interval = 2 } }; static struct play_tone_test play_tone_data_1114 = { .pdu = play_tone_1114, .pdu_len = sizeof(play_tone_1114), .qualifier = 0x00, .alpha_id = "", .tone = STK_TONE_TYPE_ERROR, .duration = { .unit = STK_DURATION_TYPE_MINUTES, .interval = 1 } }; static struct play_tone_test play_tone_data_1115 = { .pdu = play_tone_1115, .pdu_len = sizeof(play_tone_1115), .qualifier = 0x00 }; static struct play_tone_test play_tone_data_211 = { .pdu = play_tone_211, .pdu_len = sizeof(play_tone_211), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_212 = { .pdu = play_tone_212, .pdu_len = sizeof(play_tone_212), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_213 = { .pdu = play_tone_213, .pdu_len = sizeof(play_tone_213), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_311 = { .pdu = play_tone_311, .pdu_len = sizeof(play_tone_311), .qualifier = 0x00, .alpha_id = "", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct play_tone_test play_tone_data_321 = { .pdu = play_tone_321, .pdu_len = sizeof(play_tone_321), .qualifier = 0x00, .alpha_id = "", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct play_tone_test play_tone_data_331 = { .pdu = play_tone_331, .pdu_len = sizeof(play_tone_331), .qualifier = 0x00, .alpha_id = "", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x02 } }; static struct play_tone_test play_tone_data_341 = { .pdu = play_tone_341, .pdu_len = sizeof(play_tone_341), .qualifier = 0x00, .alpha_id = "", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x02 } }; static struct play_tone_test play_tone_data_411 = { .pdu = play_tone_411, .pdu_len = sizeof(play_tone_411), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Text Attribute 1" "
", }; static struct play_tone_test play_tone_data_412 = { .pdu = play_tone_412, .pdu_len = sizeof(play_tone_412), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_421 = { .pdu = play_tone_421, .pdu_len = sizeof(play_tone_421), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x01, 0xB4 } }, .html = "
Text Attribute 1" "
", }; static struct play_tone_test play_tone_data_422 = { .pdu = play_tone_422, .pdu_len = sizeof(play_tone_422), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_431 = { .pdu = play_tone_431, .pdu_len = sizeof(play_tone_431), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x02, 0xB4 } }, .html = "
Text Attribute 1" "
", }; static struct play_tone_test play_tone_data_432 = { .pdu = play_tone_432, .pdu_len = sizeof(play_tone_432), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_441 = { .pdu = play_tone_441, .pdu_len = sizeof(play_tone_441), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x04, 0xB4 } }, .html = "
" "Text Attribute 1
", }; static struct play_tone_test play_tone_data_442 = { .pdu = play_tone_442, .pdu_len = sizeof(play_tone_442), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Text Attribute 2" "
", }; static struct play_tone_test play_tone_data_443 = { .pdu = play_tone_443, .pdu_len = sizeof(play_tone_443), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_451 = { .pdu = play_tone_451, .pdu_len = sizeof(play_tone_451), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x08, 0xB4 } }, .html = "
" "Text Attribute 1
", }; static struct play_tone_test play_tone_data_452 = { .pdu = play_tone_452, .pdu_len = sizeof(play_tone_452), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Text Attribute 2" "
", }; static struct play_tone_test play_tone_data_453 = { .pdu = play_tone_453, .pdu_len = sizeof(play_tone_453), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_461 = { .pdu = play_tone_461, .pdu_len = sizeof(play_tone_461), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x10, 0xB4 } }, .html = "
" "Text Attribute
1" }; static struct play_tone_test play_tone_data_462 = { .pdu = play_tone_462, .pdu_len = sizeof(play_tone_462), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Text Attribute 2" "
", }; static struct play_tone_test play_tone_data_463 = { .pdu = play_tone_463, .pdu_len = sizeof(play_tone_463), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_471 = { .pdu = play_tone_471, .pdu_len = sizeof(play_tone_471), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x20, 0xB4 } }, .html = "
" "Text Attribute
1", }; static struct play_tone_test play_tone_data_472 = { .pdu = play_tone_472, .pdu_len = sizeof(play_tone_472), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Text Attribute 2" "
", }; static struct play_tone_test play_tone_data_473 = { .pdu = play_tone_473, .pdu_len = sizeof(play_tone_473), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_481 = { .pdu = play_tone_481, .pdu_len = sizeof(play_tone_481), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x40, 0xB4 } }, .html = "
Text Attribute 1
", }; static struct play_tone_test play_tone_data_482 = { .pdu = play_tone_482, .pdu_len = sizeof(play_tone_482), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Text Attribute 2" "
", }; static struct play_tone_test play_tone_data_483 = { .pdu = play_tone_483, .pdu_len = sizeof(play_tone_483), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_491 = { .pdu = play_tone_491, .pdu_len = sizeof(play_tone_491), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x80, 0xB4 } }, .html = "
Text Attribute 1
", }; static struct play_tone_test play_tone_data_492 = { .pdu = play_tone_492, .pdu_len = sizeof(play_tone_492), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Text Attribute 2" "
", }; static struct play_tone_test play_tone_data_493 = { .pdu = play_tone_493, .pdu_len = sizeof(play_tone_493), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_4101 = { .pdu = play_tone_4101, .pdu_len = sizeof(play_tone_4101), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Text Attribute 1" "
", }; static struct play_tone_test play_tone_data_4102 = { .pdu = play_tone_4102, .pdu_len = sizeof(play_tone_4102), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_511 = { .pdu = play_tone_511, .pdu_len = sizeof(play_tone_511), .qualifier = 0x00, .alpha_id = "中一", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_512 = { .pdu = play_tone_512, .pdu_len = sizeof(play_tone_512), .qualifier = 0x00, .alpha_id = "中一", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_513 = { .pdu = play_tone_513, .pdu_len = sizeof(play_tone_513), .qualifier = 0x00, .alpha_id = "中一", .tone = STK_TONE_TYPE_POSITIVE_ACK, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 1 } }; static struct play_tone_test play_tone_data_611 = { .pdu = play_tone_611, .pdu_len = sizeof(play_tone_611), .qualifier = 0x00, .alpha_id = "80ル0", .tone = STK_TONE_TYPE_DIAL_TONE, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; static struct play_tone_test play_tone_data_612 = { .pdu = play_tone_612, .pdu_len = sizeof(play_tone_612), .qualifier = 0x00, .alpha_id = "81ル1", .tone = STK_TONE_TYPE_DIAL_TONE, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; static struct play_tone_test play_tone_data_613 = { .pdu = play_tone_613, .pdu_len = sizeof(play_tone_613), .qualifier = 0x00, .alpha_id = "82ル2", .tone = STK_TONE_TYPE_DIAL_TONE, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 5 } }; /* Defined in TS 102.384 Section 27.22.4.5 */ static void test_play_tone(gconstpointer data) { const struct play_tone_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_PLAY_TONE); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_EARPIECE); check_alpha_id(command->play_tone.alpha_id, test->alpha_id); check_tone(command->play_tone.tone, test->tone); check_duration(&command->play_tone.duration, &test->duration); check_icon_id(&command->play_tone.icon_id, &test->icon_id); check_text_attr(&command->play_tone.text_attr, &test->text_attr); check_text_attr_html(&command->play_tone.text_attr, command->play_tone.alpha_id, test->html); check_frame_id(&command->play_tone.frame_id, &test->frame_id); stk_command_free(command); } struct poll_interval_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; struct stk_duration duration; }; static struct poll_interval_test poll_interval_data_111 = { .pdu = poll_interval_111, .pdu_len = sizeof(poll_interval_111), .qualifier = 0x00, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 20 } }; /* Defined in TS 102.384 Section 27.22.4.6 */ static void test_poll_interval(gconstpointer data) { const struct poll_interval_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_POLL_INTERVAL); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_duration(&command->poll_interval.duration, &test->duration); stk_command_free(command); } struct setup_menu_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *alpha_id; struct stk_item items[MAX_ITEM]; struct stk_items_next_action_indicator next_act; struct stk_icon_id icon_id; struct stk_item_icon_id_list item_icon_id_list; struct stk_text_attribute text_attr; struct stk_item_text_attribute_list item_text_attr_list; char *html; }; static unsigned char setup_menu_111[] = { 0xD0, 0x3B, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0C, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x04, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34 }; static unsigned char setup_menu_112[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0C, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x8F, 0x04, 0x11, 0x4F, 0x6E, 0x65, 0x8F, 0x04, 0x12, 0x54, 0x77, 0x6F }; static unsigned char setup_menu_113[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x00, 0x8F, 0x00 }; static unsigned char setup_menu_121[] = { 0xD0, 0x81, 0xFC, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0A, 0x4C, 0x61, 0x72, 0x67, 0x65, 0x4D, 0x65, 0x6E, 0x75, 0x31, 0x8F, 0x05, 0x50, 0x5A, 0x65, 0x72, 0x6F, 0x8F, 0x04, 0x4F, 0x4F, 0x6E, 0x65, 0x8F, 0x04, 0x4E, 0x54, 0x77, 0x6F, 0x8F, 0x06, 0x4D, 0x54, 0x68, 0x72, 0x65, 0x65, 0x8F, 0x05, 0x4C, 0x46, 0x6F, 0x75, 0x72, 0x8F, 0x05, 0x4B, 0x46, 0x69, 0x76, 0x65, 0x8F, 0x04, 0x4A, 0x53, 0x69, 0x78, 0x8F, 0x06, 0x49, 0x53, 0x65, 0x76, 0x65, 0x6E, 0x8F, 0x06, 0x48, 0x45, 0x69, 0x67, 0x68, 0x74, 0x8F, 0x05, 0x47, 0x4E, 0x69, 0x6E, 0x65, 0x8F, 0x06, 0x46, 0x41, 0x6C, 0x70, 0x68, 0x61, 0x8F, 0x06, 0x45, 0x42, 0x72, 0x61, 0x76, 0x6F, 0x8F, 0x08, 0x44, 0x43, 0x68, 0x61, 0x72, 0x6C, 0x69, 0x65, 0x8F, 0x06, 0x43, 0x44, 0x65, 0x6C, 0x74, 0x61, 0x8F, 0x05, 0x42, 0x45, 0x63, 0x68, 0x6F, 0x8F, 0x09, 0x41, 0x46, 0x6F, 0x78, 0x2D, 0x74, 0x72, 0x6F, 0x74, 0x8F, 0x06, 0x40, 0x42, 0x6C, 0x61, 0x63, 0x6B, 0x8F, 0x06, 0x3F, 0x42, 0x72, 0x6F, 0x77, 0x6E, 0x8F, 0x04, 0x3E, 0x52, 0x65, 0x64, 0x8F, 0x07, 0x3D, 0x4F, 0x72, 0x61, 0x6E, 0x67, 0x65, 0x8F, 0x07, 0x3C, 0x59, 0x65, 0x6C, 0x6C, 0x6F, 0x77, 0x8F, 0x06, 0x3B, 0x47, 0x72, 0x65, 0x65, 0x6E, 0x8F, 0x05, 0x3A, 0x42, 0x6C, 0x75, 0x65, 0x8F, 0x07, 0x39, 0x56, 0x69, 0x6F, 0x6C, 0x65, 0x74, 0x8F, 0x05, 0x38, 0x47, 0x72, 0x65, 0x79, 0x8F, 0x06, 0x37, 0x57, 0x68, 0x69, 0x74, 0x65, 0x8F, 0x06, 0x36, 0x6D, 0x69, 0x6C, 0x6C, 0x69, 0x8F, 0x06, 0x35, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x8F, 0x05, 0x34, 0x6E, 0x61, 0x6E, 0x6F, 0x8F, 0x05, 0x33, 0x70, 0x69, 0x63, 0x6F }; static unsigned char setup_menu_122[] = { 0xD0, 0x81, 0xF3, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0A, 0x4C, 0x61, 0x72, 0x67, 0x65, 0x4D, 0x65, 0x6E, 0x75, 0x32, 0x8F, 0x1D, 0xFF, 0x31, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x20, 0x55, 0x6E, 0x63, 0x6F, 0x6E, 0x64, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x8F, 0x1C, 0xFE, 0x32, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x20, 0x4F, 0x6E, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x42, 0x75, 0x73, 0x79, 0x8F, 0x1B, 0xFD, 0x33, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x20, 0x4F, 0x6E, 0x20, 0x4E, 0x6F, 0x20, 0x52, 0x65, 0x70, 0x6C, 0x79, 0x8F, 0x25, 0xFC, 0x34, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x20, 0x4F, 0x6E, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x4E, 0x6F, 0x74, 0x20, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6C, 0x65, 0x8F, 0x20, 0xFB, 0x35, 0x20, 0x42, 0x61, 0x72, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x4F, 0x66, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x4F, 0x75, 0x74, 0x67, 0x6F, 0x69, 0x6E, 0x67, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x73, 0x8F, 0x24, 0xFA, 0x36, 0x20, 0x42, 0x61, 0x72, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x4F, 0x66, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x4F, 0x75, 0x74, 0x67, 0x6F, 0x69, 0x6E, 0x67, 0x20, 0x49, 0x6E, 0x74, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x73, 0x8F, 0x13, 0xF9, 0x37, 0x20, 0x43, 0x4C, 0x49, 0x20, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6E, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E }; static unsigned char setup_menu_123[] = { 0xD0, 0x81, 0xFC, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x81, 0xEC, 0x54, 0x68, 0x65, 0x20, 0x53, 0x49, 0x4D, 0x20, 0x73, 0x68, 0x61, 0x6C, 0x6C, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6C, 0x79, 0x20, 0x61, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6F, 0x66, 0x20, 0x6D, 0x65, 0x6E, 0x75, 0x20, 0x69, 0x74, 0x65, 0x6D, 0x73, 0x2C, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x73, 0x68, 0x61, 0x6C, 0x6C, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6D, 0x65, 0x6E, 0x75, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x20, 0x28, 0x6F, 0x72, 0x20, 0x6F, 0x74, 0x68, 0x65, 0x72, 0x20, 0x4D, 0x4D, 0x49, 0x20, 0x66, 0x61, 0x63, 0x69, 0x6C, 0x69, 0x74, 0x79, 0x29, 0x20, 0x69, 0x6E, 0x20, 0x6F, 0x72, 0x64, 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x67, 0x69, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6F, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x69, 0x74, 0x79, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x68, 0x6F, 0x6F, 0x73, 0x65, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x6D, 0x65, 0x6E, 0x75, 0x20, 0x69, 0x74, 0x65, 0x6D, 0x73, 0x20, 0x61, 0x74, 0x20, 0x68, 0x69, 0x73, 0x20, 0x6F, 0x77, 0x6E, 0x20, 0x64, 0x69, 0x73, 0x63, 0x72, 0x65, 0x74, 0x69, 0x6F, 0x6E, 0x2E, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x72, 0x69, 0x73, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x68, 0x8F, 0x02, 0x01, 0x59 }; static unsigned char setup_menu_211[] = { 0xD0, 0x3B, 0x81, 0x03, 0x01, 0x25, 0x80, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0C, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x04, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34 }; static unsigned char setup_menu_311[] = { 0xD0, 0x41, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0C, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x04, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0x18, 0x04, 0x13, 0x10, 0x15, 0x26 }; static unsigned char setup_menu_411[] = { 0xD0, 0x3C, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0C, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x9E, 0x02, 0x01, 0x01, 0x9F, 0x04, 0x01, 0x05, 0x05, 0x05 }; static unsigned char setup_menu_421[] = { 0xD0, 0x3C, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0C, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x9E, 0x02, 0x00, 0x01, 0x9F, 0x04, 0x00, 0x05, 0x05, 0x05 }; static unsigned char setup_menu_511[] = { 0xD0, 0x29, 0x81, 0x03, 0x01, 0x25, 0x01, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0C, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32 }; static unsigned char setup_menu_611[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char setup_menu_612[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x32, 0x8F, 0x07, 0x04, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0x8F, 0x07, 0x05, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x35, 0x8F, 0x07, 0x06, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x36 }; static unsigned char setup_menu_621[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0xD0, 0x04, 0x00, 0x0E, 0x01, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x01, 0xB4, 0x00, 0x06, 0x01, 0xB4, 0x00, 0x06, 0x01, 0xB4 }; static unsigned char setup_menu_622[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x32, 0x8F, 0x07, 0x04, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0x8F, 0x07, 0x05, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x35, 0x8F, 0x07, 0x06, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x36 }; static unsigned char setup_menu_631[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0xD0, 0x04, 0x00, 0x0E, 0x02, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x02, 0xB4, 0x00, 0x06, 0x02, 0xB4, 0x00, 0x06, 0x02, 0xB4 }; static unsigned char setup_menu_632[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x32, 0x8F, 0x07, 0x04, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0x8F, 0x07, 0x05, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x35, 0x8F, 0x07, 0x06, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x36 }; static unsigned char setup_menu_641[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0xD0, 0x04, 0x00, 0x0E, 0x04, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x04, 0xB4, 0x00, 0x06, 0x04, 0xB4, 0x00, 0x06, 0x04, 0xB4 }; static unsigned char setup_menu_642[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x32, 0x8F, 0x07, 0x04, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0x8F, 0x07, 0x05, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x35, 0x8F, 0x07, 0x06, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x36, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char setup_menu_643[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x33, 0x8F, 0x07, 0x07, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x37, 0x8F, 0x07, 0x08, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x38, 0x8F, 0x07, 0x09, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x39 }; static unsigned char setup_menu_651[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0xD0, 0x04, 0x00, 0x0E, 0x08, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x08, 0xB4, 0x00, 0x06, 0x08, 0xB4, 0x00, 0x06, 0x08, 0xB4 }; static unsigned char setup_menu_661[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0xD0, 0x04, 0x00, 0x0E, 0x10, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x10, 0xB4, 0x00, 0x06, 0x10, 0xB4, 0x00, 0x06, 0x10, 0xB4 }; static unsigned char setup_menu_671[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0xD0, 0x04, 0x00, 0x0E, 0x20, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x20, 0xB4, 0x00, 0x06, 0x20, 0xB4, 0x00, 0x06, 0x20, 0xB4 }; static unsigned char setup_menu_681[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0xD0, 0x04, 0x00, 0x0E, 0x40, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x40, 0xB4, 0x00, 0x06, 0x40, 0xB4, 0x00, 0x06, 0x40, 0xB4 }; static unsigned char setup_menu_691[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0xD0, 0x04, 0x00, 0x0E, 0x80, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x80, 0xB4, 0x00, 0x06, 0x80, 0xB4, 0x00, 0x06, 0x80, 0xB4 }; static unsigned char setup_menu_6101[] = { 0xD0, 0x46, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0C, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x4D, 0x65, 0x6E, 0x75, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0xD0, 0x04, 0x00, 0x0C, 0x00, 0xB4, 0xD1, 0x0C, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char setup_menu_711[] = { 0xD0, 0x81, 0x9C, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x8F, 0x1C, 0x01, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x31, 0x8F, 0x1C, 0x02, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x32, 0x8F, 0x1C, 0x03, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x33, 0x8F, 0x1C, 0x04, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x34 }; static unsigned char setup_menu_712[] = { 0xD0, 0x60, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x8F, 0x1C, 0x11, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x35, 0x8F, 0x1C, 0x12, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x36 }; static unsigned char setup_menu_713[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x00, 0x8F, 0x00 }; static unsigned char setup_menu_811[] = { 0xD0, 0x3C, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x09, 0x80, 0x5D, 0xE5, 0x51, 0x77, 0x7B, 0xB1, 0x53, 0x55, 0x8F, 0x08, 0x01, 0x80, 0x98, 0x79, 0x76, 0xEE, 0x4E, 0x00, 0x8F, 0x08, 0x02, 0x80, 0x98, 0x79, 0x76, 0xEE, 0x4E, 0x8C, 0x8F, 0x08, 0x03, 0x80, 0x98, 0x79, 0x76, 0xEE, 0x4E, 0x09, 0x8F, 0x08, 0x04, 0x80, 0x98, 0x79, 0x76, 0xEE, 0x56, 0xDB }; static unsigned char setup_menu_812[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x09, 0x80, 0x5D, 0xE5, 0x51, 0x77, 0x7B, 0xB1, 0x53, 0x55, 0x8F, 0x04, 0x11, 0x80, 0x4E, 0x00, 0x8F, 0x04, 0x12, 0x80, 0x4E, 0x8C }; static unsigned char setup_menu_813[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x00, 0x8F, 0x00 }; static unsigned char setup_menu_911[] = { 0xD0, 0x44, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x09, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x30, 0x8F, 0x0A, 0x01, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x31, 0x8F, 0x0A, 0x02, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x32, 0x8F, 0x0A, 0x03, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x33, 0x8F, 0x0A, 0x04, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x34 }; static unsigned char setup_menu_912[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x09, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x30, 0x8F, 0x0A, 0x11, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x35, 0x8F, 0x0A, 0x12, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x36 }; static unsigned char setup_menu_913[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x00, 0x8F, 0x00 }; /* Negative case: No item is present */ static unsigned char setup_menu_neg_1[] = { 0xD0, 0x0B, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x00 }; /* Negative case: Two empty items*/ static unsigned char setup_menu_neg_2[] = { 0xD0, 0x0F, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x00, 0x8F, 0x00, 0x8F, 0x00 }; /* Negative case: valid item + empty item */ static unsigned char setup_menu_neg_3[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x00, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x00 }; /* Negative case: empty item + valid item */ static unsigned char setup_menu_neg_4[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x00, 0x8F, 0x00, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31 }; static struct setup_menu_test setup_menu_data_111 = { .pdu = setup_menu_111, .pdu_len = sizeof(setup_menu_111), .qualifier = 0x00, .alpha_id = "Toolkit Menu", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, { .id = 4, .text = "Item 4" }, } }; static struct setup_menu_test setup_menu_data_112 = { .pdu = setup_menu_112, .pdu_len = sizeof(setup_menu_112), .qualifier = 0x00, .alpha_id = "Toolkit Menu", .items = { { .id = 0x11, .text = "One" }, { .id = 0x12, .text = "Two" }, } }; static struct setup_menu_test setup_menu_data_113 = { .pdu = setup_menu_113, .pdu_len = sizeof(setup_menu_113), .qualifier = 0x00, .alpha_id = "" }; static struct setup_menu_test setup_menu_data_121 = { .pdu = setup_menu_121, .pdu_len = sizeof(setup_menu_121), .qualifier = 0x00, .alpha_id = "LargeMenu1", .items = { { .id = 0x50, .text = "Zero" }, { .id = 0x4F, .text = "One" }, { .id = 0x4E, .text = "Two" }, { .id = 0x4D, .text = "Three" }, { .id = 0x4C, .text = "Four" }, { .id = 0x4B, .text = "Five" }, { .id = 0x4A, .text = "Six" }, { .id = 0x49, .text = "Seven" }, { .id = 0x48, .text = "Eight" }, { .id = 0x47, .text = "Nine" }, { .id = 0x46, .text = "Alpha" }, { .id = 0x45, .text = "Bravo" }, { .id = 0x44, .text = "Charlie" }, { .id = 0x43, .text = "Delta" }, { .id = 0x42, .text = "Echo" }, { .id = 0x41, .text = "Fox-trot" }, { .id = 0x40, .text = "Black" }, { .id = 0x3F, .text = "Brown" }, { .id = 0x3E, .text = "Red" }, { .id = 0x3D, .text = "Orange" }, { .id = 0x3C, .text = "Yellow" }, { .id = 0x3B, .text = "Green" }, { .id = 0x3A, .text = "Blue" }, { .id = 0x39, .text = "Violet" }, { .id = 0x38, .text = "Grey" }, { .id = 0x37, .text = "White" }, { .id = 0x36, .text = "milli" }, { .id = 0x35, .text = "micro" }, { .id = 0x34, .text = "nano" }, { .id = 0x33, .text = "pico" }, } }; static struct setup_menu_test setup_menu_data_122 = { .pdu = setup_menu_122, .pdu_len = sizeof(setup_menu_122), .qualifier = 0x00, .alpha_id = "LargeMenu2", .items = { { .id = 0xFF, .text = "1 Call Forward Unconditional" }, { .id = 0xFE, .text = "2 Call Forward On User Busy" }, { .id = 0xFD, .text = "3 Call Forward On No Reply" }, { .id = 0xFC, .text = "4 Call Forward On User Not Reachable" }, { .id = 0xFB, .text = "5 Barring Of All Outgoing Calls" }, { .id = 0xFA, .text = "6 Barring Of All Outgoing Int Calls" }, { .id = 0xF9, .text = "7 CLI Presentation" }, } }; static struct setup_menu_test setup_menu_data_123 = { .pdu = setup_menu_123, .pdu_len = sizeof(setup_menu_123), .qualifier = 0x00, .alpha_id = "The SIM shall supply a set of menu items, which shall " "be integrated with the menu system (or other MMI " "facility) in order to give the user the opportunity " "to choose one of these menu items at his own " "discretion. Each item comprises a sh", .items = { { .id = 0x01, .text = "Y" } } }; static struct setup_menu_test setup_menu_data_211 = { .pdu = setup_menu_211, .pdu_len = sizeof(setup_menu_211), .qualifier = 0x80, .alpha_id = "Toolkit Menu", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, { .id = 4, .text = "Item 4" }, } }; static struct setup_menu_test setup_menu_data_311 = { .pdu = setup_menu_311, .pdu_len = sizeof(setup_menu_311), .qualifier = 0x00, .alpha_id = "Toolkit Menu", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, { .id = 4, .text = "Item 4" }, }, .next_act = { .list = { STK_COMMAND_TYPE_SEND_SMS, STK_COMMAND_TYPE_SETUP_CALL, STK_COMMAND_TYPE_LAUNCH_BROWSER, STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO }, .len = 4 } }; static struct setup_menu_test setup_menu_data_411 = { .pdu = setup_menu_411, .pdu_len = sizeof(setup_menu_411), .qualifier = 0x00, .alpha_id = "Toolkit Menu", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 1 }, .item_icon_id_list = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .list = { 5, 5, 5 }, .len = 3 } }; static struct setup_menu_test setup_menu_data_421 = { .pdu = setup_menu_421, .pdu_len = sizeof(setup_menu_421), .qualifier = 0x00, .alpha_id = "Toolkit Menu", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 1 }, .item_icon_id_list = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .list = { 5, 5, 5 }, .len = 3 } }; static struct setup_menu_test setup_menu_data_511 = { .pdu = setup_menu_511, .pdu_len = sizeof(setup_menu_511), .qualifier = 0x01, .alpha_id = "Toolkit Menu", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, } }; static struct setup_menu_test setup_menu_data_611 = { .pdu = setup_menu_611, .pdu_len = sizeof(setup_menu_611), .qualifier = 0x00, .alpha_id = "Toolkit Menu 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Menu 1" "
", }; static struct setup_menu_test setup_menu_data_612 = { .pdu = setup_menu_612, .pdu_len = sizeof(setup_menu_612), .qualifier = 0x00, .alpha_id = "Toolkit Menu 2", .items = { { .id = 4, .text = "Item 4" }, { .id = 5, .text = "Item 5" }, { .id = 6, .text = "Item 6" }, } }; static struct setup_menu_test setup_menu_data_621 = { .pdu = setup_menu_621, .pdu_len = sizeof(setup_menu_621), .qualifier = 0x00, .alpha_id = "Toolkit Menu 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x01, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x01, 0xB4, 0x00, 0x06, 0x01, 0xB4, 0x00, 0x06, 0x01, 0xB4 } }, .html = "
Toolkit Menu 1" "
" }; static struct setup_menu_test setup_menu_data_622 = { .pdu = setup_menu_622, .pdu_len = sizeof(setup_menu_622), .qualifier = 0x00, .alpha_id = "Toolkit Menu 2", .items = { { .id = 4, .text = "Item 4" }, { .id = 5, .text = "Item 5" }, { .id = 6, .text = "Item 6" }, } }; /* * Some problem with data of item #3 in item_text_attr_list * and the explanation */ static struct setup_menu_test setup_menu_data_631 = { .pdu = setup_menu_631, .pdu_len = sizeof(setup_menu_631), .qualifier = 0x00, .alpha_id = "Toolkit Menu 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x02, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x02, 0xB4, 0x00, 0x06, 0x02, 0xB4, 0x00, 0x06, 0x02, 0xB4 } }, .html = "
Toolkit Menu 1" "
" }; static struct setup_menu_test setup_menu_data_632 = { .pdu = setup_menu_632, .pdu_len = sizeof(setup_menu_632), .qualifier = 0x00, .alpha_id = "Toolkit Menu 2", .items = { { .id = 4, .text = "Item 4" }, { .id = 5, .text = "Item 5" }, { .id = 6, .text = "Item 6" }, } }; static struct setup_menu_test setup_menu_data_641 = { .pdu = setup_menu_641, .pdu_len = sizeof(setup_menu_641), .qualifier = 0x00, .alpha_id = "Toolkit Menu 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x04, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x04, 0xB4, 0x00, 0x06, 0x04, 0xB4, 0x00, 0x06, 0x04, 0xB4 } }, .html = "
" "Toolkit Menu 1
", }; static struct setup_menu_test setup_menu_data_642 = { .pdu = setup_menu_642, .pdu_len = sizeof(setup_menu_642), .qualifier = 0x00, .alpha_id = "Toolkit Menu 2", .items = { { .id = 4, .text = "Item 4" }, { .id = 5, .text = "Item 5" }, { .id = 6, .text = "Item 6" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Menu 2" "
", }; static struct setup_menu_test setup_menu_data_643 = { .pdu = setup_menu_643, .pdu_len = sizeof(setup_menu_643), .qualifier = 0x00, .alpha_id = "Toolkit Menu 3", .items = { { .id = 7, .text = "Item 7" }, { .id = 8, .text = "Item 8" }, { .id = 9, .text = "Item 9" }, } }; static struct setup_menu_test setup_menu_data_651 = { .pdu = setup_menu_651, .pdu_len = sizeof(setup_menu_651), .qualifier = 0x00, .alpha_id = "Toolkit Menu 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x08, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x08, 0xB4, 0x00, 0x06, 0x08, 0xB4, 0x00, 0x06, 0x08, 0xB4 } }, .html = "
" "Toolkit Menu 1
", }; static struct setup_menu_test setup_menu_data_661 = { .pdu = setup_menu_661, .pdu_len = sizeof(setup_menu_661), .qualifier = 0x00, .alpha_id = "Toolkit Menu 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x10, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x10, 0xB4, 0x00, 0x06, 0x10, 0xB4, 0x00, 0x06, 0x10, 0xB4 } }, .html = "
" "Toolkit Menu 1
", }; static struct setup_menu_test setup_menu_data_671 = { .pdu = setup_menu_671, .pdu_len = sizeof(setup_menu_671), .qualifier = 0x00, .alpha_id = "Toolkit Menu 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x20, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x20, 0xB4, 0x00, 0x06, 0x20, 0xB4, 0x00, 0x06, 0x20, 0xB4 } }, .html = "
" "Toolkit Menu 1
" }; static struct setup_menu_test setup_menu_data_681 = { .pdu = setup_menu_681, .pdu_len = sizeof(setup_menu_681), .qualifier = 0x00, .alpha_id = "Toolkit Menu 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x40, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x40, 0xB4, 0x00, 0x06, 0x40, 0xB4, 0x00, 0x06, 0x40, 0xB4 } }, .html = "
Toolkit Menu 1
", }; static struct setup_menu_test setup_menu_data_691 = { .pdu = setup_menu_691, .pdu_len = sizeof(setup_menu_691), .qualifier = 0x00, .alpha_id = "Toolkit Menu 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x80, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x80, 0xB4, 0x00, 0x06, 0x80, 0xB4, 0x00, 0x06, 0x80, 0xB4 } }, .html = "
Toolkit Menu 1
", }; static struct setup_menu_test setup_menu_data_6101 = { .pdu = setup_menu_6101, .pdu_len = sizeof(setup_menu_6101), .qualifier = 0x00, .alpha_id = "Toolkit Menu", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0C, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 12, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Menu" "
", }; static struct setup_menu_test setup_menu_data_711 = { .pdu = setup_menu_711, .pdu_len = sizeof(setup_menu_711), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .items = { { .id = 1, .text = "ЗДРАВСТВУЙТЕ1" }, { .id = 2, .text = "ЗДРАВСТВУЙТЕ2" }, { .id = 3, .text = "ЗДРАВСТВУЙТЕ3" }, { .id = 4, .text = "ЗДРАВСТВУЙТЕ4" }, } }; static struct setup_menu_test setup_menu_data_712 = { .pdu = setup_menu_712, .pdu_len = sizeof(setup_menu_712), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .items = { { .id = 0x11, .text = "ЗДРАВСТВУЙТЕ5" }, { .id = 0x12, .text = "ЗДРАВСТВУЙТЕ6" }, } }; static struct setup_menu_test setup_menu_data_713 = { .pdu = setup_menu_713, .pdu_len = sizeof(setup_menu_713), .qualifier = 0x00, .alpha_id = "" }; static struct setup_menu_test setup_menu_data_811 = { .pdu = setup_menu_811, .pdu_len = sizeof(setup_menu_811), .qualifier = 0x00, .alpha_id = "工具箱单", .items = { { .id = 1, .text = "项目一" }, { .id = 2, .text = "项目二" }, { .id = 3, .text = "项目三" }, { .id = 4, .text = "项目四" }, } }; static struct setup_menu_test setup_menu_data_812 = { .pdu = setup_menu_812, .pdu_len = sizeof(setup_menu_812), .qualifier = 0x00, .alpha_id = "工具箱单", .items = { { .id = 0x11, .text = "一" }, { .id = 0x12, .text = "二" }, } }; static struct setup_menu_test setup_menu_data_813 = { .pdu = setup_menu_813, .pdu_len = sizeof(setup_menu_813), .qualifier = 0x00, .alpha_id = "" }; static struct setup_menu_test setup_menu_data_911 = { .pdu = setup_menu_911, .pdu_len = sizeof(setup_menu_911), .qualifier = 0x00, .alpha_id = "80ル0", .items = { { .id = 1, .text = "80ル1" }, { .id = 2, .text = "80ル2" }, { .id = 3, .text = "80ル3" }, { .id = 4, .text = "80ル4" }, } }; static struct setup_menu_test setup_menu_data_912 = { .pdu = setup_menu_912, .pdu_len = sizeof(setup_menu_912), .qualifier = 0x00, .alpha_id = "80ル0", .items = { { .id = 0x11, .text = "80ル5" }, { .id = 0x12, .text = "80ル6" }, } }; static struct setup_menu_test setup_menu_data_913 = { .pdu = setup_menu_913, .pdu_len = sizeof(setup_menu_913), .qualifier = 0x00, .alpha_id = "" }; static struct setup_menu_test setup_menu_data_neg_1 = { .pdu = setup_menu_neg_1, .pdu_len = sizeof(setup_menu_neg_1) }; static struct setup_menu_test setup_menu_data_neg_2 = { .pdu = setup_menu_neg_2, .pdu_len = sizeof(setup_menu_neg_2) }; static struct setup_menu_test setup_menu_data_neg_3 = { .pdu = setup_menu_neg_3, .pdu_len = sizeof(setup_menu_neg_3) }; static struct setup_menu_test setup_menu_data_neg_4 = { .pdu = setup_menu_neg_4, .pdu_len = sizeof(setup_menu_neg_4) }; /* Defined in TS 102.384 Section 27.22.4.7 */ static void test_setup_menu(gconstpointer data) { const struct setup_menu_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_SETUP_MENU); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_alpha_id(command->setup_menu.alpha_id, test->alpha_id); check_items(command->setup_menu.items, test->items); check_items_next_action_indicator(&command->setup_menu.next_act, &test->next_act); check_icon_id(&command->setup_menu.icon_id, &test->icon_id); check_item_icon_id_list(&command->setup_menu.item_icon_id_list, &test->item_icon_id_list); check_text_attr(&command->setup_menu.text_attr, &test->text_attr); check_item_text_attribute_list(&command->setup_menu.item_text_attr_list, &test->item_text_attr_list); check_text_attr_html(&command->setup_menu.text_attr, command->setup_menu.alpha_id, test->html); stk_command_free(command); } static void test_setup_menu_missing_val(gconstpointer data) { const struct setup_menu_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_MISSING_VALUE); stk_command_free(command); } static void test_setup_menu_neg(gconstpointer data) { const struct setup_menu_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD); stk_command_free(command); } struct select_item_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *alpha_id; struct stk_item items[MAX_ITEM]; struct stk_items_next_action_indicator next_act; unsigned char item_id; struct stk_icon_id icon_id; struct stk_item_icon_id_list item_icon_id_list; struct stk_text_attribute text_attr; struct stk_item_text_attribute_list item_text_attr_list; struct stk_frame_id frame_id; char *html; }; static unsigned char select_item_111[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x04, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34 }; static unsigned char select_item_121[] = { 0xD0, 0x81, 0xFC, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0A, 0x4C, 0x61, 0x72, 0x67, 0x65, 0x4D, 0x65, 0x6E, 0x75, 0x31, 0x8F, 0x05, 0x50, 0x5A, 0x65, 0x72, 0x6F, 0x8F, 0x04, 0x4F, 0x4F, 0x6E, 0x65, 0x8F, 0x04, 0x4E, 0x54, 0x77, 0x6F, 0x8F, 0x06, 0x4D, 0x54, 0x68, 0x72, 0x65, 0x65, 0x8F, 0x05, 0x4C, 0x46, 0x6F, 0x75, 0x72, 0x8F, 0x05, 0x4B, 0x46, 0x69, 0x76, 0x65, 0x8F, 0x04, 0x4A, 0x53, 0x69, 0x78, 0x8F, 0x06, 0x49, 0x53, 0x65, 0x76, 0x65, 0x6E, 0x8F, 0x06, 0x48, 0x45, 0x69, 0x67, 0x68, 0x74, 0x8F, 0x05, 0x47, 0x4E, 0x69, 0x6E, 0x65, 0x8F, 0x06, 0x46, 0x41, 0x6C, 0x70, 0x68, 0x61, 0x8F, 0x06, 0x45, 0x42, 0x72, 0x61, 0x76, 0x6F, 0x8F, 0x08, 0x44, 0x43, 0x68, 0x61, 0x72, 0x6C, 0x69, 0x65, 0x8F, 0x06, 0x43, 0x44, 0x65, 0x6C, 0x74, 0x61, 0x8F, 0x05, 0x42, 0x45, 0x63, 0x68, 0x6F, 0x8F, 0x09, 0x41, 0x46, 0x6F, 0x78, 0x2D, 0x74, 0x72, 0x6F, 0x74, 0x8F, 0x06, 0x40, 0x42, 0x6C, 0x61, 0x63, 0x6B, 0x8F, 0x06, 0x3F, 0x42, 0x72, 0x6F, 0x77, 0x6E, 0x8F, 0x04, 0x3E, 0x52, 0x65, 0x64, 0x8F, 0x07, 0x3D, 0x4F, 0x72, 0x61, 0x6E, 0x67, 0x65, 0x8F, 0x07, 0x3C, 0x59, 0x65, 0x6C, 0x6C, 0x6F, 0x77, 0x8F, 0x06, 0x3B, 0x47, 0x72, 0x65, 0x65, 0x6E, 0x8F, 0x05, 0x3A, 0x42, 0x6C, 0x75, 0x65, 0x8F, 0x07, 0x39, 0x56, 0x69, 0x6F, 0x6C, 0x65, 0x74, 0x8F, 0x05, 0x38, 0x47, 0x72, 0x65, 0x79, 0x8F, 0x06, 0x37, 0x57, 0x68, 0x69, 0x74, 0x65, 0x8F, 0x06, 0x36, 0x6D, 0x69, 0x6C, 0x6C, 0x69, 0x8F, 0x06, 0x35, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x8F, 0x05, 0x34, 0x6E, 0x61, 0x6E, 0x6F, 0x8F, 0x05, 0x33, 0x70, 0x69, 0x63, 0x6F }; static unsigned char select_item_131[] = { 0xD0, 0x81, 0xFB, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0A, 0x4C, 0x61, 0x72, 0x67, 0x65, 0x4D, 0x65, 0x6E, 0x75, 0x32, 0x8F, 0x1E, 0xFF, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x55, 0x6E, 0x63, 0x6F, 0x6E, 0x64, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x8F, 0x1D, 0xFE, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x4F, 0x6E, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x42, 0x75, 0x73, 0x79, 0x8F, 0x1C, 0xFD, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x4F, 0x6E, 0x20, 0x4E, 0x6F, 0x20, 0x52, 0x65, 0x70, 0x6C, 0x79, 0x8F, 0x26, 0xFC, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x4F, 0x6E, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x4E, 0x6F, 0x74, 0x20, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6C, 0x65, 0x8F, 0x1E, 0xFB, 0x42, 0x61, 0x72, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x4F, 0x66, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x4F, 0x75, 0x74, 0x67, 0x6F, 0x69, 0x6E, 0x67, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x73, 0x8F, 0x2C, 0xFA, 0x42, 0x61, 0x72, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x4F, 0x66, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x4F, 0x75, 0x74, 0x67, 0x6F, 0x69, 0x6E, 0x67, 0x20, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x73, 0x8F, 0x11, 0xF9, 0x43, 0x4C, 0x49, 0x20, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6E, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E }; static unsigned char select_item_141[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0B, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x49, 0x74, 0x65, 0x6D, 0x8F, 0x04, 0x11, 0x4F, 0x6E, 0x65, 0x8F, 0x04, 0x12, 0x54, 0x77, 0x6F }; static unsigned char select_item_151[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x81, 0xED, 0x54, 0x68, 0x65, 0x20, 0x53, 0x49, 0x4D, 0x20, 0x73, 0x68, 0x61, 0x6C, 0x6C, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6C, 0x79, 0x20, 0x61, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6F, 0x66, 0x20, 0x69, 0x74, 0x65, 0x6D, 0x73, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x6D, 0x61, 0x79, 0x20, 0x63, 0x68, 0x6F, 0x6F, 0x73, 0x65, 0x20, 0x6F, 0x6E, 0x65, 0x2E, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, 0x69, 0x74, 0x65, 0x6D, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x72, 0x69, 0x73, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x69, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x28, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x69, 0x6E, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x29, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x61, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x2E, 0x20, 0x4F, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x49, 0x4D, 0x20, 0x6D, 0x61, 0x79, 0x20, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x20, 0x61, 0x6E, 0x20, 0x61, 0x6C, 0x70, 0x68, 0x61, 0x20, 0x69, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2E, 0x20, 0x54, 0x68, 0x65, 0x20, 0x61, 0x6C, 0x70, 0x68, 0x61, 0x20, 0x69, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x69, 0x8F, 0x02, 0x01, 0x59 }; static unsigned char select_item_161[] = { 0xD0, 0x81, 0xF3, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0A, 0x30, 0x4C, 0x61, 0x72, 0x67, 0x65, 0x4D, 0x65, 0x6E, 0x75, 0x8F, 0x1D, 0xFF, 0x31, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x20, 0x55, 0x6E, 0x63, 0x6F, 0x6E, 0x64, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x8F, 0x1C, 0xFE, 0x32, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x20, 0x4F, 0x6E, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x42, 0x75, 0x73, 0x79, 0x8F, 0x1B, 0xFD, 0x33, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x20, 0x4F, 0x6E, 0x20, 0x4E, 0x6F, 0x20, 0x52, 0x65, 0x70, 0x6C, 0x79, 0x8F, 0x25, 0xFC, 0x34, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x20, 0x4F, 0x6E, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x4E, 0x6F, 0x74, 0x20, 0x52, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6C, 0x65, 0x8F, 0x20, 0xFB, 0x35, 0x20, 0x42, 0x61, 0x72, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x4F, 0x66, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x4F, 0x75, 0x74, 0x67, 0x6F, 0x69, 0x6E, 0x67, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x73, 0x8F, 0x24, 0xFA, 0x36, 0x20, 0x42, 0x61, 0x72, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x4F, 0x66, 0x20, 0x41, 0x6C, 0x6C, 0x20, 0x4F, 0x75, 0x74, 0x67, 0x6F, 0x69, 0x6E, 0x67, 0x20, 0x49, 0x6E, 0x74, 0x20, 0x43, 0x61, 0x6C, 0x6C, 0x73, 0x8F, 0x13, 0xF9, 0x37, 0x20, 0x43, 0x4C, 0x49, 0x20, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6E, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E }; static unsigned char select_item_211[] = { 0xD0, 0x39, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x18, 0x03, 0x13, 0x10, 0x26 }; static unsigned char select_item_311[] = { 0xD0, 0x37, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x90, 0x01, 0x02 }; static unsigned char select_item_411[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x24, 0x80, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33 }; static unsigned char select_item_511[] = { 0xD0, 0x3E, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x9E, 0x02, 0x01, 0x01, 0x9F, 0x04, 0x01, 0x05, 0x05, 0x05 }; static unsigned char select_item_521[] = { 0xD0, 0x3E, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x9E, 0x02, 0x00, 0x01, 0x9F, 0x04, 0x00, 0x05, 0x05, 0x05 }; static unsigned char select_item_611[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x24, 0x03, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33 }; static unsigned char select_item_621[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x24, 0x01, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33 }; static unsigned char select_item_711[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x24, 0x04, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32 }; static unsigned char select_item_811[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0A, 0x3C, 0x54, 0x49, 0x4D, 0x45, 0x2D, 0x4F, 0x55, 0x54, 0x3E, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0x8F, 0x07, 0x03, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33 }; static unsigned char select_item_911[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char select_item_912[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x32, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34 }; static unsigned char select_item_921[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x01, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x01, 0xB4, 0x00, 0x06, 0x01, 0xB4 }; static unsigned char select_item_922[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x32, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34 }; static unsigned char select_item_931[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x02, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x02, 0xB4, 0x00, 0x06, 0x02, 0xB4 }; static unsigned char select_item_932[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x32, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34 }; static unsigned char select_item_941[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x04, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x04, 0xB4, 0x00, 0x06, 0x04, 0xB4 }; static unsigned char select_item_942[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x32, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char select_item_943[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x33, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x35, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x36 }; static unsigned char select_item_951[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x08, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x08, 0xB4, 0x00, 0x06, 0x08, 0xB4 }; static unsigned char select_item_952[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x32, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char select_item_953[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x33, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x35, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x36 }; static unsigned char select_item_961[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x10, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x10, 0xB4, 0x00, 0x06, 0x10, 0xB4 }; static unsigned char select_item_962[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x32, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char select_item_963[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x33, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x35, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x36 }; static unsigned char select_item_971[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x20, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x20, 0xB4, 0x00, 0x06, 0x20, 0xB4 }; static unsigned char select_item_972[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x32, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char select_item_973[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x33, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x35, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x36 }; static unsigned char select_item_981[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x40, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x40, 0xB4, 0x00, 0x06, 0x40, 0xB4 }; static unsigned char select_item_982[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x32, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char select_item_983[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x33, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x35, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x36 }; static unsigned char select_item_991[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x80, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x80, 0xB4, 0x00, 0x06, 0x80, 0xB4 }; static unsigned char select_item_992[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x32, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char select_item_993[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x33, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x35, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x36 }; static unsigned char select_item_9101[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x31, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x31, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4, 0xD1, 0x08, 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char select_item_9102[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x53, 0x65, 0x6C, 0x65, 0x63, 0x74, 0x20, 0x32, 0x8F, 0x07, 0x01, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x33, 0x8F, 0x07, 0x02, 0x49, 0x74, 0x65, 0x6D, 0x20, 0x34 }; static unsigned char select_item_1011[] = { 0xD0, 0x7E, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x8F, 0x1C, 0x01, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x31, 0x8F, 0x1C, 0x02, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x32, 0x8F, 0x1C, 0x03, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x33 }; static unsigned char select_item_1021[] = { 0xD0, 0x53, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0F, 0x81, 0x0C, 0x08, 0x97, 0x94, 0xA0, 0x90, 0x92, 0xA1, 0xA2, 0x92, 0xA3, 0x99, 0xA2, 0x95, 0x8F, 0x11, 0x01, 0x81, 0x0D, 0x08, 0x97, 0x94, 0xA0, 0x90, 0x92, 0xA1, 0xA2, 0x92, 0xA3, 0x99, 0xA2, 0x95, 0x31, 0x8F, 0x11, 0x02, 0x81, 0x0D, 0x08, 0x97, 0x94, 0xA0, 0x90, 0x92, 0xA1, 0xA2, 0x92, 0xA3, 0x99, 0xA2, 0x95, 0x32, 0x8F, 0x11, 0x03, 0x81, 0x0D, 0x08, 0x97, 0x94, 0xA0, 0x90, 0x92, 0xA1, 0xA2, 0x92, 0xA3, 0x99, 0xA2, 0x95, 0x33 }; static unsigned char select_item_1031[] = { 0xD0, 0x57, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x82, 0x0C, 0x04, 0x10, 0x87, 0x84, 0x90, 0x80, 0x82, 0x91, 0x92, 0x82, 0x93, 0x89, 0x92, 0x85, 0x8F, 0x12, 0x01, 0x82, 0x0D, 0x04, 0x10, 0x87, 0x84, 0x90, 0x80, 0x82, 0x91, 0x92, 0x82, 0x93, 0x89, 0x92, 0x85, 0x31, 0x8F, 0x12, 0x02, 0x82, 0x0D, 0x04, 0x10, 0x87, 0x84, 0x90, 0x80, 0x82, 0x91, 0x92, 0x82, 0x93, 0x89, 0x92, 0x85, 0x32, 0x8F, 0x12, 0x03, 0x82, 0x0D, 0x04, 0x10, 0x87, 0x84, 0x90, 0x80, 0x82, 0x91, 0x92, 0x82, 0x93, 0x89, 0x92, 0x85, 0x33 }; static unsigned char select_item_1111[] = { 0xD0, 0x3E, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0B, 0x80, 0x5D, 0xE5, 0x51, 0x77, 0x7B, 0xB1, 0x90, 0x09, 0x62, 0xE9, 0x8F, 0x08, 0x01, 0x80, 0x98, 0x79, 0x76, 0xEE, 0x4E, 0x00, 0x8F, 0x08, 0x02, 0x80, 0x98, 0x79, 0x76, 0xEE, 0x4E, 0x8C, 0x8F, 0x08, 0x03, 0x80, 0x98, 0x79, 0x76, 0xEE, 0x4E, 0x09, 0x8F, 0x08, 0x04, 0x80, 0x98, 0x79, 0x76, 0xEE, 0x56, 0xDB }; static unsigned char select_item_1211[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x09, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x30, 0x8F, 0x0A, 0x01, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x31, 0x8F, 0x0A, 0x02, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x32, 0x8F, 0x0A, 0x03, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x33 }; static unsigned char select_item_1221[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x07, 0x81, 0x04, 0x61, 0x38, 0x31, 0xEB, 0x30, 0x8F, 0x08, 0x01, 0x81, 0x04, 0x61, 0x38, 0x31, 0xEB, 0x31, 0x8F, 0x08, 0x02, 0x81, 0x04, 0x61, 0x38, 0x31, 0xEB, 0x32, 0x8F, 0x08, 0x03, 0x81, 0x04, 0x61, 0x38, 0x31, 0xEB, 0x33 }; static unsigned char select_item_1231[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x08, 0x82, 0x04, 0x30, 0xA0, 0x38, 0x32, 0xCB, 0x30, 0x8F, 0x09, 0x01, 0x82, 0x04, 0x30, 0xA0, 0x38, 0x32, 0xCB, 0x31, 0x8F, 0x09, 0x02, 0x82, 0x04, 0x30, 0xA0, 0x38, 0x32, 0xCB, 0x32, 0x8F, 0x09, 0x03, 0x82, 0x04, 0x30, 0xA0, 0x38, 0x32, 0xCB, 0x33 }; static struct select_item_test select_item_data_111 = { .pdu = select_item_111, .pdu_len = sizeof(select_item_111), .qualifier = 0x00, .alpha_id = "Toolkit Select", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, { .id = 4, .text = "Item 4" }, } }; static struct select_item_test select_item_data_121 = { .pdu = select_item_121, .pdu_len = sizeof(select_item_121), .qualifier = 0x00, .alpha_id = "LargeMenu1", .items = { { .id = 0x50, .text = "Zero" }, { .id = 0x4F, .text = "One" }, { .id = 0x4E, .text = "Two" }, { .id = 0x4D, .text = "Three" }, { .id = 0x4C, .text = "Four" }, { .id = 0x4B, .text = "Five" }, { .id = 0x4A, .text = "Six" }, { .id = 0x49, .text = "Seven" }, { .id = 0x48, .text = "Eight" }, { .id = 0x47, .text = "Nine" }, { .id = 0x46, .text = "Alpha" }, { .id = 0x45, .text = "Bravo" }, { .id = 0x44, .text = "Charlie" }, { .id = 0x43, .text = "Delta" }, { .id = 0x42, .text = "Echo" }, { .id = 0x41, .text = "Fox-trot" }, { .id = 0x40, .text = "Black" }, { .id = 0x3F, .text = "Brown" }, { .id = 0x3E, .text = "Red" }, { .id = 0x3D, .text = "Orange" }, { .id = 0x3C, .text = "Yellow" }, { .id = 0x3B, .text = "Green" }, { .id = 0x3A, .text = "Blue" }, { .id = 0x39, .text = "Violet" }, { .id = 0x38, .text = "Grey" }, { .id = 0x37, .text = "White" }, { .id = 0x36, .text = "milli" }, { .id = 0x35, .text = "micro" }, { .id = 0x34, .text = "nano" }, { .id = 0x33, .text = "pico" }, } }; static struct select_item_test select_item_data_131 = { .pdu = select_item_131, .pdu_len = sizeof(select_item_131), .qualifier = 0x00, .alpha_id = "LargeMenu2", .items = { { .id = 0xFF, .text = "Call Forwarding Unconditional" }, { .id = 0xFE, .text = "Call Forwarding On User Busy" }, { .id = 0xFD, .text = "Call Forwarding On No Reply" }, { .id = 0xFC, .text = "Call Forwarding On User Not Reachable" }, { .id = 0xFB, .text = "Barring Of All Outgoing Calls" }, { .id = 0xFA, .text = "Barring Of All Outgoing International Calls" }, { .id = 0xF9, .text = "CLI Presentation" }, } }; static struct select_item_test select_item_data_141 = { .pdu = select_item_141, .pdu_len = sizeof(select_item_141), .qualifier = 0x00, .alpha_id = "Select Item", .items = { { .id = 0x11, .text = "One" }, { .id = 0x12, .text = "Two" }, } }; static struct select_item_test select_item_data_151 = { .pdu = select_item_151, .pdu_len = sizeof(select_item_151), .qualifier = 0x00, .alpha_id = "The SIM shall supply a set of items from which the user " "may choose one. Each item comprises a short identifier (used " "to indicate the selection) and a text string. Optionally the " "SIM may include an alpha identifier. The alpha identifier i", .items = { { .id = 0x01, .text = "Y" }, } }; static struct select_item_test select_item_data_161 = { .pdu = select_item_161, .pdu_len = sizeof(select_item_161), .qualifier = 0x00, .alpha_id = "0LargeMenu", .items = { { .id = 0xFF, .text = "1 Call Forward Unconditional" }, { .id = 0xFE, .text = "2 Call Forward On User Busy" }, { .id = 0xFD, .text = "3 Call Forward On No Reply" }, { .id = 0xFC, .text = "4 Call Forward On User Not Reachable" }, { .id = 0xFB, .text = "5 Barring Of All Outgoing Calls" }, { .id = 0xFA, .text = "6 Barring Of All Outgoing Int Calls" }, { .id = 0xF9, .text = "7 CLI Presentation" }, } }; static struct select_item_test select_item_data_211 = { .pdu = select_item_211, .pdu_len = sizeof(select_item_211), .qualifier = 0x00, .alpha_id = "Toolkit Select", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .next_act = { .list = { STK_COMMAND_TYPE_SEND_SMS, STK_COMMAND_TYPE_SETUP_CALL, STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO}, .len = 3 } }; static struct select_item_test select_item_data_311 = { .pdu = select_item_311, .pdu_len = sizeof(select_item_311), .qualifier = 0x00, .alpha_id = "Toolkit Select", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .item_id = 0x02 }; static struct select_item_test select_item_data_411 = { .pdu = select_item_411, .pdu_len = sizeof(select_item_411), .qualifier = 0x80, .alpha_id = "Toolkit Select", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, } }; static struct select_item_test select_item_data_511 = { .pdu = select_item_511, .pdu_len = sizeof(select_item_511), .qualifier = 0x00, .alpha_id = "Toolkit Select", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 1 }, .item_icon_id_list = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .list = { 5, 5, 5 }, .len = 3 } }; static struct select_item_test select_item_data_521 = { .pdu = select_item_521, .pdu_len = sizeof(select_item_521), .qualifier = 0x00, .alpha_id = "Toolkit Select", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 1 }, .item_icon_id_list = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .list = { 5, 5, 5 }, .len = 3 } }; static struct select_item_test select_item_data_611 = { .pdu = select_item_611, .pdu_len = sizeof(select_item_611), .qualifier = 0x03, .alpha_id = "Toolkit Select", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, } }; static struct select_item_test select_item_data_621 = { .pdu = select_item_621, .pdu_len = sizeof(select_item_621), .qualifier = 0x01, .alpha_id = "Toolkit Select", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, } }; static struct select_item_test select_item_data_711 = { .pdu = select_item_711, .pdu_len = sizeof(select_item_711), .qualifier = 0x04, .alpha_id = "Toolkit Select", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, } }; static struct select_item_test select_item_data_811 = { .pdu = select_item_811, .pdu_len = sizeof(select_item_811), .qualifier = 0x00, .alpha_id = "", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, { .id = 3, .text = "Item 3" }, } }; static struct select_item_test select_item_data_911 = { .pdu = select_item_911, .pdu_len = sizeof(select_item_911), .qualifier = 0x00, .alpha_id = "Toolkit Select 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Select 1" "
", }; static struct select_item_test select_item_data_912 = { .pdu = select_item_912, .pdu_len = sizeof(select_item_912), .qualifier = 0x00, .alpha_id = "Toolkit Select 2", .items = { { .id = 1, .text = "Item 3" }, { .id = 2, .text = "Item 4" }, } }; static struct select_item_test select_item_data_921 = { .pdu = select_item_921, .pdu_len = sizeof(select_item_921), .qualifier = 0x00, .alpha_id = "Toolkit Select 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x01, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x01, 0xB4, 0x00, 0x06, 0x01, 0xB4 } }, .html = "
Toolkit Select 1" "
", }; static struct select_item_test select_item_data_922 = { .pdu = select_item_922, .pdu_len = sizeof(select_item_922), .qualifier = 0x00, .alpha_id = "Toolkit Select 2", .items = { { .id = 1, .text = "Item 3" }, { .id = 2, .text = "Item 4" }, } }; static struct select_item_test select_item_data_931 = { .pdu = select_item_931, .pdu_len = sizeof(select_item_931), .qualifier = 0x00, .alpha_id = "Toolkit Select 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x02, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x02, 0xB4, 0x00, 0x06, 0x02, 0xB4 } }, .html = "
Toolkit Select 1" "
" }; static struct select_item_test select_item_data_932 = { .pdu = select_item_932, .pdu_len = sizeof(select_item_932), .qualifier = 0x00, .alpha_id = "Toolkit Select 2", .items = { { .id = 1, .text = "Item 3" }, { .id = 2, .text = "Item 4" }, } }; static struct select_item_test select_item_data_941 = { .pdu = select_item_941, .pdu_len = sizeof(select_item_941), .qualifier = 0x00, .alpha_id = "Toolkit Select 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x04, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x04, 0xB4, 0x00, 0x06, 0x04, 0xB4 } }, .html = "
" "Toolkit Select 1
", }; static struct select_item_test select_item_data_942 = { .pdu = select_item_942, .pdu_len = sizeof(select_item_942), .qualifier = 0x00, .alpha_id = "Toolkit Select 2", .items = { { .id = 1, .text = "Item 3" }, { .id = 2, .text = "Item 4" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Select 2" "
", }; static struct select_item_test select_item_data_943 = { .pdu = select_item_943, .pdu_len = sizeof(select_item_943), .qualifier = 0x00, .alpha_id = "Toolkit Select 3", .items = { { .id = 1, .text = "Item 5" }, { .id = 2, .text = "Item 6" }, } }; static struct select_item_test select_item_data_951 = { .pdu = select_item_951, .pdu_len = sizeof(select_item_951), .qualifier = 0x00, .alpha_id = "Toolkit Select 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x08, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x08, 0xB4, 0x00, 0x06, 0x08, 0xB4 } }, .html = "
" "Toolkit Select 1
", }; static struct select_item_test select_item_data_952 = { .pdu = select_item_952, .pdu_len = sizeof(select_item_952), .qualifier = 0x00, .alpha_id = "Toolkit Select 2", .items = { { .id = 1, .text = "Item 3" }, { .id = 2, .text = "Item 4" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Select 2" "
", }; static struct select_item_test select_item_data_953 = { .pdu = select_item_953, .pdu_len = sizeof(select_item_953), .qualifier = 0x00, .alpha_id = "Toolkit Select 3", .items = { { .id = 1, .text = "Item 5" }, { .id = 2, .text = "Item 6" }, } }; static struct select_item_test select_item_data_961 = { .pdu = select_item_961, .pdu_len = sizeof(select_item_961), .qualifier = 0x00, .alpha_id = "Toolkit Select 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x10, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x10, 0xB4, 0x00, 0x06, 0x10, 0xB4 } }, .html = "
" "Toolkit Select 1
", }; static struct select_item_test select_item_data_962 = { .pdu = select_item_962, .pdu_len = sizeof(select_item_962), .qualifier = 0x00, .alpha_id = "Toolkit Select 2", .items = { { .id = 1, .text = "Item 3" }, { .id = 2, .text = "Item 4" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Select 2" "
", }; static struct select_item_test select_item_data_963 = { .pdu = select_item_963, .pdu_len = sizeof(select_item_963), .qualifier = 0x00, .alpha_id = "Toolkit Select 3", .items = { { .id = 1, .text = "Item 5" }, { .id = 2, .text = "Item 6" }, } }; static struct select_item_test select_item_data_971 = { .pdu = select_item_971, .pdu_len = sizeof(select_item_971), .qualifier = 0x00, .alpha_id = "Toolkit Select 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x20, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x20, 0xB4, 0x00, 0x06, 0x20, 0xB4 } }, .html = "
" "Toolkit Select 1
" }; static struct select_item_test select_item_data_972 = { .pdu = select_item_972, .pdu_len = sizeof(select_item_972), .qualifier = 0x00, .alpha_id = "Toolkit Select 2", .items = { { .id = 1, .text = "Item 3" }, { .id = 2, .text = "Item 4" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Select 2" "
", }; static struct select_item_test select_item_data_973 = { .pdu = select_item_973, .pdu_len = sizeof(select_item_973), .qualifier = 0x00, .alpha_id = "Toolkit Select 3", .items = { { .id = 1, .text = "Item 5" }, { .id = 2, .text = "Item 6" }, } }; static struct select_item_test select_item_data_981 = { .pdu = select_item_981, .pdu_len = sizeof(select_item_981), .qualifier = 0x00, .alpha_id = "Toolkit Select 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x40, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x40, 0xB4, 0x00, 0x06, 0x40, 0xB4 } }, .html = "
Toolkit Select 1
", }; static struct select_item_test select_item_data_982 = { .pdu = select_item_982, .pdu_len = sizeof(select_item_982), .qualifier = 0x00, .alpha_id = "Toolkit Select 2", .items = { { .id = 1, .text = "Item 3" }, { .id = 2, .text = "Item 4" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Select 2" "
", }; static struct select_item_test select_item_data_983 = { .pdu = select_item_983, .pdu_len = sizeof(select_item_983), .qualifier = 0x00, .alpha_id = "Toolkit Select 3", .items = { { .id = 1, .text = "Item 5" }, { .id = 2, .text = "Item 6" }, } }; static struct select_item_test select_item_data_991 = { .pdu = select_item_991, .pdu_len = sizeof(select_item_991), .qualifier = 0x00, .alpha_id = "Toolkit Select 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x80, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x80, 0xB4, 0x00, 0x06, 0x80, 0xB4 } }, .html = "
Toolkit Select 1
", }; static struct select_item_test select_item_data_992 = { .pdu = select_item_992, .pdu_len = sizeof(select_item_992), .qualifier = 0x00, .alpha_id = "Toolkit Select 2", .items = { { .id = 1, .text = "Item 3" }, { .id = 2, .text = "Item 4" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Select 2" "
", }; static struct select_item_test select_item_data_993 = { .pdu = select_item_993, .pdu_len = sizeof(select_item_993), .qualifier = 0x00, .alpha_id = "Toolkit Select 3", .items = { { .id = 1, .text = "Item 5" }, { .id = 2, .text = "Item 6" }, } }; static struct select_item_test select_item_data_9101 = { .pdu = select_item_9101, .pdu_len = sizeof(select_item_9101), .qualifier = 0x00, .alpha_id = "Toolkit Select 1", .items = { { .id = 1, .text = "Item 1" }, { .id = 2, .text = "Item 2" }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .item_text_attr_list = { .len = 8, .list = { 0x00, 0x06, 0x00, 0xB4, 0x00, 0x06, 0x00, 0xB4 } }, .html = "
Toolkit Select 1" "
", }; static struct select_item_test select_item_data_9102 = { .pdu = select_item_9102, .pdu_len = sizeof(select_item_9102), .qualifier = 0x00, .alpha_id = "Toolkit Select 2", .items = { { .id = 1, .text = "Item 3" }, { .id = 2, .text = "Item 4" }, } }; static struct select_item_test select_item_data_1011 = { .pdu = select_item_1011, .pdu_len = sizeof(select_item_1011), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .items = { { .id = 1, .text = "ЗДРАВСТВУЙТЕ1" }, { .id = 2, .text = "ЗДРАВСТВУЙТЕ2" }, { .id = 3, .text = "ЗДРАВСТВУЙТЕ3" }, } }; static struct select_item_test select_item_data_1021 = { .pdu = select_item_1021, .pdu_len = sizeof(select_item_1021), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .items = { { .id = 1, .text = "ЗДРАВСТВУЙТЕ1" }, { .id = 2, .text = "ЗДРАВСТВУЙТЕ2" }, { .id = 3, .text = "ЗДРАВСТВУЙТЕ3" }, } }; static struct select_item_test select_item_data_1031 = { .pdu = select_item_1031, .pdu_len = sizeof(select_item_1031), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .items = { { .id = 1, .text = "ЗДРАВСТВУЙТЕ1" }, { .id = 2, .text = "ЗДРАВСТВУЙТЕ2" }, { .id = 3, .text = "ЗДРАВСТВУЙТЕ3" }, } }; static struct select_item_test select_item_data_1111 = { .pdu = select_item_1111, .pdu_len = sizeof(select_item_1111), .qualifier = 0x00, .alpha_id = "工具箱选择", .items = { { .id = 1, .text = "项目一" }, { .id = 2, .text = "项目二" }, { .id = 3, .text = "项目三" }, { .id = 4, .text = "项目四" }, } }; static struct select_item_test select_item_data_1211 = { .pdu = select_item_1211, .pdu_len = sizeof(select_item_1211), .qualifier = 0x00, .alpha_id = "80ル0", .items = { { .id = 1, .text = "80ル1" }, { .id = 2, .text = "80ル2" }, { .id = 3, .text = "80ル3" }, } }; static struct select_item_test select_item_data_1221 = { .pdu = select_item_1221, .pdu_len = sizeof(select_item_1221), .qualifier = 0x00, .alpha_id = "81ル0", .items = { { .id = 1, .text = "81ル1" }, { .id = 2, .text = "81ル2" }, { .id = 3, .text = "81ル3" }, } }; static struct select_item_test select_item_data_1231 = { .pdu = select_item_1231, .pdu_len = sizeof(select_item_1231), .qualifier = 0x00, .alpha_id = "82ル0", .items = { { .id = 1, .text = "82ル1" }, { .id = 2, .text = "82ル2" }, { .id = 3, .text = "82ル3" }, } }; static void test_select_item(gconstpointer data) { const struct select_item_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_SELECT_ITEM); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_alpha_id(command->select_item.alpha_id, test->alpha_id); check_items(command->select_item.items, test->items); check_items_next_action_indicator(&command->select_item.next_act, &test->next_act); check_item_id(command->select_item.item_id, test->item_id); check_icon_id(&command->select_item.icon_id, &test->icon_id); check_item_icon_id_list(&command->select_item.item_icon_id_list, &test->item_icon_id_list); check_text_attr(&command->select_item.text_attr, &test->text_attr); check_item_text_attribute_list( &command->select_item.item_text_attr_list, &test->item_text_attr_list); check_text_attr_html(&command->select_item.text_attr, command->select_item.alpha_id, test->html); check_frame_id(&command->select_item.frame_id, &test->frame_id); stk_command_free(command); } struct send_sms_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *alpha_id; struct sms_test gsm_sms; struct stk_common_byte_array cdma_sms; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; /* 3GPP TS 31.124 Section 27.22.4.10.1.4.2 */ static unsigned char send_sms_111[] = { 0xD0, 0x37, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x07, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x53, 0x4D, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x18, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0xF4, 0x0C, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65 }; static unsigned char send_sms_121[] = { 0xD0, 0x32, 0x81, 0x03, 0x01, 0x13, 0x01, 0x82, 0x02, 0x81, 0x83, 0x85, 0x07, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x53, 0x4D, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x13, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0xF4, 0x07, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x53, 0x4D }; static unsigned char send_sms_131[] = { 0xD0, 0x3D, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0D, 0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x18, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0xF0, 0x0D, 0x53, 0xF4, 0x5B, 0x4E, 0x07, 0x35, 0xCB, 0xF3, 0x79, 0xF8, 0x5C, 0x06 }; static unsigned char send_sms_141[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01, 0x13, 0x01, 0x82, 0x02, 0x81, 0x83, 0x85, 0x38, 0x54, 0x68, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x68, 0x6F, 0x6C, 0x64, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x50, 0x11, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x11, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x81, 0xAC, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0xF4, 0xA0, 0x54, 0x77, 0x6F, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x64, 0x3A, 0x20, 0x2D, 0x20, 0x41, 0x20, 0x73, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x6E, 0x74, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x20, 0x61, 0x6E, 0x20, 0x53, 0x4D, 0x53, 0x2D, 0x53, 0x55, 0x42, 0x4D, 0x49, 0x54, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2C, 0x20, 0x6F, 0x72, 0x20, 0x61, 0x6E, 0x20, 0x53, 0x4D, 0x53, 0x2D, 0x43, 0x4F, 0x4D, 0x4D, 0x41, 0x4E, 0x44, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2C, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x63, 0x61, 0x6E, 0x20, 0x62, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x72, 0x61, 0x6E, 0x73, 0x70 }; static unsigned char send_sms_151[] = { 0xD0, 0x81, 0xE9, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x38, 0x54, 0x68, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x68, 0x6F, 0x6C, 0x64, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x50, 0x20, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6E, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x81, 0x98, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0xF0, 0xA0, 0xD4, 0xFB, 0x1B, 0x44, 0xCF, 0xC3, 0xCB, 0x73, 0x50, 0x58, 0x5E, 0x06, 0x91, 0xCB, 0xE6, 0xB4, 0xBB, 0x4C, 0xD6, 0x81, 0x5A, 0xA0, 0x20, 0x68, 0x8E, 0x7E, 0xCB, 0xE9, 0xA0, 0x76, 0x79, 0x3E, 0x0F, 0x9F, 0xCB, 0x20, 0xFA, 0x1B, 0x24, 0x2E, 0x83, 0xE6, 0x65, 0x37, 0x1D, 0x44, 0x7F, 0x83, 0xE8, 0xE8, 0x32, 0xC8, 0x5D, 0xA6, 0xDF, 0xDF, 0xF2, 0x35, 0x28, 0xED, 0x06, 0x85, 0xDD, 0xA0, 0x69, 0x73, 0xDA, 0x9A, 0x56, 0x85, 0xCD, 0x24, 0x15, 0xD4, 0x2E, 0xCF, 0xE7, 0xE1, 0x73, 0x99, 0x05, 0x7A, 0xCB, 0x41, 0x61, 0x37, 0x68, 0xDA, 0x9C, 0xB6, 0x86, 0xCF, 0x66, 0x33, 0xE8, 0x24, 0x82, 0xDA, 0xE5, 0xF9, 0x3C, 0x7C, 0x2E, 0xB3, 0x40, 0x77, 0x74, 0x59, 0x5E, 0x06, 0xD1, 0xD1, 0x65, 0x50, 0x7D, 0x5E, 0x96, 0x83, 0xC8, 0x61, 0x7A, 0x18, 0x34, 0x0E, 0xBB, 0x41, 0xE2, 0x32, 0x08, 0x1E, 0x9E, 0xCF, 0xCB, 0x64, 0x10, 0x5D, 0x1E, 0x76, 0xCF, 0xE1 }; static unsigned char send_sms_161[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x81, 0xE6, 0x54, 0x77, 0x6F, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x64, 0x3A, 0x20, 0x2D, 0x20, 0x41, 0x20, 0x73, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x6E, 0x74, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x20, 0x61, 0x6E, 0x20, 0x53, 0x4D, 0x53, 0x2D, 0x53, 0x55, 0x42, 0x4D, 0x49, 0x54, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2C, 0x20, 0x6F, 0x72, 0x20, 0x61, 0x6E, 0x20, 0x53, 0x4D, 0x53, 0x2D, 0x43, 0x4F, 0x4D, 0x4D, 0x41, 0x4E, 0x44, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2C, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x63, 0x61, 0x6E, 0x20, 0x62, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x72, 0x61, 0x6E, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6E, 0x74, 0x6C, 0x79, 0x3B, 0x20, 0x2D, 0x20, 0x41, 0x20, 0x73, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x6E, 0x74, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x20, 0x61, 0x6E, 0x20, 0x53, 0x4D, 0x53, 0x2D, 0x53, 0x55, 0x42, 0x4D, 0x49, 0x54, 0x20, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_171[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x00, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x18, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0xF4, 0x0C, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65 }; static unsigned char send_sms_181[] = { 0xD0, 0x2E, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x18, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0xF4, 0x0C, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65 }; static unsigned char send_sms_211[] = { 0xD0, 0x55, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x24, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0x08, 0x18, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15 }; static unsigned char send_sms_212[] = { 0xD0, 0x4B, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0F, 0x81, 0x0C, 0x08, 0x97, 0x94, 0xA0, 0x90, 0x92, 0xA1, 0xA2, 0x92, 0xA3, 0x99, 0xA2, 0x95, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x24, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0x08, 0x18, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15 }; static unsigned char send_sms_213[] = { 0xD0, 0x4C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x82, 0x0C, 0x04, 0x10, 0x87, 0x84, 0x90, 0x80, 0x82, 0x91, 0x92, 0x82, 0x93, 0x89, 0x92, 0x85, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x24, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0x08, 0x18, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15 }; static unsigned char send_sms_311[] = { 0xD0, 0x3B, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x07, 0x4E, 0x4F, 0x20, 0x49, 0x43, 0x4F, 0x4E, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x18, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0xF4, 0x0C, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x9E, 0x02, 0x00, 0x01 }; static unsigned char send_sms_321[] = { 0xD0, 0x3B, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x07, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x53, 0x4D, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x18, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0xF4, 0x0C, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1E, 0x02, 0x01, 0x01 }; static unsigned char send_sms_411[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_sms_412[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_421[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x01, 0xB4 }; static unsigned char send_sms_422[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_431[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x02, 0xB4 }; static unsigned char send_sms_432[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_441[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x04, 0xB4 }; static unsigned char send_sms_442[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_sms_443[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_451[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x08, 0xB4 }; static unsigned char send_sms_452[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_sms_453[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_461[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x10, 0xB4 }; static unsigned char send_sms_462[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_sms_463[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_471[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x20, 0xB4 }; static unsigned char send_sms_472[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_sms_473[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_481[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x40, 0xB4 }; static unsigned char send_sms_482[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_sms_483[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_491[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x80, 0xB4 }; static unsigned char send_sms_492[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_sms_493[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_4101[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_sms_4102[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8B, 0x09, 0x01, 0x00, 0x02, 0x91, 0x10, 0x40, 0xF0, 0x01, 0x20 }; static unsigned char send_sms_511[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x05, 0x80, 0x4E, 0x2D, 0x4E, 0x00, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x10, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0x08, 0x04, 0x4E, 0x2D, 0x4E, 0x00 }; static unsigned char send_sms_512[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x05, 0x81, 0x02, 0x9C, 0xAD, 0x80, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x10, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0x08, 0x04, 0x4E, 0x2D, 0x4E, 0x00 }; static unsigned char send_sms_513[] = { 0xD0, 0x2E, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x06, 0x82, 0x02, 0x4E, 0x00, 0xAD, 0x80, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x10, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0x08, 0x04, 0x4E, 0x2D, 0x4E, 0x00 }; static unsigned char send_sms_611[] = { 0xD0, 0x35, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x09, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x30, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x14, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0x08, 0x08, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x31 }; static unsigned char send_sms_612[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x07, 0x81, 0x04, 0x61, 0x38, 0x31, 0xEB, 0x31, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x14, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0x08, 0x08, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x32 }; static unsigned char send_sms_613[] = { 0xD0, 0x34, 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x08, 0x82, 0x04, 0x30, 0xA0, 0x38, 0x32, 0xCB, 0x32, 0x86, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xF8, 0x8B, 0x14, 0x01, 0x00, 0x09, 0x91, 0x10, 0x32, 0x54, 0x76, 0xF8, 0x40, 0x08, 0x08, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x33 }; static struct send_sms_test send_sms_data_111 = { .pdu = send_sms_111, .pdu_len = sizeof(send_sms_111), .qualifier = 0x00, .alpha_id = "Send SM", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0xF4, .udl = 12, .ud = "Test Message" } } } }; static struct send_sms_test send_sms_data_121 = { .pdu = send_sms_121, .pdu_len = sizeof(send_sms_121), .qualifier = 0x01, .alpha_id = "Send SM", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0xF0, .udl = 7, .ud = "Send SM" } } } }; static struct send_sms_test send_sms_data_131 = { .pdu = send_sms_131, .pdu_len = sizeof(send_sms_131), .qualifier = 0x00, .alpha_id = "Short Message", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0xF0, .udl = 13, .ud = "Short Message" } } }, }; static struct send_sms_test send_sms_data_141 = { .pdu = send_sms_141, .pdu_len = sizeof(send_sms_141), .qualifier = 0x01, .alpha_id = "The address data object holds the RP_Destination_Address", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0xF0, .udl = 160, .ud = "Two types are defined: - A short message to be " "sent to the network in an SMS-SUBMIT message, " "or an SMS-COMMAND message, where the user " "data can be passed transp" } } } }; static struct send_sms_test send_sms_data_151 = { .pdu = send_sms_151, .pdu_len = sizeof(send_sms_151), .qualifier = 0x00, .alpha_id = "The address data object holds the RP Destination Address", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0xF0, .udl = 160, .ud = "Two types are defined: - A short message to be " "sent to the network in an SMS-SUBMIT message, " "or an SMS-COMMAND message, where the user " "data can be passed transp" } } } }; /* There should be a space after alpha_id */ static struct send_sms_test send_sms_data_161 = { .pdu = send_sms_161, .pdu_len = sizeof(send_sms_161), .qualifier = 0x00, .alpha_id = "Two types are defined: - A short message to be sent to " "the network in an SMS-SUBMIT message, or an " "SMS-COMMAND message, where the user data can be " "passed transparently; - A short message to be sent " "to the network in an SMS-SUBMIT ", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; static struct send_sms_test send_sms_data_171 = { .pdu = send_sms_171, .pdu_len = sizeof(send_sms_171), .qualifier = 0x00, .alpha_id = "", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0xF4, .udl = 12, .ud = "Test Message" } } } }; static struct send_sms_test send_sms_data_181 = { .pdu = send_sms_181, .pdu_len = sizeof(send_sms_181), .qualifier = 0x00, .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0xF4, .udl = 12, .ud = "Test Message" } } } }; static struct send_sms_test send_sms_data_211 = { .pdu = send_sms_211, .pdu_len = sizeof(send_sms_211), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0x08, .udl = 24, .ud = "ЗДРАВСТВУЙТЕ" } } } }; static struct send_sms_test send_sms_data_212 = { .pdu = send_sms_212, .pdu_len = sizeof(send_sms_212), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0x08, .udl = 24, .ud = "ЗДРАВСТВУЙТЕ" } } } }; static struct send_sms_test send_sms_data_213 = { .pdu = send_sms_213, .pdu_len = sizeof(send_sms_213), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0x08, .udl = 24, .ud = "ЗДРАВСТВУЙТЕ" } } } }; static struct send_sms_test send_sms_data_311 = { .pdu = send_sms_311, .pdu_len = sizeof(send_sms_311), .qualifier = 0x00, .alpha_id = "NO ICON", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0xF4, .udl = 12, .ud = "Test Message" } } }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct send_sms_test send_sms_data_321 = { .pdu = send_sms_321, .pdu_len = sizeof(send_sms_321), .qualifier = 0x00, .alpha_id = "Send SM", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0xF4, .udl = 12, .ud = "Test Message" } } }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct send_sms_test send_sms_data_411 = { .pdu = send_sms_411, .pdu_len = sizeof(send_sms_411), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_sms_test send_sms_data_412 = { .pdu = send_sms_412, .pdu_len = sizeof(send_sms_412), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; static struct send_sms_test send_sms_data_421 = { .pdu = send_sms_421, .pdu_len = sizeof(send_sms_421), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x01, 0xB4 } } }; static struct send_sms_test send_sms_data_422 = { .pdu = send_sms_422, .pdu_len = sizeof(send_sms_422), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; static struct send_sms_test send_sms_data_431 = { .pdu = send_sms_431, .pdu_len = sizeof(send_sms_431), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x02, 0xB4 } } }; static struct send_sms_test send_sms_data_432 = { .pdu = send_sms_432, .pdu_len = sizeof(send_sms_432), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; static struct send_sms_test send_sms_data_441 = { .pdu = send_sms_441, .pdu_len = sizeof(send_sms_441), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x04, 0xB4 } } }; static struct send_sms_test send_sms_data_442 = { .pdu = send_sms_442, .pdu_len = sizeof(send_sms_442), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_sms_test send_sms_data_443 = { .pdu = send_sms_443, .pdu_len = sizeof(send_sms_443), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; static struct send_sms_test send_sms_data_451 = { .pdu = send_sms_451, .pdu_len = sizeof(send_sms_451), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x08, 0xB4 } } }; static struct send_sms_test send_sms_data_452 = { .pdu = send_sms_452, .pdu_len = sizeof(send_sms_452), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_sms_test send_sms_data_453 = { .pdu = send_sms_453, .pdu_len = sizeof(send_sms_453), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; static struct send_sms_test send_sms_data_461 = { .pdu = send_sms_461, .pdu_len = sizeof(send_sms_461), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x10, 0xB4 } } }; static struct send_sms_test send_sms_data_462 = { .pdu = send_sms_462, .pdu_len = sizeof(send_sms_462), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_sms_test send_sms_data_463 = { .pdu = send_sms_463, .pdu_len = sizeof(send_sms_463), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; static struct send_sms_test send_sms_data_471 = { .pdu = send_sms_471, .pdu_len = sizeof(send_sms_471), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x20, 0xB4 } } }; static struct send_sms_test send_sms_data_472 = { .pdu = send_sms_472, .pdu_len = sizeof(send_sms_472), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_sms_test send_sms_data_473 = { .pdu = send_sms_473, .pdu_len = sizeof(send_sms_473), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; static struct send_sms_test send_sms_data_481 = { .pdu = send_sms_481, .pdu_len = sizeof(send_sms_481), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x40, 0xB4 } } }; static struct send_sms_test send_sms_data_482 = { .pdu = send_sms_482, .pdu_len = sizeof(send_sms_482), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_sms_test send_sms_data_483 = { .pdu = send_sms_483, .pdu_len = sizeof(send_sms_483), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; static struct send_sms_test send_sms_data_491 = { .pdu = send_sms_491, .pdu_len = sizeof(send_sms_491), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x80, 0xB4 } } }; static struct send_sms_test send_sms_data_492 = { .pdu = send_sms_492, .pdu_len = sizeof(send_sms_492), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_sms_test send_sms_data_493 = { .pdu = send_sms_493, .pdu_len = sizeof(send_sms_493), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; static struct send_sms_test send_sms_data_4101 = { .pdu = send_sms_4101, .pdu_len = sizeof(send_sms_4101), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_sms_test send_sms_data_4102 = { .pdu = send_sms_4102, .pdu_len = sizeof(send_sms_4102), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .gsm_sms = { {}, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "01", }, .pid = 0x40, .dcs = 0xF0, .udl = 1, .ud = " " } } } }; /* The TP-UDL should be 4, instead of 24 */ static struct send_sms_test send_sms_data_511 = { .pdu = send_sms_511, .pdu_len = sizeof(send_sms_511), .qualifier = 0x00, .alpha_id = "中一", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0x08, .udl = 4, .ud = "中一" } } } }; /* The TP-UDL should be 4, instead of 24 */ static struct send_sms_test send_sms_data_512 = { .pdu = send_sms_512, .pdu_len = sizeof(send_sms_512), .qualifier = 0x00, .alpha_id = "中一", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0x08, .udl = 4, .ud = "中一" } } } }; /* The TP-UDL should be 4, instead of 24 */ static struct send_sms_test send_sms_data_513 = { .pdu = send_sms_513, .pdu_len = sizeof(send_sms_513), .qualifier = 0x00, .alpha_id = "中一", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0x08, .udl = 4, .ud = "中一" } } } }; static struct send_sms_test send_sms_data_611 = { .pdu = send_sms_611, .pdu_len = sizeof(send_sms_611), .qualifier = 0x00, .alpha_id = "80ル0", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0x08, .udl = 8, .ud = "80ル1" } } } }; static struct send_sms_test send_sms_data_612 = { .pdu = send_sms_612, .pdu_len = sizeof(send_sms_612), .qualifier = 0x00, .alpha_id = "81ル1", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0x08, .udl = 8, .ud = "80ル2" } } } }; static struct send_sms_test send_sms_data_613 = { .pdu = send_sms_613, .pdu_len = sizeof(send_sms_613), .qualifier = 0x00, .alpha_id = "82ル2", .gsm_sms = { { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "112233445566778", }, SMS_TYPE_SUBMIT, {.submit = { .mr = 0x00, .daddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "012345678", }, .pid = 0x40, .dcs = 0x08, .udl = 8, .ud = "80ル3" } } } }; static void test_send_sms(gconstpointer data) { const struct send_sms_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_SEND_SMS); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_NETWORK); check_alpha_id(command->send_sms.alpha_id, test->alpha_id); check_gsm_sms(&command->send_sms.gsm_sms, &test->gsm_sms); check_cdma_sms_tpdu(&command->send_sms.cdma_sms, &test->cdma_sms); check_icon_id(&command->send_sms.icon_id, &test->icon_id); check_text_attr(&command->send_sms.text_attr, &test->text_attr); check_frame_id(&command->send_sms.frame_id, &test->frame_id); stk_command_free(command); } struct send_ss_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *alpha_id; struct stk_ss ss; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; static unsigned char send_ss_111[] = { 0xD0, 0x29, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0C, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_141[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0C, 0x43, 0x61, 0x6C, 0x6C, 0x20, 0x46, 0x6F, 0x72, 0x77, 0x61, 0x72, 0x64, 0x89, 0x14, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0xA7, 0x11, 0xFB }; static unsigned char send_ss_151[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x81, 0xEB, 0x45, 0x76, 0x65, 0x6E, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x46, 0x69, 0x78, 0x65, 0x64, 0x20, 0x44, 0x69, 0x61, 0x6C, 0x6C, 0x69, 0x6E, 0x67, 0x20, 0x4E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x65, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x64, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6C, 0x65, 0x6D, 0x65, 0x6E, 0x74, 0x61, 0x72, 0x79, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x45, 0x4E, 0x44, 0x20, 0x53, 0x53, 0x20, 0x70, 0x72, 0x6F, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x63, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x73, 0x68, 0x61, 0x6C, 0x6C, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6B, 0x65, 0x64, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6E, 0x73, 0x74, 0x20, 0x74, 0x68, 0x6F, 0x73, 0x65, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x46, 0x44, 0x4E, 0x20, 0x6C, 0x69, 0x73, 0x74, 0x2E, 0x20, 0x55, 0x70, 0x6F, 0x6E, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4D, 0x45, 0x20, 0x73, 0x68, 0x61, 0x6C, 0x6C, 0x20, 0x64, 0x65, 0x63, 0x69, 0x89, 0x04, 0xFF, 0xBA, 0x13, 0xFB }; static unsigned char send_ss_161[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x00, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_211[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0A, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0x9E, 0x02, 0x00, 0x01 }; static unsigned char send_ss_221[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x43, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0x9E, 0x02, 0x00, 0x02 }; static unsigned char send_ss_231[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0A, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0x9E, 0x02, 0x01, 0x01 }; static unsigned char send_ss_241[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x89, 0x0E, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xB9, 0x9E, 0x02, 0x01, 0x01 }; static unsigned char send_ss_311[] = { 0xD0, 0x36, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_411[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ss_412[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_421[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x01, 0xB4 }; static unsigned char send_ss_422[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_431[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x02, 0xB4 }; static unsigned char send_ss_432[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_441[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x04, 0xB4 }; static unsigned char send_ss_442[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ss_443[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_451[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x08, 0xB4 }; static unsigned char send_ss_452[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ss_453[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_461[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x10, 0xB4 }; static unsigned char send_ss_462[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ss_463[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_471[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x20, 0xB4 }; static unsigned char send_ss_472[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ss_473[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_481[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x40, 0xB4 }; static unsigned char send_ss_482[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ss_483[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_491[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x80, 0xB4 }; static unsigned char send_ss_492[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ss_493[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_4101[] = { 0xD0, 0x33, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ss_4102[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_511[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x05, 0x80, 0x4F, 0x60, 0x59, 0x7D, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static unsigned char send_ss_611[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x11, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x03, 0x80, 0x30, 0xEB, 0x89, 0x10, 0x91, 0xAA, 0x12, 0x0A, 0x21, 0x43, 0x65, 0x87, 0x09, 0x21, 0x43, 0x65, 0x87, 0xA9, 0x01, 0xFB }; static struct send_ss_test send_ss_data_111 = { .pdu = send_ss_111, .pdu_len = sizeof(send_ss_111), .qualifier = 0x00, .alpha_id = "Call Forward", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_141 = { .pdu = send_ss_141, .pdu_len = sizeof(send_ss_141), .qualifier = 0x00, .alpha_id = "Call Forward", .ss = { .ton_npi = 0x91, .ss = "**21*0123456789012345678901234567*11#" } }; static struct send_ss_test send_ss_data_151 = { .pdu = send_ss_151, .pdu_len = sizeof(send_ss_151), .qualifier = 0x00, .alpha_id = "Even if the Fixed Dialling Number service is enabled, the " "supplementary service control string included in the SEND SS " "proactive command shall not be checked against those of the " "FDN list. Upon receiving this command, the ME shall deci", .ss = { .ton_npi = 0xFF, .ss = "*#31#" } }; static struct send_ss_test send_ss_data_161 = { .pdu = send_ss_161, .pdu_len = sizeof(send_ss_161), .qualifier = 0x00, .alpha_id = "", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_211 = { .pdu = send_ss_211, .pdu_len = sizeof(send_ss_211), .qualifier = 0x00, .alpha_id = "Basic Icon", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct send_ss_test send_ss_data_221 = { .pdu = send_ss_221, .pdu_len = sizeof(send_ss_221), .qualifier = 0x00, .alpha_id = "Colour Icon", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x02 } }; static struct send_ss_test send_ss_data_231 = { .pdu = send_ss_231, .pdu_len = sizeof(send_ss_231), .qualifier = 0x00, .alpha_id = "Basic Icon", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct send_ss_test send_ss_data_241 = { .pdu = send_ss_241, .pdu_len = sizeof(send_ss_241), .qualifier = 0x00, .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789#" }, .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct send_ss_test send_ss_data_311 = { .pdu = send_ss_311, .pdu_len = sizeof(send_ss_311), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_411 = { .pdu = send_ss_411, .pdu_len = sizeof(send_ss_411), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ss_test send_ss_data_412 = { .pdu = send_ss_412, .pdu_len = sizeof(send_ss_412), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_421 = { .pdu = send_ss_421, .pdu_len = sizeof(send_ss_421), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x01, 0xB4 } } }; static struct send_ss_test send_ss_data_422 = { .pdu = send_ss_422, .pdu_len = sizeof(send_ss_422), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_431 = { .pdu = send_ss_431, .pdu_len = sizeof(send_ss_431), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x02, 0xB4 } } }; static struct send_ss_test send_ss_data_432 = { .pdu = send_ss_432, .pdu_len = sizeof(send_ss_432), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_441 = { .pdu = send_ss_441, .pdu_len = sizeof(send_ss_441), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x04, 0xB4 } } }; static struct send_ss_test send_ss_data_442 = { .pdu = send_ss_442, .pdu_len = sizeof(send_ss_442), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ss_test send_ss_data_443 = { .pdu = send_ss_443, .pdu_len = sizeof(send_ss_443), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_451 = { .pdu = send_ss_451, .pdu_len = sizeof(send_ss_451), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x08, 0xB4 } } }; static struct send_ss_test send_ss_data_452 = { .pdu = send_ss_452, .pdu_len = sizeof(send_ss_452), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ss_test send_ss_data_453 = { .pdu = send_ss_453, .pdu_len = sizeof(send_ss_453), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_461 = { .pdu = send_ss_461, .pdu_len = sizeof(send_ss_461), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x10, 0xB4 } } }; static struct send_ss_test send_ss_data_462 = { .pdu = send_ss_462, .pdu_len = sizeof(send_ss_462), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ss_test send_ss_data_463 = { .pdu = send_ss_463, .pdu_len = sizeof(send_ss_463), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_471 = { .pdu = send_ss_471, .pdu_len = sizeof(send_ss_471), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x20, 0xB4 } } }; static struct send_ss_test send_ss_data_472 = { .pdu = send_ss_472, .pdu_len = sizeof(send_ss_472), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ss_test send_ss_data_473 = { .pdu = send_ss_473, .pdu_len = sizeof(send_ss_473), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_481 = { .pdu = send_ss_481, .pdu_len = sizeof(send_ss_481), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x40, 0xB4 } } }; static struct send_ss_test send_ss_data_482 = { .pdu = send_ss_482, .pdu_len = sizeof(send_ss_482), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ss_test send_ss_data_483 = { .pdu = send_ss_483, .pdu_len = sizeof(send_ss_483), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_491 = { .pdu = send_ss_491, .pdu_len = sizeof(send_ss_491), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x80, 0xB4 } } }; static struct send_ss_test send_ss_data_492 = { .pdu = send_ss_492, .pdu_len = sizeof(send_ss_492), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ss_test send_ss_data_493 = { .pdu = send_ss_493, .pdu_len = sizeof(send_ss_493), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_4101 = { .pdu = send_ss_4101, .pdu_len = sizeof(send_ss_4101), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" }, .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ss_test send_ss_data_4102 = { .pdu = send_ss_4102, .pdu_len = sizeof(send_ss_4102), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_511 = { .pdu = send_ss_511, .pdu_len = sizeof(send_ss_511), .qualifier = 0x00, .alpha_id = "你好", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static struct send_ss_test send_ss_data_611 = { .pdu = send_ss_611, .pdu_len = sizeof(send_ss_611), .qualifier = 0x00, .alpha_id = "ル", .ss = { .ton_npi = 0x91, .ss = "**21*01234567890123456789*10#" } }; static void test_send_ss(gconstpointer data) { const struct send_ss_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_SEND_SS); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_NETWORK); check_alpha_id(command->send_ss.alpha_id, test->alpha_id); check_ss(&command->send_ss.ss, &test->ss); check_icon_id(&command->send_ss.icon_id, &test->icon_id); check_text_attr(&command->send_ss.text_attr, &test->text_attr); check_frame_id(&command->send_ss.frame_id, &test->frame_id); stk_command_free(command); } struct send_ussd_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *alpha_id; char *ussd; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; static unsigned char send_ussd_111[] = { 0xD0, 0x50, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0A, 0x37, 0x2D, 0x62, 0x69, 0x74, 0x20, 0x55, 0x53, 0x53, 0x44, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_121[] = { 0xD0, 0x58, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0A, 0x38, 0x2D, 0x62, 0x69, 0x74, 0x20, 0x55, 0x53, 0x53, 0x44, 0x8A, 0x41, 0x44, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x2D, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x2D, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30 }; static unsigned char send_ussd_131[] = { 0xD0, 0x2F, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x09, 0x55, 0x43, 0x53, 0x32, 0x20, 0x55, 0x53, 0x53, 0x44, 0x8A, 0x19, 0x48, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15 }; static unsigned char send_ussd_161[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x81, 0xB6, 0x6F, 0x6E, 0x63, 0x65, 0x20, 0x61, 0x20, 0x52, 0x45, 0x4C, 0x45, 0x41, 0x53, 0x45, 0x20, 0x43, 0x4F, 0x4D, 0x50, 0x4C, 0x45, 0x54, 0x45, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x55, 0x53, 0x53, 0x44, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x20, 0x52, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x63, 0x6F, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x61, 0x6E, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x20, 0x68, 0x61, 0x73, 0x20, 0x62, 0x65, 0x65, 0x6E, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6E, 0x65, 0x74, 0x77, 0x6F, 0x72, 0x6B, 0x2C, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4D, 0x45, 0x20, 0x73, 0x68, 0x61, 0x6C, 0x6C, 0x20, 0x69, 0x6E, 0x66, 0x6F, 0x72, 0x6D, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x49, 0x4D, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x68, 0x61, 0x73, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_171[] = { 0xD0, 0x44, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_181[] = { 0xD0, 0x46, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x00, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_211[] = { 0xD0, 0x54, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0A, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0x9E, 0x02, 0x00, 0x01 }; static unsigned char send_ussd_221[] = { 0xD0, 0x54, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0A, 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0x9E, 0x02, 0x00, 0x02 }; static unsigned char send_ussd_231[] = { 0xD0, 0x54, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0A, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0x9E, 0x02, 0x01, 0x01 }; static unsigned char send_ussd_241[] = { 0xD0, 0x48, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0x9E, 0x02, 0x01, 0x01 }; static unsigned char send_ussd_311[] = { 0xD0, 0x5F, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_411[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ussd_412[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_421[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x01, 0xB4 }; static unsigned char send_ussd_422[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_431[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x02, 0xB4 }; static unsigned char send_ussd_432[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_441[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x04, 0xB4 }; static unsigned char send_ussd_442[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ussd_443[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_451[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x08, 0xB4 }; static unsigned char send_ussd_452[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ussd_453[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_461[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x10, 0xB4 }; static unsigned char send_ussd_462[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ussd_463[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_471[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x20, 0xB4 }; static unsigned char send_ussd_472[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ussd_473[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_481[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x40, 0xB4 }; static unsigned char send_ussd_482[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ussd_483[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_491[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x80, 0xB4 }; static unsigned char send_ussd_492[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ussd_493[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x33, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_4101[] = { 0xD0, 0x5C, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x31, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char send_ussd_4102[] = { 0xD0, 0x56, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x10, 0x54, 0x65, 0x78, 0x74, 0x20, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x32, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_511[] = { 0xD0, 0x4B, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x05, 0x80, 0x4F, 0x60, 0x59, 0x7D, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static unsigned char send_ussd_611[] = { 0xD0, 0x49, 0x81, 0x03, 0x01, 0x12, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x03, 0x80, 0x30, 0xEB, 0x8A, 0x39, 0xF0, 0x41, 0xE1, 0x90, 0x58, 0x34, 0x1E, 0x91, 0x49, 0xE5, 0x92, 0xD9, 0x74, 0x3E, 0xA1, 0x51, 0xE9, 0x94, 0x5A, 0xB5, 0x5E, 0xB1, 0x59, 0x6D, 0x2B, 0x2C, 0x1E, 0x93, 0xCB, 0xE6, 0x33, 0x3A, 0xAD, 0x5E, 0xB3, 0xDB, 0xEE, 0x37, 0x3C, 0x2E, 0x9F, 0xD3, 0xEB, 0xF6, 0x3B, 0x3E, 0xAF, 0x6F, 0xC5, 0x64, 0x33, 0x5A, 0xCD, 0x76, 0xC3, 0xE5, 0x60 }; static struct send_ussd_test send_ussd_data_111 = { .pdu = send_ussd_111, .pdu_len = sizeof(send_ussd_111), .qualifier = 0x00, .alpha_id = "7-bit USSD", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_121 = { .pdu = send_ussd_121, .pdu_len = sizeof(send_ussd_121), .qualifier = 0x00, .alpha_id = "8-bit USSD", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_131 = { .pdu = send_ussd_131, .pdu_len = sizeof(send_ussd_131), .qualifier = 0x00, .alpha_id = "UCS2 USSD", .ussd = "ЗДРАВСТВУЙТЕ" }; static struct send_ussd_test send_ussd_data_161 = { .pdu = send_ussd_161, .pdu_len = sizeof(send_ussd_161), .qualifier = 0x00, .alpha_id = "once a RELEASE COMPLETE message containing the USSD " "Return Result message not containing an error has been " "received from the network, the ME shall inform the SIM " "that the command has", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_171 = { .pdu = send_ussd_171, .pdu_len = sizeof(send_ussd_171), .qualifier = 0x00, .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_181 = { .pdu = send_ussd_181, .pdu_len = sizeof(send_ussd_181), .qualifier = 0x00, .alpha_id = "", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_211 = { .pdu = send_ussd_211, .pdu_len = sizeof(send_ussd_211), .qualifier = 0x00, .alpha_id = "Basic Icon", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct send_ussd_test send_ussd_data_221 = { .pdu = send_ussd_221, .pdu_len = sizeof(send_ussd_221), .qualifier = 0x00, .alpha_id = "Color Icon", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x02 } }; static struct send_ussd_test send_ussd_data_231 = { .pdu = send_ussd_231, .pdu_len = sizeof(send_ussd_231), .qualifier = 0x00, .alpha_id = "Basic Icon", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct send_ussd_test send_ussd_data_241 = { .pdu = send_ussd_241, .pdu_len = sizeof(send_ussd_241), .qualifier = 0x00, .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; /* The ussd is not complete in spec */ static struct send_ussd_test send_ussd_data_311 = { .pdu = send_ussd_311, .pdu_len = sizeof(send_ussd_311), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_411 = { .pdu = send_ussd_411, .pdu_len = sizeof(send_ussd_411), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ussd_test send_ussd_data_412 = { .pdu = send_ussd_412, .pdu_len = sizeof(send_ussd_412), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_421 = { .pdu = send_ussd_421, .pdu_len = sizeof(send_ussd_421), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x01, 0xB4 } } }; static struct send_ussd_test send_ussd_data_422 = { .pdu = send_ussd_422, .pdu_len = sizeof(send_ussd_422), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_431 = { .pdu = send_ussd_431, .pdu_len = sizeof(send_ussd_431), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x02, 0xB4 } } }; static struct send_ussd_test send_ussd_data_432 = { .pdu = send_ussd_432, .pdu_len = sizeof(send_ussd_432), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_441 = { .pdu = send_ussd_441, .pdu_len = sizeof(send_ussd_441), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x04, 0xB4 } } }; static struct send_ussd_test send_ussd_data_442 = { .pdu = send_ussd_442, .pdu_len = sizeof(send_ussd_442), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ussd_test send_ussd_data_443 = { .pdu = send_ussd_443, .pdu_len = sizeof(send_ussd_443), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_451 = { .pdu = send_ussd_451, .pdu_len = sizeof(send_ussd_451), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x08, 0xB4 } } }; static struct send_ussd_test send_ussd_data_452 = { .pdu = send_ussd_452, .pdu_len = sizeof(send_ussd_452), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ussd_test send_ussd_data_453 = { .pdu = send_ussd_453, .pdu_len = sizeof(send_ussd_453), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_461 = { .pdu = send_ussd_461, .pdu_len = sizeof(send_ussd_461), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x10, 0xB4 } } }; static struct send_ussd_test send_ussd_data_462 = { .pdu = send_ussd_462, .pdu_len = sizeof(send_ussd_462), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ussd_test send_ussd_data_463 = { .pdu = send_ussd_463, .pdu_len = sizeof(send_ussd_463), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_471 = { .pdu = send_ussd_471, .pdu_len = sizeof(send_ussd_471), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x20, 0xB4 } } }; static struct send_ussd_test send_ussd_data_472 = { .pdu = send_ussd_472, .pdu_len = sizeof(send_ussd_472), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ussd_test send_ussd_data_473 = { .pdu = send_ussd_473, .pdu_len = sizeof(send_ussd_473), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_481 = { .pdu = send_ussd_481, .pdu_len = sizeof(send_ussd_481), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x40, 0xB4 } } }; static struct send_ussd_test send_ussd_data_482 = { .pdu = send_ussd_482, .pdu_len = sizeof(send_ussd_482), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ussd_test send_ussd_data_483 = { .pdu = send_ussd_483, .pdu_len = sizeof(send_ussd_483), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_491 = { .pdu = send_ussd_491, .pdu_len = sizeof(send_ussd_491), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x80, 0xB4 } } }; static struct send_ussd_test send_ussd_data_492 = { .pdu = send_ussd_492, .pdu_len = sizeof(send_ussd_492), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ussd_test send_ussd_data_493 = { .pdu = send_ussd_493, .pdu_len = sizeof(send_ussd_493), .qualifier = 0x00, .alpha_id = "Text Attribute 3", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_4101 = { .pdu = send_ussd_4101, .pdu_len = sizeof(send_ussd_4101), .qualifier = 0x00, .alpha_id = "Text Attribute 1", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct send_ussd_test send_ussd_data_4102 = { .pdu = send_ussd_4102, .pdu_len = sizeof(send_ussd_4102), .qualifier = 0x00, .alpha_id = "Text Attribute 2", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_511 = { .pdu = send_ussd_511, .pdu_len = sizeof(send_ussd_511), .qualifier = 0x00, .alpha_id = "你好", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static struct send_ussd_test send_ussd_data_611 = { .pdu = send_ussd_611, .pdu_len = sizeof(send_ussd_611), .qualifier = 0x00, .alpha_id = "ル", .ussd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-" "1234567890" }; static void test_send_ussd(gconstpointer data) { const struct send_ussd_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_SEND_USSD); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_NETWORK); check_alpha_id(command->send_ussd.alpha_id, test->alpha_id); check_ussd(&command->send_ussd.ussd_string, test->ussd); check_icon_id(&command->send_ussd.icon_id, &test->icon_id); check_text_attr(&command->send_ussd.text_attr, &test->text_attr); check_frame_id(&command->send_ussd.frame_id, &test->frame_id); stk_command_free(command); } struct setup_call_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *alpha_id_usr_cfm; struct stk_address addr; struct stk_ccp ccp; struct stk_subaddress subaddr; struct stk_duration duration; struct stk_icon_id icon_id_usr_cfm; char *alpha_id_call_setup; struct stk_icon_id icon_id_call_setup; struct stk_text_attribute text_attr_usr_cfm; struct stk_text_attribute text_attr_call_setup; struct stk_frame_id frame_id; }; static unsigned char setup_call_111[] = { 0xD0, 0x1E, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x08, 0x4E, 0x6F, 0x74, 0x20, 0x62, 0x75, 0x73, 0x79, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C }; static unsigned char setup_call_141[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x10, 0x02, 0x82, 0x02, 0x81, 0x83, 0x85, 0x07, 0x4F, 0x6E, 0x20, 0x68, 0x6F, 0x6C, 0x64, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C }; static unsigned char setup_call_151[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x10, 0x04, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0A, 0x44, 0x69, 0x73, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C }; static unsigned char setup_call_181[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x11, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6C, 0x69, 0x74, 0x79, 0x20, 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x67, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x87, 0x02, 0x01, 0xA0 }; static unsigned char setup_call_191[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x10, 0x01, 0x82, 0x02, 0x81, 0x83, 0x86, 0x11, 0x91, 0x10, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, 0x54, 0x76, 0x98, 0x10 }; static unsigned char setup_call_1101[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01, 0x10, 0x01, 0x82, 0x02, 0x81, 0x83, 0x85, 0x81, 0xED, 0x54, 0x68, 0x72, 0x65, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6E, 0x65, 0x64, 0x3A, 0x20, 0x2D, 0x20, 0x73, 0x65, 0x74, 0x20, 0x75, 0x70, 0x20, 0x61, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x2C, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6F, 0x6E, 0x6C, 0x79, 0x20, 0x69, 0x66, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x74, 0x6C, 0x79, 0x20, 0x62, 0x75, 0x73, 0x79, 0x20, 0x6F, 0x6E, 0x20, 0x61, 0x6E, 0x6F, 0x74, 0x68, 0x65, 0x72, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x3B, 0x20, 0x2D, 0x20, 0x73, 0x65, 0x74, 0x20, 0x75, 0x70, 0x20, 0x61, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x2C, 0x20, 0x70, 0x75, 0x74, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x61, 0x6C, 0x6C, 0x20, 0x6F, 0x74, 0x68, 0x65, 0x72, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x73, 0x20, 0x28, 0x69, 0x66, 0x20, 0x61, 0x6E, 0x79, 0x29, 0x20, 0x6F, 0x6E, 0x20, 0x68, 0x6F, 0x6C, 0x64, 0x3B, 0x20, 0x2D, 0x20, 0x73, 0x65, 0x74, 0x20, 0x75, 0x70, 0x20, 0x61, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x2C, 0x20, 0x64, 0x69, 0x73, 0x63, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x61, 0x6C, 0x6C, 0x20, 0x6F, 0x74, 0x68, 0x65, 0x72, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x73, 0x20, 0x28, 0x69, 0x66, 0x20, 0x61, 0x6E, 0x79, 0x29, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x2E, 0x20, 0x46, 0x6F, 0x72, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2C, 0x20, 0x86, 0x02, 0x91, 0x10 }; static unsigned char setup_call_1111[] = { 0xD0, 0x2B, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0C, 0x43, 0x61, 0x6C, 0x6C, 0x65, 0x64, 0x20, 0x70, 0x61, 0x72, 0x74, 0x79, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x88, 0x07, 0x80, 0x50, 0x95, 0x95, 0x95, 0x95, 0x95 }; static unsigned char setup_call_1121[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x10, 0x01, 0x82, 0x02, 0x81, 0x83, 0x85, 0x08, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x84, 0x02, 0x01, 0x0A }; static unsigned char setup_call_211[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0C, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x04, 0x43, 0x41, 0x4C, 0x4C }; static unsigned char setup_call_311[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x16, 0x53, 0x65, 0x74, 0x20, 0x75, 0x70, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x31, 0x2E, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x9E, 0x02, 0x01, 0x01 }; static unsigned char setup_call_321[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x16, 0x53, 0x65, 0x74, 0x20, 0x75, 0x70, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x32, 0x2E, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x9E, 0x02, 0x00, 0x01 }; static unsigned char setup_call_331[] = { 0xD0, 0x30, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x16, 0x53, 0x65, 0x74, 0x20, 0x75, 0x70, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x33, 0x2E, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x9E, 0x02, 0x01, 0x02 }; static unsigned char setup_call_341[] = { 0xD0, 0x4C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x16, 0x53, 0x65, 0x74, 0x20, 0x75, 0x70, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x34, 0x2E, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x9E, 0x02, 0x00, 0x01, 0x85, 0x16, 0x53, 0x65, 0x74, 0x20, 0x75, 0x70, 0x20, 0x63, 0x61, 0x6C, 0x6C, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x34, 0x2E, 0x32, 0x9E, 0x02, 0x00, 0x01 }; static unsigned char setup_call_411[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char setup_call_412[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x32, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x32 }; static unsigned char setup_call_421[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0E, 0x01, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x01, 0xB4 }; static unsigned char setup_call_422[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x32, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x32 }; static unsigned char setup_call_431[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0E, 0x02, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x02, 0xB4 }; static unsigned char setup_call_432[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x32, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x32 }; static unsigned char setup_call_441[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0E, 0x04, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x04, 0xB4 }; static unsigned char setup_call_442[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x32, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char setup_call_443[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x33, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x33 }; static unsigned char setup_call_451[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0E, 0x08, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x08, 0xB4 }; static unsigned char setup_call_452[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x32, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char setup_call_453[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x33, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x33 }; static unsigned char setup_call_461[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0E, 0x10, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x10, 0xB4 }; static unsigned char setup_call_462[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x32, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char setup_call_463[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x33, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x33 }; static unsigned char setup_call_471[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0E, 0x20, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x20, 0xB4 }; static unsigned char setup_call_472[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x32, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char setup_call_473[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x33, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x33 }; static unsigned char setup_call_481[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0E, 0x40, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x40, 0xB4 }; static unsigned char setup_call_482[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x32, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char setup_call_483[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x33, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x33 }; static unsigned char setup_call_491[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0E, 0x80, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x80, 0xB4 }; static unsigned char setup_call_492[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x32, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x00, 0xB4 }; static unsigned char setup_call_493[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x33, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x33 }; static unsigned char setup_call_4101[] = { 0xD0, 0x38, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x31, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4, 0xD0, 0x04, 0x00, 0x06, 0x00, 0x4B }; static unsigned char setup_call_4102[] = { 0xD0, 0x2C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0E, 0x43, 0x4F, 0x4E, 0x46, 0x49, 0x52, 0x4D, 0x41, 0x54, 0x49, 0x4F, 0x4E, 0x20, 0x32, 0x86, 0x09, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x1C, 0x2C, 0x85, 0x06, 0x43, 0x41, 0x4C, 0x4C, 0x20, 0x32 }; static unsigned char setup_call_511[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x86, 0x07, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65 }; static unsigned char setup_call_521[] = { 0xD0, 0x4C, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x1B, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x31, 0x86, 0x07, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x85, 0x1B, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0x00, 0x32 }; static unsigned char setup_call_611[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x05, 0x80, 0x4E, 0x0D, 0x5F, 0xD9, 0x86, 0x07, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65 }; static unsigned char setup_call_621[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x05, 0x80, 0x78, 0x6E, 0x5B, 0x9A, 0x86, 0x07, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x85, 0x07, 0x80, 0x62, 0x53, 0x75, 0x35, 0x8B, 0xDD }; static unsigned char setup_call_711[] = { 0xD0, 0x17, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x03, 0x80, 0x30, 0xEB, 0x86, 0x07, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65 }; static unsigned char setup_call_721[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x05, 0x80, 0x30, 0xEB, 0x00, 0x31, 0x86, 0x07, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x85, 0x05, 0x80, 0x30, 0xEB, 0x00, 0x32 }; static struct setup_call_test setup_call_data_111 = { .pdu = setup_call_111, .pdu_len = sizeof(setup_call_111), .qualifier = 0x00, .alpha_id_usr_cfm = "Not busy", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" } }; static struct setup_call_test setup_call_data_141 = { .pdu = setup_call_141, .pdu_len = sizeof(setup_call_141), .qualifier = 0x02, .alpha_id_usr_cfm = "On hold", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" } }; static struct setup_call_test setup_call_data_151 = { .pdu = setup_call_151, .pdu_len = sizeof(setup_call_151), .qualifier = 0x04, .alpha_id_usr_cfm = "Disconnect", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" } }; static struct setup_call_test setup_call_data_181 = { .pdu = setup_call_181, .pdu_len = sizeof(setup_call_181), .qualifier = 0x00, .alpha_id_usr_cfm = "Capability config", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .ccp = { .len = 0x02, .ccp = { 0x01, 0xA0 } } }; static struct setup_call_test setup_call_data_191 = { .pdu = setup_call_191, .pdu_len = sizeof(setup_call_191), .qualifier = 0x01, .addr = { .ton_npi = 0x91, .number = "01234567890123456789012345678901" } }; static struct setup_call_test setup_call_data_1101 = { .pdu = setup_call_1101, .pdu_len = sizeof(setup_call_1101), .qualifier = 0x01, .alpha_id_usr_cfm = "Three types are defined: - set up a call, but " "only if not currently busy on another call; - set " "up a call, putting all other calls (if any) on hold; " "- set up a call, disconnecting all other calls (if " "any) first. For each of these types, ", .addr = { .ton_npi = 0x91, .number = "01" } }; static struct setup_call_test setup_call_data_1111 = { .pdu = setup_call_1111, .pdu_len = sizeof(setup_call_1111), .qualifier = 0x00, .alpha_id_usr_cfm = "Called party", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .subaddr = { .len = 0x07, .subaddr = { 0x80, 0x50, 0x95, 0x95, 0x95, 0x95, 0x95 } } }; static struct setup_call_test setup_call_data_1121 = { .pdu = setup_call_1121, .pdu_len = sizeof(setup_call_1121), .qualifier = 0x01, .alpha_id_usr_cfm = "Duration", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 10, } }; static struct setup_call_test setup_call_data_211 = { .pdu = setup_call_211, .pdu_len = sizeof(setup_call_211), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL" }; static struct setup_call_test setup_call_data_311 = { .pdu = setup_call_311, .pdu_len = sizeof(setup_call_311), .qualifier = 0x00, .alpha_id_usr_cfm = "Set up call Icon 3.1.1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .icon_id_usr_cfm = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct setup_call_test setup_call_data_321 = { .pdu = setup_call_321, .pdu_len = sizeof(setup_call_321), .qualifier = 0x00, .alpha_id_usr_cfm = "Set up call Icon 3.2.1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .icon_id_usr_cfm = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct setup_call_test setup_call_data_331 = { .pdu = setup_call_331, .pdu_len = sizeof(setup_call_331), .qualifier = 0x00, .alpha_id_usr_cfm = "Set up call Icon 3.3.1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .icon_id_usr_cfm = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x02 } }; static struct setup_call_test setup_call_data_341 = { .pdu = setup_call_341, .pdu_len = sizeof(setup_call_341), .qualifier = 0x00, .alpha_id_usr_cfm = "Set up call Icon 3.4.1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .icon_id_usr_cfm = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 }, .alpha_id_call_setup = "Set up call Icon 3.4.2", .icon_id_call_setup = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct setup_call_test setup_call_data_411 = { .pdu = setup_call_411, .pdu_len = sizeof(setup_call_411), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 1", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x00, 0xB4 } } }; static struct setup_call_test setup_call_data_412 = { .pdu = setup_call_412, .pdu_len = sizeof(setup_call_412), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 2", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 2" }; static struct setup_call_test setup_call_data_421 = { .pdu = setup_call_421, .pdu_len = sizeof(setup_call_421), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 1", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x01, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x01, 0xB4 } } }; static struct setup_call_test setup_call_data_422 = { .pdu = setup_call_422, .pdu_len = sizeof(setup_call_422), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 2", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 2" }; static struct setup_call_test setup_call_data_431 = { .pdu = setup_call_431, .pdu_len = sizeof(setup_call_431), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 1", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x02, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x02, 0xB4 } } }; static struct setup_call_test setup_call_data_432 = { .pdu = setup_call_432, .pdu_len = sizeof(setup_call_432), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 2", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 2" }; static struct setup_call_test setup_call_data_441 = { .pdu = setup_call_441, .pdu_len = sizeof(setup_call_441), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 1", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x04, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x04, 0xB4 } } }; static struct setup_call_test setup_call_data_442 = { .pdu = setup_call_442, .pdu_len = sizeof(setup_call_442), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 2", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 2", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x00, 0xB4 } } }; static struct setup_call_test setup_call_data_443 = { .pdu = setup_call_443, .pdu_len = sizeof(setup_call_443), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 3", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 3" }; static struct setup_call_test setup_call_data_451 = { .pdu = setup_call_451, .pdu_len = sizeof(setup_call_451), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 1", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x08, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x08, 0xB4 } } }; static struct setup_call_test setup_call_data_452 = { .pdu = setup_call_452, .pdu_len = sizeof(setup_call_452), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 2", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 2", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x00, 0xB4 } } }; static struct setup_call_test setup_call_data_453 = { .pdu = setup_call_453, .pdu_len = sizeof(setup_call_453), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 3", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 3" }; static struct setup_call_test setup_call_data_461 = { .pdu = setup_call_461, .pdu_len = sizeof(setup_call_461), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 1", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x10, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x10, 0xB4 } } }; static struct setup_call_test setup_call_data_462 = { .pdu = setup_call_462, .pdu_len = sizeof(setup_call_462), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 2", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 2", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x00, 0xB4 } } }; static struct setup_call_test setup_call_data_463 = { .pdu = setup_call_463, .pdu_len = sizeof(setup_call_463), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 3", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 3" }; static struct setup_call_test setup_call_data_471 = { .pdu = setup_call_471, .pdu_len = sizeof(setup_call_471), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 1", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x20, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x20, 0xB4 } } }; static struct setup_call_test setup_call_data_472 = { .pdu = setup_call_472, .pdu_len = sizeof(setup_call_472), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 2", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 2", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x00, 0xB4 } } }; static struct setup_call_test setup_call_data_473 = { .pdu = setup_call_473, .pdu_len = sizeof(setup_call_473), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 3", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 3" }; static struct setup_call_test setup_call_data_481 = { .pdu = setup_call_481, .pdu_len = sizeof(setup_call_481), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 1", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x40, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x40, 0xB4 } } }; static struct setup_call_test setup_call_data_482 = { .pdu = setup_call_482, .pdu_len = sizeof(setup_call_482), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 2", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 2", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x00, 0xB4 } } }; static struct setup_call_test setup_call_data_483 = { .pdu = setup_call_483, .pdu_len = sizeof(setup_call_483), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 3", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 3" }; static struct setup_call_test setup_call_data_491 = { .pdu = setup_call_491, .pdu_len = sizeof(setup_call_491), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 1", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x80, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x80, 0xB4 } } }; static struct setup_call_test setup_call_data_492 = { .pdu = setup_call_492, .pdu_len = sizeof(setup_call_492), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 2", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 2", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x00, 0xB4 } } }; static struct setup_call_test setup_call_data_493 = { .pdu = setup_call_493, .pdu_len = sizeof(setup_call_493), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 3", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 3" }; static struct setup_call_test setup_call_data_4101 = { .pdu = setup_call_4101, .pdu_len = sizeof(setup_call_4101), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 1", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 1", .text_attr_usr_cfm = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, .text_attr_call_setup = { .len = 4, .attributes = { 0x00, 0x06, 0x00, 0x4B } } }; static struct setup_call_test setup_call_data_4102 = { .pdu = setup_call_4102, .pdu_len = sizeof(setup_call_4102), .qualifier = 0x00, .alpha_id_usr_cfm = "CONFIRMATION 2", .addr = { .ton_npi = 0x91, .number = "012340123456c1c2" }, .alpha_id_call_setup = "CALL 2" }; static struct setup_call_test setup_call_data_511 = { .pdu = setup_call_511, .pdu_len = sizeof(setup_call_511), .qualifier = 0x00, .alpha_id_usr_cfm = "ЗДРАВСТВУЙТЕ", .addr = { .ton_npi = 0x91, .number = "012340123456" } }; static struct setup_call_test setup_call_data_521 = { .pdu = setup_call_521, .pdu_len = sizeof(setup_call_521), .qualifier = 0x00, .alpha_id_usr_cfm = "ЗДРАВСТВУЙТЕ1", .addr = { .ton_npi = 0x91, .number = "012340123456" }, .alpha_id_call_setup = "ЗДРАВСТВУЙТЕ2" }; static struct setup_call_test setup_call_data_611 = { .pdu = setup_call_611, .pdu_len = sizeof(setup_call_611), .qualifier = 0x00, .alpha_id_usr_cfm = "不忙", .addr = { .ton_npi = 0x91, .number = "012340123456" } }; static struct setup_call_test setup_call_data_621 = { .pdu = setup_call_621, .pdu_len = sizeof(setup_call_621), .qualifier = 0x00, .alpha_id_usr_cfm = "确定", .addr = { .ton_npi = 0x91, .number = "012340123456" }, .alpha_id_call_setup = "打电话" }; static struct setup_call_test setup_call_data_711 = { .pdu = setup_call_711, .pdu_len = sizeof(setup_call_711), .qualifier = 0x00, .alpha_id_usr_cfm = "ル", .addr = { .ton_npi = 0x91, .number = "012340123456" } }; static struct setup_call_test setup_call_data_721 = { .pdu = setup_call_721, .pdu_len = sizeof(setup_call_721), .qualifier = 0x00, .alpha_id_usr_cfm = "ル1", .addr = { .ton_npi = 0x91, .number = "012340123456" }, .alpha_id_call_setup = "ル2" }; static void test_setup_call(gconstpointer data) { const struct setup_call_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_SETUP_CALL); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_NETWORK); check_alpha_id(command->setup_call.alpha_id_usr_cfm, test->alpha_id_usr_cfm); check_address(&command->setup_call.addr, &test->addr); check_ccp(&command->setup_call.ccp, &test->ccp); check_subaddress(&command->setup_call.subaddr, &test->subaddr); check_duration(&command->setup_call.duration, &test->duration); check_icon_id(&command->setup_call.icon_id_usr_cfm, &test->icon_id_usr_cfm); check_alpha_id(command->setup_call.alpha_id_call_setup, test->alpha_id_call_setup); check_icon_id(&command->setup_call.icon_id_call_setup, &test->icon_id_call_setup); check_text_attr(&command->setup_call.text_attr_usr_cfm, &test->text_attr_usr_cfm); check_text_attr(&command->setup_call.text_attr_call_setup, &test->text_attr_call_setup); check_frame_id(&command->setup_call.frame_id, &test->frame_id); stk_command_free(command); } struct refresh_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; struct stk_file file_list[MAX_ITEM]; struct stk_aid aid; char *alpha_id; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; static unsigned char refresh_121[] = { 0xD0, 0x10, 0x81, 0x03, 0x01, 0x01, 0x01, 0x82, 0x02, 0x81, 0x82, 0x92, 0x05, 0x01, 0x3F, 0x00, 0x2F, 0xE2 }; static unsigned char refresh_151[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x01, 0x04, 0x82, 0x02, 0x81, 0x82 }; static struct refresh_test refresh_data_121 = { .pdu = refresh_121, .pdu_len = sizeof(refresh_121), .qualifier = 0x01, .file_list = {{ .len = 4, .file = { 0x3F, 0x00, 0x2F, 0xE2 } }} }; static struct refresh_test refresh_data_151 = { .pdu = refresh_151, .pdu_len = sizeof(refresh_151), .qualifier = 0x04 }; /* Defined in TS 102.384 Section 27.22.4.7 */ static void test_refresh(gconstpointer data) { const struct refresh_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_REFRESH); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_file_list(command->refresh.file_list, test->file_list); check_aid(&command->refresh.aid, &test->aid); check_alpha_id(command->refresh.alpha_id, test->alpha_id); check_icon_id(&command->refresh.icon_id, &test->icon_id); check_text_attr(&command->refresh.text_attr, &test->text_attr); check_frame_id(&command->refresh.frame_id, &test->frame_id); stk_command_free(command); } struct polling_off_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; }; static unsigned char polling_off_112[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x04, 0x00, 0x82, 0x02, 0x81, 0x82 }; static struct polling_off_test polling_off_data_112 = { .pdu = polling_off_112, .pdu_len = sizeof(polling_off_112), .qualifier = 0x00, }; static void test_polling_off(gconstpointer data) { const struct polling_off_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_POLLING_OFF); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); stk_command_free(command); } struct provide_local_info_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; }; static unsigned char provide_local_info_121[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x26, 0x01, 0x82, 0x02, 0x81, 0x82 }; static unsigned char provide_local_info_141[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x26, 0x03, 0x82, 0x02, 0x81, 0x82 }; static unsigned char provide_local_info_151[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x26, 0x04, 0x82, 0x02, 0x81, 0x82 }; static unsigned char provide_local_info_181[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x26, 0x07, 0x82, 0x02, 0x81, 0x82 }; static unsigned char provide_local_info_191[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x26, 0x08, 0x82, 0x02, 0x81, 0x82 }; static unsigned char provide_local_info_1111[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x26, 0x0A, 0x82, 0x02, 0x81, 0x82 }; static struct provide_local_info_test provide_local_info_data_121 = { .pdu = provide_local_info_121, .pdu_len = sizeof(provide_local_info_121), .qualifier = 0x01 }; static struct provide_local_info_test provide_local_info_data_141 = { .pdu = provide_local_info_141, .pdu_len = sizeof(provide_local_info_141), .qualifier = 0x03 }; static struct provide_local_info_test provide_local_info_data_151 = { .pdu = provide_local_info_151, .pdu_len = sizeof(provide_local_info_151), .qualifier = 0x04 }; static struct provide_local_info_test provide_local_info_data_181 = { .pdu = provide_local_info_181, .pdu_len = sizeof(provide_local_info_181), .qualifier = 0x07 }; static struct provide_local_info_test provide_local_info_data_191 = { .pdu = provide_local_info_191, .pdu_len = sizeof(provide_local_info_191), .qualifier = 0x08 }; static struct provide_local_info_test provide_local_info_data_1111 = { .pdu = provide_local_info_1111, .pdu_len = sizeof(provide_local_info_1111), .qualifier = 0x0A }; static void test_provide_local_info(gconstpointer data) { const struct provide_local_info_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); stk_command_free(command); } struct setup_event_list_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; struct stk_event_list event_list; }; static unsigned char setup_event_list_111[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x05, 0x00, 0x82, 0x02, 0x81, 0x82, 0x99, 0x01, 0x04 }; static unsigned char setup_event_list_121[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x05, 0x00, 0x82, 0x02, 0x81, 0x82, 0x99, 0x02, 0x05, 0x07 }; static unsigned char setup_event_list_122[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x05, 0x00, 0x82, 0x02, 0x81, 0x82, 0x99, 0x01, 0x07 }; static unsigned char setup_event_list_131[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x05, 0x00, 0x82, 0x02, 0x81, 0x82, 0x99, 0x01, 0x07 }; static unsigned char setup_event_list_132[] = { 0xD0, 0x0B, 0x81, 0x03, 0x01, 0x05, 0x00, 0x82, 0x02, 0x81, 0x82, 0x99, 0x00 }; static unsigned char setup_event_list_141[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x05, 0x00, 0x82, 0x02, 0x81, 0x82, 0x99, 0x01, 0x07 }; static struct setup_event_list_test setup_event_list_data_111 = { .pdu = setup_event_list_111, .pdu_len = sizeof(setup_event_list_111), .qualifier = 0x00, .event_list = { .len = 1, .list = { STK_EVENT_TYPE_USER_ACTIVITY } } }; static struct setup_event_list_test setup_event_list_data_121 = { .pdu = setup_event_list_121, .pdu_len = sizeof(setup_event_list_121), .qualifier = 0x00, .event_list = { .len = 2, .list = { STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE, STK_EVENT_TYPE_LANGUAGE_SELECTION } } }; static struct setup_event_list_test setup_event_list_data_122 = { .pdu = setup_event_list_122, .pdu_len = sizeof(setup_event_list_122), .qualifier = 0x00, .event_list = { .len = 1, .list = { STK_EVENT_TYPE_LANGUAGE_SELECTION } } }; static struct setup_event_list_test setup_event_list_data_131 = { .pdu = setup_event_list_131, .pdu_len = sizeof(setup_event_list_131), .qualifier = 0x00, .event_list = { .len = 1, .list = { STK_EVENT_TYPE_LANGUAGE_SELECTION } } }; static struct setup_event_list_test setup_event_list_data_132 = { .pdu = setup_event_list_132, .pdu_len = sizeof(setup_event_list_132), .qualifier = 0x00 }; static struct setup_event_list_test setup_event_list_data_141 = { .pdu = setup_event_list_141, .pdu_len = sizeof(setup_event_list_141), .qualifier = 0x00, .event_list = { .len = 1, .list = { STK_EVENT_TYPE_LANGUAGE_SELECTION } } }; static void test_setup_event_list(gconstpointer data) { const struct setup_event_list_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_SETUP_EVENT_LIST); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_event_list(&command->setup_event_list.event_list, &test->event_list); stk_command_free(command); } struct perform_card_apdu_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; unsigned char dst; struct stk_c_apdu c_apdu; }; static unsigned char perform_card_apdu_111[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x30, 0x00, 0x82, 0x02, 0x81, 0x11, 0xA2, 0x07, 0xA0, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00 }; static unsigned char perform_card_apdu_112[] = { 0xD0, 0x10, 0x81, 0x03, 0x01, 0x30, 0x00, 0x82, 0x02, 0x81, 0x11, 0xA2, 0x05, 0xA0, 0xC0, 0x00, 0x00, 0x1B }; static unsigned char perform_card_apdu_121[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x30, 0x00, 0x82, 0x02, 0x81, 0x11, 0xA2, 0x07, 0xA0, 0xA4, 0x00, 0x00, 0x02, 0x7F, 0x20 }; static unsigned char perform_card_apdu_122[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x30, 0x00, 0x82, 0x02, 0x81, 0x11, 0xA2, 0x07, 0xA0, 0xA4, 0x00, 0x00, 0x02, 0x6F, 0x30 }; static unsigned char perform_card_apdu_123[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x30, 0x00, 0x82, 0x02, 0x81, 0x11, 0xA2, 0x1D, 0xA0, 0xD6, 0x00, 0x00, 0x18, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }; static unsigned char perform_card_apdu_124[] = { 0xD0, 0x10, 0x81, 0x03, 0x01, 0x30, 0x00, 0x82, 0x02, 0x81, 0x11, 0xA2, 0x05, 0xA0, 0xB0, 0x00, 0x00, 0x18 }; static unsigned char perform_card_apdu_125[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x30, 0x00, 0x82, 0x02, 0x81, 0x11, 0xA2, 0x1D, 0xA0, 0xD6, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static unsigned char perform_card_apdu_151[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x30, 0x00, 0x82, 0x02, 0x81, 0x17, 0xA2, 0x07, 0xA0, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00 }; static unsigned char perform_card_apdu_211[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x30, 0x00, 0x82, 0x02, 0x81, 0x11, 0xA2, 0x07, 0xA0, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00 }; static struct perform_card_apdu_test perform_card_apdu_data_111 = { .pdu = perform_card_apdu_111, .pdu_len = sizeof(perform_card_apdu_111), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1, .c_apdu = { .cla = 0xA0, .ins = STK_INS_SELECT, .p1 = 0x00, .p2 = 0x00, .lc = 0x02, .data = { 0x3F, 0x00 } } }; static struct perform_card_apdu_test perform_card_apdu_data_112 = { .pdu = perform_card_apdu_112, .pdu_len = sizeof(perform_card_apdu_112), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1, .c_apdu = { .cla = 0xA0, .ins = STK_INS_GET_RESPONSE, .p1 = 0x00, .p2 = 0x00, .has_le = 1, .le = 0x1B } }; static struct perform_card_apdu_test perform_card_apdu_data_121 = { .pdu = perform_card_apdu_121, .pdu_len = sizeof(perform_card_apdu_121), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1, .c_apdu = { .cla = 0xA0, .ins = STK_INS_SELECT, .p1 = 0x00, .p2 = 0x00, .lc = 0x02, .data = { 0x7F, 0x20 } } }; static struct perform_card_apdu_test perform_card_apdu_data_122 = { .pdu = perform_card_apdu_122, .pdu_len = sizeof(perform_card_apdu_122), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1, .c_apdu = { .cla = 0xA0, .ins = STK_INS_SELECT, .p1 = 0x00, .p2 = 0x00, .lc = 0x02, .data = { 0x6F, 0x30 } } }; /* Byte 14 of Data is not correct in spec. */ static struct perform_card_apdu_test perform_card_apdu_data_123 = { .pdu = perform_card_apdu_123, .pdu_len = sizeof(perform_card_apdu_123), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1, .c_apdu = { .cla = 0xA0, .ins = STK_INS_UPDATE_BINARY_D6, .p1 = 0x00, .p2 = 0x00, .lc = 0x18, .data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 } } }; static struct perform_card_apdu_test perform_card_apdu_data_124 = { .pdu = perform_card_apdu_124, .pdu_len = sizeof(perform_card_apdu_124), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1, .c_apdu = { .cla = 0xA0, .ins = STK_INS_READ_BINARY_B0, .p1 = 0x00, .p2 = 0x00, .has_le = 1, .le = 0x18 } }; static struct perform_card_apdu_test perform_card_apdu_data_125 = { .pdu = perform_card_apdu_125, .pdu_len = sizeof(perform_card_apdu_125), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1, .c_apdu = { .cla = 0xA0, .ins = STK_INS_UPDATE_BINARY_D6, .p1 = 0x00, .p2 = 0x00, .lc = 0x18, .data = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } } }; static struct perform_card_apdu_test perform_card_apdu_data_151 = { .pdu = perform_card_apdu_151, .pdu_len = sizeof(perform_card_apdu_151), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_7, .c_apdu = { .cla = 0xA0, .ins = STK_INS_SELECT, .p1 = 0x00, .p2 = 0x00, .lc = 0x02, .data = { 0x3F, 0x00 } } }; static struct perform_card_apdu_test perform_card_apdu_data_211 = { .pdu = perform_card_apdu_211, .pdu_len = sizeof(perform_card_apdu_211), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CARD_READER_1, .c_apdu = { .cla = 0xA0, .ins = STK_INS_SELECT, .p1 = 0x00, .p2 = 0x00, .lc = 0x02, .data = { 0x3F, 0x00 } } }; static void test_perform_card_apdu(gconstpointer data) { const struct perform_card_apdu_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_PERFORM_CARD_APDU); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == test->dst); check_c_apdu(&command->perform_card_apdu.c_apdu, &test->c_apdu); stk_command_free(command); } struct get_reader_status_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; }; static unsigned char get_reader_status_111[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x33, 0x00, 0x82, 0x02, 0x81, 0x82 }; static struct get_reader_status_test get_reader_status_data_111 = { .pdu = get_reader_status_111, .pdu_len = sizeof(get_reader_status_111), .qualifier = STK_QUALIFIER_TYPE_CARD_READER_STATUS, }; static void test_get_reader_status(gconstpointer data) { const struct get_reader_status_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_GET_READER_STATUS); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); if (command->qualifier == STK_QUALIFIER_TYPE_CARD_READER_STATUS) g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); else g_assert(command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0 || command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7); stk_command_free(command); } struct timer_mgmt_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; unsigned char timer_id; struct stk_timer_value timer_value; }; static unsigned char timer_mgmt_111[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x01, 0xA5, 0x03, 0x00, 0x50, 0x00 }; static unsigned char timer_mgmt_112[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x01 }; static unsigned char timer_mgmt_113[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x01, 0xA5, 0x03, 0x00, 0x10, 0x03 }; static unsigned char timer_mgmt_114[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x01 }; static unsigned char timer_mgmt_121[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x02, 0xA5, 0x03, 0x32, 0x95, 0x95 }; static unsigned char timer_mgmt_122[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x02 }; static unsigned char timer_mgmt_123[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x02, 0xA5, 0x03, 0x00, 0x10, 0x01 }; static unsigned char timer_mgmt_124[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x02 }; static unsigned char timer_mgmt_131[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x08, 0xA5, 0x03, 0x00, 0x02, 0x00 }; static unsigned char timer_mgmt_132[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x08 }; static unsigned char timer_mgmt_133[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x08, 0xA5, 0x03, 0x10, 0x00, 0x00 }; static unsigned char timer_mgmt_134[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x08 }; static unsigned char timer_mgmt_141[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x01 }; static unsigned char timer_mgmt_142[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x02 }; static unsigned char timer_mgmt_143[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x03 }; static unsigned char timer_mgmt_144[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x04 }; static unsigned char timer_mgmt_145[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x05 }; static unsigned char timer_mgmt_146[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x06 }; static unsigned char timer_mgmt_147[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x07 }; static unsigned char timer_mgmt_148[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x08 }; static unsigned char timer_mgmt_151[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x01 }; static unsigned char timer_mgmt_152[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x02 }; static unsigned char timer_mgmt_153[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x03 }; static unsigned char timer_mgmt_154[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x04 }; static unsigned char timer_mgmt_155[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x05 }; static unsigned char timer_mgmt_156[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x06 }; static unsigned char timer_mgmt_157[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x07 }; static unsigned char timer_mgmt_158[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x08 }; static unsigned char timer_mgmt_161[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x01, 0xA5, 0x03, 0x00, 0x00, 0x50 }; static unsigned char timer_mgmt_162[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x02, 0xA5, 0x03, 0x00, 0x00, 0x50 }; static unsigned char timer_mgmt_163[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x03, 0xA5, 0x03, 0x00, 0x00, 0x50 }; static unsigned char timer_mgmt_164[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x04, 0xA5, 0x03, 0x00, 0x00, 0x50 }; static unsigned char timer_mgmt_165[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x05, 0xA5, 0x03, 0x00, 0x00, 0x50 }; static unsigned char timer_mgmt_166[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x06, 0xA5, 0x03, 0x00, 0x00, 0x50 }; static unsigned char timer_mgmt_167[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x07, 0xA5, 0x03, 0x00, 0x00, 0x50 }; static unsigned char timer_mgmt_168[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x08, 0xA5, 0x03, 0x00, 0x00, 0x50 }; static unsigned char timer_mgmt_211[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x01, 0xA5, 0x03, 0x00, 0x00, 0x01 }; static unsigned char timer_mgmt_221[] = { 0xD0, 0x11, 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA4, 0x01, 0x01, 0xA5, 0x03, 0x00, 0x00, 0x03 }; static struct timer_mgmt_test timer_mgmt_data_111 = { .pdu = timer_mgmt_111, .pdu_len = sizeof(timer_mgmt_111), .qualifier = 0x00, .timer_id = 1, .timer_value = { .minute = 5 } }; static struct timer_mgmt_test timer_mgmt_data_112 = { .pdu = timer_mgmt_112, .pdu_len = sizeof(timer_mgmt_112), .qualifier = 0x02, .timer_id = 1 }; static struct timer_mgmt_test timer_mgmt_data_113 = { .pdu = timer_mgmt_113, .pdu_len = sizeof(timer_mgmt_113), .qualifier = 0x00, .timer_id = 1, .timer_value = { .minute = 1, .second = 30 } }; static struct timer_mgmt_test timer_mgmt_data_114 = { .pdu = timer_mgmt_114, .pdu_len = sizeof(timer_mgmt_114), .qualifier = 0x01, .timer_id = 1 }; static struct timer_mgmt_test timer_mgmt_data_121 = { .pdu = timer_mgmt_121, .pdu_len = sizeof(timer_mgmt_121), .qualifier = 0x00, .timer_id = 2, .timer_value = { .hour = 23, .minute = 59, .second = 59 } }; static struct timer_mgmt_test timer_mgmt_data_122 = { .pdu = timer_mgmt_122, .pdu_len = sizeof(timer_mgmt_122), .qualifier = 0x02, .timer_id = 2 }; static struct timer_mgmt_test timer_mgmt_data_123 = { .pdu = timer_mgmt_123, .pdu_len = sizeof(timer_mgmt_123), .qualifier = 0x00, .timer_id = 2, .timer_value = { .minute = 1, .second = 10 } }; static struct timer_mgmt_test timer_mgmt_data_124 = { .pdu = timer_mgmt_124, .pdu_len = sizeof(timer_mgmt_124), .qualifier = 0x01, .timer_id = 2 }; static struct timer_mgmt_test timer_mgmt_data_131 = { .pdu = timer_mgmt_131, .pdu_len = sizeof(timer_mgmt_131), .qualifier = 0x00, .timer_id = 8, .timer_value = { .minute = 20 } }; static struct timer_mgmt_test timer_mgmt_data_132 = { .pdu = timer_mgmt_132, .pdu_len = sizeof(timer_mgmt_132), .qualifier = 0x02, .timer_id = 8 }; static struct timer_mgmt_test timer_mgmt_data_133 = { .pdu = timer_mgmt_133, .pdu_len = sizeof(timer_mgmt_133), .qualifier = 0x00, .timer_id = 8, .timer_value = { .hour = 1 } }; static struct timer_mgmt_test timer_mgmt_data_134 = { .pdu = timer_mgmt_134, .pdu_len = sizeof(timer_mgmt_134), .qualifier = 0x01, .timer_id = 8 }; static struct timer_mgmt_test timer_mgmt_data_141 = { .pdu = timer_mgmt_141, .pdu_len = sizeof(timer_mgmt_141), .qualifier = 0x02, .timer_id = 1 }; static struct timer_mgmt_test timer_mgmt_data_142 = { .pdu = timer_mgmt_142, .pdu_len = sizeof(timer_mgmt_142), .qualifier = 0x02, .timer_id = 2 }; static struct timer_mgmt_test timer_mgmt_data_143 = { .pdu = timer_mgmt_143, .pdu_len = sizeof(timer_mgmt_143), .qualifier = 0x02, .timer_id = 3 }; static struct timer_mgmt_test timer_mgmt_data_144 = { .pdu = timer_mgmt_144, .pdu_len = sizeof(timer_mgmt_144), .qualifier = 0x02, .timer_id = 4 }; static struct timer_mgmt_test timer_mgmt_data_145 = { .pdu = timer_mgmt_145, .pdu_len = sizeof(timer_mgmt_145), .qualifier = 0x02, .timer_id = 5 }; static struct timer_mgmt_test timer_mgmt_data_146 = { .pdu = timer_mgmt_146, .pdu_len = sizeof(timer_mgmt_146), .qualifier = 0x02, .timer_id = 6 }; static struct timer_mgmt_test timer_mgmt_data_147 = { .pdu = timer_mgmt_147, .pdu_len = sizeof(timer_mgmt_147), .qualifier = 0x02, .timer_id = 7 }; static struct timer_mgmt_test timer_mgmt_data_148 = { .pdu = timer_mgmt_148, .pdu_len = sizeof(timer_mgmt_148), .qualifier = 0x02, .timer_id = 8 }; static struct timer_mgmt_test timer_mgmt_data_151 = { .pdu = timer_mgmt_151, .pdu_len = sizeof(timer_mgmt_151), .qualifier = 0x01, .timer_id = 1 }; static struct timer_mgmt_test timer_mgmt_data_152 = { .pdu = timer_mgmt_152, .pdu_len = sizeof(timer_mgmt_152), .qualifier = 0x01, .timer_id = 2 }; static struct timer_mgmt_test timer_mgmt_data_153 = { .pdu = timer_mgmt_153, .pdu_len = sizeof(timer_mgmt_153), .qualifier = 0x01, .timer_id = 3 }; static struct timer_mgmt_test timer_mgmt_data_154 = { .pdu = timer_mgmt_154, .pdu_len = sizeof(timer_mgmt_154), .qualifier = 0x01, .timer_id = 4 }; static struct timer_mgmt_test timer_mgmt_data_155 = { .pdu = timer_mgmt_155, .pdu_len = sizeof(timer_mgmt_155), .qualifier = 0x01, .timer_id = 5 }; static struct timer_mgmt_test timer_mgmt_data_156 = { .pdu = timer_mgmt_156, .pdu_len = sizeof(timer_mgmt_156), .qualifier = 0x01, .timer_id = 6 }; static struct timer_mgmt_test timer_mgmt_data_157 = { .pdu = timer_mgmt_157, .pdu_len = sizeof(timer_mgmt_157), .qualifier = 0x01, .timer_id = 7 }; static struct timer_mgmt_test timer_mgmt_data_158 = { .pdu = timer_mgmt_158, .pdu_len = sizeof(timer_mgmt_158), .qualifier = 0x01, .timer_id = 8 }; static struct timer_mgmt_test timer_mgmt_data_161 = { .pdu = timer_mgmt_161, .pdu_len = sizeof(timer_mgmt_161), .qualifier = 0x00, .timer_id = 1, .timer_value = { .second = 5 } }; static struct timer_mgmt_test timer_mgmt_data_162 = { .pdu = timer_mgmt_162, .pdu_len = sizeof(timer_mgmt_162), .qualifier = 0x00, .timer_id = 2, .timer_value = { .second = 5 } }; static struct timer_mgmt_test timer_mgmt_data_163 = { .pdu = timer_mgmt_163, .pdu_len = sizeof(timer_mgmt_163), .qualifier = 0x00, .timer_id = 3, .timer_value = { .second = 5 } }; static struct timer_mgmt_test timer_mgmt_data_164 = { .pdu = timer_mgmt_164, .pdu_len = sizeof(timer_mgmt_164), .qualifier = 0x00, .timer_id = 4, .timer_value = { .second = 5 } }; static struct timer_mgmt_test timer_mgmt_data_165 = { .pdu = timer_mgmt_165, .pdu_len = sizeof(timer_mgmt_165), .qualifier = 0x00, .timer_id = 5, .timer_value = { .second = 5 } }; static struct timer_mgmt_test timer_mgmt_data_166 = { .pdu = timer_mgmt_166, .pdu_len = sizeof(timer_mgmt_166), .qualifier = 0x00, .timer_id = 6, .timer_value = { .second = 5 } }; static struct timer_mgmt_test timer_mgmt_data_167 = { .pdu = timer_mgmt_167, .pdu_len = sizeof(timer_mgmt_167), .qualifier = 0x00, .timer_id = 7, .timer_value = { .second = 5 } }; static struct timer_mgmt_test timer_mgmt_data_168 = { .pdu = timer_mgmt_168, .pdu_len = sizeof(timer_mgmt_168), .qualifier = 0x00, .timer_id = 8, .timer_value = { .second = 5 } }; static struct timer_mgmt_test timer_mgmt_data_211 = { .pdu = timer_mgmt_211, .pdu_len = sizeof(timer_mgmt_211), .qualifier = 0x00, .timer_id = 1, .timer_value = { .second = 10 } }; static struct timer_mgmt_test timer_mgmt_data_221 = { .pdu = timer_mgmt_221, .pdu_len = sizeof(timer_mgmt_221), .qualifier = 0x00, .timer_id = 1, .timer_value = { .second = 30 } }; static void test_timer_mgmt(gconstpointer data) { const struct timer_mgmt_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_TIMER_MANAGEMENT); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_timer_id(command->timer_mgmt.timer_id, test->timer_id); check_timer_value(&command->timer_mgmt.timer_value, &test->timer_value); stk_command_free(command); } struct setup_idle_mode_text_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *text; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; char *html; enum stk_command_parse_result status; }; static unsigned char setup_idle_mode_text_111[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0F, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74 }; static unsigned char setup_idle_mode_text_121[] = { 0xD0, 0x18, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0D, 0x04, 0x54, 0x6F, 0x6F, 0x6C, 0x6B, 0x69, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74 }; static unsigned char setup_idle_mode_text_131[] = { 0xD0, 0x0B, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x00 }; static unsigned char setup_idle_mode_text_171[] = { 0xD0, 0x81, 0xFD, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x81, 0xF1, 0x00, 0x54, 0x74, 0x19, 0x34, 0x4D, 0x36, 0x41, 0x73, 0x74, 0x98, 0xCD, 0x06, 0xCD, 0xEB, 0x70, 0x38, 0x3B, 0x0F, 0x0A, 0x83, 0xE8, 0x65, 0x3C, 0x1D, 0x34, 0xA7, 0xCB, 0xD3, 0xEE, 0x33, 0x0B, 0x74, 0x47, 0xA7, 0xC7, 0x68, 0xD0, 0x1C, 0x1D, 0x66, 0xB3, 0x41, 0xE2, 0x32, 0x88, 0x9C, 0x9E, 0xC3, 0xD9, 0xE1, 0x7C, 0x99, 0x0C, 0x12, 0xE7, 0x41, 0x74, 0x74, 0x19, 0xD4, 0x2C, 0x82, 0xC2, 0x73, 0x50, 0xD8, 0x0D, 0x4A, 0x93, 0xD9, 0x65, 0x50, 0xFB, 0x4D, 0x2E, 0x83, 0xE8, 0x65, 0x3C, 0x1D, 0x94, 0x36, 0x83, 0xE8, 0xE8, 0x32, 0xA8, 0x59, 0x04, 0xA5, 0xE7, 0xA0, 0xB0, 0x98, 0x5D, 0x06, 0xD1, 0xDF, 0x20, 0xF2, 0x1B, 0x94, 0xA6, 0xBB, 0xA8, 0xE8, 0x32, 0x08, 0x2E, 0x2F, 0xCF, 0xCB, 0x6E, 0x7A, 0x98, 0x9E, 0x7E, 0xBB, 0x41, 0x73, 0x7A, 0x9E, 0x5D, 0x06, 0xA5, 0xE7, 0x20, 0x76, 0xD9, 0x4C, 0x07, 0x85, 0xE7, 0xA0, 0xB0, 0x1B, 0x94, 0x6E, 0xC3, 0xD9, 0xE5, 0x76, 0xD9, 0x4D, 0x0F, 0xD3, 0xD3, 0x6F, 0x37, 0x88, 0x5C, 0x1E, 0xA7, 0xE7, 0xE9, 0xB7, 0x1B, 0x44, 0x7F, 0x83, 0xE8, 0xE8, 0x32, 0xA8, 0x59, 0x04, 0xB5, 0xC3, 0xEE, 0xBA, 0x39, 0x3C, 0xA6, 0xD7, 0xE5, 0x65, 0xB9, 0x0B, 0x44, 0x45, 0x97, 0x41, 0x69, 0x32, 0xBB, 0x0C, 0x6A, 0xBF, 0xC9, 0x65, 0x10, 0xBD, 0x8C, 0xA7, 0x83, 0xE6, 0xE8, 0x30, 0x9B, 0x0D, 0x12, 0x97, 0x41, 0xE4, 0xF4, 0x1C, 0xCE, 0x0E, 0xE7, 0xCB, 0x64, 0x50, 0xDA, 0x0D, 0x0A, 0x83, 0xDA, 0x61, 0xB7, 0xBB, 0x2C, 0x07, 0xD1, 0xD1, 0x61, 0x3A, 0xA8, 0xEC, 0x9E, 0xD7, 0xE5, 0xE5, 0x39, 0x88, 0x8E, 0x0E, 0xD3, 0x41, 0xEE, 0x32 }; static unsigned char setup_idle_mode_text_211[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x9E, 0x02, 0x00, 0x01 }; static unsigned char setup_idle_mode_text_221[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x9E, 0x02, 0x01, 0x01 }; static unsigned char setup_idle_mode_text_231[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x0A, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x9E, 0x02, 0x00, 0x02 }; static unsigned char setup_idle_mode_text_241[] = { 0xD0, 0x0F, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x00, 0x9E, 0x02, 0x01, 0x01 }; static unsigned char setup_idle_mode_text_311[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x19, 0x08, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15 }; static unsigned char setup_idle_mode_text_411[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char setup_idle_mode_text_412[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x32 }; static unsigned char setup_idle_mode_text_421[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x01, 0xB4 }; static unsigned char setup_idle_mode_text_422[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x32 }; static unsigned char setup_idle_mode_text_431[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x02, 0xB4 }; static unsigned char setup_idle_mode_text_432[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x32 }; static unsigned char setup_idle_mode_text_441[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x04, 0xB4 }; static unsigned char setup_idle_mode_text_442[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char setup_idle_mode_text_443[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x33 }; static unsigned char setup_idle_mode_text_451[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x08, 0xB4 }; static unsigned char setup_idle_mode_text_452[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char setup_idle_mode_text_453[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x33 }; static unsigned char setup_idle_mode_text_461[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x10, 0xB4 }; static unsigned char setup_idle_mode_text_462[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char setup_idle_mode_text_463[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x33 }; static unsigned char setup_idle_mode_text_471[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x20, 0xB4 }; static unsigned char setup_idle_mode_text_472[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char setup_idle_mode_text_473[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x33 }; static unsigned char setup_idle_mode_text_481[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x40, 0xB4 }; static unsigned char setup_idle_mode_text_482[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char setup_idle_mode_text_483[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x33 }; static unsigned char setup_idle_mode_text_491[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x80, 0xB4 }; static unsigned char setup_idle_mode_text_492[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char setup_idle_mode_text_493[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x33 }; static unsigned char setup_idle_mode_text_4101[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char setup_idle_mode_text_4102[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x11, 0x04, 0x49, 0x64, 0x6C, 0x65, 0x20, 0x4D, 0x6F, 0x64, 0x65, 0x20, 0x54, 0x65, 0x78, 0x74, 0x20, 0x32 }; static unsigned char setup_idle_mode_text_511[] = { 0xD0, 0x10, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x05, 0x08, 0x4F, 0x60, 0x59, 0x7D }; static unsigned char setup_idle_mode_text_611[] = { 0xD0, 0x14, 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x81, 0x82, 0x8D, 0x09, 0x08, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0x00, 0x30 }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_111 = { .pdu = setup_idle_mode_text_111, .pdu_len = sizeof(setup_idle_mode_text_111), .qualifier = 0x00, .text = "Idle Mode Text" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_121 = { .pdu = setup_idle_mode_text_121, .pdu_len = sizeof(setup_idle_mode_text_121), .qualifier = 0x00, .text = "Toolkit Test" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_131 = { .pdu = setup_idle_mode_text_131, .pdu_len = sizeof(setup_idle_mode_text_131), .qualifier = 0x00, .text = "" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_171 = { .pdu = setup_idle_mode_text_171, .pdu_len = sizeof(setup_idle_mode_text_171), .qualifier = 0x00, .text = "The SIM shall supply a text string, which shall be displayed " "by the ME as an idle mode text if the ME is able to do it." "The presentation style is left as an implementation decision " "to the ME manufacturer. The idle mode text shall be displayed " "in a manner that ensures that ne" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_211 = { .pdu = setup_idle_mode_text_211, .pdu_len = sizeof(setup_idle_mode_text_211), .qualifier = 0x00, .text = "Idle text", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_221 = { .pdu = setup_idle_mode_text_221, .pdu_len = sizeof(setup_idle_mode_text_221), .qualifier = 0x00, .text = "Idle text", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_231 = { .pdu = setup_idle_mode_text_231, .pdu_len = sizeof(setup_idle_mode_text_231), .qualifier = 0x00, .text = "Idle text", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x02 } }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_241 = { .pdu = setup_idle_mode_text_241, .pdu_len = sizeof(setup_idle_mode_text_241), .qualifier = 0x00, .text = "", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 }, .status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_311 = { .pdu = setup_idle_mode_text_311, .pdu_len = sizeof(setup_idle_mode_text_311), .qualifier = 0x00, .text = "ЗДРАВСТВУЙТЕ" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_411 = { .pdu = setup_idle_mode_text_411, .pdu_len = sizeof(setup_idle_mode_text_411), .qualifier = 0x00, .text = "Idle Mode Text 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Idle Mode Text 1" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_412 = { .pdu = setup_idle_mode_text_412, .pdu_len = sizeof(setup_idle_mode_text_412), .qualifier = 0x00, .text = "Idle Mode Text 2" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_421 = { .pdu = setup_idle_mode_text_421, .pdu_len = sizeof(setup_idle_mode_text_421), .qualifier = 0x00, .text = "Idle Mode Text 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x01, 0xB4 } }, .html = "
Idle Mode Text 1" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_422 = { .pdu = setup_idle_mode_text_422, .pdu_len = sizeof(setup_idle_mode_text_422), .qualifier = 0x00, .text = "Idle Mode Text 2" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_431 = { .pdu = setup_idle_mode_text_431, .pdu_len = sizeof(setup_idle_mode_text_431), .qualifier = 0x00, .text = "Idle Mode Text 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x02, 0xB4 } }, .html = "
Idle Mode Text 1" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_432 = { .pdu = setup_idle_mode_text_432, .pdu_len = sizeof(setup_idle_mode_text_432), .qualifier = 0x00, .text = "Idle Mode Text 2" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_441 = { .pdu = setup_idle_mode_text_441, .pdu_len = sizeof(setup_idle_mode_text_441), .qualifier = 0x00, .text = "Idle Mode Text 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x04, 0xB4 } }, .html = "
" "Idle Mode Text 1
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_442 = { .pdu = setup_idle_mode_text_442, .pdu_len = sizeof(setup_idle_mode_text_442), .qualifier = 0x00, .text = "Idle Mode Text 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Idle Mode Text 2" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_443 = { .pdu = setup_idle_mode_text_443, .pdu_len = sizeof(setup_idle_mode_text_443), .qualifier = 0x00, .text = "Idle Mode Text 3" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_451 = { .pdu = setup_idle_mode_text_451, .pdu_len = sizeof(setup_idle_mode_text_451), .qualifier = 0x00, .text = "Idle Mode Text 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x08, 0xB4 } }, .html = "
" "Idle Mode Text 1
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_452 = { .pdu = setup_idle_mode_text_452, .pdu_len = sizeof(setup_idle_mode_text_452), .qualifier = 0x00, .text = "Idle Mode Text 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Idle Mode Text 2" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_453 = { .pdu = setup_idle_mode_text_453, .pdu_len = sizeof(setup_idle_mode_text_453), .qualifier = 0x00, .text = "Idle Mode Text 3" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_461 = { .pdu = setup_idle_mode_text_461, .pdu_len = sizeof(setup_idle_mode_text_461), .qualifier = 0x00, .text = "Idle Mode Text 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x10, 0xB4 } }, .html = "
" "Idle Mode Text 1
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_462 = { .pdu = setup_idle_mode_text_462, .pdu_len = sizeof(setup_idle_mode_text_462), .qualifier = 0x00, .text = "Idle Mode Text 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Idle Mode Text 2" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_463 = { .pdu = setup_idle_mode_text_463, .pdu_len = sizeof(setup_idle_mode_text_463), .qualifier = 0x00, .text = "Idle Mode Text 3" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_471 = { .pdu = setup_idle_mode_text_471, .pdu_len = sizeof(setup_idle_mode_text_471), .qualifier = 0x00, .text = "Idle Mode Text 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x20, 0xB4 } }, .html = "
" "Idle Mode Text 1
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_472 = { .pdu = setup_idle_mode_text_472, .pdu_len = sizeof(setup_idle_mode_text_472), .qualifier = 0x00, .text = "Idle Mode Text 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Idle Mode Text 2" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_473 = { .pdu = setup_idle_mode_text_473, .pdu_len = sizeof(setup_idle_mode_text_473), .qualifier = 0x00, .text = "Idle Mode Text 3" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_481 = { .pdu = setup_idle_mode_text_481, .pdu_len = sizeof(setup_idle_mode_text_481), .qualifier = 0x00, .text = "Idle Mode Text 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x40, 0xB4 } }, .html = "
Idle Mode Text 1
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_482 = { .pdu = setup_idle_mode_text_482, .pdu_len = sizeof(setup_idle_mode_text_482), .qualifier = 0x00, .text = "Idle Mode Text 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Idle Mode Text 2" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_483 = { .pdu = setup_idle_mode_text_483, .pdu_len = sizeof(setup_idle_mode_text_483), .qualifier = 0x00, .text = "Idle Mode Text 3" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_491 = { .pdu = setup_idle_mode_text_491, .pdu_len = sizeof(setup_idle_mode_text_491), .qualifier = 0x00, .text = "Idle Mode Text 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x80, 0xB4 } }, .html = "
Idle Mode Text 1" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_492 = { .pdu = setup_idle_mode_text_492, .pdu_len = sizeof(setup_idle_mode_text_492), .qualifier = 0x00, .text = "Idle Mode Text 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Idle Mode Text 2" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_493 = { .pdu = setup_idle_mode_text_493, .pdu_len = sizeof(setup_idle_mode_text_493), .qualifier = 0x00, .text = "Idle Mode Text 3" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_4101 = { .pdu = setup_idle_mode_text_4101, .pdu_len = sizeof(setup_idle_mode_text_4101), .qualifier = 0x00, .text = "Idle Mode Text 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } }, .html = "
Idle Mode Text 1" "
", }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_4102 = { .pdu = setup_idle_mode_text_4102, .pdu_len = sizeof(setup_idle_mode_text_4102), .qualifier = 0x00, .text = "Idle Mode Text 2" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_511 = { .pdu = setup_idle_mode_text_511, .pdu_len = sizeof(setup_idle_mode_text_511), .qualifier = 0x00, .text = "你好" }; static struct setup_idle_mode_text_test setup_idle_mode_text_data_611 = { .pdu = setup_idle_mode_text_611, .pdu_len = sizeof(setup_idle_mode_text_611), .qualifier = 0x00, .text = "80ル0" }; static void test_setup_idle_mode_text(gconstpointer data) { const struct setup_idle_mode_text_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == test->status); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_text(command->setup_idle_mode_text.text, test->text); check_icon_id(&command->setup_idle_mode_text.icon_id, &test->icon_id); check_text_attr(&command->setup_idle_mode_text.text_attr, &test->text_attr); check_text_attr_html(&command->setup_idle_mode_text.text_attr, command->setup_idle_mode_text.text, test->html); check_frame_id(&command->setup_idle_mode_text.frame_id, &test->frame_id); stk_command_free(command); } struct run_at_command_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *alpha_id; char *at_command; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; enum stk_command_parse_result status; }; static unsigned char run_at_command_111[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_121[] = { 0xD0, 0x14, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x00, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_131[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0E, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_211[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0A, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0x9E, 0x02, 0x00, 0x01 }; /* The 12th byte should be 0x85, instead of 0xA8 */ static unsigned char run_at_command_221[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0B, 0x43, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0x9E, 0x02, 0x00, 0x02 }; static unsigned char run_at_command_231[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0A, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0x9E, 0x02, 0x01, 0x01 }; static unsigned char run_at_command_241[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x0B, 0x43, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0x9E, 0x02, 0x01, 0x02 }; static unsigned char run_at_command_251[] = { 0xD0, 0x16, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0x9E, 0x02, 0x01, 0x01 }; static unsigned char run_at_command_311[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x31, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char run_at_command_312[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x32, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_321[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x31, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x01, 0xB4 }; static unsigned char run_at_command_322[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x32, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_331[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x31, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x02, 0xB4 }; static unsigned char run_at_command_332[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x32, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_341[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x31, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x04, 0xB4 }; static unsigned char run_at_command_342[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x32, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char run_at_command_343[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x33, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_351[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x31, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x08, 0xB4 }; static unsigned char run_at_command_352[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x32, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char run_at_command_353[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x33, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_361[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x31, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x10, 0xB4 }; static unsigned char run_at_command_362[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x32, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char run_at_command_363[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x33, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_371[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x31, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x20, 0xB4 }; static unsigned char run_at_command_372[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x32, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char run_at_command_373[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x33, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_381[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x31, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x40, 0xB4 }; static unsigned char run_at_command_382[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x32, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char run_at_command_383[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x33, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_391[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x31, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x80, 0xB4 }; static unsigned char run_at_command_392[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x32, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char run_at_command_393[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x33, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_3101[] = { 0xD0, 0x2A, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x31, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49, 0xD0, 0x04, 0x00, 0x10, 0x00, 0xB4 }; static unsigned char run_at_command_3102[] = { 0xD0, 0x24, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x10, 0x52, 0x75, 0x6E, 0x20, 0x41, 0x54, 0x20, 0x43, 0x6F, 0x6D, 0x6D, 0x61, 0x6E, 0x64, 0x20, 0x32, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; /* The 2nd byte (total size) should be 0x2D, instead of 0x21 */ static unsigned char run_at_command_411[] = { 0xD0, 0x2D, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_511[] = { 0xD0, 0x19, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x05, 0x80, 0x4F, 0x60, 0x59, 0x7D, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static unsigned char run_at_command_611[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x81, 0x82, 0x85, 0x07, 0x80, 0x00, 0x38, 0x00, 0x30, 0x30, 0xEB, 0xA8, 0x07, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x4D, 0x49 }; static struct run_at_command_test run_at_command_data_111 = { .pdu = run_at_command_111, .pdu_len = sizeof(run_at_command_111), .qualifier = 0x00, .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_121 = { .pdu = run_at_command_121, .pdu_len = sizeof(run_at_command_121), .qualifier = 0x00, .alpha_id = "", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_131 = { .pdu = run_at_command_131, .pdu_len = sizeof(run_at_command_131), .qualifier = 0x00, .alpha_id = "Run AT Command", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_211 = { .pdu = run_at_command_211, .pdu_len = sizeof(run_at_command_211), .qualifier = 0x00, .alpha_id = "Basic Icon", .at_command = "AT+CGMI", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct run_at_command_test run_at_command_data_221 = { .pdu = run_at_command_221, .pdu_len = sizeof(run_at_command_221), .qualifier = 0x00, .alpha_id = "Colour Icon", .at_command = "AT+CGMI", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x02 } }; static struct run_at_command_test run_at_command_data_231 = { .pdu = run_at_command_231, .pdu_len = sizeof(run_at_command_231), .qualifier = 0x00, .alpha_id = "Basic Icon", .at_command = "AT+CGMI", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; /* The qualifier of icon_id should be non self-explanatory */ static struct run_at_command_test run_at_command_data_241 = { .pdu = run_at_command_241, .pdu_len = sizeof(run_at_command_241), .qualifier = 0x00, .alpha_id = "Colour Icon", .at_command = "AT+CGMI", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x02 } }; static struct run_at_command_test run_at_command_data_251 = { .pdu = run_at_command_251, .pdu_len = sizeof(run_at_command_251), .qualifier = 0x00, .at_command = "AT+CGMI", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 }, .status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD }; static struct run_at_command_test run_at_command_data_311 = { .pdu = run_at_command_311, .pdu_len = sizeof(run_at_command_311), .qualifier = 0x00, .alpha_id = "Run AT Command 1", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct run_at_command_test run_at_command_data_312 = { .pdu = run_at_command_312, .pdu_len = sizeof(run_at_command_312), .qualifier = 0x00, .alpha_id = "Run AT Command 2", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_321 = { .pdu = run_at_command_321, .pdu_len = sizeof(run_at_command_321), .qualifier = 0x00, .alpha_id = "Run AT Command 1", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x01, 0xB4 } } }; static struct run_at_command_test run_at_command_data_322 = { .pdu = run_at_command_322, .pdu_len = sizeof(run_at_command_322), .qualifier = 0x00, .alpha_id = "Run AT Command 2", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_331 = { .pdu = run_at_command_331, .pdu_len = sizeof(run_at_command_331), .qualifier = 0x00, .alpha_id = "Run AT Command 1", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x02, 0xB4 } } }; static struct run_at_command_test run_at_command_data_332 = { .pdu = run_at_command_332, .pdu_len = sizeof(run_at_command_332), .qualifier = 0x00, .alpha_id = "Run AT Command 2", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_341 = { .pdu = run_at_command_341, .pdu_len = sizeof(run_at_command_341), .qualifier = 0x00, .alpha_id = "Run AT Command 1", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x04, 0xB4 } } }; static struct run_at_command_test run_at_command_data_342 = { .pdu = run_at_command_342, .pdu_len = sizeof(run_at_command_342), .qualifier = 0x00, .alpha_id = "Run AT Command 2", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct run_at_command_test run_at_command_data_343 = { .pdu = run_at_command_343, .pdu_len = sizeof(run_at_command_343), .qualifier = 0x00, .alpha_id = "Run AT Command 3", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_351 = { .pdu = run_at_command_351, .pdu_len = sizeof(run_at_command_351), .qualifier = 0x00, .alpha_id = "Run AT Command 1", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x08, 0xB4 } } }; static struct run_at_command_test run_at_command_data_352 = { .pdu = run_at_command_352, .pdu_len = sizeof(run_at_command_352), .qualifier = 0x00, .alpha_id = "Run AT Command 2", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct run_at_command_test run_at_command_data_353 = { .pdu = run_at_command_353, .pdu_len = sizeof(run_at_command_353), .qualifier = 0x00, .alpha_id = "Run AT Command 3", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_361 = { .pdu = run_at_command_361, .pdu_len = sizeof(run_at_command_361), .qualifier = 0x00, .alpha_id = "Run AT Command 1", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x10, 0xB4 } } }; static struct run_at_command_test run_at_command_data_362 = { .pdu = run_at_command_362, .pdu_len = sizeof(run_at_command_362), .qualifier = 0x00, .alpha_id = "Run AT Command 2", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct run_at_command_test run_at_command_data_363 = { .pdu = run_at_command_363, .pdu_len = sizeof(run_at_command_363), .qualifier = 0x00, .alpha_id = "Run AT Command 3", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_371 = { .pdu = run_at_command_371, .pdu_len = sizeof(run_at_command_371), .qualifier = 0x00, .alpha_id = "Run AT Command 1", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x20, 0xB4 } } }; static struct run_at_command_test run_at_command_data_372 = { .pdu = run_at_command_372, .pdu_len = sizeof(run_at_command_372), .qualifier = 0x00, .alpha_id = "Run AT Command 2", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct run_at_command_test run_at_command_data_373 = { .pdu = run_at_command_373, .pdu_len = sizeof(run_at_command_373), .qualifier = 0x00, .alpha_id = "Run AT Command 3", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_381 = { .pdu = run_at_command_381, .pdu_len = sizeof(run_at_command_381), .qualifier = 0x00, .alpha_id = "Run AT Command 1", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x40, 0xB4 } } }; static struct run_at_command_test run_at_command_data_382 = { .pdu = run_at_command_382, .pdu_len = sizeof(run_at_command_382), .qualifier = 0x00, .alpha_id = "Run AT Command 2", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct run_at_command_test run_at_command_data_383 = { .pdu = run_at_command_383, .pdu_len = sizeof(run_at_command_383), .qualifier = 0x00, .alpha_id = "Run AT Command 3", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_391 = { .pdu = run_at_command_391, .pdu_len = sizeof(run_at_command_391), .qualifier = 0x00, .alpha_id = "Run AT Command 1", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x80, 0xB4 } } }; static struct run_at_command_test run_at_command_data_392 = { .pdu = run_at_command_392, .pdu_len = sizeof(run_at_command_392), .qualifier = 0x00, .alpha_id = "Run AT Command 2", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct run_at_command_test run_at_command_data_393 = { .pdu = run_at_command_393, .pdu_len = sizeof(run_at_command_393), .qualifier = 0x00, .alpha_id = "Run AT Command 3", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_3101 = { .pdu = run_at_command_3101, .pdu_len = sizeof(run_at_command_3101), .qualifier = 0x00, .alpha_id = "Run AT Command 1", .at_command = "AT+CGMI", .text_attr = { .len = 4, .attributes = { 0x00, 0x10, 0x00, 0xB4 } } }; static struct run_at_command_test run_at_command_data_3102 = { .pdu = run_at_command_3102, .pdu_len = sizeof(run_at_command_3102), .qualifier = 0x00, .alpha_id = "Run AT Command 2", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_411 = { .pdu = run_at_command_411, .pdu_len = sizeof(run_at_command_411), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_511 = { .pdu = run_at_command_511, .pdu_len = sizeof(run_at_command_511), .qualifier = 0x00, .alpha_id = "你好", .at_command = "AT+CGMI" }; static struct run_at_command_test run_at_command_data_611 = { .pdu = run_at_command_611, .pdu_len = sizeof(run_at_command_611), .qualifier = 0x00, .alpha_id = "80ル", .at_command = "AT+CGMI" }; static void test_run_at_command(gconstpointer data) { const struct run_at_command_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == test->status); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_RUN_AT_COMMAND); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_alpha_id(command->run_at_command.alpha_id, test->alpha_id); check_at_command(command->run_at_command.at_command, test->at_command); check_icon_id(&command->run_at_command.icon_id, &test->icon_id); check_text_attr(&command->run_at_command.text_attr, &test->text_attr); check_frame_id(&command->run_at_command.frame_id, &test->frame_id); stk_command_free(command); } struct send_dtmf_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *alpha_id; char *dtmf; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; static unsigned char send_dtmf_111[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0xAC, 0x02, 0xC1, 0xF2 }; static unsigned char send_dtmf_121[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x09, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; static unsigned char send_dtmf_131[] = { 0xD0, 0x13, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x00, 0xAC, 0x06, 0xC1, 0xCC, 0xCC, 0xCC, 0xCC, 0x2C }; static unsigned char send_dtmf_211[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0A, 0x42, 0x61, 0x73, 0x69, 0x63, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0xAC, 0x02, 0xC1, 0xF2, 0x9E, 0x02, 0x00, 0x01 }; static unsigned char send_dtmf_221[] = { 0xD0, 0x1E, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x43, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x20, 0x49, 0x63, 0x6F, 0x6E, 0xAC, 0x02, 0xC1, 0xF2, 0x9E, 0x02, 0x00, 0x02 }; static unsigned char send_dtmf_231[] = { 0xD0, 0x1C, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x09, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0xAC, 0x02, 0xC1, 0xF2, 0x9E, 0x02, 0x01, 0x01 }; static unsigned char send_dtmf_311[] = { 0xD0, 0x28, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15, 0xAC, 0x02, 0xC1, 0xF2 }; static unsigned char send_dtmf_411[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x31, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char send_dtmf_412[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x32, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; static unsigned char send_dtmf_421[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x31, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x01, 0xB4 }; static unsigned char send_dtmf_422[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x32, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; static unsigned char send_dtmf_431[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x31, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0xB0, 0x02, 0xB4 }; static unsigned char send_dtmf_432[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x32, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; static unsigned char send_dtmf_441[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x31, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x04, 0xB4 }; static unsigned char send_dtmf_442[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x32, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char send_dtmf_443[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x33, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; static unsigned char send_dtmf_451[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x31, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x08, 0xB4 }; static unsigned char send_dtmf_452[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x32, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char send_dtmf_453[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x33, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; /* The last 0x00 in spec should be removed. */ static unsigned char send_dtmf_461[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x31, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x10, 0xB4 }; static unsigned char send_dtmf_462[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x32, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char send_dtmf_463[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x33, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; static unsigned char send_dtmf_471[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x31, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x20, 0xB4 }; static unsigned char send_dtmf_472[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x32, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char send_dtmf_473[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x33, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; static unsigned char send_dtmf_481[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x31, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x40, 0xB4 }; static unsigned char send_dtmf_482[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x32, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char send_dtmf_483[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x33, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; /* The second to the last should be 0x80 */ static unsigned char send_dtmf_491[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x31, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x80, 0xB4 }; static unsigned char send_dtmf_492[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x32, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char send_dtmf_493[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x33, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; static unsigned char send_dtmf_4101[] = { 0xD0, 0x23, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x31, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4 }; static unsigned char send_dtmf_4102[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x54, 0x4D, 0x46, 0x20, 0x32, 0xAC, 0x05, 0x21, 0x43, 0x65, 0x87, 0x09 }; static unsigned char send_dtmf_511[] = { 0xD0, 0x14, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x05, 0x80, 0x4F, 0x60, 0x59, 0x7D, 0xAC, 0x02, 0xC1, 0xF2 }; static unsigned char send_dtmf_611[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x81, 0x83, 0x85, 0x03, 0x80, 0x30, 0xEB, 0xAC, 0x02, 0xC1, 0xF2 }; static struct send_dtmf_test send_dtmf_data_111 = { .pdu = send_dtmf_111, .pdu_len = sizeof(send_dtmf_111), .qualifier = 0x00, .dtmf = "1c2" }; static struct send_dtmf_test send_dtmf_data_121 = { .pdu = send_dtmf_121, .pdu_len = sizeof(send_dtmf_121), .qualifier = 0x00, .alpha_id = "Send DTMF", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_131 = { .pdu = send_dtmf_131, .pdu_len = sizeof(send_dtmf_131), .qualifier = 0x00, .alpha_id = "", .dtmf = "1cccccccccc2" }; static struct send_dtmf_test send_dtmf_data_211 = { .pdu = send_dtmf_211, .pdu_len = sizeof(send_dtmf_211), .qualifier = 0x00, .alpha_id = "Basic Icon", .dtmf = "1c2", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct send_dtmf_test send_dtmf_data_221 = { .pdu = send_dtmf_221, .pdu_len = sizeof(send_dtmf_221), .qualifier = 0x00, .alpha_id = "Colour Icon", .dtmf = "1c2", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x02 } }; static struct send_dtmf_test send_dtmf_data_231 = { .pdu = send_dtmf_231, .pdu_len = sizeof(send_dtmf_231), .qualifier = 0x00, .alpha_id = "Send DTMF", .dtmf = "1c2", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct send_dtmf_test send_dtmf_data_311 = { .pdu = send_dtmf_311, .pdu_len = sizeof(send_dtmf_311), .qualifier = 0x00, .alpha_id = "ЗДРАВСТВУЙТЕ", .dtmf = "1c2" }; static struct send_dtmf_test send_dtmf_data_411 = { .pdu = send_dtmf_411, .pdu_len = sizeof(send_dtmf_411), .qualifier = 0x00, .alpha_id = "Send DTMF 1", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_412 = { .pdu = send_dtmf_412, .pdu_len = sizeof(send_dtmf_412), .qualifier = 0x00, .alpha_id = "Send DTMF 2", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_421 = { .pdu = send_dtmf_421, .pdu_len = sizeof(send_dtmf_421), .qualifier = 0x00, .alpha_id = "Send DTMF 1", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x01, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_422 = { .pdu = send_dtmf_422, .pdu_len = sizeof(send_dtmf_422), .qualifier = 0x00, .alpha_id = "Send DTMF 2", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_431 = { .pdu = send_dtmf_431, .pdu_len = sizeof(send_dtmf_431), .qualifier = 0x00, .alpha_id = "Send DTMF 1", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0xB0, 0x02, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_432 = { .pdu = send_dtmf_432, .pdu_len = sizeof(send_dtmf_432), .qualifier = 0x00, .alpha_id = "Send DTMF 2", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_441 = { .pdu = send_dtmf_441, .pdu_len = sizeof(send_dtmf_441), .qualifier = 0x00, .alpha_id = "Send DTMF 1", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x04, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_442 = { .pdu = send_dtmf_442, .pdu_len = sizeof(send_dtmf_442), .qualifier = 0x00, .alpha_id = "Send DTMF 2", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_443 = { .pdu = send_dtmf_443, .pdu_len = sizeof(send_dtmf_443), .qualifier = 0x00, .alpha_id = "Send DTMF 3", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_451 = { .pdu = send_dtmf_451, .pdu_len = sizeof(send_dtmf_451), .qualifier = 0x00, .alpha_id = "Send DTMF 1", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x08, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_452 = { .pdu = send_dtmf_452, .pdu_len = sizeof(send_dtmf_452), .qualifier = 0x00, .alpha_id = "Send DTMF 2", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_453 = { .pdu = send_dtmf_453, .pdu_len = sizeof(send_dtmf_453), .qualifier = 0x00, .alpha_id = "Send DTMF 3", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_461 = { .pdu = send_dtmf_461, .pdu_len = sizeof(send_dtmf_461), .qualifier = 0x00, .alpha_id = "Send DTMF 1", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x10, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_462 = { .pdu = send_dtmf_462, .pdu_len = sizeof(send_dtmf_462), .qualifier = 0x00, .alpha_id = "Send DTMF 2", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_463 = { .pdu = send_dtmf_463, .pdu_len = sizeof(send_dtmf_463), .qualifier = 0x00, .alpha_id = "Send DTMF 3", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_471 = { .pdu = send_dtmf_471, .pdu_len = sizeof(send_dtmf_471), .qualifier = 0x00, .alpha_id = "Send DTMF 1", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x20, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_472 = { .pdu = send_dtmf_472, .pdu_len = sizeof(send_dtmf_472), .qualifier = 0x00, .alpha_id = "Send DTMF 2", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_473 = { .pdu = send_dtmf_473, .pdu_len = sizeof(send_dtmf_473), .qualifier = 0x00, .alpha_id = "Send DTMF 3", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_481 = { .pdu = send_dtmf_481, .pdu_len = sizeof(send_dtmf_481), .qualifier = 0x00, .alpha_id = "Send DTMF 1", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x40, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_482 = { .pdu = send_dtmf_482, .pdu_len = sizeof(send_dtmf_482), .qualifier = 0x00, .alpha_id = "Send DTMF 2", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_483 = { .pdu = send_dtmf_483, .pdu_len = sizeof(send_dtmf_483), .qualifier = 0x00, .alpha_id = "Send DTMF 3", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_491 = { .pdu = send_dtmf_491, .pdu_len = sizeof(send_dtmf_491), .qualifier = 0x00, .alpha_id = "Send DTMF 1", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x80, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_492 = { .pdu = send_dtmf_492, .pdu_len = sizeof(send_dtmf_492), .qualifier = 0x00, .alpha_id = "Send DTMF 2", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_493 = { .pdu = send_dtmf_493, .pdu_len = sizeof(send_dtmf_493), .qualifier = 0x00, .alpha_id = "Send DTMF 3", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_4101 = { .pdu = send_dtmf_4101, .pdu_len = sizeof(send_dtmf_4101), .qualifier = 0x00, .alpha_id = "Send DTMF 1", .dtmf = "1234567890", .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } } }; static struct send_dtmf_test send_dtmf_data_4102 = { .pdu = send_dtmf_4102, .pdu_len = sizeof(send_dtmf_4102), .qualifier = 0x00, .alpha_id = "Send DTMF 2", .dtmf = "1234567890" }; static struct send_dtmf_test send_dtmf_data_511 = { .pdu = send_dtmf_511, .pdu_len = sizeof(send_dtmf_511), .qualifier = 0x00, .alpha_id = "你好", .dtmf = "1c2" }; static struct send_dtmf_test send_dtmf_data_611 = { .pdu = send_dtmf_611, .pdu_len = sizeof(send_dtmf_611), .qualifier = 0x00, .alpha_id = "ル", .dtmf = "1c2" }; static void test_send_dtmf(gconstpointer data) { const struct send_dtmf_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_SEND_DTMF); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_NETWORK); check_alpha_id(command->send_dtmf.alpha_id, test->alpha_id); check_dtmf_string(command->send_dtmf.dtmf, test->dtmf); check_icon_id(&command->send_dtmf.icon_id, &test->icon_id); check_text_attr(&command->send_dtmf.text_attr, &test->text_attr); check_frame_id(&command->send_dtmf.frame_id, &test->frame_id); stk_command_free(command); } struct language_notification_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char language[3]; }; static unsigned char language_notification_111[] = { 0xD0, 0x0D, 0x81, 0x03, 0x01, 0x35, 0x01, 0x82, 0x02, 0x81, 0x82, 0xAD, 0x02, 0x73, 0x65 }; static unsigned char language_notification_121[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x35, 0x00, 0x82, 0x02, 0x81, 0x82 }; static struct language_notification_test language_notification_data_111 = { .pdu = language_notification_111, .pdu_len = sizeof(language_notification_111), .qualifier = 0x01, .language = "se" }; static struct language_notification_test language_notification_data_121 = { .pdu = language_notification_121, .pdu_len = sizeof(language_notification_121), .qualifier = 0x00 }; static void test_language_notification(gconstpointer data) { const struct language_notification_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_language(command->language_notification.language, test->language); stk_command_free(command); } struct launch_browser_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; unsigned char browser_id; char *url; struct stk_common_byte_array bearer; struct stk_file prov_file_refs[MAX_ITEM]; char *text_gateway_proxy_id; char *alpha_id; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; struct stk_common_byte_array network_name; char *text_usr; char *text_passwd; }; static unsigned char launch_browser_111[] = { 0xD0, 0x18, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0B, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C }; static unsigned char launch_browser_121[] = { 0xD0, 0x1F, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x78, 0x78, 0x78, 0x2E, 0x79, 0x79, 0x79, 0x2E, 0x7A, 0x7A, 0x7A, 0x05, 0x00 }; static unsigned char launch_browser_131[] = { 0xD0, 0x0E, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x30, 0x01, 0x00, 0x31, 0x00 }; static unsigned char launch_browser_141[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x32, 0x01, 0x03, 0x0D, 0x10, 0x04, 0x61, 0x62, 0x63, 0x2E, 0x64, 0x65, 0x66, 0x2E, 0x67, 0x68, 0x69, 0x2E, 0x6A, 0x6B, 0x6C }; static unsigned char launch_browser_211[] = { 0xD0, 0x18, 0x81, 0x03, 0x01, 0x15, 0x02, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0B, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C }; static unsigned char launch_browser_221[] = { 0xD0, 0x18, 0x81, 0x03, 0x01, 0x15, 0x03, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0B, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C }; static unsigned char launch_browser_231[] = { 0xD0, 0x0B, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00 }; static unsigned char launch_browser_311[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x15, 0x02, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x19, 0x80, 0x04, 0x17, 0x04, 0x14, 0x04, 0x20, 0x04, 0x10, 0x04, 0x12, 0x04, 0x21, 0x04, 0x22, 0x04, 0x12, 0x04, 0x23, 0x04, 0x19, 0x04, 0x22, 0x04, 0x15 }; static unsigned char launch_browser_411[] = { 0xD0, 0x21, 0x81, 0x03, 0x01, 0x15, 0x02, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x10, 0x4E, 0x6F, 0x74, 0x20, 0x73, 0x65, 0x6C, 0x66, 0x20, 0x65, 0x78, 0x70, 0x6C, 0x61, 0x6E, 0x2E, 0x1E, 0x02, 0x01, 0x01 }; static unsigned char launch_browser_421[] = { 0xD0, 0x1D, 0x81, 0x03, 0x01, 0x15, 0x02, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0C, 0x53, 0x65, 0x6C, 0x66, 0x20, 0x65, 0x78, 0x70, 0x6C, 0x61, 0x6E, 0x2E, 0x1E, 0x02, 0x00, 0x01 }; static unsigned char launch_browser_511[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0D, 0x00, 0xB4 }; static unsigned char launch_browser_512[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x32 }; static unsigned char launch_browser_521[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0D, 0x01, 0xB4 }; static unsigned char launch_browser_522[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x32 }; static unsigned char launch_browser_531[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0D, 0x02, 0xB4 }; static unsigned char launch_browser_532[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x32 }; static unsigned char launch_browser_541[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0D, 0x04, 0xB4 }; static unsigned char launch_browser_542[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0D, 0x00, 0xB4 }; static unsigned char launch_browser_543[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x33 }; static unsigned char launch_browser_551[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0D, 0x08, 0xB4 }; static unsigned char launch_browser_552[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0D, 0x00, 0xB4 }; static unsigned char launch_browser_553[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x33 }; static unsigned char launch_browser_561[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0D, 0x10, 0xB4 }; static unsigned char launch_browser_562[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0D, 0x00, 0xB4 }; static unsigned char launch_browser_563[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x33 }; static unsigned char launch_browser_571[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0D, 0x20, 0xB4 }; static unsigned char launch_browser_572[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0D, 0x00, 0xB4 }; static unsigned char launch_browser_573[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x33 }; static unsigned char launch_browser_581[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0D, 0x40, 0xB4 }; static unsigned char launch_browser_582[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0D, 0x00, 0xB4 }; static unsigned char launch_browser_583[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x33 }; static unsigned char launch_browser_591[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0D, 0x80, 0xB4 }; static unsigned char launch_browser_592[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x32, 0xD0, 0x04, 0x00, 0x0D, 0x00, 0xB4 }; static unsigned char launch_browser_593[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x33 }; static unsigned char launch_browser_5101[] = { 0xD0, 0x20, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0D, 0x00, 0xB4 }; static unsigned char launch_browser_5102[] = { 0xD0, 0x1A, 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x0D, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x55, 0x52, 0x4C, 0x20, 0x32 }; static unsigned char launch_browser_611[] = { 0xD0, 0x12, 0x81, 0x03, 0x01, 0x15, 0x02, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x05, 0x80, 0x4F, 0x60, 0x59, 0x7D }; static unsigned char launch_browser_711[] = { 0xD0, 0x10, 0x81, 0x03, 0x01, 0x15, 0x02, 0x82, 0x02, 0x81, 0x82, 0x31, 0x00, 0x05, 0x03, 0x80, 0x30, 0xEB }; static struct launch_browser_test launch_browser_data_111 = { .pdu = launch_browser_111, .pdu_len = sizeof(launch_browser_111), .qualifier = 0x00, .alpha_id = "Default URL" }; static struct launch_browser_test launch_browser_data_121 = { .pdu = launch_browser_121, .pdu_len = sizeof(launch_browser_121), .qualifier = 0x00, .alpha_id = "", .url = "http://xxx.yyy.zzz" }; static struct launch_browser_test launch_browser_data_131 = { .pdu = launch_browser_131, .pdu_len = sizeof(launch_browser_131), .qualifier = 0x00 }; static struct launch_browser_test launch_browser_data_141 = { .pdu = launch_browser_141, .pdu_len = sizeof(launch_browser_141), .qualifier = 0x00, .bearer = { .len = 1, .array = (unsigned char *) "\x03" }, .text_gateway_proxy_id = "abc.def.ghi.jkl" }; static struct launch_browser_test launch_browser_data_211 = { .pdu = launch_browser_211, .pdu_len = sizeof(launch_browser_211), .qualifier = 0x02, .alpha_id = "Default URL" }; static struct launch_browser_test launch_browser_data_221 = { .pdu = launch_browser_221, .pdu_len = sizeof(launch_browser_221), .qualifier = 0x03, .alpha_id = "Default URL" }; static struct launch_browser_test launch_browser_data_231 = { .pdu = launch_browser_231, .pdu_len = sizeof(launch_browser_231), .qualifier = 0x00 }; static struct launch_browser_test launch_browser_data_311 = { .pdu = launch_browser_311, .pdu_len = sizeof(launch_browser_311), .qualifier = 0x02, .alpha_id = "ЗДРАВСТВУЙТЕ" }; static struct launch_browser_test launch_browser_data_411 = { .pdu = launch_browser_411, .pdu_len = sizeof(launch_browser_411), .qualifier = 0x02, .alpha_id = "Not self explan.", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_NON_SELF_EXPLANATORY, .id = 0x01 } }; static struct launch_browser_test launch_browser_data_421 = { .pdu = launch_browser_421, .pdu_len = sizeof(launch_browser_421), .qualifier = 0x02, .alpha_id = "Self explan.", .icon_id = { .qualifier = STK_ICON_QUALIFIER_TYPE_SELF_EXPLANATORY, .id = 0x01 } }; static struct launch_browser_test launch_browser_data_511 = { .pdu = launch_browser_511, .pdu_len = sizeof(launch_browser_511), .qualifier = 0x00, .alpha_id = "Default URL 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x00, 0xB4 } } }; static struct launch_browser_test launch_browser_data_512 = { .pdu = launch_browser_512, .pdu_len = sizeof(launch_browser_512), .qualifier = 0x00, .alpha_id = "Default URL 2" }; static struct launch_browser_test launch_browser_data_521 = { .pdu = launch_browser_521, .pdu_len = sizeof(launch_browser_521), .qualifier = 0x00, .alpha_id = "Default URL 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x01, 0xB4 } } }; static struct launch_browser_test launch_browser_data_522 = { .pdu = launch_browser_522, .pdu_len = sizeof(launch_browser_522), .qualifier = 0x00, .alpha_id = "Default URL 2" }; static struct launch_browser_test launch_browser_data_531 = { .pdu = launch_browser_531, .pdu_len = sizeof(launch_browser_531), .qualifier = 0x00, .alpha_id = "Default URL 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x02, 0xB4 } } }; static struct launch_browser_test launch_browser_data_532 = { .pdu = launch_browser_532, .pdu_len = sizeof(launch_browser_532), .qualifier = 0x00, .alpha_id = "Default URL 2" }; static struct launch_browser_test launch_browser_data_541 = { .pdu = launch_browser_541, .pdu_len = sizeof(launch_browser_541), .qualifier = 0x00, .alpha_id = "Default URL 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x04, 0xB4 } } }; static struct launch_browser_test launch_browser_data_542 = { .pdu = launch_browser_542, .pdu_len = sizeof(launch_browser_542), .qualifier = 0x00, .alpha_id = "Default URL 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x00, 0xB4 } } }; static struct launch_browser_test launch_browser_data_543 = { .pdu = launch_browser_543, .pdu_len = sizeof(launch_browser_543), .qualifier = 0x00, .alpha_id = "Default URL 3" }; static struct launch_browser_test launch_browser_data_551 = { .pdu = launch_browser_551, .pdu_len = sizeof(launch_browser_551), .qualifier = 0x00, .alpha_id = "Default URL 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x08, 0xB4 } } }; static struct launch_browser_test launch_browser_data_552 = { .pdu = launch_browser_552, .pdu_len = sizeof(launch_browser_552), .qualifier = 0x00, .alpha_id = "Default URL 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x00, 0xB4 } } }; static struct launch_browser_test launch_browser_data_553 = { .pdu = launch_browser_553, .pdu_len = sizeof(launch_browser_553), .qualifier = 0x00, .alpha_id = "Default URL 3" }; static struct launch_browser_test launch_browser_data_561 = { .pdu = launch_browser_561, .pdu_len = sizeof(launch_browser_561), .qualifier = 0x00, .alpha_id = "Default URL 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x10, 0xB4 } } }; static struct launch_browser_test launch_browser_data_562 = { .pdu = launch_browser_562, .pdu_len = sizeof(launch_browser_562), .qualifier = 0x00, .alpha_id = "Default URL 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x00, 0xB4 } } }; static struct launch_browser_test launch_browser_data_563 = { .pdu = launch_browser_563, .pdu_len = sizeof(launch_browser_563), .qualifier = 0x00, .alpha_id = "Default URL 3" }; static struct launch_browser_test launch_browser_data_571 = { .pdu = launch_browser_571, .pdu_len = sizeof(launch_browser_571), .qualifier = 0x00, .alpha_id = "Default URL 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x20, 0xB4 } } }; static struct launch_browser_test launch_browser_data_572 = { .pdu = launch_browser_572, .pdu_len = sizeof(launch_browser_572), .qualifier = 0x00, .alpha_id = "Default URL 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x00, 0xB4 } } }; static struct launch_browser_test launch_browser_data_573 = { .pdu = launch_browser_573, .pdu_len = sizeof(launch_browser_573), .qualifier = 0x00, .alpha_id = "Default URL 3" }; static struct launch_browser_test launch_browser_data_581 = { .pdu = launch_browser_581, .pdu_len = sizeof(launch_browser_581), .qualifier = 0x00, .alpha_id = "Default URL 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x40, 0xB4 } } }; static struct launch_browser_test launch_browser_data_582 = { .pdu = launch_browser_582, .pdu_len = sizeof(launch_browser_582), .qualifier = 0x00, .alpha_id = "Default URL 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x00, 0xB4 } } }; static struct launch_browser_test launch_browser_data_583 = { .pdu = launch_browser_583, .pdu_len = sizeof(launch_browser_583), .qualifier = 0x00, .alpha_id = "Default URL 3" }; static struct launch_browser_test launch_browser_data_591 = { .pdu = launch_browser_591, .pdu_len = sizeof(launch_browser_591), .qualifier = 0x00, .alpha_id = "Default URL 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x80, 0xB4 } } }; static struct launch_browser_test launch_browser_data_592 = { .pdu = launch_browser_592, .pdu_len = sizeof(launch_browser_592), .qualifier = 0x00, .alpha_id = "Default URL 2", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x00, 0xB4 } } }; static struct launch_browser_test launch_browser_data_593 = { .pdu = launch_browser_593, .pdu_len = sizeof(launch_browser_593), .qualifier = 0x00, .alpha_id = "Default URL 3" }; static struct launch_browser_test launch_browser_data_5101 = { .pdu = launch_browser_5101, .pdu_len = sizeof(launch_browser_5101), .qualifier = 0x00, .alpha_id = "Default URL 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0D, 0x00, 0xB4 } } }; static struct launch_browser_test launch_browser_data_5102 = { .pdu = launch_browser_5102, .pdu_len = sizeof(launch_browser_5102), .qualifier = 0x00, .alpha_id = "Default URL 2" }; static struct launch_browser_test launch_browser_data_611 = { .pdu = launch_browser_611, .pdu_len = sizeof(launch_browser_611), .qualifier = 0x02, .alpha_id = "你好" }; static struct launch_browser_test launch_browser_data_711 = { .pdu = launch_browser_711, .pdu_len = sizeof(launch_browser_711), .qualifier = 0x02, .alpha_id = "ル" }; static void test_launch_browser(gconstpointer data) { const struct launch_browser_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_LAUNCH_BROWSER); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_browser_id(command->launch_browser.browser_id, test->browser_id); check_url(command->launch_browser.url, test->url); check_bearer(&command->launch_browser.bearer, &test->bearer); check_provisioning_file_references( command->launch_browser.prov_file_refs, test->prov_file_refs); check_text(command->launch_browser.text_gateway_proxy_id, test->text_gateway_proxy_id); check_alpha_id(command->launch_browser.alpha_id, test->alpha_id); check_icon_id(&command->launch_browser.icon_id, &test->icon_id); check_text_attr(&command->launch_browser.text_attr, &test->text_attr); check_frame_id(&command->launch_browser.frame_id, &test->frame_id); check_text(command->launch_browser.text_usr, test->text_usr); check_text(command->launch_browser.text_passwd, test->text_passwd); stk_command_free(command); } struct open_channel_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; char *alpha_id; struct stk_icon_id icon_id; struct stk_bearer_description bearer_desc; unsigned short buf_size; char *apn; struct stk_other_address local_addr; char *text_usr; char *text_passwd; struct stk_uicc_te_interface uti; struct stk_other_address data_dest_addr; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; static unsigned char open_channel_211[] = { 0xD0, 0x36, 0x81, 0x03, 0x01, 0x40, 0x01, 0x82, 0x02, 0x81, 0x82, 0x35, 0x07, 0x02, 0x03, 0x04, 0x03, 0x04, 0x1F, 0x02, 0x39, 0x02, 0x05, 0x78, 0x0D, 0x08, 0xF4, 0x55, 0x73, 0x65, 0x72, 0x4C, 0x6F, 0x67, 0x0D, 0x08, 0xF4, 0x55, 0x73, 0x65, 0x72, 0x50, 0x77, 0x64, 0x3C, 0x03, 0x01, 0xAD, 0x9C, 0x3E, 0x05, 0x21, 0x01, 0x01, 0x01, 0x01 }; static unsigned char open_channel_221[] = { 0xD0, 0x42, 0x81, 0x03, 0x01, 0x40, 0x01, 0x82, 0x02, 0x81, 0x82, 0x35, 0x07, 0x02, 0x03, 0x04, 0x03, 0x04, 0x1F, 0x02, 0x39, 0x02, 0x05, 0x78, 0x47, 0x0A, 0x06, 0x54, 0x65, 0x73, 0x74, 0x47, 0x70, 0x02, 0x72, 0x73, 0x0D, 0x08, 0xF4, 0x55, 0x73, 0x65, 0x72, 0x4C, 0x6F, 0x67, 0x0D, 0x08, 0xF4, 0x55, 0x73, 0x65, 0x72, 0x50, 0x77, 0x64, 0x3C, 0x03, 0x01, 0xAD, 0x9C, 0x3E, 0x05, 0x21, 0x01, 0x01, 0x01, 0x01 }; static unsigned char open_channel_231[] = { 0xD0, 0x4B, 0x81, 0x03, 0x01, 0x40, 0x01, 0x82, 0x02, 0x81, 0x82, 0x05, 0x07, 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x49, 0x44, 0x35, 0x07, 0x02, 0x03, 0x04, 0x03, 0x04, 0x1F, 0x02, 0x39, 0x02, 0x05, 0x78, 0x47, 0x0A, 0x06, 0x54, 0x65, 0x73, 0x74, 0x47, 0x70, 0x02, 0x72, 0x73, 0x0D, 0x08, 0xF4, 0x55, 0x73, 0x65, 0x72, 0x4C, 0x6F, 0x67, 0x0D, 0x08, 0xF4, 0x55, 0x73, 0x65, 0x72, 0x50, 0x77, 0x64, 0x3C, 0x03, 0x01, 0xAD, 0x9C, 0x3E, 0x05, 0x21, 0x01, 0x01, 0x01, 0x01 }; static unsigned char open_channel_241[] = { 0xD0, 0x44, 0x81, 0x03, 0x01, 0x40, 0x01, 0x82, 0x02, 0x81, 0x82, 0x05, 0x00, 0x35, 0x07, 0x02, 0x03, 0x04, 0x03, 0x04, 0x1F, 0x02, 0x39, 0x02, 0x05, 0x78, 0x47, 0x0A, 0x06, 0x54, 0x65, 0x73, 0x74, 0x47, 0x70, 0x02, 0x72, 0x73, 0x0D, 0x08, 0xF4, 0x55, 0x73, 0x65, 0x72, 0x4C, 0x6F, 0x67, 0x0D, 0x08, 0xF4, 0x55, 0x73, 0x65, 0x72, 0x50, 0x77, 0x64, 0x3C, 0x03, 0x01, 0xAD, 0x9C, 0x3E, 0x05, 0x21, 0x01, 0x01, 0x01, 0x01 }; static unsigned char open_channel_511[] = { 0xD0, 0x53, 0x81, 0x03, 0x01, 0x40, 0x01, 0x82, 0x02, 0x81, 0x82, 0x05, 0x09, 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x49, 0x44, 0x20, 0x31, 0x35, 0x07, 0x02, 0x03, 0x04, 0x03, 0x04, 0x1F, 0x02, 0x39, 0x02, 0x05, 0x78, 0x47, 0x0A, 0x06, 0x54, 0x65, 0x73, 0x74, 0x47, 0x70, 0x02, 0x72, 0x73, 0x0D, 0x08, 0xF4, 0x55, 0x73, 0x65, 0x72, 0x4C, 0x6F, 0x67, 0x0D, 0x08, 0xF4, 0x55, 0x73, 0x65, 0x72, 0x50, 0x77, 0x64, 0x3C, 0x03, 0x01, 0xAD, 0x9C, 0x3E, 0x05, 0x21, 0x01, 0x01, 0x01, 0x01, 0xD0, 0x04, 0x00, 0x09, 0x00, 0xB4 }; static struct open_channel_test open_channel_data_211 = { /* * OPEN CHANNEL, immediate link establishment, GPRS, no local address * no alpha identifier, no network access name */ .pdu = open_channel_211, .pdu_len = sizeof(open_channel_211), .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE, .bearer_desc = { .type = STK_BEARER_TYPE_GPRS_UTRAN, .gprs = { .precedence = 3, .delay = 4, .reliability = 3, .peak = 4, .mean = 31, .pdp_type = 2, }, }, .buf_size = 1400, .text_usr = "UserLog", .text_passwd = "UserPwd", .uti = { .protocol = STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE, .port = 44444, }, .data_dest_addr = { .type = STK_ADDRESS_IPV4, .addr = { .ipv4 = 0x01010101, }, }, }; static struct open_channel_test open_channel_data_221 = { /* * OPEN CHANNEL, immediate link establishment GPRS, * no alpha identifier, with network access name */ .pdu = open_channel_221, .pdu_len = sizeof(open_channel_221), .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE, .bearer_desc = { .type = STK_BEARER_TYPE_GPRS_UTRAN, .gprs = { .precedence = 3, .delay = 4, .reliability = 3, .peak = 4, .mean = 31, .pdp_type = 2, }, }, .buf_size = 1400, .apn = "TestGp.rs", .text_usr = "UserLog", .text_passwd = "UserPwd", .uti = { .protocol = STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE, .port = 44444, }, .data_dest_addr = { .type = STK_ADDRESS_IPV4, .addr = { .ipv4 = 0x01010101, }, }, }; static struct open_channel_test open_channel_data_231 = { /* * OPEN CHANNEL, immediate link establishment, GPRS * with alpha identifier */ .pdu = open_channel_231, .pdu_len = sizeof(open_channel_231), .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE, .alpha_id = "Open ID", .bearer_desc = { .type = STK_BEARER_TYPE_GPRS_UTRAN, .gprs = { .precedence = 3, .delay = 4, .reliability = 3, .peak = 4, .mean = 31, .pdp_type = 2, }, }, .buf_size = 1400, .apn = "TestGp.rs", .text_usr = "UserLog", .text_passwd = "UserPwd", .uti = { .protocol = STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE, .port = 44444, }, .data_dest_addr = { .type = STK_ADDRESS_IPV4, .addr = { .ipv4 = 0x01010101, }, }, }; static struct open_channel_test open_channel_data_241 = { /* * OPEN CHANNEL, immediate link establishment, GPRS, * with null alpha identifier */ .pdu = open_channel_241, .pdu_len = sizeof(open_channel_241), .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE, .alpha_id = "", .bearer_desc = { .type = STK_BEARER_TYPE_GPRS_UTRAN, .gprs = { .precedence = 3, .delay = 4, .reliability = 3, .peak = 4, .mean = 31, .pdp_type = 2, }, }, .buf_size = 1400, .apn = "TestGp.rs", .text_usr = "UserLog", .text_passwd = "UserPwd", .uti = { .protocol = STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE, .port = 44444, }, .data_dest_addr = { .type = STK_ADDRESS_IPV4, .addr = { .ipv4 = 0x01010101, }, }, }; static struct open_channel_test open_channel_data_511 = { /* * OPEN CHANNEL, immediate link establishment, GPRS * Text Attribute – Left Alignment */ .pdu = open_channel_511, .pdu_len = sizeof(open_channel_511), .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE, .alpha_id = "Open ID 1", .bearer_desc = { .type = STK_BEARER_TYPE_GPRS_UTRAN, .gprs = { .precedence = 3, .delay = 4, .reliability = 3, .peak = 4, .mean = 31, .pdp_type = 2, }, }, .buf_size = 1400, .apn = "TestGp.rs", .text_usr = "UserLog", .text_passwd = "UserPwd", .uti = { .protocol = STK_TRANSPORT_PROTOCOL_UDP_CLIENT_REMOTE, .port = 44444, }, .data_dest_addr = { .type = STK_ADDRESS_IPV4, .addr = { .ipv4 = 0x01010101, }, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x09, 0x00, 0xB4 } }, }; static void test_open_channel(gconstpointer data) { const struct open_channel_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_OPEN_CHANNEL); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); check_alpha_id(command->open_channel.alpha_id, test->alpha_id); check_icon_id(&command->open_channel.icon_id, &test->icon_id); check_bearer_desc(&command->open_channel.bearer_desc, &test->bearer_desc); g_assert(command->open_channel.buf_size == test->buf_size); check_network_access_name(command->open_channel.apn, test->apn); check_other_address(&command->open_channel.local_addr, &test->local_addr); check_text(command->open_channel.text_usr, test->text_usr); check_text(command->open_channel.text_passwd, test->text_passwd); check_uicc_te_interface(&command->open_channel.uti, &test->uti); check_other_address(&command->open_channel.data_dest_addr, &test->data_dest_addr); check_text_attr(&command->open_channel.text_attr, &test->text_attr); check_frame_id(&command->open_channel.frame_id, &test->frame_id); stk_command_free(command); } struct close_channel_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; enum stk_device_identity_type dst; char *alpha_id; struct stk_icon_id icon_id; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; static unsigned char close_channel_111[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x41, 0x00, 0x82, 0x02, 0x81, 0x21 }; static struct close_channel_test close_channel_data_111 = { .pdu = close_channel_111, .pdu_len = sizeof(close_channel_111), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1, }; static unsigned char close_channel_211[] = { 0xD0, 0x1B, 0x81, 0x03, 0x01, 0x41, 0x00, 0x82, 0x02, 0x81, 0x21, 0x85, 0x0A, 0x43, 0x6C, 0x6F, 0x73, 0x65, 0x20, 0x49, 0x44, 0x20, 0x31, 0xD0, 0x04, 0x00, 0x0A, 0x00, 0xB4, }; static struct close_channel_test close_channel_data_211 = { .pdu = close_channel_211, .pdu_len = sizeof(close_channel_211), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1, .alpha_id = "Close ID 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0A, 0x00, 0xB4 } }, }; static void test_close_channel(gconstpointer data) { const struct close_channel_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_CLOSE_CHANNEL); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == test->dst); check_alpha_id(command->close_channel.alpha_id, test->alpha_id); check_icon_id(&command->close_channel.icon_id, &test->icon_id); check_text_attr(&command->close_channel.text_attr, &test->text_attr); check_frame_id(&command->close_channel.frame_id, &test->frame_id); stk_command_free(command); } struct receive_data_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; enum stk_device_identity_type dst; char *alpha_id; struct stk_icon_id icon_id; unsigned char data_len; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; static unsigned char receive_data_111[] = { 0xD0, 0x0C, 0x81, 0x03, 0x01, 0x42, 0x00, 0x82, 0x02, 0x81, 0x21, 0xB7, 0x01, 0xC8 }; static struct receive_data_test receive_data_data_111 = { .pdu = receive_data_111, .pdu_len = sizeof(receive_data_111), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1, .data_len = 200, }; static unsigned char receive_data_211[] = { 0xD0, 0x22, 0x81, 0x03, 0x01, 0x42, 0x00, 0x82, 0x02, 0x81, 0x21, 0x85, 0x0E, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x31, 0xB7, 0x01, 0xC8, 0xD0, 0x04, 0x00, 0x0E, 0x00, 0xB4 }; static struct receive_data_test receive_data_data_211 = { .pdu = receive_data_211, .pdu_len = sizeof(receive_data_211), .qualifier = 0x00, .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1, .data_len = 200, .alpha_id = "Receive Data 1", .text_attr = { .len = 4, .attributes = { 0x00, 0x0E, 0x00, 0xB4 } }, }; static void test_receive_data(gconstpointer data) { const struct receive_data_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_RECEIVE_DATA); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == test->dst); check_alpha_id(command->receive_data.alpha_id, test->alpha_id); check_icon_id(&command->receive_data.icon_id, &test->icon_id); check_common_byte(command->receive_data.data_len, test->data_len); check_text_attr(&command->receive_data.text_attr, &test->text_attr); check_frame_id(&command->receive_data.frame_id, &test->frame_id); stk_command_free(command); } struct send_data_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; enum stk_device_identity_type dst; char *alpha_id; struct stk_icon_id icon_id; struct stk_common_byte_array data; struct stk_text_attribute text_attr; struct stk_frame_id frame_id; }; static unsigned char send_data_111[] = { 0xD0, 0x13, 0x81, 0x03, 0x01, 0x43, 0x01, 0x82, 0x02, 0x81, 0x21, 0xB6, 0x08, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; static struct send_data_test send_data_data_111 = { .pdu = send_data_111, .pdu_len = sizeof(send_data_111), .qualifier = STK_SEND_DATA_IMMEDIATELY, .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1, .data = { .array = (unsigned char[8]) { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }, .len = 8, }, }; static unsigned char send_data_121[] = { 0xD0, 0x81, 0xD4, 0x81, 0x03, 0x01, 0x43, 0x00, 0x82, 0x02, 0x81, 0x21, 0xB6, 0x81, 0xC8, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 }; static struct send_data_test send_data_data_121 = { .pdu = send_data_121, .pdu_len = sizeof(send_data_121), .qualifier = STK_SEND_DATA_STORE_DATA, .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1, .data = { .array = (unsigned char[200]) { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, }, .len = 200, }, }; static unsigned char send_data_211[] = { 0xD0, 0x26, 0x81, 0x03, 0x01, 0x43, 0x01, 0x82, 0x02, 0x81, 0x21, 0x85, 0x0B, 0x53, 0x65, 0x6E, 0x64, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x31, 0xB6, 0x08, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xD0, 0x04, 0x00, 0x0B, 0x00, 0xB4, }; static struct send_data_test send_data_data_211 = { .pdu = send_data_211, .pdu_len = sizeof(send_data_211), .qualifier = STK_SEND_DATA_IMMEDIATELY, .dst = STK_DEVICE_IDENTITY_TYPE_CHANNEL_1, .alpha_id = "Send Data 1", .data = { .array = (unsigned char[8]) { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }, .len = 8, }, .text_attr = { .len = 4, .attributes = { 0x00, 0x0B, 0x00, 0xB4 } }, }; static void test_send_data(gconstpointer data) { const struct send_data_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_SEND_DATA); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == test->dst); check_alpha_id(command->send_data.alpha_id, test->alpha_id); check_icon_id(&command->send_data.icon_id, &test->icon_id); check_channel_data(&command->send_data.data, &test->data); check_text_attr(&command->send_data.text_attr, &test->text_attr); check_frame_id(&command->send_data.frame_id, &test->frame_id); stk_command_free(command); } struct get_channel_status_test { const unsigned char *pdu; unsigned int pdu_len; unsigned char qualifier; }; static unsigned char get_channel_status_111[] = { 0xD0, 0x09, 0x81, 0x03, 0x01, 0x44, 0x00, 0x82, 0x02, 0x81, 0x82 }; static struct get_channel_status_test get_channel_status_data_111 = { .pdu = get_channel_status_111, .pdu_len = sizeof(get_channel_status_111), .qualifier = 0x00, }; static void test_get_channel_status(gconstpointer data) { const struct get_channel_status_test *test = data; struct stk_command *command; command = stk_command_new_from_pdu(test->pdu, test->pdu_len); g_assert(command); g_assert(command->status == STK_PARSE_RESULT_OK); g_assert(command->number == 1); g_assert(command->type == STK_COMMAND_TYPE_GET_CHANNEL_STATUS); g_assert(command->qualifier == test->qualifier); g_assert(command->src == STK_DEVICE_IDENTITY_TYPE_UICC); g_assert(command->dst == STK_DEVICE_IDENTITY_TYPE_TERMINAL); stk_command_free(command); } struct terminal_response_test { const unsigned char *pdu; unsigned int pdu_len; struct stk_response response; }; static void test_terminal_response_encoding(gconstpointer data) { const struct terminal_response_test *test = data; const unsigned char *pdu; unsigned int pdu_len; pdu = stk_pdu_from_response(&test->response, &pdu_len); if (test->pdu) g_assert(pdu); else g_assert(pdu == NULL); g_assert(pdu_len == test->pdu_len); g_assert(memcmp(pdu, test->pdu, pdu_len) == 0); } static const struct terminal_response_test display_text_response_data_111 = { .pdu = display_text_response_111, .pdu_len = sizeof(display_text_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_DISPLAY_TEXT, .qualifier = 0x80, /* Wait for user to clear */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const struct terminal_response_test display_text_response_data_121 = { .pdu = display_text_response_121, .pdu_len = sizeof(display_text_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_DISPLAY_TEXT, .qualifier = 0x80, /* Wait for user to clear */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TERMINAL_BUSY, .additional_len = 1, /* Screen is busy */ .additional = (unsigned char *) "\1", }, }, }; static const struct terminal_response_test display_text_response_data_131 = { .pdu = display_text_response_131, .pdu_len = sizeof(display_text_response_131), .response = { .number = 1, .type = STK_COMMAND_TYPE_DISPLAY_TEXT, .qualifier = 0x81, /* Wait for user to clear, High priority */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const struct terminal_response_test display_text_response_data_151 = { .pdu = display_text_response_151, .pdu_len = sizeof(display_text_response_151), .response = { .number = 1, .type = STK_COMMAND_TYPE_DISPLAY_TEXT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const struct terminal_response_test display_text_response_data_171 = { .pdu = display_text_response_171, .pdu_len = sizeof(display_text_response_171), .response = { .number = 1, .type = STK_COMMAND_TYPE_DISPLAY_TEXT, .qualifier = 0x80, /* Wait for user to clear */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_GO_BACK, }, }, }; static const struct terminal_response_test display_text_response_data_181 = { .pdu = display_text_response_181, .pdu_len = sizeof(display_text_response_181), .response = { .number = 1, .type = STK_COMMAND_TYPE_DISPLAY_TEXT, .qualifier = 0x80, /* Wait for user to clear */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_USER_TERMINATED, }, }, }; static const struct terminal_response_test display_text_response_data_191 = { .pdu = display_text_response_191, .pdu_len = sizeof(display_text_response_191), .response = { .number = 1, .type = STK_COMMAND_TYPE_DISPLAY_TEXT, .qualifier = 0x80, /* Wait for user to clear */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD, }, }, }; static const struct terminal_response_test display_text_response_data_211 = { .pdu = display_text_response_211, .pdu_len = sizeof(display_text_response_211), .response = { .number = 1, .type = STK_COMMAND_TYPE_DISPLAY_TEXT, .qualifier = 0x80, /* Wait for user to clear */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_RESPONSE, }, }, }; static const struct terminal_response_test display_text_response_data_511b = { .pdu = display_text_response_511b, .pdu_len = sizeof(display_text_response_511b), .response = { .number = 1, .type = STK_COMMAND_TYPE_DISPLAY_TEXT, .qualifier = 0x80, /* Wait for user to clear */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, }, }; static const struct terminal_response_test get_inkey_response_data_111 = { .pdu = get_inkey_response_111, .pdu_len = sizeof(get_inkey_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = "+", }, }}, }, }; static const struct terminal_response_test get_inkey_response_data_121 = { .pdu = get_inkey_response_121, .pdu_len = sizeof(get_inkey_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = "0", }, }}, }, }; static const struct terminal_response_test get_inkey_response_data_131 = { .pdu = get_inkey_response_131, .pdu_len = sizeof(get_inkey_response_131), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_GO_BACK, }, }, }; static const struct terminal_response_test get_inkey_response_data_141 = { .pdu = get_inkey_response_141, .pdu_len = sizeof(get_inkey_response_141), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_USER_TERMINATED, }, }, }; static const struct terminal_response_test get_inkey_response_data_151 = { .pdu = get_inkey_response_151, .pdu_len = sizeof(get_inkey_response_151), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x01, /* SMS alphabet */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = "q", }, }}, }, }; static const struct terminal_response_test get_inkey_response_data_161 = { .pdu = get_inkey_response_161, .pdu_len = sizeof(get_inkey_response_161), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x01, /* SMS alphabet */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = "x", }, }}, }, }; static const struct terminal_response_test get_inkey_response_data_211 = { .pdu = get_inkey_response_211, .pdu_len = sizeof(get_inkey_response_211), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_RESPONSE, }, }, }; static const struct terminal_response_test get_inkey_response_data_411 = { .pdu = get_inkey_response_411, .pdu_len = sizeof(get_inkey_response_411), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x03, /* UCS2 alphabet */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = "Д", }, }}, }, }; static const struct terminal_response_test get_inkey_response_data_511 = { .pdu = get_inkey_response_511, .pdu_len = sizeof(get_inkey_response_511), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x04, /* Yes/No response */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = "Yes", .yesno = TRUE, }, }}, }, }; static const struct terminal_response_test get_inkey_response_data_512 = { .pdu = get_inkey_response_512, .pdu_len = sizeof(get_inkey_response_512), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x04, /* Yes/No response */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = NULL, .yesno = TRUE, }, }}, }, }; static const unsigned char get_inkey_response_611b[] = { 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, 0x8d, 0x02, 0x04, 0x2b, }; static const struct terminal_response_test get_inkey_response_data_611b = { .pdu = get_inkey_response_611b, .pdu_len = sizeof(get_inkey_response_611b), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, { .get_inkey = { .text = { .text = "+", }, }}, }, }; static const unsigned char get_inkey_response_711[] = { 0x81, 0x03, 0x01, 0x22, 0x80, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x13, }; static const struct terminal_response_test get_inkey_response_data_711 = { .pdu = get_inkey_response_711, .pdu_len = sizeof(get_inkey_response_711), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x80, /* Help information available */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_HELP_REQUESTED, }, }, }; static const unsigned char get_inkey_response_712[] = { 0x81, 0x03, 0x01, 0x22, 0x80, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x2b, }; static const struct terminal_response_test get_inkey_response_data_712 = { .pdu = get_inkey_response_712, .pdu_len = sizeof(get_inkey_response_712), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x80, /* Help information available */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = "+", }, }}, }, }; static const struct terminal_response_test get_inkey_response_data_811 = { .pdu = get_inkey_response_811, .pdu_len = sizeof(get_inkey_response_811), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_RESPONSE, }, { .get_inkey = { .duration = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 11, }, }}, }, }; static const unsigned char get_inkey_response_912[] = { 0x81, 0x03, 0x01, 0x22, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x02, 0x04, 0x23, }; static const struct terminal_response_test get_inkey_response_data_912 = { .pdu = get_inkey_response_912, .pdu_len = sizeof(get_inkey_response_912), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = "#", }, }}, }, }; static const struct terminal_response_test get_inkey_response_data_1111 = { .pdu = get_inkey_response_1111, .pdu_len = sizeof(get_inkey_response_1111), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x03, /* UCS2 alphabet */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = "好", }, }}, }, }; static const struct terminal_response_test get_inkey_response_data_1311 = { .pdu = get_inkey_response_1311, .pdu_len = sizeof(get_inkey_response_1311), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INKEY, .qualifier = 0x03, /* UCS2 alphabet */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_inkey = { .text = { .text = "ル", }, }}, }, }; static const struct terminal_response_test get_input_response_data_111 = { .pdu = get_input_response_111, .pdu_len = sizeof(get_input_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "12345", }, }}, }, }; static const struct terminal_response_test get_input_response_data_121 = { .pdu = get_input_response_121, .pdu_len = sizeof(get_input_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x08, /* Input is packed */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "67*#+", .packed = TRUE, }, }}, }, }; static const struct terminal_response_test get_input_response_data_131 = { .pdu = get_input_response_131, .pdu_len = sizeof(get_input_response_131), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x01, /* Allow all SMS characters */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "AbCdE", }, }}, }, }; static const struct terminal_response_test get_input_response_data_141 = { .pdu = get_input_response_141, .pdu_len = sizeof(get_input_response_141), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x04, /* Hide text */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "2345678", }, }}, }, }; static const struct terminal_response_test get_input_response_data_151 = { .pdu = get_input_response_151, .pdu_len = sizeof(get_input_response_151), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "12345678901234567890", }, }}, }, }; static const struct terminal_response_test get_input_response_data_161 = { .pdu = get_input_response_161, .pdu_len = sizeof(get_input_response_161), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_GO_BACK, }, }, }; static const struct terminal_response_test get_input_response_data_171 = { .pdu = get_input_response_171, .pdu_len = sizeof(get_input_response_171), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_USER_TERMINATED, }, }, }; static const struct terminal_response_test get_input_response_data_181 = { .pdu = get_input_response_181, .pdu_len = sizeof(get_input_response_181), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "***1111111111###***2222222222###" "***3333333333###***4444444444###" "***5555555555###***6666666666###" "***7777777777###***8888888888###" "***9999999999###***0000000000###", }, }}, }, }; static const unsigned char get_input_response_191b[] = { 0x81, 0x03, 0x01, 0x23, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x00, }; static const struct terminal_response_test get_input_response_data_191 = { /* Either get_input_response_191a or get_input_response_191b is ok */ .pdu = get_input_response_191a, .pdu_len = sizeof(get_input_response_191a), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, /* Allow all SMS characters */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "", }, }}, }, }; static const struct terminal_response_test get_input_response_data_211 = { .pdu = get_input_response_211, .pdu_len = sizeof(get_input_response_211), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_RESPONSE, }, }, }; static const struct terminal_response_test get_input_response_data_311 = { .pdu = get_input_response_311, .pdu_len = sizeof(get_input_response_311), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x01, /* Allow all SMS characters */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "HELLO", }, }}, }, }; static const struct terminal_response_test get_input_response_data_411 = { .pdu = get_input_response_411, .pdu_len = sizeof(get_input_response_411), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x03, /* Allow all UCS2 characters */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "ЗДРАВСТВУЙТЕ", }, }}, }, }; static const struct terminal_response_test get_input_response_data_421 = { .pdu = get_input_response_421, .pdu_len = sizeof(get_input_response_421), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x03, /* Allow all UCS2 characters */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙТЕ" "ЗДРАВСТВУЙТЕЗДРАВСТВУЙ", }, }}, }, }; static const struct terminal_response_test get_input_response_data_611a = { .pdu = get_input_response_611a, .pdu_len = sizeof(get_input_response_611a), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "+", }, }}, }, }; static const unsigned char get_input_response_611b[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, 0x8d, 0x02, 0x04, 0x2b, }; static const struct terminal_response_test get_input_response_data_611b = { .pdu = get_input_response_611b, .pdu_len = sizeof(get_input_response_611b), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, { .get_input = { .text = { .text = "+", }, }}, }, }; static const unsigned char get_input_response_711[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x13, }; static const struct terminal_response_test get_input_response_data_711 = { .pdu = get_input_response_711, .pdu_len = sizeof(get_input_response_711), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_HELP_REQUESTED, }, }, }; static const unsigned char get_input_response_812[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x04, 0x32, 0x32, 0x32, 0x32, 0x32, }; static const struct terminal_response_test get_input_response_data_812 = { .pdu = get_input_response_812, .pdu_len = sizeof(get_input_response_812), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "22222", }, }}, }, }; static const unsigned char get_input_response_843[] = { 0x81, 0x03, 0x01, 0x23, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x8d, 0x06, 0x04, 0x33, 0x33, 0x33, 0x33, 0x33, }; static const struct terminal_response_test get_input_response_data_843 = { .pdu = get_input_response_843, .pdu_len = sizeof(get_input_response_843), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "33333", }, }}, }, }; static const struct terminal_response_test get_input_response_data_1011 = { .pdu = get_input_response_1011, .pdu_len = sizeof(get_input_response_1011), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x03, /* Allow all UCS2 characters */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "你好", }, }}, }, }; static const struct terminal_response_test get_input_response_data_1021 = { .pdu = get_input_response_1021, .pdu_len = sizeof(get_input_response_1021), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x03, /* Allow all UCS2 characters */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "你好你好你好你好你好你好" "你好你好你好你好你好你好" "你好你好你好你好你好你好" "你好你好你好你好你好你好" "你好你好你好你好你好你好" "你好你好你好你好你好", }, }}, }, }; static const struct terminal_response_test get_input_response_data_1211 = { .pdu = get_input_response_1211, .pdu_len = sizeof(get_input_response_1211), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x03, /* Allow all UCS2 characters */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "ルル", }, }}, }, }; static const struct terminal_response_test get_input_response_data_1221 = { .pdu = get_input_response_1221, .pdu_len = sizeof(get_input_response_1221), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_INPUT, .qualifier = 0x03, /* Allow all UCS2 characters */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .get_input = { .text = { .text = "ルルルルルルルルルルルル" "ルルルルルルルルルルルル" "ルルルルルルルルルルルル" "ルルルルルルルルルルルル" "ルルルルルルルルルルルル" "ルルルルルルルルルル", }, }}, }, }; static const struct terminal_response_test more_time_response_data_111 = { .pdu = more_time_response_111, .pdu_len = sizeof(more_time_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_MORE_TIME, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char send_sms_response_111[] = { 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test send_sms_response_data_111 = { .pdu = send_sms_response_111, .pdu_len = sizeof(send_sms_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_SEND_SMS, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char send_sms_response_121[] = { 0x81, 0x03, 0x01, 0x13, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test send_sms_response_data_121 = { .pdu = send_sms_response_121, .pdu_len = sizeof(send_sms_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_SEND_SMS, .qualifier = 0x01, /* Packing required */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char send_sms_response_311b[] = { 0x81, 0x03, 0x01, 0x13, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, }; static const struct terminal_response_test send_sms_response_data_311b = { .pdu = send_sms_response_311b, .pdu_len = sizeof(send_sms_response_311b), .response = { .number = 1, .type = STK_COMMAND_TYPE_SEND_SMS, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, }, }; static const struct terminal_response_test play_tone_response_data_111 = { .pdu = play_tone_response_111, .pdu_len = sizeof(play_tone_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_PLAY_TONE, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char play_tone_response_119b[] = { 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x30, }; static const struct terminal_response_test play_tone_response_data_119b = { .pdu = play_tone_response_119b, .pdu_len = sizeof(play_tone_response_119b), .response = { .number = 1, .type = STK_COMMAND_TYPE_PLAY_TONE, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NOT_CAPABLE, }, }, }; static const struct terminal_response_test play_tone_response_data_1114 = { .pdu = play_tone_response_1114, .pdu_len = sizeof(play_tone_response_1114), .response = { .number = 1, .type = STK_COMMAND_TYPE_PLAY_TONE, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_USER_TERMINATED, }, }, }; static const unsigned char play_tone_response_311b[] = { 0x81, 0x03, 0x01, 0x20, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, }; static const struct terminal_response_test play_tone_response_data_311b = { .pdu = play_tone_response_311b, .pdu_len = sizeof(play_tone_response_311b), .response = { .number = 1, .type = STK_COMMAND_TYPE_PLAY_TONE, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, }, }; static const struct terminal_response_test poll_interval_response_data_111 = { .pdu = poll_interval_response_111, .pdu_len = sizeof(poll_interval_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_POLL_INTERVAL, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .poll_interval = { .max_interval = { .unit = STK_DURATION_TYPE_SECONDS, .interval = 20, }, }}, }, }; /* 3GPP TS 31.124 */ static const unsigned char poll_interval_response_111a[] = { 0x81, 0x03, 0x01, 0x03, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x84, 0x02, 0x00, 0x01, }; static const unsigned char poll_interval_response_111b[] = { 0x81, 0x03, 0x01, 0x03, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x84, 0x02, 0x01, 0x3c, }; static const struct terminal_response_test poll_interval_response_data_111a = { /* Either poll_interval_response_111a or b is ok */ .pdu = poll_interval_response_111a, .pdu_len = sizeof(poll_interval_response_111a), .response = { .number = 1, .type = STK_COMMAND_TYPE_POLL_INTERVAL, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .poll_interval = { .max_interval = { .unit = STK_DURATION_TYPE_MINUTES, .interval = 1, }, }}, }, }; static const unsigned char refresh_response_111a[] = { 0x81, 0x03, 0x01, 0x01, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test refresh_response_data_111a = { .pdu = refresh_response_111a, .pdu_len = sizeof(refresh_response_111a), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x03, /* USIM Initialization */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char refresh_response_111b[] = { 0x81, 0x03, 0x01, 0x01, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x03, }; static const struct terminal_response_test refresh_response_data_111b = { .pdu = refresh_response_111b, .pdu_len = sizeof(refresh_response_111b), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x03, /* USIM Initialization */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_REFRESH_WITH_EFS, }, }, }; static const unsigned char refresh_response_121a[] = { 0x81, 0x03, 0x01, 0x01, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test refresh_response_data_121a = { .pdu = refresh_response_121a, .pdu_len = sizeof(refresh_response_121a), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x01, /* File Change Notification */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char refresh_response_121b[] = { 0x81, 0x03, 0x01, 0x01, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x03, }; static const struct terminal_response_test refresh_response_data_121b = { .pdu = refresh_response_121b, .pdu_len = sizeof(refresh_response_121b), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x01, /* File Change Notification */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_REFRESH_WITH_EFS, }, }, }; static const unsigned char refresh_response_131a[] = { 0x81, 0x03, 0x01, 0x01, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test refresh_response_data_131a = { .pdu = refresh_response_131a, .pdu_len = sizeof(refresh_response_131a), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x02, /* USIM Initialization & File Change */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char refresh_response_131b[] = { 0x81, 0x03, 0x01, 0x01, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x03, }; static const struct terminal_response_test refresh_response_data_131b = { .pdu = refresh_response_131b, .pdu_len = sizeof(refresh_response_131b), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x02, /* USIM Initialization & File Change */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_REFRESH_WITH_EFS, }, }, }; static const unsigned char refresh_response_141a[] = { 0x81, 0x03, 0x01, 0x01, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test refresh_response_data_141a = { .pdu = refresh_response_141a, .pdu_len = sizeof(refresh_response_141a), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x00, /* USIM Initialization & Full File Change */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char refresh_response_141b[] = { 0x81, 0x03, 0x01, 0x01, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x03, }; static const struct terminal_response_test refresh_response_data_141b = { .pdu = refresh_response_141b, .pdu_len = sizeof(refresh_response_141b), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x00, /* USIM Initialization & Full File Change */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_REFRESH_WITH_EFS, }, }, }; static const unsigned char refresh_response_171[] = { 0x81, 0x03, 0x01, 0x01, 0x05, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test refresh_response_data_171 = { .pdu = refresh_response_171, .pdu_len = sizeof(refresh_response_171), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x05, /* USIM Application Reset */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char refresh_response_241a[] = { 0x81, 0x03, 0x01, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x20, 0x02, }; static const struct terminal_response_test refresh_response_data_241a = { .pdu = refresh_response_241a, .pdu_len = sizeof(refresh_response_241a), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x06, /* 3G Session Reset */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TERMINAL_BUSY, .additional_len = 1, /* ME currently busy on call */ .additional = (unsigned char *) "\2", }, }, }; static const unsigned char refresh_response_241b[] = { 0x81, 0x03, 0x01, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x20, 0x01, }; static const struct terminal_response_test refresh_response_data_241b = { .pdu = refresh_response_241b, .pdu_len = sizeof(refresh_response_241b), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x06, /* 3G Session Reset */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TERMINAL_BUSY, .additional_len = 1, /* Screen is busy */ .additional = (unsigned char *) "\1", }, }, }; static const unsigned char refresh_response_311[] = { 0x81, 0x03, 0x01, 0x01, 0x07, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x20, }; static const struct terminal_response_test refresh_response_data_311 = { .pdu = refresh_response_311, .pdu_len = sizeof(refresh_response_311), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x07, /* Steering of roaming */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TERMINAL_BUSY, }, }, }; static const unsigned char refresh_response_312[] = { 0x81, 0x03, 0x01, 0x01, 0x07, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test refresh_response_data_312 = { .pdu = refresh_response_312, .pdu_len = sizeof(refresh_response_312), .response = { .number = 1, .type = STK_COMMAND_TYPE_REFRESH, .qualifier = 0x07, /* Steering of roaming */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char set_up_menu_response_111[] = { 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test set_up_menu_response_data_111 = { .pdu = set_up_menu_response_111, .pdu_len = sizeof(set_up_menu_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_MENU, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char set_up_menu_response_411b[] = { 0x81, 0x03, 0x01, 0x25, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, }; static const struct terminal_response_test set_up_menu_response_data_411b = { .pdu = set_up_menu_response_411b, .pdu_len = sizeof(set_up_menu_response_411b), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_MENU, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, }, }; static const unsigned char set_up_menu_response_511[] = { 0x81, 0x03, 0x01, 0x25, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test set_up_menu_response_data_511 = { .pdu = set_up_menu_response_511, .pdu_len = sizeof(set_up_menu_response_511), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_MENU, .qualifier = 0x01, /* Soft key selection preferred */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char select_item_response_111[] = { 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x02, }; static const struct terminal_response_test select_item_response_data_111 = { .pdu = select_item_response_111, .pdu_len = sizeof(select_item_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .select_item = { .item_id = 2, }}, }, }; static const unsigned char select_item_response_121[] = { 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x3d, }; static const struct terminal_response_test select_item_response_data_121 = { .pdu = select_item_response_121, .pdu_len = sizeof(select_item_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .select_item = { .item_id = 61, }}, }, }; static const unsigned char select_item_response_131[] = { 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0xfb, }; static const struct terminal_response_test select_item_response_data_131 = { .pdu = select_item_response_131, .pdu_len = sizeof(select_item_response_131), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .select_item = { .item_id = 251, }}, }, }; static const unsigned char select_item_response_141[] = { 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x11, }; static const struct terminal_response_test select_item_response_data_141 = { /* The response can be select_item_response_141 or it can optionally * have an ITEM_ID data object appended with any id (90 01 XX). */ .pdu = select_item_response_141, .pdu_len = sizeof(select_item_response_141), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_GO_BACK, }, }, }; static const unsigned char select_item_response_142[] = { 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x10, }; static const struct terminal_response_test select_item_response_data_142 = { /* The response can be select_item_response_142 or it can optionally * have an ITEM_ID data object appended with any id (90 01 XX). */ .pdu = select_item_response_142, .pdu_len = sizeof(select_item_response_142), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_USER_TERMINATED, }, }, }; static const unsigned char select_item_response_151[] = { 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x01, }; static const struct terminal_response_test select_item_response_data_151 = { .pdu = select_item_response_151, .pdu_len = sizeof(select_item_response_151), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .select_item = { .item_id = 1, }}, }, }; static const unsigned char select_item_response_311[] = { 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x03, }; static const struct terminal_response_test select_item_response_data_311 = { .pdu = select_item_response_311, .pdu_len = sizeof(select_item_response_311), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .select_item = { .item_id = 3, }}, }, }; static const unsigned char select_item_response_411[] = { 0x81, 0x03, 0x01, 0x24, 0x80, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x13, 0x90, 0x01, 0x01, }; static const struct terminal_response_test select_item_response_data_411 = { .pdu = select_item_response_411, .pdu_len = sizeof(select_item_response_411), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x80, /* Help information available */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_HELP_REQUESTED, }, { .select_item = { .item_id = 1, }}, }, }; static const unsigned char select_item_response_511b[] = { 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, 0x90, 0x01, 0x01, }; static const struct terminal_response_test select_item_response_data_511b = { .pdu = select_item_response_511b, .pdu_len = sizeof(select_item_response_511b), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, { .select_item = { .item_id = 1, }}, }, }; static const unsigned char select_item_response_611[] = { 0x81, 0x03, 0x01, 0x24, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x01, }; static const struct terminal_response_test select_item_response_data_611 = { .pdu = select_item_response_611, .pdu_len = sizeof(select_item_response_611), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x03, /* Choice of navigation options */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .select_item = { .item_id = 1, }}, }, }; static const unsigned char select_item_response_621[] = { 0x81, 0x03, 0x01, 0x24, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x01, }; static const struct terminal_response_test select_item_response_data_621 = { .pdu = select_item_response_621, .pdu_len = sizeof(select_item_response_621), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x01, /* Choice of data values presentation */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .select_item = { .item_id = 1, }}, }, }; static const unsigned char select_item_response_711[] = { 0x81, 0x03, 0x01, 0x24, 0x04, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x90, 0x01, 0x01, }; static const struct terminal_response_test select_item_response_data_711 = { .pdu = select_item_response_711, .pdu_len = sizeof(select_item_response_711), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x04, /* Selection using soft keys preferred */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .select_item = { .item_id = 1, }}, }, }; static const unsigned char select_item_response_811[] = { 0x81, 0x03, 0x01, 0x24, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x12, }; static const struct terminal_response_test select_item_response_data_811 = { .pdu = select_item_response_811, .pdu_len = sizeof(select_item_response_811), .response = { .number = 1, .type = STK_COMMAND_TYPE_SELECT_ITEM, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_RESPONSE, }, }, }; static const unsigned char set_up_call_response_111[] = { 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test set_up_call_response_data_111 = { .pdu = set_up_call_response_111, .pdu_len = sizeof(set_up_call_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x00, /* Only if not busy on another call */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char set_up_call_response_121[] = { 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x22, }; static const struct terminal_response_test set_up_call_response_data_121 = { .pdu = set_up_call_response_121, .pdu_len = sizeof(set_up_call_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x00, /* Only if not busy on another call */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_USER_REJECT, }, }, }; static const unsigned char set_up_call_response_141[] = { 0x81, 0x03, 0x01, 0x10, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test set_up_call_response_data_141 = { .pdu = set_up_call_response_141, .pdu_len = sizeof(set_up_call_response_141), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x02, /* Put all other calls on hold */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char set_up_call_response_151[] = { 0x81, 0x03, 0x01, 0x10, 0x04, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test set_up_call_response_data_151 = { .pdu = set_up_call_response_151, .pdu_len = sizeof(set_up_call_response_151), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x04, /* Disconnect all other calls */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char set_up_call_response_161[] = { 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x20, 0x02, }; static const struct terminal_response_test set_up_call_response_data_161 = { .pdu = set_up_call_response_161, .pdu_len = sizeof(set_up_call_response_161), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x00, /* Only if not busy on another call */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TERMINAL_BUSY, .additional_len = 1, /* ME currently busy on call */ .additional = (unsigned char[1]) { 0x02 }, }, }, }; static const unsigned char set_up_call_response_171a[] = { 0x81, 0x03, 0x01, 0x10, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x21, 0x00, }; static const struct terminal_response_test set_up_call_response_data_171a = { .pdu = set_up_call_response_171a, .pdu_len = sizeof(set_up_call_response_171a), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x02, /* Put all other calls on hold */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NETWORK_UNAVAILABLE, .additional_len = 1, /* No specific cause given */ .additional = (unsigned char[1]) { 0x00 }, }, }, }; static const unsigned char set_up_call_response_171b[] = { 0x81, 0x03, 0x01, 0x10, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x21, 0x9d, }; static const struct terminal_response_test set_up_call_response_data_171b = { .pdu = set_up_call_response_171b, .pdu_len = sizeof(set_up_call_response_171b), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x02, /* Put all other calls on hold */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NETWORK_UNAVAILABLE, .additional_len = 1, /* Facility rejected */ .additional = (unsigned char[1]) { 0x9d }, }, }, }; static const unsigned char set_up_call_response_1101[] = { 0x81, 0x03, 0x01, 0x10, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test set_up_call_response_data_1101 = { .pdu = set_up_call_response_1101, .pdu_len = sizeof(set_up_call_response_1101), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x01, /* Only if not busy, with redial */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char set_up_call_response_1111b[] = { 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x30, }; static const struct terminal_response_test set_up_call_response_data_1111b = { .pdu = set_up_call_response_1111b, .pdu_len = sizeof(set_up_call_response_1111b), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x00, /* Only if not busy on another call */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NOT_CAPABLE, }, }, }; static const unsigned char set_up_call_response_1121[] = { 0x81, 0x03, 0x01, 0x10, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x21, 0x91, }; static const struct terminal_response_test set_up_call_response_data_1121 = { .pdu = set_up_call_response_1121, .pdu_len = sizeof(set_up_call_response_1121), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x01, /* Only if not busy, with redial */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NETWORK_UNAVAILABLE, .additional_len = 1, /* User busy */ .additional = (unsigned char[1]) { 0x91 }, }, }, }; static const unsigned char set_up_call_response_311b[] = { 0x81, 0x03, 0x01, 0x10, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, }; static const struct terminal_response_test set_up_call_response_data_311b = { .pdu = set_up_call_response_311b, .pdu_len = sizeof(set_up_call_response_311b), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_CALL, .qualifier = 0x00, /* Only if not busy on another call */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, }, }; static const unsigned char polling_off_response_112[] = { 0x81, 0x03, 0x01, 0x04, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test polling_off_response_data_112 = { .pdu = polling_off_response_112, .pdu_len = sizeof(polling_off_response_112), .response = { .number = 1, .type = STK_COMMAND_TYPE_POLLING_OFF, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char provide_local_info_response_111a[] = { 0x81, 0x03, 0x01, 0x26, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x93, 0x07, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x01, }; static const struct terminal_response_test provide_local_info_response_data_111a = { .pdu = provide_local_info_response_111a, .pdu_len = sizeof(provide_local_info_response_111a), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x00, /* Location information (MCC MNC LAC CI) */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .location = { .mcc = "001", .mnc = "01", .lac_tac = 0x0001, .has_ci = TRUE, .ci = 0x0001, }}, }}, }, }; static const unsigned char provide_local_info_response_111b[] = { 0x81, 0x03, 0x01, 0x26, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x93, 0x07, 0x00, 0x11, 0x10, 0x00, 0x01, 0x00, 0x01, }; static const struct terminal_response_test provide_local_info_response_data_111b = { .pdu = provide_local_info_response_111b, .pdu_len = sizeof(provide_local_info_response_111b), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x00, /* Location information (MCC MNC LAC CI) */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .location = { .mcc = "001", .mnc = "011", .lac_tac = 0x0001, .has_ci = TRUE, .ci = 0x0001, }}, }}, }, }; static const unsigned char provide_local_info_response_121[] = { 0x81, 0x03, 0x01, 0x26, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x94, 0x08, 0x1a, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, 0x54, /* Typo in TS 102 384? */ }; static const struct terminal_response_test provide_local_info_response_data_121 = { .pdu = provide_local_info_response_121, .pdu_len = sizeof(provide_local_info_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x01, /* IMEI of the Terminal */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .imei = "123456789012345", } }}, }, }; static const unsigned char provide_local_info_response_131[] = { 0x81, 0x03, 0x01, 0x26, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x96, 0x10, 0x34, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x0d, 0x8c, 0x63, 0x58, 0xe2, 0x39, 0x8f, 0x63, 0xf9, 0x06, 0x45, 0x91, 0xa4, 0x90, }; static const short bcch_channels_131[] = { }; static const struct terminal_response_test provide_local_info_response_data_131 = { .pdu = provide_local_info_response_131, .pdu_len = sizeof(provide_local_info_response_131), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x02, /* Network Measurement Results */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .nmr = { .nmr = { /* RXLEV-FULL-SERVING-CELL=52, no BA, * no DTX */ .array = (unsigned char[16]) { 0x34, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, .len = 16, }, .bcch_ch_list = { .channels = { 561, 565, 568, 569, 573, 575, 577, 581, 582, 585, }, .num = 10, .has_list = TRUE, }, }}, }}, }, }; static const unsigned char provide_local_info_response_141[] = { 0x81, 0x03, 0x01, 0x26, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa6, 0x07, 0x20, 0x50, 0x70, 0x41, 0x80, 0x71, 0xff, }; static const struct terminal_response_test provide_local_info_response_data_141 = { .pdu = provide_local_info_response_141, .pdu_len = sizeof(provide_local_info_response_141), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x03, /* Date Time and Time Zone */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .datetime = { .year = 2, /* 2002 - 1900 - 100 */ .month = 5, .day = 7, .hour = 14, .minute = 8, .second = 17, .timezone = 0xff, /* No information */ }}, }}, }, }; static const unsigned char provide_local_info_response_151[] = { 0x81, 0x03, 0x01, 0x26, 0x04, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xad, 0x02, 0x65, 0x6e, }; static const struct terminal_response_test provide_local_info_response_data_151 = { .pdu = provide_local_info_response_151, .pdu_len = sizeof(provide_local_info_response_151), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x04, /* Language setting */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .language = "en", } }}, }, }; static const unsigned char provide_local_info_response_161[] = { 0x81, 0x03, 0x01, 0x26, 0x05, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xae, 0x02, 0x00, 0x00, }; static const struct terminal_response_test provide_local_info_response_data_161 = { .pdu = provide_local_info_response_161, .pdu_len = sizeof(provide_local_info_response_161), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x05, /* Timing Advance */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .tadv = { .status = STK_ME_STATUS_IDLE, .advance = 0, }}, }}, }, }; static const unsigned char provide_local_info_response_171[] = { 0x81, 0x03, 0x01, 0x26, 0x06, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x3f, 0x01, 0x03, }; static const struct terminal_response_test provide_local_info_response_data_171 = { .pdu = provide_local_info_response_171, .pdu_len = sizeof(provide_local_info_response_171), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x06, /* Access technology */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .access_technology = STK_ACCESS_TECHNOLOGY_UTRAN, } }}, }, }; static const unsigned char provide_local_info_response_181[] = { 0x81, 0x03, 0x01, 0x26, 0x07, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xc6, 0x04, 0x01, 0x02, 0x03, 0x04, }; static const struct terminal_response_test provide_local_info_response_data_181 = { .pdu = provide_local_info_response_181, .pdu_len = sizeof(provide_local_info_response_181), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x07, /* ESN of the terminal */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .esn = 0x01020304, } }}, }, }; static const unsigned char provide_local_info_response_191[] = { 0x81, 0x03, 0x01, 0x26, 0x08, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xe2, 0x09, 0x13, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, 0x54, 0xf6, }; static const struct terminal_response_test provide_local_info_response_data_191 = { .pdu = provide_local_info_response_191, .pdu_len = sizeof(provide_local_info_response_191), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x08, /* IMEISV of the terminal */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .imeisv = "1234567890123456", } }}, }, }; static const unsigned char provide_local_info_response_1111[] = { 0x81, 0x03, 0x01, 0x26, 0x0a, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xe3, 0x01, 0x04, }; static const struct terminal_response_test provide_local_info_response_data_1111 = { .pdu = provide_local_info_response_1111, .pdu_len = sizeof(provide_local_info_response_1111), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x0a, /* Charge state of the battery */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .battery_charge = STK_BATTERY_FULL, } }}, }, }; static const unsigned char provide_local_info_response_1121[] = { 0x81, 0x03, 0x01, 0x26, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x96, 0x02, 0x80, 0x00, /* Intra-frequency UTRAN Measurement report in ASN.1 goes here */ /* "The remaining bytes shall not be verified" */ }; static const struct terminal_response_test provide_local_info_response_data_1121 = { .pdu = provide_local_info_response_1121, .pdu_len = sizeof(provide_local_info_response_1121), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x02, /* Network Measurement Results */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .nmr = { .nmr = { .array = (unsigned char[2]) { 0x80, 0x00 }, .len = 2, }, }}, }}, }, }; static const unsigned char provide_local_info_response_1131[] = { 0x81, 0x03, 0x01, 0x26, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x96, 0x02, 0x80, 0x11, /* Inter-frequency UTRAN Measurement report in ASN.1 goes here */ /* "The remaining bytes shall not be verified" */ }; static const struct terminal_response_test provide_local_info_response_data_1131 = { .pdu = provide_local_info_response_1131, .pdu_len = sizeof(provide_local_info_response_1131), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x02, /* Network Measurement Results */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .nmr = { .nmr = { .array = (unsigned char[2]) { 0x80, 0x11}, .len = 2, }, }}, }}, }, }; static const unsigned char provide_local_info_response_1141[] = { 0x81, 0x03, 0x01, 0x26, 0x06, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x3f, 0x01, 0x08, }; static const struct terminal_response_test provide_local_info_response_data_1141 = { .pdu = provide_local_info_response_1141, .pdu_len = sizeof(provide_local_info_response_1141), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x06, /* Access technology */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .access_technology = STK_ACCESS_TECHNOLOGY_EUTRAN, } }}, }, }; static const unsigned char provide_local_info_response_1151[] = { 0x81, 0x03, 0x01, 0x26, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x96, 0x02, 0x80, 0x00, /* Intra-frequency E-UTRAN Measurement report in ASN.1 goes here */ /* "The remaining bytes shall not be verified" */ }; static const struct terminal_response_test provide_local_info_response_data_1151 = { .pdu = provide_local_info_response_1151, .pdu_len = sizeof(provide_local_info_response_1151), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x02, /* Network Measurement Results */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .nmr = { .nmr = { .array = (unsigned char[2]) { 0x80, 0x00}, .len = 2, }, }}, }}, }, }; static const unsigned char provide_local_info_response_1161[] = { 0x81, 0x03, 0x01, 0x26, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x96, 0x02, 0x80, 0x11, /* Inter-frequency E-UTRAN Measurement report in ASN.1 goes here */ /* "The remaining bytes shall not be verified" */ }; static const struct terminal_response_test provide_local_info_response_data_1161 = { .pdu = provide_local_info_response_1161, .pdu_len = sizeof(provide_local_info_response_1161), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x02, /* Network Measurement Results */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .nmr = { .nmr = { .array = (unsigned char[2]) { 0x80, 0x11}, .len = 2, }, }}, }}, }, }; static const unsigned char provide_local_info_response_1171[] = { 0x81, 0x03, 0x01, 0x26, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x93, 0x09, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1f, /* Typo in TS 102 223? Byte 18 changed to 01 here */ }; static const struct terminal_response_test provide_local_info_response_data_1171 = { .pdu = provide_local_info_response_1171, .pdu_len = sizeof(provide_local_info_response_1171), .response = { .number = 1, .type = STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO, .qualifier = 0x00, /* Location information (MCC MNC LAC CI) */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .provide_local_info = { { .location = { .mcc = "001", .mnc = "01", .lac_tac = 0x0001, .has_eutran_ci = TRUE, .eutran_ci = 0x0000001, }}, }}, }, }; static const unsigned char set_up_event_list_response_111[] = { 0x81, 0x03, 0x01, 0x05, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test set_up_event_list_response_data_111 = { .pdu = set_up_event_list_response_111, .pdu_len = sizeof(set_up_event_list_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_EVENT_LIST, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char timer_mgmt_response_111[] = { 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x01, }; static const struct terminal_response_test timer_mgmt_response_data_111 = { .pdu = timer_mgmt_response_111, .pdu_len = sizeof(timer_mgmt_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x00, /* Start the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 1, }}, }, }; static const unsigned char timer_mgmt_response_112[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x01, 0xa5, 0x03, 0x00, 0x30, 0x95, }; static const struct terminal_response_test timer_mgmt_response_data_112 = { .pdu = timer_mgmt_response_112, .pdu_len = sizeof(timer_mgmt_response_112), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 1, .value = { .minute = 3, .second = 59, .has_value = TRUE, }, }}, }, }; static const unsigned char timer_mgmt_response_114[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x01, 0xa5, 0x03, 0x00, 0x00, 0x95, }; static const struct terminal_response_test timer_mgmt_response_data_114 = { .pdu = timer_mgmt_response_114, .pdu_len = sizeof(timer_mgmt_response_114), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 1, .value = { .second = 59, .has_value = TRUE, }, }}, }, }; static const unsigned char timer_mgmt_response_121[] = { 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x02, }; static const struct terminal_response_test timer_mgmt_response_data_121 = { .pdu = timer_mgmt_response_121, .pdu_len = sizeof(timer_mgmt_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x00, /* Start the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 2, }}, }, }; static const unsigned char timer_mgmt_response_122[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x02, 0xa5, 0x03, 0x32, 0x85, 0x85, }; static const struct terminal_response_test timer_mgmt_response_data_122 = { .pdu = timer_mgmt_response_122, .pdu_len = sizeof(timer_mgmt_response_122), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 2, .value = { .hour = 23, .minute = 58, .second = 58, .has_value = TRUE, }, }}, }, }; static const unsigned char timer_mgmt_response_124[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x02, 0xa5, 0x03, 0x00, 0x00, 0x95, }; static const struct terminal_response_test timer_mgmt_response_data_124 = { .pdu = timer_mgmt_response_124, .pdu_len = sizeof(timer_mgmt_response_124), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 2, .value = { .second = 59, .has_value = TRUE, }, }}, }, }; static const unsigned char timer_mgmt_response_131[] = { 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x08, }; static const struct terminal_response_test timer_mgmt_response_data_131 = { .pdu = timer_mgmt_response_131, .pdu_len = sizeof(timer_mgmt_response_131), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x00, /* Start the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 8, }}, }, }; static const unsigned char timer_mgmt_response_132[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x08, 0xa5, 0x03, 0x00, 0x81, 0x95, }; static const struct terminal_response_test timer_mgmt_response_data_132 = { .pdu = timer_mgmt_response_132, .pdu_len = sizeof(timer_mgmt_response_132), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 8, .value = { .minute = 18, .second = 59, .has_value = TRUE, }, }}, }, }; static const unsigned char timer_mgmt_response_134[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x08, 0xa5, 0x03, 0x00, 0x95, 0x92, }; static const struct terminal_response_test timer_mgmt_response_data_134 = { .pdu = timer_mgmt_response_134, .pdu_len = sizeof(timer_mgmt_response_134), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 8, .value = { .minute = 59, .second = 29, .has_value = TRUE, }, }}, }, }; static const unsigned char timer_mgmt_response_141a[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x01, }; static const struct terminal_response_test timer_mgmt_response_data_141a = { .pdu = timer_mgmt_response_141a, .pdu_len = sizeof(timer_mgmt_response_141a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 1, }}, }, }; static const unsigned char timer_mgmt_response_141b[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, }; static const struct terminal_response_test timer_mgmt_response_data_141b = { .pdu = timer_mgmt_response_141b, .pdu_len = sizeof(timer_mgmt_response_141b), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, }, }; static const unsigned char timer_mgmt_response_142a[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x02, }; static const struct terminal_response_test timer_mgmt_response_data_142a = { .pdu = timer_mgmt_response_142a, .pdu_len = sizeof(timer_mgmt_response_142a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 2, }}, }, }; static const unsigned char timer_mgmt_response_143a[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x03, }; static const struct terminal_response_test timer_mgmt_response_data_143a = { .pdu = timer_mgmt_response_143a, .pdu_len = sizeof(timer_mgmt_response_143a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 3, }}, }, }; static const unsigned char timer_mgmt_response_144a[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x04, }; static const struct terminal_response_test timer_mgmt_response_data_144a = { .pdu = timer_mgmt_response_144a, .pdu_len = sizeof(timer_mgmt_response_144a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 4, }}, }, }; static const unsigned char timer_mgmt_response_145a[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x05, }; static const struct terminal_response_test timer_mgmt_response_data_145a = { .pdu = timer_mgmt_response_145a, .pdu_len = sizeof(timer_mgmt_response_145a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 5, }}, }, }; static const unsigned char timer_mgmt_response_146a[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x06, }; static const struct terminal_response_test timer_mgmt_response_data_146a = { .pdu = timer_mgmt_response_146a, .pdu_len = sizeof(timer_mgmt_response_146a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 6, }}, }, }; static const unsigned char timer_mgmt_response_147a[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x07, }; static const struct terminal_response_test timer_mgmt_response_data_147a = { .pdu = timer_mgmt_response_147a, .pdu_len = sizeof(timer_mgmt_response_147a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 7, }}, }, }; static const unsigned char timer_mgmt_response_148a[] = { 0x81, 0x03, 0x01, 0x27, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x08, }; static const struct terminal_response_test timer_mgmt_response_data_148a = { .pdu = timer_mgmt_response_148a, .pdu_len = sizeof(timer_mgmt_response_148a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x02, /* Get the current value of the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 8, }}, }, }; static const unsigned char timer_mgmt_response_151a[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x01, }; static const struct terminal_response_test timer_mgmt_response_data_151a = { .pdu = timer_mgmt_response_151a, .pdu_len = sizeof(timer_mgmt_response_151a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 1, }}, }, }; static const unsigned char timer_mgmt_response_151b[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, }; static const struct terminal_response_test timer_mgmt_response_data_151b = { .pdu = timer_mgmt_response_151b, .pdu_len = sizeof(timer_mgmt_response_151b), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, }, }; static const unsigned char timer_mgmt_response_152a[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x02, }; static const struct terminal_response_test timer_mgmt_response_data_152a = { .pdu = timer_mgmt_response_152a, .pdu_len = sizeof(timer_mgmt_response_152a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 2, }}, }, }; static const unsigned char timer_mgmt_response_153a[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x03, }; static const struct terminal_response_test timer_mgmt_response_data_153a = { .pdu = timer_mgmt_response_153a, .pdu_len = sizeof(timer_mgmt_response_153a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 3, }}, }, }; static const unsigned char timer_mgmt_response_154a[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x04, }; static const struct terminal_response_test timer_mgmt_response_data_154a = { .pdu = timer_mgmt_response_154a, .pdu_len = sizeof(timer_mgmt_response_154a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 4, }}, }, }; static const unsigned char timer_mgmt_response_155a[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x05, }; static const struct terminal_response_test timer_mgmt_response_data_155a = { .pdu = timer_mgmt_response_155a, .pdu_len = sizeof(timer_mgmt_response_155a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 5, }}, }, }; static const unsigned char timer_mgmt_response_156a[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x06, }; static const struct terminal_response_test timer_mgmt_response_data_156a = { .pdu = timer_mgmt_response_156a, .pdu_len = sizeof(timer_mgmt_response_156a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 6, }}, }, }; static const unsigned char timer_mgmt_response_157a[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x07, }; static const struct terminal_response_test timer_mgmt_response_data_157a = { .pdu = timer_mgmt_response_157a, .pdu_len = sizeof(timer_mgmt_response_157a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 7, }}, }, }; static const unsigned char timer_mgmt_response_158a[] = { 0x81, 0x03, 0x01, 0x27, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x24, 0xa4, 0x01, 0x08, }; static const struct terminal_response_test timer_mgmt_response_data_158a = { .pdu = timer_mgmt_response_158a, .pdu_len = sizeof(timer_mgmt_response_158a), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x01, /* Deactivate the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TIMER_CONFLICT, }, { .timer_mgmt = { .id = 8, }}, }, }; static const unsigned char timer_mgmt_response_163[] = { 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x03, }; static const struct terminal_response_test timer_mgmt_response_data_163 = { .pdu = timer_mgmt_response_163, .pdu_len = sizeof(timer_mgmt_response_163), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x00, /* Start the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 3, }}, }, }; static const unsigned char timer_mgmt_response_164[] = { 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x04, }; static const struct terminal_response_test timer_mgmt_response_data_164 = { .pdu = timer_mgmt_response_164, .pdu_len = sizeof(timer_mgmt_response_164), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x00, /* Start the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 4, }}, }, }; static const unsigned char timer_mgmt_response_165[] = { 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x05, }; static const struct terminal_response_test timer_mgmt_response_data_165 = { .pdu = timer_mgmt_response_165, .pdu_len = sizeof(timer_mgmt_response_165), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x00, /* Start the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 5, }}, }, }; static const unsigned char timer_mgmt_response_166[] = { 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x06, }; static const struct terminal_response_test timer_mgmt_response_data_166 = { .pdu = timer_mgmt_response_166, .pdu_len = sizeof(timer_mgmt_response_166), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x00, /* Start the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 6, }}, }, }; static const unsigned char timer_mgmt_response_167[] = { 0x81, 0x03, 0x01, 0x27, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xa4, 0x01, 0x07, }; static const struct terminal_response_test timer_mgmt_response_data_167 = { .pdu = timer_mgmt_response_167, .pdu_len = sizeof(timer_mgmt_response_167), .response = { .number = 1, .type = STK_COMMAND_TYPE_TIMER_MANAGEMENT, .qualifier = 0x00, /* Start the Timer */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .timer_mgmt = { .id = 7, }}, }, }; static const unsigned char set_up_idle_mode_text_response_111[] = { 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test set_up_idle_mode_text_response_data_111 = { .pdu = set_up_idle_mode_text_response_111, .pdu_len = sizeof(set_up_idle_mode_text_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char set_up_idle_mode_text_response_211b[] = { 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, }; static const struct terminal_response_test set_up_idle_mode_text_response_data_211b = { .pdu = set_up_idle_mode_text_response_211b, .pdu_len = sizeof(set_up_idle_mode_text_response_211b), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, }, }; static const unsigned char set_up_idle_mode_text_response_241[] = { 0x81, 0x03, 0x01, 0x28, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x32, }; static const struct terminal_response_test set_up_idle_mode_text_response_data_241 = { .pdu = set_up_idle_mode_text_response_241, .pdu_len = sizeof(set_up_idle_mode_text_response_241), .response = { .number = 1, .type = STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD, }, }, }; static const unsigned char run_at_command_response_111[] = { 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xA9, 0x05, 0x2b, 0x43, 0x47, 0x4d, 0x49, }; static const struct terminal_response_test run_at_command_response_data_111 = { .pdu = run_at_command_response_111, .pdu_len = sizeof(run_at_command_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_RUN_AT_COMMAND, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .run_at_command = { .at_response = "+CGMI", }}, }, }; static const unsigned char run_at_command_response_211b[] = { 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, 0xA9, 0x05, 0x2b, 0x43, 0x47, 0x4d, 0x49, }; static const struct terminal_response_test run_at_command_response_data_211b = { .pdu = run_at_command_response_211b, .pdu_len = sizeof(run_at_command_response_211b), .response = { .number = 1, .type = STK_COMMAND_TYPE_RUN_AT_COMMAND, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, { .run_at_command = { .at_response = "+CGMI", }}, }, }; static const unsigned char run_at_command_response_251[] = { 0x81, 0x03, 0x01, 0x34, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x32, }; static const struct terminal_response_test run_at_command_response_data_251 = { .pdu = run_at_command_response_251, .pdu_len = sizeof(run_at_command_response_251), .response = { .number = 1, .type = STK_COMMAND_TYPE_RUN_AT_COMMAND, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD, }, }, }; static const unsigned char send_dtmf_response_111[] = { 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test send_dtmf_response_data_111 = { .pdu = send_dtmf_response_111, .pdu_len = sizeof(send_dtmf_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_SEND_DTMF, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char send_dtmf_response_141[] = { 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x20, 0x07, }; static const struct terminal_response_test send_dtmf_response_data_141 = { .pdu = send_dtmf_response_141, .pdu_len = sizeof(send_dtmf_response_141), .response = { .number = 1, .type = STK_COMMAND_TYPE_SEND_DTMF, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_TERMINAL_BUSY, .additional_len = 1, /* Not in speech call */ .additional = (unsigned char[1]) { 0x07 }, }, }, }; static const unsigned char send_dtmf_response_211b[] = { 0x81, 0x03, 0x01, 0x14, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, }; static const struct terminal_response_test send_dtmf_response_data_211b = { .pdu = send_dtmf_response_211b, .pdu_len = sizeof(send_dtmf_response_211b), .response = { .number = 1, .type = STK_COMMAND_TYPE_SEND_DTMF, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, }, }; static const unsigned char language_notification_response_111[] = { 0x81, 0x03, 0x01, 0x35, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test language_notification_response_data_111 = { .pdu = language_notification_response_111, .pdu_len = sizeof(language_notification_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION, .qualifier = 0x01, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char language_notification_response_121[] = { 0x81, 0x03, 0x01, 0x35, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test language_notification_response_data_121 = { .pdu = language_notification_response_121, .pdu_len = sizeof(language_notification_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char launch_browser_response_111[] = { 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test launch_browser_response_data_111 = { .pdu = launch_browser_response_111, .pdu_len = sizeof(launch_browser_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_LAUNCH_BROWSER, .qualifier = 0x00, /* Launch browser, if not running */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char launch_browser_response_211[] = { 0x81, 0x03, 0x01, 0x15, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test launch_browser_response_data_211 = { .pdu = launch_browser_response_211, .pdu_len = sizeof(launch_browser_response_211), .response = { .number = 1, .type = STK_COMMAND_TYPE_LAUNCH_BROWSER, .qualifier = 0x02, /* Use the existing browser */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char launch_browser_response_221[] = { 0x81, 0x03, 0x01, 0x15, 0x03, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, }; static const struct terminal_response_test launch_browser_response_data_221 = { .pdu = launch_browser_response_221, .pdu_len = sizeof(launch_browser_response_221), .response = { .number = 1, .type = STK_COMMAND_TYPE_LAUNCH_BROWSER, .qualifier = 0x03, /* Re-start browser session */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, }, }; static const unsigned char launch_browser_response_231[] = { 0x81, 0x03, 0x01, 0x15, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x26, 0x02, }; static const struct terminal_response_test launch_browser_response_data_231 = { .pdu = launch_browser_response_231, .pdu_len = sizeof(launch_browser_response_231), .response = { .number = 1, .type = STK_COMMAND_TYPE_LAUNCH_BROWSER, .qualifier = 0x00, /* Launch browser, if not running */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_BROWSER_TEMPORARY, .additional_len = 1, /* Browser unavailable */ .additional = (unsigned char[1]) { 0x02 }, }, }, }; static const unsigned char launch_browser_response_411b[] = { 0x81, 0x03, 0x01, 0x15, 0x02, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x04, }; static const struct terminal_response_test launch_browser_response_data_411b = { .pdu = launch_browser_response_411b, .pdu_len = sizeof(launch_browser_response_411b), .response = { .number = 1, .type = STK_COMMAND_TYPE_LAUNCH_BROWSER, .qualifier = 0x02, /* Use the existing browser */ .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_NO_ICON, }, }, }; static const unsigned char open_channel_response_211[] = { 0x81, 0x03, 0x01, 0x40, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0x38, 0x02, 0x81, 0x00, 0x35, 0x07, 0x02, 0x03, 0x04, 0x03, 0x04, 0x1F, 0x02, 0x39, 0x02, 0x05, 0x78, }; static const struct terminal_response_test open_channel_response_data_211 = { .pdu = open_channel_response_211, .pdu_len = sizeof(open_channel_response_211), .response = { .number = 1, .type = STK_COMMAND_TYPE_OPEN_CHANNEL, .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .open_channel = { .channel = { .id = 1, .status = STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED, }, .bearer_desc = { .type = STK_BEARER_TYPE_GPRS_UTRAN, .gprs = { .precedence = 3, .delay = 4, .reliability = 3, .peak = 4, .mean = 31, .pdp_type = 2, }, }, .buf_size = 1400, } }, }, }; static const unsigned char open_channel_response_271[] = { 0x81, 0x03, 0x01, 0x40, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x22, 0x35, 0x07, 0x02, 0x03, 0x04, 0x03, 0x04, 0x1F, 0x02, 0x39, 0x02, 0x05, 0x78, }; static const struct terminal_response_test open_channel_response_data_271 = { .pdu = open_channel_response_271, .pdu_len = sizeof(open_channel_response_271), .response = { .number = 1, .type = STK_COMMAND_TYPE_OPEN_CHANNEL, .qualifier = STK_OPEN_CHANNEL_FLAG_IMMEDIATE, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_USER_REJECT, }, { .open_channel = { .bearer_desc = { .type = STK_BEARER_TYPE_GPRS_UTRAN, .gprs = { .precedence = 3, .delay = 4, .reliability = 3, .peak = 4, .mean = 31, .pdp_type = 2, }, }, .buf_size = 1400, } }, }, }; static const unsigned char close_channel_response_121[] = { 0x81, 0x03, 0x01, 0x41, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x3A, 0x03, }; static const struct terminal_response_test close_channel_response_data_121 = { .pdu = close_channel_response_121, .pdu_len = sizeof(close_channel_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_CLOSE_CHANNEL, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_BIP_ERROR, .additional_len = 1, /* Channel identifier not valid */ .additional = (unsigned char[1]) { 0x03 }, }, }, }; static const unsigned char close_channel_response_131[] = { 0x81, 0x03, 0x01, 0x41, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x3A, 0x02, }; static const struct terminal_response_test close_channel_response_data_131 = { .pdu = close_channel_response_131, .pdu_len = sizeof(close_channel_response_131), .response = { .number = 1, .type = STK_COMMAND_TYPE_CLOSE_CHANNEL, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_BIP_ERROR, .additional_len = 1, /* Channel already closed */ .additional = (unsigned char[1]) { 0x02 }, }, }, }; static const unsigned char receive_data_response_111[] = { 0x81, 0x03, 0x01, 0x42, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xB6, 0x81, 0xC8, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xB7, 0x01, 0xFF, }; static const struct terminal_response_test receive_data_response_data_111 = { .pdu = receive_data_response_111, .pdu_len = sizeof(receive_data_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_RECEIVE_DATA, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .receive_data = { .rx_data = { .array = (unsigned char[200]) { 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, }, .len = 200, }, .rx_remaining = 0xFF, } }, }, }; static const unsigned char send_data_response_111[] = { 0x81, 0x03, 0x01, 0x43, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xB7, 0x01, 0xFF, }; static const struct terminal_response_test send_data_response_data_111 = { .pdu = send_data_response_111, .pdu_len = sizeof(send_data_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_SEND_DATA, .qualifier = STK_SEND_DATA_IMMEDIATELY, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .send_data = { /* More than 255 bytes of space available */ .tx_avail = 0xFF, } }, }, }; static const unsigned char send_data_response_121[] = { 0x81, 0x03, 0x01, 0x43, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xB7, 0x01, 0xFF, }; static const struct terminal_response_test send_data_response_data_121 = { .pdu = send_data_response_121, .pdu_len = sizeof(send_data_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_SEND_DATA, .qualifier = STK_SEND_DATA_STORE_DATA, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .send_data = { /* More than 255 bytes of space available */ .tx_avail = 0xFF, } }, }, }; static const unsigned char send_data_response_151[] = { 0x81, 0x03, 0x01, 0x43, 0x01, 0x82, 0x02, 0x82, 0x81, 0x83, 0x02, 0x3A, 0x03, }; static const struct terminal_response_test send_data_response_data_151 = { .pdu = send_data_response_151, .pdu_len = sizeof(send_data_response_151), .response = { .number = 1, .type = STK_COMMAND_TYPE_SEND_DATA, .qualifier = STK_SEND_DATA_IMMEDIATELY, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_BIP_ERROR, .additional_len = 1, /* Channel identifier not valid */ .additional = (unsigned char[1]) { 0x03 }, }, }, }; static const unsigned char get_channel_status_response_111[] = { 0x81, 0x03, 0x01, 0x44, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xB8, 0x02, 0x00, 0x00, }; static const struct terminal_response_test get_channel_status_response_data_111 = { .pdu = get_channel_status_response_111, .pdu_len = sizeof(get_channel_status_response_111), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_CHANNEL_STATUS, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .channel_status = { /* * No Channel available, link not established or * PDP context not activated */ .channel = { .id = 0, .status = STK_CHANNEL_PACKET_DATA_SERVICE_NOT_ACTIVATED, } } }, }, }; static const unsigned char get_channel_status_response_121[] = { 0x81, 0x03, 0x01, 0x44, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xB8, 0x02, 0x81, 0x00, }; static const struct terminal_response_test get_channel_status_response_data_121 = { .pdu = get_channel_status_response_121, .pdu_len = sizeof(get_channel_status_response_121), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_CHANNEL_STATUS, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .channel_status = { /* Channel 1 open, link established or PDP context activated */ .channel = { .id = 1, .status = STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED, }, } }, }, }; static const unsigned char get_channel_status_response_131[] = { 0x81, 0x03, 0x01, 0x44, 0x00, 0x82, 0x02, 0x82, 0x81, 0x83, 0x01, 0x00, 0xB8, 0x02, 0x01, 0x05, }; static const struct terminal_response_test get_channel_status_response_data_131 = { .pdu = get_channel_status_response_131, .pdu_len = sizeof(get_channel_status_response_131), .response = { .number = 1, .type = STK_COMMAND_TYPE_GET_CHANNEL_STATUS, .qualifier = 0x00, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, .result = { .type = STK_RESULT_TYPE_SUCCESS, }, { .channel_status = { /* Channel 1, link dropped */ .channel = { .id = 1, .status = STK_CHANNEL_LINK_DROPPED, }, } }, }, }; struct envelope_test { const unsigned char *pdu; unsigned int pdu_len; struct stk_envelope envelope; }; static void test_envelope_encoding(gconstpointer data) { const struct envelope_test *test = data; const unsigned char *pdu; unsigned int pdu_len; pdu = stk_pdu_from_envelope(&test->envelope, &pdu_len); if (test->pdu) g_assert(pdu); else g_assert(pdu == NULL); g_assert(pdu_len == test->pdu_len); g_assert(memcmp(pdu, test->pdu, pdu_len) == 0); } static const unsigned char sms_pp_data_download_161[] = { 0xd1, 0x2d, 0x82, 0x02, 0x83, 0x81, 0x06, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xf8, 0x8b, 0x1c, 0x04, 0x04, 0x91, 0x21, 0x43, 0x7f, 0x16, 0x89, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, }; static const struct envelope_test sms_pp_data_download_data_161 = { .pdu = sms_pp_data_download_161, .pdu_len = sizeof(sms_pp_data_download_161), .envelope = { .type = STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .sms_pp_download = { .address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "112233445566778", }, .message = { .oaddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "1234", }, .pid = SMS_PID_TYPE_USIM_DOWNLOAD, .dcs = 0x16, /* Uncompressed, Class 2, 8-bit */ .scts = { .year = 98, .month = 1, .day = 1, .has_timezone = TRUE, .timezone = 0, }, .udl = 13, .ud = "Short Message", }, }}, }, }; static const unsigned char sms_pp_data_download_162[] = { 0xd1, 0x2d, 0x82, 0x02, 0x83, 0x81, 0x06, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xf8, 0x8b, 0x1c, 0x04, 0x04, 0x91, 0x21, 0x43, 0x7f, 0xf6, 0x89, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, }; static const struct envelope_test sms_pp_data_download_data_162 = { .pdu = sms_pp_data_download_162, .pdu_len = sizeof(sms_pp_data_download_162), .envelope = { .type = STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .sms_pp_download = { .address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "112233445566778", }, .message = { .oaddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "1234", }, .pid = SMS_PID_TYPE_USIM_DOWNLOAD, .dcs = 0xf6, /* Data, Class 2, 8-bit */ .scts = { .year = 98, .month = 1, .day = 1, .has_timezone = TRUE, .timezone = 0, }, .udl = 13, .ud = "Short Message", }, }}, }, }; static const unsigned char sms_pp_data_download_182[] = { 0xd1, 0x3e, 0x82, 0x02, 0x83, 0x81, 0x06, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xf8, 0x8b, 0x2d, 0x44, 0x04, 0x91, 0x21, 0x43, 0x7f, 0xf6, 0x89, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x02, 0x70, 0x00, 0x00, 0x19, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, }; static const struct envelope_test sms_pp_data_download_data_182 = { .pdu = sms_pp_data_download_182, .pdu_len = sizeof(sms_pp_data_download_182), .envelope = { .type = STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .sms_pp_download = { .address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "112233445566778", }, .message = { .udhi = TRUE, .oaddr = { .number_type = SMS_NUMBER_TYPE_INTERNATIONAL, .numbering_plan = SMS_NUMBERING_PLAN_ISDN, .address = "1234", }, .pid = SMS_PID_TYPE_USIM_DOWNLOAD, .dcs = 0xf6, /* Data, Class 2, 8-bit */ .scts = { .year = 98, .month = 1, .day = 1, .has_timezone = TRUE, .timezone = 0, }, .udl = 30, .ud = { 0x02, 0x70, 0x00, 0x00, 0x19, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, }, }, }}, }, }; static const unsigned char cbs_pp_data_download_11[] = { 0xd2, 0x5e, 0x82, 0x02, 0x83, 0x81, 0x8c, 0x58, 0xc0, 0x11, 0x10, 0x01, 0x01, 0x11, 0xc3, 0x32, 0x9b, 0x0d, 0x12, 0xca, 0xdf, 0x61, 0xf2, 0x38, 0x3c, 0xa7, 0x83, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, }; static const struct envelope_test cbs_pp_data_download_data_11 = { .pdu = cbs_pp_data_download_11, .pdu_len = sizeof(cbs_pp_data_download_11), .envelope = { .type = STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .cbs_pp_download = { .page = { .gs = CBS_GEO_SCOPE_CELL_NORMAL, .message_code = 1, .update_number = 1, .message_identifier = 0x1001, .dcs = CBS_LANGUAGE_ENGLISH, /* GSM 7-bit */ .max_pages = 1, .page = 1, .ud = { /* 7-bit "Cell Broadcast " repeated */ 0xc3, 0x32, 0x9b, 0x0d, 0x12, 0xca, 0xdf, 0x61, 0xf2, 0x38, 0x3c, 0xa7, 0x83, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x81, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, }, }, }}, }, }; static const unsigned char cbs_pp_data_download_17[] = { 0xd2, 0x5e, 0x82, 0x02, 0x83, 0x81, 0x8c, 0x58, 0xc0, 0x11, 0x10, 0x01, 0x96, 0x11, 0x02, 0x70, 0x00, 0x00, 0x4d, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, }; static const struct envelope_test cbs_pp_data_download_data_17 = { .pdu = cbs_pp_data_download_17, .pdu_len = sizeof(cbs_pp_data_download_17), .envelope = { .type = STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .cbs_pp_download = { .page = { .gs = CBS_GEO_SCOPE_CELL_NORMAL, .message_code = 1, .update_number = 1, .message_identifier = 0x1001, .dcs = SMS_CLASS_2 | (SMS_CHARSET_8BIT << 2) | (9 << 4), /* UDHI present */ .max_pages = 1, .page = 1, .ud = { /* Secured User Header */ 0x02, 0x70, 0x00, 0x00, 0x4d, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, }, }, }}, }, }; static const unsigned char menu_selection_111[] = { 0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01, 0x02, }; static const struct envelope_test menu_selection_data_111 = { .pdu = menu_selection_111, .pdu_len = sizeof(menu_selection_111), .envelope = { .type = STK_ENVELOPE_TYPE_MENU_SELECTION, .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .menu_selection = { .item_id = 0x2, }}, }, }; static const unsigned char menu_selection_112[] = { 0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01, 0x12, }; static const struct envelope_test menu_selection_data_112 = { .pdu = menu_selection_112, .pdu_len = sizeof(menu_selection_112), .envelope = { .type = STK_ENVELOPE_TYPE_MENU_SELECTION, .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .menu_selection = { .item_id = 0x12, }}, }, }; static const unsigned char menu_selection_121[] = { 0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01, 0x3d, }; static const struct envelope_test menu_selection_data_121 = { .pdu = menu_selection_121, .pdu_len = sizeof(menu_selection_121), .envelope = { .type = STK_ENVELOPE_TYPE_MENU_SELECTION, .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .menu_selection = { .item_id = 0x3d, }}, }, }; static const unsigned char menu_selection_122[] = { 0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01, 0xfb, }; static const struct envelope_test menu_selection_data_122 = { .pdu = menu_selection_122, .pdu_len = sizeof(menu_selection_122), .envelope = { .type = STK_ENVELOPE_TYPE_MENU_SELECTION, .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .menu_selection = { .item_id = 0xfb, }}, }, }; static const unsigned char menu_selection_123[] = { 0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01, 0x01, }; static const struct envelope_test menu_selection_data_123 = { .pdu = menu_selection_123, .pdu_len = sizeof(menu_selection_123), .envelope = { .type = STK_ENVELOPE_TYPE_MENU_SELECTION, .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .menu_selection = { .item_id = 0x1, }}, }, }; static const unsigned char menu_selection_211[] = { 0xd3, 0x09, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01, 0x02, 0x15, 0x00, }; static const struct envelope_test menu_selection_data_211 = { .pdu = menu_selection_211, .pdu_len = sizeof(menu_selection_211), .envelope = { .type = STK_ENVELOPE_TYPE_MENU_SELECTION, .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .menu_selection = { .item_id = 0x2, .help_request = TRUE, }}, }, }; static const unsigned char menu_selection_612[] = { 0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01, 0x05, }; static const struct envelope_test menu_selection_data_612 = { .pdu = menu_selection_612, .pdu_len = sizeof(menu_selection_612), .envelope = { .type = STK_ENVELOPE_TYPE_MENU_SELECTION, .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .menu_selection = { .item_id = 0x5, }}, }, }; static const unsigned char menu_selection_641[] = { 0xd3, 0x07, 0x82, 0x02, 0x01, 0x81, 0x90, 0x01, 0x08, }; static const struct envelope_test menu_selection_data_641 = { .pdu = menu_selection_641, .pdu_len = sizeof(menu_selection_641), .envelope = { .type = STK_ENVELOPE_TYPE_MENU_SELECTION, .src = STK_DEVICE_IDENTITY_TYPE_KEYPAD, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .menu_selection = { .item_id = 0x8, }}, }, }; static const unsigned char call_control_111a[] = { 0xd4, 0x25, 0x82, 0x02, 0x82, 0x81, 0x86, 0x0b, 0x91, 0x10, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, 0x54, 0x76, 0x98, 0x07, 0x07, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, 0x13, 0x09, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, }; static const struct envelope_test call_control_data_111a = { .pdu = call_control_111a, .pdu_len = sizeof(call_control_111a), .envelope = { .type = STK_ENVELOPE_TYPE_CALL_CONTROL, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .call_control = { .type = STK_CC_TYPE_CALL_SETUP, { .address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "01234567890123456789", }}, .ccp1 = { .ccp = { 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, }, .len = 6, }, .location = { .mcc = "001", .mnc = "01", .lac_tac = 0x0001, .has_ci = TRUE, .ci = 0x0001, .has_ext_ci = TRUE, .ext_ci = 0x0001, }, }}, }, }; static const unsigned char call_control_111b[] = { 0xd4, 0x23, 0x82, 0x02, 0x82, 0x81, 0x86, 0x0b, 0x91, 0x10, 0x32, 0x54, 0x76, 0x98, 0x10, 0x32, 0x54, 0x76, 0x98, 0x07, 0x07, 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, 0x13, 0x07, 0x00, 0x11, 0x10, 0x00, 0x01, 0x00, 0x01, }; static const struct envelope_test call_control_data_111b = { .pdu = call_control_111b, .pdu_len = sizeof(call_control_111b), .envelope = { .type = STK_ENVELOPE_TYPE_CALL_CONTROL, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .call_control = { .type = STK_CC_TYPE_CALL_SETUP, { .address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "01234567890123456789", }}, .ccp1 = { .ccp = { 0x60, 0x04, 0x02, 0x00, 0x05, 0x81, }, .len = 6, }, .location = { .mcc = "001", .mnc = "011", .lac_tac = 0x0001, .has_ci = TRUE, .ci = 0x0001, }, }}, }, }; static const unsigned char call_control_131a[] = { 0xd4, 0x18, 0x82, 0x02, 0x82, 0x81, 0x86, 0x07, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x13, 0x09, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, /* * Byte 3 changed to 0x82 and byte 7 changed to 0x86 (Comprehension * Required should be set according to TS 102 223 7.3.1.6) */ }; static const struct envelope_test call_control_data_131a = { .pdu = call_control_131a, .pdu_len = sizeof(call_control_131a), .envelope = { .type = STK_ENVELOPE_TYPE_CALL_CONTROL, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .call_control = { .type = STK_CC_TYPE_CALL_SETUP, { .address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "012340123456", }}, .location = { .mcc = "001", .mnc = "01", .lac_tac = 0x0001, .has_ci = TRUE, .ci = 0x0001, .has_ext_ci = TRUE, .ext_ci = 0x0001, }, }}, }, }; static const unsigned char call_control_131b[] = { 0xd4, 0x16, 0x82, 0x02, 0x82, 0x81, 0x86, 0x07, 0x91, 0x10, 0x32, 0x04, 0x21, 0x43, 0x65, 0x13, 0x07, 0x00, 0x11, 0x10, 0x00, 0x01, 0x00, 0x01, /* * Byte 3 changed to 0x82 and byte 7 changed to 0x86 (Comprehension * Required should be set according to TS 102 223 7.3.1.6) */ }; static const struct envelope_test call_control_data_131b = { .pdu = call_control_131b, .pdu_len = sizeof(call_control_131b), .envelope = { .type = STK_ENVELOPE_TYPE_CALL_CONTROL, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .call_control = { .type = STK_CC_TYPE_CALL_SETUP, { .address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "012340123456", }}, .location = { .mcc = "001", .mnc = "011", .lac_tac = 0x0001, .has_ci = TRUE, .ci = 0x0001, }, }}, }, }; static const unsigned char mo_short_message_control_111a[] = { 0xd5, 0x22, 0x02, 0x02, 0x82, 0x81, 0x06, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xf8, 0x06, 0x06, 0x91, 0x10, 0x32, 0x54, 0x76, 0xf8, 0x13, 0x09, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, }; static const struct envelope_test mo_short_message_control_data_111a = { .pdu = mo_short_message_control_111a, .pdu_len = sizeof(mo_short_message_control_111a), .envelope = { .type = STK_ENVELOPE_TYPE_MO_SMS_CONTROL, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .sms_mo_control = { .sc_address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "112233445566778", }, .dest_address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "012345678", }, .location = { .mcc = "001", .mnc = "01", .lac_tac = 0x0001, .has_ci = TRUE, .ci = 0x0001, .has_ext_ci = TRUE, .ext_ci = 0x0001, }, }}, }, }; static const unsigned char mo_short_message_control_111b[] = { 0xd5, 0x20, 0x02, 0x02, 0x82, 0x81, 0x06, 0x09, 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xf8, 0x06, 0x06, 0x91, 0x10, 0x32, 0x54, 0x76, 0xf8, 0x13, 0x07, 0x00, 0x11, 0x10, 0x00, 0x01, 0x00, 0x01, }; static const struct envelope_test mo_short_message_control_data_111b = { .pdu = mo_short_message_control_111b, .pdu_len = sizeof(mo_short_message_control_111b), .envelope = { .type = STK_ENVELOPE_TYPE_MO_SMS_CONTROL, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .sms_mo_control = { .sc_address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "112233445566778", }, .dest_address = { .ton_npi = 0x91, /* Intl, ISDN */ .number = "012345678", }, .location = { .mcc = "001", .mnc = "011", .lac_tac = 0x0001, .has_ci = TRUE, .ci = 0x0001, }, }}, }, }; static const unsigned char event_download_mt_call_111[] = { 0xd6, 0x0a, 0x99, 0x01, 0x00, 0x82, 0x02, 0x83, 0x81, 0x9c, 0x01, 0x00, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c (Comprehension * Required should be set according to TS 102 223 7.5.1.2) */ }; static const struct envelope_test event_download_mt_call_data_111 = { .pdu = event_download_mt_call_111, .pdu_len = sizeof(event_download_mt_call_111), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_MT_CALL, { .mt_call = { .transaction_id = 0, }}, }}, }, }; static const unsigned char event_download_mt_call_112[] = { 0xd6, 0x0f, 0x99, 0x01, 0x00, 0x82, 0x02, 0x83, 0x81, 0x9c, 0x01, 0x00, 0x06, 0x03, 0x81, 0x89, 0x67, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to * 0x06 (Comprehension Required should be set according to * TS 102 223 7.5.1.2) */ }; static const struct envelope_test event_download_mt_call_data_112 = { .pdu = event_download_mt_call_112, .pdu_len = sizeof(event_download_mt_call_112), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_MT_CALL, { .mt_call = { .transaction_id = 0, .caller_address = { .ton_npi = 0x81, /* Unknown, ISDN */ .number = "9876", }, }}, }}, }, }; static const unsigned char event_download_call_connected_111[] = { 0xd6, 0x0a, 0x99, 0x01, 0x01, 0x82, 0x02, 0x82, 0x81, 0x9c, 0x01, 0x80, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c (Comprehension * Required should be set according to TS 102 223 7.5.2.2) */ }; static const struct envelope_test event_download_call_connected_data_111 = { .pdu = event_download_call_connected_111, .pdu_len = sizeof(event_download_call_connected_111), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CALL_CONNECTED, { .call_connected = { .transaction_id = 0x80, }}, }}, }, }; static const unsigned char event_download_call_connected_112[] = { 0xd6, 0x0a, 0x99, 0x01, 0x01, 0x82, 0x02, 0x83, 0x81, 0x9c, 0x01, 0x80, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c (Comprehension * Required should be set according to TS 102 223 7.5.2.2) */ }; static const struct envelope_test event_download_call_connected_data_112 = { .pdu = event_download_call_connected_112, .pdu_len = sizeof(event_download_call_connected_112), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CALL_CONNECTED, { .call_connected = { .transaction_id = 0x80, }}, }}, }, }; static const unsigned char event_download_call_disconnected_111[] = { 0xd6, 0x0a, 0x99, 0x01, 0x02, 0x82, 0x02, 0x83, 0x81, 0x9c, 0x01, 0x80, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c (Comprehension * Required should be set according to TS 102 223 7.5.3.2) */ }; static const struct envelope_test event_download_call_disconnected_data_111 = { .pdu = event_download_call_disconnected_111, .pdu_len = sizeof(event_download_call_disconnected_111), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CALL_DISCONNECTED, { .call_disconnected = { .transaction_ids = { .len = 1, .list = { 0x80 }, }, }}, }}, }, }; static const unsigned char event_download_call_disconnected_112a[] = { 0xd6, 0x0a, 0x99, 0x01, 0x02, 0x82, 0x02, 0x82, 0x81, 0x9c, 0x01, 0x80, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c (Comprehension * Required should be set according to TS 102 223 7.5.3.2) */ }; static const struct envelope_test event_download_call_disconnected_data_112a = { .pdu = event_download_call_disconnected_112a, .pdu_len = sizeof(event_download_call_disconnected_112a), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CALL_DISCONNECTED, { .call_disconnected = { .transaction_ids = { .len = 1, .list = { 0x80 }, }, }}, }}, }, }; static const unsigned char event_download_call_disconnected_112b[] = { 0xd6, 0x0e, 0x99, 0x01, 0x02, 0x82, 0x02, 0x82, 0x81, 0x9c, 0x01, 0x80, 0x1a, 0x02, 0x60, 0x90, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to * 1a (Comprehension Required should be set according to TS * 102 223 7.5.3.2) */ }; static const struct envelope_test event_download_call_disconnected_data_112b = { .pdu = event_download_call_disconnected_112b, .pdu_len = sizeof(event_download_call_disconnected_112b), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CALL_DISCONNECTED, { .call_disconnected = { .transaction_ids = { .len = 1, .list = { 0x80 }, }, .cause = { .has_cause = TRUE, .len = 2, /* Normal call clearing */ .cause = { 0x60, 0x90 }, }, }}, }}, }, }; static const unsigned char event_download_call_disconnected_112c[] = { 0xd6, 0x0e, 0x99, 0x01, 0x02, 0x82, 0x02, 0x82, 0x81, 0x9c, 0x01, 0x80, 0x1a, 0x02, 0xe0, 0x90, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to * 1a (Comprehension Required should be set according to TS * 102 223 7.5.3.2) */ }; static const struct envelope_test event_download_call_disconnected_data_112c = { .pdu = event_download_call_disconnected_112c, .pdu_len = sizeof(event_download_call_disconnected_112c), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CALL_DISCONNECTED, { .call_disconnected = { .transaction_ids = { .len = 1, .list = { 0x80 }, }, .cause = { .has_cause = TRUE, .len = 2, /* Normal call clearing */ .cause = { 0xe0, 0x90 }, }, }}, }}, }, }; static const unsigned char event_download_call_disconnected_113a[] = { 0xd6, 0x0e, 0x99, 0x01, 0x02, 0x82, 0x02, 0x83, 0x81, 0x9c, 0x01, 0x00, 0x1a, 0x02, 0x60, 0x90, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to * 1a (Comprehension Required should be set according to TS * 102 223 7.5.3.2) */ }; static const struct envelope_test event_download_call_disconnected_data_113a = { .pdu = event_download_call_disconnected_113a, .pdu_len = sizeof(event_download_call_disconnected_113a), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CALL_DISCONNECTED, { .call_disconnected = { .transaction_ids = { .len = 1, .list = { 0 }, }, .cause = { .has_cause = TRUE, .len = 2, /* Normal call clearing */ .cause = { 0x60, 0x90 }, }, }}, }}, }, }; static const unsigned char event_download_call_disconnected_113b[] = { 0xd6, 0x0e, 0x99, 0x01, 0x02, 0x82, 0x02, 0x83, 0x81, 0x9c, 0x01, 0x00, 0x1a, 0x02, 0xe0, 0x90, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to * 1a (Comprehension Required should be set according to TS * 102 223 7.5.3.2) */ }; static const struct envelope_test event_download_call_disconnected_data_113b = { .pdu = event_download_call_disconnected_113b, .pdu_len = sizeof(event_download_call_disconnected_113b), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CALL_DISCONNECTED, { .call_disconnected = { .transaction_ids = { .len = 1, .list = { 0 }, }, .cause = { .has_cause = TRUE, .len = 2, /* Normal call clearing */ .cause = { 0xe0, 0x90 }, }, }}, }}, }, }; static const unsigned char event_download_call_disconnected_114a[] = { 0xd6, 0x0c, 0x99, 0x01, 0x02, 0x82, 0x02, 0x82, 0x81, 0x9c, 0x01, 0x80, 0x1a, 0x00, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to * 1a (Comprehension Required should be set according to TS * 102 223 7.5.3.2) */ }; static const struct envelope_test event_download_call_disconnected_data_114a = { .pdu = event_download_call_disconnected_114a, .pdu_len = sizeof(event_download_call_disconnected_114a), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CALL_DISCONNECTED, { .call_disconnected = { .transaction_ids = { .len = 1, .list = { 0x80 }, }, .cause = { .has_cause = TRUE, /* Radio link failure */ }, }}, }}, }, }; static const unsigned char event_download_call_disconnected_114b[] = { 0xd6, 0x0c, 0x99, 0x01, 0x02, 0x82, 0x02, 0x82, 0x81, 0x9c, 0x01, 0x00, 0x1a, 0x00, /* * Byte 3 changed to 0x99 and byte 10 to 0x9c and byte 13 to * 1a (Comprehension Required should be set according to TS * 102 223 7.5.3.2) */ }; static const struct envelope_test event_download_call_disconnected_data_114b = { .pdu = event_download_call_disconnected_114b, .pdu_len = sizeof(event_download_call_disconnected_114b), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CALL_DISCONNECTED, { .call_disconnected = { .transaction_ids = { .len = 1, .list = { 0 }, }, .cause = { .has_cause = TRUE, /* Radio link failure */ }, }}, }}, }, }; static const unsigned char event_download_location_status_111[] = { 0xd6, 0x0a, 0x99, 0x01, 0x03, 0x82, 0x02, 0x82, 0x81, 0x9b, 0x01, 0x02, /* * Byte 3 changed to 0x99 and byte 10 to 0x9b (Comprehension * Required should be set according to TS 102 223 7.5.4.2) */ }; static const struct envelope_test event_download_location_status_data_111 = { .pdu = event_download_location_status_111, .pdu_len = sizeof(event_download_location_status_111), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_LOCATION_STATUS, { .location_status = { .state = STK_NO_SERVICE, }}, }}, }, }; static const unsigned char event_download_location_status_112a[] = { 0xd6, 0x15, 0x99, 0x01, 0x03, 0x82, 0x02, 0x82, 0x81, 0x9b, 0x01, 0x00, 0x13, 0x09, 0x00, 0xf1, 0x10, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, /* * Byte 3 changed to 0x99 and byte 10 to 0x9b (Comprehension * Required should be set according to TS 102 223 7.5.4.2) */ }; static const struct envelope_test event_download_location_status_data_112a = { .pdu = event_download_location_status_112a, .pdu_len = sizeof(event_download_location_status_112a), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_LOCATION_STATUS, { .location_status = { .state = STK_NORMAL_SERVICE, .info = { .mcc = "001", .mnc = "01", .lac_tac = 0x0002, .has_ci = TRUE, .ci = 0x0002, .has_ext_ci = TRUE, .ext_ci = 0x0001, }, }}, }}, }, }; static const unsigned char event_download_location_status_112b[] = { 0xd6, 0x13, 0x99, 0x01, 0x03, 0x82, 0x02, 0x82, 0x81, 0x9b, 0x01, 0x00, 0x13, 0x07, 0x00, 0x11, 0x10, 0x00, 0x02, 0x00, 0x02, /* * Byte 3 changed to 0x99 and byte 10 to 0x9b (Comprehension * Required should be set according to TS 102 223 7.5.4.2) */ }; static const struct envelope_test event_download_location_status_data_112b = { .pdu = event_download_location_status_112b, .pdu_len = sizeof(event_download_location_status_112b), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_LOCATION_STATUS, { .location_status = { .state = STK_NORMAL_SERVICE, .info = { .mcc = "001", .mnc = "011", .lac_tac = 0x0002, .has_ci = TRUE, .ci = 0x0002, }, }}, }}, }, }; static const unsigned char event_download_location_status_122[] = { 0xd6, 0x15, 0x99, 0x01, 0x03, 0x82, 0x02, 0x82, 0x81, 0x9b, 0x01, 0x00, 0x13, 0x09, 0x00, 0xf1, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2f, /* * Byte 3 changed to 0x99 and byte 10 to 0x9b (Comprehension * Required should be set according to TS 102 223 7.5.4.2) */ }; static const struct envelope_test event_download_location_status_data_122 = { .pdu = event_download_location_status_122, .pdu_len = sizeof(event_download_location_status_122), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_LOCATION_STATUS, { .location_status = { .state = STK_NORMAL_SERVICE, .info = { .mcc = "001", .mnc = "01", .lac_tac = 0x0002, .has_eutran_ci = TRUE, .eutran_ci = 0x0000002, }, }}, }}, }, }; /* * This is from 27.22.7.5. The ENVELOPE given in 27.22.4.16.1.1 seems to * have invalid length value (2nd byte), but in turn the Comprehension * Required bit is set correctly.. */ static const unsigned char event_download_user_activity_111[] = { 0xd6, 0x07, 0x99, 0x01, 0x04, 0x82, 0x02, 0x82, 0x81, /* * Byte 3 changed to 0x99 (Comprehension Required should be * set according to TS 102 223 7.5.5.2) */ }; static const struct envelope_test event_download_user_activity_data_111 = { .pdu = event_download_user_activity_111, .pdu_len = sizeof(event_download_user_activity_111), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_USER_ACTIVITY, }}, }, }; static const unsigned char event_download_idle_screen_available_111[] = { 0xd6, 0x07, 0x99, 0x01, 0x05, 0x82, 0x02, 0x02, 0x81, /* * Byte 3 changed to 0x99 (Comprehension Required should be * set according to TS 102 223 7.5.6.2) */ }; static const struct envelope_test event_download_idle_screen_available_data_111 = { .pdu = event_download_idle_screen_available_111, .pdu_len = sizeof(event_download_idle_screen_available_111), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_DISPLAY, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE, }}, }, }; static const unsigned char event_download_card_reader_status_111a[] = { 0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0xa0, 0x01, 0x79, }; static const struct envelope_test event_download_card_reader_status_data_111a = { .pdu = event_download_card_reader_status_111a, .pdu_len = sizeof(event_download_card_reader_status_111a), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CARD_READER_STATUS, { .card_reader_status = { .id = 1, .removable = TRUE, .present = TRUE, .id1_size = TRUE, .card_present = TRUE, .card_powered = FALSE, }}, }}, }, }; static const unsigned char event_download_card_reader_status_111b[] = { 0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0xa0, 0x01, 0x59, }; static const struct envelope_test event_download_card_reader_status_data_111b = { .pdu = event_download_card_reader_status_111b, .pdu_len = sizeof(event_download_card_reader_status_111b), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CARD_READER_STATUS, { .card_reader_status = { .id = 1, .removable = TRUE, .present = TRUE, .id1_size = FALSE, .card_present = TRUE, .card_powered = FALSE, }}, }}, }, }; static const unsigned char event_download_card_reader_status_111c[] = { 0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0xa0, 0x01, 0x71, }; static const struct envelope_test event_download_card_reader_status_data_111c = { .pdu = event_download_card_reader_status_111c, .pdu_len = sizeof(event_download_card_reader_status_111c), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CARD_READER_STATUS, { .card_reader_status = { .id = 1, .removable = FALSE, .present = TRUE, .id1_size = TRUE, .card_present = TRUE, .card_powered = FALSE, }}, }}, }, }; static const unsigned char event_download_card_reader_status_111d[] = { 0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0xa0, 0x01, 0x51, }; static const struct envelope_test event_download_card_reader_status_data_111d = { .pdu = event_download_card_reader_status_111d, .pdu_len = sizeof(event_download_card_reader_status_111d), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CARD_READER_STATUS, { .card_reader_status = { .id = 1, .removable = FALSE, .present = TRUE, .id1_size = FALSE, .card_present = TRUE, .card_powered = FALSE, }}, }}, }, }; static const unsigned char event_download_card_reader_status_112a[] = { 0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0xa0, 0x01, 0x39, }; static const struct envelope_test event_download_card_reader_status_data_112a = { .pdu = event_download_card_reader_status_112a, .pdu_len = sizeof(event_download_card_reader_status_112a), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CARD_READER_STATUS, { .card_reader_status = { .id = 1, .removable = TRUE, .present = TRUE, .id1_size = TRUE, .card_present = FALSE, }}, }}, }, }; static const unsigned char event_download_card_reader_status_112b[] = { 0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0xa0, 0x01, 0x19, }; static const struct envelope_test event_download_card_reader_status_data_112b = { .pdu = event_download_card_reader_status_112b, .pdu_len = sizeof(event_download_card_reader_status_112b), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CARD_READER_STATUS, { .card_reader_status = { .id = 1, .removable = TRUE, .present = TRUE, .id1_size = FALSE, .card_present = FALSE, }}, }}, }, }; static const unsigned char event_download_card_reader_status_112c[] = { 0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0xa0, 0x01, 0x31, }; static const struct envelope_test event_download_card_reader_status_data_112c = { .pdu = event_download_card_reader_status_112c, .pdu_len = sizeof(event_download_card_reader_status_112c), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CARD_READER_STATUS, { .card_reader_status = { .id = 1, .removable = FALSE, .present = TRUE, .id1_size = TRUE, .card_present = FALSE, }}, }}, }, }; static const unsigned char event_download_card_reader_status_112d[] = { 0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0xa0, 0x01, 0x11, }; static const struct envelope_test event_download_card_reader_status_data_112d = { .pdu = event_download_card_reader_status_112d, .pdu_len = sizeof(event_download_card_reader_status_112d), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CARD_READER_STATUS, { .card_reader_status = { .id = 1, .removable = FALSE, .present = TRUE, .id1_size = FALSE, .card_present = FALSE, }}, }}, }, }; static const unsigned char event_download_card_reader_status_212a[] = { 0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0xa0, 0x01, 0x29, }; static const struct envelope_test event_download_card_reader_status_data_212a = { .pdu = event_download_card_reader_status_212a, .pdu_len = sizeof(event_download_card_reader_status_212a), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CARD_READER_STATUS, { .card_reader_status = { .id = 1, .removable = TRUE, .present = FALSE, .id1_size = TRUE, .card_present = FALSE, }}, }}, }, }; static const unsigned char event_download_card_reader_status_212b[] = { 0xd6, 0x0a, 0x99, 0x01, 0x06, 0x82, 0x02, 0x82, 0x81, 0xa0, 0x01, 0x09, }; static const struct envelope_test event_download_card_reader_status_data_212b = { .pdu = event_download_card_reader_status_212b, .pdu_len = sizeof(event_download_card_reader_status_212b), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CARD_READER_STATUS, { .card_reader_status = { .id = 1, .removable = TRUE, .present = FALSE, .id1_size = FALSE, .card_present = FALSE, }}, }}, }, }; static const unsigned char event_download_language_selection_111[] = { 0xd6, 0x0b, 0x99, 0x01, 0x07, 0x82, 0x02, 0x82, 0x81, 0xad, 0x02, 0x64, 0x65, /* * Byte 3 changed to 0x99 and byte 10 to 0xad (Comprehension * Required should be set according to TS 102 223 7.5.8.2) */ }; static const struct envelope_test event_download_language_selection_data_111 = { .pdu = event_download_language_selection_111, .pdu_len = sizeof(event_download_language_selection_111), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_LANGUAGE_SELECTION, { .language_selection = "de" }, }}, }, }; static const unsigned char event_download_language_selection_122[] = { 0xd6, 0x0b, 0x99, 0x01, 0x07, 0x82, 0x02, 0x82, 0x81, 0xad, 0x02, 0x73, 0x65, /* Byte 5 changed to 0x07 (Event: Language Selection) */ /* Byte 8 changed to 0x82 (Source device: Terminal) */ /* Removed the (unexpected?) Transaction ID data object (0x2d) */ }; static const struct envelope_test event_download_language_selection_data_122 = { .pdu = event_download_language_selection_122, .pdu_len = sizeof(event_download_language_selection_122), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_LANGUAGE_SELECTION, { .language_selection = "se" }, }}, }, }; static const unsigned char event_download_browser_termination_111[] = { 0xd6, 0x0a, 0x99, 0x01, 0x08, 0x82, 0x02, 0x82, 0x81, 0xb4, 0x01, 0x00, }; static const struct envelope_test event_download_browser_termination_data_111 = { .pdu = event_download_browser_termination_111, .pdu_len = sizeof(event_download_browser_termination_111), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_BROWSER_TERMINATION, { .browser_termination = { .cause = STK_BROWSER_USER_TERMINATION, }}, }}, }, }; static const unsigned char event_download_data_available_111[] = { 0xd6, 0x0e, 0x99, 0x01, 0x09, 0x82, 0x02, 0x82, 0x81, 0xb8, 0x02, 0x81, 0x00, 0xb7, 0x01, 0xff, }; static const struct envelope_test event_download_data_available_data_111 = { .pdu = event_download_data_available_111, .pdu_len = sizeof(event_download_data_available_111), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_DATA_AVAILABLE, { .data_available = { /* Channel 1 open, Link established */ .channel = { .id = 1, .status = STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED, }, .channel_data_len = 255, } }, } }, }, }; static const unsigned char event_download_data_available_211[] = { 0xd6, 0x0e, 0x99, 0x01, 0x09, 0x82, 0x02, 0x82, 0x81, 0xb8, 0x02, 0x81, 0x00, 0xb7, 0x01, 0xff, }; static const struct envelope_test event_download_data_available_data_211 = { .pdu = event_download_data_available_211, .pdu_len = sizeof(event_download_data_available_211), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_DATA_AVAILABLE, { .data_available = { /* Channel 1 open, Link established */ .channel = { .id = 1, .status = STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED, }, .channel_data_len = 255, } }, } }, }, }; static const unsigned char event_download_channel_status_131[] = { 0xd6, 0x0b, 0x99, 0x01, 0x0a, 0x82, 0x02, 0x82, 0x81, 0xb8, 0x02, 0x01, 0x05, }; static const struct envelope_test event_download_channel_status_data_131 = { .pdu = event_download_channel_status_131, .pdu_len = sizeof(event_download_channel_status_131), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CHANNEL_STATUS, { .channel_status = { /* Channel 1, Link dropped */ .channel = { .id = 1, .status = STK_CHANNEL_LINK_DROPPED, }, } }, } }, }, }; static const unsigned char event_download_channel_status_211[] = { 0xd6, 0x0b, 0x99, 0x01, 0x0a, 0x82, 0x02, 0x82, 0x81, 0xb8, 0x02, 0x41, 0x00, /* * Byte 10 changed to 0xb8 (Comprehension Required should be * set according to TS 102 223 7.5.11.2) */ }; static const struct envelope_test event_download_channel_status_data_211 = { .pdu = event_download_channel_status_211, .pdu_len = sizeof(event_download_channel_status_211), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CHANNEL_STATUS, { .channel_status = { /* Channel 1, TCP in LISTEN state */ .channel = { .id = 1, .status = STK_CHANNEL_TCP_IN_LISTEN_STATE, }, } }, } }, }, }; static const unsigned char event_download_channel_status_221[] = { 0xd6, 0x0b, 0x99, 0x01, 0x0a, 0x82, 0x02, 0x82, 0x81, 0xb8, 0x02, 0x81, 0x00, /* * Byte 10 changed to 0xb8 (Comprehension Required should be * set according to TS 102 223 7.5.11.2) */ }; static const struct envelope_test event_download_channel_status_data_221 = { .pdu = event_download_channel_status_221, .pdu_len = sizeof(event_download_channel_status_221), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_CHANNEL_STATUS, { .channel_status = { /* Channel 1 open, TCP Link established */ .channel = { .id = 1, .status = STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED, }, } }, } }, }, }; static const unsigned char event_download_network_rejection_111[] = { 0xd6, 0x17, 0x99, 0x01, 0x12, 0x82, 0x02, 0x83, 0x81, 0x7d, 0x05, 0x00, 0xf1, 0x10, 0x00, 0x01, 0xbf, 0x01, 0x08, 0xf4, 0x01, 0x09, 0xf5, 0x01, 0x0b, /* * Byte 3 changed to 99, byte 17 changed to bf, byte 19 to f4 and * byte 22 to f5 (Comprehension Required should be set according * to TS 131 111 7.5.2.2) */ }; static const struct envelope_test event_download_network_rejection_data_111 = { .pdu = event_download_network_rejection_111, .pdu_len = sizeof(event_download_network_rejection_111), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_NETWORK_REJECTION, { .network_rejection = { .tai = { .mcc = "001", .mnc = "01", .tac = 0x0001, }, .access_tech = STK_ACCESS_TECHNOLOGY_EUTRAN, .update_attach = STK_UPDATE_ATTACH_EPS_ATTACH, .cause = STK_CAUSE_EMM_PLMN_NOT_ALLOWED, }}, }}, }, }; static const unsigned char event_download_network_rejection_121[] = { 0xd6, 0x17, 0x99, 0x01, 0x12, 0x82, 0x02, 0x83, 0x81, 0x7d, 0x05, 0x00, 0xf1, 0x10, 0x00, 0x01, 0xbf, 0x01, 0x08, 0xf4, 0x01, 0x0b, 0xf5, 0x01, 0x0c, /* * Byte 3 changed to 99, byte 17 changed to bf, byte 19 to f4 and * byte 22 to f5 (Comprehension Required should be set according * to TS 131 111 7.5.2.2) */ }; static const struct envelope_test event_download_network_rejection_data_121 = { .pdu = event_download_network_rejection_121, .pdu_len = sizeof(event_download_network_rejection_121), .envelope = { .type = STK_ENVELOPE_TYPE_EVENT_DOWNLOAD, .src = STK_DEVICE_IDENTITY_TYPE_NETWORK, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .event_download = { .type = STK_EVENT_TYPE_NETWORK_REJECTION, { .network_rejection = { .tai = { .mcc = "001", .mnc = "01", .tac = 0x0001, }, .access_tech = STK_ACCESS_TECHNOLOGY_EUTRAN, .update_attach = STK_UPDATE_ATTACH_TA_UPDATING, .cause = STK_CAUSE_EMM_TAC_NOT_ALLOWED, }}, }}, }, }; static const unsigned char timer_expiration_211[] = { 0xd7, 0x0c, 0x82, 0x02, 0x82, 0x81, 0xa4, 0x01, 0x01, 0xa5, 0x03, 0x00, 0x00, 0x01, }; static const struct envelope_test timer_expiration_data_211 = { .pdu = timer_expiration_211, .pdu_len = sizeof(timer_expiration_211), .envelope = { .type = STK_ENVELOPE_TYPE_TIMER_EXPIRATION, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .timer_expiration = { .id = 1, .value = { .second = 10, .has_value = TRUE, }, }}, }, }; static const unsigned char timer_expiration_221a[] = { 0xd7, 0x0c, 0x82, 0x02, 0x82, 0x81, 0xa4, 0x01, 0x01, 0xa5, 0x03, 0x00, 0x00, 0x03, }; static const struct envelope_test timer_expiration_data_221a = { .pdu = timer_expiration_221a, .pdu_len = sizeof(timer_expiration_221a), .envelope = { .type = STK_ENVELOPE_TYPE_TIMER_EXPIRATION, .src = STK_DEVICE_IDENTITY_TYPE_TERMINAL, .dst = STK_DEVICE_IDENTITY_TYPE_UICC, { .timer_expiration = { .id = 1, .value = { .second = 30, .has_value = TRUE, }, }}, }, }; struct html_attr_test { char *text; struct stk_text_attribute text_attr; char *html; }; static struct html_attr_test html_attr_data_1 = { .text = "Blue green green green", .text_attr = { .len = 8, .attributes = { 0x00, 0x00, 0x03, 0x94, 0x00, 0x04, 0x03, 0x96 }, }, .html = "" "Blue green green green", }; static struct html_attr_test html_attr_data_2 = { .text = "abc", .text_attr = { .len = 8, .attributes = { 0x00, 0x02, 0x03, 0x94, 0x01, 0x02, 0x03, 0x96 }, }, .html = "" "abc", }; static struct html_attr_test html_attr_data_3 = { .text = "1 < 2, 2 > 1, 1 & 0 == 0\nSpecial Chars are Fun\r\nTo Write", .text_attr = { .len = 4, .attributes = { 0x00, 0x00, 0x03, 0x00 }, }, .html = "1 < 2, 2 > 1, 1 & 0 == 0
Special Chars are Fun" "
To Write", }; static struct html_attr_test html_attr_data_4 = { .text = "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€" "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€" "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€" "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€" "€€€€€€€€€€€€€€€", .text_attr = { .len = 4, .attributes = { 0x00, 0x00, 0x03, 0x94 }, }, .html = "" "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€" "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€" "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€" "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€" "€€€€€€€€€€€€€€€", }; static void test_html_attr(gconstpointer data) { const struct html_attr_test *test = data; check_text_attr_html(&test->text_attr, test->text, test->html); } struct img_xpm_test { const unsigned char *img; unsigned int len; const unsigned char *clut; unsigned short clut_len; guint8 scheme; char *xpm; }; const unsigned char img1[] = { 0x05, 0x05, 0xFE, 0xEB, 0xBF, 0xFF, 0xFF, 0xFF }; const unsigned char img2[] = { 0x08, 0x08, 0x02, 0x03, 0x00, 0x16, 0xAA, 0xAA, 0x80, 0x02, 0x85, 0x42, 0x81, 0x42, 0x81, 0x42, 0x81, 0x52, 0x80, 0x02, 0xAA, 0xAA, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF }; const unsigned char img3[] = { 0x2E, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x77, 0xFE, 0x00, 0x00, 0x00, 0x01, 0xBF, 0xF8, 0x00, 0x00, 0x00, 0x06, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x1A, 0x03, 0x80, 0x00, 0x00, 0x00, 0x6B, 0xF6, 0xBC, 0x00, 0x00, 0x01, 0xAF, 0xD8, 0x38, 0x00, 0x00, 0x06, 0xBF, 0x60, 0x20, 0x00, 0x00, 0x1A, 0xFD, 0x80, 0x40, 0x00, 0x00, 0x6B, 0xF6, 0x00, 0x80, 0x00, 0x01, 0xA0, 0x1F, 0x02, 0x00, 0x00, 0x06, 0xFF, 0xE4, 0x04, 0x00, 0x00, 0x1B, 0xFF, 0x90, 0x10, 0x00, 0x00, 0x6D, 0xEE, 0x40, 0x40, 0x00, 0x01, 0xBF, 0xF9, 0x01, 0x00, 0x00, 0x6F, 0xFF, 0xE4, 0x04, 0x00, 0x00, 0x1B, 0xFF, 0x90, 0x10, 0x00, 0x00, 0x6F, 0xFE, 0x40, 0x40, 0x00, 0x01, 0xBF, 0xF9, 0x01, 0x00, 0x00, 0x06, 0xFF, 0xE6, 0x04, 0x00, 0x00, 0x1B, 0xFF, 0x88, 0x10, 0x00, 0x00, 0x6F, 0xFE, 0x20, 0x40, 0x00, 0x01, 0xBF, 0xF8, 0x66, 0x00, 0x00, 0x06, 0xFF, 0xE0, 0xF0, 0x00, 0x00, 0x1B, 0xFF, 0x80, 0x80, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x21, 0x08, 0x44, 0xEE, 0x00, 0x48, 0xC4, 0x31, 0x92, 0x20, 0x01, 0x25, 0x11, 0x45, 0x50, 0x80, 0x07, 0x14, 0x45, 0x15, 0x43, 0x80, 0x12, 0x71, 0x1C, 0x4D, 0x08, 0x00, 0x4A, 0x24, 0x89, 0x32, 0x20, 0x01, 0xC8, 0x9E, 0x24, 0x4E, 0xE0 }; const unsigned char img4[] = { 0x18, 0x10, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x8F, 0x3C, 0xF1, 0x89, 0x20, 0x81, 0x89, 0x20, 0x81, 0x89, 0x20, 0xF1, 0x89, 0x20, 0x11, 0x89, 0x20, 0x11, 0x89, 0x20, 0x11, 0x8F, 0x3C, 0xF1, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0xFF, 0xFF, 0xFF }; const unsigned char img5[] = { 0x08, 0x08, 0xFF, 0x03, 0xA5, 0x99, 0x99, 0xA5, 0xC3, 0xFF }; static struct img_xpm_test xpm_test_1 = { .img = img1, .len = sizeof(img1), .scheme = STK_IMG_SCHEME_BASIC, .xpm = "/* XPM */\n" "static char *xpm[] = {\n" "\"5 5 2 1\",\n" "\"0 c #000000\",\n" "\"1 c #FFFFFF\",\n" "\"11111\",\n" "\"11011\",\n" "\"10101\",\n" "\"11011\",\n" "\"11111\",\n" "};", }; static struct img_xpm_test xpm_test_2 = { .img = img2, .len = sizeof(img2), .clut = img2 + 0x16, .clut_len = 0x09, .scheme = STK_IMG_SCHEME_COLOR, .xpm = "/* XPM */\n" "static char *xpm[] = {\n" "\"8 8 3 1\",\n" "\"0 c #FF0000\",\n" "\"1 c #00FF00\",\n" "\"2 c #0000FF\",\n" "\"22222222\",\n" "\"20000002\",\n" "\"20111002\",\n" "\"20011002\",\n" "\"20011002\",\n" "\"20011102\",\n" "\"20000002\",\n" "\"22222222\",\n" "};", }; static struct img_xpm_test xpm_test_3 = { .img = img3, .len = sizeof(img3), .scheme = STK_IMG_SCHEME_BASIC, .xpm = "/* XPM */\n" "static char *xpm[] = {\n" "\"46 40 2 1\",\n" "\"0 c #000000\",\n" "\"1 c #FFFFFF\",\n" "\"0000000000000000000000000000000000000000000000\",\n" "\"0000000000000000011111111110000000000000000000\",\n" "\"0000000000000000111111111111000000000000000000\",\n" "\"0000000000000001110111111111100000000000000000\",\n" "\"0000000000000001101111111111100000000000000000\",\n" "\"0000000000000001101111111111100000000000000000\",\n" "\"0000000000000001101000000011100000000000000000\",\n" "\"0000000000000001101011111101101011110000000000\",\n" "\"0000000000000001101011111101100000111000000000\",\n" "\"0000000000000001101011111101100000001000000000\",\n" "\"0000000000000001101011111101100000000100000000\",\n" "\"0000000000000001101011111101100000000010000000\",\n" "\"0000000000000001101000000001111100000010000000\",\n" "\"0000000000000001101111111111100100000001000000\",\n" "\"0000000000000001101111111111100100000001000000\",\n" "\"0000000000000001101101111011100100000001000000\",\n" "\"0000000000000001101111111111100100000001000000\",\n" "\"0000000000011011111111111111100100000001000000\",\n" "\"0000000000000001101111111111100100000001000000\",\n" "\"0000000000000001101111111111100100000001000000\",\n" "\"0000000000000001101111111111100100000001000000\",\n" "\"0000000000000001101111111111100110000001000000\",\n" "\"0000000000000001101111111111100010000001000000\",\n" "\"0000000000000001101111111111100010000001000000\",\n" "\"0000000000000001101111111111100001100110000000\",\n" "\"0000000000000001101111111111100000111100000000\",\n" "\"0000000000000001101111111111100000001000000000\",\n" "\"0000000000000001111111111111100000000000000000\",\n" "\"0000000000000011000000000000110000000000000000\",\n" "\"0000000000000111111111111111111000000000000000\",\n" "\"0000000000000000000000000000000000000000000000\",\n" "\"0000000000000000000000000000000000000000000000\",\n" "\"0000000000000000000000000000000000000000000000\",\n" "\"0000011100001000010000100001000100111011100000\",\n" "\"0000010010001100010000110001100100100010000000\",\n" "\"0000010010010100010001010001010101000010000000\",\n" "\"0000011100010100010001010001010101000011100000\",\n" "\"0000010010011100010001110001001101000010000000\",\n" "\"0000010010100010010010001001001100100010000000\",\n" "\"0000011100100010011110001001000100111011100000\",\n" "};", }; static struct img_xpm_test xpm_test_4 = { .img = img4, .len = sizeof(img4), .scheme = STK_IMG_SCHEME_BASIC, .xpm = "/* XPM */\n" "static char *xpm[] = {\n" "\"24 16 2 1\",\n" "\"0 c #000000\",\n" "\"1 c #FFFFFF\",\n" "\"111111111111111111111111\",\n" "\"100000000000000000000001\",\n" "\"100000000000000000000001\",\n" "\"100000000000000000000001\",\n" "\"100011110011110011110001\",\n" "\"100010010010000010000001\",\n" "\"100010010010000010000001\",\n" "\"100010010010000011110001\",\n" "\"100010010010000000010001\",\n" "\"100010010010000000010001\",\n" "\"100010010010000000010001\",\n" "\"100011110011110011110001\",\n" "\"100000000000000000000001\",\n" "\"100000000000000000000001\",\n" "\"100000000000000000000001\",\n" "\"111111111111111111111111\",\n" "};", }; static struct img_xpm_test xpm_test_5 = { .img = img5, .len = sizeof(img5), .scheme = STK_IMG_SCHEME_BASIC, .xpm = "/* XPM */\n" "static char *xpm[] = {\n" "\"8 8 2 1\",\n" "\"0 c #000000\",\n" "\"1 c #FFFFFF\",\n" "\"11111111\",\n" "\"00000011\",\n" "\"10100101\",\n" "\"10011001\",\n" "\"10011001\",\n" "\"10100101\",\n" "\"11000011\",\n" "\"11111111\",\n" "};", }; static struct img_xpm_test xpm_test_6 = { .img = img2, .len = sizeof(img2), .clut = img2 + 0x16, .clut_len = 0x09, .scheme = STK_IMG_SCHEME_TRANSPARENCY, .xpm = "/* XPM */\n" "static char *xpm[] = {\n" "\"8 8 3 1\",\n" "\"0 c #FF0000\",\n" "\"1 c #00FF00\",\n" "\"2 c None\",\n" "\"22222222\",\n" "\"20000002\",\n" "\"20111002\",\n" "\"20011002\",\n" "\"20011002\",\n" "\"20011102\",\n" "\"20000002\",\n" "\"22222222\",\n" "};", }; static void test_img_to_xpm(gconstpointer data) { const struct img_xpm_test *test = data; char *xpm; xpm = stk_image_to_xpm(test->img, test->len, test->scheme, test->clut, test->clut_len); g_assert(memcmp(xpm, test->xpm, strlen(test->xpm)) == 0); g_free(xpm); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_data_func("/teststk/Display Text 1.1.1", &display_text_data_111, test_display_text); g_test_add_data_func("/teststk/Display Text 1.3.1", &display_text_data_131, test_display_text); g_test_add_data_func("/teststk/Display Text 1.4.1", &display_text_data_141, test_display_text); g_test_add_data_func("/teststk/Display Text 1.5.1", &display_text_data_151, test_display_text); g_test_add_data_func("/teststk/Display Text 1.6.1", &display_text_data_161, test_display_text); g_test_add_data_func("/teststk/Display Text 1.7.1", &display_text_data_171, test_display_text); g_test_add_data_func("/teststk/Display Text 5.1.1", &display_text_data_511, test_display_text); g_test_add_data_func("/teststk/Display Text 5.2.1", &display_text_data_521, test_display_text); g_test_add_data_func("/teststk/Display Text 5.3.1", &display_text_data_531, test_display_text); g_test_add_data_func("/teststk/Display Text 6.1.1", &display_text_data_611, test_display_text); g_test_add_data_func("/teststk/Display Text 7.1.1", &display_text_data_711, test_display_text); g_test_add_data_func("/teststk/Display Text 8.1.1", &display_text_data_811, test_display_text); g_test_add_data_func("/teststk/Display Text 8.2.1", &display_text_data_821, test_display_text); g_test_add_data_func("/teststk/Display Text 8.3.1", &display_text_data_831, test_display_text); g_test_add_data_func("/teststk/Display Text 8.4.1", &display_text_data_841, test_display_text); g_test_add_data_func("/teststk/Display Text 8.5.1", &display_text_data_851, test_display_text); g_test_add_data_func("/teststk/Display Text 8.6.1", &display_text_data_861, test_display_text); g_test_add_data_func("/teststk/Display Text 8.7.1", &display_text_data_871, test_display_text); g_test_add_data_func("/teststk/Display Text 8.8.1", &display_text_data_881, test_display_text); g_test_add_data_func("/teststk/Display Text 8.9.1", &display_text_data_891, test_display_text); g_test_add_data_func("/teststk/Display Text 9.1.1", &display_text_data_911, test_display_text); g_test_add_data_func("/teststk/Display Text 10.1.1", &display_text_data_1011, test_display_text); g_test_add_data_func("/teststk/Display Text response 1.1.1", &display_text_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Display Text response 1.2.1", &display_text_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Display Text response 1.3.1", &display_text_response_data_131, test_terminal_response_encoding); g_test_add_data_func("/teststk/Display Text response 1.5.1", &display_text_response_data_151, test_terminal_response_encoding); g_test_add_data_func("/teststk/Display Text response 1.7.1", &display_text_response_data_171, test_terminal_response_encoding); g_test_add_data_func("/teststk/Display Text response 1.8.1", &display_text_response_data_181, test_terminal_response_encoding); g_test_add_data_func("/teststk/Display Text response 1.9.1", &display_text_response_data_191, test_terminal_response_encoding); g_test_add_data_func("/teststk/Display Text response 2.1.1", &display_text_response_data_211, test_terminal_response_encoding); g_test_add_data_func("/teststk/Display Text response 5.1.1B", &display_text_response_data_511b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey 1.1.1", &get_inkey_data_111, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 1.2.1", &get_inkey_data_121, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 1.3.1", &get_inkey_data_131, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 1.4.1", &get_inkey_data_141, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 1.5.1", &get_inkey_data_151, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 1.6.1", &get_inkey_data_161, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 2.1.1", &get_inkey_data_211, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 3.1.1", &get_inkey_data_311, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 3.2.1", &get_inkey_data_321, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 4.1.1", &get_inkey_data_411, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 5.1.1", &get_inkey_data_511, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 5.1.2", &get_inkey_data_512, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 6.1.1", &get_inkey_data_611, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 6.2.1", &get_inkey_data_621, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 6.3.1", &get_inkey_data_631, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 6.4.1", &get_inkey_data_641, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 7.1.1", &get_inkey_data_711, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 7.1.2", &get_inkey_data_712, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 8.1.1", &get_inkey_data_811, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.1.1", &get_inkey_data_911, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.1.2", &get_inkey_data_912, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.2.1", &get_inkey_data_921, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.2.2", &get_inkey_data_922, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.3.1", &get_inkey_data_931, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.3.2", &get_inkey_data_932, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.4.1", &get_inkey_data_941, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.4.2", &get_inkey_data_942, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.4.3", &get_inkey_data_943, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.5.1", &get_inkey_data_951, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.5.2", &get_inkey_data_952, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.5.3", &get_inkey_data_953, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.6.1", &get_inkey_data_961, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.6.2", &get_inkey_data_962, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.6.3", &get_inkey_data_963, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.7.1", &get_inkey_data_971, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.7.2", &get_inkey_data_972, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.7.3", &get_inkey_data_973, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.8.1", &get_inkey_data_981, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.8.2", &get_inkey_data_982, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.8.3", &get_inkey_data_983, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.9.1", &get_inkey_data_991, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.9.2a", &get_inkey_data_992a, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.9.2b", &get_inkey_data_992b, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.9.3", &get_inkey_data_993, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.10.1", &get_inkey_data_9101, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 9.10.2", &get_inkey_data_9102, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 10.1.1", &get_inkey_data_1011, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 10.2.1", &get_inkey_data_1021, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 11.1.1", &get_inkey_data_1111, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 12.1.1", &get_inkey_data_1211, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 12.2.1", &get_inkey_data_1221, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey 13.1.1", &get_inkey_data_1311, test_get_inkey); g_test_add_data_func("/teststk/Get Inkey response 1.1.1", &get_inkey_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 1.2.1", &get_inkey_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 1.3.1", &get_inkey_response_data_131, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 1.4.1", &get_inkey_response_data_141, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 1.5.1", &get_inkey_response_data_151, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 1.6.1", &get_inkey_response_data_161, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 2.1.1", &get_inkey_response_data_211, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 4.1.1", &get_inkey_response_data_411, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 5.1.1", &get_inkey_response_data_511, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 5.1.2", &get_inkey_response_data_512, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 6.1.1B", &get_inkey_response_data_611b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 7.1.1", &get_inkey_response_data_711, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 7.1.2", &get_inkey_response_data_712, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 8.1.1", &get_inkey_response_data_811, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 9.1.2", &get_inkey_response_data_912, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 11.1.1", &get_inkey_response_data_1111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Inkey response 13.1.1", &get_inkey_response_data_1311, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input 1.1.1", &get_input_data_111, test_get_input); g_test_add_data_func("/teststk/Get Input 1.2.1", &get_input_data_121, test_get_input); g_test_add_data_func("/teststk/Get Input 1.3.1", &get_input_data_131, test_get_input); g_test_add_data_func("/teststk/Get Input 1.4.1", &get_input_data_141, test_get_input); g_test_add_data_func("/teststk/Get Input 1.5.1", &get_input_data_151, test_get_input); g_test_add_data_func("/teststk/Get Input 1.6.1", &get_input_data_161, test_get_input); g_test_add_data_func("/teststk/Get Input 1.7.1", &get_input_data_171, test_get_input); g_test_add_data_func("/teststk/Get Input 1.8.1", &get_input_data_181, test_get_input); g_test_add_data_func("/teststk/Get Input 1.9.1", &get_input_data_191, test_get_input); g_test_add_data_func("/teststk/Get Input 1.10.1", &get_input_data_1101, test_get_input); g_test_add_data_func("/teststk/Get Input 2.1.1", &get_input_data_211, test_get_input); g_test_add_data_func("/teststk/Get Input 3.1.1", &get_input_data_311, test_get_input); g_test_add_data_func("/teststk/Get Input 3.2.1", &get_input_data_321, test_get_input); g_test_add_data_func("/teststk/Get Input 4.1.1", &get_input_data_411, test_get_input); g_test_add_data_func("/teststk/Get Input 4.2.1", &get_input_data_421, test_get_input); g_test_add_data_func("/teststk/Get Input 5.1.1", &get_input_data_511, test_get_input); g_test_add_data_func("/teststk/Get Input 5.2.1", &get_input_data_521, test_get_input); g_test_add_data_func("/teststk/Get Input 6.1.1", &get_input_data_611, test_get_input); g_test_add_data_func("/teststk/Get Input 6.2.1", &get_input_data_621, test_get_input); g_test_add_data_func("/teststk/Get Input 6.3.1", &get_input_data_631, test_get_input); g_test_add_data_func("/teststk/Get Input 6.4.1", &get_input_data_641, test_get_input); g_test_add_data_func("/teststk/Get Input 7.1.1", &get_input_data_711, test_get_input); g_test_add_data_func("/teststk/Get Input 8.1.1", &get_input_data_811, test_get_input); g_test_add_data_func("/teststk/Get Input 8.1.2", &get_input_data_812, test_get_input); g_test_add_data_func("/teststk/Get Input 8.2.1", &get_input_data_821, test_get_input); g_test_add_data_func("/teststk/Get Input 8.2.2", &get_input_data_822, test_get_input); g_test_add_data_func("/teststk/Get Input 8.3.1", &get_input_data_831, test_get_input); g_test_add_data_func("/teststk/Get Input 8.3.2", &get_input_data_832, test_get_input); g_test_add_data_func("/teststk/Get Input 8.4.1", &get_input_data_841, test_get_input); g_test_add_data_func("/teststk/Get Input 8.4.2", &get_input_data_842, test_get_input); g_test_add_data_func("/teststk/Get Input 8.4.3", &get_input_data_843, test_get_input); g_test_add_data_func("/teststk/Get Input 8.5.1", &get_input_data_851, test_get_input); g_test_add_data_func("/teststk/Get Input 8.5.2", &get_input_data_852, test_get_input); g_test_add_data_func("/teststk/Get Input 8.5.3", &get_input_data_853, test_get_input); g_test_add_data_func("/teststk/Get Input 8.6.1", &get_input_data_861, test_get_input); g_test_add_data_func("/teststk/Get Input 8.6.2", &get_input_data_862, test_get_input); g_test_add_data_func("/teststk/Get Input 8.6.3", &get_input_data_863, test_get_input); g_test_add_data_func("/teststk/Get Input 8.7.1", &get_input_data_871, test_get_input); g_test_add_data_func("/teststk/Get Input 8.7.2", &get_input_data_872, test_get_input); g_test_add_data_func("/teststk/Get Input 8.7.3", &get_input_data_873, test_get_input); g_test_add_data_func("/teststk/Get Input 8.8.1", &get_input_data_881, test_get_input); g_test_add_data_func("/teststk/Get Input 8.8.2", &get_input_data_882, test_get_input); g_test_add_data_func("/teststk/Get Input 8.8.3", &get_input_data_883, test_get_input); g_test_add_data_func("/teststk/Get Input 8.9.1", &get_input_data_891, test_get_input); g_test_add_data_func("/teststk/Get Input 8.9.2", &get_input_data_892, test_get_input); g_test_add_data_func("/teststk/Get Input 8.9.3", &get_input_data_893, test_get_input); g_test_add_data_func("/teststk/Get Input 8.10.1", &get_input_data_8101, test_get_input); g_test_add_data_func("/teststk/Get Input 8.10.2", &get_input_data_8102, test_get_input); g_test_add_data_func("/teststk/Get Input 9.1.1", &get_input_data_911, test_get_input); g_test_add_data_func("/teststk/Get Input 9.2.1", &get_input_data_921, test_get_input); g_test_add_data_func("/teststk/Get Input 10.1.1", &get_input_data_1011, test_get_input); g_test_add_data_func("/teststk/Get Input 10.2.1", &get_input_data_1021, test_get_input); g_test_add_data_func("/teststk/Get Input 11.1.1", &get_input_data_1111, test_get_input); g_test_add_data_func("/teststk/Get Input 11.2.1", &get_input_data_1121, test_get_input); g_test_add_data_func("/teststk/Get Input 12.1.1", &get_input_data_1211, test_get_input); g_test_add_data_func("/teststk/Get Input 12.2.1", &get_input_data_1221, test_get_input); g_test_add_data_func("/teststk/Get Input response 1.1.1", &get_input_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 1.2.1", &get_input_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 1.3.1", &get_input_response_data_131, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 1.4.1", &get_input_response_data_141, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 1.5.1", &get_input_response_data_151, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 1.6.1", &get_input_response_data_161, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 1.7.1", &get_input_response_data_171, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 1.8.1", &get_input_response_data_181, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 1.9.1", &get_input_response_data_191, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 2.1.1", &get_input_response_data_211, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 3.1.1", &get_input_response_data_311, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 4.1.1", &get_input_response_data_411, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 4.2.1", &get_input_response_data_421, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 6.1.1A", &get_input_response_data_611a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 6.1.1B", &get_input_response_data_611b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 7.1.1", &get_input_response_data_711, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 8.1.2", &get_input_response_data_812, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 8.4.3", &get_input_response_data_843, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 10.1.1", &get_input_response_data_1011, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 10.2.1", &get_input_response_data_1021, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 12.1.1", &get_input_response_data_1211, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Input response 12.2.1", &get_input_response_data_1221, test_terminal_response_encoding); g_test_add_data_func("/teststk/More Time 1.1.1", &more_time_data_111, test_more_time); g_test_add_data_func("/teststk/More Time response 1.1.1", &more_time_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Play Tone 1.1.1", &play_tone_data_111, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.2", &play_tone_data_112, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.3", &play_tone_data_113, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.4", &play_tone_data_114, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.5", &play_tone_data_115, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.6", &play_tone_data_116, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.7", &play_tone_data_117, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.8", &play_tone_data_118, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.9", &play_tone_data_119, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.10", &play_tone_data_1110, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.11", &play_tone_data_1111, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.12", &play_tone_data_1112, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.13", &play_tone_data_1113, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.14", &play_tone_data_1114, test_play_tone); g_test_add_data_func("/teststk/Play Tone 1.1.15", &play_tone_data_1115, test_play_tone); g_test_add_data_func("/teststk/Play Tone 2.1.1", &play_tone_data_211, test_play_tone); g_test_add_data_func("/teststk/Play Tone 2.1.2", &play_tone_data_212, test_play_tone); g_test_add_data_func("/teststk/Play Tone 2.1.3", &play_tone_data_213, test_play_tone); g_test_add_data_func("/teststk/Play Tone 3.1.1", &play_tone_data_311, test_play_tone); g_test_add_data_func("/teststk/Play Tone 3.2.1", &play_tone_data_321, test_play_tone); g_test_add_data_func("/teststk/Play Tone 3.3.1", &play_tone_data_331, test_play_tone); g_test_add_data_func("/teststk/Play Tone 3.4.1", &play_tone_data_341, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.1.1", &play_tone_data_411, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.1.2", &play_tone_data_412, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.2.1", &play_tone_data_421, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.2.2", &play_tone_data_422, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.3.1", &play_tone_data_431, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.3.2", &play_tone_data_432, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.4.1", &play_tone_data_441, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.4.2", &play_tone_data_442, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.4.3", &play_tone_data_443, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.5.1", &play_tone_data_451, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.5.2", &play_tone_data_452, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.5.3", &play_tone_data_453, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.6.1", &play_tone_data_461, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.6.2", &play_tone_data_462, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.6.3", &play_tone_data_463, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.7.1", &play_tone_data_471, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.7.2", &play_tone_data_472, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.7.3", &play_tone_data_473, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.8.1", &play_tone_data_481, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.8.2", &play_tone_data_482, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.8.3", &play_tone_data_483, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.9.1", &play_tone_data_491, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.9.2", &play_tone_data_492, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.9.3", &play_tone_data_493, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.10.1", &play_tone_data_4101, test_play_tone); g_test_add_data_func("/teststk/Play Tone 4.10.2", &play_tone_data_4102, test_play_tone); g_test_add_data_func("/teststk/Play Tone 5.1.1", &play_tone_data_511, test_play_tone); g_test_add_data_func("/teststk/Play Tone 5.1.2", &play_tone_data_512, test_play_tone); g_test_add_data_func("/teststk/Play Tone 5.1.3", &play_tone_data_513, test_play_tone); g_test_add_data_func("/teststk/Play Tone 6.1.1", &play_tone_data_611, test_play_tone); g_test_add_data_func("/teststk/Play Tone 6.1.2", &play_tone_data_612, test_play_tone); g_test_add_data_func("/teststk/Play Tone 6.1.3", &play_tone_data_613, test_play_tone); g_test_add_data_func("/teststk/Play Tone response 1.1.1", &play_tone_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Play Tone response 1.1.9B", &play_tone_response_data_119b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Play Tone response 1.1.14", &play_tone_response_data_1114, test_terminal_response_encoding); g_test_add_data_func("/teststk/Play Tone response 3.1.1B", &play_tone_response_data_311b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Poll Interval 1.1.1", &poll_interval_data_111, test_poll_interval); g_test_add_data_func("/teststk/Poll Interval response 1.1.1", &poll_interval_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Poll Interval response 1.1.1A", &poll_interval_response_data_111a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Setup Menu 1.1.1", &setup_menu_data_111, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 1.1.2", &setup_menu_data_112, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 1.1.3", &setup_menu_data_113, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 1.2.1", &setup_menu_data_121, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 1.2.2", &setup_menu_data_122, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 1.2.3", &setup_menu_data_123, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 2.1.1", &setup_menu_data_211, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 3.1.1", &setup_menu_data_311, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 4.1.1", &setup_menu_data_411, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 4.2.1", &setup_menu_data_421, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 5.1.1", &setup_menu_data_511, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.1.1", &setup_menu_data_611, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.1.2", &setup_menu_data_612, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.2.1", &setup_menu_data_621, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.2.2", &setup_menu_data_622, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.3.1", &setup_menu_data_631, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.3.2", &setup_menu_data_632, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.4.1", &setup_menu_data_641, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.4.2", &setup_menu_data_642, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.4.3", &setup_menu_data_643, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.5.1", &setup_menu_data_651, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.6.1", &setup_menu_data_661, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.7.1", &setup_menu_data_671, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.8.1", &setup_menu_data_681, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.9.1", &setup_menu_data_691, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 6.10.1", &setup_menu_data_6101, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 7.1.1", &setup_menu_data_711, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 7.1.2", &setup_menu_data_712, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 7.1.3", &setup_menu_data_713, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 8.1.1", &setup_menu_data_811, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 8.1.2", &setup_menu_data_812, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 8.1.3", &setup_menu_data_813, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 9.1.1", &setup_menu_data_911, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 9.1.2", &setup_menu_data_912, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu 9.1.3", &setup_menu_data_913, test_setup_menu); g_test_add_data_func("/teststk/Setup Menu Negative 1", &setup_menu_data_neg_1, test_setup_menu_missing_val); g_test_add_data_func("/teststk/Setup Menu Negative 2", &setup_menu_data_neg_2, test_setup_menu_neg); g_test_add_data_func("/teststk/Setup Menu Negative 3", &setup_menu_data_neg_3, test_setup_menu_neg); g_test_add_data_func("/teststk/Setup Menu Negative 4", &setup_menu_data_neg_4, test_setup_menu_neg); g_test_add_data_func("/teststk/Set Up Menu response 1.1.1", &set_up_menu_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Menu response 4.1.1B", &set_up_menu_response_data_411b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Menu response 5.1.1", &set_up_menu_response_data_511, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item 1.1.1", &select_item_data_111, test_select_item); g_test_add_data_func("/teststk/Select Item 1.2.1", &select_item_data_121, test_select_item); g_test_add_data_func("/teststk/Select Item 1.3.1", &select_item_data_131, test_select_item); g_test_add_data_func("/teststk/Select Item 1.4.1", &select_item_data_141, test_select_item); g_test_add_data_func("/teststk/Select Item 1.5.1", &select_item_data_151, test_select_item); g_test_add_data_func("/teststk/Select Item 1.6.1", &select_item_data_161, test_select_item); g_test_add_data_func("/teststk/Select Item 2.1.1", &select_item_data_211, test_select_item); g_test_add_data_func("/teststk/Select Item 3.1.1", &select_item_data_311, test_select_item); g_test_add_data_func("/teststk/Select Item 4.1.1", &select_item_data_411, test_select_item); g_test_add_data_func("/teststk/Select Item 5.1.1", &select_item_data_511, test_select_item); g_test_add_data_func("/teststk/Select Item 5.2.1", &select_item_data_521, test_select_item); g_test_add_data_func("/teststk/Select Item 6.1.1", &select_item_data_611, test_select_item); g_test_add_data_func("/teststk/Select Item 6.2.1", &select_item_data_621, test_select_item); g_test_add_data_func("/teststk/Select Item 7.1.1", &select_item_data_711, test_select_item); g_test_add_data_func("/teststk/Select Item 8.1.1", &select_item_data_811, test_select_item); g_test_add_data_func("/teststk/Select Item 9.1.1", &select_item_data_911, test_select_item); g_test_add_data_func("/teststk/Select Item 9.1.2", &select_item_data_912, test_select_item); g_test_add_data_func("/teststk/Select Item 9.2.1", &select_item_data_921, test_select_item); g_test_add_data_func("/teststk/Select Item 9.2.2", &select_item_data_922, test_select_item); g_test_add_data_func("/teststk/Select Item 9.3.1", &select_item_data_931, test_select_item); g_test_add_data_func("/teststk/Select Item 9.3.2", &select_item_data_932, test_select_item); g_test_add_data_func("/teststk/Select Item 9.4.1", &select_item_data_941, test_select_item); g_test_add_data_func("/teststk/Select Item 9.4.2", &select_item_data_942, test_select_item); g_test_add_data_func("/teststk/Select Item 9.4.3", &select_item_data_943, test_select_item); g_test_add_data_func("/teststk/Select Item 9.5.1", &select_item_data_951, test_select_item); g_test_add_data_func("/teststk/Select Item 9.5.2", &select_item_data_952, test_select_item); g_test_add_data_func("/teststk/Select Item 9.5.3", &select_item_data_953, test_select_item); g_test_add_data_func("/teststk/Select Item 9.6.1", &select_item_data_961, test_select_item); g_test_add_data_func("/teststk/Select Item 9.6.2", &select_item_data_962, test_select_item); g_test_add_data_func("/teststk/Select Item 9.6.3", &select_item_data_963, test_select_item); g_test_add_data_func("/teststk/Select Item 9.7.1", &select_item_data_971, test_select_item); g_test_add_data_func("/teststk/Select Item 9.7.2", &select_item_data_972, test_select_item); g_test_add_data_func("/teststk/Select Item 9.7.3", &select_item_data_973, test_select_item); g_test_add_data_func("/teststk/Select Item 9.8.1", &select_item_data_981, test_select_item); g_test_add_data_func("/teststk/Select Item 9.8.2", &select_item_data_982, test_select_item); g_test_add_data_func("/teststk/Select Item 9.8.3", &select_item_data_983, test_select_item); g_test_add_data_func("/teststk/Select Item 9.9.1", &select_item_data_991, test_select_item); g_test_add_data_func("/teststk/Select Item 9.9.2", &select_item_data_992, test_select_item); g_test_add_data_func("/teststk/Select Item 9.9.3", &select_item_data_993, test_select_item); g_test_add_data_func("/teststk/Select Item 9.10.1", &select_item_data_9101, test_select_item); g_test_add_data_func("/teststk/Select Item 9.10.2", &select_item_data_9102, test_select_item); g_test_add_data_func("/teststk/Select Item 10.1.1", &select_item_data_1011, test_select_item); g_test_add_data_func("/teststk/Select Item 10.2.1", &select_item_data_1021, test_select_item); g_test_add_data_func("/teststk/Select Item 10.3.1", &select_item_data_1031, test_select_item); g_test_add_data_func("/teststk/Select Item 11.1.1", &select_item_data_1111, test_select_item); g_test_add_data_func("/teststk/Select Item 12.1.1", &select_item_data_1211, test_select_item); g_test_add_data_func("/teststk/Select Item 12.2.1", &select_item_data_1221, test_select_item); g_test_add_data_func("/teststk/Select Item 12.3.1", &select_item_data_1231, test_select_item); g_test_add_data_func("/teststk/Select Item response 1.1.1", &select_item_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 1.2.1", &select_item_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 1.3.1", &select_item_response_data_131, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 1.4.1", &select_item_response_data_141, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 1.4.2", &select_item_response_data_142, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 1.5.1", &select_item_response_data_151, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 3.1.1", &select_item_response_data_311, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 4.1.1", &select_item_response_data_411, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 5.1.1B", &select_item_response_data_511b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 6.1.1", &select_item_response_data_611, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 6.2.1", &select_item_response_data_621, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 7.1.1", &select_item_response_data_711, test_terminal_response_encoding); g_test_add_data_func("/teststk/Select Item response 8.1.1", &select_item_response_data_811, test_terminal_response_encoding); g_test_add_data_func("/teststk/Send SMS 1.1.1", &send_sms_data_111, test_send_sms); g_test_add_data_func("/teststk/Send SMS 1.2.1", &send_sms_data_121, test_send_sms); g_test_add_data_func("/teststk/Send SMS 1.3.1", &send_sms_data_131, test_send_sms); g_test_add_data_func("/teststk/Send SMS 1.4.1", &send_sms_data_141, test_send_sms); g_test_add_data_func("/teststk/Send SMS 1.5.1", &send_sms_data_151, test_send_sms); g_test_add_data_func("/teststk/Send SMS 1.6.1", &send_sms_data_161, test_send_sms); g_test_add_data_func("/teststk/Send SMS 1.7.1", &send_sms_data_171, test_send_sms); g_test_add_data_func("/teststk/Send SMS 1.8.1", &send_sms_data_181, test_send_sms); g_test_add_data_func("/teststk/Send SMS 2.1.1", &send_sms_data_211, test_send_sms); g_test_add_data_func("/teststk/Send SMS 2.1.2", &send_sms_data_212, test_send_sms); g_test_add_data_func("/teststk/Send SMS 2.1.3", &send_sms_data_213, test_send_sms); g_test_add_data_func("/teststk/Send SMS 3.1.1", &send_sms_data_311, test_send_sms); g_test_add_data_func("/teststk/Send SMS 3.2.1", &send_sms_data_321, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.1.1", &send_sms_data_411, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.1.2", &send_sms_data_412, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.2.1", &send_sms_data_421, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.2.2", &send_sms_data_422, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.3.1", &send_sms_data_431, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.3.2", &send_sms_data_432, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.4.1", &send_sms_data_441, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.4.2", &send_sms_data_442, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.4.3", &send_sms_data_443, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.5.1", &send_sms_data_451, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.5.2", &send_sms_data_452, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.5.3", &send_sms_data_453, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.6.1", &send_sms_data_461, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.6.2", &send_sms_data_462, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.6.3", &send_sms_data_463, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.7.1", &send_sms_data_471, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.7.2", &send_sms_data_472, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.7.3", &send_sms_data_473, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.8.1", &send_sms_data_481, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.8.2", &send_sms_data_482, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.8.3", &send_sms_data_483, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.9.1", &send_sms_data_491, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.9.2", &send_sms_data_492, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.9.3", &send_sms_data_493, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.10.1", &send_sms_data_4101, test_send_sms); g_test_add_data_func("/teststk/Send SMS 4.10.2", &send_sms_data_4102, test_send_sms); g_test_add_data_func("/teststk/Send SMS 5.1.1", &send_sms_data_511, test_send_sms); g_test_add_data_func("/teststk/Send SMS 5.1.2", &send_sms_data_512, test_send_sms); g_test_add_data_func("/teststk/Send SMS 5.1.3", &send_sms_data_513, test_send_sms); g_test_add_data_func("/teststk/Send SMS 6.1.1", &send_sms_data_611, test_send_sms); g_test_add_data_func("/teststk/Send SMS 6.1.2", &send_sms_data_612, test_send_sms); g_test_add_data_func("/teststk/Send SMS 6.1.3", &send_sms_data_613, test_send_sms); g_test_add_data_func("/teststk/Send SS 1.1.1", &send_ss_data_111, test_send_ss); g_test_add_data_func("/teststk/Send SS 1.4.1", &send_ss_data_141, test_send_ss); g_test_add_data_func("/teststk/Send SS 1.5.1", &send_ss_data_151, test_send_ss); g_test_add_data_func("/teststk/Send SS 1.6.1", &send_ss_data_161, test_send_ss); g_test_add_data_func("/teststk/Send SS 2.1.1", &send_ss_data_211, test_send_ss); g_test_add_data_func("/teststk/Send SS 2.2.1", &send_ss_data_221, test_send_ss); g_test_add_data_func("/teststk/Send SS 2.3.1", &send_ss_data_231, test_send_ss); g_test_add_data_func("/teststk/Send SS 2.4.1", &send_ss_data_241, test_send_ss); g_test_add_data_func("/teststk/Send SS 3.1.1", &send_ss_data_311, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.1.1", &send_ss_data_411, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.1.2", &send_ss_data_412, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.2.1", &send_ss_data_421, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.2.2", &send_ss_data_422, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.3.1", &send_ss_data_431, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.3.2", &send_ss_data_432, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.4.1", &send_ss_data_441, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.4.2", &send_ss_data_442, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.4.3", &send_ss_data_443, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.5.1", &send_ss_data_451, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.5.2", &send_ss_data_452, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.5.3", &send_ss_data_453, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.6.1", &send_ss_data_461, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.6.2", &send_ss_data_462, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.6.3", &send_ss_data_463, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.7.1", &send_ss_data_471, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.7.2", &send_ss_data_472, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.7.3", &send_ss_data_473, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.8.1", &send_ss_data_481, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.8.2", &send_ss_data_482, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.8.3", &send_ss_data_483, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.9.1", &send_ss_data_491, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.9.2", &send_ss_data_492, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.9.3", &send_ss_data_493, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.10.1", &send_ss_data_4101, test_send_ss); g_test_add_data_func("/teststk/Send SS 4.10.2", &send_ss_data_4102, test_send_ss); g_test_add_data_func("/teststk/Send SS 5.1.1", &send_ss_data_511, test_send_ss); g_test_add_data_func("/teststk/Send SS 6.1.1", &send_ss_data_611, test_send_ss); g_test_add_data_func("/teststk/Send USSD 1.1.1", &send_ussd_data_111, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 1.2.1", &send_ussd_data_121, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 1.3.1", &send_ussd_data_131, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 1.6.1", &send_ussd_data_161, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 1.7.1", &send_ussd_data_171, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 1.8.1", &send_ussd_data_181, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 2.1.1", &send_ussd_data_211, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 2.2.1", &send_ussd_data_221, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 2.3.1", &send_ussd_data_231, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 2.4.1", &send_ussd_data_241, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 3.1.1", &send_ussd_data_311, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.1.1", &send_ussd_data_411, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.1.2", &send_ussd_data_412, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.2.1", &send_ussd_data_421, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.2.2", &send_ussd_data_422, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.3.1", &send_ussd_data_431, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.3.2", &send_ussd_data_432, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.4.1", &send_ussd_data_441, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.4.2", &send_ussd_data_442, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.4.3", &send_ussd_data_443, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.5.1", &send_ussd_data_451, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.5.2", &send_ussd_data_452, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.5.3", &send_ussd_data_453, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.6.1", &send_ussd_data_461, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.6.2", &send_ussd_data_462, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.6.3", &send_ussd_data_463, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.7.1", &send_ussd_data_471, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.7.2", &send_ussd_data_472, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.7.3", &send_ussd_data_473, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.8.1", &send_ussd_data_481, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.8.2", &send_ussd_data_482, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.8.3", &send_ussd_data_483, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.9.1", &send_ussd_data_491, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.9.2", &send_ussd_data_492, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.9.3", &send_ussd_data_493, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.10.1", &send_ussd_data_4101, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 4.10.2", &send_ussd_data_4102, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 5.1.1", &send_ussd_data_511, test_send_ussd); g_test_add_data_func("/teststk/Send USSD 6.1.1", &send_ussd_data_611, test_send_ussd); g_test_add_data_func("/teststk/Send SMS response 1.1.1", &send_sms_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Send SMS response 1.2.1", &send_sms_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Send SMS response 3.1.1B", &send_sms_response_data_311b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Setup Call 1.1.1", &setup_call_data_111, test_setup_call); g_test_add_data_func("/teststk/Setup Call 1.4.1", &setup_call_data_141, test_setup_call); g_test_add_data_func("/teststk/Setup Call 1.5.1", &setup_call_data_151, test_setup_call); g_test_add_data_func("/teststk/Setup Call 1.8.1", &setup_call_data_181, test_setup_call); g_test_add_data_func("/teststk/Setup Call 1.9.1", &setup_call_data_191, test_setup_call); g_test_add_data_func("/teststk/Setup Call 1.10.1", &setup_call_data_1101, test_setup_call); g_test_add_data_func("/teststk/Setup Call 1.11.1", &setup_call_data_1111, test_setup_call); g_test_add_data_func("/teststk/Setup Call 1.12.1", &setup_call_data_1121, test_setup_call); g_test_add_data_func("/teststk/Setup Call 2.1.1", &setup_call_data_211, test_setup_call); g_test_add_data_func("/teststk/Setup Call 3.1.1", &setup_call_data_311, test_setup_call); g_test_add_data_func("/teststk/Setup Call 3.2.1", &setup_call_data_321, test_setup_call); g_test_add_data_func("/teststk/Setup Call 3.3.1", &setup_call_data_331, test_setup_call); g_test_add_data_func("/teststk/Setup Call 3.4.1", &setup_call_data_341, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.1.1", &setup_call_data_411, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.1.2", &setup_call_data_412, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.2.1", &setup_call_data_421, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.2.2", &setup_call_data_422, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.3.1", &setup_call_data_431, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.3.2", &setup_call_data_432, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.4.1", &setup_call_data_441, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.4.2", &setup_call_data_442, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.4.3", &setup_call_data_443, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.5.1", &setup_call_data_451, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.5.2", &setup_call_data_452, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.5.3", &setup_call_data_453, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.6.1", &setup_call_data_461, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.6.2", &setup_call_data_462, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.6.3", &setup_call_data_463, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.7.1", &setup_call_data_471, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.7.2", &setup_call_data_472, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.7.3", &setup_call_data_473, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.8.1", &setup_call_data_481, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.8.2", &setup_call_data_482, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.8.3", &setup_call_data_483, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.9.1", &setup_call_data_491, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.9.2", &setup_call_data_492, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.9.3", &setup_call_data_493, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.10.1", &setup_call_data_4101, test_setup_call); g_test_add_data_func("/teststk/Setup Call 4.10.2", &setup_call_data_4102, test_setup_call); g_test_add_data_func("/teststk/Setup Call 5.1.1", &setup_call_data_511, test_setup_call); g_test_add_data_func("/teststk/Setup Call 5.2.1", &setup_call_data_521, test_setup_call); g_test_add_data_func("/teststk/Setup Call 6.1.1", &setup_call_data_611, test_setup_call); g_test_add_data_func("/teststk/Setup Call 6.2.1", &setup_call_data_621, test_setup_call); g_test_add_data_func("/teststk/Setup Call 7.1.1", &setup_call_data_711, test_setup_call); g_test_add_data_func("/teststk/Setup Call 7.2.1", &setup_call_data_721, test_setup_call); g_test_add_data_func("/teststk/Set Up Call response 1.1.1", &set_up_call_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Call response 1.2.1", &set_up_call_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Call response 1.4.1", &set_up_call_response_data_141, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Call response 1.5.1", &set_up_call_response_data_151, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Call response 1.6.1", &set_up_call_response_data_161, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Call response 1.7.1A", &set_up_call_response_data_171a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Call response 1.7.1B", &set_up_call_response_data_171b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Call response 1.10.1", &set_up_call_response_data_1101, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Call response 1.11.1B", &set_up_call_response_data_1111b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Call response 1.12.1", &set_up_call_response_data_1121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Call response 3.1.1B", &set_up_call_response_data_311b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh 1.2.1", &refresh_data_121, test_refresh); g_test_add_data_func("/teststk/Refresh 1.5.1", &refresh_data_151, test_refresh); g_test_add_data_func("/teststk/Refresh response 1.1.1A", &refresh_response_data_111a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 1.1.1B", &refresh_response_data_111b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 1.2.1A", &refresh_response_data_121a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 1.2.1B", &refresh_response_data_121b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 1.3.1A", &refresh_response_data_131a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 1.3.1B", &refresh_response_data_141b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 1.4.1A", &refresh_response_data_141a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 1.4.1B", &refresh_response_data_141b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 1.7.1", &refresh_response_data_171, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 2.4.1A", &refresh_response_data_241a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 2.4.1B", &refresh_response_data_241b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 3.1.1", &refresh_response_data_311, test_terminal_response_encoding); g_test_add_data_func("/teststk/Refresh response 3.1.2", &refresh_response_data_312, test_terminal_response_encoding); g_test_add_data_func("/teststk/Polling off 1.1.2", &polling_off_data_112, test_polling_off); g_test_add_data_func("/teststk/Polling off response 1.1.2", &polling_off_response_data_112, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info 1.2.1", &provide_local_info_data_121, test_provide_local_info); g_test_add_data_func("/teststk/Provide Local Info 1.4.1", &provide_local_info_data_141, test_provide_local_info); g_test_add_data_func("/teststk/Provide Local Info 1.5.1", &provide_local_info_data_151, test_provide_local_info); g_test_add_data_func("/teststk/Provide Local Info 1.8.1", &provide_local_info_data_181, test_provide_local_info); g_test_add_data_func("/teststk/Provide Local Info 1.9.1", &provide_local_info_data_191, test_provide_local_info); g_test_add_data_func("/teststk/Provide Local Info 1.11.1", &provide_local_info_data_1111, test_provide_local_info); g_test_add_data_func("/teststk/Provide Local Info response 1.1.1A", &provide_local_info_response_data_111a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.1.1B", &provide_local_info_response_data_111b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.2.1", &provide_local_info_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.3.1", &provide_local_info_response_data_131, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.4.1", &provide_local_info_response_data_141, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.5.1", &provide_local_info_response_data_151, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.6.1", &provide_local_info_response_data_161, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.7.1", &provide_local_info_response_data_171, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.8.1", &provide_local_info_response_data_181, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.9.1", &provide_local_info_response_data_191, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.11.1", &provide_local_info_response_data_1111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.12.1", &provide_local_info_response_data_1121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.13.1", &provide_local_info_response_data_1131, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.14.1", &provide_local_info_response_data_1141, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.15.1", &provide_local_info_response_data_1151, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.16.1", &provide_local_info_response_data_1161, test_terminal_response_encoding); g_test_add_data_func("/teststk/Provide Local Info response 1.17.1", &provide_local_info_response_data_1171, test_terminal_response_encoding); g_test_add_data_func("/teststk/Setup Event List 1.1.1", &setup_event_list_data_111, test_setup_event_list); g_test_add_data_func("/teststk/Setup Event List 1.2.1", &setup_event_list_data_121, test_setup_event_list); g_test_add_data_func("/teststk/Setup Event List 1.2.2", &setup_event_list_data_122, test_setup_event_list); g_test_add_data_func("/teststk/Setup Event List 1.3.1", &setup_event_list_data_131, test_setup_event_list); g_test_add_data_func("/teststk/Setup Event List 1.3.2", &setup_event_list_data_132, test_setup_event_list); g_test_add_data_func("/teststk/Setup Event List 1.4.1", &setup_event_list_data_141, test_setup_event_list); g_test_add_data_func("/teststk/Set Up Event List response 1.1.1", &set_up_event_list_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Perform Card APDU 1.1.1", &perform_card_apdu_data_111, test_perform_card_apdu); g_test_add_data_func("/teststk/Perform Card APDU 1.1.2", &perform_card_apdu_data_112, test_perform_card_apdu); g_test_add_data_func("/teststk/Perform Card APDU 1.2.1", &perform_card_apdu_data_121, test_perform_card_apdu); g_test_add_data_func("/teststk/Perform Card APDU 1.2.2", &perform_card_apdu_data_122, test_perform_card_apdu); g_test_add_data_func("/teststk/Perform Card APDU 1.2.3", &perform_card_apdu_data_123, test_perform_card_apdu); g_test_add_data_func("/teststk/Perform Card APDU 1.2.4", &perform_card_apdu_data_124, test_perform_card_apdu); g_test_add_data_func("/teststk/Perform Card APDU 1.2.5", &perform_card_apdu_data_125, test_perform_card_apdu); g_test_add_data_func("/teststk/Perform Card APDU 1.5.1", &perform_card_apdu_data_151, test_perform_card_apdu); g_test_add_data_func("/teststk/Perform Card APDU 2.1.1", &perform_card_apdu_data_211, test_perform_card_apdu); g_test_add_data_func("/teststk/Get Reader Status 1.1.1", &get_reader_status_data_111, test_get_reader_status); g_test_add_data_func("/teststk/Timer Management 1.1.1", &timer_mgmt_data_111, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.1.2", &timer_mgmt_data_112, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.1.3", &timer_mgmt_data_113, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.1.4", &timer_mgmt_data_114, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.2.1", &timer_mgmt_data_121, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.2.2", &timer_mgmt_data_122, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.2.3", &timer_mgmt_data_123, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.2.4", &timer_mgmt_data_124, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.3.1", &timer_mgmt_data_131, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.3.2", &timer_mgmt_data_132, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.3.3", &timer_mgmt_data_133, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.3.4", &timer_mgmt_data_134, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.4.1", &timer_mgmt_data_141, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.4.2", &timer_mgmt_data_142, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.4.3", &timer_mgmt_data_143, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.4.4", &timer_mgmt_data_144, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.4.5", &timer_mgmt_data_145, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.4.6", &timer_mgmt_data_146, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.4.7", &timer_mgmt_data_147, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.4.8", &timer_mgmt_data_148, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.5.1", &timer_mgmt_data_151, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.5.2", &timer_mgmt_data_152, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.5.3", &timer_mgmt_data_153, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.5.4", &timer_mgmt_data_154, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.5.5", &timer_mgmt_data_155, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.5.6", &timer_mgmt_data_156, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.5.7", &timer_mgmt_data_157, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.5.8", &timer_mgmt_data_158, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.6.1", &timer_mgmt_data_161, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.6.2", &timer_mgmt_data_162, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.6.3", &timer_mgmt_data_163, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.6.4", &timer_mgmt_data_164, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.6.5", &timer_mgmt_data_165, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.6.6", &timer_mgmt_data_166, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.6.7", &timer_mgmt_data_167, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 1.6.8", &timer_mgmt_data_168, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 2.1.1", &timer_mgmt_data_211, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management 2.2.1", &timer_mgmt_data_221, test_timer_mgmt); g_test_add_data_func("/teststk/Timer Management response 1.1.1", &timer_mgmt_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.1.2", &timer_mgmt_response_data_112, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.1.4", &timer_mgmt_response_data_114, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.2.1", &timer_mgmt_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.2.2", &timer_mgmt_response_data_122, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.2.4", &timer_mgmt_response_data_124, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.3.1", &timer_mgmt_response_data_131, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.3.2", &timer_mgmt_response_data_132, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.3.4", &timer_mgmt_response_data_134, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.4.1A", &timer_mgmt_response_data_141a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.4.1B", &timer_mgmt_response_data_141b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.4.2A", &timer_mgmt_response_data_142a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.4.3A", &timer_mgmt_response_data_143a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.4.4A", &timer_mgmt_response_data_144a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.4.5A", &timer_mgmt_response_data_145a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.4.6A", &timer_mgmt_response_data_146a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.4.7A", &timer_mgmt_response_data_147a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.4.8A", &timer_mgmt_response_data_148a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.5.1A", &timer_mgmt_response_data_151a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.5.1B", &timer_mgmt_response_data_151b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.5.2A", &timer_mgmt_response_data_152a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.5.3A", &timer_mgmt_response_data_153a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.5.4A", &timer_mgmt_response_data_154a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.5.5A", &timer_mgmt_response_data_155a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.5.6A", &timer_mgmt_response_data_156a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.5.7A", &timer_mgmt_response_data_157a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.5.8A", &timer_mgmt_response_data_158a, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.6.3", &timer_mgmt_response_data_163, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.6.4", &timer_mgmt_response_data_164, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.6.5", &timer_mgmt_response_data_165, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.6.6", &timer_mgmt_response_data_166, test_terminal_response_encoding); g_test_add_data_func("/teststk/Timer Management response 1.6.7", &timer_mgmt_response_data_167, test_terminal_response_encoding); g_test_add_data_func("/teststk/Setup Idle Mode Text 1.1.1", &setup_idle_mode_text_data_111, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 1.2.1", &setup_idle_mode_text_data_121, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 1.3.1", &setup_idle_mode_text_data_131, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 1.7.1", &setup_idle_mode_text_data_171, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 2.1.1", &setup_idle_mode_text_data_211, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 2.2.1", &setup_idle_mode_text_data_221, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 2.3.1", &setup_idle_mode_text_data_231, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 2.4.1", &setup_idle_mode_text_data_241, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 3.1.1", &setup_idle_mode_text_data_311, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.1.1", &setup_idle_mode_text_data_411, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.1.2", &setup_idle_mode_text_data_412, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.2.1", &setup_idle_mode_text_data_421, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.2.2", &setup_idle_mode_text_data_422, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.3.1", &setup_idle_mode_text_data_431, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.3.2", &setup_idle_mode_text_data_432, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.4.1", &setup_idle_mode_text_data_441, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.4.2", &setup_idle_mode_text_data_442, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.4.3", &setup_idle_mode_text_data_443, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.5.1", &setup_idle_mode_text_data_451, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.5.2", &setup_idle_mode_text_data_452, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.5.3", &setup_idle_mode_text_data_453, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.6.1", &setup_idle_mode_text_data_461, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.6.2", &setup_idle_mode_text_data_462, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.6.3", &setup_idle_mode_text_data_463, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.7.1", &setup_idle_mode_text_data_471, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.7.2", &setup_idle_mode_text_data_472, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.7.3", &setup_idle_mode_text_data_473, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.8.1", &setup_idle_mode_text_data_481, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.8.2", &setup_idle_mode_text_data_482, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.8.3", &setup_idle_mode_text_data_483, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.9.1", &setup_idle_mode_text_data_491, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.9.2", &setup_idle_mode_text_data_492, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.9.3", &setup_idle_mode_text_data_493, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.10.1", &setup_idle_mode_text_data_4101, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 4.10.2", &setup_idle_mode_text_data_4102, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 5.1.1", &setup_idle_mode_text_data_511, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Setup Idle Mode Text 6.1.1", &setup_idle_mode_text_data_611, test_setup_idle_mode_text); g_test_add_data_func("/teststk/Set Up Idle Mode Text response 1.1.1", &set_up_idle_mode_text_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Idle Mode Text response 2.1.1B", &set_up_idle_mode_text_response_data_211b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Set Up Idle Mode Text response 2.4.1", &set_up_idle_mode_text_response_data_241, test_terminal_response_encoding); g_test_add_data_func("/teststk/Run At Command 1.1.1", &run_at_command_data_111, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 1.2.1", &run_at_command_data_121, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 1.3.1", &run_at_command_data_131, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 2.1.1", &run_at_command_data_211, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 2.2.1", &run_at_command_data_221, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 2.3.1", &run_at_command_data_231, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 2.4.1", &run_at_command_data_241, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 2.5.1", &run_at_command_data_251, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.1.1", &run_at_command_data_311, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.1.2", &run_at_command_data_312, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.2.1", &run_at_command_data_321, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.2.2", &run_at_command_data_322, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.3.1", &run_at_command_data_331, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.3.2", &run_at_command_data_332, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.4.1", &run_at_command_data_341, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.4.2", &run_at_command_data_342, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.4.3", &run_at_command_data_343, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.5.1", &run_at_command_data_351, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.5.2", &run_at_command_data_352, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.5.3", &run_at_command_data_353, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.6.1", &run_at_command_data_361, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.6.2", &run_at_command_data_362, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.6.3", &run_at_command_data_363, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.7.1", &run_at_command_data_371, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.7.2", &run_at_command_data_372, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.7.3", &run_at_command_data_373, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.8.1", &run_at_command_data_381, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.8.2", &run_at_command_data_382, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.8.3", &run_at_command_data_383, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.9.1", &run_at_command_data_391, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.9.2", &run_at_command_data_392, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.9.3", &run_at_command_data_393, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.10.1", &run_at_command_data_3101, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 3.10.2", &run_at_command_data_3102, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 4.1.1", &run_at_command_data_411, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 5.1.1", &run_at_command_data_511, test_run_at_command); g_test_add_data_func("/teststk/Run At Command 6.1.1", &run_at_command_data_611, test_run_at_command); g_test_add_data_func("/teststk/Run AT Command response 1.1.1", &run_at_command_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Run AT Command response 2.1.1B", &run_at_command_response_data_211b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Run AT Command response 2.5.1", &run_at_command_response_data_251, test_terminal_response_encoding); g_test_add_data_func("/teststk/Send DTMF 1.1.1", &send_dtmf_data_111, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 1.2.1", &send_dtmf_data_121, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 1.3.1", &send_dtmf_data_131, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 2.1.1", &send_dtmf_data_211, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 2.2.1", &send_dtmf_data_221, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 2.3.1", &send_dtmf_data_231, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 3.1.1", &send_dtmf_data_311, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.1.1", &send_dtmf_data_411, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.1.2", &send_dtmf_data_412, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.2.1", &send_dtmf_data_421, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.2.2", &send_dtmf_data_422, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.3.1", &send_dtmf_data_431, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.3.2", &send_dtmf_data_432, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.4.1", &send_dtmf_data_441, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.4.2", &send_dtmf_data_442, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.4.3", &send_dtmf_data_443, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.5.1", &send_dtmf_data_451, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.5.2", &send_dtmf_data_452, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.5.3", &send_dtmf_data_453, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.6.1", &send_dtmf_data_461, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.6.2", &send_dtmf_data_462, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.6.3", &send_dtmf_data_463, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.7.1", &send_dtmf_data_471, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.7.2", &send_dtmf_data_472, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.7.3", &send_dtmf_data_473, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.8.1", &send_dtmf_data_481, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.8.2", &send_dtmf_data_482, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.8.3", &send_dtmf_data_483, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.9.1", &send_dtmf_data_491, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.9.2", &send_dtmf_data_492, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.9.3", &send_dtmf_data_493, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.10.1", &send_dtmf_data_4101, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 4.10.2", &send_dtmf_data_4102, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 5.1.1", &send_dtmf_data_511, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF 6.1.1", &send_dtmf_data_611, test_send_dtmf); g_test_add_data_func("/teststk/Send DTMF response 1.1.1", &send_dtmf_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Send DTMF response 1.4.1", &send_dtmf_response_data_141, test_terminal_response_encoding); g_test_add_data_func("/teststk/Send DTMF response 2.1.1B", &send_dtmf_response_data_211b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Language Notification 1.1.1", &language_notification_data_111, test_language_notification); g_test_add_data_func("/teststk/Language Notification 1.2.1", &language_notification_data_121, test_language_notification); g_test_add_data_func("/teststk/Language Notification response 1.1.1", &language_notification_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Language Notification response 1.2.1", &language_notification_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Launch Browser 1.1.1", &launch_browser_data_111, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 1.2.1", &launch_browser_data_121, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 1.3.1", &launch_browser_data_131, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 1.4.1", &launch_browser_data_141, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 2.1.1", &launch_browser_data_211, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 2.2.1", &launch_browser_data_221, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 2.3.1", &launch_browser_data_231, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 3.1.1", &launch_browser_data_311, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 4.1.1", &launch_browser_data_411, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 4.2.1", &launch_browser_data_421, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.1.1", &launch_browser_data_511, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.1.2", &launch_browser_data_512, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.2.1", &launch_browser_data_521, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.2.2", &launch_browser_data_522, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.3.1", &launch_browser_data_531, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.3.2", &launch_browser_data_532, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.4.1", &launch_browser_data_541, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.4.2", &launch_browser_data_542, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.4.3", &launch_browser_data_543, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.5.1", &launch_browser_data_551, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.5.2", &launch_browser_data_552, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.5.3", &launch_browser_data_553, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.6.1", &launch_browser_data_561, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.6.2", &launch_browser_data_562, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.6.3", &launch_browser_data_563, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.7.1", &launch_browser_data_571, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.7.2", &launch_browser_data_572, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.7.3", &launch_browser_data_573, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.8.1", &launch_browser_data_581, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.8.2", &launch_browser_data_582, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.8.3", &launch_browser_data_583, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.9.1", &launch_browser_data_591, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.9.2", &launch_browser_data_592, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.9.3", &launch_browser_data_593, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.10.1", &launch_browser_data_5101, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 5.10.2", &launch_browser_data_5102, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 6.1.1", &launch_browser_data_611, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser 7.1.1", &launch_browser_data_711, test_launch_browser); g_test_add_data_func("/teststk/Launch Browser response 1.1.1", &launch_browser_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Launch Browser response 2.1.1", &launch_browser_response_data_211, test_terminal_response_encoding); g_test_add_data_func("/teststk/Launch Browser response 2.2.1", &launch_browser_response_data_221, test_terminal_response_encoding); g_test_add_data_func("/teststk/Launch Browser response 2.3.1", &launch_browser_response_data_231, test_terminal_response_encoding); g_test_add_data_func("/teststk/Launch Browser response 4.1.1B", &launch_browser_response_data_411b, test_terminal_response_encoding); g_test_add_data_func("/teststk/Open channel 2.1.1", &open_channel_data_211, test_open_channel); g_test_add_data_func("/teststk/Open channel 2.2.1", &open_channel_data_221, test_open_channel); g_test_add_data_func("/teststk/Open channel 2.3.1", &open_channel_data_231, test_open_channel); g_test_add_data_func("/teststk/Open channel 2.4.1", &open_channel_data_241, test_open_channel); g_test_add_data_func("/teststk/Open channel 5.1.1", &open_channel_data_511, test_open_channel); g_test_add_data_func("/teststk/Open channel response 2.1.1", &open_channel_response_data_211, test_terminal_response_encoding); g_test_add_data_func("/teststk/Open channel response 2.7.1", &open_channel_response_data_271, test_terminal_response_encoding); g_test_add_data_func("/teststk/Close channel 1.1.1", &close_channel_data_111, test_close_channel); g_test_add_data_func("/teststk/Close channel 2.1.1", &close_channel_data_211, test_close_channel); g_test_add_data_func("/teststk/Close channel response 1.2.1", &close_channel_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Close channel response 1.3.1", &close_channel_response_data_131, test_terminal_response_encoding); g_test_add_data_func("/teststk/Receive data 1.1.1", &receive_data_data_111, test_receive_data); g_test_add_data_func("/teststk/Receive data 2.1.1", &receive_data_data_211, test_receive_data); g_test_add_data_func("/teststk/Receive data response 1.1.1", &receive_data_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Send data 1.1.1", &send_data_data_111, test_send_data); g_test_add_data_func("/teststk/Send data 1.2.1", &send_data_data_121, test_send_data); g_test_add_data_func("/teststk/Send data 2.1.1", &send_data_data_211, test_send_data); g_test_add_data_func("/teststk/Send data response 1.1.1", &send_data_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Send data response 1.2.1", &send_data_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Send data response 1.5.1", &send_data_response_data_151, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Channel status 1.1.1", &get_channel_status_data_111, test_get_channel_status); g_test_add_data_func("/teststk/Get Channel status response 1.1.1", &get_channel_status_response_data_111, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Channel status response 1.2.1", &get_channel_status_response_data_121, test_terminal_response_encoding); g_test_add_data_func("/teststk/Get Channel status response 1.3.1", &get_channel_status_response_data_131, test_terminal_response_encoding); g_test_add_data_func("/teststk/SMS-PP data download 1.6.1", &sms_pp_data_download_data_161, test_envelope_encoding); g_test_add_data_func("/teststk/SMS-PP data download 1.6.2", &sms_pp_data_download_data_162, test_envelope_encoding); g_test_add_data_func("/teststk/SMS-PP data download 1.8.2", &sms_pp_data_download_data_182, test_envelope_encoding); g_test_add_data_func("/teststk/CBS-PP data download 1.1", &cbs_pp_data_download_data_11, test_envelope_encoding); g_test_add_data_func("/teststk/CBS-PP data download 1.7", &cbs_pp_data_download_data_17, test_envelope_encoding); g_test_add_data_func("/teststk/Menu Selection 1.1.1", &menu_selection_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Menu Selection 1.1.2", &menu_selection_data_112, test_envelope_encoding); g_test_add_data_func("/teststk/Menu Selection 1.2.1", &menu_selection_data_121, test_envelope_encoding); g_test_add_data_func("/teststk/Menu Selection 1.2.2", &menu_selection_data_122, test_envelope_encoding); g_test_add_data_func("/teststk/Menu Selection 1.2.3", &menu_selection_data_123, test_envelope_encoding); g_test_add_data_func("/teststk/Menu Selection 2.1.1", &menu_selection_data_211, test_envelope_encoding); g_test_add_data_func("/teststk/Menu Selection 6.1.2", &menu_selection_data_612, test_envelope_encoding); g_test_add_data_func("/teststk/Menu Selection 6.4.1", &menu_selection_data_641, test_envelope_encoding); g_test_add_data_func("/teststk/Call Control 1.1.1A", &call_control_data_111a, test_envelope_encoding); g_test_add_data_func("/teststk/Call Control 1.1.1B", &call_control_data_111b, test_envelope_encoding); g_test_add_data_func("/teststk/Call Control 1.3.1A", &call_control_data_131a, test_envelope_encoding); g_test_add_data_func("/teststk/Call Control 1.3.1B", &call_control_data_131b, test_envelope_encoding); g_test_add_data_func("/teststk/MO Short Message Control 1.1.1A", &mo_short_message_control_data_111a, test_envelope_encoding); g_test_add_data_func("/teststk/MO Short Message Control 1.1.1B", &mo_short_message_control_data_111b, test_envelope_encoding); g_test_add_data_func("/teststk/Event: MT Call 1.1.1", &event_download_mt_call_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Event: MT Call 1.1.2", &event_download_mt_call_data_112, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Call Connected 1.1.1", &event_download_call_connected_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Call Connected 1.1.2", &event_download_call_connected_data_112, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.1", &event_download_call_disconnected_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.2A", &event_download_call_disconnected_data_112a, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.2B", &event_download_call_disconnected_data_112b, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.2C", &event_download_call_disconnected_data_112c, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.3A", &event_download_call_disconnected_data_113a, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.3B", &event_download_call_disconnected_data_113b, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.4A", &event_download_call_disconnected_data_114a, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Call Disconnected 1.1.4B", &event_download_call_disconnected_data_114b, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Location Status 1.1.1", &event_download_location_status_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Location Status 1.1.2A", &event_download_location_status_data_112a, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Location Status 1.1.2B", &event_download_location_status_data_112b, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Location Status 1.2.2", &event_download_location_status_data_122, test_envelope_encoding); g_test_add_data_func("/teststk/Event: User Activity 1.1.1", &event_download_user_activity_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Idle Screen Available 1.1.1", &event_download_idle_screen_available_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.1A", &event_download_card_reader_status_data_111a, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.1B", &event_download_card_reader_status_data_111b, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.1C", &event_download_card_reader_status_data_111c, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.1D", &event_download_card_reader_status_data_111d, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.2A", &event_download_card_reader_status_data_112a, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.2B", &event_download_card_reader_status_data_112b, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.2C", &event_download_card_reader_status_data_112c, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Card Reader Status 1.1.2D", &event_download_card_reader_status_data_112d, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Card Reader Status 2.1.2A", &event_download_card_reader_status_data_212a, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Card Reader Status 2.1.2B", &event_download_card_reader_status_data_212b, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Language Selection 1.1.1", &event_download_language_selection_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Language Selection 1.2.2", &event_download_language_selection_data_122, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Browser Termination 1.1.1", &event_download_browser_termination_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Data Available 1.1.1", &event_download_data_available_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Data Available 2.1.1", &event_download_data_available_data_211, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Channel Status 1.3.1", &event_download_channel_status_data_131, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Channel Status 2.1.1", &event_download_channel_status_data_211, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Channel Status 2.2.1", &event_download_channel_status_data_221, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Network Rejection 1.1.1", &event_download_network_rejection_data_111, test_envelope_encoding); g_test_add_data_func("/teststk/Event: Network Rejection 1.2.1", &event_download_network_rejection_data_121, test_envelope_encoding); g_test_add_data_func("/teststk/Timer Expiration 2.1.1", &timer_expiration_data_211, test_envelope_encoding); g_test_add_data_func("/teststk/Timer Expiration 2.2.1A", &timer_expiration_data_221a, test_envelope_encoding); g_test_add_data_func("/teststk/HTML Attribute Test 1", &html_attr_data_1, test_html_attr); g_test_add_data_func("/teststk/HTML Attribute Test 2", &html_attr_data_2, test_html_attr); g_test_add_data_func("/teststk/HTML Attribute Test 3", &html_attr_data_3, test_html_attr); g_test_add_data_func("/teststk/HTML Attribute Test 4", &html_attr_data_4, test_html_attr); g_test_add_data_func("/teststk/IMG to XPM Test 1", &xpm_test_1, test_img_to_xpm); g_test_add_data_func("/teststk/IMG to XPM Test 2", &xpm_test_2, test_img_to_xpm); g_test_add_data_func("/teststk/IMG to XPM Test 3", &xpm_test_3, test_img_to_xpm); g_test_add_data_func("/teststk/IMG to XPM Test 4", &xpm_test_4, test_img_to_xpm); g_test_add_data_func("/teststk/IMG to XPM Test 5", &xpm_test_5, test_img_to_xpm); g_test_add_data_func("/teststk/IMG to XPM Test 6", &xpm_test_6, test_img_to_xpm); return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-rilmodem-cb.c0000644000015600001650000004155512671500073022707 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2015 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "ril_constants.h" #include "rilmodem-test-server.h" static GMainLoop *mainloop; static const struct ofono_call_barring_driver *cbdriver; struct rilmodem_cb_data { GRil *ril; struct ofono_modem *modem; gconstpointer test_data; struct ofono_call_barring *cb; struct server_data *serverd; }; typedef gboolean (*StartFunc)(gpointer data); struct cb_data { StartFunc start_func; const char *lock; int enable; const char *passwd; const char *new_passwd; int cls; struct rilmodem_test_data rtd; enum ofono_error_type error_type; int status; }; static void query_callback(const struct ofono_error *error, int status, gpointer data) { struct rilmodem_cb_data *rsd = data; const struct cb_data *cbd = rsd->test_data; g_assert(error->type == cbd->error_type); if (error->type == OFONO_ERROR_TYPE_NO_ERROR) g_assert(status == cbd->status); g_main_loop_quit(mainloop); } static gboolean trigger_query(gpointer data) { struct rilmodem_cb_data *rsd = data; const struct cb_data *cbd = rsd->test_data; g_assert(cbdriver->query != NULL); cbdriver->query(rsd->cb, cbd->lock, cbd->cls, query_callback, rsd); return FALSE; } static void set_callback(const struct ofono_error *error, gpointer data) { struct rilmodem_cb_data *rsd = data; const struct cb_data *cbd = rsd->test_data; g_assert(error->type == cbd->error_type); g_main_loop_quit(mainloop); } static gboolean trigger_set(gpointer data) { struct rilmodem_cb_data *rsd = data; const struct cb_data *cbd = rsd->test_data; g_assert(cbdriver->set != NULL); cbdriver->set(rsd->cb, cbd->lock, cbd->enable, cbd->passwd, cbd->cls, set_callback, rsd); return FALSE; } static void set_passwd_callback(const struct ofono_error *error, gpointer data) { struct rilmodem_cb_data *rsd = data; const struct cb_data *cbd = rsd->test_data; g_assert(error->type == cbd->error_type); g_main_loop_quit(mainloop); } static gboolean trigger_set_passwd(gpointer data) { struct rilmodem_cb_data *rsd = data; const struct cb_data *cbd = rsd->test_data; g_assert(cbdriver->set_passwd != NULL); cbdriver->set_passwd(rsd->cb, cbd->lock, cbd->passwd, cbd->new_passwd, set_passwd_callback, rsd); return FALSE; } /* RIL_REQUEST_GET_FACILITY_LOCK witht the following parameters: * * facility="OI" (outgoing international calls) * service class=1 ( VOICE ) */ static const guchar req_get_facility_lock_parcel_1[] = { 0x00, 0x00, 0x00, 0x2c, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; /* * The following structure contains test data for a valid * RIL_REQUEST_GET_FACILITY_LOCK reply with parameter {1} * which indicates that call-barring is activated for the * previously specified facility for the VOICE class. */ static const guchar reply_get_facility_lock_data_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct cb_data testdata_query_valid_1 = { .start_func = trigger_query, .lock = "OI", .cls = BEARER_CLASS_VOICE, .rtd = { .req_data = req_get_facility_lock_parcel_1, .req_size = sizeof(req_get_facility_lock_parcel_1), .rsp_data = reply_get_facility_lock_data_valid_1, .rsp_size = sizeof(reply_get_facility_lock_data_valid_1), }, .status = BEARER_CLASS_VOICE, }; /* GENERIC_FAILURE returned in RIL reply */ static const struct cb_data testdata_query_invalid_1 = { .start_func = trigger_query, .lock = "OI", .cls = BEARER_CLASS_VOICE, .rtd = { .req_data = req_get_facility_lock_parcel_1, .req_size = sizeof(req_get_facility_lock_parcel_1), .rsp_data = reply_get_facility_lock_data_valid_1, .rsp_size = sizeof(reply_get_facility_lock_data_valid_1), .rsp_error = RIL_E_GENERIC_FAILURE, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* * The following structure contains test data for a valid * RIL_REQUEST_GET_FACILITY_LOCK reply with invalid number * of parameters {0} specified. */ static const guchar reply_get_facility_lock_data_invalid_2[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct cb_data testdata_query_invalid_2 = { .start_func = trigger_query, .lock = "OI", .cls = BEARER_CLASS_VOICE, .rtd = { .req_data = req_get_facility_lock_parcel_1, .req_size = sizeof(req_get_facility_lock_parcel_1), .rsp_data = reply_get_facility_lock_data_invalid_2, .rsp_size = sizeof(reply_get_facility_lock_data_invalid_2), }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* * The following structure contains test data for an invalid * RIL_REQUEST_GET_FACILITY_LOCK reply with an invalid class * mask (-255). */ static const guchar reply_get_facility_lock_data_invalid_3[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff }; static const struct cb_data testdata_query_invalid_3 = { .start_func = trigger_query, .lock = "OI", .cls = BEARER_CLASS_VOICE, .rtd = { .req_data = req_get_facility_lock_parcel_1, .req_size = sizeof(req_get_facility_lock_parcel_1), .rsp_data = reply_get_facility_lock_data_invalid_3, .rsp_size = sizeof(reply_get_facility_lock_data_invalid_3), }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* * The following structure contains test data for a * RIL_REQUEST_GET_FACILITY_LOCK reply with an incomplete * integer parameter, which will trigger a malformed parcel * error. */ static const guchar reply_get_facility_lock_data_invalid_4[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 }; static const struct cb_data testdata_query_invalid_4 = { .start_func = trigger_query, .lock = "OI", .cls = BEARER_CLASS_VOICE, .rtd = { .req_data = req_get_facility_lock_parcel_1, .req_size = sizeof(req_get_facility_lock_parcel_1), .rsp_data = reply_get_facility_lock_data_invalid_4, .rsp_size = sizeof(reply_get_facility_lock_data_invalid_4), }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* RIL_REQUEST_SET_FACILITY_LOCK witht the following parameters: * * facility="OI" (outgoing international calls) * unlock (0) * passwd="0000" * service class=1 ( VOICE ) */ static const guchar req_set_facility_lock_parcel_1[] = { 0x00, 0x00, 0x00, 0x3c, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; /* * This test doesn't specify any data for RIL_REQUEST_SET_FACILITY_LOCK reply * to simulate a reply generated by mako. */ static const struct cb_data testdata_set_valid_1 = { .start_func = trigger_set, .lock = "OI", .passwd = "0000", .cls = BEARER_CLASS_VOICE, .rtd = { .req_data = req_set_facility_lock_parcel_1, .req_size = sizeof(req_set_facility_lock_parcel_1), }, }; /* RIL_REQUEST_SET_FACILITY_LOCK witht the following parameters: * * facility="OI" (outgoing international calls) * unlock (1) * passwd="0000" * service class=0 ( NONE ) */ static const guchar req_set_facility_lock_parcel_2[] = { 0x00, 0x00, 0x00, 0x3c, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; /* * The following structure contains test data for a valid * RIL_REQUEST_SET_FACILITY_LOCK reply with parameter {1} */ static const guchar reply_set_facility_lock_data_valid_2[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct cb_data testdata_set_valid_2 = { .start_func = trigger_set, .lock = "OI", .enable = 1, .passwd = "0000", .cls = BEARER_CLASS_DEFAULT, /* updated to NONE in outgoing parcel */ .rtd = { .req_data = req_set_facility_lock_parcel_2, .req_size = sizeof(req_set_facility_lock_parcel_2), .rsp_data = reply_set_facility_lock_data_valid_2, .rsp_size = sizeof(reply_set_facility_lock_data_valid_2), }, }; /* GENERIC_FAILURE returned in RIL reply */ static const struct cb_data testdata_set_invalid_1 = { .start_func = trigger_set, .lock = "OI", .enable = 1, .passwd = "0000", .cls = BEARER_CLASS_DEFAULT, .rtd = { .req_data = req_set_facility_lock_parcel_2, .req_size = sizeof(req_set_facility_lock_parcel_2), .rsp_error = RIL_E_GENERIC_FAILURE, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* * The following structure contains test data for a * RIL_REQUEST_SET_FACILITY_LOCK reply with an invalid * number of parameters {2} */ static const guchar reply_set_facility_lock_data_invalid_2[] = { 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct cb_data testdata_set_invalid_2 = { .start_func = trigger_set, .lock = "OI", .enable = 1, .passwd = "0000", .cls = BEARER_CLASS_DEFAULT, .rtd = { .req_data = req_set_facility_lock_parcel_2, .req_size = sizeof(req_set_facility_lock_parcel_2), .rsp_data = reply_set_facility_lock_data_invalid_2, .rsp_size = sizeof(reply_set_facility_lock_data_invalid_2), }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* * The following structure contains test data for a * RIL_REQUEST_SET_FACILITY_LOCK reply with an incomplete * integer parameter, which will trigger a malformed parcel * error. */ static const guchar reply_set_facility_lock_data_invalid_3[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 }; static const struct cb_data testdata_set_invalid_3 = { .start_func = trigger_set, .lock = "OI", .enable = 1, .passwd = "0000", .cls = BEARER_CLASS_DEFAULT, .rtd = { .req_data = req_set_facility_lock_parcel_2, .req_size = sizeof(req_set_facility_lock_parcel_2), .rsp_data = reply_set_facility_lock_data_invalid_3, .rsp_size = sizeof(reply_set_facility_lock_data_invalid_3), }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* RIL_REQUEST_CHANGE_BARRING_PASSWORD with the following parameters: * * facility="OI" (outgoing international calls) * old passwd="1111" * new_passwd="0000" */ static const guchar req_change_barring_passwd_parcel_1[] = { 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* * This test doesn't specify any data for RIL_REQUEST_SET_FACILITY_LOCK reply * to simulate a reply generated by mako. */ static const struct cb_data testdata_set_passwd_valid_1 = { .start_func = trigger_set_passwd, .lock = "OI", .passwd = "1111", .new_passwd = "0000", .rtd = { .req_data = req_change_barring_passwd_parcel_1, .req_size = sizeof(req_change_barring_passwd_parcel_1), }, }; /* GENERIC_FAILURE returned in RIL reply */ static const struct cb_data testdata_set_passwd_invalid_1 = { .start_func = trigger_set_passwd, .lock = "OI", .passwd = "1111", .new_passwd = "0000", .rtd = { .req_data = req_change_barring_passwd_parcel_1, .req_size = sizeof(req_change_barring_passwd_parcel_1), .rsp_error = RIL_E_GENERIC_FAILURE, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* Declarations && Re-implementations of core functions. */ void ril_call_barring_exit(void); void ril_call_barring_init(void); struct ofono_call_barring { void *driver_data; const struct cb_data *cbd; }; struct ofono_call_barring *ofono_call_barring_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct rilmodem_cb_data *rsd = data; struct ofono_call_barring *cb = g_new0(struct ofono_call_barring, 1); int retval; retval = cbdriver->probe(cb, OFONO_RIL_VENDOR_AOSP, rsd->ril); g_assert(retval == 0); return cb; } int ofono_call_barring_driver_register(const struct ofono_call_barring_driver *d) { if (cbdriver == NULL) cbdriver = d; return 0; } void ofono_call_barring_set_data(struct ofono_call_barring *cb, void *data) { cb->driver_data = data; } void *ofono_call_barring_get_data(struct ofono_call_barring *cb) { return cb->driver_data; } void ofono_call_barring_register(struct ofono_call_barring *cb) { } void ofono_call_barring_driver_unregister(const struct ofono_call_barring_driver *d) { } /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN static void server_connect_cb(gpointer data) { struct rilmodem_cb_data *rsd = data; const struct cb_data *cbd = rsd->test_data; /* This causes local impl of _create() to call driver's probe func. */ rsd->cb = ofono_call_barring_create(NULL, OFONO_RIL_VENDOR_AOSP, "rilmodem", rsd); rsd->cb->cbd = cbd; /* add_idle doesn't work, read blocks main loop!!! */ if (cbd->rtd.unsol_test) g_idle_add(cbd->start_func, (void *) rsd); else g_assert(cbd->start_func(rsd) == FALSE); } /* * This unit test: * - does some test data setup * - configures a dummy server socket * - creates a new gril client instance * - triggers a connect to the dummy * server socket * - starts a mainloop */ static void test_call_barring_func(gconstpointer data) { const struct cb_data *sd = data; struct rilmodem_cb_data *rsd; ril_call_barring_init(); rsd = g_new0(struct rilmodem_cb_data, 1); rsd->test_data = sd; rsd->serverd = rilmodem_test_server_create(&server_connect_cb, &sd->rtd, rsd); rsd->ril = g_ril_new(rilmodem_test_get_socket_name(rsd->serverd), OFONO_RIL_VENDOR_AOSP); g_assert(rsd->ril != NULL); mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); g_main_loop_unref(mainloop); cbdriver->remove(rsd->cb); g_ril_unref(rsd->ril); g_free(rsd); rilmodem_test_server_close(rsd->serverd); ril_call_barring_exit(); } #endif int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN g_test_add_data_func("/testrilmodemcallbarring/query/valid/1", &testdata_query_valid_1, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/query/invalid/1", &testdata_query_invalid_1, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/query/invalid/2", &testdata_query_invalid_2, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/query/invalid/3", &testdata_query_invalid_3, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/query/invalid/4", &testdata_query_invalid_3, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/set/valid/1", &testdata_set_valid_1, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/set/valid/2", &testdata_set_valid_2, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/set/invalid/1", &testdata_set_invalid_1, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/set/invalid/2", &testdata_set_invalid_2, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/set/invalid/3", &testdata_set_invalid_3, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/set_passwd/valid/1", &testdata_set_passwd_valid_1, test_call_barring_func); g_test_add_data_func("/testrilmodemcallbarring/set_passwd/invalid/1", &testdata_set_passwd_invalid_1, test_call_barring_func); #endif return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-grilunsol.c0000644000015600001650000007535012671500073022533 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include "gril.h" #include "grilunsol.h" /* * TODO: It may make sense to split this file into * domain-specific files ( eg. test-grilrequest-gprs-context.c ) * once more tests are added. */ /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN typedef struct signal_strength_test signal_strength_test; struct signal_strength_test { int strength; int ril_tech; const struct ril_msg msg; }; static const struct ril_msg reply_data_call_invalid_1 = { .buf = "", .buf_len = 0, .unsolicited = FALSE, .req = RIL_REQUEST_SETUP_DATA_CALL, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data equates te the following * RIL_REQUEST_SETUP_DATA_CALL reply parameters: * * {version=2,num=2 [status=0,retry=-1,cid=0,active=2,type=IP} * Parcel is truncated, as num=2 should trigger a failure. */ static const guchar reply_data_call_invalid_parcel2[] = { 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_data_call_invalid_2 = { .buf = (gchar *) &reply_data_call_invalid_parcel2, .buf_len = sizeof(reply_data_call_invalid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_SETUP_DATA_CALL, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data is a binary representation of * a parcel containing an invalid RIL_REQUEST_SETUP_DATA_CALL reply * with a NULL string specified for 'type': * * {version=7,num=1 [status=0,retry=-1,cid=0,active=2,type=NULL * ifname=rmnet_usb0,address=10.181.235.154/30, * dns=172.16.145.103 172.16.145.103,gateways=10.181.235.153]} */ static const guchar reply_data_call_invalid_parcel3[] = { 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x5f, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x38, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x20, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x38, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_data_call_invalid_3 = { .buf = (gchar *) &reply_data_call_invalid_parcel3, .buf_len = sizeof(reply_data_call_invalid_parcel3), .unsolicited = FALSE, .req = RIL_REQUEST_SETUP_DATA_CALL, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data is a binary representation of * a parcel containing an invalid RIL_REQUEST_SETUP_DATA_CALL reply * with a NULL string specified for 'ifname': * * {version=7,num=1 [status=0,retry=-1,cid=0,active=2,type=IP * ifname=NULL,address=10.181.235.154/30, * dns=172.16.145.103 172.16.145.103,gateways=10.181.235.153]} */ static const guchar reply_data_call_invalid_parcel4[] = { 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x11, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x38, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x20, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x38, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_data_call_invalid_4 = { .buf = (gchar *) &reply_data_call_invalid_parcel4, .buf_len = sizeof(reply_data_call_invalid_parcel4), .unsolicited = FALSE, .req = RIL_REQUEST_SETUP_DATA_CALL, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data is a binary representation of * a parcel containing an invalid RIL_REQUEST_SETUP_DATA_CALL reply * with a NULL string specified for 'address': * * {version=7,num=1 [status=0,retry=-1,cid=0,active=2,type=IP * ifname=rmnet_usb0,address=NULL, * dns=172.16.145.103 172.16.145.103,gateways=10.181.235.153]} */ static const guchar reply_data_call_invalid_parcel5[] = { 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x5f, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x00, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x20, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x38, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_data_call_invalid_5 = { .buf = (gchar *) &reply_data_call_invalid_parcel5, .buf_len = sizeof(reply_data_call_invalid_parcel5), .unsolicited = FALSE, .req = RIL_REQUEST_SETUP_DATA_CALL, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing an invalid RIL_REQUEST_SETUP_DATA_CALL reply * with a NULL string specified for 'gateways': * * {version=7,num=1 [status=0,retry=-1,cid=0,active=2,type=IP * ifname=rmnet_usb0,address=10.181.235.154/30, * dns=172.16.145.103 172.16.145.103,gateways=NULL]} */ static const guchar reply_data_call_invalid_parcel6[] = { 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x5f, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x38, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x20, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; static const struct ril_msg reply_data_call_invalid_6 = { .buf = (gchar *) &reply_data_call_invalid_parcel6, .buf_len = sizeof(reply_data_call_invalid_parcel6), .unsolicited = FALSE, .req = RIL_REQUEST_SETUP_DATA_CALL, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_REQUEST_SETUP_DATA_CALL reply with the * following parameters: * * {version=7,num=1 [status=0,retry=-1,cid=0,active=2,type=IP, * ifname=rmnet_usb0,address=10.181.235.154/30, * dns=172.16.145.103 172.16.145.103,gateways=10.181.235.153]} */ static const guchar reply_data_call_valid_parcel1[] = { 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x5f, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x38, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x20, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x38, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_data_call_valid_1 = { .buf = (gchar *) &reply_data_call_valid_parcel1, .buf_len = sizeof(reply_data_call_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_SETUP_DATA_CALL, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing an valid RIL_REQUEST_SETUP_DATA_CALL reply with * a NULL string specified for 'dns' (note that some data calls without DNS * information might still be rejected by the GPRS context driver): * * {version=7,num=1 [status=0,retry=-1,cid=0,active=2,type=IP * ifname=rmnet_usb0,address=10.181.235.154/30, * dns=NULL,gateways=10.181.235.153]} */ static const guchar reply_data_call_valid_parcel2[] = { 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x5f, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x38, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x38, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x35, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_data_call_valid_2 = { .buf = (gchar *) &reply_data_call_valid_parcel2, .buf_len = sizeof(reply_data_call_valid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_SETUP_DATA_CALL, .serial_no = 0, .error = 0, }; static const struct ril_msg unsol_data_call_list_changed_invalid_1 = { .buf = "", .buf_len = 0, .unsolicited = TRUE, .req = RIL_UNSOL_DATA_CALL_LIST_CHANGED, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_DATA_CALL_LIST_CHANGED message * with the following parameters: * * (version=7,num=1 [status=0,retry=-1,cid=0,active=1,type=IP, * ifname=rmnet_usb0,address=10.209.114.102/30, * dns=172.16.145.103 172.16.145.103,gateways=10.209.114.101]} */ static const char unsol_data_call_list_changed_parcel1[] = { 0x00, 0x00, 0x00, 0xd4, 0x01, 0x00, 0x00, 0x00, 0xf2, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x5f, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x30, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x32, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x20, 0x00, 0x31, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x33, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x00, 0x30, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_data_call_list_changed_valid_1 = { .buf = (gchar *) &unsol_data_call_list_changed_parcel1, .buf_len = sizeof(unsol_data_call_list_changed_parcel1), .unsolicited = TRUE, .req = RIL_UNSOL_DATA_CALL_LIST_CHANGED, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_RESPONSE_NEW_SMS message * with the following parameter (SMSC address length is 7): * * {07914306073011F0040B914336543980F50000310113212002400AC8373B0C6AD7DDE437} */ static const char unsol_response_new_sms_parcel1[] = { 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x39, 0x00, 0x31, 0x00, 0x34, 0x00, 0x33, 0x00, 0x30, 0x00, 0x36, 0x00, 0x30, 0x00, 0x37, 0x00, 0x33, 0x00, 0x30, 0x00, 0x31, 0x00, 0x31, 0x00, 0x46, 0x00, 0x30, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x42, 0x00, 0x39, 0x00, 0x31, 0x00, 0x34, 0x00, 0x33, 0x00, 0x33, 0x00, 0x36, 0x00, 0x35, 0x00, 0x34, 0x00, 0x33, 0x00, 0x39, 0x00, 0x38, 0x00, 0x30, 0x00, 0x46, 0x00, 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x31, 0x00, 0x30, 0x00, 0x31, 0x00, 0x31, 0x00, 0x33, 0x00, 0x32, 0x00, 0x31, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x30, 0x00, 0x30, 0x00, 0x41, 0x00, 0x43, 0x00, 0x38, 0x00, 0x33, 0x00, 0x37, 0x00, 0x33, 0x00, 0x42, 0x00, 0x30, 0x00, 0x43, 0x00, 0x36, 0x00, 0x41, 0x00, 0x44, 0x00, 0x37, 0x00, 0x44, 0x00, 0x44, 0x00, 0x45, 0x00, 0x34, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_response_new_sms_valid_1 = { .buf = (gchar *) &unsol_response_new_sms_parcel1, .buf_len = sizeof(unsol_response_new_sms_parcel1), .unsolicited = TRUE, .req = RIL_UNSOL_RESPONSE_NEW_SMS, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_RESPONSE_NEW_SMS message * with the following parameter (SMSC address length is 6): * * {069143060730F0040B914336543980F50000310113212002400AC8373B0C6AD7DDE437} */ static const char unsol_response_new_sms_parcel2[] = { 0x46, 0x00, 0x00, 0x00, 0x30, 0x00, 0x36, 0x00, 0x39, 0x00, 0x31, 0x00, 0x34, 0x00, 0x33, 0x00, 0x30, 0x00, 0x36, 0x00, 0x30, 0x00, 0x37, 0x00, 0x33, 0x00, 0x30, 0x00, 0x46, 0x00, 0x30, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x42, 0x00, 0x39, 0x00, 0x31, 0x00, 0x34, 0x00, 0x33, 0x00, 0x33, 0x00, 0x36, 0x00, 0x35, 0x00, 0x34, 0x00, 0x33, 0x00, 0x39, 0x00, 0x38, 0x00, 0x30, 0x00, 0x46, 0x00, 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x31, 0x00, 0x30, 0x00, 0x31, 0x00, 0x31, 0x00, 0x33, 0x00, 0x32, 0x00, 0x31, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x30, 0x00, 0x30, 0x00, 0x41, 0x00, 0x43, 0x00, 0x38, 0x00, 0x33, 0x00, 0x37, 0x00, 0x33, 0x00, 0x42, 0x00, 0x30, 0x00, 0x43, 0x00, 0x36, 0x00, 0x41, 0x00, 0x44, 0x00, 0x37, 0x00, 0x44, 0x00, 0x44, 0x00, 0x45, 0x00, 0x34, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_response_new_sms_valid_2 = { .buf = (gchar *) &unsol_response_new_sms_parcel2, .buf_len = sizeof(unsol_response_new_sms_parcel2), .unsolicited = TRUE, .req = RIL_UNSOL_RESPONSE_NEW_SMS, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_SUPP_SVC_NOTIFICATION message * with the following parameters: * * {1,2,0,0,} -> call has been put on hold */ static const guchar unsol_supp_svc_notif_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; static const struct ril_msg unsol_supp_svc_notif_valid_1 = { .buf = (gchar *) &unsol_supp_svc_notif_parcel1, .buf_len = sizeof(unsol_supp_svc_notif_parcel1), .unsolicited = TRUE, .req = RIL_UNSOL_SUPP_SVC_NOTIFICATION, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_SIGNAL_STRENGTH message * with the following parameters: * * (gw: 14, cdma: -1, evdo: -1, lte: 99) * * Note, the return value for gw sigmal is: (gw * 100) / 31, which * in this case equals 45. */ static const guchar unsol_signal_strength_parcel1[] = { 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x63, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f }; static const signal_strength_test unsol_signal_strength_valid_1 = { .strength = 45, .ril_tech = RADIO_TECH_GPRS, .msg = { .buf = (gchar *) &unsol_signal_strength_parcel1, .buf_len = sizeof(unsol_signal_strength_parcel1), .unsolicited = TRUE, .req = RIL_UNSOL_SIGNAL_STRENGTH, .serial_no = 0, .error = 0, } }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_SIGNAL_STRENGTH message * with the following parameters: * * (gw: 99, cdma: 0, evdo: 0, lte: 99) */ static const guchar unsol_signal_strength_parcel2[] = { 0x63, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0x2d, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const signal_strength_test unsol_signal_strength_valid_2 = { .strength = -1, .ril_tech = RADIO_TECH_GPRS, .msg = { .buf = (gchar *) &unsol_signal_strength_parcel2, .buf_len = sizeof(unsol_signal_strength_parcel2), .unsolicited = TRUE, .req = RIL_UNSOL_SIGNAL_STRENGTH, .serial_no = 0, .error = 0, } }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_ON_USSD message with the following * parameters: * * {0,Spain 12:56 09/12/13 Canary 11:56 09/12/13 } */ static const guchar unsol_on_ussd_parcel1[] = { 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x53, 0x00, 0x70, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x31, 0x00, 0x32, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x36, 0x00, 0x20, 0x00, 0x30, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x33, 0x00, 0x20, 0x00, 0x20, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x20, 0x00, 0x31, 0x00, 0x31, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x36, 0x00, 0x20, 0x00, 0x30, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x33, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_on_ussd_valid_1 = { .buf = (gchar *) &unsol_on_ussd_parcel1, .buf_len = sizeof(unsol_on_ussd_parcel1), .unsolicited = TRUE, .req = RIL_UNSOL_ON_USSD, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_RIL_CONNECTED message with the * following parameters: * * [1, 10] */ static const guchar unsol_on_ril_connected_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_on_ril_connected_valid_1 = { .buf = (gchar *) &unsol_on_ril_connected_valid_parcel1, .buf_len = sizeof(unsol_on_ril_connected_valid_parcel1), .unsolicited = TRUE, .req = RIL_UNSOL_RIL_CONNECTED, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_RIL_CONNECTED message with the * following parameters: * * [2, 10, 11] */ static const guchar unsol_on_ril_connected_valid_parcel2[] = { 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_on_ril_connected_valid_2 = { .buf = (gchar *) &unsol_on_ril_connected_valid_parcel2, .buf_len = sizeof(unsol_on_ril_connected_valid_parcel2), .unsolicited = TRUE, .req = RIL_UNSOL_RIL_CONNECTED, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_RIL_CONNECTED message with the * following parameters: * * [1, 10, 11] */ static const guchar unsol_on_ril_connected_valid_parcel3[] = { 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_on_ril_connected_valid_3 = { .buf = (gchar *) &unsol_on_ril_connected_valid_parcel3, .buf_len = sizeof(unsol_on_ril_connected_valid_parcel3), .unsolicited = TRUE, .req = RIL_UNSOL_RIL_CONNECTED, .serial_no = 0, .error = 0, }; static const struct ril_msg unsol_on_ril_connected_invalid_1 = { .buf = "", .buf_len = 0, .unsolicited = TRUE, .req = RIL_UNSOL_RIL_CONNECTED, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing an invalid RIL_UNSOL_RIL_CONNECTED message with the * following parameters: * * [0] */ static const guchar unsol_on_ril_connected_invalid_parcel2[] = { 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_on_ril_connected_invalid_2 = { .buf = (gchar *) &unsol_on_ril_connected_invalid_parcel2, .buf_len = sizeof(unsol_on_ril_connected_invalid_parcel2), .unsolicited = TRUE, .req = RIL_UNSOL_RIL_CONNECTED, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing an invalid RIL_UNSOL_RIL_CONNECTED message with the * following parameters: * * [1] */ static const guchar unsol_on_ril_connected_invalid_parcel3[] = { 0x01, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_on_ril_connected_invalid_3 = { .buf = (gchar *) &unsol_on_ril_connected_invalid_parcel3, .buf_len = sizeof(unsol_on_ril_connected_invalid_parcel3), .unsolicited = TRUE, .req = RIL_UNSOL_RIL_CONNECTED, .serial_no = 0, .error = 0, }; static void test_reply_data_call_invalid(gconstpointer data) { struct ril_data_call_list *call_list; call_list = g_ril_unsol_parse_data_call_list(NULL, data); g_assert(call_list == NULL); } static void test_reply_data_call_valid(gconstpointer data) { struct ril_data_call_list *call_list = g_ril_unsol_parse_data_call_list(NULL, data); g_assert(call_list != NULL); g_ril_unsol_free_data_call_list(call_list); } static void test_unsol_data_call_list_changed_invalid(gconstpointer data) { struct ril_data_call_list *unsol; unsol = g_ril_unsol_parse_data_call_list(NULL, data); g_assert(unsol == NULL); } static void test_unsol_data_call_list_changed_valid(gconstpointer data) { struct ril_data_call_list *unsol; unsol = g_ril_unsol_parse_data_call_list(NULL, data); g_assert(unsol != NULL); g_ril_unsol_free_data_call_list(unsol); } static void test_signal_strength_valid(gconstpointer data) { const signal_strength_test *test = data; int strength = g_ril_unsol_parse_signal_strength(NULL, &test->msg, test->ril_tech); g_assert(strength == test->strength); } static void test_unsol_response_new_sms_valid(gconstpointer data) { struct unsol_sms_data *sms_data; sms_data = g_ril_unsol_parse_new_sms(NULL, data); g_assert(sms_data != NULL); g_assert(sms_data->data != NULL); g_assert(sms_data->length > 0); g_ril_unsol_free_sms_data(sms_data); } static void test_unsol_supp_svc_notif_valid(gconstpointer data) { struct unsol_supp_svc_notif *unsol; unsol = g_ril_unsol_parse_supp_svc_notif(NULL, (struct ril_msg *) data); g_assert(unsol != NULL); g_ril_unsol_free_supp_svc_notif(unsol); } static void test_unsol_on_ussd_valid(gconstpointer data) { struct unsol_ussd *unsol; unsol = g_ril_unsol_parse_ussd(NULL, (struct ril_msg *) data); g_assert(unsol != NULL); g_ril_unsol_free_ussd(unsol); } static void test_unsol_on_ril_connected_valid(gconstpointer data) { int version; version = g_ril_unsol_parse_connected(NULL, (struct ril_msg *) data); g_assert(version == 10); } static void test_unsol_on_ril_connected_invalid(gconstpointer data) { int version; version = g_ril_unsol_parse_connected(NULL, (struct ril_msg *) data); g_assert(version == RIL_VERSION_UNSPECIFIED); } #endif int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN g_test_add_data_func("/testgrilunsol/gprs-context: " "invalid DATA_CALL_LIST_CHANGED Test 1", &unsol_data_call_list_changed_invalid_1, test_unsol_data_call_list_changed_invalid); g_test_add_data_func("/testgrilunsol/gprs-context: " "valid DATA_CALL_LIST_CHANGED Test 1", &unsol_data_call_list_changed_valid_1, test_unsol_data_call_list_changed_valid); g_test_add_data_func("/testgrilunsol/gprs-context: " "invalid SETUP_DATA_CALL Test 1", &reply_data_call_invalid_1, test_reply_data_call_invalid); g_test_add_data_func("/testgrilunsol/gprs-context: " "invalid SETUP_DATA_CALL Test 2", &reply_data_call_invalid_2, test_reply_data_call_invalid); g_test_add_data_func("/testgrilunsol/gprs-context: " "invalid SETUP_DATA_CALL Test 3", &reply_data_call_invalid_3, test_reply_data_call_invalid); g_test_add_data_func("/testgrilunsol/gprs-context: " "invalid SETUP_DATA_CALL Test 4", &reply_data_call_invalid_4, test_reply_data_call_invalid); g_test_add_data_func("/testgrilunsol/gprs-context: " "invalid SETUP_DATA_CALL Test 5", &reply_data_call_invalid_5, test_reply_data_call_invalid); g_test_add_data_func("/testgrilunsol/gprs-context: " "invalid SETUP_DATA_CALL Test 6", &reply_data_call_invalid_6, test_reply_data_call_invalid); g_test_add_data_func("/testgrilunsol/gprs-context: " "valid SETUP_DATA_CALL Test 1", &reply_data_call_valid_1, test_reply_data_call_valid); g_test_add_data_func("/testgrilunsol/gprs-context: " "valid SETUP_DATA_CALL Test 2", &reply_data_call_valid_2, test_reply_data_call_valid); g_test_add_data_func("/testgrilunsol/sms: " "valid RESPONSE_NEW_SMS Test 1", &unsol_response_new_sms_valid_1, test_unsol_response_new_sms_valid); g_test_add_data_func("/testgrilunsol/sms: " "valid RESPONSE_NEW_SMS Test 2", &unsol_response_new_sms_valid_2, test_unsol_response_new_sms_valid); g_test_add_data_func("/testgrilunsol/voicecall: " "valid SUPP_SVC_NOTIF Test 1", &unsol_supp_svc_notif_valid_1, test_unsol_supp_svc_notif_valid); g_test_add_data_func("/testgrilunsol/voicecall: " "valid SIGNAL_STRENGTH Test 1", &unsol_signal_strength_valid_1, test_signal_strength_valid); g_test_add_data_func("/testgrilunsol/voicecall: " "valid SIGNAL_STRENGTH Test 2", &unsol_signal_strength_valid_2, test_signal_strength_valid); g_test_add_data_func("/testgrilunsol/ussd: " "valid ON_USSD Test 1", &unsol_on_ussd_valid_1, test_unsol_on_ussd_valid); g_test_add_data_func("/testgrilunsol/ril-connected: " "valid RIL_CONNECTED Test 1", &unsol_on_ril_connected_valid_1, test_unsol_on_ril_connected_valid); g_test_add_data_func("/testgrilunsol/ril-connected: " "valid RIL_CONNECTED Test 2", &unsol_on_ril_connected_valid_2, test_unsol_on_ril_connected_valid); g_test_add_data_func("/testgrilunsol/ril-connected: " "valid RIL_CONNECTED Test 3", &unsol_on_ril_connected_valid_3, test_unsol_on_ril_connected_valid); g_test_add_data_func("/testgrilunsol/ril-connected: " "invalid RIL_CONNECTED Test 1", &unsol_on_ril_connected_invalid_1, test_unsol_on_ril_connected_invalid); g_test_add_data_func("/testgrilunsol/ril-connected: " "invalid RIL_CONNECTED Test 2", &unsol_on_ril_connected_invalid_2, test_unsol_on_ril_connected_invalid); g_test_add_data_func("/testgrilunsol/ril-connected: " "invalid RIL_CONNECTED Test 3", &unsol_on_ril_connected_invalid_3, test_unsol_on_ril_connected_invalid); #endif return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-mtkunsol.c0000644000015600001650000001372212671500024022360 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2014 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include "gril.h" #include "grilunsol.h" #include "drivers/mtkmodem/mtk_constants.h" #include "drivers/mtkmodem/mtkunsol.h" /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN /* * The following hexadecimal data represents a serialized Binder parcel instance * containing a valid MTK_RIL_UNSOL_INCOMING_CALL_INDICATION message with the * following parameters: * * {1,677777777,161,0,1} */ static const guchar unsol_incoming_call_indication_parcel1[] = { 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x36, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x37, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x36, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_incoming_call_indication_valid_1 = { .buf = (gchar *) &unsol_incoming_call_indication_parcel1, .buf_len = sizeof(unsol_incoming_call_indication_parcel1), .unsolicited = TRUE, .req = MTK_RIL_UNSOL_INCOMING_CALL_INDICATION, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel instance * containing a valid MTK_RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED message with * the following parameters: * * {1} */ static const guchar unsol_registration_suspended_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_registration_suspended_valid_1 = { .buf = (gchar *) &unsol_registration_suspended_parcel1, .buf_len = sizeof(unsol_registration_suspended_parcel1), .unsolicited = TRUE, .req = MTK_RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel instance * containing a valid MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED message with the * following parameters: * * {21401} */ static const guchar unsol_plmn_changed_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x32, 0x00, 0x31, 0x00, 0x34, 0x00, 0x30, 0x00, 0x31, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_plmn_changed_valid_1 = { .buf = (gchar *) &unsol_plmn_changed_parcel1, .buf_len = sizeof(unsol_plmn_changed_parcel1), .unsolicited = TRUE, .req = MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data represents a serialized Binder parcel instance * containing a MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED message with no strings. */ static const guchar unsol_plmn_changed_parcel2[] = { 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg unsol_plmn_changed_invalid_1 = { .buf = (gchar *) &unsol_plmn_changed_parcel2, .buf_len = sizeof(unsol_plmn_changed_parcel2), .unsolicited = TRUE, .req = MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED, .serial_no = 0, .error = 0, }; static void test_unsol_incoming_call_indication_valid(gconstpointer data) { struct unsol_call_indication *unsol; unsol = g_mtk_unsol_parse_incoming_call_indication(NULL, (struct ril_msg *) data); g_assert(unsol != NULL); g_mtk_unsol_free_call_indication(unsol); } static void test_unsol_registration_suspended_valid(gconstpointer data) { int suspended; suspended = g_mtk_unsol_parse_registration_suspended(NULL, (struct ril_msg *) data); g_assert(suspended > 0); } static void test_unsol_plmn_changed_valid(gconstpointer data) { struct parcel_str_array *plmns; plmns = g_mtk_unsol_parse_plmn_changed(NULL, (struct ril_msg *) data); g_assert(plmns != NULL); parcel_free_str_array(plmns); } static void test_unsol_plmn_changed_invalid(gconstpointer data) { struct parcel_str_array *plmns; plmns = g_mtk_unsol_parse_plmn_changed(NULL, (struct ril_msg *) data); g_assert(plmns == NULL); } #endif /* LITTLE_ENDIAN */ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN g_test_add_data_func("/testmtkunsol/voicecall: " "valid INCOMING_CALL_INDICATION Test 1", &unsol_incoming_call_indication_valid_1, test_unsol_incoming_call_indication_valid); g_test_add_data_func("/testmtkunsol/network: " "valid RESPONSE_REGISTRATION_SUSPENDED Test 1", &unsol_registration_suspended_valid_1, test_unsol_registration_suspended_valid); g_test_add_data_func("/testmtkunsol/fw: " "valid RESPONSE_PLMN_CHANGED Test 1", &unsol_plmn_changed_valid_1, test_unsol_plmn_changed_valid); g_test_add_data_func("/testmtkunsol/fw: " "invalid RESPONSE_PLMN_CHANGED Test 1", &unsol_plmn_changed_invalid_1, test_unsol_plmn_changed_invalid); #endif /* LITTLE_ENDIAN */ return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-rilmodem-cs.c0000644000015600001650000003560112671500073022723 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2015 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "ril_constants.h" #include "rilmodem-test-server.h" static GMainLoop *mainloop; static const struct ofono_call_settings_driver *csdriver; struct rilmodem_cs_data { GRil *ril; struct ofono_modem *modem; gconstpointer test_data; struct ofono_call_settings *cs; struct server_data *serverd; }; typedef gboolean (*StartFunc)(gpointer data); struct cs_data { StartFunc start_func; gint param_int1; gint param_int2; struct rilmodem_test_data rtd; enum ofono_error_type error_type; gint cb_int1; gint cb_int2; }; static void status_query_callback(const struct ofono_error *error, int status, gpointer data) { struct rilmodem_cs_data *rcd = data; const struct cs_data *csd = rcd->test_data; g_assert(error->type == csd->error_type); if (error->type == OFONO_ERROR_TYPE_NO_ERROR) g_assert(status == csd->cb_int1); g_main_loop_quit(mainloop); } static void clir_query_callback(const struct ofono_error *error, int override, int network, gpointer data) { struct rilmodem_cs_data *rcd = data; const struct cs_data *csd = rcd->test_data; g_assert(error->type == csd->error_type); if (error->type == OFONO_ERROR_TYPE_NO_ERROR) { g_assert(override == csd->cb_int1); g_assert(network == csd->cb_int2); } g_main_loop_quit(mainloop); } static void set_callback(const struct ofono_error *error, gpointer data) { struct rilmodem_cs_data *rcd = data; const struct cs_data *csd = rcd->test_data; g_assert(error->type == csd->error_type); g_main_loop_quit(mainloop); } static gboolean trigger_clip_query(gpointer data) { struct rilmodem_cs_data *rcd = data; g_assert(csdriver->clip_query != NULL); csdriver->clip_query(rcd->cs, status_query_callback, rcd); return FALSE; } static gboolean trigger_cw_query(gpointer data) { struct rilmodem_cs_data *rcd = data; g_assert(csdriver->cw_query != NULL); /* cls is explicitly ignored by rilmodem; just use 0 */ csdriver->cw_query(rcd->cs, 0, status_query_callback, rcd); return FALSE; } static gboolean trigger_cw_set(gpointer data) { struct rilmodem_cs_data *rcd = data; const struct cs_data *csd = rcd->test_data; g_assert(csdriver->cw_set != NULL); csdriver->cw_set(rcd->cs, csd->param_int1, csd->param_int2, set_callback, rcd); return FALSE; } static gboolean trigger_clir_query(gpointer data) { struct rilmodem_cs_data *rcd = data; g_assert(csdriver->clir_query != NULL); csdriver->clir_query(rcd->cs, clir_query_callback, rcd); return FALSE; } static gboolean trigger_clir_set(gpointer data) { struct rilmodem_cs_data *rcd = data; const struct cs_data *csd = rcd->test_data; g_assert(csdriver->clir_set != NULL); csdriver->clir_set(rcd->cs, csd->param_int1, set_callback, rcd); return FALSE; } /* RIL_REQUEST_QUERY_CLIP */ static const guchar req_clip_query_parcel_1[] = { 0x00, 0x00, 0x00, 0x08, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* reply data for QUErY_CLIP: 0x01 = 'CLIP provisioned' */ static const guchar rsp_clip_query_data_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct cs_data testdata_clip_query_valid_1 = { .start_func = trigger_clip_query, .rtd = { .req_data = req_clip_query_parcel_1, .req_size = sizeof(req_clip_query_parcel_1), .rsp_data = rsp_clip_query_data_1, .rsp_size = sizeof(rsp_clip_query_data_1), .rsp_error = RIL_E_SUCCESS, }, .cb_int1 = 1, .error_type = OFONO_ERROR_TYPE_NO_ERROR, }; /* reply data for QUErY_CLIP: invalid num_params=0x02' */ static const guchar rsp_clip_query_data_2[] = { 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; /* reply parse error causes status to be returned as -1 */ static const struct cs_data testdata_clip_query_invalid_1 = { .start_func = trigger_clip_query, .rtd = { .req_data = req_clip_query_parcel_1, .req_size = sizeof(req_clip_query_parcel_1), .rsp_data = rsp_clip_query_data_2, .rsp_size = sizeof(rsp_clip_query_data_2), .rsp_error = RIL_E_SUCCESS, }, .cb_int1 = -1, .error_type = OFONO_ERROR_TYPE_NO_ERROR, }; /* error triggered by RIL reply error */ static const struct cs_data testdata_clip_query_invalid_2 = { .start_func = trigger_clip_query, .rtd = { .req_data = req_clip_query_parcel_1, .req_size = sizeof(req_clip_query_parcel_1), .rsp_error = RIL_E_GENERIC_FAILURE, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* RIL_REQUEST_QUERY_CALL_WAITING */ static const guchar req_cw_query_parcel_1[] = { 0x00, 0x00, 0x00, 0x10, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* reply data for QUErY_CALL_WAITING: 1='enabled' 3='data|voice' */ static const guchar rsp_cw_query_data_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3, 0x00, 0x00, 0x00 }; static const struct cs_data testdata_cw_query_valid_1 = { .start_func = trigger_cw_query, .rtd = { .req_data = req_cw_query_parcel_1, .req_size = sizeof(req_cw_query_parcel_1), .rsp_data = rsp_cw_query_data_1, .rsp_size = sizeof(rsp_cw_query_data_1), .rsp_error = RIL_E_SUCCESS, }, .cb_int1 = 3, .error_type = OFONO_ERROR_TYPE_NO_ERROR, }; /* reply data for QUErY_CALL_WAITING: invalid num_params=0x00' */ static const guchar rsp_cw_query_data_2[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; /* reply parse error causes status to be returned as -1 */ static const struct cs_data testdata_cw_query_invalid_1 = { .start_func = trigger_cw_query, .rtd = { .req_data = req_cw_query_parcel_1, .req_size = sizeof(req_cw_query_parcel_1), .rsp_data = rsp_cw_query_data_2, .rsp_size = sizeof(rsp_cw_query_data_2), .rsp_error = RIL_E_SUCCESS, }, .cb_int1 = -1, .error_type = OFONO_ERROR_TYPE_NO_ERROR, }; /* GENERIC_FAILURE returned in RIL reply */ static const struct cs_data testdata_cw_query_invalid_2 = { .start_func = trigger_cw_query, .rtd = { .req_data = req_cw_query_parcel_1, .req_size = sizeof(req_cw_query_parcel_1), .rsp_data = rsp_cw_query_data_2, .rsp_size = sizeof(rsp_cw_query_data_2), .rsp_error = RIL_E_GENERIC_FAILURE, }, .cb_int1 = -1, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* RIL_REQUEST_SET_CALL_WAITING: enabled cls=BEARER_CLASS_DEFAULT (7) */ /* Note - driver atom checks for cls=7, and changes to cls=1 */ static const guchar req_cw_set_enabled_parcel_1[] = { 0x00, 0x00, 0x00, 0x14, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct cs_data testdata_cw_set_valid_1 = { .start_func = trigger_cw_set, .param_int1 = 1, .param_int2 = BEARER_CLASS_DEFAULT, .rtd = { .req_data = req_cw_set_enabled_parcel_1, .req_size = sizeof(req_cw_set_enabled_parcel_1), .rsp_error = RIL_E_SUCCESS, }, .error_type = OFONO_ERROR_TYPE_NO_ERROR, }; /* RIL_REQUEST_SET_CALL_WAITING: disabled cls=0 */ static const guchar req_cw_set_disabled_parcel_2[] = { 0x00, 0x00, 0x00, 0x14, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* GENERIC_FAILURE returned in RIL reply */ static const struct cs_data testdata_cw_set_invalid_1 = { .start_func = trigger_cw_set, .param_int1 = 0, .param_int2 = 0, .rtd = { .req_data = req_cw_set_disabled_parcel_2, .req_size = sizeof(req_cw_set_disabled_parcel_2), .rsp_error = RIL_E_GENERIC_FAILURE, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* RIL_REQUEST_GET_CLIR */ static const guchar req_clir_query_parcel_1[] = { 0x00, 0x00, 0x00, 0x08, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* reply data for GET_CLIR: m=2 n=4; see TS 27.007 Section 7.7 */ static const guchar rsp_clir_query_data_1[] = { 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 }; static const struct cs_data testdata_clir_query_valid_1 = { .start_func = trigger_clir_query, .rtd = { .req_data = req_clir_query_parcel_1, .req_size = sizeof(req_clir_query_parcel_1), .rsp_data = rsp_clir_query_data_1, .rsp_size = sizeof(rsp_clir_query_data_1), .rsp_error = RIL_E_SUCCESS, }, .cb_int1 = 2, .cb_int2 = 4, .error_type = OFONO_ERROR_TYPE_NO_ERROR, }; /* invalid reply data for GET_CLIR: num params is 3 instead of 2 */ static const guchar rsp_clir_query_data_2[] = { 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 }; static const struct cs_data testdata_clir_query_invalid_1 = { .start_func = trigger_clir_query, .rtd = { .req_data = req_clir_query_parcel_1, .req_size = sizeof(req_clir_query_parcel_1), .rsp_data = rsp_clir_query_data_2, .rsp_size = sizeof(rsp_clir_query_data_2), .rsp_error = RIL_E_SUCCESS, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* RIL_REQUEST_SET_CLIR: mode=DEFAULT */ static const guchar req_clir_set_mode0_parcel_1[] = { 0x00, 0x00, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct cs_data testdata_clir_set_valid_1 = { .start_func = trigger_clir_set, .param_int1 = OFONO_CLIR_OPTION_DEFAULT, .rtd = { .req_data = req_clir_set_mode0_parcel_1, .req_size = sizeof(req_clir_set_mode0_parcel_1), .rsp_error = RIL_E_SUCCESS, }, .error_type = OFONO_ERROR_TYPE_NO_ERROR, }; /* RIL_REQUEST_SET_CLIR: mode=INVOCATION */ static const guchar req_clir_set_mode0_parcel_2[] = { 0x00, 0x00, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; /* GENERIC_FAILURE returned in RIL reply */ static const struct cs_data testdata_clir_set_invalid_1 = { .start_func = trigger_clir_set, .param_int1 = OFONO_CLIR_OPTION_INVOCATION, .rtd = { .req_data = req_clir_set_mode0_parcel_2, .req_size = sizeof(req_clir_set_mode0_parcel_2), .rsp_error = RIL_E_GENERIC_FAILURE, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* Declarations && Re-implementations of core functions. */ void ril_call_settings_exit(void); void ril_call_settings_init(void); struct ofono_call_settings { void *driver_data; }; struct ofono_call_settings *ofono_call_settings_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct rilmodem_cs_data *rcd = data; struct ofono_call_settings *cs = g_new0(struct ofono_call_settings, 1); int retval; retval = csdriver->probe(cs, OFONO_RIL_VENDOR_AOSP, rcd->ril); g_assert(retval == 0); return cs; } int ofono_call_settings_driver_register(const struct ofono_call_settings_driver *d) { if (csdriver == NULL) csdriver = d; return 0; } void ofono_call_settings_set_data(struct ofono_call_settings *cs, void *data) { cs->driver_data = data; } void *ofono_call_settings_get_data(struct ofono_call_settings *cs) { return cs->driver_data; } void ofono_call_settings_register(struct ofono_call_settings *cs) { } void ofono_call_settings_driver_unregister(const struct ofono_call_settings_driver *d) { } /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN static void server_connect_cb(gpointer data) { struct rilmodem_cs_data *rcd = data; const struct cs_data *csd = rcd->test_data; /* This causes local impl of _create() to call driver's probe func. */ rcd->cs = ofono_call_settings_create(NULL, OFONO_RIL_VENDOR_AOSP, "rilmodem", rcd); /* add_idle doesn't work, read blocks main loop!!! */ g_assert(csd->start_func(rcd) == FALSE); } /* * This unit test: * - does some test data setup * - configures a dummy server socket * - creates a new gril client instance * - triggers a connect to the dummy * server socket * - starts a mainloop */ static void test_cs_func(gconstpointer data) { const struct cs_data *csd = data; struct rilmodem_cs_data *rcd; ril_call_settings_init(); rcd = g_new0(struct rilmodem_cs_data, 1); rcd->test_data = csd; rcd->serverd = rilmodem_test_server_create(&server_connect_cb, &csd->rtd, rcd); rcd->ril = g_ril_new(rilmodem_test_get_socket_name(rcd->serverd), OFONO_RIL_VENDOR_AOSP); g_assert(rcd->ril != NULL); mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); g_main_loop_unref(mainloop); csdriver->remove(rcd->cs); g_ril_unref(rcd->ril); g_free(rcd); rilmodem_test_server_close(rcd->serverd); ril_call_settings_exit(); } #endif int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN g_test_add_data_func("/testrilmodemcs/clip_query/valid/1", &testdata_clip_query_valid_1, test_cs_func); g_test_add_data_func("/testrilmodemcs/clip_query/invalid/1", &testdata_clip_query_invalid_1, test_cs_func); g_test_add_data_func("/testrilmodemcs/clip_query/invalid/2", &testdata_clip_query_invalid_2, test_cs_func); g_test_add_data_func("/testrilmodemcs/cw_query/valid/1", &testdata_cw_query_valid_1, test_cs_func); g_test_add_data_func("/testrilmodemcs/cw_query/invalid/1", &testdata_cw_query_invalid_1, test_cs_func); g_test_add_data_func("/testrilmodemcs/cw_query/invalid/2", &testdata_cw_query_invalid_2, test_cs_func); g_test_add_data_func("/testrilmodemcs/cw_set/valid/1", &testdata_cw_set_valid_1, test_cs_func); g_test_add_data_func("/testrilmodemcs/cw_set/invalid/1", &testdata_cw_set_invalid_1, test_cs_func); g_test_add_data_func("/testrilmodemcs/clir_query/valid/1", &testdata_clir_query_valid_1, test_cs_func); g_test_add_data_func("/testrilmodemcs/clir_query/invalid/1", &testdata_clir_query_invalid_1, test_cs_func); g_test_add_data_func("/testrilmodemcs/clir_set/valid/1", &testdata_clir_set_valid_1, test_cs_func); g_test_add_data_func("/testrilmodemcs/clir_set/invalid/1", &testdata_clir_set_invalid_1, test_cs_func); #endif return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-grilrequest.c0000644000015600001650000014476412671500024023065 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "grilrequest.h" /* * TODO: It may make sense to split this file into * domain-specific files ( eg. test-grilrequest-gprs-context.c ) * once more tests are added. */ /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN struct request_test_data { gconstpointer request; const guchar *parcel_data; gsize parcel_size; }; static const struct req_deactivate_data_call req_deact_data_call_invalid_1 = { .cid = 1, .reason = 10, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_REQUEST_DEACTIVATE_DATA_CALL message * with the following parameters: * * (cid=1,reason=0) */ static const guchar req_deact_data_call_valid_parcel1[] = { 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00 }; static const struct req_deactivate_data_call req_deact_data_call_valid_1 = { .cid = 1, .reason = RIL_DEACTIVATE_DATA_CALL_NO_REASON, }; static const struct request_test_data deact_data_call_valid_test_1 = { .request = &req_deact_data_call_valid_1, .parcel_data = (guchar *) &req_deact_data_call_valid_parcel1, .parcel_size = sizeof(req_deact_data_call_valid_parcel1), }; static const struct req_setup_data_call req_setup_data_call_invalid_1 = { .tech = RADIO_TECH_UNKNOWN, }; static const struct req_setup_data_call req_setup_data_call_invalid_2 = { .tech = 2112, }; static const struct req_setup_data_call req_setup_data_call_invalid_3 = { .tech = RADIO_TECH_GPRS, .data_profile = 5, }; static const struct req_setup_data_call req_setup_data_call_invalid_4 = { .tech = RADIO_TECH_GPRS, .data_profile = RIL_DATA_PROFILE_DEFAULT, .apn = NULL, }; static const struct req_setup_data_call req_setup_data_call_invalid_5 = { .tech = RADIO_TECH_GPRS, .data_profile = RIL_DATA_PROFILE_DEFAULT, .apn = "test.apn", .auth_type = 4, }; static const struct req_setup_data_call req_setup_data_call_invalid_6 = { .tech = RADIO_TECH_GPRS, .data_profile = RIL_DATA_PROFILE_DEFAULT, .apn = "test.apn", .auth_type = RIL_AUTH_BOTH, .protocol = 3, }; static const struct req_setup_data_call req_setup_data_call_valid_1 = { .tech = RADIO_TECH_GPRS, .data_profile = RIL_DATA_PROFILE_DEFAULT, .apn = "test.apn", .username = NULL, .password = NULL, .auth_type = RIL_AUTH_BOTH, .protocol = OFONO_GPRS_PROTO_IP, }; static const struct req_setup_data_call req_setup_data_call_valid_2 = { .tech = RADIO_TECH_GPRS, .data_profile = RIL_DATA_PROFILE_DEFAULT, .apn = "test.apn", .username = "", .password = "", .auth_type = RIL_AUTH_NONE, .protocol = OFONO_GPRS_PROTO_IP, }; static const struct req_setup_data_call req_setup_data_call_valid_3 = { .tech = RADIO_TECH_GPRS, .data_profile = RIL_DATA_PROFILE_DEFAULT, .apn = "test.apn", .username = "phablet", .password = "phablet", .auth_type = RIL_AUTH_BOTH, .protocol = OFONO_GPRS_PROTO_IPV4V6, }; static const struct req_setup_data_call req_setup_data_call_valid_4 = { .tech = RADIO_TECH_GPRS, .data_profile = RIL_DATA_PROFILE_DEFAULT, .apn = "test.apn", .username = "phablet", .password = "phablet", .auth_type = RIL_AUTH_BOTH, .protocol = OFONO_GPRS_PROTO_IPV6, }; static const struct req_setup_data_call req_setup_data_call_valid_5 = { .tech = RADIO_TECH_GPRS, .data_profile = RIL_DATA_PROFILE_DEFAULT, .apn = "", }; static const struct req_setup_data_call req_setup_data_call_valid_6 = { .tech = RADIO_TECH_GPRS, .data_profile = RIL_DATA_PROFILE_DEFAULT, .apn = "12345678901234567890123456789012345678901234567890" "123456789012345678901234567890123456789012345678901", }; static const char sim_read_info_path_valid_1[] = {0x3F, 0x00}; static const struct req_sim_read_info req_sim_read_info_valid_1 = { .app_type = RIL_APPTYPE_USIM, .aid_str = "1234567890123456", .fileid = 0x7F01, .path = (const unsigned char *) sim_read_info_path_valid_1, .path_len = sizeof(sim_read_info_path_valid_1), }; static const unsigned char sim_read_info_path_invalid_1[] = {0x3F, 0x00, 0x11, 0x22, 0x7F, 0x00, 0x11, 0x22}; static const struct req_sim_read_info req_sim_read_info_invalid_1 = { .app_type = RIL_APPTYPE_ISIM + 10, .aid_str = "1234567890123456", .fileid = 0x7F01, .path = sim_read_info_path_invalid_1, .path_len = sizeof(sim_read_info_path_invalid_1), }; /* sim_read_binary tests */ static const guchar req_sim_read_binary_parcel_valid_1[] = { 0xb0, 0x00, 0x00, 0x00, 0xe2, 0x2f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x33, 0x00, 0x46, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const unsigned char sim_read_binary_path_valid_1[] = {0x3F, 0x00}; static const struct req_sim_read_binary req_sim_read_binary_valid_1 = { .app_type = RIL_APPTYPE_UNKNOWN, .aid_str = "", .fileid = 0x2FE2, .path = sim_read_binary_path_valid_1, .path_len = sizeof(sim_read_binary_path_valid_1), .start = 0, .length = 0x0A, }; static const struct request_test_data sim_read_binary_valid_test_1 = { .request = &req_sim_read_binary_valid_1, .parcel_data = (guchar *) &req_sim_read_binary_parcel_valid_1, .parcel_size = sizeof(req_sim_read_binary_parcel_valid_1), }; /* sim_read_record tests */ static const guchar req_sim_read_record_parcel_valid_1[] = { 0xb2, 0x00, 0x00, 0x00, 0xe2, 0x2f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x33, 0x00, 0x46, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const unsigned char sim_read_record_path_valid_1[] = {0x3F, 0x00}; static const struct req_sim_read_record req_sim_read_record_valid_1 = { .app_type = RIL_APPTYPE_UNKNOWN, .aid_str = "", .fileid = 0x2FE2, .path = sim_read_record_path_valid_1, .path_len = sizeof(sim_read_record_path_valid_1), .record = 5, .length = 0x0A, }; static const struct request_test_data sim_read_record_valid_test_1 = { .request = &req_sim_read_record_valid_1, .parcel_data = (guchar *) &req_sim_read_record_parcel_valid_1, .parcel_size = sizeof(req_sim_read_record_parcel_valid_1), }; /* sim_write_binary tests */ static const guchar req_sim_write_binary_parcel_valid_1[] = { 0xd6, 0x00, 0x00, 0x00, 0xcb, 0x6f, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x33, 0x00, 0x46, 0x00, 0x30, 0x00, 0x30, 0x00, 0x37, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x38, 0x00, 0x30, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x61, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x37, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, 0x32, 0x00, 0x66, 0x00, 0x66, 0x00, 0x34, 0x00, 0x34, 0x00, 0x66, 0x00, 0x66, 0x00, 0x31, 0x00, 0x32, 0x00, 0x38, 0x00, 0x39, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char sim_write_binary_path_valid_1[] = { 0x3F, 0x00, 0x7F, 0xFF }; static const struct req_sim_write_binary req_sim_write_binary_valid_1 = { .app_type = RIL_APPTYPE_UNKNOWN, .aid_str = "a0000000871002ff44ff128900000100", .fileid = 0x6FCB, .path = sim_write_binary_path_valid_1, .path_len = sizeof(sim_write_binary_path_valid_1), .start = 0, .length = 16, .data = (unsigned char[]) { 0x01, 0x00, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } }; static const struct request_test_data sim_write_binary_valid_test_1 = { .request = &req_sim_write_binary_valid_1, .parcel_data = (guchar *) &req_sim_write_binary_parcel_valid_1, .parcel_size = sizeof(req_sim_write_binary_parcel_valid_1), }; /* sim_write_record tests */ static const guchar req_sim_write_record_parcel_valid_1[] = { 0xdc, 0x00, 0x00, 0x00, 0xcb, 0x6f, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x33, 0x00, 0x46, 0x00, 0x30, 0x00, 0x30, 0x00, 0x37, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x38, 0x00, 0x30, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x61, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x37, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, 0x32, 0x00, 0x66, 0x00, 0x66, 0x00, 0x34, 0x00, 0x34, 0x00, 0x66, 0x00, 0x66, 0x00, 0x31, 0x00, 0x32, 0x00, 0x38, 0x00, 0x39, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char sim_write_record_path_valid_1[] = {0x3F, 0x00, 0x7F, 0xFF}; static const struct req_sim_write_record req_sim_write_record_valid_1 = { .app_type = RIL_APPTYPE_UNKNOWN, .aid_str = "a0000000871002ff44ff128900000100", .fileid = 0x6FCB, .path = sim_write_record_path_valid_1, .path_len = sizeof(sim_write_record_path_valid_1), .mode = GRIL_REC_ACCESS_MODE_ABSOLUTE, .record = 1, .length = 16, .data = (unsigned char[]) { 0x01, 0x00, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } }; static const struct request_test_data sim_write_record_valid_test_1 = { .request = &req_sim_write_record_valid_1, .parcel_data = (guchar *) &req_sim_write_record_parcel_valid_1, .parcel_size = sizeof(req_sim_write_record_parcel_valid_1), }; /* read_imsi tests */ static const guchar req_read_imsi_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const char read_imsi_aid_str_1[] = ""; static const struct request_test_data read_imsi_valid_test_1 = { .request = &read_imsi_aid_str_1, .parcel_data = (guchar *) &req_read_imsi_parcel_valid_1, .parcel_size = sizeof(req_read_imsi_parcel_valid_1), }; /* pin_send tests */ struct request_test_pin_send_data { const char *passwd; const gchar *aid_str; guchar *parcel_data; gsize parcel_size; }; static const guchar req_pin_send_parcel_valid_1[] = { 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const struct request_test_pin_send_data pin_send_record_valid_test_1 = { .passwd = "1234", .aid_str = "", .parcel_data = (guchar *) &req_pin_send_parcel_valid_1, .parcel_size = sizeof(req_pin_send_parcel_valid_1), }; /* pin_change_state tests */ static const guchar req_pin_change_state_valid_1[] = { 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, }; static const struct req_pin_change_state req_pin_change_state_valid1 = { .aid_str = NULL, .passwd_type = OFONO_SIM_PASSWORD_PHSIM_PIN, .enable = 0, .passwd = "1234", }; static const struct request_test_data pin_change_state_valid_test_1 = { .request = &req_pin_change_state_valid1, .parcel_data = (guchar *) &req_pin_change_state_valid_1, .parcel_size = sizeof(req_pin_change_state_valid_1), }; /* pin_send_puk tests */ struct request_test_pin_send_puk_data { const char *puk; const char *passwd; const gchar *aid_str; const guchar *parcel_data; gsize parcel_size; }; static const guchar req_pin_send_puk_parcel_valid_1[] = { 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const struct request_test_pin_send_puk_data pin_send_puk_valid_test_1 = { .puk = "12345678", .passwd = "1234", .aid_str = "", .parcel_data = req_pin_send_puk_parcel_valid_1, .parcel_size = sizeof(req_pin_send_puk_parcel_valid_1), }; /* change_passwd tests */ struct request_test_change_passwd_data { const char *old_passwd; const char *new_passwd; const gchar *aid_str; const guchar *parcel_data; gsize parcel_size; }; static const guchar req_change_passwd_parcel_valid_1[] = { 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const struct request_test_change_passwd_data change_passwd_valid_test_1 = { .old_passwd = "1234", .new_passwd = "5678", .aid_str = "", .parcel_data = req_change_passwd_parcel_valid_1, .parcel_size = sizeof(req_change_passwd_parcel_valid_1), }; /* sms_cmgs tests */ static const unsigned char req_sms_cmgs_pdu_valid_1[] = { 0x00, 0x11, 0x00, 0x09, 0x81, 0x36, 0x54, 0x39, 0x80, 0xf5, 0x00, 0x00, 0xa7, 0x0a, 0xc8, 0x37, 0x3b, 0x0c, 0x6a, 0xd7, 0xdd, 0xe4, 0x37 }; static const guchar req_sms_cmgs_parcel_valid_1[] = { 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x39, 0x00, 0x38, 0x00, 0x31, 0x00, 0x33, 0x00, 0x36, 0x00, 0x35, 0x00, 0x34, 0x00, 0x33, 0x00, 0x39, 0x00, 0x38, 0x00, 0x30, 0x00, 0x46, 0x00, 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x41, 0x00, 0x37, 0x00, 0x30, 0x00, 0x41, 0x00, 0x43, 0x00, 0x38, 0x00, 0x33, 0x00, 0x37, 0x00, 0x33, 0x00, 0x42, 0x00, 0x30, 0x00, 0x43, 0x00, 0x36, 0x00, 0x41, 0x00, 0x44, 0x00, 0x37, 0x00, 0x44, 0x00, 0x44, 0x00, 0x45, 0x00, 0x34, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct req_sms_cmgs req_sms_cmgs_valid1 = { .pdu = req_sms_cmgs_pdu_valid_1, .pdu_len = sizeof(req_sms_cmgs_pdu_valid_1), .tpdu_len = sizeof(req_sms_cmgs_pdu_valid_1) - 1, }; static const struct request_test_data sms_cmgs_valid_test_1 = { .request = &req_sms_cmgs_valid1, .parcel_data = (guchar *) &req_sms_cmgs_parcel_valid_1, .parcel_size = sizeof(req_sms_cmgs_parcel_valid_1), }; /* sms_acknowledge tests */ static const guchar req_sms_acknowledge_parcel_valid_1[] = { 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct request_test_data sms_acknowledge_valid_test_1 = { .request = NULL, .parcel_data = (guchar *) &req_sms_acknowledge_parcel_valid_1, .parcel_size = sizeof(req_sms_acknowledge_parcel_valid_1), }; /* set_smsc_address tests */ static const guchar req_set_smsc_address_valid_parcel1[] = { 0x0e, 0x00, 0x00, 0x00, 0x22, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x34, 0x00, 0x36, 0x00, 0x30, 0x00, 0x37, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ofono_phone_number smsc_address_valid1 = { .type = 145, .number = "34607003110" }; static const struct request_test_data smsc_address_valid_test_1 = { .request = &smsc_address_valid1, .parcel_data = (guchar *) &req_set_smsc_address_valid_parcel1, .parcel_size = sizeof(req_set_smsc_address_valid_parcel1), }; /* dial tests */ struct request_test_dial_data { const struct ofono_phone_number ph; enum ofono_clir_option clir; const guchar *parcel_data; size_t parcel_size; }; static const guchar req_dial_parcel_valid_1[] = { 0x09, 0x00, 0x00, 0x00, 0x39, 0x00, 0x31, 0x00, 0x37, 0x00, 0x35, 0x00, 0x32, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const struct request_test_dial_data dial_valid_test_1 = { .ph = { .number = "917525555", .type = 129 }, .clir = OFONO_CLIR_OPTION_DEFAULT, .parcel_data = req_dial_parcel_valid_1, .parcel_size = sizeof(req_dial_parcel_valid_1), }; /* hangup tests */ static const guchar req_hangup_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static unsigned hangup_call_id_valid_1 = 1; static const struct request_test_data set_hangup_valid_test_1 = { .request = &hangup_call_id_valid_1, .parcel_data = req_hangup_parcel_valid_1, .parcel_size = sizeof(req_hangup_parcel_valid_1), }; /* dtmf tests */ static const guchar req_dtmf_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00 }; static char dtmf_char_valid_1 = '4'; static const struct request_test_data dtmf_valid_test_1 = { .request = &dtmf_char_valid_1, .parcel_data = req_dtmf_parcel_valid_1, .parcel_size = sizeof(req_dtmf_parcel_valid_1), }; /* separate_conn tests */ static const guchar req_separate_conn_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static unsigned separate_conn_call_id_valid_1 = 1; static const struct request_test_data separate_conn_valid_test_1 = { .request = &separate_conn_call_id_valid_1, .parcel_data = req_separate_conn_parcel_valid_1, .parcel_size = sizeof(req_separate_conn_parcel_valid_1), }; /* set_supp_svc_notif tests */ static const guchar req_set_supp_svc_notif_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct request_test_data set_supp_svc_notif_valid_test_1 = { .request = NULL, .parcel_data = req_set_supp_svc_notif_parcel_valid_1, .parcel_size = sizeof(req_set_supp_svc_notif_parcel_valid_1), }; /* set_mute tests */ static const int mute_off = 0; static const int mute_on = 1; static const guchar req_set_mute_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct request_test_data set_mute_valid_test_1 = { .request = &mute_off, .parcel_data = (guchar *) &req_set_mute_valid_parcel1, .parcel_size = sizeof(req_set_mute_valid_parcel1), }; static const guchar req_set_mute_valid_parcel2[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct request_test_data set_mute_valid_test_2 = { .request = &mute_on, .parcel_data = (guchar *) &req_set_mute_valid_parcel2, .parcel_size = sizeof(req_set_mute_valid_parcel2), }; /* send_ussd tests */ static const guchar req_send_ussd_parcel_valid_1[] = { 0x05, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x31, 0x00, 0x38, 0x00, 0x23, 0x00, 0x00, 0x00 }; static const struct request_test_data send_ussd_valid_test_1 = { .request = "*118#", .parcel_data = req_send_ussd_parcel_valid_1, .parcel_size = sizeof(req_send_ussd_parcel_valid_1), }; /* set_call_waiting tests */ struct request_test_set_call_waiting { int enabled; int serviceclass; const char *parcel_data; size_t parcel_size; }; static const char req_set_call_waiting_parcel_valid_1[] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct request_test_set_call_waiting set_call_waiting_valid_test_1 = { .enabled = 0, .serviceclass = 0x01, .parcel_data = req_set_call_waiting_parcel_valid_1, .parcel_size = sizeof(req_set_call_waiting_parcel_valid_1), }; /* query_call_waiting tests */ const int query_call_waiting_mode_0 = 0; static const guchar req_query_call_waiting_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct request_test_data query_call_waiting_valid_test_1 = { .request = &query_call_waiting_mode_0, .parcel_data = req_query_call_waiting_parcel_valid_1, .parcel_size = sizeof(req_query_call_waiting_parcel_valid_1), }; /* set_clir tests */ const int set_clir_mode_0 = 0; static const guchar req_set_clir_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct request_test_data set_clir_valid_test_1 = { .request = &set_clir_mode_0, .parcel_data = req_set_clir_parcel_valid_1, .parcel_size = sizeof(req_set_clir_parcel_valid_1), }; /* screen_state tests */ const int screen_state_0 = 0; const int screen_state_1 = 1; static const guchar req_screen_state_parcel_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct request_test_data screen_state_valid_test_1 = { .request = &screen_state_0, .parcel_data = req_screen_state_parcel_valid_1, .parcel_size = sizeof(req_screen_state_parcel_valid_1), }; static const guchar req_screen_state_parcel_valid_2[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct request_test_data screen_state_valid_test_2 = { .request = &screen_state_1, .parcel_data = req_screen_state_parcel_valid_2, .parcel_size = sizeof(req_screen_state_parcel_valid_2), }; /* set_preferred_network_type tests */ const int preferred_network_type_gsm_only = PREF_NET_TYPE_GSM_ONLY; static const guchar req_set_preferred_network_type_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct request_test_data set_preferred_network_type_valid_test_1 = { .request = &preferred_network_type_gsm_only, .parcel_data = req_set_preferred_network_type_valid_1, .parcel_size = sizeof(req_set_preferred_network_type_valid_1), }; /* query_facility_lock tests */ struct request_test_query_facility_lock_data { const char *facility; int services; const guchar *parcel_data; gsize parcel_size; }; static const guchar req_query_facility_lock_valid_1[] = { 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x41, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; static const struct request_test_query_facility_lock_data query_facility_lock_valid_test_1 = { .facility = "AO", .services = SERVICE_CLASS_NONE, .parcel_data = req_query_facility_lock_valid_1, .parcel_size = sizeof(req_query_facility_lock_valid_1), }; /* set_facility_lock tests */ struct request_test_set_facility_lock_data { const char *facility; int enable; const char *passwd; int services; const guchar *parcel_data; gsize parcel_size; }; static const guchar req_set_facility_lock_valid_1[] = { 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; static const struct request_test_set_facility_lock_data set_facility_lock_valid_test_1 = { .facility = "OI", .enable = 0, .passwd = "0000", .services = SERVICE_CLASS_NONE, .parcel_data = req_set_facility_lock_valid_1, .parcel_size = sizeof(req_set_facility_lock_valid_1), }; /* change_barring_password tests */ struct request_test_change_barring_password_data { const char *facility; const char *old_passwd; const char *new_passwd; const guchar *parcel_data; gsize parcel_size; }; static const guchar req_change_barring_password_valid_1[] = { 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x41, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct request_test_change_barring_password_data change_barring_password_valid_test_1 = { .facility = "AB", .old_passwd = "1111", .new_passwd = "0000", .parcel_data = req_change_barring_password_valid_1, .parcel_size = sizeof(req_change_barring_password_valid_1), }; /* oem_hook_raw tests */ struct request_test_oem_hook_raw_data { const guchar *data; gsize size; const guchar *parcel_data; gsize parcel_size; }; static const guchar req_oem_hook_raw_valid_1[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct request_test_oem_hook_raw_data oem_hook_raw_valid_test_1 = { .data = req_oem_hook_raw_valid_1 + sizeof(int32_t), .size = sizeof(req_oem_hook_raw_valid_1) - sizeof(int32_t), .parcel_data = (guchar *) &req_oem_hook_raw_valid_1, .parcel_size = sizeof(req_oem_hook_raw_valid_1), }; static const guchar req_oem_hook_raw_valid_2[] = { 0xFF, 0xFF, 0xFF, 0xFF }; static const struct request_test_oem_hook_raw_data oem_hook_raw_valid_test_2 = { .data = NULL, .size = 0, .parcel_data = (guchar *) &req_oem_hook_raw_valid_2, .parcel_size = sizeof(req_oem_hook_raw_valid_2), }; /* set_initial_attach_apn tests */ struct request_test_set_initial_attach_apn { const char *apn; const int proto; const char *user; const char *passwd; const char *mccmnc; const guchar *parcel_data; gsize parcel_size; }; static const guchar req_set_initial_attach_apn_valid_1[] = { 0x0c, 0x00, 0x00, 0x00, 0x61, 0x00, 0x69, 0x00, 0x72, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x77, 0x00, 0x61, 0x00, 0x70, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x77, 0x00, 0x61, 0x00, 0x70, 0x00, 0x40, 0x00, 0x77, 0x00, 0x61, 0x00, 0x70, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x77, 0x00, 0x61, 0x00, 0x70, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct request_test_set_initial_attach_apn set_initial_attach_apn_valid_test_1 = { .apn = "airtelwap.es", .proto = OFONO_GPRS_PROTO_IP, .user = "wap@wap", .passwd = "wap125", .mccmnc = "21401", .parcel_data = req_set_initial_attach_apn_valid_1, .parcel_size = sizeof(req_set_initial_attach_apn_valid_1), }; /* oem_hook_strings tests */ struct request_test_oem_hook_strings { const int num_str; const char **str; const guchar *parcel_data; gsize parcel_size; }; static const guchar req_oem_hook_strings_valid_1[] = { 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x41, 0x00, 0x54, 0x00, 0x2b, 0x00, 0x45, 0x00, 0x50, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x45, 0x00, 0x50, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x43, 0x00, 0x3a, 0x00, 0x00, 0x00 }; static const char *oem_hook_strings_valid_1[] = { "AT+EPINC", "+EPINC:" }; static const struct request_test_oem_hook_strings oem_hook_strings_valid_test_1 = { .num_str = G_N_ELEMENTS(oem_hook_strings_valid_1), .str = oem_hook_strings_valid_1, .parcel_data = req_oem_hook_strings_valid_1, .parcel_size = sizeof(req_oem_hook_strings_valid_1), }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_REQUEST_RADIO_POWER 'OFF' message. */ static const guchar req_power_off_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_REQUEST_RADIO_POWER 'ON' message. */ static const guchar req_power_on_valid_parcel2[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const gboolean power_off = FALSE; static const gboolean power_on = TRUE; static const struct request_test_data power_valid_test_1 = { .request = &power_off, .parcel_data = (guchar *) &req_power_off_valid_parcel1, .parcel_size = sizeof(req_power_off_valid_parcel1), }; static const struct request_test_data power_valid_test_2 = { .request = &power_on, .parcel_data = (guchar *) &req_power_on_valid_parcel2, .parcel_size = sizeof(req_power_on_valid_parcel2), }; static void test_deactivate_data_call_invalid(gconstpointer data) { const struct req_deactivate_data_call *request = data; gboolean result; struct parcel rilp; struct ofono_error error; /* * No parcel_init needed, as these tests all fail during * param validation */ result = g_ril_request_deactivate_data_call(NULL, request, &rilp, &error); g_assert(result == FALSE); g_assert(error.type == OFONO_ERROR_TYPE_FAILURE && error.error == -EINVAL); } static void test_deactivate_data_call_valid(gconstpointer data) { const struct request_test_data *test_data = data; const struct req_deactivate_data_call *request = test_data->request; gboolean result; struct parcel rilp; struct ofono_error error; result = g_ril_request_deactivate_data_call(NULL, request, &rilp, &error); g_assert(result == TRUE); g_assert(error.type == OFONO_ERROR_TYPE_NO_ERROR && error.error == 0); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_setup_data_call_invalid(gconstpointer data) { const struct req_setup_data_call *request = data; gboolean result; struct parcel rilp; struct ofono_error error; /* * No parcel_init needed, as these tests all fail during * param validation */ result = g_ril_request_setup_data_call(NULL, request, &rilp, &error); g_assert(result == FALSE); g_assert(error.type == OFONO_ERROR_TYPE_FAILURE && error.error == -EINVAL); } static void test_request_setup_data_call_valid(gconstpointer data) { const struct req_setup_data_call *request = data; gboolean result; struct parcel rilp; struct ofono_error error; result = g_ril_request_setup_data_call(NULL, request, &rilp, &error); g_assert(result == TRUE); g_assert(error.type == OFONO_ERROR_TYPE_NO_ERROR && error.error == 0); parcel_free(&rilp); /* TODO: add unit 3 tests to validate binary parcel result */ } static void test_request_power_valid(gconstpointer data) { const struct request_test_data *test_data = data; const gboolean *online = test_data->request; struct parcel rilp; g_ril_request_power(NULL, *online, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_sim_read_info_valid(gconstpointer data) { const struct req_sim_read_info *req = data; gboolean result; struct parcel rilp; result = g_ril_request_sim_read_info(NULL, req, &rilp); g_assert(result == TRUE); parcel_free(&rilp); } static void test_request_sim_read_info_invalid(gconstpointer data) { const struct req_sim_read_info *req = data; gboolean result; struct parcel rilp; result = g_ril_request_sim_read_info(NULL, req, &rilp); g_assert(result == FALSE); parcel_free(&rilp); } static void test_request_sim_read_binary_valid(gconstpointer data) { const struct request_test_data *test_data = data; const struct req_sim_read_binary *req = test_data->request; struct parcel rilp; gboolean result; result = g_ril_request_sim_read_binary(NULL, req, &rilp); g_assert(result == TRUE); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_sim_read_record_valid(gconstpointer data) { const struct request_test_data *test_data = data; const struct req_sim_read_record *req = test_data->request; struct parcel rilp; gboolean result; result = g_ril_request_sim_read_record(NULL, req, &rilp); g_assert(result == TRUE); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_sim_write_binary_valid(gconstpointer data) { const struct request_test_data *test_data = data; const struct req_sim_write_binary *req = test_data->request; struct parcel rilp; gboolean result; result = g_ril_request_sim_write_binary(NULL, req, &rilp); g_assert(result == TRUE); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_sim_write_record_valid(gconstpointer data) { const struct request_test_data *test_data = data; const struct req_sim_write_record *req = test_data->request; struct parcel rilp; gboolean result; result = g_ril_request_sim_write_record(NULL, req, &rilp); g_assert(result == TRUE); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_read_imsi(gconstpointer data) { const struct request_test_data *test_data = data; const char *aid_str = test_data->request; struct parcel rilp; g_ril_request_read_imsi(NULL, aid_str, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_pin_send(gconstpointer data) { const struct request_test_pin_send_data *test_data = data; const char *passwd = test_data->passwd; const char *aid_str = test_data->aid_str; struct parcel rilp; g_ril_request_pin_send(NULL, passwd, aid_str, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_pin_change_state(gconstpointer data) { const struct request_test_data *test_data = data; const struct req_pin_change_state *req = test_data->request; struct parcel rilp; gboolean result; result = g_ril_request_pin_change_state(NULL, req, &rilp); g_assert(result == TRUE); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_pin_send_puk(gconstpointer data) { const struct request_test_pin_send_puk_data *test_data = data; const char *puk = test_data->puk; const char *passwd = test_data->passwd; const char *aid_str = test_data->aid_str; struct parcel rilp; g_ril_request_pin_send_puk(NULL, puk, passwd, aid_str, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_change_passwd(gconstpointer data) { const struct request_test_change_passwd_data *test_data = data; const char *old_passwd = test_data->old_passwd; const char *new_passwd = test_data->new_passwd; const char *aid_str = test_data->aid_str; struct parcel rilp; g_ril_request_change_passwd(NULL, old_passwd, new_passwd, aid_str, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_sms_cmgs(gconstpointer data) { const struct request_test_data *test_data = data; const struct req_sms_cmgs *req = test_data->request; struct parcel rilp; g_ril_request_sms_cmgs(NULL, req, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_sms_acknowledge(gconstpointer data) { const struct request_test_data *test_data = data; struct parcel rilp; g_ril_request_sms_acknowledge(NULL, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_set_smsc_address(gconstpointer data) { const struct request_test_data *test_data = data; const struct ofono_phone_number *number = test_data->request; struct parcel rilp; g_ril_request_set_smsc_address(NULL, number, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_dial(gconstpointer data) { const struct request_test_dial_data *test_data = data; const struct ofono_phone_number *ph = &test_data->ph; enum ofono_clir_option clir = test_data->clir; struct parcel rilp; g_ril_request_dial(NULL, ph, clir, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_hangup(gconstpointer data) { const struct request_test_data *test_data = data; const unsigned *call_id = test_data->request; struct parcel rilp; g_ril_request_hangup(NULL, *call_id, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_dtmf(gconstpointer data) { const struct request_test_data *test_data = data; const char *dtmf_char = test_data->request; struct parcel rilp; g_ril_request_dtmf(NULL, *dtmf_char, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_separate_conn(gconstpointer data) { const struct request_test_data *test_data = data; const unsigned *call_id = test_data->request; struct parcel rilp; g_ril_request_separate_conn(NULL, *call_id, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_set_supp_svc_notif(gconstpointer data) { const struct request_test_data *test_data = data; struct parcel rilp; g_ril_request_set_supp_svc_notif(NULL, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_set_mute_valid(gconstpointer data) { const struct request_test_data *test_data = data; const int *muted = test_data->request; struct parcel rilp; g_ril_request_set_mute(NULL, *muted, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_send_ussd(gconstpointer data) { const struct request_test_data *test_data = data; struct parcel rilp; g_ril_request_send_ussd(NULL, test_data->request, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_set_call_waiting(gconstpointer data) { const struct request_test_set_call_waiting *test_data = data; struct parcel rilp; g_ril_request_set_call_waiting(NULL, test_data->enabled, test_data->serviceclass, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_query_call_waiting(gconstpointer data) { const struct request_test_data *test_data = data; int mode = *(int *) test_data->request; struct parcel rilp; g_ril_request_query_call_waiting(NULL, mode, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_set_clir(gconstpointer data) { const struct request_test_data *test_data = data; int mode = *(int *) test_data->request; struct parcel rilp; g_ril_request_set_clir(NULL, mode, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_screen_state(gconstpointer data) { const struct request_test_data *test_data = data; int state = *(int *) test_data->request; struct parcel rilp; g_ril_request_screen_state(NULL, state, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_set_preferred_network_type(gconstpointer data) { const struct request_test_data *test_data = data; int preferred_network_type = *(int *) test_data->request; struct parcel rilp; g_ril_request_set_preferred_network_type(NULL, preferred_network_type, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_query_facility_lock(gconstpointer data) { const struct request_test_query_facility_lock_data *test_data = data; struct parcel rilp; g_ril_request_query_facility_lock(NULL, test_data->facility, "", test_data->services, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_set_facility_lock(gconstpointer data) { const struct request_test_set_facility_lock_data *test_data = data; struct parcel rilp; g_ril_request_set_facility_lock(NULL, test_data->facility, test_data->enable, test_data->passwd, test_data->services, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_set_initial_attach_apn(gconstpointer data) { const struct request_test_set_initial_attach_apn *test_data = data; struct parcel rilp; g_ril_request_set_initial_attach_apn(NULL, test_data->apn, test_data->proto, test_data->user, test_data->passwd, test_data->mccmnc, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_change_barring_password(gconstpointer data) { const struct request_test_change_barring_password_data *test_data = data; struct parcel rilp; g_ril_request_change_barring_password(NULL, test_data->facility, test_data->old_passwd, test_data->new_passwd, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_oem_hook_raw(gconstpointer data) { const struct request_test_oem_hook_raw_data *test_data = data; struct parcel rilp; g_ril_request_oem_hook_raw(NULL, test_data->data, test_data->size, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } static void test_request_oem_hook_strings(gconstpointer data) { const struct request_test_oem_hook_strings *test_data = data; struct parcel rilp; g_ril_request_oem_hook_strings(NULL, test_data->str, test_data->num_str, &rilp); g_assert(!memcmp(rilp.data, test_data->parcel_data, test_data->parcel_size)); parcel_free(&rilp); } #endif int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN g_test_add_data_func("/testgrilrequest/gprs-context: " "invalid DEACTIVATE_DATA_CALL Test 1", &req_deact_data_call_invalid_1, test_deactivate_data_call_invalid); g_test_add_data_func("/testgrilrequest/gprs-context: " "valid DEACTIVATE_DATA_CALL Test 1", &deact_data_call_valid_test_1, test_deactivate_data_call_valid); g_test_add_data_func("/testgrilrequest/gprs-context: " "invalid SETUP_DATA_CALL Test 1", &req_setup_data_call_invalid_1, test_request_setup_data_call_invalid); g_test_add_data_func("/testgrilrequest/gprs-context: " "invalid SETUP_DATA_CALL Test 2", &req_setup_data_call_invalid_2, test_request_setup_data_call_invalid); g_test_add_data_func("/testgrilrequest/gprs-context: " "invalid SETUP_DATA_CALL Test 3", &req_setup_data_call_invalid_3, test_request_setup_data_call_invalid); g_test_add_data_func("/testgrilrequest/gprs-context: " "invalid SETUP_DATA_CALL Test 4", &req_setup_data_call_invalid_4, test_request_setup_data_call_invalid); g_test_add_data_func("/testgrilrequest/gprs-context: " "invalid SETUP_DATA_CALL Test 5", &req_setup_data_call_invalid_5, test_request_setup_data_call_invalid); g_test_add_data_func("/testgrilrequest/gprs-context: " "invalid SETUP_DATA_CALL Test 6", &req_setup_data_call_invalid_6, test_request_setup_data_call_invalid); g_test_add_data_func("/testgrilrequest/gprs-context: " "valid SETUP_DATA_CALL Test 1", &req_setup_data_call_valid_1, test_request_setup_data_call_valid); g_test_add_data_func("/testgrilrequest/gprs-context: " "valid SETUP_DATA_CALL Test 2", &req_setup_data_call_valid_2, test_request_setup_data_call_valid); g_test_add_data_func("/testgrilrequest/gprs-context: " "valid SETUP_DATA_CALL Test 3", &req_setup_data_call_valid_3, test_request_setup_data_call_valid); g_test_add_data_func("/testgrilrequest/gprs-context: " "valid SETUP_DATA_CALL Test 4", &req_setup_data_call_valid_4, test_request_setup_data_call_valid); g_test_add_data_func("/testgrilrequest/gprs-context: " "valid SETUP_DATA_CALL Test 5", &req_setup_data_call_valid_5, test_request_setup_data_call_valid); g_test_add_data_func("/testgrilrequest/gprs-context: " "valid SETUP_DATA_CALL Test 6", &req_setup_data_call_valid_6, test_request_setup_data_call_valid); g_test_add_data_func("/testgrilrequest/power: " "valid POWER Test 1", &power_valid_test_1, test_request_power_valid); g_test_add_data_func("/testgrilrequest/power: " "valid POWER Test 2", &power_valid_test_2, test_request_power_valid); g_test_add_data_func("/testgrilrequest/sim: " "valid SIM_READ_INFO Test 1", &req_sim_read_info_valid_1, test_request_sim_read_info_valid); g_test_add_data_func("/testgrilrequest/sim: " "invalid SIM_READ_INFO Test 1", &req_sim_read_info_invalid_1, test_request_sim_read_info_invalid); g_test_add_data_func("/testgrilrequest/sim: " "valid SIM_READ_BINARY Test 1", &sim_read_binary_valid_test_1, test_request_sim_read_binary_valid); g_test_add_data_func("/testgrilrequest/sim: " "valid SIM_READ_RECORD Test 1", &sim_read_record_valid_test_1, test_request_sim_read_record_valid); g_test_add_data_func("/testgrilrequest/sim: " "valid SIM_WRITE_BINARY Test 1", &sim_write_binary_valid_test_1, test_request_sim_write_binary_valid); g_test_add_data_func("/testgrilrequest/sim: " "valid SIM_WRITE_RECORD Test 1", &sim_write_record_valid_test_1, test_request_sim_write_record_valid); g_test_add_data_func("/testgrilrequest/sim: " "valid READ_IMSI Test 1", &read_imsi_valid_test_1, test_request_read_imsi); g_test_add_data_func("/testgrilrequest/sim: " "valid PIN_SEND Test 1", &pin_send_record_valid_test_1, test_request_pin_send); g_test_add_data_func("/testgrilrequest/sim: " "valid PIN_CHANGE_STATE Test 1", &pin_change_state_valid_test_1, test_request_pin_change_state); g_test_add_data_func("/testgrilrequest/sim: " "valid PIN_SEND_PUK Test 1", &pin_send_puk_valid_test_1, test_request_pin_send_puk); g_test_add_data_func("/testgrilrequest/sim: " "valid CHANGE_PASSWD Test 1", &change_passwd_valid_test_1, test_request_change_passwd); g_test_add_data_func("/testgrilrequest/sms: " "valid SMS_CMGS Test 1", &sms_cmgs_valid_test_1, test_request_sms_cmgs); g_test_add_data_func("/testgrilrequest/sms: " "valid SMS_ACKNOWLEDGE Test 1", &sms_acknowledge_valid_test_1, test_request_sms_acknowledge); g_test_add_data_func("/testgrilrequest/sms: " "valid SET_SMSC_ADDRESS Test 1", &smsc_address_valid_test_1, test_request_set_smsc_address); g_test_add_data_func("/testgrilrequest/voicecall: " "valid DIAL Test 1", &dial_valid_test_1, test_request_dial); g_test_add_data_func("/testgrilrequest/voicecall: " "valid HANGUP Test 1", &set_hangup_valid_test_1, test_request_hangup); g_test_add_data_func("/testgrilrequest/voicecall: " "valid DTMF Test 1", &dtmf_valid_test_1, test_request_dtmf); g_test_add_data_func("/testgrilrequest/voicecall: " "valid SEPARATE_CONN Test 1", &separate_conn_valid_test_1, test_request_separate_conn); g_test_add_data_func("/testgrilrequest/voicecall: " "valid SET_SUPP_SVC_NOTIF Test 1", &set_supp_svc_notif_valid_test_1, test_request_set_supp_svc_notif); g_test_add_data_func("/testgrilrequest/call-volume: " "valid SET_MUTE Test 1", &set_mute_valid_test_1, test_request_set_mute_valid); g_test_add_data_func("/testgrilrequest/call-volume: " "valid SET_MUTE Test 2", &set_mute_valid_test_2, test_request_set_mute_valid); g_test_add_data_func("/testgrilrequest/ussd: " "valid SEND_USSD Test 1", &send_ussd_valid_test_1, test_request_send_ussd); g_test_add_data_func("/testgrilrequest/call-settings: " "valid SET_CALL_WAITING Test 1", &set_call_waiting_valid_test_1, test_request_set_call_waiting); g_test_add_data_func("/testgrilrequest/call-settings: " "valid QUERY_CALL_WAITING Test 1", &query_call_waiting_valid_test_1, test_request_query_call_waiting); g_test_add_data_func("/testgrilrequest/call-settings: " "valid SET_CLIR Test 1", &set_clir_valid_test_1, test_request_set_clir); g_test_add_data_func("/testgrilrequest/radio-settings: " "valid SCREEN_STATE Test 1", &screen_state_valid_test_1, test_request_screen_state); g_test_add_data_func("/testgrilrequest/radio-settings: " "valid SCREEN_STATE Test 2", &screen_state_valid_test_2, test_request_screen_state); g_test_add_data_func("/testgrilrequest/radio-settings: " "valid SET_PREFERRED_NETWORK_TYPE Test 1", &set_preferred_network_type_valid_test_1, test_request_set_preferred_network_type); g_test_add_data_func("/testgrilrequest/call-barring: " "valid QUERY_FACILITY_LOCK Test 1", &query_facility_lock_valid_test_1, test_request_query_facility_lock); g_test_add_data_func("/testgrilrequest/call-barring: " "valid SET_FACILITY_LOCK Test 1", &set_facility_lock_valid_test_1, test_request_set_facility_lock); g_test_add_data_func("/testgrilrequest/call-barring: " "valid CHANGE_BARRING_PASSWORD Test 1", &change_barring_password_valid_test_1, test_request_change_barring_password); g_test_add_data_func("/testgrilrequest/oem-hook-raw: " "valid OEM_HOOK_RAW Test 1", &oem_hook_raw_valid_test_1, test_request_oem_hook_raw); g_test_add_data_func("/testgrilrequest/oem-hook-raw: " "valid OEM_HOOK_RAW Test 2", &oem_hook_raw_valid_test_2, test_request_oem_hook_raw); g_test_add_data_func("/testgrilrequest/set-ia-apn: " "valid SET_INITIAL_ATTACH_APN Test 1", &set_initial_attach_apn_valid_test_1, test_request_set_initial_attach_apn); g_test_add_data_func("/testgrilrequest/oem-hook-strings: " "valid OEM_HOOK_STRINGS Test 1", &oem_hook_strings_valid_test_1, test_request_oem_hook_strings); #endif return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-mux.c0000644000015600001650000003653312671500024021322 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include #include #include "gatmux.h" #include "gsm0710.h" static int do_connect(const char *address, unsigned short port) { struct sockaddr_in addr; int sk, err; sk = socket(PF_INET, SOCK_STREAM, 0); if (sk < 0) return sk; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(address); addr.sin_port = htons(port); err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { close(sk); return err; } return sk; } static GMainLoop *mainloop; static GAtMux *mux; static gboolean cleanup_callback(gpointer data) { g_at_mux_unref(mux); g_main_loop_quit(mainloop); return FALSE; } static gboolean chat_cleanup(gpointer data) { GAtChat *chat = data; g_at_chat_unref(chat); return FALSE; } static void chat_callback(gboolean ok, GAtResult *result, gpointer user_data) { GAtResultIter iter; g_at_result_iter_init(&iter, result); g_print("chat: callback [ok %d]\n", ok); g_print("%s\n", g_at_result_final_response(result)); g_idle_add(chat_cleanup, user_data); } static void mux_debug(const char *str, void *data) { g_print("%s: %s\n", (char *) data, str); } static void mux_setup(GAtMux *m, gpointer data) { GAtChat *chat = data; GIOChannel *io; GAtSyntax *syntax; mux = m; g_print("mux_setup: %p\n", mux); if (mux == NULL) { g_at_chat_unref(chat); g_main_loop_quit(mainloop); return; } g_at_mux_start(mux); io = g_at_mux_create_channel(mux); syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); g_at_chat_set_debug(chat, mux_debug, "CHAT1"); g_at_chat_set_wakeup_command(chat, "\r", 1000, 5000); g_at_chat_send(chat, "AT+CGMI", NULL, NULL, NULL, NULL); g_at_chat_send(chat, "AT+CGMR", NULL, chat_callback, chat, NULL); io = g_at_mux_create_channel(mux); syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); g_at_chat_set_debug(chat, mux_debug, "CHAT2"); g_at_chat_set_wakeup_command(chat, "\r", 1000, 5000); g_at_chat_send(chat, "AT+CGMI", NULL, NULL, NULL, NULL); g_at_chat_send(chat, "AT+CGMR", NULL, chat_callback, chat, NULL); io = g_at_mux_create_channel(mux); syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); g_at_chat_set_debug(chat, mux_debug, "CHAT3"); g_at_chat_set_wakeup_command(chat, "\r", 1000, 5000); g_at_chat_send(chat, "AT+CGMI", NULL, NULL, NULL, NULL); g_at_chat_send(chat, "AT+CGMR", NULL, chat_callback, chat, NULL); io = g_at_mux_create_channel(mux); syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); g_at_chat_set_debug(chat, mux_debug, "CHAT4"); g_at_chat_set_wakeup_command(chat, "\r", 1000, 5000); g_at_chat_send(chat, "AT+CGMI", NULL, NULL, NULL, NULL); g_at_chat_send(chat, "AT+CGMR", NULL, chat_callback, chat, NULL); g_timeout_add_seconds(7, cleanup_callback, NULL); } static void mux_init(gboolean ok, GAtResult *result, gpointer data) { GAtChat *chat = data; g_print("mux_init: %d\n", ok); if (ok == FALSE) { g_at_chat_unref(chat); g_main_loop_quit(mainloop); return; } g_at_mux_setup_gsm0710(chat, mux_setup, chat, NULL); } static void test_mux(void) { GIOChannel *io; GAtChat *chat; GAtSyntax *syntax; int sk; sk = do_connect("192.168.0.202", 2000); if (sk < 0) { g_printerr("connect failed\n"); return; } mux = NULL; io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(io, TRUE); syntax = g_at_syntax_new_gsm_permissive(); chat = g_at_chat_new(io, syntax); g_at_syntax_unref(syntax); g_io_channel_unref(io); if (chat == NULL) { g_printerr("Chat creation failed\n"); return; } g_at_chat_set_debug(chat, mux_debug, "MUX"); g_at_chat_set_wakeup_command(chat, "\r", 1000, 5000); g_at_chat_send(chat, "ATE0", NULL, mux_init, chat, NULL); mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); g_main_loop_unref(mainloop); } static void test_basic(void) { g_test_trap_subprocess("/testmux/basic:subprocess", 60 * 1000 * 1000, 0); g_test_trap_assert_passed(); //g_test_trap_assert_stderr("failed"); } /* DLC 1, Open Channel */ static const guint8 basic_open[] = { 0xF9, 0x07, 0x3F, 0x01, 0xDE, 0xF9 }; /* DLC 1, Close Channel */ static char const basic_close[] = { 0xF9, 0x07, 0x53, 0x01, 0x3F, 0xF9 }; /* DLC 1, Data */ static const guint8 basic_data[] = { 0x12, 0x34, 0x56 }; static const guint8 basic_data_result[] = { 0xF9, 0x07, 0xEF, 0x07, 0x12, 0x34, 0x56, 0xD3, 0xF9 }; /* DLC 1, Long Data */ static const guint8 basic_long_frame[] = { 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, }; static const guint8 basic_long_frame_result[] = { 0xF9, 0x07, 0xEF, 0x10, 0x01, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x88, 0xF9 }; static void test_fill_basic(void) { guint8 control_frame[6]; guint8 data_frame[128]; guint8 long_frame[256]; int s; s = gsm0710_basic_fill_frame(control_frame, 1, GSM0710_OPEN_CHANNEL, NULL, 0); g_assert(s == sizeof(basic_open)); g_assert(memcmp(basic_open, control_frame, s) == 0); s = gsm0710_basic_fill_frame(control_frame, 1, GSM0710_CLOSE_CHANNEL, NULL, 0); g_assert(s == sizeof(basic_close)); g_assert(memcmp(basic_close, control_frame, s) == 0); s = gsm0710_basic_fill_frame(data_frame, 1, GSM0710_DATA, basic_data, sizeof(basic_data)); g_assert(s == sizeof(basic_data_result)); g_assert(memcmp(basic_data_result, data_frame, s) == 0); s = gsm0710_basic_fill_frame(long_frame, 1, GSM0710_DATA, basic_long_frame, sizeof(basic_long_frame)); g_assert(s == sizeof(basic_long_frame_result)); g_assert(memcmp(basic_long_frame_result, long_frame, s) == 0); } /* DLC 1, Open Channel */ static const guint8 advanced_open[] = { 0x7E, 0x07, 0x3F, 0x89, 0x7E }; /* DLC 1, Close Channel */ static const guint8 advanced_close[] = { 0x7E, 0x07, 0x53, 0xC8, 0x7E }; /* DLC 1, Data */ static const guint8 advanced_data[] = { 0x12, 0x34, 0x56 }; static const guint8 advanced_data_result[] = { 0x7E, 0x07, 0xEF, 0x12, 0x34, 0x56, 0x05, 0x7E }; /* DLC 1, Quoted data */ static const guint8 advanced_quoted_data[] = { 0x12, 0x34, 0x56, 0x7E, 0x78, 0x7D }; static const guint8 advanced_quoted_data_result[] = { 0x7E, 0x07, 0xEF, 0x12, 0x34, 0x56, 0x7D, 0x5E, 0x78, 0x7D, 0x5D, 0x05, 0x7E }; static void test_fill_advanced(void) { guint8 control_frame[8]; guint8 data_frame[128]; int s; s = gsm0710_advanced_fill_frame(control_frame, 1, GSM0710_OPEN_CHANNEL, NULL, 0); g_assert(s == sizeof(advanced_open)); g_assert(memcmp(advanced_open, control_frame, s) == 0); s = gsm0710_advanced_fill_frame(control_frame, 1, GSM0710_CLOSE_CHANNEL, NULL, 0); g_assert(s == sizeof(advanced_close)); g_assert(memcmp(advanced_close, control_frame, s) == 0); s = gsm0710_advanced_fill_frame(data_frame, 1, GSM0710_DATA, advanced_data, sizeof(advanced_data)); g_assert(s == sizeof(advanced_data_result)); g_assert(memcmp(advanced_data_result, data_frame, s) == 0); s = gsm0710_advanced_fill_frame(data_frame, 1, GSM0710_DATA, advanced_quoted_data, sizeof(advanced_quoted_data)); g_assert(s == sizeof(advanced_quoted_data_result)); g_assert(memcmp(advanced_quoted_data_result, data_frame, s) == 0); } static guint8 basic_input[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x07, 0xEF, 0x07, 0x12, 0x34, 0x56, 0xD3, 0xF9, 0x07, 0xEF, 0x07, 0x12, 0x34, 0x56, 0xD3, 0xF9 }; static guint8 basic_input2[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x07, 0xEF, 0x07, 0x12, 0x34, 0x56, 0xD3, 0xF9, 0xF9, 0x07, 0xEF, 0x07, 0x12, 0x34, 0x56, 0xD3, 0xF9 }; static int basic_garbage_size = 4; static int basic_frame_size = 7; static const guint8 basic_output[] = { 0x12, 0x34, 0x56 }; static void test_extract_basic(void) { int total = 0; int nread; guint8 dlc; guint8 ctrl; guint8 *frame; int frame_size; frame = NULL; frame_size = 0; nread = gsm0710_basic_extract_frame(basic_input + total, basic_garbage_size, &dlc, &ctrl, &frame, &frame_size); g_assert(frame == NULL); g_assert(frame_size == 0); total += nread; /* Try to read with just the open flag */ nread = gsm0710_basic_extract_frame(basic_input + total, basic_frame_size + 1, &dlc, &ctrl, &frame, &frame_size); g_assert(nread == 0); g_assert(frame == NULL); /* Now read with the close flag as well */ nread = gsm0710_basic_extract_frame(basic_input + total, basic_frame_size + 2, &dlc, &ctrl, &frame, &frame_size); /* Extracted the open flag + frame */ g_assert(nread == basic_frame_size + 1); g_assert(frame_size == sizeof(basic_output)); g_assert(memcmp(basic_output, frame, frame_size) == 0); total += nread; nread = gsm0710_basic_extract_frame(basic_input + total, sizeof(basic_input) - total, &dlc, &ctrl, &frame, &frame_size); g_assert(nread == (int)(sizeof(basic_input) - total - 1)); g_assert(frame_size == sizeof(basic_output)); g_assert(memcmp(basic_output, frame, frame_size) == 0); total += nread; nread = gsm0710_basic_extract_frame(basic_input + total, sizeof(basic_input) - total, &dlc, &ctrl, &frame, &frame_size); g_assert(nread == 0); total = 0; nread = gsm0710_basic_extract_frame(basic_input2 + total, sizeof(basic_input2) - total, &dlc, &ctrl, &frame, &frame_size); g_assert(nread == basic_garbage_size + basic_frame_size + 1); g_assert(frame_size == sizeof(basic_output)); g_assert(memcmp(basic_output, frame, frame_size) == 0); total += nread; nread = gsm0710_basic_extract_frame(basic_input2 + total, sizeof(basic_input2) - total, &dlc, &ctrl, &frame, &frame_size); g_assert(frame_size == sizeof(basic_output)); g_assert(memcmp(basic_output, frame, frame_size) == 0); total += nread; g_assert(total == sizeof(basic_input2) - 1); } static guint8 advanced_input[] = { 0xFF, 0xFF, 0xFF, 0x7E, 0x07, 0xEF, 0x12, 0x34, 0x56, 0x05, 0x7E, 0x07, 0xEF, 0x12, 0x34, 0x56, 0x05, 0x7E }; static guint8 advanced_input2[] = { 0xFF, 0xFF, 0xFF, 0x7E, 0x07, 0xEF, 0x12, 0x34, 0x56, 0x05, 0x7E, 0x07, 0xEF, 0x12, 0x34, 0x56, 0x05, 0x7E }; static int advanced_garbage_size = 3; static int advanced_frame_size = 6; static const guint8 advanced_output[] = { 0x12, 0x34, 0x56 }; static void test_extract_advanced(void) { int total = 0; int nread; guint8 dlc; guint8 ctrl; guint8 *frame; int frame_size; frame = NULL; frame_size = 0; nread = gsm0710_advanced_extract_frame(advanced_input + total, advanced_garbage_size, &dlc, &ctrl, &frame, &frame_size); g_assert(frame == NULL); g_assert(frame_size == 0); total += nread; /* Try to read with just the open flag */ nread = gsm0710_advanced_extract_frame(advanced_input + total, advanced_frame_size + 1, &dlc, &ctrl, &frame, &frame_size); g_assert(nread == 0); g_assert(frame == NULL); /* Now read with the close flag as well */ nread = gsm0710_advanced_extract_frame(advanced_input + total, advanced_frame_size + 2, &dlc, &ctrl, &frame, &frame_size); /* Extracted the open flag + frame */ g_assert(nread == advanced_frame_size + 1); g_assert(frame_size == sizeof(advanced_output)); g_assert(memcmp(advanced_output, frame, frame_size) == 0); total += nread; nread = gsm0710_advanced_extract_frame(advanced_input + total, sizeof(advanced_input) - total, &dlc, &ctrl, &frame, &frame_size); g_assert(nread == (int)(sizeof(advanced_input) - total - 1)); g_assert(frame_size == sizeof(advanced_output)); g_assert(memcmp(advanced_output, frame, frame_size) == 0); total += nread; nread = gsm0710_advanced_extract_frame(advanced_input + total, sizeof(advanced_input) - total, &dlc, &ctrl, &frame, &frame_size); g_assert(nread == 0); total = 0; nread = gsm0710_advanced_extract_frame(advanced_input2 + total, sizeof(advanced_input2) - total, &dlc, &ctrl, &frame, &frame_size); g_assert(nread == advanced_garbage_size + advanced_frame_size + 1); g_assert(frame_size == sizeof(advanced_output)); g_assert(memcmp(advanced_output, frame, frame_size) == 0); total += nread; nread = gsm0710_advanced_extract_frame(advanced_input2 + total, sizeof(advanced_input2) - total, &dlc, &ctrl, &frame, &frame_size); g_assert(frame_size == sizeof(advanced_output)); g_assert(memcmp(advanced_output, frame, frame_size) == 0); total += nread; g_assert(total == sizeof(advanced_input2) - 1); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/testmux/fill_basic", test_fill_basic); g_test_add_func("/testmux/fill_advanced", test_fill_advanced); g_test_add_func("/testmux/extract_basic", test_extract_basic); g_test_add_func("/testmux/extract_advanced", test_extract_advanced); g_test_add_func("/testmux/basic", test_basic); g_test_add_func("/testmux/basic:subprocess", test_mux); return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/rilmodem-test-server.c0000644000015600001650000001270212671500073023621 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2015 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "rilmodem-test-server.h" #define MAX_REQUEST_SIZE 4096 #define RIL_SERVER_SOCK_PATH "/tmp/unittestril" struct server_data { int server_sk; ConnectFunc connect_func; GIOChannel *server_io; char *sock_name; const struct rilmodem_test_data *rtd; void *user_data; }; /* Warning: length is stored in network order */ struct rsp_hdr { uint32_t length; uint32_t unsolicited; uint32_t serial; uint32_t error; }; static gboolean read_server(gpointer data) { struct server_data *sd = data; GIOStatus status; gsize offset, rbytes, wbytes; gchar *buf, *bufp; uint32_t req_serial; struct rsp_hdr rsp; buf = g_malloc0(MAX_REQUEST_SIZE); status = g_io_channel_read_chars(sd->server_io, buf, MAX_REQUEST_SIZE, &rbytes, NULL); g_assert(status == G_IO_STATUS_NORMAL); g_assert(rbytes == sd->rtd->req_size); /* validate len, and request_id */ g_assert(!memcmp(buf, sd->rtd->req_data, (sizeof(uint32_t) * 2))); /* * header: size (uint32), reqid (uin32), serial (uint32) * header size == 16 ( excludes sizeof(size) ) */ /* advance past request_no */ bufp = buf + (sizeof(uint32_t) * 2); req_serial = (uint32_t) *bufp; /* advance past serial_no */ bufp += sizeof(uint32_t); /* validate the rest of the parcel... */ offset = (sizeof(uint32_t) * 3); g_assert(!memcmp(bufp, sd->rtd->req_data + offset, sd->rtd->req_size - offset)); /* Length does not include the length field. Network order. */ rsp.length = htonl(sizeof(rsp) - sizeof(rsp.length) + sd->rtd->rsp_size); rsp.unsolicited = 0; rsp.serial = req_serial; rsp.error = sd->rtd->rsp_error; /* copy header */ memcpy(buf, &rsp, sizeof(rsp)); if (sd->rtd->rsp_size) { bufp = buf + sizeof(rsp); memcpy(bufp, sd->rtd->rsp_data, sd->rtd->rsp_size); } status = g_io_channel_write_chars(sd->server_io, buf, sizeof(rsp) + sd->rtd->rsp_size, &wbytes, NULL); /* FIXME: assert wbytes is correct */ g_assert(status == G_IO_STATUS_NORMAL); g_free(buf); g_io_channel_unref(sd->server_io); return FALSE; } static gboolean on_socket_connected(GIOChannel *chan, GIOCondition cond, gpointer data) { struct server_data *sd = data; struct sockaddr saddr; unsigned int len = sizeof(saddr); int fd; GIOStatus status; g_assert(cond == G_IO_IN); fd = accept(sd->server_sk, &saddr, &len); g_assert(fd != -1); sd->server_io = g_io_channel_unix_new(fd); g_assert(sd->server_io != NULL); status = g_io_channel_set_encoding(sd->server_io, NULL, NULL); g_assert(status == G_IO_STATUS_NORMAL); g_io_channel_set_buffered(sd->server_io, FALSE); g_io_channel_set_close_on_unref(sd->server_io, TRUE); if (sd->connect_func) sd->connect_func(sd->user_data); if (sd->rtd->unsol_test == FALSE) g_idle_add(read_server, sd); return FALSE; } void rilmodem_test_server_close(struct server_data *sd) { g_assert(sd->server_sk); close(sd->server_sk); remove(sd->sock_name); g_free(sd->sock_name); g_free(sd); } struct server_data *rilmodem_test_server_create(ConnectFunc connect, const struct rilmodem_test_data *test_data, void *data) { GIOChannel *io; struct sockaddr_un addr; int retval; struct server_data *sd; sd = g_new0(struct server_data, 1); sd->connect_func = connect; sd->user_data = data; sd->rtd = test_data; sd->server_sk = socket(AF_UNIX, SOCK_STREAM, 0); g_assert(sd->server_sk); sd->sock_name = g_strdup_printf(RIL_SERVER_SOCK_PATH"%u", (unsigned) getpid()); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sd->sock_name, sizeof(addr.sun_path) - 1); /* Unlink any existing socket for this session */ unlink(addr.sun_path); retval = bind(sd->server_sk, (struct sockaddr *) &addr, sizeof(addr)); g_assert(retval >= 0); retval = listen(sd->server_sk, 0); g_assert(retval >= 0); io = g_io_channel_unix_new(sd->server_sk); g_assert(io != NULL); g_io_channel_set_close_on_unref(io, TRUE); g_io_add_watch_full(io, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, on_socket_connected, sd, NULL); g_io_channel_unref(io); return sd; } void rilmodem_test_server_write(struct server_data *sd, const unsigned char *buf, const size_t buf_len) { GIOStatus status; gsize wbytes; status = g_io_channel_write_chars(sd->server_io, (const char *) buf, buf_len, &wbytes, NULL); g_assert(status == G_IO_STATUS_NORMAL); status = g_io_channel_flush(sd->server_io, NULL); g_assert(status == G_IO_STATUS_NORMAL); } const char *rilmodem_test_get_socket_name(struct server_data *sd) { return sd->sock_name; } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-grilreply.c0000644000015600001650000020542712671500073022526 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2013 Canonical 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. * * 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 #endif #include #include #include #include #include #include #include #include #include "common.h" #include "grilreply.h" /* * TODO: It may make sense to split this file into * domain-specific files ( eg. test-grilreply-gprs-context.c ) * once more tests are added. */ /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN typedef struct reg_state_test reg_state_test; struct reg_state_test { int status; int tech; const struct ril_msg msg; }; typedef struct get_preferred_network_test get_preferred_network_test; struct get_preferred_network_test { int preferred; const struct ril_msg msg; }; struct query_facility_lock_test { int status; const struct ril_msg msg; }; struct set_facility_lock_test { int retries; const struct ril_msg msg; }; struct sim_password_test { int retries; enum ofono_sim_password_type passwd_type; const struct ril_msg msg; }; struct oem_hook_raw_test { const unsigned char *data; int size; const struct ril_msg msg; }; struct oem_hook_strings_test { int num_str; const char **str; const struct ril_msg msg; }; /* Invalid RIL_REQUEST_DATA_REGISTRATION_STATE: buffer too small */ static const struct ril_msg reply_data_reg_state_invalid_1 = { .buf = "XYZ", .buf_len = 3, .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, }; /* * The following hex data represents a RIL_REQUEST_DATA_REGISTRATION_STATE * reply with an invalid number of parameters ( 0 ). */ static const guchar reply_data_reg_state_invalid_parcel2[] = { 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_data_reg_state_invalid_2 = { .buf = (gchar *) &reply_data_reg_state_invalid_parcel2, .buf_len = sizeof(reply_data_reg_state_invalid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, }; /* * The following hex data represents a RIL_REQUEST_DATA_REGISTRATION_STATE * reply with a null status parameter: * * {(null),1b3f,07eaf3dc,HSPA,(null),20} */ static const guchar reply_data_reg_state_invalid_parcel3[] = { 0x06, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_data_reg_state_invalid_3 = { .buf = (gchar *) &reply_data_reg_state_invalid_parcel3, .buf_len = sizeof(reply_data_reg_state_invalid_parcel3), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, }; /* * The following hex data represents a valid RIL_REQUEST_DATA_REGISTRATION_STATE * reply with the following parameters: * * RADIO_TECH_UNKNOWN (0) * * {registered,1b3f,07eaf3dc,UNKNOWN,(null),20} */ static const guchar reply_data_reg_state_valid_parcel1[] = { 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const reg_state_test data_reg_valid_1 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_UNKNOWN, .msg = { .buf = (gchar *) &reply_data_reg_state_valid_parcel1, .buf_len = sizeof(reply_data_reg_state_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid RIL_REQUEST_DATA_REGISTRATION_STATE * reply with the following parameters: * * RADIO_TECH_GSM (16) * * {registered,1b3f,07eaf3dc,GSM,(null),20} */ static const guchar reply_data_reg_state_valid_parcel2[] = { 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const reg_state_test data_reg_valid_2 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_GSM, .msg = { .buf = (gchar *) &reply_data_reg_state_valid_parcel2, .buf_len = sizeof(reply_data_reg_state_valid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid RIL_REQUEST_DATA_REGISTRATION_STATE * reply with the following parameters: * * RADIO_TECH_GPRS (1) * * {registered,1b3f,07eaf3dc,GPRS,(null),20} */ static const guchar reply_data_reg_state_valid_parcel3[] = { 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const reg_state_test data_reg_valid_3 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_GPRS, .msg = { .buf = (gchar *) &reply_data_reg_state_valid_parcel3, .buf_len = sizeof(reply_data_reg_state_valid_parcel3), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid RIL_REQUEST_DATA_REGISTRATION_STATE * reply with the following parameters: * * RADIO_TECH_EDGE (2) * * {registered,1b3f,07eaf3dc,EDGE,(null),20} */ static const guchar reply_data_reg_state_valid_parcel4[] = { 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const reg_state_test data_reg_valid_4 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_EDGE, .msg = { .buf = (gchar *) &reply_data_reg_state_valid_parcel4, .buf_len = sizeof(reply_data_reg_state_valid_parcel4), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid RIL_REQUEST_DATA_REGISTRATION_STATE * reply with the following parameters: * * RADIO_TECH_UMTS (3) * * {registered,1b3f,07eaf3dc,UMTS,(null),20} */ static const guchar reply_data_reg_state_valid_parcel5[] = { 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const reg_state_test data_reg_valid_5 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_UMTS, .msg = { .buf = (gchar *) &reply_data_reg_state_valid_parcel5, .buf_len = sizeof(reply_data_reg_state_valid_parcel5), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid RIL_REQUEST_DATA_REGISTRATION_STATE * reply with the following parameters: * * RADIO_TECH_HSDPA (9) * * {registered,1b3f,07eaf3dc,HSDPA,(null),20} */ static const guchar reply_data_reg_state_valid_parcel6[] = { 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const reg_state_test data_reg_valid_6 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_HSDPA, .msg = { .buf = (gchar *) &reply_data_reg_state_valid_parcel6, .buf_len = sizeof(reply_data_reg_state_valid_parcel6), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid RIL_REQUEST_DATA_REGISTRATION_STATE * reply with the following parameters: * * RADIO_TECH_HSUPA (10) * * {registered,1b3f,07eaf3dc,HSUPA,(null),20} */ static const guchar reply_data_reg_state_valid_parcel7[] = { 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const reg_state_test data_reg_valid_7 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_HSUPA, .msg = { .buf = (gchar *) &reply_data_reg_state_valid_parcel7, .buf_len = sizeof(reply_data_reg_state_valid_parcel7), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid RIL_REQUEST_DATA_REGISTRATION_STATE * reply with the following parameters: * * RADIO_TECH_HSPA (11) * * {registered,1b3f,07eaf3dc,HSPA,(null),20} */ static const guchar reply_data_reg_state_valid_parcel8[] = { 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const reg_state_test data_reg_valid_8 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_HSPA, .msg = { .buf = (gchar *) &reply_data_reg_state_valid_parcel8, .buf_len = sizeof(reply_data_reg_state_valid_parcel8), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid RIL_REQUEST_DATA_REGISTRATION_STATE * reply with the following parameters: * * RADIO_TECH_LTE (14) * * {registered,1b3f,07eaf3dc,LTE,(null),20} */ static const guchar reply_data_reg_state_valid_parcel9[] = { 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const reg_state_test data_reg_valid_9 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_LTE, .msg = { .buf = (gchar *) &reply_data_reg_state_valid_parcel9, .buf_len = sizeof(reply_data_reg_state_valid_parcel9), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid RIL_REQUEST_DATA_REGISTRATION_STATE * reply with the following parameters: * * RADIO_TECH_HSPAP (15) * * Note, as ofono currently doesn't define a bearer enum that represents HSPA+, * it's currently mapped to HSPA. * * {registered,1b3f,07eaf3dc,HSPAP,(null),20} */ static const guchar reply_data_reg_state_valid_parcel10[] = { 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x66, 0x00, 0x33, 0x00, 0x64, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x32, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const reg_state_test data_reg_valid_10 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_HSPAP, .msg = { .buf = (gchar *) &reply_data_reg_state_valid_parcel10, .buf_len = sizeof(reply_data_reg_state_valid_parcel10), .unsolicited = FALSE, .req = RIL_REQUEST_DATA_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * TODO: investigate creation of a base reply, which could * then be modified for each test, as opposed to duplicating * the bulk of the data for each test. */ /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * RADIO_TECH_UMTS (3) * * {registered,1b3f,07eaf3dc,UMTS,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel1[] = { 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x35, 0x00, 0x31, 0x00, 0x37, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_1 = { .status = NETWORK_REGISTRATION_STATUS_REGISTERED, .tech = RADIO_TECH_UMTS, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel1, .buf_len = sizeof(reply_voice_reg_state_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * * RADIO_TECH_GPRS (1) * * {unregistered,1b3f,07eaf3dc,GPRS,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel2[] = { 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x35, 0x00, 0x31, 0x00, 0x37, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_2 = { .status = (int) NETWORK_REGISTRATION_STATUS_NOT_REGISTERED, .tech = RADIO_TECH_GPRS, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel2, .buf_len = sizeof(reply_voice_reg_state_valid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * RADIO_TECH_GSM (16) * * {searching,1b3f,07eaf3dc,GSM,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel3[] = { 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x35, 0x00, 0x31, 0x00, 0x30, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_3 = { .status = NETWORK_REGISTRATION_STATUS_SEARCHING, .tech = RADIO_TECH_GSM, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel3, .buf_len = sizeof(reply_voice_reg_state_valid_parcel3), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * RADIO_TECH_EDGE (2) * * {denied,1b3f,07eaf3dc,EDGE,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel4[] = { 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x35, 0x00, 0x31, 0x00, 0x30, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_4 = { .status = NETWORK_REGISTRATION_STATUS_DENIED, .tech = RADIO_TECH_EDGE, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel4, .buf_len = sizeof(reply_voice_reg_state_valid_parcel4), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * RADIO_TECH_UNKNOWN (0) * * {unknown,1b3f,07eaf3dc,UNKNOWN,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel5[] = { 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x35, 0x00, 0x31, 0x00, 0x30, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_5 = { .status = NETWORK_REGISTRATION_STATUS_UNKNOWN, .tech = RADIO_TECH_UNKNOWN, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel5, .buf_len = sizeof(reply_voice_reg_state_valid_parcel5), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * RADIO_TECH_LTE (14) * * {roaming,1b3f,07eaf3dc,LTE,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel6[] = { 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x35, 0x00, 0x31, 0x00, 0x30, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_6 = { .status = NETWORK_REGISTRATION_STATUS_ROAMING, .tech = RADIO_TECH_LTE, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel6, .buf_len = sizeof(reply_voice_reg_state_valid_parcel6), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * RADIO_TECH_HSDPA (9) * * {roaming,1b3f,07eaf3dc,HSDPA,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel7[] = { 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x35, 0x00, 0x31, 0x00, 0x30, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_7 = { .status = NETWORK_REGISTRATION_STATUS_ROAMING, .tech = RADIO_TECH_HSDPA, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel7, .buf_len = sizeof(reply_voice_reg_state_valid_parcel7), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * RADIO_TECH_HSUPA (10) * * {roaming,1b3f,07eaf3dc,HSUPA,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel8[] = { 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x35, 0x00, 0x31, 0x00, 0x30, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_8 = { .status = NETWORK_REGISTRATION_STATUS_ROAMING, .tech = RADIO_TECH_HSUPA, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel8, .buf_len = sizeof(reply_voice_reg_state_valid_parcel8), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * RADIO_TECH_HSPA (11) * * {roaming,1b3f,07eaf3dc,HSPA,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel9[] = { 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x35, 0x00, 0x31, 0x00, 0x30, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_9 = { .status = NETWORK_REGISTRATION_STATUS_ROAMING, .tech = RADIO_TECH_HSPA, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel9, .buf_len = sizeof(reply_voice_reg_state_valid_parcel9), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * RADIO_TECH_HSPAP (15) * * {roaming,1b3f,07eaf3dc,HSPAP,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel10[] = { 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x33, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x65, 0x00, 0x61, 0x00, 0x35, 0x00, 0x31, 0x00, 0x30, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_10 = { .status = NETWORK_REGISTRATION_STATUS_ROAMING, .tech = RADIO_TECH_HSPAP, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel10, .buf_len = sizeof(reply_voice_reg_state_valid_parcel10), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * {unregistered,(null),(null),UNKNOWN,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel11[] = { 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static const reg_state_test voice_reg_valid_11 = { .status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED, .tech = RADIO_TECH_UNKNOWN, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel11, .buf_len = sizeof(reply_voice_reg_state_valid_parcel11), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; /* * The following hex data represents a valid * RIL_REQUEST_VOICE_REGISTRATION_STATE reply with the following parameters: * * {13,(null),(null),UNKNOWN,(null),(null)} */ static const guchar reply_voice_reg_state_valid_parcel12[] = { 0x0f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x31, 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00 }; static const reg_state_test voice_reg_valid_12 = { .status = NETWORK_REGISTRATION_STATUS_DENIED, .tech = RADIO_TECH_UNKNOWN, .msg = { .buf = (gchar *) &reply_voice_reg_state_valid_parcel12, .buf_len = sizeof(reply_voice_reg_state_valid_parcel12), .unsolicited = FALSE, .req = RIL_REQUEST_VOICE_REGISTRATION_STATE, .serial_no = 0, .error = 0, } }; static const struct ril_msg reply_operator_invalid_1 = { .buf = "", .buf_len = 0, .unsolicited = FALSE, .req = RIL_REQUEST_OPERATOR, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of an * invalid RIL_REQUEST_OPERATOR with an invald number of parameters * * {lalpha=AT&T, salpha=} */ static const guchar reply_operator_invalid_parcel2[] = { 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x54, 0x00, 0x26, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_operator_invalid_2 = { .buf = (char *) &reply_operator_invalid_parcel2, .buf_len = sizeof(reply_operator_invalid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_OPERATOR, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a * valid RIL_REQUEST_OPERATOR with the following parameters: * * {lalpha=AT&T, salpha=, numeric=310410} */ static const guchar reply_operator_valid_parcel1[] = { 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x54, 0x00, 0x26, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x31, 0x00, 0x30, 0x00, 0x34, 0x00, 0x31, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_operator_valid_1 = { .buf = (char *) &reply_operator_valid_parcel1, .buf_len = sizeof(reply_operator_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_OPERATOR, .serial_no = 0, .error = 0, }; static const struct ril_msg reply_avail_ops_invalid_1 = { .buf = "", .buf_len = 0, .unsolicited = FALSE, .req = RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of an * invalid RIL_REQUEST_QUERY_AVAILABLE_NETWORKS with an invald * number of strings ( not evenly divisible by 4, the count of * strings per operator ). */ static const guchar reply_avail_ops_invalid_parcel2[] = { 0x0b, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_avail_ops_invalid_2 = { .buf = (char *) &reply_avail_ops_invalid_parcel2, .buf_len = sizeof(reply_avail_ops_invalid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a * valid RIL_REQUEST_AVAILABLE_NETWORKS with the following parameters: * * {lalpha=AT&T, salpha=, numeric=310410, status=available} */ static const guchar reply_avail_ops_valid_parcel1[] = { 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x54, 0x00, 0x26, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x33, 0x00, 0x31, 0x00, 0x30, 0x00, 0x34, 0x00, 0x31, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x61, 0x00, 0x76, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_avail_ops_valid_1 = { .buf = (char *) &reply_avail_ops_valid_parcel1, .buf_len = sizeof(reply_avail_ops_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_SIM_IO reply with the following parameters: * * {sw1=0x90,sw2=0x00,0000000a2fe2040000000005020000} * This is a reply to a select file for EF_ICCID. */ static const guchar reply_sim_io_valid_parcel1[] = { 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x61, 0x00, 0x32, 0x00, 0x66, 0x00, 0x65, 0x00, 0x32, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x35, 0x00, 0x30, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_sim_io_valid_1 = { .buf = (gchar *) reply_sim_io_valid_parcel1, .buf_len = sizeof(reply_sim_io_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_SIM_IO, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of an valid * RIL_REQUEST_SIM_IO reply with the following parameters: * * {sw1=0x90,sw2=0x00,(null)} * This is a reply to a select file for EF_ICCID. */ static const guchar reply_sim_io_valid_parcel2[] = { 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; static const struct ril_msg reply_sim_io_valid_2 = { .buf = (gchar *) reply_sim_io_valid_parcel2, .buf_len = sizeof(reply_sim_io_valid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_SIM_IO, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of an invalid * RIL_REQUEST_SIM_IO reply with the following parameters: * * Note - this is invalid because the response includes a non-hex char ('Z'). * * {sw1=0x90,sw2=0x00,Z000000a2fe2040000000005020000} * This is a reply to a select file for EF_ICCID. */ static const guchar reply_sim_io_invalid_parcel1[] = { 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x61, 0x00, 0x32, 0x00, 0x66, 0x00, 0x65, 0x00, 0x32, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x35, 0x00, 0x30, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_sim_io_invalid_1 = { .buf = (gchar *) reply_sim_io_invalid_parcel1, .buf_len = sizeof(reply_sim_io_invalid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_SIM_IO, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of an invalid * RIL_REQUEST_SIM_IO reply with the following parameters: * * {sw1=0x90,sw2=0x00,} * This is a reply to a select file for EF_ICCID. */ static const guchar reply_sim_io_invalid_parcel2[] = { 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff }; static const struct ril_msg reply_sim_io_invalid_2 = { .buf = (gchar *) reply_sim_io_invalid_parcel2, .buf_len = sizeof(reply_sim_io_invalid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_SIM_IO, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_GET_IMSI reply with the following parameters: * * {214060200695834} */ static const guchar reply_imsi_valid_parcel1[] = { 0x0f, 0x00, 0x00, 0x00, 0x32, 0x00, 0x31, 0x00, 0x34, 0x00, 0x30, 0x00, 0x36, 0x00, 0x30, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x36, 0x00, 0x39, 0x00, 0x35, 0x00, 0x38, 0x00, 0x33, 0x00, 0x34, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_imsi_valid_1 = { .buf = (gchar *) reply_imsi_valid_parcel1, .buf_len = sizeof(reply_imsi_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_GET_IMSI, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_GET_SIM_STATUS reply with the following parameters: * * {card_state=1,universal_pin_state=0,gsm_umts_index=0,cdma_index=-1, * ims_index=-1, [app_type=1,app_state=5,perso_substate=2,aid_ptr=, * app_label_ptr=(null),pin1_replaced=0,pin1=3,pin2=1],} */ static const guchar reply_sim_status_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_sim_status_valid_1 = { .buf = (gchar *) reply_sim_status_valid_parcel1, .buf_len = sizeof(reply_sim_status_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_GET_SIM_STATUS, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_GET_SMSC_ADDRESS reply with the following parameters: * * {type=145,number=34607003110} */ static const guchar reply_get_smsc_address_valid_parcel1[] = { 0x12, 0x00, 0x00, 0x00, 0x22, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x34, 0x00, 0x36, 0x00, 0x30, 0x00, 0x37, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x34, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_get_smsc_address_valid_1 = { .buf = (gchar *) reply_get_smsc_address_valid_parcel1, .buf_len = sizeof(reply_get_smsc_address_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_GET_SMSC_ADDRESS, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_GET_CURRENT_CALLS reply with the following parameters: * * {[id=2,status=0,type=1,number=686732222,name=] * [id=1,status=1,type=1,number=917525555,name=]} */ static const guchar reply_get_current_calls_valid_parcel1[] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x36, 0x00, 0x38, 0x00, 0x36, 0x00, 0x37, 0x00, 0x33, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x39, 0x00, 0x31, 0x00, 0x37, 0x00, 0x35, 0x00, 0x32, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_get_current_calls_valid_1 = { .buf = (gchar *) reply_get_current_calls_valid_parcel1, .buf_len = sizeof(reply_get_current_calls_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_GET_CURRENT_CALLS, .serial_no = 0, .error = 0, }; /* RIL_REQUEST_GET_CURRENT_CALLS NULL reply */ static const struct ril_msg reply_get_current_calls_invalid_1 = { .buf = NULL, .buf_len = 0, .unsolicited = FALSE, .req = RIL_REQUEST_GET_CURRENT_CALLS, .serial_no = 0, .error = 0, }; /* RIL_REQUEST_GET_CURRENT_CALLS no calls */ static const guchar reply_get_current_calls_invalid_parcel2[] = { 0x00, 0x00, 0x00, 0x00, }; static const struct ril_msg reply_get_current_calls_invalid_2 = { .buf = (gchar *) reply_get_current_calls_invalid_parcel2, .buf_len = sizeof(reply_get_current_calls_invalid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_GET_CURRENT_CALLS, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_LAST_CALL_FAIL_CAUSE reply with the following parameters: * * {16} */ static const guchar reply_call_fail_cause_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_call_fail_cause_valid_1 = { .buf = (gchar *) reply_call_fail_cause_valid_parcel1, .buf_len = sizeof(reply_call_fail_cause_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_LAST_CALL_FAIL_CAUSE, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_GET_MUTE reply with the following parameters: * * {muted=0} */ static const guchar reply_get_mute_off_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_get_mute_off_1 = { .buf = (gchar *) reply_get_mute_off_parcel1, .buf_len = sizeof(reply_get_mute_off_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_GET_MUTE, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_GET_MUTE reply with the following parameters: * * {muted=1} */ static const guchar reply_get_mute_on_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_get_mute_on_1 = { .buf = (gchar *) reply_get_mute_on_parcel1, .buf_len = sizeof(reply_get_mute_on_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_GET_MUTE, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_BASEBAND_VERSION reply with the following parameters: * * {M9615A-CEFWMAZM-2.0.1700.48} */ static const guchar reply_baseband_version_valid_parcel1[] = { 0x1b, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x39, 0x00, 0x36, 0x00, 0x31, 0x00, 0x35, 0x00, 0x41, 0x00, 0x2d, 0x00, 0x43, 0x00, 0x45, 0x00, 0x46, 0x00, 0x57, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x5a, 0x00, 0x4d, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x37, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x38, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_baseband_version_valid_1 = { .buf = (gchar *) reply_baseband_version_valid_parcel1, .buf_len = sizeof(reply_baseband_version_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_BASEBAND_VERSION, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_GET_IMEI reply with the following parameters: * * {355136050779043} */ static const guchar reply_get_imei_valid_parcel1[] = { 0x0f, 0x00, 0x00, 0x00, 0x33, 0x00, 0x35, 0x00, 0x35, 0x00, 0x31, 0x00, 0x33, 0x00, 0x36, 0x00, 0x30, 0x00, 0x35, 0x00, 0x30, 0x00, 0x37, 0x00, 0x37, 0x00, 0x39, 0x00, 0x30, 0x00, 0x34, 0x00, 0x33, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_get_imei_valid_1 = { .buf = (gchar *) reply_get_imei_valid_parcel1, .buf_len = sizeof(reply_get_imei_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_GET_IMEI, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_QUERY_CALL_WAITING reply with the following parameters: * * {1,0x30} */ static const guchar reply_query_call_waiting_valid_parcel1[] = { 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_query_call_waiting_valid_1 = { .buf = (gchar *) reply_query_call_waiting_valid_parcel1, .buf_len = sizeof(reply_query_call_waiting_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_QUERY_CALL_WAITING, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_QUERY_CLIP reply with the following parameters: * * {1} */ static const guchar reply_query_clip_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_query_clip_valid_1 = { .buf = (gchar *) reply_query_clip_valid_parcel1, .buf_len = sizeof(reply_query_clip_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_QUERY_CLIP, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_GET_CLIR reply with the following parameters: * * {2,4} */ static const guchar reply_get_clir_valid_parcel1[] = { 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 }; static const struct ril_msg reply_get_clir_valid_1 = { .buf = (gchar *) reply_get_clir_valid_parcel1, .buf_len = sizeof(reply_get_clir_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_GET_CLIR, .serial_no = 0, .error = 0, }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE reply with the following parameters: * * {0} */ static const guchar reply_get_preferred_network_type_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const get_preferred_network_test reply_get_preferred_network_type_valid_1 = { .preferred = 0, .msg = { .buf = (gchar *) reply_get_preferred_network_type_valid_parcel1, .buf_len = sizeof(reply_get_preferred_network_type_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, .serial_no = 0, .error = 0, } }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_QUERY_FACILITY_LOCK reply with the following parameters: * * {0} */ static const guchar reply_query_facility_lock_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct query_facility_lock_test reply_query_facility_lock_valid_1 = { .status = 0, .msg = { .buf = (gchar *) reply_query_facility_lock_valid_parcel1, .buf_len = sizeof(reply_query_facility_lock_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_QUERY_FACILITY_LOCK, .serial_no = 0, .error = 0, } }; /* * The following hexadecimal data contains the event data of a valid * RIL_REQUEST_QUERY_FACILITY_LOCK reply with the following parameters: * * {0,0} (infineon: two integers are returned) */ static const guchar reply_query_facility_lock_valid_parcel2[] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct query_facility_lock_test reply_query_facility_lock_valid_2 = { .status = 0, .msg = { .buf = (gchar *) reply_query_facility_lock_valid_parcel2, .buf_len = sizeof(reply_query_facility_lock_valid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_QUERY_FACILITY_LOCK, .serial_no = 0, .error = 0, } }; /* * The following structure contains test data for a valid * RIL_REQUEST_SET_FACILITY_LOCK reply with no parameters. */ static const struct set_facility_lock_test reply_set_facility_lock_valid_1 = { .retries = 0, .msg = { .buf = NULL, .buf_len = 0, .unsolicited = FALSE, .req = RIL_REQUEST_SET_FACILITY_LOCK, .serial_no = 0, .error = 0, } }; /* * The following structure contains test data for a valid * RIL_REQUEST_SET_FACILITY_LOCK reply with parameter {2} */ static const guchar reply_set_facility_lock_valid_parcel2[] = { 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }; static const struct set_facility_lock_test reply_set_facility_lock_valid_2 = { .retries = 2, .msg = { .buf = (gchar *) reply_set_facility_lock_valid_parcel2, .buf_len = sizeof(reply_set_facility_lock_valid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_SET_FACILITY_LOCK, .serial_no = 0, .error = 0, } }; /* * The following structure contains test data for a valid * RIL_REQUEST_ENTER_SIM_PIN reply with parameter {0} */ static const guchar reply_enter_sim_pin_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct sim_password_test reply_enter_sim_pin_valid_1 = { .retries = -1, .passwd_type = OFONO_SIM_PASSWORD_SIM_PIN, .msg = { .buf = (gchar *) reply_enter_sim_pin_valid_parcel1, .buf_len = sizeof(reply_enter_sim_pin_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_ENTER_SIM_PIN, .serial_no = 0, .error = RIL_E_SUCCESS, } }; /* * The following structure contains test data for a valid * RIL_REQUEST_ENTER_SIM_PIN reply with parameter {2} */ static const guchar reply_enter_sim_pin_valid_parcel2[] = { 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }; static const struct sim_password_test reply_enter_sim_pin_valid_2 = { .retries = 2, .passwd_type = OFONO_SIM_PASSWORD_SIM_PIN, .msg = { .buf = (gchar *) reply_enter_sim_pin_valid_parcel2, .buf_len = sizeof(reply_enter_sim_pin_valid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_ENTER_SIM_PIN, .serial_no = 0, .error = RIL_E_PASSWORD_INCORRECT, } }; /* * The following structure contains test data for a valid * RIL_REQUEST_OEM_HOOK_RAW reply with parameter {4,0x11111111} */ static const guchar reply_oem_hook_raw_valid_parcel1[] = { 0x04, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11 }; static const struct oem_hook_raw_test reply_oem_hook_raw_valid_1 = { .data = reply_oem_hook_raw_valid_parcel1 + sizeof(int32_t), .size = (int) (sizeof(reply_oem_hook_raw_valid_parcel1) - sizeof(int32_t)), .msg = { .buf = (gchar *) reply_oem_hook_raw_valid_parcel1, .buf_len = sizeof(reply_oem_hook_raw_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_OEM_HOOK_RAW, .serial_no = 0, .error = RIL_E_SUCCESS, } }; /* * The following structure contains test data for a valid * RIL_REQUEST_OEM_HOOK_RAW reply with parameter {-1} */ static const guchar reply_oem_hook_raw_valid_parcel2[] = { 0xFF, 0xFF, 0xFF, 0xFF }; static const struct oem_hook_raw_test reply_oem_hook_raw_valid_2 = { .data = NULL, .size = -1, .msg = { .buf = (gchar *) reply_oem_hook_raw_valid_parcel2, .buf_len = sizeof(reply_oem_hook_raw_valid_parcel2), .unsolicited = FALSE, .req = RIL_REQUEST_OEM_HOOK_RAW, .serial_no = 0, .error = RIL_E_SUCCESS, } }; /* * The following structure contains test data for a valid * RIL_REQUEST_OEM_HOOK_STRINGS reply with parameter {+EPINC: 3, 3, 10, 10} */ static const guchar reply_oem_hook_strings_valid_parcel1[] = { 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x45, 0x00, 0x50, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x43, 0x00, 0x3a, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x31, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const char *strings_valid_parcel1[] = { "+EPINC: 3, 3, 10, 10" }; static const struct oem_hook_strings_test reply_oem_hook_strings_valid_1 = { .msg = { .buf = (gchar *) reply_oem_hook_strings_valid_parcel1, .buf_len = sizeof(reply_oem_hook_strings_valid_parcel1), .unsolicited = FALSE, .req = RIL_REQUEST_OEM_HOOK_STRINGS, .serial_no = 0, .error = RIL_E_SUCCESS, }, .num_str = G_N_ELEMENTS(strings_valid_parcel1), .str = strings_valid_parcel1 }; static void test_reply_voice_reg_state_valid(gconstpointer data) { const reg_state_test *test = data; struct reply_reg_state *reply = g_ril_reply_parse_voice_reg_state(NULL, &test->msg); g_assert(reply != NULL); g_assert(reply->status == test->status); g_assert(reply->tech == test->tech); g_free(reply); } static void test_reply_data_reg_state_invalid(gconstpointer data) { struct reply_data_reg_state *reply = g_ril_reply_parse_data_reg_state(NULL, data); g_assert(reply == NULL); } static void test_reply_data_reg_state_valid(gconstpointer data) { const reg_state_test *test = data; struct reply_data_reg_state *reply = g_ril_reply_parse_data_reg_state(NULL, &test->msg); g_assert(reply != NULL); g_assert(reply->reg_state.status == test->status); g_assert(reply->reg_state.tech == test->tech); g_free(reply); } static void test_reply_operator_invalid(gconstpointer data) { struct reply_operator *reply = g_ril_reply_parse_operator(NULL, data); g_assert(reply == NULL); } static void test_reply_operator_valid(gconstpointer data) { struct reply_operator *reply = g_ril_reply_parse_operator(NULL, data); g_assert(reply != NULL); } static void test_reply_avail_ops_invalid(gconstpointer data) { struct reply_avail_ops *reply = g_ril_reply_parse_avail_ops(NULL, data); g_assert(reply == NULL); } static void test_reply_avail_ops_valid(gconstpointer data) { struct reply_avail_ops *reply = g_ril_reply_parse_avail_ops(NULL, data); g_assert(reply != NULL); } static void test_reply_sim_io_valid(gconstpointer data) { struct reply_sim_io *reply = g_ril_reply_parse_sim_io(NULL, data); g_assert(reply != NULL); g_ril_reply_free_sim_io(reply); } static void test_reply_sim_io_invalid(gconstpointer data) { struct reply_sim_io *reply = g_ril_reply_parse_sim_io(NULL, data); g_assert(reply == NULL); } static void test_reply_imsi_valid(gconstpointer data) { gchar *reply = g_ril_reply_parse_imsi(NULL, data); g_assert(reply != NULL); g_free(reply); } static void test_reply_sim_status_valid(gconstpointer data) { struct reply_sim_status *reply; reply = g_ril_reply_parse_sim_status(NULL, data); g_assert(reply != NULL); g_ril_reply_free_sim_status(reply); } static void test_reply_get_smsc_address_valid(gconstpointer data) { struct ofono_phone_number *reply; reply = g_ril_reply_parse_get_smsc_address(NULL, data); g_assert(reply != NULL); g_free(reply); } static void test_reply_get_current_calls_valid(gconstpointer data) { GSList *calls; calls = g_ril_reply_parse_get_calls(NULL, data); g_assert(calls != NULL); g_slist_foreach(calls, (GFunc) g_free, NULL); g_slist_free(calls); } static void test_reply_get_current_calls_invalid(gconstpointer data) { GSList *calls; calls = g_ril_reply_parse_get_calls(NULL, data); g_assert(calls == NULL); } static void test_reply_call_fail_cause_valid(gconstpointer data) { enum ofono_disconnect_reason reason; reason = g_ril_reply_parse_call_fail_cause(NULL, data); g_assert(reason == OFONO_DISCONNECT_REASON_REMOTE_HANGUP); } static void test_reply_get_mute_off(gconstpointer data) { int muted; muted = g_ril_reply_parse_get_mute(NULL, data); g_assert(muted == 0); } static void test_reply_get_mute_on(gconstpointer data) { int muted; muted = g_ril_reply_parse_get_mute(NULL, data); g_assert(muted == 1); } static void test_reply_baseband_version_valid(gconstpointer data) { char *version; version = g_ril_reply_parse_baseband_version(NULL, data); g_assert(version != NULL); g_free(version); } static void test_reply_get_imei_valid(gconstpointer data) { char *imei; imei = g_ril_reply_parse_get_imei(NULL, data); g_assert(imei != NULL); g_free(imei); } static void test_reply_query_call_waiting_valid(gconstpointer data) { int cls; cls = g_ril_reply_parse_query_call_waiting(NULL, data); g_assert(cls != -1); } static void test_reply_query_clip_valid(gconstpointer data) { int clip_status; clip_status = g_ril_reply_parse_query_clip(NULL, data); g_assert(clip_status != -1); } static void test_reply_get_clir_valid(gconstpointer data) { struct reply_clir *reply; reply = g_ril_reply_parse_get_clir(NULL, data); g_assert(reply != NULL); g_ril_reply_free_get_clir(reply); } static void test_reply_get_preferred_network_type_valid(gconstpointer data) { const get_preferred_network_test *test = data; int type = g_ril_reply_parse_get_preferred_network_type(NULL, &test->msg); g_assert(type == test->preferred); } static void test_reply_query_facility_lock_valid(gconstpointer data) { const struct query_facility_lock_test *test = data; int status = g_ril_reply_parse_query_facility_lock(NULL, &test->msg); g_assert(status == test->status); } static void test_reply_set_facility_lock_valid(gconstpointer data) { const struct set_facility_lock_test *test = data; int retries = g_ril_reply_parse_set_facility_lock(NULL, &test->msg); g_assert(retries == test->retries); } static void test_reply_enter_sim_pin_valid(gconstpointer data) { const struct sim_password_test *test = data; int *retries = g_ril_reply_parse_retries(NULL, &test->msg, test->passwd_type); g_assert(retries != NULL); g_assert(retries[test->passwd_type] == test->retries); g_free(retries); } static void test_reply_oem_hook_raw_valid(gconstpointer data) { const struct oem_hook_raw_test *test = data; struct reply_oem_hook *reply = g_ril_reply_oem_hook_raw(NULL, &test->msg); g_assert(reply->length == test->size); if (reply->length >= 0) g_assert(!memcmp(reply->data, test->data, test->size)); else g_assert(reply->data == NULL); g_ril_reply_free_oem_hook(reply); } static void test_reply_oem_hook_strings_valid(gconstpointer data) { int i; const struct oem_hook_strings_test *test = data; struct parcel_str_array *reply = g_ril_reply_oem_hook_strings(NULL, &test->msg); g_assert(reply != NULL); g_assert(reply->num_str == test->num_str); for (i = 0; i < reply->num_str; ++i) g_assert(strcmp(reply->str[i], test->str[i]) == 0); parcel_free_str_array(reply); } #endif int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN g_test_add_data_func("/testgrilreply/gprs: " "invalid DATA_REG_STATE Test 1", &reply_data_reg_state_invalid_1, test_reply_data_reg_state_invalid); g_test_add_data_func("/testgrilreply/gprs: " "invalid DATA_REG_STATE Test 2", &reply_data_reg_state_invalid_2, test_reply_data_reg_state_invalid); g_test_add_data_func("/testgrilreply/gprs: " "invalid DATA_REG_STATE Test 3", &reply_data_reg_state_invalid_3, test_reply_data_reg_state_invalid); g_test_add_data_func("/testgrilreply/gprs: " "valid DATA_REG_STATE Test 1", &data_reg_valid_1, test_reply_data_reg_state_valid); g_test_add_data_func("/testgrilreply/gprs: " "valid DATA_REG_STATE Test 2", &data_reg_valid_2, test_reply_data_reg_state_valid); g_test_add_data_func("/testgrilreply/gprs: " "valid DATA_REG_STATE Test 3", &data_reg_valid_3, test_reply_data_reg_state_valid); g_test_add_data_func("/testgrilreply/gprs: " "valid DATA_REG_STATE Test 4", &data_reg_valid_4, test_reply_data_reg_state_valid); g_test_add_data_func("/testgrilreply/gprs: " "valid DATA_REG_STATE Test 5", &data_reg_valid_5, test_reply_data_reg_state_valid); g_test_add_data_func("/testgrilreply/gprs: " "valid DATA_REG_STATE Test 6", &data_reg_valid_6, test_reply_data_reg_state_valid); g_test_add_data_func("/testgrilreply/gprs: " "valid DATA_REG_STATE Test 7", &data_reg_valid_7, test_reply_data_reg_state_valid); g_test_add_data_func("/testgrilreply/gprs: " "valid DATA_REG_STATE Test 8", &data_reg_valid_8, test_reply_data_reg_state_valid); g_test_add_data_func("/testgrilreply/gprs: " "valid DATA_REG_STATE Test 9", &data_reg_valid_9, test_reply_data_reg_state_valid); g_test_add_data_func("/testgrilreply/gprs: " "valid DATA_REG_STATE Test 10", &data_reg_valid_10, test_reply_data_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 1", &voice_reg_valid_1, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 2", &voice_reg_valid_2, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 3", &voice_reg_valid_3, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 4", &voice_reg_valid_4, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 5", &voice_reg_valid_5, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 6", &voice_reg_valid_6, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 7", &voice_reg_valid_7, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 8", &voice_reg_valid_8, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 9", &voice_reg_valid_9, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 10", &voice_reg_valid_10, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 11", &voice_reg_valid_11, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "valid VOICE_REG_STATE Test 12", &voice_reg_valid_12, test_reply_voice_reg_state_valid); g_test_add_data_func("/testgrilreply/netreg: " "invalid GET_OPERATOR Test 1", &reply_operator_invalid_1, test_reply_operator_invalid); g_test_add_data_func("/testgrilreply/netreg: " "invalid GET_OPERATOR Test 2", &reply_operator_invalid_2, test_reply_operator_invalid); g_test_add_data_func("/testgrilreply/netreg: " "valid GET_OPERATOR Test 1", &reply_operator_valid_1, test_reply_operator_valid); g_test_add_data_func("/testgrilreply/netreg: " "invalid QUERY_AVAIL_OPS Test 1", &reply_avail_ops_invalid_1, test_reply_avail_ops_invalid); g_test_add_data_func("/testgrilreply/netreg: " "invalid QUERY_AVAIL_OPS Test 2", &reply_avail_ops_invalid_2, test_reply_avail_ops_invalid); g_test_add_data_func("/testgrilreply/netreg: " "valid QUERY_AVAIL_OPS Test 1", &reply_avail_ops_valid_1, test_reply_avail_ops_valid); g_test_add_data_func("/testgrilreply/sim: " "valid SIM_IO Test 1", &reply_sim_io_valid_1, test_reply_sim_io_valid); g_test_add_data_func("/testgrilreply/sim: " "valid SIM_IO Test 2", &reply_sim_io_valid_2, test_reply_sim_io_valid); g_test_add_data_func("/testgrilreply/sim: " "invalid SIM_IO Test 1", &reply_sim_io_invalid_1, test_reply_sim_io_invalid); g_test_add_data_func("/testgrilreply/sim: " "invalid SIM_IO Test 2", &reply_sim_io_invalid_2, test_reply_sim_io_invalid); g_test_add_data_func("/testgrilreply/sim: " "valid GET_IMSI Test 1", &reply_imsi_valid_1, test_reply_imsi_valid); g_test_add_data_func("/testgrilreply/sim: " "valid GET_SIM_STATUS Test 1", &reply_sim_status_valid_1, test_reply_sim_status_valid); g_test_add_data_func("/testgrilreply/sim: " "valid GET_SMSC_ADDRESS Test 1", &reply_get_smsc_address_valid_1, test_reply_get_smsc_address_valid); g_test_add_data_func("/testgrilreply/voicecall: " "valid GET_CURRENT_CALLS Test 1", &reply_get_current_calls_valid_1, test_reply_get_current_calls_valid); g_test_add_data_func("/testgrilreply/voicecall: " "invalid GET_CURRENT_CALLS Test 1", &reply_get_current_calls_invalid_1, test_reply_get_current_calls_invalid); g_test_add_data_func("/testgrilreply/voicecall: " "invalid GET_CURRENT_CALLS Test 2", &reply_get_current_calls_invalid_2, test_reply_get_current_calls_invalid); g_test_add_data_func("/testgrilreply/voicecall: " "valid CALL_FAIL_CAUSE Test 1", &reply_call_fail_cause_valid_1, test_reply_call_fail_cause_valid); g_test_add_data_func("/testgrilreply/call-volume: " "off GET_MUTE Test 1", &reply_get_mute_off_1, test_reply_get_mute_off); g_test_add_data_func("/testgrilreply/call-volume: " "on GET_MUTE Test 1", &reply_get_mute_on_1, test_reply_get_mute_on); g_test_add_data_func("/testgrilreply/devinfo: " "valid BASEBAND_VERSION Test 1", &reply_baseband_version_valid_1, test_reply_baseband_version_valid); g_test_add_data_func("/testgrilreply/devinfo: " "valid GET_IMEI Test 1", &reply_get_imei_valid_1, test_reply_get_imei_valid); g_test_add_data_func("/testgrilreply/call-settings: " "valid QUERY_CALL_WAITING Test 1", &reply_query_call_waiting_valid_1, test_reply_query_call_waiting_valid); g_test_add_data_func("/testgrilreply/call-settings: " "valid QUERY_CLIP Test 1", &reply_query_clip_valid_1, test_reply_query_clip_valid); g_test_add_data_func("/testgrilreply/call-settings: " "valid GET_CLIR Test 1", &reply_get_clir_valid_1, test_reply_get_clir_valid); g_test_add_data_func("/testgrilreply/radio-settings: " "valid GET_PREFERRED_NETWORK_TYPE Test 1", &reply_get_preferred_network_type_valid_1, test_reply_get_preferred_network_type_valid); g_test_add_data_func("/testgrilreply/call-barring: " "valid QUERY_FACILITY_LOCK Test 1", &reply_query_facility_lock_valid_1, test_reply_query_facility_lock_valid); g_test_add_data_func("/testgrilreply/call-barring: " "valid QUERY_FACILITY_LOCK Test 2", &reply_query_facility_lock_valid_2, test_reply_query_facility_lock_valid); g_test_add_data_func("/testgrilreply/call-barring: " "valid SET_FACILITY_LOCK Test 1", &reply_set_facility_lock_valid_1, test_reply_set_facility_lock_valid); g_test_add_data_func("/testgrilreply/call-barring: " "valid SET_FACILITY_LOCK Test 2", &reply_set_facility_lock_valid_2, test_reply_set_facility_lock_valid); g_test_add_data_func("/testgrilreply/sim: " "valid ENTER_SIM_PIN Test 1", &reply_enter_sim_pin_valid_1, test_reply_enter_sim_pin_valid); g_test_add_data_func("/testgrilreply/sim: " "valid ENTER_SIM_PIN Test 2", &reply_enter_sim_pin_valid_2, test_reply_enter_sim_pin_valid); g_test_add_data_func("/testgrilreply/oem: " "valid OEM_HOOK_RAW Test 1", &reply_oem_hook_raw_valid_1, test_reply_oem_hook_raw_valid); g_test_add_data_func("/testgrilreply/oem: " "valid OEM_HOOK_RAW Test 2", &reply_oem_hook_raw_valid_2, test_reply_oem_hook_raw_valid); g_test_add_data_func("/testgrilreply/oem: " "valid OEM_HOOK_STRINGS Test 1", &reply_oem_hook_strings_valid_1, test_reply_oem_hook_strings_valid); #endif return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/rilmodem-test-server.h0000644000015600001650000000254512671500073023632 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2015 Canonical 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. * * 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 server_data; struct rilmodem_test_data { const unsigned char *req_data; const size_t req_size; uint32_t rsp_error; const unsigned char *rsp_data; const size_t rsp_size; gboolean unsol_test; }; typedef void (*ConnectFunc)(void *data); void rilmodem_test_server_close(struct server_data *sd); struct server_data *rilmodem_test_server_create(ConnectFunc connect, const struct rilmodem_test_data *test_data, void *data); void rilmodem_test_server_write(struct server_data *sd, const unsigned char *buf, const size_t buf_len); const char *rilmodem_test_get_socket_name(struct server_data *sd); ofono-1.17.bzr6912+16.04.20160314.3/unit/test-rilmodem-sms.c0000644000015600001650000004237112671500073023122 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2015 Canonical 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. * * 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 #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "ril_constants.h" #include "rilmodem-test-server.h" static GMainLoop *mainloop; static const struct ofono_sms_driver *smsdriver; struct rilmodem_sms_data { GRil *ril; struct ofono_modem *modem; gconstpointer test_data; struct ofono_sms *sms; struct server_data *serverd; }; typedef gboolean (*StartFunc)(gpointer data); struct sms_data { StartFunc start_func; const unsigned char *pdu; gint pdu_len; gint tpdu_len; gint mms; struct rilmodem_test_data rtd; enum ofono_error_type error_type; const struct ofono_phone_number ph; gint mr; }; static void sca_query_callback(const struct ofono_error *error, const struct ofono_phone_number *ph, gpointer data) { struct rilmodem_sms_data *rsd = data; const struct sms_data *sd = rsd->test_data; g_assert(error->type == sd->error_type); if (error->type == OFONO_ERROR_TYPE_NO_ERROR) { g_assert(ph->type == sd->ph.type); g_assert(strcmp(ph->number, sd->ph.number) == 0); } g_main_loop_quit(mainloop); } static void sca_set_callback(const struct ofono_error *error, gpointer data) { struct rilmodem_sms_data *rsd = data; const struct sms_data *sd = rsd->test_data; g_assert(error->type == sd->error_type); g_main_loop_quit(mainloop); } static void submit_callback(const struct ofono_error *error, int mr, gpointer data) { struct rilmodem_sms_data *rsd = data; const struct sms_data *sd = rsd->test_data; g_assert(error->type == sd->error_type); g_assert(mr == sd->mr); g_main_loop_quit(mainloop); } static gboolean trigger_sca_query(gpointer data) { struct rilmodem_sms_data *rsd = data; g_assert(smsdriver->sca_query != NULL); smsdriver->sca_query(rsd->sms, sca_query_callback, rsd); return FALSE; } static gboolean trigger_sca_set(gpointer data) { struct rilmodem_sms_data *rsd = data; const struct sms_data *sd = rsd->test_data; g_assert(smsdriver->sca_set != NULL); smsdriver->sca_set(rsd->sms, &sd->ph, sca_set_callback, rsd); return FALSE; } static gboolean trigger_submit(gpointer data) { struct rilmodem_sms_data *rsd = data; const struct sms_data *sd = rsd->test_data; g_assert(smsdriver->submit != NULL); smsdriver->submit(rsd->sms, sd->pdu, sd->pdu_len, sd->tpdu_len, sd->mms, submit_callback, rsd); return FALSE; } static gboolean trigger_new_sms(gpointer data) { struct rilmodem_sms_data *rsd = data; const struct sms_data *sd = rsd->test_data; rilmodem_test_server_write(rsd->serverd, sd->rtd.req_data, sd->rtd.req_size); return FALSE; } /* RIL_REQUEST_GET_SMSC_ADDRESS */ static const guchar req_get_smsc_address_parcel_1[] = { 0x00, 0x00, 0x00, 0x08, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* * RIL_REQUEST_GET_SMSC_ADDRESS reply with the following data: * * {number="+34607003110"} */ static const guchar rsp_get_smsc_address_data_1[] = { 0x0d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x34, 0x00, 0x36, 0x00, 0x30, 0x00, 0x37, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct sms_data testdata_sca_query_valid_1 = { .start_func = trigger_sca_query, .rtd = { .req_data = req_get_smsc_address_parcel_1, .req_size = sizeof(req_get_smsc_address_parcel_1), .rsp_data = rsp_get_smsc_address_data_1, .rsp_size = sizeof(rsp_get_smsc_address_data_1), .rsp_error = RIL_E_SUCCESS, }, .ph = { .number = "34607003110", .type = 145 }, .error_type = OFONO_ERROR_TYPE_NO_ERROR, }; /* * RIL_REQUEST_GET_SMSC_ADDRESS reply with no data, which should * trigger a callback failure. */ static const struct sms_data testdata_sca_query_invalid_1 = { .start_func = trigger_sca_query, .rtd = { .req_data = req_get_smsc_address_parcel_1, .req_size = sizeof(req_get_smsc_address_parcel_1), .rsp_error = RIL_E_SUCCESS, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* * RIL_REQUEST_GET_SMSC_ADDRESS reply with no quotes found which * should trigger a callback failure. */ static const guchar rsp_get_smsc_address_data_3[] = { 0x02, 0x00, 0x00, 0x00, 0x22, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct sms_data testdata_sca_query_invalid_2 = { .start_func = trigger_sca_query, .ph = { .number = "34607003110", .type = 145 }, .rtd = { .req_data = req_get_smsc_address_parcel_1, .req_size = sizeof(req_get_smsc_address_parcel_1), .rsp_data = rsp_get_smsc_address_data_3, .rsp_size = sizeof(rsp_get_smsc_address_data_3), .rsp_error = RIL_E_SUCCESS, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* GENERIC_FAILURE returned in RIL reply */ static const struct sms_data testdata_sca_query_invalid_3 = { .start_func = trigger_sca_query, .rtd = { .req_data = req_get_smsc_address_parcel_1, .req_size = sizeof(req_get_smsc_address_parcel_1), .rsp_error = RIL_E_GENERIC_FAILURE, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* * RIL_REQUEST_SET_SMSC_ADDRESS with the following data: * * {number="+34607003110"} */ static const guchar req_set_smsc_address_parcel_1[] = { +0x00, 0x00, 0x00, 0x2C, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x0e, 0x00, 0x00, 0x00, 0x22, 0x00, 0x2b, 0x00, 0x33, 0x00, 0x34, 0x00, +0x36, 0x00, 0x30, 0x00, 0x37, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, +0x31, 0x00, 0x31, 0x00, 0x30, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct sms_data testdata_sca_set_valid_1 = { .start_func = trigger_sca_set, .ph = { .number = "34607003110", .type = 145 }, .rtd = { .req_data = req_set_smsc_address_parcel_1, .req_size = sizeof(req_set_smsc_address_parcel_1), .rsp_error = RIL_E_SUCCESS, }, .error_type = OFONO_ERROR_TYPE_NO_ERROR, }; /* GENERIC_FAILURE returned in RIL reply */ static const struct sms_data testdata_sca_set_invalid_1 = { .start_func = trigger_sca_set, .ph = { .number = "34607003110", .type = 145 }, .rtd = { .req_data = req_set_smsc_address_parcel_1, .req_size = sizeof(req_set_smsc_address_parcel_1), .rsp_error = RIL_E_GENERIC_FAILURE, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; static const unsigned char req_send_sms_pdu_valid_1[] = { 0x00, 0x11, 0x00, 0x09, 0x81, 0x36, 0x54, 0x39, 0x80, 0xf5, 0x00, 0x00, 0xa7, 0x0a, 0xc8, 0x37, 0x3b, 0x0c, 0x6a, 0xd7, 0xdd, 0xe4, 0x37 }; static const guchar req_send_sms_parcel_1[] = { 0x00, 0x00, 0x00, 0x70, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x39, 0x00, 0x38, 0x00, 0x31, 0x00, 0x33, 0x00, 0x36, 0x00, 0x35, 0x00, 0x34, 0x00, 0x33, 0x00, 0x39, 0x00, 0x38, 0x00, 0x30, 0x00, 0x46, 0x00, 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x41, 0x00, 0x37, 0x00, 0x30, 0x00, 0x41, 0x00, 0x43, 0x00, 0x38, 0x00, 0x33, 0x00, 0x37, 0x00, 0x33, 0x00, 0x42, 0x00, 0x30, 0x00, 0x43, 0x00, 0x36, 0x00, 0x41, 0x00, 0x44, 0x00, 0x37, 0x00, 0x44, 0x00, 0x44, 0x00, 0x45, 0x00, 0x34, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* * SEND_SMS reply with the following data: * * messageRef=1 * ackPDU=NULL * errorCode=0 */ static const guchar rsp_send_sms_valid_1[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const struct sms_data testdata_submit_valid_1 = { .start_func = trigger_submit, .pdu = req_send_sms_pdu_valid_1, .pdu_len = sizeof(req_send_sms_pdu_valid_1), .tpdu_len = sizeof(req_send_sms_pdu_valid_1) - 1, .mms = 0, .rtd = { .req_data = req_send_sms_parcel_1, .req_size = sizeof(req_send_sms_parcel_1), .rsp_data = rsp_send_sms_valid_1, .rsp_size = sizeof(rsp_send_sms_valid_1), .rsp_error = RIL_E_SUCCESS, }, .mr = 1, .error_type = OFONO_ERROR_TYPE_NO_ERROR, }; /* * SEND_SMS reply with failure indicated */ static const struct sms_data testdata_submit_invalid_1 = { .start_func = trigger_submit, .pdu = req_send_sms_pdu_valid_1, .pdu_len = sizeof(req_send_sms_pdu_valid_1), .tpdu_len = sizeof(req_send_sms_pdu_valid_1) - 1, .mms = 0, .rtd = { .req_data = req_send_sms_parcel_1, .req_size = sizeof(req_send_sms_parcel_1), .rsp_error = RIL_E_GENERIC_FAILURE, }, .error_type = OFONO_ERROR_TYPE_FAILURE, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid RIL_UNSOL_RESPONSE_NEW_SMS message * with the following parameter (SMSC address length is 7): * * {07914306073011F0040B914336543980F50000310113212002400AC8373B0C6AD7DDE437} * {069143060730F0040B914336543980F50000310113212002400AC8373B0C6AD7DDE437} */ static const guchar unsol_response_new_sms_parcel_1[] = { 0x00, 0x00, 0x00, 0xA0, 0x01, 0x00, 0x00, 0x00, 0xEB, 0x03, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x37, 0x00, 0x39, 0x00, 0x31, 0x00, 0x34, 0x00, 0x33, 0x00, 0x30, 0x00, 0x36, 0x00, 0x30, 0x00, 0x37, 0x00, 0x33, 0x00, 0x30, 0x00, 0x31, 0x00, 0x31, 0x00, 0x46, 0x00, 0x30, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x42, 0x00, 0x39, 0x00, 0x31, 0x00, 0x34, 0x00, 0x33, 0x00, 0x33, 0x00, 0x36, 0x00, 0x35, 0x00, 0x34, 0x00, 0x33, 0x00, 0x39, 0x00, 0x38, 0x00, 0x30, 0x00, 0x46, 0x00, 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x31, 0x00, 0x30, 0x00, 0x31, 0x00, 0x31, 0x00, 0x33, 0x00, 0x32, 0x00, 0x31, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x30, 0x00, 0x30, 0x00, 0x41, 0x00, 0x43, 0x00, 0x38, 0x00, 0x33, 0x00, 0x37, 0x00, 0x33, 0x00, 0x42, 0x00, 0x30, 0x00, 0x43, 0x00, 0x36, 0x00, 0x41, 0x00, 0x44, 0x00, 0x37, 0x00, 0x44, 0x00, 0x44, 0x00, 0x45, 0x00, 0x34, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00 }; const unsigned char new_sms_pdu_valid_1[] = { 0x07, 0x91, 0x43, 0x06, 0x07, 0x30, 0x11, 0xf0, 0x04, 0x0b, 0x91, 0x43, 0x36, 0x54, 0x39, 0x80, 0xf5, 0x00, 0x00, 0x31, 0x01, 0x13, 0x21, 0x20, 0x02, 0x40, 0x0a, 0xc8, 0x37, 0x3b, 0x0c, 0x6a, 0xd7, 0xdd, 0xe4, 0x37 }; static const struct sms_data testdata_new_sms_valid_1 = { .start_func = trigger_new_sms, .rtd = { .req_data = unsol_response_new_sms_parcel_1, .req_size = sizeof(unsol_response_new_sms_parcel_1), .unsol_test = TRUE, }, .pdu = new_sms_pdu_valid_1, .pdu_len = sizeof(new_sms_pdu_valid_1), .tpdu_len = 28, }; /* * The following hexadecimal data represents a serialized Binder parcel * instance containing a valid UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT message * with the following parameter (SMSC address length is 6): * * {069143060730F0040B914336543980F50000310113212002400AC8373B0C6AD7DDE437} */ static const guchar unsol_response_new_sms_parcel_2[] = { 0x00, 0x00, 0x00, 0x9C, 0x01, 0x00, 0x00, 0x00, 0xEC, 0x03, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x30, 0x00, 0x36, 0x00, 0x39, 0x00, 0x31, 0x00, 0x34, 0x00, 0x33, 0x00, 0x30, 0x00, 0x36, 0x00, 0x30, 0x00, 0x37, 0x00, 0x33, 0x00, 0x30, 0x00, 0x46, 0x00, 0x30, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x42, 0x00, 0x39, 0x00, 0x31, 0x00, 0x34, 0x00, 0x33, 0x00, 0x33, 0x00, 0x36, 0x00, 0x35, 0x00, 0x34, 0x00, 0x33, 0x00, 0x39, 0x00, 0x38, 0x00, 0x30, 0x00, 0x46, 0x00, 0x35, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x31, 0x00, 0x30, 0x00, 0x31, 0x00, 0x31, 0x00, 0x33, 0x00, 0x32, 0x00, 0x31, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x30, 0x00, 0x30, 0x00, 0x41, 0x00, 0x43, 0x00, 0x38, 0x00, 0x33, 0x00, 0x37, 0x00, 0x33, 0x00, 0x42, 0x00, 0x30, 0x00, 0x43, 0x00, 0x36, 0x00, 0x41, 0x00, 0x44, 0x00, 0x37, 0x00, 0x44, 0x00, 0x44, 0x00, 0x45, 0x00, 0x34, 0x00, 0x33, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00 }; const unsigned char new_sms_pdu_valid_2[] = { 0x06, 0x91, 0x43, 0x06, 0x07, 0x30, 0xf0, 0x04, 0x0b, 0x91, 0x43, 0x36, 0x54, 0x39, 0x80, 0xf5, 0x00, 0x00, 0x31, 0x01, 0x13, 0x21, 0x20, 0x02, 0x40, 0x0a, 0xc8, 0x37, 0x3b, 0x0c, 0x6a, 0xd7, 0xdd, 0xe4, 0x37 }; static const struct sms_data testdata_new_sms_valid_2 = { .start_func = trigger_new_sms, .rtd = { .req_data = unsol_response_new_sms_parcel_2, .req_size = sizeof(unsol_response_new_sms_parcel_2), .unsol_test = TRUE, }, .pdu = new_sms_pdu_valid_2, .pdu_len = sizeof(new_sms_pdu_valid_2), .tpdu_len = 28, }; /* Declarations && Re-implementations of core functions. */ void ril_sms_exit(void); void ril_sms_init(void); struct ofono_sms { void *driver_data; const struct sms_data *sd; }; struct ofono_sms *ofono_sms_create(struct ofono_modem *modem, unsigned int vendor, const char *driver, void *data) { struct rilmodem_sms_data *rsd = data; struct ofono_sms *sms = g_new0(struct ofono_sms, 1); int retval; retval = smsdriver->probe(sms, OFONO_RIL_VENDOR_AOSP, rsd->ril); g_assert(retval == 0); return sms; } int ofono_sms_driver_register(const struct ofono_sms_driver *d) { if (smsdriver == NULL) smsdriver = d; return 0; } void ofono_sms_set_data(struct ofono_sms *sms, void *data) { sms->driver_data = data; } void *ofono_sms_get_data(struct ofono_sms *sms) { return sms->driver_data; } void ofono_sms_register(struct ofono_sms *sms) { } void ofono_sms_driver_unregister(const struct ofono_sms_driver *d) { } void ofono_sms_deliver_notify(struct ofono_sms *sms, const unsigned char *pdu, int len, int tpdu_len) { g_assert(sms->sd->pdu_len == len); g_assert(sms->sd->tpdu_len == tpdu_len); g_assert(!memcmp(pdu, sms->sd->pdu, len)); g_main_loop_quit(mainloop); } void ofono_sms_status_notify(struct ofono_sms *sms, const unsigned char *pdu, int len, int tpdu_len) { ofono_sms_deliver_notify(sms, pdu, len, tpdu_len); } /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN static void server_connect_cb(gpointer data) { struct rilmodem_sms_data *rsd = data; const struct sms_data *sd = rsd->test_data; /* This causes local impl of _create() to call driver's probe func. */ rsd->sms = ofono_sms_create(NULL, OFONO_RIL_VENDOR_AOSP, "rilmodem", rsd); rsd->sms->sd = sd; /* add_idle doesn't work, read blocks main loop!!! */ if (sd->rtd.unsol_test) g_idle_add(sd->start_func, (void *) rsd); else g_assert(sd->start_func(rsd) == FALSE); } /* * This unit test: * - does some test data setup * - configures a dummy server socket * - creates a new gril client instance * - triggers a connect to the dummy * server socket * - starts a mainloop */ static void test_sms_func(gconstpointer data) { const struct sms_data *sd = data; struct rilmodem_sms_data *rsd; ril_sms_init(); rsd = g_new0(struct rilmodem_sms_data, 1); rsd->test_data = sd; rsd->serverd = rilmodem_test_server_create(&server_connect_cb, &sd->rtd, rsd); rsd->ril = g_ril_new(rilmodem_test_get_socket_name(rsd->serverd), OFONO_RIL_VENDOR_AOSP); g_assert(rsd->ril != NULL); mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); g_main_loop_unref(mainloop); smsdriver->remove(rsd->sms); g_ril_unref(rsd->ril); g_free(rsd); rilmodem_test_server_close(rsd->serverd); ril_sms_exit(); } #endif int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); /* * As all our architectures are little-endian except for * PowerPC, and the Binder wire-format differs slightly * depending on endian-ness, the following guards against test * failures when run on PowerPC. */ #if BYTE_ORDER == LITTLE_ENDIAN g_test_add_data_func("/testrilmodemsms/sca_query/valid/1", &testdata_sca_query_valid_1, test_sms_func); g_test_add_data_func("/testrilmodemsms/sca_query/invalid/1", &testdata_sca_query_invalid_1, test_sms_func); g_test_add_data_func("/testrilmodemsms/sca_query/invalid/2", &testdata_sca_query_invalid_2, test_sms_func); g_test_add_data_func("/testrilmodemsms/sca_query/invalid/3", &testdata_sca_query_invalid_3, test_sms_func); g_test_add_data_func("/testrilmodemsms/sca_set/valid/1", &testdata_sca_set_valid_1, test_sms_func); g_test_add_data_func("/testrilmodemsms/sca_set/invalid/1", &testdata_sca_set_invalid_1, test_sms_func); g_test_add_data_func("/testrilmodemsms/submit/valid/1", &testdata_submit_valid_1, test_sms_func); g_test_add_data_func("/testrilmodemsms/submit/invalid/1", &testdata_submit_invalid_1, test_sms_func); g_test_add_data_func("/testrilmodemsms/new_sms/valid/1", &testdata_new_sms_valid_1, test_sms_func); g_test_add_data_func("/testrilmodemsms/new_sms/valid/2", &testdata_new_sms_valid_2, test_sms_func); #endif return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-util.c0000644000015600001650000005430412671500024021462 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include "util.h" const unsigned char invalid_gsm_extended[] = { 0x1b, 0x15 }; const unsigned char invalid_gsm_extended_len[] = { 0x1b, 0x28, 0x1b }; const unsigned char invalid_ucs2[] = { 0x03, 0x93, 0x00, 0x00 }; unsigned short gsm_to_unicode_map[] = { 0x00, 0x0040, 0x01, 0x00A3, 0x02, 0x0024, 0x03, 0x00A5, 0x04, 0x00E8, 0x05, 0x00E9, 0x06, 0x00F9, 0x07, 0x00EC, 0x08, 0x00F2, 0x09, 0x00C7, 0x0A, 0x000A, 0x0B, 0x00D8, 0x0C, 0x00F8, 0x0D, 0x000D, 0x0E, 0x00C5, 0x0F, 0x00E5, 0x10, 0x0394, 0x11, 0x005F, 0x12, 0x03A6, 0x13, 0x0393, 0x14, 0x039B, 0x15, 0x03A9, 0x16, 0x03A0, 0x17, 0x03A8, 0x18, 0x03A3, 0x19, 0x0398, 0x1A, 0x039E, /*0x1B, 0x00A0,*/ 0x1B0A, 0x000C, 0x1B14, 0x005E, 0x1B28, 0x007B, 0x1B29, 0x007D, 0x1B2F, 0x005C, 0x1B3C, 0x005B, 0x1B3D, 0x007E, 0x1B3E, 0x005D, 0x1B40, 0x007C, 0x1B65, 0x20AC, 0x1C, 0x00C6, 0x1D, 0x00E6, 0x1E, 0x00DF, 0x1F, 0x00C9, 0x20, 0x0020, 0x21, 0x0021, 0x22, 0x0022, 0x23, 0x0023, 0x24, 0x00A4, 0x25, 0x0025, 0x26, 0x0026, 0x27, 0x0027, 0x28, 0x0028, 0x29, 0x0029, 0x2A, 0x002A, 0x2B, 0x002B, 0x2C, 0x002C, 0x2D, 0x002D, 0x2E, 0x002E, 0x2F, 0x002F, 0x30, 0x0030, 0x31, 0x0031, 0x32, 0x0032, 0x33, 0x0033, 0x34, 0x0034, 0x35, 0x0035, 0x36, 0x0036, 0x37, 0x0037, 0x38, 0x0038, 0x39, 0x0039, 0x3A, 0x003A, 0x3B, 0x003B, 0x3C, 0x003C, 0x3D, 0x003D, 0x3E, 0x003E, 0x3F, 0x003F, 0x40, 0x00A1, 0x41, 0x0041, 0x42, 0x0042, 0x43, 0x0043, 0x44, 0x0044, 0x45, 0x0045, 0x46, 0x0046, 0x47, 0x0047, 0x48, 0x0048, 0x49, 0x0049, 0x4A, 0x004A, 0x4B, 0x004B, 0x4C, 0x004C, 0x4D, 0x004D, 0x4E, 0x004E, 0x4F, 0x004F, 0x50, 0x0050, 0x51, 0x0051, 0x52, 0x0052, 0x53, 0x0053, 0x54, 0x0054, 0x55, 0x0055, 0x56, 0x0056, 0x57, 0x0057, 0x58, 0x0058, 0x59, 0x0059, 0x5A, 0x005A, 0x5B, 0x00C4, 0x5C, 0x00D6, 0x5D, 0x00D1, 0x5E, 0x00DC, 0x5F, 0x00A7, 0x60, 0x00BF, 0x61, 0x0061, 0x62, 0x0062, 0x63, 0x0063, 0x64, 0x0064, 0x65, 0x0065, 0x66, 0x0066, 0x67, 0x0067, 0x68, 0x0068, 0x69, 0x0069, 0x6A, 0x006A, 0x6B, 0x006B, 0x6C, 0x006C, 0x6D, 0x006D, 0x6E, 0x006E, 0x6F, 0x006F, 0x70, 0x0070, 0x71, 0x0071, 0x72, 0x0072, 0x73, 0x0073, 0x74, 0x0074, 0x75, 0x0075, 0x76, 0x0076, 0x77, 0x0077, 0x78, 0x0078, 0x79, 0x0079, 0x7A, 0x007A, 0x7B, 0x00E4, 0x7C, 0x00F6, 0x7D, 0x00F1, 0x7E, 0x00FC, 0x7F, 0x00E0, }; unsigned short gsm_turkish_to_unicode_map[] = { 0x00, 0x0040, 0x01, 0x00A3, 0x02, 0x0024, 0x03, 0x00A5, 0x04, 0x20AC, 0x05, 0x00E9, 0x06, 0x00F9, 0x07, 0x0131, 0x08, 0x00F2, 0x09, 0x00C7, 0x0A, 0x000A, 0x0B, 0x011E, 0x0C, 0x011F, 0x0D, 0x000D, 0x0E, 0x00C5, 0x0F, 0x00E5, 0x10, 0x0394, 0x11, 0x005F, 0x12, 0x03A6, 0x13, 0x0393, 0x14, 0x039B, 0x15, 0x03A9, 0x16, 0x03A0, 0x17, 0x03A8, 0x18, 0x03A3, 0x19, 0x0398, 0x1A, 0x039E, /* We're not including some of the single shift codes to this map, * because the turkish variant isn't symmetric, i.e., the same * character is present in both the locking shift table as well as the * single shift table */ 0x1B0A, 0x000C, 0x1B14, 0x005E, 0x1B28, 0x007B, 0x1B29, 0x007D, 0x1B2F, 0x005C, 0x1B3C, 0x005B, 0x1B3D, 0x007E, 0x1B3E, 0x005D, 0x1B40, 0x007C, /*0x1B47, 0x011E,*/ /*0x1B49, 0x0130,*/ /*0x1B53, 0x015E,*/ /*0x1B63, 0x00E7,*/ /*0x1B65, 0x20AC,*/ /*0x1B67, 0x011F,*/ /*0x1B69, 0x0131,*/ /*0x1B73, 0x015F,*/ 0x1C, 0x015E, 0x1D, 0x015F, 0x1E, 0x00DF, 0x1F, 0x00C9, 0x20, 0x0020, 0x21, 0x0021, 0x22, 0x0022, 0x23, 0x0023, 0x24, 0x00A4, 0x25, 0x0025, 0x26, 0x0026, 0x27, 0x0027, 0x28, 0x0028, 0x29, 0x0029, 0x2A, 0x002A, 0x2B, 0x002B, 0x2C, 0x002C, 0x2D, 0x002D, 0x2E, 0x002E, 0x2F, 0x002F, 0x30, 0x0030, 0x31, 0x0031, 0x32, 0x0032, 0x33, 0x0033, 0x34, 0x0034, 0x35, 0x0035, 0x36, 0x0036, 0x37, 0x0037, 0x38, 0x0038, 0x39, 0x0039, 0x40, 0x0130, 0x3A, 0x003A, 0x3B, 0x003B, 0x3C, 0x003C, 0x3D, 0x003D, 0x3E, 0x003E, 0x3F, 0x003F, 0x40, 0x0130, 0x41, 0x0041, 0x42, 0x0042, 0x43, 0x0043, 0x44, 0x0044, 0x45, 0x0045, 0x46, 0x0046, 0x47, 0x0047, 0x48, 0x0048, 0x49, 0x0049, 0x4A, 0x004A, 0x4B, 0x004B, 0x4C, 0x004C, 0x4D, 0x004D, 0x4E, 0x004E, 0x4F, 0x004F, 0x50, 0x0050, 0x51, 0x0051, 0x52, 0x0052, 0x53, 0x0053, 0x54, 0x0054, 0x55, 0x0055, 0x56, 0x0056, 0x57, 0x0057, 0x58, 0x0058, 0x59, 0x0059, 0x5A, 0x005A, 0x5B, 0x00C4, 0x5C, 0x00D6, 0x5D, 0x00D1, 0x5E, 0x00DC, 0x5F, 0x00A7, 0x60, 0x00E7, 0x61, 0x0061, 0x62, 0x0062, 0x63, 0x0063, 0x64, 0x0064, 0x65, 0x0065, 0x66, 0x0066, 0x67, 0x0067, 0x68, 0x0068, 0x69, 0x0069, 0x6A, 0x006A, 0x6B, 0x006B, 0x6C, 0x006C, 0x6D, 0x006D, 0x6E, 0x006E, 0x6F, 0x006F, 0x70, 0x0070, 0x71, 0x0071, 0x72, 0x0072, 0x73, 0x0073, 0x74, 0x0074, 0x75, 0x0075, 0x76, 0x0076, 0x77, 0x0077, 0x78, 0x0078, 0x79, 0x0079, 0x7A, 0x007A, 0x7B, 0x00E4, 0x7C, 0x00F6, 0x7D, 0x00F1, 0x7E, 0x00FC, 0x7F, 0x00E0 }; #define UTF8_LENGTH(c) \ ((c) < 0x80 ? 1 : \ ((c) < 0x800 ? 2 : 3)) static void test_invalid(void) { long nwritten; long nread; short unsigned int exp_code; long exp_res_length; char *res, *exp_res = NULL; unsigned char *gsm; res = convert_gsm_to_utf8(invalid_gsm_extended, 0, &nread, &nwritten, 0); g_assert(res); g_assert(nread == 0); g_assert(nwritten == 0); g_assert(res[0] == '\0'); g_free(res); /* * In case of invalid GSM extended code, we should display * the character of the main default alphabet table. */ res = convert_gsm_to_utf8(invalid_gsm_extended, sizeof(invalid_gsm_extended), &nread, &nwritten, 0); exp_code = gsm_to_unicode_map[invalid_gsm_extended[1]*2 + 1]; exp_res_length = UTF8_LENGTH(exp_code); exp_res = g_new0(char, exp_res_length + 1); g_unichar_to_utf8(exp_code, exp_res); g_assert(g_strcmp0(res, exp_res) == 0); g_assert(nread == exp_res_length); g_free(exp_res); g_free(res); res = convert_gsm_to_utf8(invalid_gsm_extended_len, sizeof(invalid_gsm_extended_len), &nread, &nwritten, 0); g_assert(res == NULL); g_assert(nread == 3); gsm = convert_ucs2_to_gsm(invalid_ucs2, sizeof(invalid_ucs2), &nread, &nwritten, 0); g_assert(gsm == NULL); g_assert(nread == 2); nread = 0; gsm = convert_ucs2_to_gsm(invalid_ucs2, sizeof(invalid_ucs2) - 1, &nread, &nwritten, 0); g_assert(gsm == NULL); g_assert(nread == 0); } static void test_valid(void) { long nwritten; long nread; char *res; int i; long size; gunichar *verify; unsigned char *back; unsigned char buf[2]; static int map_size = sizeof(gsm_to_unicode_map) / sizeof(unsigned short) / 2; for (i = 0; i < map_size; i++) { unsigned short c = gsm_to_unicode_map[i*2]; if (c & 0x1b00) { buf[0] = 0x1b; buf[1] = c & 0x7f; size = 2; } else { size = 1; buf[0] = c & 0x7f; } res = convert_gsm_to_utf8(buf, size, &nread, &nwritten, 0); g_assert(res); if (g_test_verbose()) g_print("size: %ld, nread:%ld, nwritten:%ld, %s\n", size, nread, nwritten, res); g_assert(nread == size); verify = g_utf8_to_ucs4(res, -1, NULL, NULL, NULL); g_assert(verify[0] == gsm_to_unicode_map[i*2+1]); g_assert(verify[1] == 0); g_assert(nwritten == UTF8_LENGTH(verify[0])); back = convert_utf8_to_gsm(res, -1, &nread, &nwritten, 0); g_assert(back); g_assert(nwritten == size); if (c & 0x1b00) { g_assert(back[0] == 0x1b); g_assert(back[1] == (c & 0x7f)); } else { g_assert(back[0] == (c & 0x7f)); } g_free(back); g_free(verify); g_free(res); } } static void test_valid_turkish(void) { long nwritten; long nread; char *res; int i; long size; gunichar *verify; unsigned char *back; unsigned char buf[2]; static int map_size = sizeof(gsm_turkish_to_unicode_map) / sizeof(unsigned short) / 2; for (i = 0; i < map_size; i++) { unsigned short c = gsm_turkish_to_unicode_map[i*2]; if (c & 0x1b00) { buf[0] = 0x1b; buf[1] = c & 0x7f; size = 2; } else { size = 1; buf[0] = c & 0x7f; } res = convert_gsm_to_utf8_with_lang(buf, size, &nread, &nwritten, 0, 1, 1); g_assert(res); if (g_test_verbose()) g_print("size: %ld, nread:%ld, nwritten:%ld, %s\n", size, nread, nwritten, res); g_assert(nread == size); verify = g_utf8_to_ucs4(res, -1, NULL, NULL, NULL); g_assert(verify[0] == gsm_turkish_to_unicode_map[i*2+1]); g_assert(verify[1] == 0); g_assert(nwritten == UTF8_LENGTH(verify[0])); back = convert_utf8_to_gsm_with_lang(res, -1, &nread, &nwritten, 0, 1, 1); g_assert(back); g_assert(nwritten == size); if (c & 0x1b00) { g_assert(back[0] == 0x1b); g_assert(back[1] == (c & 0x7f)); } else { g_assert(back[0] == (c & 0x7f)); } g_free(back); g_free(verify); g_free(res); } } static const char hex_packed_sms[] = "493A283D0795C3F33C88FE06C9CB6132885EC6D34" "1EDF27C1E3E97E7207B3A0C0A5241E377BB1D" "7693E72E"; static const char expected[] = "It is easy to read text messages via AT " "commands."; static int reported_text_size = 49; static void test_decode_encode(void) { const char *sms = hex_packed_sms; unsigned char *decoded, *packed; char *utf8, *hex_packed; unsigned char *gsm, *gsm_encoded; long hex_decoded_size; long unpacked_size, packed_size; long gsm_encoded_size; long i; if (g_test_verbose()) g_print("Size of the orig string: %u\n", (unsigned int)strlen(sms)); decoded = decode_hex(sms, -1, &hex_decoded_size, 0); g_assert(decoded != NULL); if (g_test_verbose()) g_print("Decode to %ld bytes\n", hex_decoded_size); if (g_test_verbose()) { g_print("%s\n", sms); for (i = 0; i < hex_decoded_size; i++) g_print("%02X", decoded[i]); g_print("\n"); } gsm = unpack_7bit(decoded, hex_decoded_size, 0, FALSE, reported_text_size, &unpacked_size, 0xff); g_assert(gsm != NULL); if (g_test_verbose()) g_print("String unpacked to %ld bytes\n", unpacked_size); utf8 = convert_gsm_to_utf8(gsm, -1, NULL, NULL, 0xff); g_assert(utf8 != NULL); if (g_test_verbose()) g_print("String is: -->%s<--\n", utf8); g_assert(strcmp(utf8, expected) == 0); gsm_encoded = convert_utf8_to_gsm(utf8, -1, NULL, &gsm_encoded_size, 0xff); g_assert(gsm_encoded != NULL); if (g_test_verbose()) g_print("Converted back to GSM string of %ld bytes\n", gsm_encoded_size); g_assert(gsm_encoded[gsm_encoded_size] == 0xff); g_assert(gsm_encoded_size == unpacked_size); g_assert(memcmp(gsm_encoded, gsm, gsm_encoded_size) == 0); g_free(utf8); g_free(gsm); packed = pack_7bit(gsm_encoded, -1, 0, FALSE, &packed_size, 0xff); g_free(gsm_encoded); g_assert(packed != NULL); if (g_test_verbose()) g_print("Packed GSM to size of %ld bytes\n", packed_size); if (g_test_verbose()) { for (i = 0; i < packed_size; i++) g_print("%02X", packed[i]); g_print("\n"); } g_assert(packed_size == hex_decoded_size); g_assert(memcmp(packed, decoded, packed_size) == 0); g_free(decoded); hex_packed = encode_hex(packed, packed_size, 0); g_assert(hex_packed != NULL); g_free(packed); if (g_test_verbose()) g_print("Hex encoded packed to size %ld bytes\n", (long)strlen(hex_packed)); g_assert(strlen(hex_packed) == strlen(sms)); g_assert(strcmp(hex_packed, sms) == 0); g_free(hex_packed); } static void test_pack_size(void) { unsigned char c1[] = { 'a' }; unsigned char c2[] = { 'a', 'b' }; unsigned char c3[] = { 'a', 'b', 'c' }; unsigned char c4[] = { 'a', 'b', 'c', 'd' }; unsigned char c5[] = { 'a', 'b', 'c', 'd', 'e' }; unsigned char c6[] = { 'a', 'b', 'c', 'd', 'e', 'f' }; unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; unsigned char c8[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; unsigned char *packed; long size; packed = pack_7bit(c1, 1, 0, FALSE, &size, 0); g_assert(packed != NULL); g_assert(size == 1); g_free(packed); packed = pack_7bit(c2, 2, 0, FALSE, &size, 0); g_assert(packed != NULL); g_assert(size == 2); g_free(packed); packed = pack_7bit(c3, 3, 0, FALSE, &size, 0); g_assert(packed != NULL); g_assert(size == 3); g_free(packed); packed = pack_7bit(c4, 4, 0, FALSE, &size, 0); g_assert(packed != NULL); g_assert(size == 4); g_free(packed); packed = pack_7bit(c5, 5, 0, FALSE, &size, 0); g_assert(packed != NULL); g_assert(size == 5); g_free(packed); packed = pack_7bit(c6, 6, 0, FALSE, &size, 0); g_assert(packed != NULL); g_assert(size == 6); g_free(packed); packed = pack_7bit(c7, 7, 0, FALSE, &size, 0); g_assert(packed != NULL); g_assert(size == 7); g_assert((packed[6] & 0xfe) == 0); g_free(packed); packed = pack_7bit(c7, 7, 0, TRUE, &size, 0); g_assert(packed != NULL); g_assert(size == 7); g_assert(((packed[6] & 0xfe) >> 1) == '\r'); g_free(packed); packed = pack_7bit(c8, 8, 0, FALSE, &size, 0); g_assert(packed != NULL); g_assert(size == 7); g_free(packed); } static void test_cr_handling(void) { unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; unsigned char c7_expected[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', '\r' }; unsigned char c8[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', '\r' }; unsigned char c8_expected[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', '\r', '\r' }; unsigned char *packed; unsigned char *unpacked; long packed_size; long unpacked_size; packed = pack_7bit(c8, 8, 0, TRUE, &packed_size, 0); g_assert(packed != NULL); g_assert(packed_size == 8); g_assert(((packed[6] & 0xfe) >> 1) == '\r'); g_assert((packed[7] & 0x7f) == '\r'); unpacked = unpack_7bit(packed, 8, 0, TRUE, -1, &unpacked_size, 0); if (g_test_verbose()) g_print("Unpacked to size: %ld\n", unpacked_size); g_assert(unpacked != NULL); g_assert(unpacked_size == 9); g_assert(memcmp(c8_expected, unpacked, 9) == 0); g_free(unpacked); g_free(packed); packed = pack_7bit(c7, 7, 0, TRUE, &packed_size, 0); g_assert(packed != NULL); g_assert(packed_size == 7); g_assert(((packed[6] & 0xfe) >> 1) == '\r'); unpacked = unpack_7bit(packed, 7, 0, TRUE, -1, &unpacked_size, 0); if (g_test_verbose()) g_print("Unpacked to size: %ld\n", unpacked_size); g_assert(unpacked != NULL); g_assert(unpacked_size == 7); g_assert(memcmp(c7, unpacked, 7) == 0); g_free(unpacked); g_free(packed); /* As above, but now unpack using SMS style, we should now have cr at * the end of the stream */ packed = pack_7bit(c7, 7, 0, TRUE, &packed_size, 0); unpacked = unpack_7bit(packed, 7, 0, FALSE, 8, &unpacked_size, 0); if (g_test_verbose()) g_print("Unpacked to size: %ld\n", unpacked_size); g_assert(unpacked != NULL); g_assert(unpacked_size == 8); g_assert(memcmp(c7_expected, unpacked, 8) == 0); g_free(unpacked); g_free(packed); } static void test_sms_handling(void) { unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; unsigned char *packed; unsigned char *unpacked; long packed_size; long unpacked_size; packed = pack_7bit(c7, 7, 0, FALSE, &packed_size, 0); g_assert(packed != NULL); g_assert(packed_size == 7); unpacked = unpack_7bit(packed, 7, 0, FALSE, 8, &unpacked_size, 0xff); if (g_test_verbose()) g_print("Unpacked to size: %ld\n", unpacked_size); g_assert(unpacked != NULL); g_assert(unpacked_size == 8); g_assert(unpacked[7] == 0); g_assert(unpacked[8] == 0xff); g_free(unpacked); g_free(packed); packed = pack_7bit(c7, 7, 0, FALSE, &packed_size, 0); g_assert(packed != NULL); g_assert(packed_size == 7); unpacked = unpack_7bit(packed, 7, 0, FALSE, 7, &unpacked_size, 0xff); if (g_test_verbose()) g_print("Unpacked to size: %ld\n", unpacked_size); g_assert(unpacked != NULL); g_assert(unpacked_size == 7); g_assert(unpacked[7] == 0xff); g_free(unpacked); g_free(packed); } static void test_offset_handling(void) { unsigned char c7[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; unsigned char c8[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; unsigned char c9[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' }; unsigned char *packed; unsigned char *unpacked; long packed_size; long unpacked_size; /* Pack at offset = 2 bytes, e.g. starting with 21st bit */ packed = pack_7bit(c7, 6, 2, FALSE, &packed_size, 0); if (g_test_verbose()) g_print("Packed to size: %ld\n", packed_size); g_assert(packed != NULL); g_assert(packed_size == 6); unpacked = unpack_7bit(packed, 6, 2, FALSE, 6, &unpacked_size, 0xff); if (g_test_verbose()) g_print("Unpacked to size: %ld\n", unpacked_size); g_assert(unpacked != NULL); g_assert(unpacked_size == 6); g_assert(unpacked[6] == 0xff); g_assert(unpacked[0] == 'a'); g_assert(unpacked[5] == 'f'); g_free(unpacked); g_free(packed); /* Pack at offset = 6 bytes, we should be able to fit one character * into the first byte, and the other 7 characters into the following * 7 bytes. The 7 MSB bits of the last byte should be 0 since * we're not using CBS packing */ packed = pack_7bit(c8, 8, 6, FALSE, &packed_size, 0); if (g_test_verbose()) g_print("Packed to size: %ld\n", packed_size); g_assert(packed != NULL); g_assert(packed_size == 8); unpacked = unpack_7bit(packed, 8, 6, FALSE, 8, &unpacked_size, 0xff); if (g_test_verbose()) g_print("Unpacked to size: %ld\n", unpacked_size); g_assert(unpacked != NULL); g_assert(unpacked_size == 8); g_assert(unpacked[8] == 0xff); g_assert(unpacked[0] == 'a'); g_assert(unpacked[7] == 'h'); g_free(unpacked); g_free(packed); /* Same as above, but instead pack in 9 characters */ packed = pack_7bit(c9, 9, 6, FALSE, &packed_size, 0); if (g_test_verbose()) g_print("Packed to size: %ld\n", packed_size); g_assert(packed != NULL); g_assert(packed_size == 8); unpacked = unpack_7bit(packed, 8, 6, FALSE, 9, &unpacked_size, 0xff); if (g_test_verbose()) g_print("Unpacked to size: %ld\n", unpacked_size); g_assert(unpacked != NULL); g_assert(unpacked_size == 9); g_assert(unpacked[9] == 0xff); g_assert(unpacked[0] == 'a'); g_assert(unpacked[8] == 'i'); g_free(unpacked); g_free(packed); } static unsigned char sim_7bit[] = { 0x6F, 0x46, 0x6F, 0x6E, 0x6F, 0xFF, 0xFF }; static unsigned char sim_80_1[] = { 0x80, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x6F }; static unsigned char sim_80_2[] = { 0x80, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x6F, 0xFF, 0xFF, 0xFF}; static unsigned char sim_80_3[] = { 0x80, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x6F, 0xFF, 0xFF}; static unsigned char sim_81_0[] = { 0x81, 0x05, 0x13, 0x53, 0x95, 0xA6, 0xA6, 0xFF, 0xFF }; static unsigned char sim_81_1[] = { 0x81, 0x03, 0x00, 0x6F, 0x6E, 0x6F, 0xFF }; static unsigned char sim_81_2[] = { 0x81, 0x05, 0x08, 0xB3, 0xB4, 0xB5, 0x53, 0x54, 0xFF, 0xFF, 0xFF }; static unsigned char sim_82_0[] = { 0x82, 0x05, 0x05, 0x30, 0x2D, 0x82, 0xD3, 0x2D, 0x31 }; static unsigned char sim_82_1[] = { 0x82, 0x05, 0x04, 0x00, 0x2D, 0xB3, 0xB4, 0x2D, 0x31 }; static unsigned char sim_82_2[] = { 0x82, 0x05, 0xD8, 0x00, 0x2D, 0xB3, 0xB4, 0x2D, 0x31 }; static unsigned char sim_7bit_empty[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static void test_sim(void) { char *utf8; utf8 = sim_string_to_utf8(sim_7bit, sizeof(sim_7bit)); g_assert(utf8); g_assert(strcmp(utf8, "oFono") == 0); g_free(utf8); utf8 = sim_string_to_utf8(sim_80_1, sizeof(sim_80_1)); g_assert(utf8); g_assert(strcmp(utf8, "ono") == 0); g_free(utf8); utf8 = sim_string_to_utf8(sim_80_2, sizeof(sim_80_2)); g_assert(utf8); g_assert(strcmp(utf8, "ono") == 0); g_free(utf8); utf8 = sim_string_to_utf8(sim_80_3, sizeof(sim_80_3)); g_assert(utf8); g_assert(strcmp(utf8, "ono") == 0); g_free(utf8); utf8 = sim_string_to_utf8(sim_81_0, sizeof(sim_81_0)); g_assert(utf8); g_free(utf8); utf8 = sim_string_to_utf8(sim_81_2, sizeof(sim_81_2)); g_assert(utf8); g_free(utf8); utf8 = sim_string_to_utf8(sim_81_1, sizeof(sim_81_1)); g_assert(utf8); g_assert(strcmp(utf8, "ono") == 0); g_free(utf8); utf8 = sim_string_to_utf8(sim_82_0, sizeof(sim_82_0)); g_assert(utf8); g_free(utf8); utf8 = sim_string_to_utf8(sim_82_1, sizeof(sim_82_1)); g_assert(utf8); g_free(utf8); utf8 = sim_string_to_utf8(sim_82_2, sizeof(sim_82_2)); g_assert(utf8 == NULL); utf8 = sim_string_to_utf8(sim_7bit_empty, sizeof(sim_7bit_empty)); g_assert(utf8); g_assert(strcmp(utf8, "") == 0); g_free(utf8); } static void test_unicode_to_gsm(void) { long nwritten; long nread; int i; unsigned char *res; char *utf8; unsigned char buf[2]; unsigned char *back; gunichar2 verify; static int map_size = sizeof(gsm_to_unicode_map) / sizeof(unsigned short) / 2; for (i = 0; i < map_size; i++) { unsigned short c = gsm_to_unicode_map[i*2+1]; buf[0] = c >> 8; buf[1] = c & 0xff; res = convert_ucs2_to_gsm(buf, 2, &nread, &nwritten, 0); g_assert(res); if (g_test_verbose()) g_print("nread:%ld, nwritten:%ld, %s\n", nread, nwritten, res); if (res[0] == 0x1B) g_assert(nwritten == 2); else g_assert(nwritten == 1); utf8 = g_convert((const gchar *) buf, 2, "UTF-8", "UCS-2BE", NULL, NULL, NULL); g_assert(utf8); back = convert_utf8_to_gsm(utf8, strlen(utf8), &nread, &nwritten, 0); g_assert(back); if (back[0] == 0x1B) { g_assert(nwritten == 2); verify = back[0] << 8 | back[1]; } else { g_assert(nwritten == 1); verify = back[0]; } if (g_test_verbose()) g_print("nwritten:%ld, verify: 0x%x\n", nwritten, verify); g_assert(verify == gsm_to_unicode_map[i*2]); g_free(res); g_free(back); g_free(utf8); } } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/testutil/Invalid Conversions", test_invalid); g_test_add_func("/testutil/Valid Conversions", test_valid); g_test_add_func("/testutil/Valid Turkish National Variant Conversions", test_valid_turkish); g_test_add_func("/testutil/Decode Encode", test_decode_encode); g_test_add_func("/testutil/Pack Size", test_pack_size); g_test_add_func("/testutil/CBS CR Handling", test_cr_handling); g_test_add_func("/testutil/SMS Handling", test_sms_handling); g_test_add_func("/testutil/Offset Handling", test_offset_handling); g_test_add_func("/testutil/SIM conversions", test_sim); g_test_add_func("/testutil/Valid Unicode to GSM Conversion", test_unicode_to_gsm); return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/unit/test-sms-root.c0000644000015600001650000000732612671500024022272 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include "util.h" #include "smsutil.h" static const char *assembly_pdu1 = "038121F340048155550119906041001222048C0500" "031E0301041804420430043A002C002004100" "43B0435043A04410430043D04340440002000" "200441043B044304480430043B00200437043" "000200434043204350440044C044E00200020" "04380020002004320441043500200431043E0" "43B044C044804350020043F04400435043804" "41043F043E043B043D044F043B0441044F002" "000200433043D0435"; static int assembly_pdu_len1 = 155; static const char *assembly_pdu2 = "038121F340048155550119906041001222048C0500" "031E03020432043E043C002E000A041D04300" "43A043E043D04350446002C0020043D043500" "200432002004410438043B043004450020043" "40430043B043504350020044204350440043F" "04350442044C002C0020043E043D002004410" "44204400435043C043804420435043B044C04" "3D043E002004320431043504360430043B002" "004320020043A043E"; static int assembly_pdu_len2 = 155; static const char *assembly_pdu3 = "038121F340048155550119906041001222044A0500" "031E0303043C043D043004420443002C00200" "43F043E043704300431044B0432000A043404" "3004360435002C002004470442043E0020002" "00431044B043B0020043D04300433002E"; static int assembly_pdu_len3 = 89; static void test_serialize_assembly(void) { unsigned char pdu[176]; long pdu_len; struct sms sms; struct sms_assembly *assembly = sms_assembly_new("1234"); guint16 ref; guint8 max; guint8 seq; GSList *l; decode_hex_own_buf(assembly_pdu1, -1, &pdu_len, 0, pdu); sms_decode(pdu, pdu_len, FALSE, assembly_pdu_len1, &sms); sms_extract_concatenation(&sms, &ref, &max, &seq); l = sms_assembly_add_fragment(assembly, &sms, time(NULL), &sms.deliver.oaddr, ref, max, seq); if (g_test_verbose()) { g_print("Ref: %u\n", ref); g_print("Max: %u\n", max); g_print("From: %s\n", sms_address_to_string(&sms.deliver.oaddr)); } g_assert(g_slist_length(assembly->assembly_list) == 1); g_assert(l == NULL); decode_hex_own_buf(assembly_pdu2, -1, &pdu_len, 0, pdu); sms_decode(pdu, pdu_len, FALSE, assembly_pdu_len2, &sms); sms_extract_concatenation(&sms, &ref, &max, &seq); l = sms_assembly_add_fragment(assembly, &sms, time(NULL), &sms.deliver.oaddr, ref, max, seq); g_assert(l == NULL); sms_assembly_free(assembly); assembly = sms_assembly_new("1234"); decode_hex_own_buf(assembly_pdu3, -1, &pdu_len, 0, pdu); sms_decode(pdu, pdu_len, FALSE, assembly_pdu_len3, &sms); sms_extract_concatenation(&sms, &ref, &max, &seq); l = sms_assembly_add_fragment(assembly, &sms, time(NULL), &sms.deliver.oaddr, ref, max, seq); g_assert(l != NULL); sms_assembly_free(assembly); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/testsms/Test SMS Assembly Serialize", test_serialize_assembly); return g_test_run(); } ofono-1.17.bzr6912+16.04.20160314.3/configure.ac0000644000015600001650000002262212671500073020675 0ustar pbuserpbgroup00000000000000AC_PREREQ(2.60) AC_INIT(ofono, 1.17) AM_INIT_AUTOMAKE([foreign subdir-objects color-tests]) AC_CONFIG_HEADERS(config.h) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_MAINTAINER_MODE AC_PREFIX_DEFAULT(/usr/local) PKG_PROG_PKG_CONFIG COMPILER_FLAGS AC_LANG_C AC_C_RESTRICT AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CC_PIE AC_PROG_INSTALL AC_PROG_MKDIR_P m4_define([_LT_AC_TAGCONFIG], []) m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])]) AC_DISABLE_STATIC 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" fi ]) AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable compiling with debugging information]), [ if (test "${enableval}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then CFLAGS="$CFLAGS -g" fi ]) AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie], [enable position independent executables flag]), [ if (test "${enableval}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then CFLAGS="$CFLAGS -fPIE" LDFLAGS="$LDFLAGS -pie" fi ]) AC_ARG_ENABLE(threads, AC_HELP_STRING([--enable-threads], [enable threading support]), [enable_threads=${enableval}]) AC_CHECK_FUNC(signalfd, dummy=yes, AC_MSG_ERROR(signalfd support is required)) AC_CHECK_LIB(dl, dlopen, dummy=yes, AC_MSG_ERROR(dynamic linking loader is required)) PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes, AC_MSG_ERROR(GLib >= 2.28 is required)) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_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, AC_MSG_ERROR(GThread >= 2.16 is required)) GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS" GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS" fi PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes, AC_MSG_ERROR(D-Bus >= 1.4 is required)) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) AC_ARG_WITH(dbusconfdir, AC_HELP_STRING([--with-dbusconfdir=PATH], [path to D-Bus config directory]), [path_dbusconf=${withval}], [path_dbusconf="`$PKG_CONFIG --variable=sysconfdir dbus-1`"]) if (test -z "${path_dbusconf}"); then DBUS_CONFDIR="${sysconfdir}/dbus-1/system.d" else DBUS_CONFDIR="${path_dbusconf}/dbus-1/system.d" fi AC_SUBST(DBUS_CONFDIR) AC_ARG_WITH(dbusdatadir, AC_HELP_STRING([--with-dbusdatadir=PATH], [path to D-Bus data directory]), [path_dbusdata=${withval}], [path_dbusdata="`$PKG_CONFIG --variable=datadir dbus-1`"]) if (test -z "${path_dbusdata}"); then DBUS_DATADIR="${datadir}/dbus-1/system-services" else DBUS_DATADIR="${path_dbusdata}/dbus-1/system-services" fi AC_SUBST(DBUS_DATADIR) AC_ARG_WITH([systemdunitdir], AC_HELP_STRING([--with-systemdunitdir=DIR], [path to systemd service directory]), [path_systemdunit=${withval}], [path_systemdunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"]) if (test -n "${path_systemdunit}"); then SYSTEMD_UNITDIR="${path_systemdunit}" AC_SUBST(SYSTEMD_UNITDIR) fi AM_CONDITIONAL(SYSTEMD, test -n "${path_systemdunit}") AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test], [enable test/example scripts]), [enable_test=${enableval}]) AM_CONDITIONAL(TEST, test "${enable_test}" = "yes") AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools], [enable testing tools]), [enable_tools=${enableval}]) AM_CONDITIONAL(TOOLS, test "${enable_tools}" = "yes") AC_ARG_ENABLE(dundee, AC_HELP_STRING([--enable-dundee], [enable dialup deamon support]), [enable_dundee=${enableval}]) AM_CONDITIONAL(DUNDEE, test "${enable_dundee}" = "yes") AC_ARG_ENABLE(udev, AC_HELP_STRING([--disable-udev], [disable udev modem detection support]), [enable_udev=${enableval}]) if (test "${enable_udev}" != "no"); then PKG_CHECK_MODULES(UDEV, libudev >= 143, [enable_udev="yes"], AC_MSG_ERROR(libudev >= 143 is required)) UDEV_DATADIR="`$PKG_CONFIG --variable=udevdir udev`" if (test -z "${UDEV_DATADIR}"); then UDEV_DATADIR="${sysconfdir}/udev/rules.d" else UDEV_DATADIR="${UDEV_DATADIR}/rules.d" fi AC_SUBST(UDEV_DATADIR) fi AC_SUBST(UDEV_CFLAGS) AC_SUBST(UDEV_LIBS) AM_CONDITIONAL(UDEV, test "${enable_udev}" = "yes") AC_ARG_ENABLE(atmodem, AC_HELP_STRING([--disable-atmodem], [disable ETSI AT modem support]), [enable_atmodem=${enableval}]) AM_CONDITIONAL(ATMODEM, test "${enable_atmodem}" != "no") AC_ARG_ENABLE(cdmamodem, AC_HELP_STRING([--disable-cdmamodem], [disable CDMA AT modem support]), [enable_cdmamodem=${enableval}]) AM_CONDITIONAL(CDMAMODEM, test "${enable_cdmamodem}" != "no") AC_ARG_ENABLE(phonesim, AC_HELP_STRING([--disable-phonesim], [disable Phone simulator support]), [enable_phonesim=${enableval}]) AM_CONDITIONAL(PHONESIM, test "${enable_phonesim}" != "no" && test "${enable_atmodem}" != "no") AC_ARG_ENABLE(isimodem, AC_HELP_STRING([--disable-isimodem], [disable PhoNet/ISI modem support]), [enable_isimodem=${enableval}]) AM_CONDITIONAL(ISIMODEM, test "${enable_isimodem}" != "no") AC_ARG_ENABLE(rilmodem, AC_HELP_STRING([--disable-rilmodem], [disable RIL modem support]), [enable_rilmodem=${enableval}]) AM_CONDITIONAL(RILMODEM, test "${enable_rilmodem}" != "no") AC_ARG_ENABLE(qmimodem, AC_HELP_STRING([--disable-qmimodem], [disable Qualcomm QMI modem support]), [enable_qmimodem=${enableval}]) AM_CONDITIONAL(QMIMODEM, test "${enable_qmimodem}" != "no") AC_ARG_ENABLE(bluetooth, AC_HELP_STRING([--disable-bluetooth], [disable BlueZ 4 and BlueZ 5 plugins support]), [enable_bluetooth=${enableval}]) AC_ARG_ENABLE(bluez4, AC_HELP_STRING([--enable-bluez4], [enable BlueZ 4 plugins support prior to BlueZ 5]), [enable_bluez4=${enableval}]) if (test "${enable_bluez4}" = "yes"); then PKG_CHECK_MODULES(BLUEZ, bluez >= 4.99 bluez < 5, dummy=yes, AC_MSG_ERROR(Bluetooth library >= 4.99 and < 5 is required)) AC_SUBST(BLUEZ_CFLAGS) AC_SUBST(BLUEZ_LIBS) fi AM_CONDITIONAL(BLUEZ4, test "${enable_bluetooth}" != "no" && test "${enable_bluez4}" = "yes") AM_CONDITIONAL(BLUETOOTH, test "${enable_bluetooth}" != "no") AC_ARG_ENABLE(cares, AC_HELP_STRING([--disable-cares], [disable DNS client using c-ares library]), [enable_cares=${enableval}]) if (test "${enable_cares}" != "no"); then PKG_CHECK_MODULES(CARES, libcares, dummy=yes, AC_MSG_ERROR(libcares is required)) fi AC_SUBST(CARES_CFLAGS) AC_SUBST(CARES_LIBS) AM_CONDITIONAL(CARES, test "${enable_cares}" != "no") AC_ARG_ENABLE(accounts-settings, AC_HELP_STRING([--disable-accounts-settings], [disable using accounts service for system settings]), [enable_accounts_settings=${enableval}]) if (test "${enable_accounts_settings=$}" != "no"); then PKG_CHECK_MODULES(SYSTEMD, libsystemd, dummy=yes, AC_MSG_ERROR(libsystemd is required)) fi AC_SUBST(SYSTEMD_CFLAGS) AC_SUBST(SYSTEMD_LIBS) AM_CONDITIONAL(ACCOUNTSSETTINGS, test "${enable_accounts_settings=$}" != "no") AC_ARG_ENABLE(nettime, AC_HELP_STRING([--disable-nettime], [disable Nettime plugin]), [enable_nettime=${enableval}]) AM_CONDITIONAL(NETTIME, test "${enable_nettime}" != "no") AC_ARG_WITH([provisiondb], AC_HELP_STRING([--with-provisiondb=FILE], [location of provision database]), [path_provisiondb=${withval}]) AC_ARG_ENABLE(provision, AC_HELP_STRING([--disable-provision], [disable provisioning support]), [enable_provision=${enableval}]) if (test "${enable_provision}" != "no"); then if (test -n "${path_provisiondb}"); then AC_DEFINE_UNQUOTED(PROVIDER_DATABASE, "${path_provisiondb}", [Mobile provider database]) else AC_MSG_CHECKING([for mobile-broadband-provider-info]) PKG_CHECK_EXISTS(mobile-broadband-provider-info, _PKG_CONFIG(PROVIDER_DATABASE, [variable=database], [mobile-broadband-provider-info]) AC_DEFINE_UNQUOTED(PROVIDER_DATABASE, "$pkg_cv_PROVIDER_DATABASE", [Mobile provider database]) AC_MSG_RESULT([yes]), AC_MSG_ERROR(Mobile broadband provider database is required)) fi fi AM_CONDITIONAL(PROVISION, test "${enable_provision}" != "no") AC_ARG_ENABLE(upower, AC_HELP_STRING([--disable-upower], [disable UPower plugin]), [enable_upower=${enableval}]) AM_CONDITIONAL(UPOWER, test "${enable_power}" != "no") AC_ARG_ENABLE(android_wakelock, AC_HELP_STRING([--enable-android-wakelock], [enable Enable support for Android wakelocks]), [enable_android_wakelock=${enableval}]) AM_CONDITIONAL(ANDROID_WAKELOCK, test "${enable_android_wakelock}" = "yes") 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") if (test "${prefix}" = "NONE"); then dnl no prefix and no localstatedir, so default to /var if (test "$localstatedir" = '${prefix}/var'); then AC_SUBST([localstatedir], ['/var']) fi prefix="${ac_default_prefix}" fi if (test "$localstatedir" = '${prefix}/var'); then storagedir="${prefix}/var/lib/ofono" else storagedir="${localstatedir}/lib/ofono" fi AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}", [Directory for the storage files]) if (test "$sysconfdir" = '${prefix}/etc'); then configdir="${prefix}/etc/ofono" else configdir="${sysconfdir}/ofono" fi AC_DEFINE_UNQUOTED(CONFIGDIR, "${configdir}", [Directory for the configuration files]) AC_OUTPUT(Makefile include/version.h src/ofono.service ofono.pc \ dundee/dundee.service) ofono-1.17.bzr6912+16.04.20160314.3/NEWS0000644000015600001650000000000012671500024017064 0ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/dundee/0000755000015600001650000000000012671500304017644 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/dundee/dundee.service.in0000644000015600001650000000027412671500024023101 0ustar pbuserpbgroup00000000000000[Unit] Description=DUN service After=syslog.target [Service] Type=dbus BusName=org.ofono.dundee ExecStart=@prefix@/sbin/dundee -n StandardError=null [Install] WantedBy=multi-user.target ofono-1.17.bzr6912+16.04.20160314.3/dundee/main.c0000644000015600001650000001206312671500024020735 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 Intel Corporation. All rights reserved. * Copyright (C) 2012 BMW Car IT GmbH. 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 #endif #include #include #include #include #include #include #include #include "dundee.h" #define SHUTDOWN_GRACE_SECONDS 10 static GMainLoop *event_loop; void __dundee_exit(void) { g_main_loop_quit(event_loop); } static gboolean quit_eventloop(gpointer user_data) { __dundee_exit(); return FALSE; } static unsigned int __terminated = 0; static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct signalfd_siginfo si; ssize_t result; int fd; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; fd = g_io_channel_unix_get_fd(channel); result = read(fd, &si, sizeof(si)); if (result != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGINT: case SIGTERM: if (__terminated == 0) { ofono_info("Terminating"); g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, quit_eventloop, NULL); __dundee_device_shutdown(); } __terminated = 1; break; } return TRUE; } static guint setup_signalfd(void) { GIOChannel *channel; guint source; sigset_t mask; int fd; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { perror("Failed to set signal mask"); return 0; } fd = signalfd(-1, &mask, 0); if (fd < 0) { perror("Failed to create signal descriptor"); return 0; } channel = g_io_channel_unix_new(fd); g_io_channel_set_close_on_unref(channel, TRUE); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); source = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_handler, NULL); g_io_channel_unref(channel); return source; } static void system_bus_disconnected(DBusConnection *conn, void *user_data) { ofono_error("System bus has disconnected!"); g_main_loop_quit(event_loop); } static gchar *option_debug = NULL; static gboolean option_detach = TRUE; static gboolean option_version = FALSE; static gboolean parse_debug(const char *key, const char *value, gpointer user_data, GError **error) { if (value) option_debug = g_strdup(value); else option_debug = g_strdup("*"); return TRUE; } static GOptionEntry options[] = { { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_debug, "Specify debug options to enable", "DEBUG" }, { "nodetach", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &option_detach, "Don't run as daemon in background" }, { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit" }, { NULL }, }; int main(int argc, char **argv) { GOptionContext *context; GError *err = NULL; DBusConnection *conn; DBusError error; guint signal; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) { if (err != NULL) { g_printerr("%s\n", err->message); g_error_free(err); return 1; } g_printerr("An unknown error occurred\n"); return 1; } g_option_context_free(context); if (option_version == TRUE) { printf("%s\n", VERSION); exit(0); } if (option_detach == TRUE) { if (daemon(0, 0)) { perror("Can't start daemon"); return 1; } } event_loop = g_main_loop_new(NULL, FALSE); signal = setup_signalfd(); __ofono_log_init(argv[0], option_debug, option_detach); dbus_error_init(&error); conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, DUNDEE_SERVICE, &error); if (conn == NULL) { if (dbus_error_is_set(&error) == TRUE) { ofono_error("Unable to hop onto D-Bus: %s", error.message); dbus_error_free(&error); } else { ofono_error("Unable to hop onto D-Bus"); } goto cleanup; } g_dbus_set_disconnect_function(conn, system_bus_disconnected, NULL, NULL); __ofono_dbus_init(conn); __dundee_manager_init(); __dundee_device_init(); __dundee_bluetooth_init(); g_main_loop_run(event_loop); __dundee_bluetooth_cleanup(); __dundee_device_cleanup(); __dundee_manager_cleanup(); __ofono_dbus_cleanup(); dbus_connection_unref(conn); cleanup: g_source_remove(signal); g_main_loop_unref(event_loop); __ofono_log_cleanup(); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/dundee/bluez4.c0000644000015600001650000001354412671500024021223 0ustar pbuserpbgroup00000000000000/* * oFono - Open Source Telephony * * Copyright (C) 2008-2012 Intel Corporation. All rights reserved. * Copyright (C) 2012 BMW Car IT GmbH. 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 #endif #include #include #include #include #include #include #include #include "plugins/bluez4.h" #include "dundee.h" static GHashTable *bluetooth_hash; struct bluetooth_device { struct dundee_device *device; char *path; char *address; char *name; int fd; DBusPendingCall *call; }; static void bt_disconnect(struct dundee_device *device, dundee_device_disconnect_cb_t cb, void *data) { struct bluetooth_device *bt = dundee_device_get_data(device); DBG("%p", bt); shutdown(bt->fd, SHUT_RDWR); CALLBACK_WITH_SUCCESS(cb, data); } static void bt_connect_reply(DBusPendingCall *call, gpointer user_data) { struct cb_data *cbd = user_data; dundee_device_connect_cb_t cb = cbd->cb; struct bluetooth_device *bt = cbd->user; DBusMessage *reply; DBusError derr; int fd; DBG("%p", bt); reply = dbus_pending_call_steal_reply(call); bt->call = NULL; dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { DBG("Connection to bt serial returned with error: %s, %s", derr.name, derr.message); dbus_error_free(&derr); CALLBACK_WITH_FAILURE(cb, -1, cbd->data); goto done; } dbus_message_get_args(reply, NULL, DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID); DBG("%p fd %d", bt, fd); if (fd < 0) { CALLBACK_WITH_FAILURE(cb, -1, cbd->data); goto done; } bt->fd = fd; CALLBACK_WITH_SUCCESS(cb, fd, cbd->data); done: dbus_message_unref(reply); g_free(cbd); } static void bt_connect(struct dundee_device *device, dundee_device_connect_cb_t cb, void *data) { struct bluetooth_device *bt = dundee_device_get_data(device); struct cb_data *cbd = cb_data_new(cb, data); char *profile = "dun"; int status; DBG("%p", bt); cbd->user = bt; status = bluetooth_send_with_reply(bt->path, BLUEZ_SERIAL_INTERFACE, "ConnectFD", &bt->call, bt_connect_reply, cbd, NULL, DBUS_TIMEOUT, DBUS_TYPE_STRING, &profile, DBUS_TYPE_INVALID); if (status == 0) return; CALLBACK_WITH_FAILURE(cb, -1, cbd->data); g_free(cbd); } struct dundee_device_driver bluetooth_driver = { .name = "bluetooth", .connect = bt_connect, .disconnect = bt_disconnect, }; static int bt_probe(const char *path, const char *dev_addr, const char *adapter_addr, const char *alias) { struct bluetooth_device *bt; struct dundee_device *device; char buf[256]; DBG(""); /* We already have this device in our hash, ignore */ if (g_hash_table_lookup(bluetooth_hash, path) != NULL) return -EALREADY; ofono_info("Using device: %s, devaddr: %s, adapter: %s", path, dev_addr, adapter_addr); strcpy(buf, "dun/"); bluetooth_create_path(dev_addr, adapter_addr, buf + 4, sizeof(buf) - 4); bt = g_try_new0(struct bluetooth_device, 1); if (bt == NULL) return -ENOMEM; DBG("%p", bt); device = dundee_device_create(&bluetooth_driver); if (device == NULL) goto free; dundee_device_set_data(device, bt); bt->path = g_strdup(path); if (bt->path == NULL) goto free; bt->address = g_strdup(dev_addr); if (bt->address == NULL) goto free; bt->name = g_strdup(alias); if (bt->name == NULL) goto free; dundee_device_set_name(device, bt->name); if (dundee_device_register(device) < 0) { g_free(device); goto free; } bt->device = device; g_hash_table_insert(bluetooth_hash, g_strdup(path), bt); return 0; free: g_free(bt->path); g_free(bt->address); g_free(bt->name); g_free(bt); return -ENOMEM; } static void destroy_device(gpointer user) { struct bluetooth_device *bt = user; DBG("%p", bt); if (bt->call != NULL) dbus_pending_call_cancel(bt->call); g_free(bt->path); g_free(bt->address); g_free(bt); } static gboolean bt_remove_device(gpointer key, gpointer value, gpointer user_data) { struct bluetooth_device *bt = value; const char *path = key; const char *prefix = user_data; DBG("%p", bt); if (prefix && g_str_has_prefix(path, prefix) == FALSE) return FALSE; dundee_device_unregister(bt->device); return TRUE; } static void bt_remove(const char *prefix) { DBG("%s", prefix); if (bluetooth_hash == NULL) return; g_hash_table_foreach_remove(bluetooth_hash, bt_remove_device, (gpointer) prefix); } static void bt_set_alias(const char *path, const char *alias) { struct bluetooth_device *bt; DBG(""); if (path == NULL || alias == NULL) return; bt = g_hash_table_lookup(bluetooth_hash, path); if (bt == NULL) return; g_free(bt->name); bt->name = g_strdup(alias); dundee_device_set_name(bt->device, bt->name); } static struct bluetooth_profile dun_profile = { .name = "dun_dt", .probe = bt_probe, .remove = bt_remove, .set_alias = bt_set_alias, }; int __dundee_bluetooth_init(void) { int err; DBG(""); err = bluetooth_register_uuid(DUN_GW_UUID, &dun_profile); if (err < 0) return err; bluetooth_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_device); return 0; } void __dundee_bluetooth_cleanup(void) { DBG(""); bluetooth_unregister_uuid(DUN_GW_UUID); g_hash_table_destroy(bluetooth_hash); } ofono-1.17.bzr6912+16.04.20160314.3/dundee/dundee.h0000644000015600001650000001022412671500024021257 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 Intel Corporation. All rights reserved. * Copyright (C) 2012 BMW Car IT GmbH. 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 #define OFONO_API_SUBJECT_TO_CHANGE #include #define DUN_VERSION_1_2 0x0102 void __dundee_exit(void); enum dundee_error_type { DUNDEE_ERROR_TYPE_NO_ERROR = 0, DUNDEE_ERROR_TYPE_FAILURE, }; struct dundee_error { enum dundee_error_type type; int error; }; 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 = g_new0(struct cb_data, 1); ret->cb = cb; ret->data = data; return ret; } #define CALLBACK_WITH_FAILURE(cb, args...) \ do { \ struct dundee_error cb_e; \ cb_e.type = DUNDEE_ERROR_TYPE_FAILURE; \ cb_e.error = 0; \ \ cb(&cb_e, ##args); \ } while (0) \ #define CALLBACK_WITH_SUCCESS(f, args...) \ do { \ struct dundee_error e; \ e.type = DUNDEE_ERROR_TYPE_NO_ERROR; \ e.error = 0; \ f(&e, ##args); \ } while(0) \ #include int __ofono_log_init(const char *program, const char *debug, ofono_bool_t detach); void __ofono_log_cleanup(void); void __ofono_log_enable(struct ofono_debug_desc *start, struct ofono_debug_desc *stop); #include #define DUNDEE_SERVICE "org.ofono.dundee" #define DUNDEE_MANAGER_INTERFACE "org.ofono.dundee.Manager" #define DUNDEE_DEVICE_INTERFACE "org.ofono.dundee.Device" #define DUNDEE_MANAGER_PATH "/" int __ofono_dbus_init(DBusConnection *conn); void __ofono_dbus_cleanup(void); void __ofono_dbus_pending_reply(DBusMessage **msg, DBusMessage *reply); DBusMessage *__dundee_error_invalid_args(DBusMessage *msg); DBusMessage *__dundee_error_failed(DBusMessage *msg); DBusMessage *__dundee_error_in_progress(DBusMessage *msg); DBusMessage *__dundee_error_timed_out(DBusMessage *msg); int __dundee_manager_init(void); void __dundee_manager_cleanup(void); struct dundee_device; int __dundee_device_init(void); void __dundee_device_cleanup(void); void __dundee_device_shutdown(void); typedef void (*dundee_device_connect_cb_t)(const struct dundee_error *error, int fd, void *data); typedef void (*dundee_device_disconnect_cb_t)(const struct dundee_error *error, void *data); struct dundee_device_driver { const char *name; /* Connect and dial */ void (*connect)(struct dundee_device *device, dundee_device_connect_cb_t cb, void *data); /* Hangup and disconnect */ void (*disconnect)(struct dundee_device *device, dundee_device_disconnect_cb_t cb, void *data); }; struct dundee_device *dundee_device_create(struct dundee_device_driver *d); int dundee_device_register(struct dundee_device *device); void dundee_device_unregister(struct dundee_device *device); void dundee_device_disconnect(const struct dundee_error *error, struct dundee_device *device); void dundee_device_set_data(struct dundee_device *device, void *data); void *dundee_device_get_data(struct dundee_device *device); int dundee_device_set_name(struct dundee_device *device, const char *name); typedef void (*dundee_device_foreach_func)(struct dundee_device *device, void *data); void __dundee_device_foreach(dundee_device_foreach_func cb, void *userdata); const char *__dundee_device_get_path(struct dundee_device *device); void __dundee_device_append_properties(struct dundee_device *device, DBusMessageIter *dict); int __dundee_bluetooth_init(void); void __dundee_bluetooth_cleanup(void); ofono-1.17.bzr6912+16.04.20160314.3/dundee/dbus.c0000644000015600001650000000313712671500024020750 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2012 BMW Car IT GmbH. 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 #endif #include #include #include "dundee.h" #define DUNDEE_ERROR_INTERFACE "org.ofono.dundee.Error" DBusMessage *__dundee_error_invalid_args(DBusMessage *msg) { return g_dbus_create_error(msg, DUNDEE_ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); } DBusMessage *__dundee_error_failed(DBusMessage *msg) { return g_dbus_create_error(msg, DUNDEE_ERROR_INTERFACE ".Failed", "Operation failed"); } DBusMessage *__dundee_error_in_progress(DBusMessage *msg) { return g_dbus_create_error(msg, DUNDEE_ERROR_INTERFACE ".InProgress", "Operation already in progress"); } DBusMessage *__dundee_error_timed_out(DBusMessage *msg) { return g_dbus_create_error(msg, DUNDEE_ERROR_INTERFACE ".Timedout", "Operation failure due to timeout"); } ofono-1.17.bzr6912+16.04.20160314.3/dundee/bluez5.c0000644000015600001650000002370712671500024021226 0ustar pbuserpbgroup00000000000000/* * oFono - Open Source Telephony * * Copyright (C) 2013 Instituto Nokia de Tecnologia - INdT * * 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 #endif #include #include #include #include #include "dundee.h" #include "plugins/bluez5.h" #define DUN_DT_PROFILE_PATH "/bluetooth/profile/dun_dt" static GDBusClient *bluez; static GHashTable *registered_devices; struct bluetooth_device { struct dundee_device *device; char *path; char *address; char *name; struct cb_data *connect_cbd; int fd; }; static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct bluetooth_device *bt_device; dundee_device_connect_cb_t cb; DBusMessageIter iter; const char *path; int fd; if (!dbus_message_iter_init(msg, &iter)) goto error; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) goto error; dbus_message_iter_get_basic(&iter, &path); bt_device = g_hash_table_lookup(registered_devices, path); if (bt_device == NULL) goto error; cb = bt_device->connect_cbd->cb; dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UNIX_FD) goto call_failure; dbus_message_iter_get_basic(&iter, &fd); if (fd < 0) goto call_failure; DBG("%s %d", bt_device->path, fd); bt_device->fd = fd; CALLBACK_WITH_SUCCESS(cb, fd, bt_device->connect_cbd->data); g_free(bt_device->connect_cbd); bt_device->connect_cbd = NULL; return dbus_message_new_method_return(msg); call_failure: CALLBACK_WITH_FAILURE(cb, -1, bt_device->connect_cbd->data); g_free(bt_device->connect_cbd); bt_device->connect_cbd = NULL; error: return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Invalid arguments in method call"); } static DBusMessage *profile_release(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBG(""); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".NotImplemented", "Implementation not provided"); } static DBusMessage *profile_cancel(DBusConnection *conn, DBusMessage *msg, void *user_data) { DBG(""); return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".NotImplemented", "Implementation not provided"); } static DBusMessage *profile_disconnection(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct bluetooth_device *bt_device; DBusMessageIter iter; const char *path; if (!dbus_message_iter_init(msg, &iter)) goto error; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) goto error; dbus_message_iter_get_basic(&iter, &path); bt_device = g_hash_table_lookup(registered_devices, path); if (bt_device == NULL) goto error; DBG("%s", bt_device->path); CALLBACK_WITH_SUCCESS(dundee_device_disconnect, bt_device->device); return dbus_message_new_method_return(msg); error: return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected", "Invalid arguments in method call"); } static const GDBusMethodTable profile_methods[] = { { GDBUS_ASYNC_METHOD("NewConnection", GDBUS_ARGS({ "device", "o"}, { "fd", "h"}, { "fd_properties", "a{sv}" }), NULL, profile_new_connection) }, { GDBUS_METHOD("Release", NULL, NULL, profile_release) }, { GDBUS_METHOD("Cancel", NULL, NULL, profile_cancel) }, { GDBUS_METHOD("RequestDisconnection", GDBUS_ARGS({"device", "o"}), NULL, profile_disconnection) }, { } }; static void bluetooth_device_destroy(gpointer user_data) { struct bluetooth_device *bt_device = user_data; DBG("%s", bt_device->path); if (bt_device->device != NULL) dundee_device_unregister(bt_device->device); if (bt_device->connect_cbd != NULL) g_free(bt_device->connect_cbd); g_free(bt_device->path); g_free(bt_device->address); g_free(bt_device->name); g_free(bt_device); } static void bluetooth_device_connect_callback(gboolean success, gpointer user_data) { struct bluetooth_device *bt_device = user_data; if (success) { DBG("Success"); return; } DBG("ConnectProfile() returned an error"); g_free(bt_device->connect_cbd); bt_device->connect_cbd = NULL; } static void bluetooth_device_connect(struct dundee_device *device, dundee_device_connect_cb_t cb, void *data) { struct bluetooth_device *bt_device = dundee_device_get_data(device); struct cb_data *cbd = cb_data_new(cb, data); DBG("%s", bt_device->path); cbd->user = bt_device; bt_device->connect_cbd = cbd; bt_connect_profile(ofono_dbus_get_connection(), bt_device->path, DUN_GW_UUID, bluetooth_device_connect_callback, bt_device); } static void bluetooth_device_disconnect(struct dundee_device *device, dundee_device_disconnect_cb_t cb, void *data) { struct bluetooth_device *bt_device = dundee_device_get_data(device); DBG("%s", bt_device->path); shutdown(bt_device->fd, SHUT_RDWR); CALLBACK_WITH_SUCCESS(cb, data); } struct dundee_device_driver bluetooth_driver = { .name = "bluetooth", .connect = bluetooth_device_connect, .disconnect = bluetooth_device_disconnect, }; static struct bluetooth_device *bluetooth_device_create(const char *path, const char *address, const char *alias) { struct bluetooth_device *bt_device; DBG("%s %s %s", path, address, alias); bt_device = g_try_new0(struct bluetooth_device, 1); if (bt_device == NULL) return NULL; bt_device->path = g_strdup(path); bt_device->address = g_strdup(address); bt_device->name = g_strdup(alias); return bt_device; } static struct bluetooth_device *bluetooth_device_register(GDBusProxy *proxy) { const char *path = g_dbus_proxy_get_path(proxy); const char *alias, *address; struct bluetooth_device *bt_device; struct dundee_device *device; DBusMessageIter iter; DBG("%s", path); if (g_hash_table_lookup(registered_devices, path) != NULL) return NULL; if (!g_dbus_proxy_get_property(proxy, "Address", &iter)) return NULL; dbus_message_iter_get_basic(&iter, &address); if (!g_dbus_proxy_get_property(proxy, "Alias", &iter)) return NULL; dbus_message_iter_get_basic(&iter, &alias); bt_device = bluetooth_device_create(path, address, alias); if (bt_device == NULL) { ofono_error("Register bluetooth device failed"); return NULL; } device = dundee_device_create(&bluetooth_driver); if (device == NULL) goto free; dundee_device_set_data(device, bt_device); dundee_device_set_name(device, bt_device->name); if (dundee_device_register(device) < 0) { g_free(device); goto free; } bt_device->device = device; g_hash_table_insert(registered_devices, g_strdup(path), bt_device); return bt_device; free: bluetooth_device_destroy(bt_device); return NULL; } static void bluetooth_device_unregister(const char *path) { DBG(""); g_hash_table_remove(registered_devices, path); } static gboolean has_dun_uuid(DBusMessageIter *array) { DBusMessageIter value; if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) return FALSE; dbus_message_iter_recurse(array, &value); while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { const char *uuid; dbus_message_iter_get_basic(&value, &uuid); if (g_str_equal(uuid, DUN_GW_UUID)) return TRUE; dbus_message_iter_next(&value); } return FALSE; } static void alias_changed(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data) { const char *alias; struct bluetooth_device *bt_device = user_data; if (!g_str_equal("Alias", name)) return; dbus_message_iter_get_basic(iter, &alias); bt_device->name = g_strdup(alias); } static void bluetooth_device_removed(GDBusProxy *proxy, void *user_data) { struct bluetooth_device *bt_device = user_data; DBG("%s", bt_device->path); bluetooth_device_unregister(bt_device->path); } static void proxy_added(GDBusProxy *proxy, void *user_data) { const char *path = g_dbus_proxy_get_path(proxy); const char *interface = g_dbus_proxy_get_interface(proxy); struct bluetooth_device *bt_device; DBusMessageIter iter; if (!g_str_equal(BLUEZ_DEVICE_INTERFACE, interface)) return; if (!g_dbus_proxy_get_property(proxy, "UUIDs", &iter)) return; DBG("%s %s", path, interface); if (!has_dun_uuid(&iter)) return; bt_device = bluetooth_device_register(proxy); g_dbus_proxy_set_property_watch(proxy, alias_changed, bt_device); g_dbus_proxy_set_removed_watch(proxy, bluetooth_device_removed, bt_device); } static void connect_handler(DBusConnection *conn, void *user_data) { DBG(""); bt_register_profile(conn, DUN_GW_UUID, DUN_VERSION_1_2, "dun_dt", DUN_DT_PROFILE_PATH, "client", 0); } int __dundee_bluetooth_init(void) { DBusConnection *conn = ofono_dbus_get_connection(); DBG(""); if (!g_dbus_register_interface(conn, DUN_DT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE, profile_methods, NULL, NULL, NULL, NULL)) { ofono_error("Register Profile interface failed: %s", DUN_DT_PROFILE_PATH); return -EIO; } bluez = g_dbus_client_new(conn, BLUEZ_SERVICE, BLUEZ_MANAGER_PATH); g_dbus_client_set_connect_watch(bluez, connect_handler, NULL); g_dbus_client_set_proxy_handlers(bluez, proxy_added, NULL, NULL, NULL); registered_devices = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, bluetooth_device_destroy); return 0; } void __dundee_bluetooth_cleanup(void) { DBusConnection *conn = ofono_dbus_get_connection(); DBG(""); g_dbus_unregister_interface(conn, DUN_DT_PROFILE_PATH, BLUEZ_PROFILE_INTERFACE); g_dbus_client_unref(bluez); g_hash_table_destroy(registered_devices); } ofono-1.17.bzr6912+16.04.20160314.3/dundee/device.c0000644000015600001650000003750612671500024021261 0ustar pbuserpbgroup00000000000000/* * oFono - Open Source Telephony * * Copyright (C) 2008-2012 Intel Corporation. All rights reserved. * Copyright (C) 2012 BMW Car IT GmbH. 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 #endif #include #include #include #include #include #include #include #include #include "dundee.h" #define PPP_TIMEOUT 15 static int next_device_id = 0; static GHashTable *device_hash; static const char *none_prefix[] = { NULL }; struct ipv4_settings { char *interface; char *ip; char **nameservers; }; struct dundee_device { char *path; struct dundee_device_driver *driver; gboolean registered; GAtPPP *ppp; GAtChat *chat; char *name; gboolean active; struct ipv4_settings settings; DBusMessage *pending; guint connect_timeout; void *data; }; const char *__dundee_device_get_path(struct dundee_device *device) { return device->path; } static void settings_append(struct dundee_device *device, DBusMessageIter *iter) { DBusMessageIter variant; DBusMessageIter array; char typesig[5]; char arraysig[6]; arraysig[0] = DBUS_TYPE_ARRAY; arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR; arraysig[2] = typesig[1] = DBUS_TYPE_STRING; arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT; arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR; arraysig[5] = typesig[4] = '\0'; dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, arraysig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, typesig, &array); if (device->active == FALSE) goto out; if (device->settings.interface) ofono_dbus_dict_append(&array, "Interface", DBUS_TYPE_STRING, &device->settings.interface); if (device->settings.ip) ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING, &device->settings.ip); if (device->settings.nameservers) ofono_dbus_dict_append_array(&array, "DomainNameServers", DBUS_TYPE_STRING, &device->settings.nameservers); out: dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(iter, &variant); } static void settings_append_dict(struct dundee_device *device, DBusMessageIter *dict) { DBusMessageIter entry; const char *key = "Settings"; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); settings_append(device, &entry); dbus_message_iter_close_container(dict, &entry); } void __dundee_device_append_properties(struct dundee_device *device, DBusMessageIter *dict) { settings_append_dict(device, dict); ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING, &device->name); ofono_dbus_dict_append(dict, "Active", DBUS_TYPE_BOOLEAN, &device->active); } void __dundee_device_foreach(dundee_device_foreach_func func, void *userdata) { GHashTableIter iter; gpointer key, value; DBG(""); g_hash_table_iter_init(&iter, device_hash); while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) { struct dundee_device *device = value; func(device, userdata); } } static void settings_changed(struct dundee_device *device) { DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *signal; DBusMessageIter iter; const char *key = "Settings"; signal = dbus_message_new_signal(device->path, DUNDEE_DEVICE_INTERFACE, "PropertyChanged"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key); settings_append(device, &iter); g_dbus_send_message(conn, signal); } static DBusMessage *device_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { struct dundee_device *device = data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter dict; reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); __dundee_device_append_properties(device, &dict); dbus_message_iter_close_container(&iter, &dict); return reply; } static void debug(const char *str, void *data) { DBG("%s: %s\n", (const char *) data, str); } static void ppp_connect(const char *iface, const char *local, const char *peer, const char *dns1, const char *dns2, gpointer user_data) { DBusConnection *conn = ofono_dbus_get_connection(); struct dundee_device *device = user_data; const char *dns[3] = { dns1, dns2, 0 }; DBG("%p", device); DBG("Network Device: %s\n", iface); DBG("IP Address: %s\n", local); DBG("Peer IP Address: %s\n", peer); DBG("Primary DNS Server: %s\n", dns1); DBG("Secondary DNS Server: %s\n", dns2); if (device->connect_timeout > 0) { g_source_remove(device->connect_timeout); device->connect_timeout = 0; } g_free(device->settings.interface); device->settings.interface = g_strdup(iface); if (device->settings.interface == NULL) goto err; g_free(device->settings.ip); device->settings.ip = g_strdup(local); if (device->settings.ip == NULL) goto err; g_strfreev(device->settings.nameservers); device->settings.nameservers = g_strdupv((gchar **)dns); if (device->settings.nameservers == NULL) goto err; __ofono_dbus_pending_reply(&device->pending, dbus_message_new_method_return(device->pending)); device->pending = NULL; device->active = TRUE; settings_changed(device); ofono_dbus_signal_property_changed(conn, device->path, DUNDEE_DEVICE_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &device->active); return; err: g_free(device->settings.interface); g_free(device->settings.ip); g_strfreev(device->settings.nameservers); device->settings.interface = NULL; device->settings.ip = NULL; device->settings.nameservers = NULL; __ofono_dbus_pending_reply(&device->pending, __dundee_error_failed(device->pending)); device->pending = NULL; } void dundee_device_disconnect(const struct dundee_error *error, struct dundee_device *device) { if (device == NULL) return; DBG("%s", device->path); g_at_chat_unref(device->chat); device->chat = NULL; if (device->pending == NULL) return; if (error->type != DUNDEE_ERROR_TYPE_NO_ERROR) { __ofono_dbus_pending_reply(&device->pending, __dundee_error_failed(device->pending)); goto out; } __ofono_dbus_pending_reply(&device->pending, dbus_message_new_method_return(device->pending)); out: device->pending = NULL; } static void disconnect_callback(const struct dundee_error *error, void *data) { struct dundee_device *device = data; dundee_device_disconnect(error, device); } static gboolean ppp_connect_timeout(gpointer user_data) { struct dundee_device *device = user_data; if (device->pending != NULL) { __ofono_dbus_pending_reply(&device->pending, __dundee_error_timed_out(device->pending)); device->pending = NULL; } device->driver->disconnect(device, disconnect_callback, device); device->connect_timeout = 0; return FALSE; } static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data) { DBusConnection *conn = ofono_dbus_get_connection(); struct dundee_device *device = user_data; DBG("%p", device); DBG("PPP Link down: %d\n", reason); g_at_ppp_unref(device->ppp); device->ppp = NULL; g_at_chat_resume(device->chat); g_free(device->settings.interface); g_free(device->settings.ip); g_strfreev(device->settings.nameservers); device->settings.interface = NULL; device->settings.ip = NULL; device->settings.nameservers = NULL; device->active = FALSE; settings_changed(device); ofono_dbus_signal_property_changed(conn, device->path, DUNDEE_DEVICE_INTERFACE, "Active", DBUS_TYPE_BOOLEAN, &device->active); device->driver->disconnect(device, disconnect_callback, device); } static void dial_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct dundee_device *device = user_data; GAtIO *io; if (!ok) { DBG("Unable to define context\n"); goto err; } /* get the data IO channel */ io = g_at_chat_get_io(device->chat); /* * shutdown gatchat or else it tries to take all the input * from the modem and does not let PPP get it. */ g_at_chat_suspend(device->chat); /* open ppp */ device->ppp = g_at_ppp_new(); if (device->ppp == NULL) { DBG("Unable to create PPP object\n"); goto err; } g_at_ppp_set_debug(device->ppp, debug, "PPP"); device->connect_timeout = g_timeout_add_seconds(PPP_TIMEOUT, ppp_connect_timeout, device); /* set connect and disconnect callbacks */ g_at_ppp_set_connect_function(device->ppp, ppp_connect, device); g_at_ppp_set_disconnect_function(device->ppp, ppp_disconnect, device); /* open the ppp connection */ g_at_ppp_open(device->ppp, io); return; err: __ofono_dbus_pending_reply(&device->pending, __dundee_error_failed(device->pending)); device->pending = NULL; device->driver->disconnect(device, disconnect_callback, device); } static int device_dial_setup(struct dundee_device *device, int fd) { GAtSyntax *syntax; GIOChannel *io; io = g_io_channel_unix_new(fd); if (io == NULL) return -EIO; syntax = g_at_syntax_new_gsm_permissive(); device->chat = g_at_chat_new(io, syntax); g_io_channel_unref(io); g_at_syntax_unref(syntax); if (device->chat == NULL) return -EIO; g_at_chat_set_debug(device->chat, debug, "Control"); g_at_chat_send(device->chat, "ATD*99#", none_prefix, dial_cb, device, NULL); return 0; } static void connect_callback(const struct dundee_error *error, int fd, void *data) { struct dundee_device *device = data; int err; DBG("%p", device); if (error->type != DUNDEE_ERROR_TYPE_NO_ERROR) goto err; err = device_dial_setup(device, fd); if (err < 0) goto err; return; err: __ofono_dbus_pending_reply(&device->pending, __dundee_error_failed(device->pending)); device->pending = NULL; } static DBusMessage *set_property_active(struct dundee_device *device, DBusMessage *msg, DBusMessageIter *var) { ofono_bool_t active; DBG("%p path %s", device, device->path); if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) return __dundee_error_invalid_args(msg); if (device->pending) return __dundee_error_in_progress(msg); dbus_message_iter_get_basic(var, &active); device->pending = dbus_message_ref(msg); if (active) device->driver->connect(device, connect_callback, device); else if (device->ppp) g_at_ppp_shutdown(device->ppp); return NULL; } static DBusMessage *device_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { struct dundee_device *device = data; DBusMessageIter iter, var; const char *name; if (dbus_message_iter_init(msg, &iter) == FALSE) return __dundee_error_invalid_args(msg); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __dundee_error_invalid_args(msg); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return __dundee_error_invalid_args(msg); dbus_message_iter_recurse(&iter, &var); if (g_str_equal(name, "Active")) return set_property_active(device, msg, &var); return __dundee_error_invalid_args(msg); } static const GDBusMethodTable device_methods[] = { { GDBUS_METHOD("GetProperties", NULL, GDBUS_ARGS({ "properties", "a{sv}" }), device_get_properties) }, { GDBUS_ASYNC_METHOD("SetProperty", GDBUS_ARGS({ "property", "s" }, { "value", "v" }), NULL, device_set_property) }, { } }; static const GDBusSignalTable device_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, { } }; static int register_device(struct dundee_device *device) { DBusConnection *conn = ofono_dbus_get_connection(); DBusMessage *signal; DBusMessageIter iter; DBusMessageIter dict; DBG("%p path %s", device, device->path); if (!g_dbus_register_interface(conn, device->path, DUNDEE_DEVICE_INTERFACE, device_methods, device_signals, NULL, device, NULL)) { ofono_error("Could not register Device %s", device->path); return -EIO; } signal = dbus_message_new_signal(DUNDEE_MANAGER_PATH, DUNDEE_MANAGER_INTERFACE, "DeviceAdded"); if (signal == NULL) return -ENOMEM; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &device->path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); __dundee_device_append_properties(device, &dict); dbus_message_iter_close_container(&iter, &dict); g_dbus_send_message(conn, signal); return 0; } static int unregister_device(struct dundee_device *device) { DBusConnection *conn = ofono_dbus_get_connection(); DBG("%p path %s", device, device->path); g_dbus_unregister_interface(conn, device->path, DUNDEE_DEVICE_INTERFACE); g_dbus_emit_signal(conn, DUNDEE_MANAGER_PATH, DUNDEE_MANAGER_INTERFACE, "DeviceRemoved", DBUS_TYPE_OBJECT_PATH, &device->path, DBUS_TYPE_INVALID); return 0; } static void destroy_device(gpointer user) { struct dundee_device *device = user; if (device->chat != NULL) g_at_chat_unref(device->chat); if (device->ppp != NULL) g_at_ppp_unref(device->ppp); if (device->pending) dbus_message_unref(device->pending); g_free(device->settings.interface); g_free(device->settings.ip); g_strfreev(device->settings.nameservers); g_free(device->path); g_free(device->name); g_free(device); } struct dundee_device *dundee_device_create(struct dundee_device_driver *d) { struct dundee_device *device; device = g_try_new0(struct dundee_device, 1); if (device == NULL) return NULL; device->driver = d; device->path = g_strdup_printf("/device%d", next_device_id); if (device->path == NULL) { g_free(device); return NULL; } next_device_id += 1; return device; } int dundee_device_register(struct dundee_device *device) { int err; err = register_device(device); if (err < 0) return err; device->registered = TRUE; g_hash_table_insert(device_hash, g_strdup(device->path), device); return 0; } void dundee_device_unregister(struct dundee_device *device) { DBG("%p", device); unregister_device(device); device->registered = FALSE; g_hash_table_remove(device_hash, device->path); } void dundee_device_set_data(struct dundee_device *device, void *data) { device->data = data; } void *dundee_device_get_data(struct dundee_device *device) { return device->data; } int dundee_device_set_name(struct dundee_device *device, const char *name) { DBusConnection *conn = ofono_dbus_get_connection(); DBG("%p name %s", device, name); g_free(device->name); device->name = g_strdup(name); if (device->registered == FALSE) return 0; ofono_dbus_signal_property_changed(conn, device->path, DUNDEE_DEVICE_INTERFACE, "Name", DBUS_TYPE_STRING, &device->name); return 0; } static void device_shutdown(gpointer key, gpointer value, gpointer user_data) { struct dundee_device *device = value; unregister_device(device); } void __dundee_device_shutdown(void) { g_hash_table_foreach(device_hash, device_shutdown, NULL); __dundee_exit(); } int __dundee_device_init(void) { DBG(""); device_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_device); return 0; } void __dundee_device_cleanup(void) { DBG(""); g_hash_table_destroy(device_hash); } ofono-1.17.bzr6912+16.04.20160314.3/dundee/dundee.conf0000644000015600001650000000123712671500024021761 0ustar pbuserpbgroup00000000000000 ofono-1.17.bzr6912+16.04.20160314.3/dundee/manager.c0000644000015600001650000000626012671500024021425 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2012 Intel Corporation. All rights reserved. * Copyright (C) 2012 BMW Car IT GmbH. 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 #endif #include #include #include #include "dundee.h" static void append_device(struct dundee_device *device, void *userdata) { DBusMessageIter *array = userdata; const char *path = __dundee_device_get_path(device); DBusMessageIter entry, dict; dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path); dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); __dundee_device_append_properties(device, &dict); dbus_message_iter_close_container(&entry, &dict); dbus_message_iter_close_container(array, &entry); } static DBusMessage *manager_get_devices(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; DBG(""); reply = dbus_message_new_method_return(msg); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_STRUCT_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING, &array); __dundee_device_foreach(append_device, &array); dbus_message_iter_close_container(&iter, &array); return reply; } static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetDevices", NULL, GDBUS_ARGS({ "devices", "a(oa{sv})" }), manager_get_devices) }, { } }; static const GDBusSignalTable manager_signals[] = { { GDBUS_SIGNAL("DeviceAdded", GDBUS_ARGS({ "path", "o"},{ "properties", "a{sv}" })) }, { GDBUS_SIGNAL("DeviceRemoved", GDBUS_ARGS({ "path", "o"})) }, { } }; int __dundee_manager_init(void) { DBusConnection *conn = ofono_dbus_get_connection(); gboolean ret; ret = g_dbus_register_interface(conn, DUNDEE_MANAGER_PATH, DUNDEE_MANAGER_INTERFACE, manager_methods, manager_signals, NULL, NULL, NULL); if (ret == FALSE) return -1; return 0; } void __dundee_manager_cleanup(void) { DBusConnection *conn = ofono_dbus_get_connection(); g_dbus_unregister_interface(conn, DUNDEE_MANAGER_PATH, DUNDEE_MANAGER_INTERFACE); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/0000755000015600001650000000000012671500304020013 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/gatchat/crc-ccitt.h0000644000015600001650000000200412671500024022032 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include extern guint16 const crc_ccitt_table[256]; static inline guint16 crc_ccitt_byte(guint16 crc, const guint8 c) { return (crc >> 8) ^ crc_ccitt_table[(crc ^ c) & 0xff]; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatresult.h0000644000015600001650000000445012671500024022200 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 * */ #ifndef __GATCHAT_RESULT_H #define __GATCHAT_RESULT_H #ifdef __cplusplus extern "C" { #endif struct _GAtResult { GSList *lines; char *final_or_pdu; }; typedef struct _GAtResult GAtResult; #define G_AT_RESULT_LINE_LENGTH_MAX 2048 struct _GAtResultIter { GAtResult *result; GSList *l; char buf[G_AT_RESULT_LINE_LENGTH_MAX + 1]; unsigned int line_pos; GSList pre; }; typedef struct _GAtResultIter GAtResultIter; void g_at_result_iter_init(GAtResultIter *iter, GAtResult *result); gboolean g_at_result_iter_next(GAtResultIter *iter, const char *prefix); gboolean g_at_result_iter_open_list(GAtResultIter *iter); gboolean g_at_result_iter_close_list(GAtResultIter *iter); gboolean g_at_result_iter_skip_next(GAtResultIter *iter); gboolean g_at_result_iter_next_range(GAtResultIter *iter, gint *min, gint *max); gboolean g_at_result_iter_next_string(GAtResultIter *iter, const char **str); gboolean g_at_result_iter_next_unquoted_string(GAtResultIter *iter, const char **str); gboolean g_at_result_iter_next_number(GAtResultIter *iter, gint *number); gboolean g_at_result_iter_next_number_default(GAtResultIter *iter, gint dflt, gint *number); gboolean g_at_result_iter_next_hexstring(GAtResultIter *iter, const guint8 **str, gint *length); const char *g_at_result_iter_raw_line(GAtResultIter *iter); const char *g_at_result_final_response(GAtResult *result); const char *g_at_result_pdu(GAtResult *result); gint g_at_result_num_response_lines(GAtResult *result); #ifdef __cplusplus } #endif #endif /* __GATCHAT_RESULT_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatresult.c0000644000015600001650000002050012671500024022165 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include #include #include #include "gatresult.h" void g_at_result_iter_init(GAtResultIter *iter, GAtResult *result) { iter->result = result; iter->pre.next = result->lines; iter->pre.data = NULL; iter->l = &iter->pre; iter->line_pos = 0; } gboolean g_at_result_iter_next(GAtResultIter *iter, const char *prefix) { char *line; int prefix_len = prefix ? strlen(prefix) : 0; int linelen; while ((iter->l = iter->l->next)) { line = iter->l->data; linelen = strlen(line); if (linelen > G_AT_RESULT_LINE_LENGTH_MAX) continue; if (prefix_len == 0) { iter->line_pos = 0; goto out; } if (g_str_has_prefix(line, prefix) == FALSE) continue; iter->line_pos = prefix_len; while (iter->line_pos < strlen(line) && line[iter->line_pos] == ' ') iter->line_pos += 1; goto out; } return FALSE; out: /* Already checked the length to be no more than buflen */ strcpy(iter->buf, line); return TRUE; } const char *g_at_result_iter_raw_line(GAtResultIter *iter) { const char *line; if (iter == NULL) return NULL; if (iter->l == NULL) return NULL; line = iter->l->data; line += iter->line_pos; return line; } static inline int skip_to_next_field(const char *line, int pos, int len) { if (pos < len && line[pos] == ',') pos += 1; while (pos < len && line[pos] == ' ') pos += 1; return pos; } gboolean g_at_result_iter_next_unquoted_string(GAtResultIter *iter, const char **str) { unsigned int pos; unsigned int end; unsigned int len; char *line; if (iter == NULL) return FALSE; if (iter->l == NULL) return FALSE; line = iter->l->data; len = strlen(line); pos = iter->line_pos; /* Omitted string */ if (line[pos] == ',') { end = pos; iter->buf[pos] = '\0'; goto out; } if (line[pos] == '"' || line[pos] == ')') return FALSE; end = pos; while (end < len && line[end] != ',' && line[end] != ')') end += 1; iter->buf[end] = '\0'; out: iter->line_pos = skip_to_next_field(line, end, len); if (str) *str = iter->buf + pos; return TRUE; } gboolean g_at_result_iter_next_string(GAtResultIter *iter, const char **str) { unsigned int pos; unsigned int end; unsigned int len; char *line; if (iter == NULL) return FALSE; if (iter->l == NULL) return FALSE; line = iter->l->data; len = strlen(line); pos = iter->line_pos; /* Omitted string */ if (line[pos] == ',') { end = pos; iter->buf[pos] = '\0'; goto out; } if (line[pos++] != '"') return FALSE; end = pos; while (end < len && line[end] != '"') end += 1; if (line[end] != '"') return FALSE; iter->buf[end] = '\0'; /* Skip " */ end += 1; out: iter->line_pos = skip_to_next_field(line, end, len); if (str) *str = iter->buf + pos; return TRUE; } gboolean g_at_result_iter_next_hexstring(GAtResultIter *iter, const guint8 **str, gint *length) { unsigned int pos; unsigned int end; unsigned int len; char *line; char *bufpos; if (iter == NULL) return FALSE; if (iter->l == NULL) return FALSE; line = iter->l->data; len = strlen(line); pos = iter->line_pos; bufpos = iter->buf + pos; /* Omitted string */ if (line[pos] == ',') { end = pos; iter->buf[pos] = '\0'; goto out; } if (line[pos] == '"') pos += 1; end = pos; while (end < len && g_ascii_isxdigit(line[end])) end += 1; if ((end - pos) & 1) return FALSE; *length = (end - pos) / 2; for (; pos < end; pos += 2) sscanf(line + pos, "%02hhx", bufpos++); if (line[end] == '"') end += 1; out: iter->line_pos = skip_to_next_field(line, end, len); if (str) *str = (guint8 *) bufpos - *length; return TRUE; } gboolean g_at_result_iter_next_number(GAtResultIter *iter, gint *number) { int pos; int end; int len; int value = 0; char *line; if (iter == NULL) return FALSE; if (iter->l == NULL) return FALSE; line = iter->l->data; len = strlen(line); pos = iter->line_pos; end = pos; while (line[end] >= '0' && line[end] <= '9') { value = value * 10 + (int)(line[end] - '0'); end += 1; } if (pos == end) return FALSE; iter->line_pos = skip_to_next_field(line, end, len); if (number) *number = value; return TRUE; } gboolean g_at_result_iter_next_number_default(GAtResultIter *iter, gint dflt, gint *number) { unsigned int pos; int len; char *line; if (iter == NULL) return FALSE; if (iter->l == NULL) return FALSE; line = iter->l->data; len = strlen(line); pos = skip_to_next_field(line, iter->line_pos, len); if (pos != iter->line_pos) { iter->line_pos = pos; if (number) *number = dflt; return TRUE; } return g_at_result_iter_next_number(iter, number); } gboolean g_at_result_iter_next_range(GAtResultIter *iter, gint *min, gint *max) { int pos; int end; int len; int low = 0; int high = 0; char *line; if (iter == NULL) return FALSE; if (iter->l == NULL) return FALSE; line = iter->l->data; len = strlen(line); pos = iter->line_pos; while (pos < len && line[pos] == ' ') pos += 1; end = pos; while (line[end] >= '0' && line[end] <= '9') { low = low * 10 + (int)(line[end] - '0'); end += 1; } if (pos == end) return FALSE; if (line[end] != '-') { high = low; goto out; } pos = end = end + 1; while (line[end] >= '0' && line[end] <= '9') { high = high * 10 + (int)(line[end] - '0'); end += 1; } if (pos == end) return FALSE; out: iter->line_pos = skip_to_next_field(line, end, len); if (min) *min = low; if (max) *max = high; return TRUE; } static gint skip_until(const char *line, int start, const char delim) { int len = strlen(line); int i = start; while (i < len) { if (line[i] == delim) return i; if (line[i] == '\"') { i += 1; while (i < len && line[i] != '\"') i += 1; if (i < len) i += 1; continue; } if (line[i] != '(') { i += 1; continue; } i = skip_until(line, i+1, ')'); if (i < len) i += 1; } return i; } gboolean g_at_result_iter_skip_next(GAtResultIter *iter) { unsigned int skipped_to; char *line; if (iter == NULL) return FALSE; if (iter->l == NULL) return FALSE; line = iter->l->data; skipped_to = skip_until(line, iter->line_pos, ','); if (skipped_to == iter->line_pos && line[skipped_to] != ',') return FALSE; iter->line_pos = skip_to_next_field(line, skipped_to, strlen(line)); return TRUE; } gboolean g_at_result_iter_open_list(GAtResultIter *iter) { char *line; unsigned int len; if (iter == NULL) return FALSE; if (iter->l == NULL) return FALSE; line = iter->l->data; len = strlen(line); if (iter->line_pos >= len) return FALSE; if (line[iter->line_pos] != '(') return FALSE; iter->line_pos += 1; while (iter->line_pos < strlen(line) && line[iter->line_pos] == ' ') iter->line_pos += 1; return TRUE; } gboolean g_at_result_iter_close_list(GAtResultIter *iter) { char *line; unsigned int len; if (iter == NULL) return FALSE; if (iter->l == NULL) return FALSE; line = iter->l->data; len = strlen(line); if (iter->line_pos >= len) return FALSE; if (line[iter->line_pos] != ')') return FALSE; iter->line_pos += 1; iter->line_pos = skip_to_next_field(line, iter->line_pos, len); return TRUE; } const char *g_at_result_final_response(GAtResult *result) { if (result == NULL) return NULL; return result->final_or_pdu; } const char *g_at_result_pdu(GAtResult *result) { if (result == NULL) return NULL; return result->final_or_pdu; } gint g_at_result_num_response_lines(GAtResult *result) { if (result == NULL) return 0; if (result->lines == NULL) return 0; return g_slist_length(result->lines); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gsmdial.c0000644000015600001650000004227612671500024021611 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define IFCONFIG_PATH "/sbin/ifconfig" static const char *none_prefix[] = { NULL }; static const char *cfun_prefix[] = { "+CFUN:", NULL }; static const char *creg_prefix[] = { "+CREG:", NULL }; static const char *cgreg_prefix[] = { "+CGREG:", NULL }; static gchar *option_ip = NULL; static gint option_port = 0; static gchar *option_modem = NULL; static gchar *option_control = NULL; static gint option_cid = 0; static gchar *option_apn = NULL; static gint option_offmode = 0; static gboolean option_legacy = FALSE; static gchar *option_username = NULL; static gchar *option_password = NULL; static gchar *option_pppdump = NULL; static gboolean option_bluetooth = FALSE; static gboolean option_acfc = FALSE; static gboolean option_pfc = FALSE; static GAtPPP *ppp; static GAtChat *control; static GAtChat *modem; static GMainLoop *event_loop; enum state { STATE_NONE = 0, STATE_REGISTERING, STATE_ATTACHING, STATE_ACTIVATING }; static int state = 0; static int oldmode = 0; static void gsmdial_debug(const char *str, void *data) { g_print("%s: %s\n", (const char *) data, str); } static gboolean quit_eventloop(gpointer user_data) { g_main_loop_quit(event_loop); return FALSE; } static void power_down(gboolean ok, GAtResult *result, gpointer user_data) { g_main_loop_quit(event_loop); } static void kill_ppp(gboolean ok, GAtResult *result, gpointer user_data) { g_print("kill_ppp: %d\n", ok); if (ok == FALSE) return; g_at_ppp_unref(ppp); ppp = NULL; } static void ppp_suspend_ath0(gpointer user_data) { g_at_chat_resume(modem); g_at_chat_send(modem, "ATH0", none_prefix, kill_ppp, NULL, NULL); } static void resume_ppp(gboolean ok, GAtResult *result, gpointer user_data) { g_print("resume_ppp: %d\n", ok); if (ok == FALSE) return; g_at_chat_suspend(modem); g_at_ppp_resume(ppp); } static void ppp_suspend_ato0(gpointer user_data) { g_at_chat_resume(modem); g_at_chat_send(modem, "ATO0", none_prefix, resume_ppp, NULL, NULL); } static gboolean signal_cb(GIOChannel *channel, GIOCondition cond, gpointer data) { static int terminated = 0; int signal_fd = GPOINTER_TO_INT(data); struct signalfd_siginfo si; ssize_t res; if (cond & (G_IO_NVAL | G_IO_ERR)) return FALSE; res = read(signal_fd, &si, sizeof(si)); if (res != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGINT: case SIGTERM: if (terminated == 0) { g_timeout_add_seconds(10, quit_eventloop, NULL); if (ppp == NULL) { char buf[64]; sprintf(buf, "AT+CFUN=%u", option_offmode); g_at_chat_send(control, buf, none_prefix, power_down, NULL, NULL); } else g_at_ppp_shutdown(ppp); } terminated++; break; case SIGUSR1: if (ppp == NULL) break; g_at_ppp_set_suspend_function(ppp, ppp_suspend_ato0, NULL); g_at_ppp_suspend(ppp); break; case SIGUSR2: if (ppp == NULL) break; g_at_ppp_set_suspend_function(ppp, ppp_suspend_ath0, NULL); g_at_ppp_suspend(ppp); break; default: break; } return TRUE; } static gboolean at_util_parse_reg_unsolicited(GAtResult *result, const char *prefix, int *status, int *lac, int *ci, int *tech) { GAtResultIter iter; int s; int l = -1, c = -1, t = -1; const char *str; g_at_result_iter_init(&iter, result); if (g_at_result_iter_next(&iter, prefix) == FALSE) return FALSE; if (g_at_result_iter_next_number(&iter, &s) == FALSE) return FALSE; if (g_at_result_iter_next_string(&iter, &str) == TRUE) l = strtol(str, NULL, 16); else goto out; if (g_at_result_iter_next_string(&iter, &str) == TRUE) c = strtol(str, NULL, 16); else goto out; g_at_result_iter_next_number(&iter, &t); out: if (status) *status = s; if (lac) *lac = l; if (ci) *ci = c; if (tech) *tech = t; return TRUE; } static gboolean at_util_parse_reg(GAtResult *result, const char *prefix, int *mode, int *status, int *lac, int *ci, int *tech) { GAtResultIter iter; int m, s; int l = -1, c = -1, t = -1; const char *str; g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, prefix)) { g_at_result_iter_next_number(&iter, &m); /* Sometimes we get an unsolicited CREG/CGREG here, skip it */ if (g_at_result_iter_next_number(&iter, &s) == FALSE) continue; if (g_at_result_iter_next_string(&iter, &str) == TRUE) l = strtol(str, NULL, 16); else goto out; if (g_at_result_iter_next_string(&iter, &str) == TRUE) c = strtol(str, NULL, 16); else goto out; g_at_result_iter_next_number(&iter, &t); out: if (mode) *mode = m; if (status) *status = s; if (lac) *lac = l; if (ci) *ci = c; if (tech) *tech = t; return TRUE; } return FALSE; } static gboolean execute(const char *cmd) { int status; status = system(cmd); if (status < 0) { g_print("Failed to execute command: %s\n", strerror(errno)); return FALSE; } return TRUE; } static void ppp_connect(const char *iface, const char *local, const char *peer, const char *dns1, const char *dns2, gpointer user_data) { char buf[512]; /* print out the negotiated address and dns server */ g_print("Network Device: %s\n", iface); g_print("IP Address: %s\n", local); g_print("Peer IP Address: %s\n", peer); g_print("Primary DNS Server: %s\n", dns1); g_print("Secondary DNS Server: %s\n", dns2); if (getuid() != 0) { g_print("Need root privilege to config PPP interface\n"); return; } snprintf(buf, sizeof(buf), "%s %s up", IFCONFIG_PATH, iface); execute(buf); snprintf(buf, sizeof(buf), "%s %s %s pointopoint %s", IFCONFIG_PATH, iface, local, peer); execute(buf); } static void no_carrier_notify(GAtResult *result, gpointer user_data) { char buf[64]; if (option_bluetooth) { g_main_loop_quit(event_loop); return; } sprintf(buf, "AT+CFUN=%u", option_offmode); g_at_chat_send(control, buf, none_prefix, power_down, NULL, NULL); } static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user_data) { g_print("PPP Link down: %d\n", reason); g_at_ppp_unref(ppp); ppp = NULL; if (option_modem == NULL) g_at_chat_set_debug(modem, gsmdial_debug, ""); else g_at_chat_set_debug(modem, gsmdial_debug, "Modem"); g_at_chat_register(modem, "NO CARRIER", no_carrier_notify, FALSE, NULL, NULL); g_at_chat_resume(modem); } static void connect_cb(gboolean ok, GAtResult *result, gpointer user_data) { GAtIO *io; if (!ok) { g_print("Unable to define context\n"); exit(1); } /* get the data IO channel */ io = g_at_chat_get_io(modem); /* * shutdown gatchat or else it tries to take all the input * from the modem and does not let PPP get it. */ g_at_chat_suspend(modem); /* open ppp */ ppp = g_at_ppp_new(); if (ppp == NULL) { g_print("Unable to create PPP object\n"); exit(1); } g_at_ppp_set_debug(ppp, gsmdial_debug, "PPP"); g_at_ppp_set_credentials(ppp, option_username, option_password); g_at_ppp_set_acfc_enabled(ppp, option_acfc); g_at_ppp_set_pfc_enabled(ppp, option_pfc); /* set connect and disconnect callbacks */ g_at_ppp_set_connect_function(ppp, ppp_connect, NULL); g_at_ppp_set_disconnect_function(ppp, ppp_disconnect, NULL); /* open the ppp connection */ g_at_ppp_open(ppp, io); if (option_pppdump) g_at_ppp_set_recording(ppp, option_pppdump); } static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data) { char buf[64]; if (!ok) { g_print("Unable to define context\n"); exit(1); } if (option_legacy == TRUE) sprintf(buf, "ATD*99***%u#", option_cid); else sprintf(buf, "AT+CGDATA=\"PPP\",%u", option_cid); g_at_chat_send(modem, buf, none_prefix, connect_cb, NULL, NULL); } static void setup_context(int status) { char buf[1024]; int len; state = STATE_ACTIVATING; g_print("Registered to GPRS network, roaming=%s\n", status == 5 ? "true" : "false"); len = sprintf(buf, "AT+CGDCONT=%u,\"IP\"", option_cid); snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", option_apn); g_at_chat_send(control, buf, none_prefix, at_cgdcont_cb, NULL, NULL); } static void cgreg_notify(GAtResult *result, gpointer user_data) { int status, lac, ci, tech; if (state != STATE_ATTACHING) return; if (at_util_parse_reg_unsolicited(result, "+CGREG:", &status, &lac, &ci, &tech) == FALSE) return; if (status != 1 && status != 5) return; setup_context(status); } static void cgreg_cb(gboolean ok, GAtResult *result, gpointer user_data) { int status, lac, ci, tech; if (!ok) return; if (at_util_parse_reg(result, "+CGREG:", NULL, &status, &lac, &ci, &tech) == FALSE) return; if (status != 1 && status != 5) { g_at_chat_register(control, "+CGREG:", cgreg_notify, FALSE, NULL, NULL); return; } setup_context(status); } static void attached_cb(gboolean ok, GAtResult *result, gpointer user_data) { if (!ok) return; g_at_chat_send(control, "AT+CGREG?", cgreg_prefix, cgreg_cb, NULL, NULL); } static void activate_gprs(int status) { state = STATE_ATTACHING; g_print("Registered to network, roaming=%s\n", status == 5 ? "true" : "false"); g_print("Activating GPRS network...\n"); g_at_chat_send(control, "AT+CGATT=1", none_prefix, attached_cb, NULL, NULL); } static void creg_notify(GAtResult *result, gpointer user_data) { int status, lac, ci, tech; if (state != STATE_REGISTERING) return; if (at_util_parse_reg_unsolicited(result, "+CREG:", &status, &lac, &ci, &tech) == FALSE) return; if (status != 1 && status != 5) return; activate_gprs(status); } static void creg_cb(gboolean ok, GAtResult *result, gpointer user_data) { int status, lac, ci, tech; if (!ok) return; if (at_util_parse_reg(result, "+CREG:", NULL, &status, &lac, &ci, &tech) == FALSE) return; if (status != 1 && status != 5) { g_at_chat_register(control, "+CREG:", creg_notify, FALSE, NULL, NULL); return; } activate_gprs(status); } static void register_cb(gboolean ok, GAtResult *result, gpointer user_data) { if (!ok) { g_print("Couldn't register to network, exiting...\n"); exit(1); } state = STATE_REGISTERING; g_print("Waiting for network registration...\n"); g_at_chat_send(control, "AT+CREG?", creg_prefix, creg_cb, NULL, NULL); } static void start_dial(gboolean ok, GAtResult *result, gpointer user_data) { if (!ok) { g_print("Checking PIN status failed\n"); exit(1); } g_at_chat_send(control, "AT+CREG=2", none_prefix, NULL, NULL, NULL); g_at_chat_send(control, "AT+CGREG=2", none_prefix, NULL, NULL, NULL); g_at_chat_send(control, "AT+COPS=0", none_prefix, register_cb, NULL, NULL); } static void check_pin(gboolean ok, GAtResult *result, gpointer user_data) { if (!ok) { g_print("Turning on the modem failed\n"); exit(1); } g_at_chat_send(control, "AT+CPIN?", NULL, start_dial, NULL, NULL); } static void check_mode(gboolean ok, GAtResult *result, gpointer user_data) { GAtResultIter iter; if (!ok) { g_print("Checking modem mode failed\n"); exit(1); } g_at_result_iter_init(&iter, result); g_at_result_iter_next(&iter, "+CFUN:"); g_at_result_iter_next_number(&iter, &oldmode); g_print("Current modem mode is %d\n", oldmode); if (oldmode == 1) { check_pin(ok, result, user_data); return; } g_at_chat_send(control, "AT+CFUN=1", NULL, check_pin, NULL, NULL); } static int open_serial(void) { GAtSyntax *syntax; GIOChannel *channel; channel = g_at_tty_open(option_control, NULL); if (channel == NULL) return -EIO; syntax = g_at_syntax_new_gsm_permissive(); control = g_at_chat_new(channel, syntax); g_io_channel_unref(channel); g_at_syntax_unref(syntax); if (control == NULL) return -EIO; if (option_modem == NULL) { g_at_chat_ref(control); modem = control; g_at_chat_set_debug(control, gsmdial_debug, ""); } else { g_at_chat_set_debug(control, gsmdial_debug, "Control"); channel = g_at_tty_open(option_modem, NULL); if (channel == NULL) return -EIO; syntax = g_at_syntax_new_gsm_permissive(); modem = g_at_chat_new(channel, syntax); g_io_channel_unref(channel); g_at_syntax_unref(syntax); if (modem == NULL) return -EIO; g_at_chat_set_debug(modem, gsmdial_debug, "Modem"); } return 0; } static int open_ip(void) { int sk, err; struct sockaddr_in addr; GAtSyntax *syntax; GIOChannel *channel; sk = socket(PF_INET, SOCK_STREAM, 0); if (sk < 0) return -EINVAL; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(option_ip); addr.sin_port = htons(option_port); err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { close(sk); return err; } channel = g_io_channel_unix_new(sk); if (channel == NULL) { close(sk); return -ENOMEM; } syntax = g_at_syntax_new_gsmv1(); control = g_at_chat_new(channel, syntax); g_io_channel_unref(channel); g_at_syntax_unref(syntax); if (control == NULL) return -ENOMEM; g_at_chat_ref(control); modem = control; g_at_chat_set_debug(control, gsmdial_debug, ""); return 0; } static GOptionEntry options[] = { { "ip", 'i', 0, G_OPTION_ARG_STRING, &option_ip, "Specify IP" }, { "port", 'p', 0, G_OPTION_ARG_INT, &option_port, "Specify IP Port" }, { "control", 'n', 0, G_OPTION_ARG_FILENAME, &option_control, "Specify Modem Control port" }, { "modem", 'm', 0, G_OPTION_ARG_FILENAME, &option_modem, "Specify Modem port (ppp), if not provided" " the control port will be used" }, { "cid", 'c', 0, G_OPTION_ARG_INT, &option_cid, "Specify CID to use" }, { "apn", 'a', 0, G_OPTION_ARG_STRING, &option_apn, "Specify APN" }, { "offmode", 'o', 0, G_OPTION_ARG_INT, &option_offmode, "Specify CFUN offmode" }, { "legacy", 'l', 0, G_OPTION_ARG_NONE, &option_legacy, "Use ATD*99***#" }, { "bluetooth", 'b', 0, G_OPTION_ARG_NONE, &option_bluetooth, "Use only ATD*99" }, { "username", 'u', 0, G_OPTION_ARG_STRING, &option_username, "Specify PPP username" }, { "password", 'w', 0, G_OPTION_ARG_STRING, &option_password, "Specify PPP password" }, { "pppdump", 'D', 0, G_OPTION_ARG_STRING, &option_pppdump, "Specify pppdump filename" }, { "pfc", 0, 0, G_OPTION_ARG_NONE, &option_pfc, "Use Protocol Field Compression" }, { "acfc", 0, 0, G_OPTION_ARG_NONE, &option_acfc, "Use Address & Control Field Compression" }, { NULL }, }; int main(int argc, char **argv) { GOptionContext *context; GError *err = NULL; sigset_t mask; int signal_fd; GIOChannel *signal_io; int signal_source; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) { if (err != NULL) { g_printerr("%s\n", err->message); g_error_free(err); return 1; } g_printerr("An unknown error occurred\n"); return 1; } g_option_context_free(context); if (option_control) { int ret; g_print("Control: %s\n", option_control); if (option_modem) g_print("Modem: %s\n", option_modem); ret = open_serial(); g_free(option_control); g_free(option_modem); if (ret < 0) goto out; } else { int ret; g_print("IP: %s\n", option_ip); g_print("Port: %d\n", option_port); ret = open_ip(); g_free(option_ip); if (ret < 0) goto out; } g_print("APN: %s\n", option_apn); g_print("CID: %d\n", option_cid); sigemptyset(&mask); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGUSR1); sigaddset(&mask, SIGUSR2); sigaddset(&mask, SIGPIPE); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { perror("Can't set signal mask"); return 1; } signal_fd = signalfd(-1, &mask, 0); if (signal_fd < 0) { perror("Can't create signal filedescriptor"); return 1; } signal_io = g_io_channel_unix_new(signal_fd); g_io_channel_set_close_on_unref(signal_io, TRUE); signal_source = g_io_add_watch(signal_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_cb, GINT_TO_POINTER(signal_fd)); g_io_channel_unref(signal_io); event_loop = g_main_loop_new(NULL, FALSE); if (option_bluetooth) { g_at_chat_send(control, "ATD*99#", none_prefix, connect_cb, NULL, NULL); } else { g_at_chat_send(control, "ATE0Q0V1", NULL, NULL, NULL, NULL); g_at_chat_send(control, "AT+CFUN?", cfun_prefix, check_mode, NULL, NULL); } g_main_loop_run(event_loop); g_source_remove(signal_source); g_main_loop_unref(event_loop); out: if (ppp == NULL) { g_at_chat_unref(control); g_at_chat_unref(modem); } else g_at_ppp_unref(ppp); g_free(option_apn); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/test-qcdm.c0000644000015600001650000001053112671500024022057 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include "gattty.h" #include "gathdlc.h" static gboolean option_debug = FALSE; static gchar *option_device = NULL; static GMainLoop *event_loop; struct version_info { char comp_date[11]; char comp_time[8]; char rel_date[11]; char rel_time[8]; char model[8]; guint8 scm; guint8 mob_cai_rev; guint8 mob_model; guint16 mob_firmware_rev; guint8 slot_cycle_index; guint8 msm_ver; guint8 unknown; } __attribute__ ((packed)); static void parse_qcdm(const unsigned char *buf, gsize len) { struct version_info *verinfo; char str[12]; guint8 cmd = buf[0]; switch (cmd) { case 0x00: g_print("==> Version information\n"); verinfo = (struct version_info *) (buf + 1); snprintf(str, 12, "%s", verinfo->comp_date); g_print("Compiled Date: %s\n", str); snprintf(str, 9, "%s", verinfo->comp_time); g_print("Compiled Time: %s\n", str); snprintf(str, 12, "%s", verinfo->rel_date); g_print("Release Date: %s\n", str); snprintf(str, 9, "%s", verinfo->rel_time); g_print("Release Time: %s\n", str); snprintf(str, 9, "%s", verinfo->model); g_print("Model: %s\n", str); g_print("MSM version: %d\n", verinfo->msm_ver); break; case 0x13: g_print("==> Invalid command response\n"); break; case 0x4b: g_print("==> Subsystem response\n"); break; case 0x51: g_print("==> Features response\n"); break; default: g_print("==> Unknown command 0x%02x\n", cmd); break; } } static void hdlc_debug(const char *str, void *data) { g_print("%s: %s\n", (const char *) data, str); } static void hdlc_receive(const unsigned char *buf, gsize len, void *data) { parse_qcdm(buf, len); } static void send_command(GAtHDLC *hdlc, guint8 cmd) { unsigned char cmdbuf[1]; cmdbuf[0] = cmd; g_at_hdlc_send(hdlc, cmdbuf, sizeof(cmdbuf)); } static void send_subsys_command(GAtHDLC *hdlc, guint8 id, guint16 cmd) { unsigned char cmdbuf[4]; cmdbuf[0] = 0x4b; cmdbuf[1] = id; cmdbuf[2] = cmd & 0xff; cmdbuf[3] = cmd >> 8; g_at_hdlc_send(hdlc, cmdbuf, sizeof(cmdbuf)); } static GOptionEntry options[] = { { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug, "Enable debugging" }, { "device", 'n', 0, G_OPTION_ARG_STRING, &option_device, "Specify device" }, { NULL }, }; int main(int argc, char **argv) { GOptionContext *context; GError *err = NULL; GIOChannel *channel; GAtHDLC *hdlc; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) { if (err != NULL) { g_printerr("%s\n", err->message); g_error_free(err); return 1; } g_printerr("An unknown error occurred\n"); return 1; } g_option_context_free(context); if (option_device == NULL) option_device = g_strdup("/dev/ttyUSB1"); g_print("Device: %s\n", option_device); channel = g_at_tty_open_qcdm(option_device); if (channel == NULL) { g_printerr("Failed to open QCDM device\n"); return 1; } event_loop = g_main_loop_new(NULL, FALSE); hdlc = g_at_hdlc_new(channel); g_io_channel_unref(channel); if (hdlc == NULL) return 1; if (option_debug == TRUE) g_at_hdlc_set_debug(hdlc, hdlc_debug, "HDLC"); g_at_hdlc_set_xmit_accm(hdlc, 0); g_at_hdlc_set_recv_accm(hdlc, 0); g_at_hdlc_set_receive(hdlc, hdlc_receive, NULL); send_command(hdlc, 0x00); /* Version info */ send_command(hdlc, 0x51); /* Features query */ send_subsys_command(hdlc, 250, 7); /* Novatel modem status */ g_main_loop_run(event_loop); g_at_hdlc_unref(hdlc); g_main_loop_unref(event_loop); g_free(option_device); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/ppp.h0000644000015600001650000001115712671500024020767 0ustar pbuserpbgroup00000000000000/* * * PPP library with GLib integration * * Copyright (C) 2009-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 * */ #include "ppp_cp.h" #define LCP_PROTOCOL 0xc021 #define PAP_PROTOCOL 0xc023 #define CHAP_PROTOCOL 0xc223 #define IPCP_PROTO 0x8021 #define IPV6CP_PROTO 0x8057 #define PPP_IP_PROTO 0x0021 #define PPP_IPV6_PROTO 0x0057 #define MD5 5 #define DBG(p, fmt, arg...) do { \ char *str = g_strdup_printf("%s:%s() " fmt, __FILE__, \ __FUNCTION__ , ## arg); \ ppp_debug(p, str); \ g_free(str); \ } while (0) struct ppp_chap; struct ppp_net; struct ppp_pap; struct ppp_header { guint8 address; guint8 control; guint16 proto; guint8 info[0]; } __attribute__((packed)); struct packed_short { guint16 s; } __attribute__((packed)); struct packed_long { guint32 l; } __attribute__((packed)); static inline guint32 __get_unaligned_long(const void *p) { const struct packed_long *ptr = p; return ptr->l; } static inline guint16 __get_unaligned_short(const void *p) { const struct packed_short *ptr = p; return ptr->s; } static inline void __put_unaligned_short(void *p, guint16 val) { struct packed_short *ptr = p; ptr->s = val; } #define get_host_long(p) \ (ntohl(__get_unaligned_long(p))) #define get_host_short(p) \ (ntohs(__get_unaligned_short(p))) #define put_network_short(p, val) \ (__put_unaligned_short(p, htons(val))) #define ppp_proto(packet) \ (get_host_short(packet + 2)) /* LCP related functions */ struct pppcp_data *lcp_new(GAtPPP *ppp, gboolean dormant); void lcp_free(struct pppcp_data *lcp); void lcp_protocol_reject(struct pppcp_data *lcp, guint8 *packet, gsize len); void lcp_set_acfc_enabled(struct pppcp_data *pppcp, gboolean enabled); void lcp_set_pfc_enabled(struct pppcp_data *pppcp, gboolean enabled); /* IPCP related functions */ struct pppcp_data *ipcp_new(GAtPPP *ppp, gboolean is_server, guint32 ip); void ipcp_free(struct pppcp_data *data); void ipcp_set_server_info(struct pppcp_data *ipcp, guint32 peer_addr, guint32 dns1, guint32 dns2); /* IPv6 CP related functions */ struct pppcp_data *ipv6cp_new(GAtPPP *ppp, gboolean is_server, const char *local, const char *peer, GError **error); void ipv6cp_free(struct pppcp_data *data); /* CHAP related functions */ struct ppp_chap *ppp_chap_new(GAtPPP *ppp, guint8 method); void ppp_chap_free(struct ppp_chap *chap); void ppp_chap_process_packet(struct ppp_chap *chap, const guint8 *new_packet, gsize len); /* PAP related functions */ struct ppp_pap *ppp_pap_new(GAtPPP *ppp); void ppp_pap_free(struct ppp_pap *pap); gboolean ppp_pap_start(struct ppp_pap *pap); void ppp_pap_process_packet(struct ppp_pap *pap, const guint8 *new_packet, gsize len); /* TUN / Network related functions */ struct ppp_net *ppp_net_new(GAtPPP *ppp, int fd); const char *ppp_net_get_interface(struct ppp_net *net); void ppp_net_process_packet(struct ppp_net *net, const guint8 *packet, gsize len); void ppp_net_free(struct ppp_net *net); gboolean ppp_net_set_mtu(struct ppp_net *net, guint16 mtu); void ppp_net_suspend_interface(struct ppp_net *net); void ppp_net_resume_interface(struct ppp_net *net); /* PPP functions related to main GAtPPP object */ void ppp_debug(GAtPPP *ppp, const char *str); void ppp_transmit(GAtPPP *ppp, guint8 *packet, guint infolen); void ppp_set_auth(GAtPPP *ppp, const guint8 *auth_data); void ppp_auth_notify(GAtPPP *ppp, gboolean success); void ppp_ipcp_up_notify(GAtPPP *ppp, const char *local, const char *peer, const char *dns1, const char *dns2); void ppp_ipcp_down_notify(GAtPPP *ppp); void ppp_ipcp_finished_notify(GAtPPP *ppp); void ppp_lcp_up_notify(GAtPPP *ppp); void ppp_lcp_down_notify(GAtPPP *ppp); void ppp_lcp_finished_notify(GAtPPP *ppp); void ppp_set_recv_accm(GAtPPP *ppp, guint32 accm); void ppp_set_xmit_accm(GAtPPP *ppp, guint32 accm); void ppp_set_mtu(GAtPPP *ppp, const guint8 *data); void ppp_set_xmit_acfc(GAtPPP *ppp, gboolean acfc); void ppp_set_xmit_pfc(GAtPPP *ppp, gboolean pfc); struct ppp_header *ppp_packet_new(gsize infolen, guint16 protocol); ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatrawip.c0000644000015600001650000001170612671500024022001 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include #include #include #include #include #include #include #include "ringbuffer.h" #include "gatrawip.h" struct _GAtRawIP { gint ref_count; GAtIO *io; GAtIO *tun_io; char *ifname; struct ring_buffer *write_buffer; struct ring_buffer *tun_write_buffer; GAtDebugFunc debugf; gpointer debug_data; }; GAtRawIP *g_at_rawip_new(GIOChannel *channel) { GAtRawIP *rawip; GAtIO *io; io = g_at_io_new(channel); if (io == NULL) return NULL; rawip = g_at_rawip_new_from_io(io); g_at_io_unref(io); return rawip; } GAtRawIP *g_at_rawip_new_from_io(GAtIO *io) { GAtRawIP *rawip; rawip = g_try_new0(GAtRawIP, 1); if (rawip == NULL) return NULL; rawip->ref_count = 1; rawip->write_buffer = NULL; rawip->tun_write_buffer = NULL; rawip->io = g_at_io_ref(io); return rawip; } GAtRawIP *g_at_rawip_ref(GAtRawIP *rawip) { if (rawip == NULL) return NULL; g_atomic_int_inc(&rawip->ref_count); return rawip; } void g_at_rawip_unref(GAtRawIP *rawip) { if (rawip == NULL) return; if (g_atomic_int_dec_and_test(&rawip->ref_count) == FALSE) return; g_at_rawip_shutdown(rawip); g_at_io_unref(rawip->io); rawip->io = NULL; g_free(rawip->ifname); rawip->ifname = NULL; g_free(rawip); } static gboolean can_write_data(gpointer data) { GAtRawIP *rawip = data; unsigned int len; unsigned char *buf; gsize bytes_written; if (rawip->write_buffer == NULL) return FALSE; len = ring_buffer_len_no_wrap(rawip->write_buffer); buf = ring_buffer_read_ptr(rawip->write_buffer, 0); bytes_written = g_at_io_write(rawip->io, (gchar *) buf, len); ring_buffer_drain(rawip->write_buffer, bytes_written); if (ring_buffer_len(rawip->write_buffer) > 0) return TRUE; rawip->write_buffer = NULL; return FALSE; } static gboolean tun_write_data(gpointer data) { GAtRawIP *rawip = data; unsigned int len; unsigned char *buf; gsize bytes_written; if (rawip->tun_write_buffer == NULL) return FALSE; len = ring_buffer_len_no_wrap(rawip->tun_write_buffer); buf = ring_buffer_read_ptr(rawip->tun_write_buffer, 0); bytes_written = g_at_io_write(rawip->tun_io, (gchar *) buf, len); ring_buffer_drain(rawip->tun_write_buffer, bytes_written); if (ring_buffer_len(rawip->tun_write_buffer) > 0) return TRUE; rawip->tun_write_buffer = NULL; return FALSE; } static void new_bytes(struct ring_buffer *rbuf, gpointer user_data) { GAtRawIP *rawip = user_data; rawip->tun_write_buffer = rbuf; g_at_io_set_write_handler(rawip->tun_io, tun_write_data, rawip); } static void tun_bytes(struct ring_buffer *rbuf, gpointer user_data) { GAtRawIP *rawip = user_data; rawip->write_buffer = rbuf; g_at_io_set_write_handler(rawip->io, can_write_data, rawip); } static void create_tun(GAtRawIP *rawip) { GIOChannel *channel; struct ifreq ifr; int fd, err; fd = open("/dev/net/tun", O_RDWR); if (fd < 0) return; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; strcpy(ifr.ifr_name, "gprs%d"); err = ioctl(fd, TUNSETIFF, (void *) &ifr); if (err < 0) { close(fd); return; } rawip->ifname = g_strdup(ifr.ifr_name); channel = g_io_channel_unix_new(fd); if (channel == NULL) { close(fd); return; } rawip->tun_io = g_at_io_new(channel); g_io_channel_unref(channel); } void g_at_rawip_open(GAtRawIP *rawip) { if (rawip == NULL) return; create_tun(rawip); if (rawip->tun_io == NULL) return; g_at_io_set_read_handler(rawip->io, new_bytes, rawip); g_at_io_set_read_handler(rawip->tun_io, tun_bytes, rawip); } void g_at_rawip_shutdown(GAtRawIP *rawip) { if (rawip == NULL) return; if (rawip->tun_io == NULL) return; g_at_io_set_read_handler(rawip->io, NULL, NULL); g_at_io_set_read_handler(rawip->tun_io, NULL, NULL); rawip->write_buffer = NULL; rawip->tun_write_buffer = NULL; g_at_io_unref(rawip->tun_io); rawip->tun_io = NULL; } const char *g_at_rawip_get_interface(GAtRawIP *rawip) { if (rawip == NULL) return NULL; return rawip->ifname; } void g_at_rawip_set_debug(GAtRawIP *rawip, GAtDebugFunc func, gpointer user_data) { if (rawip == NULL) return; rawip->debugf = func; rawip->debug_data = user_data; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/ringbuffer.c0000644000015600001650000001050012671500024022303 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include #include #include "ringbuffer.h" #define MAX_SIZE 262144 struct ring_buffer { unsigned char *buffer; unsigned int size; unsigned int mask; unsigned int in; unsigned int out; }; struct ring_buffer *ring_buffer_new(unsigned int size) { unsigned int real_size = 1; struct ring_buffer *buffer; /* Find the next power of two for size */ while (real_size < size && real_size < MAX_SIZE) real_size = real_size << 1; if (real_size > MAX_SIZE) return NULL; buffer = g_slice_new(struct ring_buffer); if (buffer == NULL) return NULL; buffer->buffer = g_slice_alloc(real_size); if (buffer->buffer == NULL) { g_free(buffer); return NULL; } buffer->size = real_size; buffer->mask = real_size - 1; buffer->in = 0; buffer->out = 0; return buffer; } int ring_buffer_write(struct ring_buffer *buf, const void *data, unsigned int len) { unsigned int end; unsigned int offset; const unsigned char *d = data; /* Needed to satisfy non-gcc compilers */ /* Determine how much we can actually write */ len = MIN(len, buf->size - buf->in + buf->out); /* Determine how much to write before wrapping */ offset = buf->in & buf->mask; end = MIN(len, buf->size - offset); memcpy(buf->buffer+offset, d, end); /* Now put the remainder on the beginning of the buffer */ memcpy(buf->buffer, d + end, len - end); buf->in += len; return len; } unsigned char *ring_buffer_write_ptr(struct ring_buffer *buf, unsigned int offset) { return buf->buffer + ((buf->in + offset) & buf->mask); } int ring_buffer_avail_no_wrap(struct ring_buffer *buf) { unsigned int offset = buf->in & buf->mask; unsigned int len = buf->size - buf->in + buf->out; return MIN(len, buf->size - offset); } int ring_buffer_write_advance(struct ring_buffer *buf, unsigned int len) { len = MIN(len, buf->size - buf->in + buf->out); buf->in += len; return len; } int ring_buffer_read(struct ring_buffer *buf, void *data, unsigned int len) { unsigned int end; unsigned int offset; unsigned char *d = data; len = MIN(len, buf->in - buf->out); /* Grab data from buffer starting at offset until the end */ offset = buf->out & buf->mask; end = MIN(len, buf->size - offset); memcpy(d, buf->buffer + offset, end); /* Now grab remainder from the beginning */ memcpy(d + end, buf->buffer, len - end); buf->out += len; if (buf->out == buf->in) buf->out = buf->in = 0; return len; } int ring_buffer_drain(struct ring_buffer *buf, unsigned int len) { len = MIN(len, buf->in - buf->out); buf->out += len; if (buf->out == buf->in) buf->out = buf->in = 0; return len; } int ring_buffer_len_no_wrap(struct ring_buffer *buf) { unsigned int offset = buf->out & buf->mask; unsigned int len = buf->in - buf->out; return MIN(len, buf->size - offset); } unsigned char *ring_buffer_read_ptr(struct ring_buffer *buf, unsigned int offset) { return buf->buffer + ((buf->out + offset) & buf->mask); } int ring_buffer_len(struct ring_buffer *buf) { if (buf == NULL) return -1; return buf->in - buf->out; } void ring_buffer_reset(struct ring_buffer *buf) { if (buf == NULL) return; buf->in = 0; buf->out = 0; } int ring_buffer_avail(struct ring_buffer *buf) { if (buf == NULL) return -1; return buf->size - buf->in + buf->out; } int ring_buffer_capacity(struct ring_buffer *buf) { if (buf == NULL) return -1; return buf->size; } void ring_buffer_free(struct ring_buffer *buf) { if (buf == NULL) return; g_slice_free1(buf->size, buf->buffer); g_slice_free1(sizeof(struct ring_buffer), buf); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/ppp_cp.h0000644000015600001650000000770412671500024021454 0ustar pbuserpbgroup00000000000000/* * * PPP library with GLib integration * * Copyright (C) 2009-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 * */ struct pppcp_data; struct ppp_option_iter; /* option format */ struct ppp_option { guint8 type; guint8 length; guint8 data[0]; }; enum rcr_result { RCR_ACCEPT, RCR_REJECT, RCR_NAK, }; enum pppcp_code { PPPCP_CODE_TYPE_CONFIGURE_REQUEST = 1, PPPCP_CODE_TYPE_CONFIGURE_ACK, PPPCP_CODE_TYPE_CONFIGURE_NAK, PPPCP_CODE_TYPE_CONFIGURE_REJECT, PPPCP_CODE_TYPE_TERMINATE_REQUEST, PPPCP_CODE_TYPE_TERMINATE_ACK, PPPCP_CODE_TYPE_CODE_REJECT, PPPCP_CODE_TYPE_PROTOCOL_REJECT, PPPCP_CODE_TYPE_ECHO_REQUEST, PPPCP_CODE_TYPE_ECHO_REPLY, PPPCP_CODE_TYPE_DISCARD_REQUEST }; struct pppcp_packet { guint8 code; guint8 identifier; guint16 length; guint8 data[0]; } __attribute__((packed)); struct ppp_option_iter { guint16 max; guint16 pos; const guint8 *pdata; guint8 type; guint8 len; const guint8 *option_data; }; struct pppcp_proto { guint16 proto; const char *name; guint16 supported_codes; void (*this_layer_up)(struct pppcp_data *data); void (*this_layer_down)(struct pppcp_data *data); void (*this_layer_started)(struct pppcp_data *data); void (*this_layer_finished)(struct pppcp_data *data); /* Remote side acked these options, we can now use them */ void (*rca)(struct pppcp_data *pppcp, const struct pppcp_packet *pkt); /* * Remote side sent us an Conf-Req-Nak or Conf-Req-Rej. The protocol * driver should examine the packet and update its options accordingly, * then use set_local_options to set a new set of options to try * before returning */ void (*rcn_nak)(struct pppcp_data *pppcp, const struct pppcp_packet *pkt); void (*rcn_rej)(struct pppcp_data *pppcp, const struct pppcp_packet *pkt); /* * Remote side has sent us a request with its options, return whether * we should ack / nak / rej these options. In the case of nak / rej, * the list of options to be sent to the peer is given in the * new_options & new_len out arguments */ enum rcr_result (*rcr)(struct pppcp_data *pppcp, const struct pppcp_packet *pkt, guint8 **new_options, guint16 *new_len); }; void ppp_option_iter_init(struct ppp_option_iter *iter, const struct pppcp_packet *packet); gboolean ppp_option_iter_next(struct ppp_option_iter *iter); guint8 ppp_option_iter_get_type(struct ppp_option_iter *iter); guint8 ppp_option_iter_get_length(struct ppp_option_iter *iter); const guint8 *ppp_option_iter_get_data(struct ppp_option_iter *iter); struct pppcp_data *pppcp_new(GAtPPP *ppp, const struct pppcp_proto *proto, gboolean dormant, guint max_failure); void pppcp_free(struct pppcp_data *data); void pppcp_set_data(struct pppcp_data *pppcp, gpointer data); gpointer pppcp_get_data(struct pppcp_data *pppcp); GAtPPP *pppcp_get_ppp(struct pppcp_data *pppcp); guint8 pppcp_get_code(const guint8 *data); void pppcp_set_local_options(struct pppcp_data *data, const guint8 *options, guint16 len); void pppcp_process_packet(gpointer priv, const guint8 *new_packet, gsize len); void pppcp_send_protocol_reject(struct pppcp_data *data, const guint8 *rejected_packet, gsize len); void pppcp_signal_open(struct pppcp_data *data); void pppcp_signal_close(struct pppcp_data *data); void pppcp_signal_up(struct pppcp_data *data); void pppcp_signal_down(struct pppcp_data *data); ofono-1.17.bzr6912+16.04.20160314.3/gatchat/ppp_ipcp.c0000644000015600001650000002747612671500024022010 0ustar pbuserpbgroup00000000000000/* * * PPP library with GLib integration * * Copyright (C) 2009-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 #endif #include #include #include #include #include #include #include #include "gatutil.h" #include "gatppp.h" #include "ppp.h" #define IPCP_SUPPORTED_CODES ((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_NAK) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_REJECT) | \ (1 << PPPCP_CODE_TYPE_TERMINATE_REQUEST) | \ (1 << PPPCP_CODE_TYPE_TERMINATE_ACK) | \ (1 << PPPCP_CODE_TYPE_CODE_REJECT)) enum ipcp_option_types { IP_ADDRESSES = 1, IP_COMPRESSION_PROTO = 2, IP_ADDRESS = 3, MOBILE_IPV4 = 4, PRIMARY_DNS_SERVER = 129, PRIMARY_NBNS_SERVER = 130, SECONDARY_DNS_SERVER = 131, SECONDARY_NBNS_SERVER = 132, }; /* We request IP_ADDRESS, PRIMARY/SECONDARY DNS & NBNS */ #define MAX_CONFIG_OPTION_SIZE 5*6 #define REQ_OPTION_IPADDR 0x01 #define REQ_OPTION_DNS1 0x02 #define REQ_OPTION_DNS2 0x04 #define REQ_OPTION_NBNS1 0x08 #define REQ_OPTION_NBNS2 0x10 #define MAX_IPCP_FAILURE 100 struct ipcp_data { guint8 options[MAX_CONFIG_OPTION_SIZE]; guint16 options_len; guint8 req_options; guint32 local_addr; guint32 peer_addr; guint32 dns1; guint32 dns2; guint32 nbns1; guint32 nbns2; gboolean is_server; }; #define FILL_IP(options, req, type, var) \ if (req) { \ options[len] = type; \ options[len + 1] = 6; \ memcpy(options + len + 2, var, 4); \ \ len += 6; \ } \ static void ipcp_generate_config_options(struct ipcp_data *ipcp) { guint16 len = 0; FILL_IP(ipcp->options, ipcp->req_options & REQ_OPTION_IPADDR, IP_ADDRESS, &ipcp->local_addr); FILL_IP(ipcp->options, ipcp->req_options & REQ_OPTION_DNS1, PRIMARY_DNS_SERVER, &ipcp->dns1); FILL_IP(ipcp->options, ipcp->req_options & REQ_OPTION_DNS2, SECONDARY_DNS_SERVER, &ipcp->dns2); FILL_IP(ipcp->options, ipcp->req_options & REQ_OPTION_NBNS1, PRIMARY_NBNS_SERVER, &ipcp->nbns1); FILL_IP(ipcp->options, ipcp->req_options & REQ_OPTION_NBNS2, SECONDARY_NBNS_SERVER, &ipcp->nbns2); ipcp->options_len = len; } static void ipcp_reset_client_config_options(struct ipcp_data *ipcp) { ipcp->req_options = REQ_OPTION_IPADDR | REQ_OPTION_DNS1 | REQ_OPTION_DNS2 | REQ_OPTION_NBNS1 | REQ_OPTION_NBNS2; ipcp->local_addr = 0; ipcp->peer_addr = 0; ipcp->dns1 = 0; ipcp->dns2 = 0; ipcp->nbns1 = 0; ipcp->nbns2 = 0; ipcp_generate_config_options(ipcp); } static void ipcp_reset_server_config_options(struct ipcp_data *ipcp) { if (ipcp->local_addr != 0) ipcp->req_options = REQ_OPTION_IPADDR; else ipcp->req_options = 0; ipcp_generate_config_options(ipcp); } void ipcp_set_server_info(struct pppcp_data *pppcp, guint32 peer_addr, guint32 dns1, guint32 dns2) { struct ipcp_data *ipcp = pppcp_get_data(pppcp); ipcp->peer_addr = peer_addr; ipcp->dns1 = dns1; ipcp->dns2 = dns2; } static void ipcp_up(struct pppcp_data *pppcp) { struct ipcp_data *ipcp = pppcp_get_data(pppcp); char local[INET_ADDRSTRLEN]; char peer[INET_ADDRSTRLEN]; char dns1[INET_ADDRSTRLEN]; char dns2[INET_ADDRSTRLEN]; struct in_addr addr; memset(local, 0, sizeof(local)); addr.s_addr = ipcp->local_addr; inet_ntop(AF_INET, &addr, local, INET_ADDRSTRLEN); memset(peer, 0, sizeof(peer)); addr.s_addr = ipcp->peer_addr; inet_ntop(AF_INET, &addr, peer, INET_ADDRSTRLEN); memset(dns1, 0, sizeof(dns1)); addr.s_addr = ipcp->dns1; inet_ntop(AF_INET, &addr, dns1, INET_ADDRSTRLEN); memset(dns2, 0, sizeof(dns2)); addr.s_addr = ipcp->dns2; inet_ntop(AF_INET, &addr, dns2, INET_ADDRSTRLEN); ppp_ipcp_up_notify(pppcp_get_ppp(pppcp), local[0] ? local : NULL, peer[0] ? peer : NULL, dns1[0] ? dns1 : NULL, dns2[0] ? dns2 : NULL); } static void ipcp_down(struct pppcp_data *pppcp) { struct ipcp_data *ipcp = pppcp_get_data(pppcp); if (ipcp->is_server) ipcp_reset_server_config_options(ipcp); else ipcp_reset_client_config_options(ipcp); pppcp_set_local_options(pppcp, ipcp->options, ipcp->options_len); ppp_ipcp_down_notify(pppcp_get_ppp(pppcp)); } static void ipcp_finished(struct pppcp_data *pppcp) { ppp_ipcp_finished_notify(pppcp_get_ppp(pppcp)); } static void ipcp_rca(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { struct ipcp_data *ipcp = pppcp_get_data(pppcp); struct ppp_option_iter iter; if (ipcp->is_server) return; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { const guint8 *data = ppp_option_iter_get_data(&iter); switch (ppp_option_iter_get_type(&iter)) { case IP_ADDRESS: memcpy(&ipcp->local_addr, data, 4); break; case PRIMARY_DNS_SERVER: memcpy(&ipcp->dns1, data, 4); break; case PRIMARY_NBNS_SERVER: memcpy(&ipcp->nbns1, data, 4); break; case SECONDARY_DNS_SERVER: memcpy(&ipcp->dns2, data, 4); break; case SECONDARY_NBNS_SERVER: memcpy(&ipcp->nbns2, data, 4); break; default: break; } } } static void ipcp_rcn_nak(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { struct ipcp_data *ipcp = pppcp_get_data(pppcp); struct ppp_option_iter iter; if (ipcp->is_server) return; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { const guint8 *data = ppp_option_iter_get_data(&iter); switch (ppp_option_iter_get_type(&iter)) { case IP_ADDRESS: ipcp->req_options |= REQ_OPTION_IPADDR; memcpy(&ipcp->local_addr, data, 4); break; case PRIMARY_DNS_SERVER: ipcp->req_options |= REQ_OPTION_DNS1; memcpy(&ipcp->dns1, data, 4); break; case PRIMARY_NBNS_SERVER: ipcp->req_options |= REQ_OPTION_NBNS1; memcpy(&ipcp->nbns1, data, 4); break; case SECONDARY_DNS_SERVER: ipcp->req_options |= REQ_OPTION_DNS2; memcpy(&ipcp->dns2, data, 4); break; case SECONDARY_NBNS_SERVER: ipcp->req_options |= REQ_OPTION_NBNS2; memcpy(&ipcp->nbns2, data, 4); break; default: break; } } ipcp_generate_config_options(ipcp); pppcp_set_local_options(pppcp, ipcp->options, ipcp->options_len); } static void ipcp_rcn_rej(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { struct ipcp_data *ipcp = pppcp_get_data(pppcp); struct ppp_option_iter iter; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { switch (ppp_option_iter_get_type(&iter)) { case IP_ADDRESS: ipcp->req_options &= ~REQ_OPTION_IPADDR; break; case PRIMARY_DNS_SERVER: ipcp->req_options &= ~REQ_OPTION_DNS1; break; case PRIMARY_NBNS_SERVER: ipcp->req_options &= ~REQ_OPTION_NBNS1; break; case SECONDARY_DNS_SERVER: ipcp->req_options &= ~REQ_OPTION_DNS2; break; case SECONDARY_NBNS_SERVER: ipcp->req_options &= ~REQ_OPTION_NBNS2; break; default: break; } } ipcp_generate_config_options(ipcp); pppcp_set_local_options(pppcp, ipcp->options, ipcp->options_len); } static enum rcr_result ipcp_server_rcr(struct ipcp_data *ipcp, const struct pppcp_packet *packet, guint8 **new_options, guint16 *new_len) { struct ppp_option_iter iter; guint8 nak_options[MAX_CONFIG_OPTION_SIZE]; guint16 len = 0; guint8 *rej_options = NULL; guint16 rej_len = 0; guint32 addr; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { const guint8 *data = ppp_option_iter_get_data(&iter); guint8 type = ppp_option_iter_get_type(&iter); switch (type) { case IP_ADDRESS: memcpy(&addr, data, 4); FILL_IP(nak_options, addr != ipcp->peer_addr || addr == 0, type, &ipcp->peer_addr); break; case PRIMARY_DNS_SERVER: memcpy(&addr, data, 4); FILL_IP(nak_options, addr != ipcp->dns1 || addr == 0, type, &ipcp->dns1); break; case SECONDARY_DNS_SERVER: memcpy(&addr, data, 4); FILL_IP(nak_options, addr != ipcp->dns2 || addr == 0, type, &ipcp->dns2); break; default: /* Reject */ if (rej_options == NULL) { guint16 max_len = ntohs(packet->length) - 4; rej_options = g_new0(guint8, max_len); } if (rej_options != NULL) { guint8 opt_len = ppp_option_iter_get_length(&iter); rej_options[rej_len] = type; rej_options[rej_len + 1] = opt_len + 2; memcpy(rej_options + rej_len + 2, data, opt_len); rej_len += opt_len + 2; } break; } } if (rej_len > 0) { *new_len = rej_len; *new_options = rej_options; return RCR_REJECT; } if (len > 0) { *new_len = len; *new_options = g_memdup(nak_options, len); return RCR_NAK; } return RCR_ACCEPT; } static enum rcr_result ipcp_client_rcr(struct ipcp_data *ipcp, const struct pppcp_packet *packet, guint8 **new_options, guint16 *new_len) { guint8 *options = NULL; struct ppp_option_iter iter; guint8 len = 0; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { const guint8 *data = ppp_option_iter_get_data(&iter); guint8 type = ppp_option_iter_get_type(&iter); switch (type) { case IP_ADDRESS: memcpy(&ipcp->peer_addr, data, 4); if (ipcp->peer_addr != 0) break; /* * Fall through, reject IP_ADDRESS if peer sends * us 0 (expecting us to provide its IP address) */ default: if (options == NULL) { guint16 max_len = ntohs(packet->length) - 4; options = g_new0(guint8, max_len); } if (options != NULL) { guint8 opt_len = ppp_option_iter_get_length(&iter); options[len] = type; options[len + 1] = opt_len + 2; memcpy(options + len + 2, data, opt_len); len += opt_len + 2; } break; } } if (len > 0) { *new_len = len; *new_options = options; return RCR_REJECT; } return RCR_ACCEPT; } static enum rcr_result ipcp_rcr(struct pppcp_data *pppcp, const struct pppcp_packet *packet, guint8 **new_options, guint16 *new_len) { struct ipcp_data *ipcp = pppcp_get_data(pppcp); if (ipcp->is_server) return ipcp_server_rcr(ipcp, packet, new_options, new_len); else return ipcp_client_rcr(ipcp, packet, new_options, new_len); } struct pppcp_proto ipcp_proto = { .proto = IPCP_PROTO, .name = "ipcp", .supported_codes = IPCP_SUPPORTED_CODES, .this_layer_up = ipcp_up, .this_layer_down = ipcp_down, .this_layer_finished = ipcp_finished, .rca = ipcp_rca, .rcn_nak = ipcp_rcn_nak, .rcn_rej = ipcp_rcn_rej, .rcr = ipcp_rcr, }; struct pppcp_data *ipcp_new(GAtPPP *ppp, gboolean is_server, guint32 ip) { struct ipcp_data *ipcp; struct pppcp_data *pppcp; ipcp = g_try_new0(struct ipcp_data, 1); if (ipcp == NULL) return NULL; /* * Some 3G modems use repeated IPCP NAKs as the way of stalling * util sending us the client IP address. So we increase the * default number of NAKs we accept before start treating them * as rejects. */ pppcp = pppcp_new(ppp, &ipcp_proto, FALSE, MAX_IPCP_FAILURE); if (pppcp == NULL) { g_free(ipcp); return NULL; } pppcp_set_data(pppcp, ipcp); ipcp->is_server = is_server; if (is_server) { ipcp->local_addr = ip; ipcp_reset_server_config_options(ipcp); } else ipcp_reset_client_config_options(ipcp); pppcp_set_local_options(pppcp, ipcp->options, ipcp->options_len); return pppcp; } void ipcp_free(struct pppcp_data *data) { struct ipcp_data *ipcp = pppcp_get_data(data); g_free(ipcp); pppcp_free(data); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gsm0710.h0000644000015600001650000000316012671500024021261 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * Copyright (C) 2009 Trolltech ASA. * * 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 * */ #ifndef __GSM0710_H #define __GSM0710_H #ifdef __cplusplus extern "C" { #endif /* Frame types and subtypes */ #define GSM0710_OPEN_CHANNEL 0x3F #define GSM0710_CLOSE_CHANNEL 0x53 #define GSM0710_DATA 0xEF #define GSM0710_DATA_ALT 0x03 #define GSM0710_STATUS_SET 0xE3 #define GSM0710_STATUS_ACK 0xE1 int gsm0710_basic_extract_frame(guint8 *data, int len, guint8 *out_dlc, guint8 *out_type, guint8 **frame, int *out_len); int gsm0710_basic_fill_frame(guint8 *frame, guint8 dlc, guint8 type, const guint8 *data, int len); int gsm0710_advanced_extract_frame(guint8 *data, int len, guint8 *out_dlc, guint8 *out_type, guint8 **frame, int *out_len); int gsm0710_advanced_fill_frame(guint8 *frame, guint8 dlc, guint8 type, const guint8 *data, int len); #ifdef __cplusplus }; #endif #endif ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatchat.h0000644000015600001650000001411412671500024021577 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 * */ #ifndef __GATCHAT_H #define __GATCHAT_H #ifdef __cplusplus extern "C" { #endif #include "gatresult.h" #include "gatsyntax.h" #include "gatutil.h" #include "gatio.h" struct _GAtChat; typedef struct _GAtChat GAtChat; typedef void (*GAtResultFunc)(gboolean success, GAtResult *result, gpointer user_data); typedef void (*GAtNotifyFunc)(GAtResult *result, gpointer user_data); enum _GAtChatTerminator { G_AT_CHAT_TERMINATOR_OK, G_AT_CHAT_TERMINATOR_ERROR, G_AT_CHAT_TERMINATOR_NO_DIALTONE, G_AT_CHAT_TERMINATOR_BUSY, G_AT_CHAT_TERMINATOR_NO_CARRIER, G_AT_CHAT_TERMINATOR_CONNECT, G_AT_CHAT_TERMINATOR_NO_ANSWER, G_AT_CHAT_TERMINATOR_CMS_ERROR, G_AT_CHAT_TERMINATOR_CME_ERROR, G_AT_CHAT_TERMINATOR_EXT_ERROR, }; typedef enum _GAtChatTerminator GAtChatTerminator; GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax); GAtChat *g_at_chat_new_blocking(GIOChannel *channel, GAtSyntax *syntax); GIOChannel *g_at_chat_get_channel(GAtChat *chat); GAtIO *g_at_chat_get_io(GAtChat *chat); GAtChat *g_at_chat_ref(GAtChat *chat); void g_at_chat_unref(GAtChat *chat); GAtChat *g_at_chat_clone(GAtChat *chat); GAtChat *g_at_chat_set_slave(GAtChat *chat, GAtChat *slave); GAtChat *g_at_chat_get_slave(GAtChat *chat); void g_at_chat_suspend(GAtChat *chat); void g_at_chat_resume(GAtChat *chat); gboolean g_at_chat_set_disconnect_function(GAtChat *chat, GAtDisconnectFunc disconnect, gpointer user_data); /*! * If the function is not NULL, then on every read/write from the GIOChannel * provided to GAtChat the logging function will be called with the * input/output string and user data */ gboolean g_at_chat_set_debug(GAtChat *chat, GAtDebugFunc func, gpointer user_data); /*! * Queue an AT command for execution. The command contents are given * in cmd. Once the command executes, the callback function given by * func is called with user provided data in user_data. * * Returns an id of the queued command which can be canceled using * g_at_chat_cancel. If an error occurred, an id of 0 is returned. * * This function can be used in three ways: * - Send a simple command such as g_at_chat_send(p, "AT+CGMI?", ... * * - Send a compound command: g_at_chat_send(p, "AT+CMD1;+CMD2", ... * * - Send a command requiring a prompt. The command up to '\r' is sent * after which time a '> ' prompt is expected from the modem. Further * contents of the command are sent until a '\r' or end of string is * encountered. If end of string is encountered, the Ctrl-Z character * is sent automatically. There is no need to include the Ctrl-Z * by the caller. * * The valid_resp field can be used to send an array of strings which will * be accepted as a valid response for this command. This is treated as a * simple prefix match. If a response line comes in from the modem and it * does not match any of the prefixes in valid_resp, it is treated as an * unsolicited notification. If valid_resp is NULL, then all response * lines after command submission and final response line are treated as * part of the command response. This can be used to get around broken * modems which send unsolicited notifications during command processing. */ guint g_at_chat_send(GAtChat *chat, const char *cmd, const char **valid_resp, GAtResultFunc func, gpointer user_data, GDestroyNotify notify); /*! * Same as the above command, except that the caller wishes to receive the * intermediate responses immediately through the GAtNotifyFunc callback. * The final response will still be sent to GAtResultFunc callback. The * final GAtResult will not contain any lines from the intermediate responses. * This is useful for listing commands such as CPBR. */ guint g_at_chat_send_listing(GAtChat *chat, const char *cmd, const char **valid_resp, GAtNotifyFunc listing, GAtResultFunc func, gpointer user_data, GDestroyNotify notify); /*! * Same as g_at_chat_send_listing except every response line in valid_resp * is expected to be followed by a PDU. The listing function will be called * with the intermediate response and the following PDU line. * * This is useful for PDU listing commands like the +CMGL */ guint g_at_chat_send_pdu_listing(GAtChat *chat, const char *cmd, const char **valid_resp, GAtNotifyFunc listing, GAtResultFunc func, gpointer user_data, GDestroyNotify notify); /*! * Same as g_at_chat_send except parser will know to expect short prompt syntax * used with +CPOS. */ guint g_at_chat_send_and_expect_short_prompt(GAtChat *chat, const char *cmd, const char **valid_resp, GAtResultFunc func, gpointer user_data, GDestroyNotify notify); gboolean g_at_chat_cancel(GAtChat *chat, guint id); gboolean g_at_chat_cancel_all(GAtChat *chat); gpointer g_at_chat_get_userdata(GAtChat *chat, guint id); guint g_at_chat_register(GAtChat *chat, const char *prefix, GAtNotifyFunc func, gboolean expect_pdu, gpointer user_data, GDestroyNotify notify); gboolean g_at_chat_unregister(GAtChat *chat, guint id); gboolean g_at_chat_unregister_all(GAtChat *chat); gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd, guint timeout, guint msec); void g_at_chat_add_terminator(GAtChat *chat, char *terminator, int len, gboolean success); void g_at_chat_blacklist_terminator(GAtChat *chat, GAtChatTerminator terminator); #ifdef __cplusplus } #endif #endif /* __GATCHAT_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatio.c0000644000015600001650000002033412671500024021263 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include #include #include #include #include #include #include #include "ringbuffer.h" #include "gatio.h" #include "gatutil.h" struct _GAtIO { gint ref_count; /* Ref count */ guint read_watch; /* GSource read id, 0 if no */ guint write_watch; /* GSource write id, 0 if no */ GIOChannel *channel; /* comms channel */ GAtDisconnectFunc user_disconnect; /* user disconnect func */ gpointer user_disconnect_data; /* user disconnect data */ struct ring_buffer *buf; /* Current read buffer */ guint max_read_attempts; /* max reads / select */ GAtIOReadFunc read_handler; /* Read callback */ gpointer read_data; /* Read callback userdata */ gboolean use_write_watch; /* Use write select */ GAtIOWriteFunc write_handler; /* Write callback */ gpointer write_data; /* Write callback userdata */ GAtDebugFunc debugf; /* debugging output function */ gpointer debug_data; /* Data to pass to debug func */ GAtDisconnectFunc write_done_func; /* tx empty notifier */ gpointer write_done_data; /* tx empty data */ gboolean destroyed; /* Re-entrancy guard */ }; static void read_watcher_destroy_notify(gpointer user_data) { GAtIO *io = user_data; ring_buffer_free(io->buf); io->buf = NULL; io->debugf = NULL; io->debug_data = NULL; io->read_watch = 0; io->read_handler = NULL; io->read_data = NULL; io->channel = NULL; if (io->destroyed) g_free(io); else if (io->user_disconnect) io->user_disconnect(io->user_disconnect_data); } static gboolean received_data(GIOChannel *channel, GIOCondition cond, gpointer data) { unsigned char *buf; GAtIO *io = data; GIOStatus status; gsize rbytes; gsize toread; gsize total_read = 0; guint read_count = 0; if (cond & G_IO_NVAL) return FALSE; /* Regardless of condition, try to read all the data available */ do { toread = ring_buffer_avail_no_wrap(io->buf); if (toread == 0) break; rbytes = 0; buf = ring_buffer_write_ptr(io->buf, 0); status = g_io_channel_read_chars(channel, (char *) buf, toread, &rbytes, NULL); g_at_util_debug_chat(TRUE, (char *)buf, rbytes, io->debugf, io->debug_data); read_count++; total_read += rbytes; if (rbytes > 0) ring_buffer_write_advance(io->buf, rbytes); } while (status == G_IO_STATUS_NORMAL && rbytes > 0 && read_count < io->max_read_attempts); if (total_read > 0 && io->read_handler) io->read_handler(io->buf, io->read_data); if (cond & (G_IO_HUP | G_IO_ERR)) return FALSE; if (read_count > 0 && rbytes == 0 && status != G_IO_STATUS_AGAIN) return FALSE; /* We're overflowing the buffer, shutdown the socket */ if (ring_buffer_avail(io->buf) == 0) return FALSE; return TRUE; } gsize g_at_io_write(GAtIO *io, const gchar *data, gsize count) { GIOStatus status; gsize bytes_written; status = g_io_channel_write_chars(io->channel, data, count, &bytes_written, NULL); if (status != G_IO_STATUS_NORMAL) { g_source_remove(io->read_watch); return 0; } g_at_util_debug_chat(FALSE, data, bytes_written, io->debugf, io->debug_data); return bytes_written; } static void write_watcher_destroy_notify(gpointer user_data) { GAtIO *io = user_data; io->write_watch = 0; io->write_handler = NULL; io->write_data = NULL; if (io->write_done_func) { io->write_done_func(io->write_done_data); io->write_done_func = NULL; io->write_done_data = NULL; } } static gboolean can_write_data(GIOChannel *channel, GIOCondition cond, gpointer data) { GAtIO *io = data; if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) return FALSE; if (io->write_handler == NULL) return FALSE; return io->write_handler(io->write_data); } static GAtIO *create_io(GIOChannel *channel, GIOFlags flags) { GAtIO *io; if (channel == NULL) return NULL; io = g_try_new0(GAtIO, 1); if (io == NULL) return io; io->ref_count = 1; io->debugf = NULL; if (flags & G_IO_FLAG_NONBLOCK) { io->max_read_attempts = 3; io->use_write_watch = TRUE; } else { io->max_read_attempts = 1; io->use_write_watch = FALSE; } io->buf = ring_buffer_new(8192); if (!io->buf) goto error; if (!g_at_util_setup_io(channel, flags)) goto error; io->channel = channel; io->read_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, received_data, io, read_watcher_destroy_notify); return io; error: if (io->buf) ring_buffer_free(io->buf); g_free(io); return NULL; } GAtIO *g_at_io_new(GIOChannel *channel) { return create_io(channel, G_IO_FLAG_NONBLOCK); } GAtIO *g_at_io_new_blocking(GIOChannel *channel) { return create_io(channel, 0); } GIOChannel *g_at_io_get_channel(GAtIO *io) { if (io == NULL) return NULL; return io->channel; } gboolean g_at_io_set_read_handler(GAtIO *io, GAtIOReadFunc read_handler, gpointer user_data) { if (io == NULL) return FALSE; io->read_handler = read_handler; io->read_data = user_data; if (read_handler && ring_buffer_len(io->buf) > 0) read_handler(io->buf, user_data); return TRUE; } static gboolean call_blocking_read(gpointer user_data) { GAtIO *io = user_data; while (can_write_data(io->channel, G_IO_OUT, io) == TRUE); write_watcher_destroy_notify(io); return FALSE; } gboolean g_at_io_set_write_handler(GAtIO *io, GAtIOWriteFunc write_handler, gpointer user_data) { if (io == NULL) return FALSE; if (io->write_watch > 0) { if (write_handler == NULL) { g_source_remove(io->write_watch); return TRUE; } return FALSE; } if (write_handler == NULL) return FALSE; io->write_handler = write_handler; io->write_data = user_data; if (io->use_write_watch == TRUE) io->write_watch = g_io_add_watch_full(io->channel, G_PRIORITY_HIGH, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, can_write_data, io, write_watcher_destroy_notify); else io->write_watch = g_idle_add(call_blocking_read, io); return TRUE; } GAtIO *g_at_io_ref(GAtIO *io) { if (io == NULL) return NULL; g_atomic_int_inc(&io->ref_count); return io; } static gboolean io_shutdown(GAtIO *io) { /* Don't trigger user disconnect on shutdown */ io->user_disconnect = NULL; io->user_disconnect_data = NULL; if (io->read_watch > 0) g_source_remove(io->read_watch); if (io->write_watch > 0) g_source_remove(io->write_watch); return TRUE; } void g_at_io_unref(GAtIO *io) { gboolean is_zero; if (io == NULL) return; is_zero = g_atomic_int_dec_and_test(&io->ref_count); if (is_zero == FALSE) return; io_shutdown(io); /* glib delays the destruction of the watcher until it exits, this * means we can't free the data just yet, even though we've been * destroyed already. We have to wait until the read_watcher * destroy function gets called */ if (io->read_watch > 0) io->destroyed = TRUE; else g_free(io); } gboolean g_at_io_set_disconnect_function(GAtIO *io, GAtDisconnectFunc disconnect, gpointer user_data) { if (io == NULL) return FALSE; io->user_disconnect = disconnect; io->user_disconnect_data = user_data; return TRUE; } gboolean g_at_io_set_debug(GAtIO *io, GAtDebugFunc func, gpointer user_data) { if (io == NULL) return FALSE; io->debugf = func; io->debug_data = user_data; return TRUE; } void g_at_io_set_write_done(GAtIO *io, GAtDisconnectFunc func, gpointer user_data) { if (io == NULL) return; io->write_done_func = func; io->write_done_data = user_data; } void g_at_io_drain_ring_buffer(GAtIO *io, guint len) { ring_buffer_drain(io->buf, len); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatserver.c0000644000015600001650000010266312671500024022170 0ustar pbuserpbgroup00000000000000/* * * AT server library with GLib integration * * 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 #endif #include #include #include #include #include "ringbuffer.h" #include "gatserver.h" #include "gatio.h" #define BUF_SIZE 4096 /* + the max length of information text + */ #define MAX_TEXT_SIZE 2052 /* #define WRITE_SCHEDULER_DEBUG 1 */ enum ParserState { PARSER_STATE_IDLE, PARSER_STATE_A, PARSER_STATE_COMMAND, PARSER_STATE_GARBAGE, }; enum ParserResult { PARSER_RESULT_COMMAND, PARSER_RESULT_EMPTY_COMMAND, PARSER_RESULT_REPEAT_LAST, PARSER_RESULT_GARBAGE, PARSER_RESULT_UNSURE, }; /* V.250 Table 1/V.250 Result codes */ static const char *server_result_to_string(GAtServerResult result) { switch (result) { case G_AT_SERVER_RESULT_OK: return "OK"; case G_AT_SERVER_RESULT_CONNECT: return "CONNECT"; case G_AT_SERVER_RESULT_RING: return "RING"; case G_AT_SERVER_RESULT_NO_CARRIER: return "NO CARRIER"; case G_AT_SERVER_RESULT_ERROR: return "ERROR"; case G_AT_SERVER_RESULT_NO_DIALTONE: return "NO DIALTONE"; case G_AT_SERVER_RESULT_BUSY: return "BUSY"; case G_AT_SERVER_RESULT_NO_ANSWER: return "NO ANSWER"; default: return NULL; } } /* Basic command setting for V.250 */ struct v250_settings { char s0; /* set by S0= */ char s3; /* set by S3= */ char s4; /* set by S4= */ char s5; /* set by S5= */ int s6; /* set by S6= */ int s7; /* set by S7= */ int s8; /* set by S8= */ int s10; /* set by S10= */ gboolean echo; /* set by E */ gboolean quiet; /* set by Q */ gboolean is_v1; /* set by V, v0 or v1 */ int res_format; /* set by X */ int c109; /* set by &C */ int c108; /* set by &D */ char l; /* set by L */ char m; /* set by M */ char dial_mode; /* set by P or T */ }; /* AT command set that server supported */ struct at_command { GAtServerNotifyFunc notify; gpointer user_data; GDestroyNotify destroy_notify; }; struct _GAtServer { gint ref_count; /* Ref count */ struct v250_settings v250; /* V.250 command setting */ GAtIO *io; /* Server IO */ guint read_so_far; /* Number of bytes processed */ GAtDisconnectFunc user_disconnect; /* User disconnect func */ gpointer user_disconnect_data; /* User disconnect data */ GAtDebugFunc debugf; /* Debugging output function */ gpointer debug_data; /* Data to pass to debug func */ GHashTable *command_list; /* List of AT commands */ GQueue *write_queue; /* Write buffer queue */ guint max_read_attempts; /* Max reads per select */ enum ParserState parser_state; gboolean destroyed; /* Re-entrancy guard */ char *last_line; /* Last read line */ unsigned int cur_pos; /* Where we are on the line */ GAtServerResult last_result; gboolean final_sent; gboolean final_async; gboolean in_read_handler; GAtServerFinishFunc finishf; /* Callback when cmd finishes */ gpointer finish_data; /* Finish func data */ }; static void server_wakeup_writer(GAtServer *server); static void server_parse_line(GAtServer *server); static struct ring_buffer *allocate_next(GAtServer *server) { struct ring_buffer *buf = ring_buffer_new(BUF_SIZE); if (buf == NULL) return NULL; g_queue_push_tail(server->write_queue, buf); return buf; } static void send_common(GAtServer *server, const char *buf, unsigned int len) { gsize towrite = len; gsize bytes_written = 0; struct ring_buffer *write_buf; write_buf = g_queue_peek_tail(server->write_queue); while (bytes_written < towrite) { gsize wbytes = MIN((gsize)ring_buffer_avail(write_buf), towrite - bytes_written); bytes_written += ring_buffer_write(write_buf, buf + bytes_written, wbytes); /* * Make sure we don't allocate a buffer if we've written * everything out already */ if (ring_buffer_avail(write_buf) == 0 && bytes_written < towrite) write_buf = allocate_next(server); } server_wakeup_writer(server); } static void send_result_common(GAtServer *server, const char *result) { struct v250_settings v250 = server->v250; char buf[MAX_TEXT_SIZE + 1]; char t = v250.s3; char r = v250.s4; unsigned int len; if (v250.quiet) return; if (result == NULL) return; if (strlen(result) > 2048) return; if (v250.is_v1) len = sprintf(buf, "%c%c%s%c%c", t, r, result, t, r); else len = sprintf(buf, "%s%c", result, t); send_common(server, buf, len); } static inline void send_final_common(GAtServer *server, const char *result) { send_result_common(server, result); server->final_async = FALSE; if (server->finishf) server->finishf(server, server->finish_data); } static inline void send_final_numeric(GAtServer *server, GAtServerResult result) { char buf[1024]; if (server->v250.is_v1) sprintf(buf, "%s", server_result_to_string(result)); else sprintf(buf, "%u", (unsigned int)result); send_final_common(server, buf); } void g_at_server_send_final(GAtServer *server, GAtServerResult result) { if (server == NULL) return; if (server->final_sent != FALSE) return; server->final_sent = TRUE; server->last_result = result; if (result == G_AT_SERVER_RESULT_OK) { if (server->final_async) server_parse_line(server); return; } send_final_numeric(server, result); } void g_at_server_send_ext_final(GAtServer *server, const char *result) { server->final_sent = TRUE; server->last_result = G_AT_SERVER_RESULT_EXT_ERROR; send_final_common(server, result); } void g_at_server_send_intermediate(GAtServer *server, const char *result) { send_result_common(server, result); } void g_at_server_send_unsolicited(GAtServer *server, const char *result) { send_result_common(server, result); } void g_at_server_send_info(GAtServer *server, const char *line, gboolean last) { char buf[MAX_TEXT_SIZE + 1]; char t = server->v250.s3; char r = server->v250.s4; unsigned int len; if (strlen(line) > 2048) return; if (last) len = sprintf(buf, "%c%c%s%c%c", t, r, line, t, r); else len = sprintf(buf, "%c%c%s", t, r, line); send_common(server, buf, len); } static gboolean get_result_value(GAtServer *server, GAtResult *result, int min, int max, int *value) { GAtResultIter iter; int val; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "")) return FALSE; if (!g_at_result_iter_next_number(&iter, &val)) return FALSE; if (val < min || val > max) return FALSE; if (value) *value = val; return TRUE; } static void v250_settings_create(struct v250_settings *v250) { v250->s0 = 0; v250->s3 = '\r'; v250->s4 = '\n'; v250->s5 = '\b'; v250->s6 = 2; v250->s7 = 50; v250->s8 = 2; v250->s10 = 2; v250->echo = TRUE; v250->quiet = FALSE; v250->is_v1 = TRUE; v250->res_format = 0; v250->c109 = 1; v250->c108 = 0; v250->l = 0; v250->m = 1; v250->dial_mode = 'T'; } static void s_template_cb(GAtServerRequestType type, GAtResult *result, GAtServer *server, char *sreg, const char *prefix, int min, int max) { char buf[20]; int tmp; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: if (!get_result_value(server, result, min, max, &tmp)) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } *sreg = tmp; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: tmp = *sreg; sprintf(buf, "%03d", tmp); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: sprintf(buf, "%s: (%d-%d)", prefix, min, max); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void at_s0_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { s_template_cb(type, result, server, &server->v250.s0, "S0", 0, 7); } static void at_s3_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { s_template_cb(type, result, server, &server->v250.s3, "S3", 0, 127); } static void at_s4_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { s_template_cb(type, result, server, &server->v250.s4, "S4", 0, 127); } static void at_s5_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { s_template_cb(type, result, server, &server->v250.s5, "S5", 0, 127); } static void at_l_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { s_template_cb(type, result, server, &server->v250.l, "L", 0, 3); } static void at_m_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { s_template_cb(type, result, server, &server->v250.m, "M", 0, 2); } static void at_template_cb(GAtServerRequestType type, GAtResult *result, GAtServer *server, int *value, const char *prefix, int min, int max, int deftval) { char buf[20]; int tmp; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: if (!get_result_value(server, result, min, max, &tmp)) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } *value = tmp; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: tmp = *value; sprintf(buf, "%s: %d", prefix, tmp); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: sprintf(buf, "%s: (%d-%d)", prefix, min, max); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: *value = deftval; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void at_e_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { at_template_cb(type, result, server, &server->v250.echo, "E", 0, 1, 1); } static void at_q_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { at_template_cb(type, result, server, &server->v250.quiet, "Q", 0, 1, 0); } static void at_v_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { at_template_cb(type, result, server, &server->v250.is_v1, "V", 0, 1, 1); } static void at_x_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { at_template_cb(type, result, server, &server->v250.res_format, "X", 0, 4, 4); } static void at_s6_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { at_template_cb(type, result, server, &server->v250.s6, "S6", 0, 1, 1); } static void at_s7_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { at_template_cb(type, result, server, &server->v250.s7, "S7", 1, 255, 50); } static void at_s8_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { at_template_cb(type, result, server, &server->v250.s8, "S8", 1, 255, 2); } static void at_s10_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { at_template_cb(type, result, server, &server->v250.s10, "S10", 1, 254, 2); } static void at_c109_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { at_template_cb(type, result, server, &server->v250.c109, "&C", 0, 1, 1); } static void at_c108_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { at_template_cb(type, result, server, &server->v250.c108, "&D", 0, 2, 2); } /* According to ITU V.250 6.3.2 and 6.3.3: "Implementation of this command * is mandatory; however, if DTMF or pulse dialling is not implemented, * this command will have no effect" */ static void at_t_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: server->v250.dial_mode = 'T'; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void at_p_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: server->v250.dial_mode = 'P'; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void at_f_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: if (!get_result_value(server, result, 0, 0, NULL)) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } /* intentional fallback here */ case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: /* default behavior on AT&F same as ATZ */ v250_settings_create(&server->v250); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void at_z_cb(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: v250_settings_create(&server->v250); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static inline gboolean is_extended_command_prefix(const char c) { switch (c) { case '+': case '*': case '!': case '%': return TRUE; default: return FALSE; } } static void at_command_notify(GAtServer *server, char *command, char *prefix, GAtServerRequestType type) { struct at_command *node; GAtResult result; node = g_hash_table_lookup(server->command_list, prefix); if (node == NULL) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } result.lines = g_slist_prepend(NULL, command); result.final_or_pdu = 0; node->notify(server, type, &result, node->user_data); g_slist_free(result.lines); } static unsigned int parse_extended_command(GAtServer *server, char *buf) { const char *valid_extended_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789!%-./:_"; const char *separators = ";?="; unsigned int prefix_len, i; gboolean in_string = FALSE; gboolean seen_equals = FALSE; char prefix[18]; /* According to V250, 5.4.1 */ GAtServerRequestType type; char tmp; unsigned int cmd_start; prefix_len = strcspn(buf, separators); if (prefix_len > 17 || prefix_len < 2) return 0; /* Convert to upper case, we will always use upper case naming */ for (i = 0; i < prefix_len; i++) prefix[i] = g_ascii_toupper(buf[i]); prefix[prefix_len] = '\0'; if (strspn(prefix + 1, valid_extended_chars) != (prefix_len - 1)) return 0; /* * V.250 Section 5.4.1: "The first character following "+" shall be * an alphabetic character in the range "A" through "Z". */ if (prefix[1] <= 'A' || prefix[1] >= 'Z') return 0; if (buf[i] != '\0' && buf[i] != ';' && buf[i] != '?' && buf[i] != '=') return 0; type = G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY; cmd_start = prefix_len; /* Continue until we hit eol or ';' */ while (buf[i] && !(buf[i] == ';' && in_string == FALSE)) { if (buf[i] == '"') { in_string = !in_string; goto next; } if (in_string == TRUE) goto next; if (buf[i] == '?') { if (seen_equals && buf[i-1] != '=') return 0; if (buf[i + 1] != '\0' && buf[i + 1] != ';') return 0; type = G_AT_SERVER_REQUEST_TYPE_QUERY; cmd_start += 1; if (seen_equals) type = G_AT_SERVER_REQUEST_TYPE_SUPPORT; } else if (buf[i] == '=') { if (seen_equals) return 0; seen_equals = TRUE; type = G_AT_SERVER_REQUEST_TYPE_SET; cmd_start += 1; } next: i++; } /* We can scratch in this buffer, so mark ';' as null */ tmp = buf[i]; buf[i] = '\0'; at_command_notify(server, buf + cmd_start, prefix, type); buf[i] = tmp; /* Also consume the terminating null */ return i + 1; } static int get_basic_prefix_size(const char *buf) { if (g_ascii_isalpha(buf[0])) { if (g_ascii_toupper(buf[0]) == 'S') { int size; /* V.250 5.3.2 'S' command follows with a parameter * number. */ for (size = 1; g_ascii_isdigit(buf[size]); size++) ; /* * Do some basic sanity checking, don't accept 00, 01, * etc or empty S values */ if (size == 1) return 0; if (size > 2 && buf[1] == '0') return 0; return size; } /* All other cases it is a simple 1 character prefix */ return 1; } if (buf[0] == '&') { if (g_ascii_isalpha(buf[1]) == FALSE) return 0; return 2; } return 0; } static unsigned int parse_basic_command(GAtServer *server, char *buf) { gboolean seen_equals = FALSE; char prefix[4], tmp; unsigned int i, prefix_size; GAtServerRequestType type; unsigned int cmd_start; prefix_size = get_basic_prefix_size(buf); if (prefix_size == 0) return 0; i = prefix_size; prefix[0] = g_ascii_toupper(buf[0]); cmd_start = prefix_size; if (prefix[0] == 'D') { type = G_AT_SERVER_REQUEST_TYPE_SET; /* All characters appearing on the same line, up to a * semicolon character (IA5 3/11) or the end of the * command line is the part of the call. */ while (buf[i] != '\0' && buf[i] != ';') i += 1; if (buf[i] == ';') i += 1; goto done; } type = G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY; /* Match '?', '=', '=?' and '=xxx' */ if (buf[i] == '=') { seen_equals = TRUE; i += 1; cmd_start += 1; } if (buf[i] == '?') { i += 1; cmd_start += 1; if (seen_equals) type = G_AT_SERVER_REQUEST_TYPE_SUPPORT; else type = G_AT_SERVER_REQUEST_TYPE_QUERY; } else { int before = i; /* V.250 5.3.1 The subparameter (if any) are all digits */ while (g_ascii_isdigit(buf[i])) i++; if (i - before > 0) type = G_AT_SERVER_REQUEST_TYPE_SET; } done: if (prefix_size <= 3) { memcpy(prefix + 1, buf + 1, prefix_size - 1); prefix[prefix_size] = '\0'; tmp = buf[i]; buf[i] = '\0'; at_command_notify(server, buf + cmd_start, prefix, type); buf[i] = tmp; } else /* Handle S-parameter with 100+ */ g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); /* * Commands like ATA, ATZ cause the remainder linevto be ignored. * In GSM/UMTS the ATD uses the separator ';' character as a voicecall * modifier, so we ignore everything coming after that character * as well. */ if (prefix[0] == 'A' || prefix[0] == 'Z' || prefix[0] == 'D') return strlen(buf); /* Consume the seperator ';' */ if (buf[i] == ';') i += 1; return i; } static void server_parse_line(GAtServer *server) { char *line = server->last_line; unsigned int pos = server->cur_pos; unsigned int len = strlen(line); while (pos < len) { unsigned int consumed; server->final_sent = FALSE; server->final_async = FALSE; if (is_extended_command_prefix(line[pos])) consumed = parse_extended_command(server, line + pos); else consumed = parse_basic_command(server, line + pos); if (consumed == 0) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } pos += consumed; server->cur_pos = pos; /* * We wait the callback until it finished processing * the command and called the send_final. */ if (server->final_sent == FALSE) { server->final_async = TRUE; return; } if (server->last_result != G_AT_SERVER_RESULT_OK) return; } send_final_numeric(server, G_AT_SERVER_RESULT_OK); } static enum ParserResult server_feed(GAtServer *server, const char *bytes, gsize *len) { gsize i = 0; enum ParserResult res = PARSER_RESULT_UNSURE; char s3 = server->v250.s3; while (i < *len) { char byte = bytes[i]; switch (server->parser_state) { case PARSER_STATE_IDLE: if (byte == s3) { i += 1; res = PARSER_RESULT_EMPTY_COMMAND; goto out; } else if (byte == '\n') { i += 1; res = PARSER_RESULT_GARBAGE; goto out; } else if (byte == 'A' || byte == 'a') server->parser_state = PARSER_STATE_A; else if (byte != ' ' && byte != '\t') server->parser_state = PARSER_STATE_GARBAGE; break; case PARSER_STATE_A: if (byte == s3) { server->parser_state = PARSER_STATE_IDLE; i += 1; res = PARSER_RESULT_GARBAGE; goto out; } else if (byte == '/') { server->parser_state = PARSER_STATE_IDLE; i += 1; res = PARSER_RESULT_REPEAT_LAST; goto out; } else if (byte == 'T' || byte == 't') server->parser_state = PARSER_STATE_COMMAND; else server->parser_state = PARSER_STATE_GARBAGE; break; case PARSER_STATE_COMMAND: if (byte == s3) { server->parser_state = PARSER_STATE_IDLE; i += 1; res = PARSER_RESULT_COMMAND; goto out; } break; case PARSER_STATE_GARBAGE: /* Detect CR or HDLC frame marker flag */ if (byte == s3 || byte == '~') { server->parser_state = PARSER_STATE_IDLE; i += 1; res = PARSER_RESULT_GARBAGE; goto out; } break; default: break; }; i += 1; } out: *len = i; return res; } static char *extract_line(GAtServer *p, struct ring_buffer *rbuf) { unsigned int wrap = ring_buffer_len_no_wrap(rbuf); unsigned int pos = 0; unsigned char *buf = ring_buffer_read_ptr(rbuf, pos); int strip_front = 0; int line_length = 0; gboolean in_string = FALSE; char s3 = p->v250.s3; char s5 = p->v250.s5; char *line; int i; while (pos < p->read_so_far) { if (*buf == '"') in_string = !in_string; if (in_string == FALSE && (*buf == ' ' || *buf == '\t')) { if (line_length == 0) strip_front += 1; } else line_length += 1; buf += 1; pos += 1; if (pos == wrap) buf = ring_buffer_read_ptr(rbuf, pos); } /* We will strip AT and S3 */ line_length -= 3; line = g_try_new(char, line_length + 1); if (line == NULL) { ring_buffer_drain(rbuf, p->read_so_far); return NULL; } /* Strip leading whitespace + AT */ ring_buffer_drain(rbuf, strip_front + 2); pos = 0; i = 0; wrap = ring_buffer_len_no_wrap(rbuf); buf = ring_buffer_read_ptr(rbuf, pos); while (pos < (p->read_so_far - strip_front - 2)) { if (*buf == '"') in_string = !in_string; if (*buf == s5) { if (i != 0) i -= 1; } else if ((*buf == ' ' || *buf == '\t') && in_string == FALSE) ; /* Skip */ else if (*buf != s3) line[i++] = *buf; buf += 1; pos += 1; if (pos == wrap) buf = ring_buffer_read_ptr(rbuf, pos); } /* Strip S3 */ ring_buffer_drain(rbuf, p->read_so_far - strip_front - 2); line[i] = '\0'; return line; } static void new_bytes(struct ring_buffer *rbuf, gpointer user_data) { GAtServer *p = user_data; unsigned int len = ring_buffer_len(rbuf); unsigned int wrap = ring_buffer_len_no_wrap(rbuf); unsigned char *buf = ring_buffer_read_ptr(rbuf, p->read_so_far); enum ParserResult result; /* We do not support command abortion, so ignore input */ if (p->final_async) { ring_buffer_drain(rbuf, len); return; } p->in_read_handler = TRUE; while (p->io && (p->read_so_far < len)) { gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far); result = server_feed(p, (char *)buf, &rbytes); if (p->v250.echo) send_common(p, (char *)buf, rbytes); buf += rbytes; p->read_so_far += rbytes; if (p->read_so_far == wrap) { buf = ring_buffer_read_ptr(rbuf, p->read_so_far); wrap = len; } switch (result) { case PARSER_RESULT_UNSURE: continue; case PARSER_RESULT_EMPTY_COMMAND: /* * According to section 5.2.4 and 5.6 of V250, * Empty commands must be OK by the DCE */ g_at_server_send_final(p, G_AT_SERVER_RESULT_OK); ring_buffer_drain(rbuf, p->read_so_far); break; case PARSER_RESULT_COMMAND: { g_free(p->last_line); p->last_line = extract_line(p, rbuf); p->cur_pos = 0; if (p->last_line) server_parse_line(p); else g_at_server_send_final(p, G_AT_SERVER_RESULT_ERROR); break; } case PARSER_RESULT_REPEAT_LAST: p->cur_pos = 0; ring_buffer_drain(rbuf, p->read_so_far); if (p->last_line) server_parse_line(p); else g_at_server_send_final(p, G_AT_SERVER_RESULT_OK); break; case PARSER_RESULT_GARBAGE: ring_buffer_drain(rbuf, p->read_so_far); break; } len -= p->read_so_far; wrap -= p->read_so_far; p->read_so_far = 0; /* * Handle situations where we receive two command lines in * one read, which should not be possible (and implies the * earlier command should be canceled. * * e.g. AT+CMD1\rAT+CMD2 */ if (result != PARSER_RESULT_GARBAGE) { ring_buffer_drain(rbuf, len); break; } } p->in_read_handler = FALSE; if (p->destroyed) g_free(p); } static gboolean can_write_data(gpointer data) { GAtServer *server = data; gsize bytes_written; gsize towrite; struct ring_buffer *write_buf; unsigned char *buf; #ifdef WRITE_SCHEDULER_DEBUG int limiter; #endif if (!server->write_queue) return FALSE; /* Write data out from the head of the queue */ write_buf = g_queue_peek_head(server->write_queue); buf = ring_buffer_read_ptr(write_buf, 0); towrite = ring_buffer_len_no_wrap(write_buf); #ifdef WRITE_SCHEDULER_DEBUG limiter = towrite; if (limiter > 5) limiter = 5; #endif bytes_written = g_at_io_write(server->io, (char *)buf, #ifdef WRITE_SCHEDULER_DEBUG limiter #else towrite #endif ); if (bytes_written == 0) return FALSE; ring_buffer_drain(write_buf, bytes_written); /* All data in current buffer is written, free it * unless it's the last buffer in the queue. */ if ((ring_buffer_len(write_buf) == 0) && (g_queue_get_length(server->write_queue) > 1)) { write_buf = g_queue_pop_head(server->write_queue); ring_buffer_free(write_buf); write_buf = g_queue_peek_head(server->write_queue); } if (ring_buffer_len(write_buf) > 0) return TRUE; return FALSE; } static void write_queue_free(GQueue *write_queue) { struct ring_buffer *write_buf; while ((write_buf = g_queue_pop_head(write_queue))) ring_buffer_free(write_buf); g_queue_free(write_queue); } static void g_at_server_cleanup(GAtServer *server) { /* Cleanup pending data to write */ write_queue_free(server->write_queue); g_hash_table_destroy(server->command_list); server->command_list = NULL; g_free(server->last_line); g_at_io_unref(server->io); server->io = NULL; } static void io_disconnect(gpointer user_data) { GAtServer *server = user_data; g_at_server_cleanup(server); if (server->user_disconnect) server->user_disconnect(server->user_disconnect_data); } static void server_wakeup_writer(GAtServer *server) { g_at_io_set_write_handler(server->io, can_write_data, server); } static void at_notify_node_destroy(gpointer data) { struct at_command *node = data; if (node->destroy_notify) node->destroy_notify(node->user_data); g_free(node); } static void basic_command_register(GAtServer *server) { g_at_server_register(server, "S0", at_s0_cb, NULL, NULL); g_at_server_register(server, "S3", at_s3_cb, NULL, NULL); g_at_server_register(server, "S4", at_s4_cb, NULL, NULL); g_at_server_register(server, "S5", at_s5_cb, NULL, NULL); g_at_server_register(server, "E", at_e_cb, NULL, NULL); g_at_server_register(server, "Q", at_q_cb, NULL, NULL); g_at_server_register(server, "V", at_v_cb, NULL, NULL); g_at_server_register(server, "X", at_x_cb, NULL, NULL); g_at_server_register(server, "S6", at_s6_cb, NULL, NULL); g_at_server_register(server, "S7", at_s7_cb, NULL, NULL); g_at_server_register(server, "S8", at_s8_cb, NULL, NULL); g_at_server_register(server, "S10", at_s10_cb, NULL, NULL); g_at_server_register(server, "&C", at_c109_cb, NULL, NULL); g_at_server_register(server, "&D", at_c108_cb, NULL, NULL); g_at_server_register(server, "Z", at_z_cb, NULL, NULL); g_at_server_register(server, "&F", at_f_cb, NULL, NULL); g_at_server_register(server, "L", at_l_cb, NULL, NULL); g_at_server_register(server, "M", at_m_cb, NULL, NULL); g_at_server_register(server, "T", at_t_cb, NULL, NULL); g_at_server_register(server, "P", at_p_cb, NULL, NULL); } GAtServer *g_at_server_new(GIOChannel *io) { GAtServer *server; if (io == NULL) return NULL; server = g_try_new0(GAtServer, 1); if (server == NULL) return NULL; server->ref_count = 1; v250_settings_create(&server->v250); server->io = g_at_io_new(io); if (!server->io) goto error; g_at_io_set_disconnect_function(server->io, io_disconnect, server); server->command_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, at_notify_node_destroy); server->write_queue = g_queue_new(); if (!server->write_queue) goto error; if (allocate_next(server) == NULL) goto error; server->max_read_attempts = 3; g_at_io_set_read_handler(server->io, new_bytes, server); basic_command_register(server); return server; error: g_at_io_unref(server->io); if (server->command_list) g_hash_table_destroy(server->command_list); if (server->write_queue) write_queue_free(server->write_queue); if (server) g_free(server); return NULL; } GIOChannel *g_at_server_get_channel(GAtServer *server) { if (server == NULL || server->io == NULL) return NULL; return g_at_io_get_channel(server->io); } GAtIO *g_at_server_get_io(GAtServer *server) { if (server == NULL) return NULL; return server->io; } GAtServer *g_at_server_ref(GAtServer *server) { if (server == NULL) return NULL; g_atomic_int_inc(&server->ref_count); return server; } void g_at_server_suspend(GAtServer *server) { if (server == NULL) return; g_at_io_set_write_handler(server->io, NULL, NULL); g_at_io_set_read_handler(server->io, NULL, NULL); g_at_io_set_debug(server->io, NULL, NULL); } void g_at_server_resume(GAtServer *server) { if (server == NULL) return; if (g_at_io_get_channel(server->io) == NULL) { io_disconnect(server); return; } g_at_io_set_disconnect_function(server->io, io_disconnect, server); g_at_io_set_debug(server->io, server->debugf, server->debug_data); g_at_io_set_read_handler(server->io, new_bytes, server); if (g_queue_get_length(server->write_queue) > 0) server_wakeup_writer(server); } void g_at_server_unref(GAtServer *server) { gboolean is_zero; if (server == NULL) return; is_zero = g_atomic_int_dec_and_test(&server->ref_count); if (is_zero == FALSE) return; if (server->io) { g_at_server_suspend(server); g_at_server_cleanup(server); } g_at_server_shutdown(server); /* glib delays the destruction of the watcher until it exits, this * means we can't free the data just yet, even though we've been * destroyed already. We have to wait until the read_watcher * destroy function gets called */ if (server->in_read_handler) server->destroyed = TRUE; else g_free(server); } gboolean g_at_server_shutdown(GAtServer *server) { if (server == NULL) return FALSE; /* Don't trigger user disconnect on shutdown */ server->user_disconnect = NULL; server->user_disconnect_data = NULL; return TRUE; } gboolean g_at_server_set_echo(GAtServer *server, gboolean echo) { if (server == NULL) return FALSE; server->v250.echo = echo; return TRUE; } gboolean g_at_server_set_disconnect_function(GAtServer *server, GAtDisconnectFunc disconnect, gpointer user_data) { if (server == NULL) return FALSE; server->user_disconnect = disconnect; server->user_disconnect_data = user_data; return TRUE; } gboolean g_at_server_set_debug(GAtServer *server, GAtDebugFunc func, gpointer user_data) { if (server == NULL) return FALSE; server->debugf = func; server->debug_data = user_data; g_at_io_set_debug(server->io, server->debugf, server->debug_data); return TRUE; } gboolean g_at_server_register(GAtServer *server, const char *prefix, GAtServerNotifyFunc notify, gpointer user_data, GDestroyNotify destroy_notify) { struct at_command *node; if (server == NULL || server->command_list == NULL) return FALSE; if (notify == NULL) return FALSE; if (prefix == NULL || strlen(prefix) == 0) return FALSE; node = g_try_new0(struct at_command, 1); if (node == NULL) return FALSE; node->notify = notify; node->user_data = user_data; node->destroy_notify = destroy_notify; g_hash_table_replace(server->command_list, g_strdup(prefix), node); return TRUE; } gboolean g_at_server_unregister(GAtServer *server, const char *prefix) { struct at_command *node; if (server == NULL || server->command_list == NULL) return FALSE; if (prefix == NULL || strlen(prefix) == 0) return FALSE; node = g_hash_table_lookup(server->command_list, prefix); if (node == NULL) return FALSE; g_hash_table_remove(server->command_list, prefix); return TRUE; } gboolean g_at_server_set_finish_callback(GAtServer *server, GAtServerFinishFunc finishf, gpointer user_data) { if (server == NULL) return FALSE; server->finishf = finishf; server->finish_data = user_data; return TRUE; } gboolean g_at_server_command_pending(GAtServer *server) { if (server == NULL) return FALSE; return server->final_async; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gsm0710.c0000644000015600001650000002022212671500024021252 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * Copyright (C) 2011 Intel Corporation. All rights reserved. * Copyright (C) 2009 Trolltech ASA. * * 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 #endif #include #include #include "gsm0710.h" static const unsigned char crc_table[256] = { 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF }; static inline guint8 gsm0710_crc(const guint8 *data, int len) { guint8 crc = 0xFF; int i; for (i = 0; i < len; i++) crc = crc_table[crc ^ data[i]]; return crc; } static inline guint8 gsm0710_fcs(const guint8 *data, int len) { return 0xff - gsm0710_crc(data, len); } static inline gboolean gsm0710_check_fcs(const guint8 *data, int len, guint8 cfcs) { guint8 fcs = gsm0710_crc(data, len); fcs = crc_table[fcs ^ cfcs]; if (fcs == 0xcf) return TRUE; return FALSE; } int gsm0710_advanced_extract_frame(guint8 *buf, int len, guint8 *out_dlc, guint8 *out_control, guint8 **out_frame, int *out_len) { int posn = 0; int posn2; int framelen; guint8 dlc; guint8 control; while (posn < len) { if (buf[posn] != 0x7E) { posn += 1; continue; } /* Skip additional 0x7E bytes between frames */ while ((posn + 1) < len && buf[posn + 1] == 0x7E) posn += 1; /* Search for the end of the packet (the next 0x7E byte) */ framelen = posn + 1; while (framelen < len && buf[framelen] != 0x7E) framelen += 1; if (framelen >= len) break; if (framelen < 4) { posn = framelen; continue; } /* Undo control byte quoting in the packet */ posn2 = 0; ++posn; while (posn < framelen) { if (buf[posn] == 0x7D) { ++posn; if (posn >= framelen) break; buf[posn2++] = buf[posn++] ^ 0x20; } else { buf[posn2++] = buf[posn++]; } } /* Validate the checksum on the packet header */ if (!gsm0710_check_fcs(buf, 2, buf[posn2 - 1])) continue; /* Decode and dispatch the packet */ dlc = (buf[0] >> 2) & 0x3F; control = buf[1] & 0xEF; /* Strip "PF" bit */ if (out_frame) *out_frame = buf + 2; if (out_len) *out_len = posn2 - 3; if (out_dlc) *out_dlc = dlc; if (out_control) *out_control = control; break; } return posn; } int gsm0710_advanced_fill_frame(guint8 *frame, guint8 dlc, guint8 type, const guint8 *data, int len) { int temp, crc; int size; frame[0] = 0x7E; frame[1] = ((dlc << 2) | 0x03); frame[2] = type; crc = gsm0710_fcs(frame + 1, 2); /* The Address field might need to be escaped if this is a response * frame */ /* Need to quote the type field now that crc has been computed */ if (type == 0x7E || type == 0x7D) { frame[2] = 0x7D; frame[3] = (type ^ 0x20); size = 4; } else { size = 3; } while (len > 0) { temp = *data++ & 0xFF; --len; if (temp != 0x7E && temp != 0x7D) { frame[size++] = temp; } else { frame[size++] = 0x7D; frame[size++] = (temp ^ 0x20); } } if (crc != 0x7E && crc != 0x7D) { frame[size++] = crc; } else { frame[size++] = 0x7D; frame[size++] = (crc ^ 0x20); } frame[size++] = 0x7E; return size; } int gsm0710_basic_extract_frame(guint8 *buf, int len, guint8 *out_dlc, guint8 *out_control, guint8 **out_frame, int *out_len) { int posn = 0; int framelen; int header_size; guint8 fcs; guint8 dlc; guint8 type; while (posn < len) { if (buf[posn] != 0xF9) { posn += 1; continue; } /* Skip additional 0xF9 bytes between frames */ while ((posn + 1) < len && buf[posn + 1] == 0xF9) posn += 1; /* We need at least 4 bytes for the flag + header */ if ((posn + 4) > len) break; /* The low bit of the second byte should be 1, which indicates a short channel number. According to 27.010 Section 5.2.3, if this is not true, then the frame is invalid and should be discarded */ if ((buf[posn + 1] & 0x01) == 0) { ++posn; continue; } /* Get the packet length and validate it */ framelen = buf[posn + 3] >> 1; if ((buf[posn + 3] & 0x01) != 0) { /* Single-byte length indication */ header_size = 3; } else { /* Double-byte length indication */ if ((posn + 5) > len) break; framelen |= buf[posn + 4] << 7; header_size = 4; } /* Total size of the packet is the flag + 3 or 4 byte header * Address Control Length followed by Information and FCS. * However, we must check the presence of the end flag * according to 27.010 Section 5.2.3 */ if ((posn + header_size + 3 + framelen) > len) break; fcs = buf[posn + 1 + header_size + framelen]; /* * The end flag is not guaranteed to be only ours * according to 27.010 Section 5.2.6.1: * "The closing flag may also be the opening flag of the * following frame", thus we do not consume it in the following * stages */ /* * If FCS is invalid, discard the packet in accordance to * Section 5.2.3 of 27.010 */ if (!gsm0710_check_fcs(buf + posn + 1, header_size, fcs)) { posn += header_size + framelen + 2; continue; } if (buf[posn + header_size + framelen + 2] != 0xF9) { posn += header_size + framelen + 2; continue; } /* Get the channel number and packet type from the header */ dlc = buf[posn + 1] >> 2; type = buf[posn + 2] & 0xEF; /* Strip "PF" bit */ if (out_frame) *out_frame = buf + posn + 1 + header_size; if (out_len) *out_len = framelen; if (out_dlc) *out_dlc = dlc; if (out_control) *out_control = type; posn += header_size + framelen + 2; break; } return posn; } int gsm0710_basic_fill_frame(guint8 *frame, guint8 dlc, guint8 type, const guint8 *data, int len) { int size; int header_size; frame[0] = 0xF9; frame[1] = ((dlc << 2) | 0x03); frame[2] = type; if (len <= 127) { frame[3] = ((len << 1) | 0x01); header_size = 4; } else { frame[3] = (len << 1); frame[4] = (len >> 7); header_size = 5; } size = header_size; if (len > 0) { memcpy(frame + header_size, data, len); size += len; } /* Note: GSM 07.10 says that the CRC is only computed over the header */ frame[size++] = gsm0710_fcs(frame + 1, header_size - 1); frame[size++] = 0xF9; return size; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/ppp_cp.c0000644000015600001650000006105312671500024021444 0ustar pbuserpbgroup00000000000000/* * * PPP library with GLib integration * * Copyright (C) 2009-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 #endif #include #include #include #include #include #include #include #include "gatppp.h" #include "ppp.h" static const char *pppcp_state_strings[] = { "INITIAL", "STARTING", "CLOSED", "STOPPED", "CLOSING", "STOPPING", "REQSENT", "ACKRCVD", "ACKSENT", "OPENED" }; static const char *pppcp_event_strings[] = { "Up", "Down", "Open", "Close", "TO+", "TO-", "RCR+", "RCR-", "RCA", "RCN", "RTR", "RTA", "RUC", "RXJ+", "RXJ-", "RXR" }; #define pppcp_trace(p) do { \ char *str = g_strdup_printf("%s: %s: current state %d:%s", \ p->driver->name, __FUNCTION__, \ p->state, pppcp_state_strings[p->state]); \ ppp_debug(p->ppp, str); \ g_free(str); \ } while (0); #define pppcp_trace_event(p, type, actions, state) do { \ char *str = g_strdup_printf("event: %d (%s), " \ "action: %x, new_state: %d (%s)", \ type, pppcp_event_strings[type], \ actions, state, pppcp_state_strings[state]); \ ppp_debug(p->ppp, str); \ g_free(str); \ } while (0); #define pppcp_to_ppp_packet(p) \ (((guint8 *) p) - sizeof(struct ppp_header)) #define INITIAL_RESTART_TIMEOUT 3 /* restart interval in seconds */ #define MAX_TERMINATE 2 #define MAX_CONFIGURE 10 #define MAX_FAILURE 5 #define CP_HEADER_SZ 4 enum pppcp_state { INITIAL = 0, STARTING = 1, CLOSED = 2, STOPPED = 3, CLOSING = 4, STOPPING = 5, REQSENT = 6, ACKRCVD = 7, ACKSENT = 8, OPENED = 9, }; enum actions { INV = 0x10, IRC = 0x20, ZRC = 0x40, TLU = 0x100, TLD = 0x200, TLS = 0x400, TLF = 0x800, SCR = 0x1000, SCA = 0x2000, SCN = 0x4000, STR = 0x8000, STA = 0x10000, SCJ = 0x20000, SER = 0x40000, }; /* * Transition table straight from RFC 1661 Section 4.1 * Y coordinate is the events, while X coordinate is the state * * Magic of bitwise operations allows the table to describe all state * transitions defined in the specification */ static int cp_transitions[16][10] = { /* Up */ { 2, IRC|SCR|6, INV, INV, INV, INV, INV, INV, INV, INV }, /* Down */ { INV, INV, 0, TLS|1, 0, 1, 1, 1, 1, TLD|1 }, /* Open */ { TLS|1, 1, IRC|SCR|6, 3, 5, 5, 6, 7, 8, 9 }, /* Close */ { 0, TLF|0, 2, 2, 4, 4, IRC|STR|4, IRC|STR|4, IRC|STR|4, TLD|IRC|STR|4 }, /* TO+ */ { INV, INV, INV, INV, STR|4, STR|5, SCR|6, SCR|6, SCR|8, INV }, /* TO- */ { INV, INV, INV, INV, TLF|2, TLF|3, TLF|3, TLF|3, TLF|3, INV }, /* RCR+ */ { INV, INV, STA|2, IRC|SCR|SCA|8, 4, 5, SCA|8, SCA|TLU|9, SCA|8, TLD|SCR|SCA|8 }, /* RCR- */ { INV, INV, STA|2, IRC|SCR|SCN|6, 4, 5, SCN|6, SCN|7, SCN|6, TLD|SCR|SCN|6 }, /* RCA */ { INV, INV, STA|2, STA|3, 4, 5, IRC|7, SCR|6, IRC|TLU|9, TLD|SCR|6 }, /* RCN */ { INV, INV, STA|2, STA|3, 4, 5, IRC|SCR|6, SCR|6, IRC|SCR|8, TLD|SCR|6 }, /* RTR */ { INV, INV, STA|2, STA|3, STA|4, STA|5, STA|6, STA|6, STA|6, TLD|ZRC|STA|5 }, /* RTA */ { INV, INV, 2, 3, TLF|2, TLF|3, 6, 6, 8, TLD|SCR|6 }, /* RUC */ { INV, INV, SCJ|2, SCJ|3, SCJ|4, SCJ|5, SCJ|6, SCJ|7, SCJ|8, SCJ|9 }, /* RXJ+ */ { INV, INV, 2, 3, 4, 5, 6, 6, 8, 9 }, /* RXJ- */ { INV, INV, TLF|2, TLF|3, TLF|2, TLF|3, TLF|3, TLF|3, TLF|3, TLD|IRC|STR|5 }, /* RXR */ { INV, INV, 2, 3, 4, 5, 6, 7, 8, SER|9 }, }; enum pppcp_event_type { UP = 0, DOWN = 1, OPEN = 2, CLOSE = 3, TO_PLUS = 4, TO_MINUS = 5, RCR_PLUS = 6, RCR_MINUS = 7, RCA = 8, RCN = 9, RTR = 10, RTA = 11, RUC = 12, RXJ_PLUS = 13, RXJ_MINUS = 14, RXR = 15, }; struct pppcp_timer_data { struct pppcp_data *data; guint restart_counter; guint restart_interval; guint max_counter; guint restart_timer; }; struct pppcp_data { unsigned char state; struct pppcp_timer_data config_timer_data; struct pppcp_timer_data terminate_timer_data; guint max_failure; guint failure_counter; GAtPPP *ppp; guint8 config_identifier; guint8 terminate_identifier; guint8 reject_identifier; const guint8 *local_options; guint16 local_options_len; guint8 *peer_options; guint16 peer_options_len; gboolean send_reject; const struct pppcp_proto *driver; gpointer priv; }; static void pppcp_generate_event(struct pppcp_data *data, enum pppcp_event_type event_type, const guint8 *packet, guint len); static void pppcp_packet_free(struct pppcp_packet *packet) { g_free(pppcp_to_ppp_packet(packet)); } static struct pppcp_packet *pppcp_packet_new(struct pppcp_data *data, guint type, guint bufferlen) { struct pppcp_packet *packet; struct ppp_header *ppp_packet; guint16 packet_length = bufferlen + sizeof(*packet); ppp_packet = ppp_packet_new(packet_length, data->driver->proto); if (ppp_packet == NULL) return NULL; /* advance past protocol to add CP header information */ packet = (struct pppcp_packet *) (ppp_packet->info); packet->length = htons(packet_length); packet->code = type; return packet; } void ppp_option_iter_init(struct ppp_option_iter *iter, const struct pppcp_packet *packet) { iter->max = ntohs(packet->length) - CP_HEADER_SZ; iter->pdata = packet->data; iter->pos = 0; iter->type = 0; iter->len = 0; iter->option_data = NULL; } gboolean ppp_option_iter_next(struct ppp_option_iter *iter) { const guint8 *cur = iter->pdata + iter->pos; const guint8 *end = iter->pdata + iter->max; if (cur + 1 > end) return FALSE; if (cur + cur[1] > end) return FALSE; iter->type = cur[0]; iter->len = cur[1] - 2; iter->option_data = cur + 2; iter->pos += cur[1]; return TRUE; } guint8 ppp_option_iter_get_type(struct ppp_option_iter *iter) { return iter->type; } guint8 ppp_option_iter_get_length(struct ppp_option_iter *iter) { return iter->len; } const guint8 *ppp_option_iter_get_data(struct ppp_option_iter *iter) { return iter->option_data; } guint8 pppcp_get_code(const guint8 *data) { struct ppp_header *ppp_packet = (struct ppp_header *) data; struct pppcp_packet *packet = (struct pppcp_packet *) ppp_packet->info; return packet->code; } static gboolean pppcp_timeout(gpointer user_data) { struct pppcp_timer_data *timer_data = user_data; pppcp_trace(timer_data->data); timer_data->restart_timer = 0; if (timer_data->restart_counter > 0) pppcp_generate_event(timer_data->data, TO_PLUS, NULL, 0); else pppcp_generate_event(timer_data->data, TO_MINUS, NULL, 0); return FALSE; } static void pppcp_stop_timer(struct pppcp_timer_data *timer_data) { if (timer_data->restart_timer > 0) { g_source_remove(timer_data->restart_timer); timer_data->restart_timer = 0; } } static void pppcp_start_timer(struct pppcp_timer_data *timer_data) { pppcp_stop_timer(timer_data); timer_data->restart_timer = g_timeout_add_seconds(timer_data->restart_interval, pppcp_timeout, timer_data); } static gboolean is_first_request(struct pppcp_timer_data *timer_data) { return (timer_data->restart_counter == timer_data->max_counter); } /* actions */ /* log an illegal event, but otherwise do nothing */ static void pppcp_illegal_event(GAtPPP *ppp, guint8 state, guint8 type) { DBG(ppp, "Illegal event %d while in state %d", type, state); } static void pppcp_this_layer_up(struct pppcp_data *data) { if (data->driver->this_layer_up) data->driver->this_layer_up(data); } static void pppcp_this_layer_down(struct pppcp_data *data) { if (data->driver->this_layer_down) data->driver->this_layer_down(data); } static void pppcp_this_layer_started(struct pppcp_data *data) { if (data->driver->this_layer_started) data->driver->this_layer_started(data); } static void pppcp_this_layer_finished(struct pppcp_data *data) { pppcp_trace(data); if (data->driver->this_layer_finished) data->driver->this_layer_finished(data); } /* * set the restart counter to either max-terminate * or max-configure. The counter is decremented for * each transmission, including the first. */ static void pppcp_initialize_restart_count(struct pppcp_timer_data *timer_data) { struct pppcp_data *data = timer_data->data; pppcp_trace(data); timer_data->restart_counter = timer_data->max_counter; } /* * set restart counter to zero */ static void pppcp_zero_restart_count(struct pppcp_timer_data *timer_data) { timer_data->restart_counter = 0; } /* * TBD - generate new identifier for packet */ static guint8 new_identity(struct pppcp_data *data, guint prev_identifier) { return prev_identifier + 1; } /* * transmit a Configure-Request packet * start the restart timer * decrement the restart counter */ static void pppcp_send_configure_request(struct pppcp_data *pppcp) { struct pppcp_packet *packet; struct pppcp_timer_data *timer_data = &pppcp->config_timer_data; pppcp_trace(pppcp); packet = pppcp_packet_new(pppcp, PPPCP_CODE_TYPE_CONFIGURE_REQUEST, pppcp->local_options_len); memcpy(packet->data, pppcp->local_options, pppcp->local_options_len); /* * if this is the first request, we need a new identifier. * if this is a retransmission, leave the identifier alone. */ if (is_first_request(timer_data)) pppcp->config_identifier = new_identity(pppcp, pppcp->config_identifier); packet->identifier = pppcp->config_identifier; ppp_transmit(pppcp->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); /* start timer for retransmission */ timer_data->restart_counter--; pppcp_start_timer(timer_data); } /* * transmit a Configure-Ack packet */ static void pppcp_send_configure_ack(struct pppcp_data *pppcp, const guint8 *request) { struct pppcp_packet *packet; struct pppcp_packet *cr_req = (struct pppcp_packet *) request; guint16 len; pppcp_trace(pppcp); pppcp->failure_counter = 0; /* subtract for header. */ len = ntohs(cr_req->length) - CP_HEADER_SZ; packet = pppcp_packet_new(pppcp, PPPCP_CODE_TYPE_CONFIGURE_ACK, len); memcpy(packet->data, cr_req->data, len); packet->identifier = cr_req->identifier; ppp_transmit(pppcp->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); } /* * transmit a Configure-Nak or Configure-Reject packet */ static void pppcp_send_configure_nak(struct pppcp_data *pppcp, const guint8 *request) { struct pppcp_packet *packet; struct pppcp_packet *cr_req = (struct pppcp_packet *) request; pppcp_trace(pppcp); /* * if we have exceeded our Max-Failure counter, we simply reject all * the options. */ if (pppcp->failure_counter >= pppcp->max_failure) { guint16 len = ntohs(cr_req->length) - CP_HEADER_SZ; packet = pppcp_packet_new(pppcp, PPPCP_CODE_TYPE_CONFIGURE_REJECT, len); memcpy(packet->data, cr_req->data, len); } else { enum pppcp_code code = PPPCP_CODE_TYPE_CONFIGURE_NAK; if (pppcp->send_reject == TRUE) code = PPPCP_CODE_TYPE_CONFIGURE_REJECT; else pppcp->failure_counter++; packet = pppcp_packet_new(pppcp, code, pppcp->peer_options_len); memcpy(packet->data, pppcp->peer_options, pppcp->peer_options_len); } packet->identifier = cr_req->identifier; ppp_transmit(pppcp->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); g_free(pppcp->peer_options); pppcp->peer_options = NULL; pppcp->peer_options_len = 0; } /* * transmit a Terminate-Request packet. * start the restart timer. * decrement the restart counter */ static void pppcp_send_terminate_request(struct pppcp_data *data) { struct pppcp_packet *packet; struct pppcp_timer_data *timer_data = &data->terminate_timer_data; pppcp_trace(data); /* * the data field can be used by the sender (us). * leave this empty for now. */ packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_TERMINATE_REQUEST, 0); /* * Is this a retransmission? If so, do not change * the identifier. If not, we need a fresh identity. */ if (is_first_request(timer_data)) data->terminate_identifier = new_identity(data, data->terminate_identifier); packet->identifier = data->terminate_identifier; ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); timer_data->restart_counter--; pppcp_start_timer(timer_data); } /* * transmit a Terminate-Ack packet */ static void pppcp_send_terminate_ack(struct pppcp_data *data, const guint8 *request) { struct pppcp_packet *packet; struct pppcp_packet *pppcp_header = (struct pppcp_packet *) request; struct pppcp_timer_data *timer_data = &data->terminate_timer_data; pppcp_trace(data); packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_TERMINATE_ACK, 0); /* match identifier of the request */ packet->identifier = pppcp_header->identifier; ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet), ntohs(pppcp_header->length)); pppcp_packet_free(packet); pppcp_start_timer(timer_data); } /* * transmit a Code-Reject packet * * XXX this seg faults. */ static void pppcp_send_code_reject(struct pppcp_data *data, const guint8 *rejected_packet) { struct pppcp_packet *packet; const struct pppcp_packet *old_packet = (const struct pppcp_packet *) rejected_packet; pppcp_trace(data); packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_CODE_REJECT, ntohs(old_packet->length)); /* * Identifier must be changed for each Code-Reject sent */ packet->identifier = new_identity(data, data->reject_identifier); /* * rejected packet should be copied in, but it should be * truncated if it needs to be to comply with mtu requirement */ memcpy(packet->data, rejected_packet, ntohs(packet->length) - CP_HEADER_SZ); ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); } /* * transmit an Echo-Reply packet */ static void pppcp_send_echo_reply(struct pppcp_data *data, const guint8 *request) { struct pppcp_packet *packet; struct pppcp_packet *header = (struct pppcp_packet *) request; /* * 0 bytes for data, 4 bytes for magic number */ packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_ECHO_REPLY, 4); /* * match identifier of request */ packet->identifier = header->identifier; /* magic number will always be zero */ ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); } static void pppcp_transition_state(enum pppcp_state new_state, struct pppcp_data *data) { /* * if switching from a state where * TO events occur, to one where they * may not, shut off the timer */ switch (new_state) { case INITIAL: case STARTING: case CLOSED: case STOPPED: case OPENED: pppcp_stop_timer(&data->config_timer_data); pppcp_stop_timer(&data->terminate_timer_data); break; case CLOSING: case STOPPING: case REQSENT: case ACKRCVD: case ACKSENT: break; } data->state = new_state; } /* * send the event handler a new event to process */ static void pppcp_generate_event(struct pppcp_data *data, enum pppcp_event_type event_type, const guint8 *packet, guint len) { int actions; unsigned char new_state; if (event_type > RXR) goto error; pppcp_trace(data); actions = cp_transitions[event_type][data->state]; new_state = actions & 0xf; pppcp_trace_event(data, event_type, actions, new_state); if (actions & INV) goto error; if (actions & IRC) { struct pppcp_timer_data *timer_data; if (new_state == CLOSING || new_state == STOPPING) timer_data = &data->terminate_timer_data; else timer_data = &data->config_timer_data; pppcp_initialize_restart_count(timer_data); } else if (actions & ZRC) pppcp_zero_restart_count(&data->terminate_timer_data); if (actions & SCR) pppcp_send_configure_request(data); if (actions & SCA) pppcp_send_configure_ack(data, packet); else if (actions & SCN) pppcp_send_configure_nak(data, packet); if (actions & STR) pppcp_send_terminate_request(data); else if (actions & STA) pppcp_send_terminate_ack(data, packet); if (actions & SCJ) pppcp_send_code_reject(data, packet); if (actions & SER) pppcp_send_echo_reply(data, packet); pppcp_transition_state(new_state, data); if (actions & TLS) pppcp_this_layer_started(data); else if (actions & TLU) pppcp_this_layer_up(data); else if (actions & TLD) pppcp_this_layer_down(data); else if (actions & TLF) pppcp_this_layer_finished(data); return; error: pppcp_illegal_event(data->ppp, data->state, event_type); } void pppcp_signal_open(struct pppcp_data *data) { pppcp_generate_event(data, OPEN, NULL, 0); } void pppcp_signal_close(struct pppcp_data *data) { pppcp_generate_event(data, CLOSE, NULL, 0); } void pppcp_signal_up(struct pppcp_data *data) { pppcp_generate_event(data, UP, NULL, 0); } void pppcp_signal_down(struct pppcp_data *data) { pppcp_generate_event(data, DOWN, NULL, 0); } static guint8 pppcp_process_configure_request(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { pppcp_trace(pppcp); if (pppcp->failure_counter >= pppcp->max_failure) return RCR_MINUS; if (pppcp->driver->rcr) { enum rcr_result res; res = pppcp->driver->rcr(pppcp, packet, &pppcp->peer_options, &pppcp->peer_options_len); if (res == RCR_REJECT) { pppcp->send_reject = TRUE; return RCR_MINUS; } else if (res == RCR_NAK) { pppcp->send_reject = FALSE; return RCR_MINUS; } } return RCR_PLUS; } static guint8 pppcp_process_configure_ack(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { gint len; pppcp_trace(pppcp); len = ntohs(packet->length) - CP_HEADER_SZ; /* if identifiers don't match, we should silently discard */ if (packet->identifier != pppcp->config_identifier) { return 0; } /* * First we must sanity check that all config options acked are * equal to the config options sent and are in the same order. * If this is not the case, then silently drop the packet */ if (pppcp->local_options_len != len) return 0; if (memcmp(pppcp->local_options, packet->data, len)) return 0; /* Otherwise, apply local options */ if (pppcp->driver->rca) pppcp->driver->rca(pppcp, packet); return RCA; } static guint8 pppcp_process_configure_nak(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { pppcp_trace(pppcp); /* if identifiers don't match, we should silently discard */ if (packet->identifier != pppcp->config_identifier) return 0; if (pppcp->driver->rcn_nak) pppcp->driver->rcn_nak(pppcp, packet); return RCN; } static guint8 pppcp_process_configure_reject(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { pppcp_trace(pppcp); /* * make sure identifier matches that of last sent configure * request */ if (packet->identifier != pppcp->config_identifier) return 0; /* * check to see which options were rejected * Rejected options must be a subset of requested * options and in the same order. * * when a new configure-request is sent, we may * not request any of these options be negotiated */ if (pppcp->driver->rcn_rej) pppcp->driver->rcn_rej(pppcp, packet); return RCN; } static guint8 pppcp_process_terminate_request(struct pppcp_data *data, const struct pppcp_packet *packet) { pppcp_trace(data); return RTR; } static guint8 pppcp_process_terminate_ack(struct pppcp_data *data, const struct pppcp_packet *packet) { /* * if we wind up using the data field for anything, then * we'd want to check the identifier. * even if the identifiers don't match, we still handle * a terminate ack, as it is allowed to be unelicited */ pppcp_trace(data); return RTA; } static guint8 pppcp_process_code_reject(struct pppcp_data *data, const struct pppcp_packet *packet) { /* * determine if the code reject is catastrophic or not. * return RXJ_PLUS if this reject is acceptable, RXJ_MINUS if * it is catastrophic. * * for now we always return RXJ_MINUS. Any code * reject will be catastrophic, since we only support the * bare minimum number of codes necessary to function. */ return RXJ_MINUS; } static guint8 pppcp_process_protocol_reject(struct pppcp_data *data, const struct pppcp_packet *packet) { /* * determine if the protocol reject is catastrophic or not. * return RXJ_PLUS if this reject is acceptable, RXJ_MINUS if * it is catastrophic. * * for now we always return RXJ_MINUS. Any protocol * reject will be catastrophic, since we only support the * bare minimum number of protocols necessary to function. */ return RXJ_MINUS; } /* * For Echo-Request, Echo-Reply, and Discard-Request, we will not * bother checking the magic number of the packet, because we will * never send an echo or discard request. We can't reliably detect * loop back anyway, since we don't negotiate a magic number. */ static guint8 pppcp_process_echo_request(struct pppcp_data *data, const struct pppcp_packet *packet) { return RXR; } static guint8 pppcp_process_echo_reply(struct pppcp_data *data, const struct pppcp_packet *packet) { return 0; } static guint8 pppcp_process_discard_request(struct pppcp_data *data, const struct pppcp_packet *packet) { return 0; } static guint8 (*packet_ops[11])(struct pppcp_data *data, const struct pppcp_packet *packet) = { pppcp_process_configure_request, pppcp_process_configure_ack, pppcp_process_configure_nak, pppcp_process_configure_reject, pppcp_process_terminate_request, pppcp_process_terminate_ack, pppcp_process_code_reject, pppcp_process_protocol_reject, pppcp_process_echo_request, pppcp_process_echo_reply, pppcp_process_discard_request, }; void pppcp_send_protocol_reject(struct pppcp_data *data, const guint8 *rejected_packet, gsize len) { struct pppcp_packet *packet; pppcp_trace(data); /* * Protocol-Reject can only be sent when we are in * the OPENED state. If in any other state, silently discard. */ if (data->state != OPENED) return; /* * info should contain the old packet info, plus the 16bit * protocol number we are rejecting. */ packet = pppcp_packet_new(data, PPPCP_CODE_TYPE_PROTOCOL_REJECT, len - 2); /* * Identifier must be changed for each Protocol-Reject sent */ packet->identifier = new_identity(data, data->reject_identifier); /* * rejected packet should be copied in, but it should be * truncated if it needs to be to comply with mtu requirement */ memcpy(packet->data, rejected_packet + 2, len - 2); ppp_transmit(data->ppp, pppcp_to_ppp_packet(packet), ntohs(packet->length)); pppcp_packet_free(packet); } /* * parse the packet and determine which event this packet caused */ void pppcp_process_packet(gpointer priv, const guint8 *new_packet, gsize len) { struct pppcp_data *data = priv; const struct pppcp_packet *packet = (const struct pppcp_packet *) new_packet; guint8 event_type; guint data_len = 0; if (len < sizeof(struct pppcp_packet)) return; /* check flags to see if we support this code */ if (!(data->driver->supported_codes & (1 << packet->code))) event_type = RUC; else event_type = packet_ops[packet->code-1](data, packet); if (event_type) { data_len = ntohs(packet->length); pppcp_generate_event(data, event_type, new_packet, data_len); } } void pppcp_free(struct pppcp_data *pppcp) { pppcp_stop_timer(&pppcp->config_timer_data); pppcp_stop_timer(&pppcp->terminate_timer_data); g_free(pppcp->peer_options); g_free(pppcp); } void pppcp_set_data(struct pppcp_data *pppcp, gpointer data) { pppcp->priv = data; } gpointer pppcp_get_data(struct pppcp_data *pppcp) { return pppcp->priv; } GAtPPP *pppcp_get_ppp(struct pppcp_data *pppcp) { return pppcp->ppp; } void pppcp_set_local_options(struct pppcp_data *pppcp, const guint8 *options, guint16 len) { pppcp->local_options = options; pppcp->local_options_len = len; } struct pppcp_data *pppcp_new(GAtPPP *ppp, const struct pppcp_proto *proto, gboolean dormant, guint max_failure) { struct pppcp_data *data; data = g_try_malloc0(sizeof(struct pppcp_data)); if (data == NULL) return NULL; if (dormant) data->state = STOPPED; else data->state = INITIAL; data->config_timer_data.restart_interval = INITIAL_RESTART_TIMEOUT; data->terminate_timer_data.restart_interval = INITIAL_RESTART_TIMEOUT; data->config_timer_data.max_counter = MAX_CONFIGURE; data->terminate_timer_data.max_counter = MAX_TERMINATE; data->config_timer_data.data = data; data->terminate_timer_data.data = data; if (max_failure) data->max_failure = max_failure; else data->max_failure = MAX_FAILURE; data->ppp = ppp; data->driver = proto; return data; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatchat.c0000644000015600001650000010213412671500024021572 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include #include #include #include #include #include #include #include "ringbuffer.h" #include "gatchat.h" #include "gatio.h" /* #define WRITE_SCHEDULER_DEBUG 1 */ #define COMMAND_FLAG_EXPECT_PDU 0x1 #define COMMAND_FLAG_EXPECT_SHORT_PROMPT 0x2 struct at_chat; static void chat_wakeup_writer(struct at_chat *chat); static const char *none_prefix[] = { NULL }; struct at_command { char *cmd; char **prefixes; guint flags; guint id; guint gid; GAtResultFunc callback; GAtNotifyFunc listing; gpointer user_data; GDestroyNotify notify; }; struct at_notify_node { guint id; guint gid; GAtNotifyFunc callback; gpointer user_data; GDestroyNotify notify; gboolean destroyed; }; typedef gboolean (*node_remove_func)(struct at_notify_node *node, gpointer user_data); struct at_notify { GSList *nodes; gboolean pdu; }; struct at_chat { gint ref_count; /* Ref count */ guint next_cmd_id; /* Next command id */ guint next_notify_id; /* Next notify id */ guint next_gid; /* Next group id */ GAtIO *io; /* AT IO */ GQueue *command_queue; /* Command queue */ guint cmd_bytes_written; /* bytes written from cmd */ GHashTable *notify_list; /* List of notification reg */ GAtDisconnectFunc user_disconnect; /* user disconnect func */ gpointer user_disconnect_data; /* user disconnect data */ guint read_so_far; /* Number of bytes processed */ gboolean suspended; /* Are we suspended? */ GAtDebugFunc debugf; /* debugging output function */ gpointer debug_data; /* Data to pass to debug func */ char *pdu_notify; /* Unsolicited Resp w/ PDU */ GSList *response_lines; /* char * lines of the response */ char *wakeup; /* command sent to wakeup modem */ gint timeout_source; gdouble inactivity_time; /* Period of inactivity */ guint wakeup_timeout; /* How long to wait for resp */ GTimer *wakeup_timer; /* Keep track of elapsed time */ GAtSyntax *syntax; gboolean destroyed; /* Re-entrancy guard */ gboolean in_read_handler; /* Re-entrancy guard */ gboolean in_notify; GSList *terminator_list; /* Non-standard terminator */ guint16 terminator_blacklist; /* Blacklisted terinators */ }; struct _GAtChat { gint ref_count; struct at_chat *parent; guint group; GAtChat *slave; }; struct terminator_info { char *terminator; int len; gboolean success; }; static gboolean node_is_destroyed(struct at_notify_node *node, gpointer user) { return node->destroyed; } static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b) { const struct at_notify_node *node = a; guint id = GPOINTER_TO_UINT(b); if (node->id < id) return -1; if (node->id > id) return 1; return 0; } static void at_notify_node_destroy(gpointer data, gpointer user_data) { struct at_notify_node *node = data; if (node->notify) node->notify(node->user_data); g_free(node); } static void at_notify_destroy(gpointer user_data) { struct at_notify *notify = user_data; g_slist_foreach(notify->nodes, at_notify_node_destroy, NULL); g_slist_free(notify->nodes); g_free(notify); } static gint at_command_compare_by_id(gconstpointer a, gconstpointer b) { const struct at_command *command = a; guint id = GPOINTER_TO_UINT(b); if (command->id < id) return -1; if (command->id > id) return 1; return 0; } static gboolean at_chat_unregister_all(struct at_chat *chat, gboolean mark_only, node_remove_func func, gpointer userdata) { GHashTableIter iter; struct at_notify *notify; struct at_notify_node *node; gpointer key, value; GSList *p; GSList *c; GSList *t; if (chat->notify_list == NULL) return FALSE; g_hash_table_iter_init(&iter, chat->notify_list); while (g_hash_table_iter_next(&iter, &key, &value)) { notify = value; p = NULL; c = notify->nodes; while (c) { node = c->data; if (func(node, userdata) != TRUE) { p = c; c = c->next; continue; } if (mark_only) { node->destroyed = TRUE; p = c; c = c->next; continue; } if (p) p->next = c->next; else notify->nodes = c->next; at_notify_node_destroy(node, NULL); t = c; c = c->next; g_slist_free_1(t); } if (notify->nodes == NULL) g_hash_table_iter_remove(&iter); } return TRUE; } static struct at_command *at_command_create(guint gid, const char *cmd, const char **prefix_list, guint flags, GAtNotifyFunc listing, GAtResultFunc func, gpointer user_data, GDestroyNotify notify, gboolean wakeup) { struct at_command *c; gsize len; char **prefixes = NULL; if (prefix_list) { int num_prefixes = 0; int i; while (prefix_list[num_prefixes]) num_prefixes += 1; prefixes = g_new(char *, num_prefixes + 1); for (i = 0; i < num_prefixes; i++) prefixes[i] = strdup(prefix_list[i]); prefixes[num_prefixes] = NULL; } c = g_try_new0(struct at_command, 1); if (c == NULL) return 0; len = strlen(cmd); c->cmd = g_try_new(char, len + 2); if (c->cmd == NULL) { g_free(c); return 0; } memcpy(c->cmd, cmd, len); /* If we have embedded '\r' then this is a command expecting a prompt * from the modem. Embed Ctrl-Z at the very end automatically */ if (wakeup == FALSE) { if (strchr(cmd, '\r')) c->cmd[len] = 26; else c->cmd[len] = '\r'; len += 1; } c->cmd[len] = '\0'; c->gid = gid; c->flags = flags; c->prefixes = prefixes; c->callback = func; c->listing = listing; c->user_data = user_data; c->notify = notify; return c; } static void at_command_destroy(struct at_command *cmd) { if (cmd->notify) cmd->notify(cmd->user_data); g_strfreev(cmd->prefixes); g_free(cmd->cmd); g_free(cmd); } static void free_terminator(struct terminator_info *info) { g_free(info->terminator); info->terminator = NULL; g_free(info); info = NULL; } static void chat_cleanup(struct at_chat *chat) { struct at_command *c; /* Cleanup pending commands */ while ((c = g_queue_pop_head(chat->command_queue))) at_command_destroy(c); g_queue_free(chat->command_queue); chat->command_queue = NULL; /* Cleanup any response lines we have pending */ g_slist_foreach(chat->response_lines, (GFunc)g_free, NULL); g_slist_free(chat->response_lines); chat->response_lines = NULL; /* Cleanup registered notifications */ g_hash_table_destroy(chat->notify_list); chat->notify_list = NULL; if (chat->pdu_notify) { g_free(chat->pdu_notify); chat->pdu_notify = NULL; } if (chat->wakeup) { g_free(chat->wakeup); chat->wakeup = NULL; } if (chat->wakeup_timer) { g_timer_destroy(chat->wakeup_timer); chat->wakeup_timer = 0; } if (chat->timeout_source) { g_source_remove(chat->timeout_source); chat->timeout_source = 0; } g_at_syntax_unref(chat->syntax); chat->syntax = NULL; if (chat->terminator_list) { g_slist_foreach(chat->terminator_list, (GFunc)free_terminator, NULL); g_slist_free(chat->terminator_list); chat->terminator_list = NULL; } } static void io_disconnect(gpointer user_data) { struct at_chat *chat = user_data; chat_cleanup(chat); g_at_io_unref(chat->io); chat->io = NULL; if (chat->user_disconnect) chat->user_disconnect(chat->user_disconnect_data); } static void at_notify_call_callback(gpointer data, gpointer user_data) { struct at_notify_node *node = data; GAtResult *result = user_data; node->callback(result, node->user_data); } static gboolean at_chat_match_notify(struct at_chat *chat, char *line) { GHashTableIter iter; struct at_notify *notify; gpointer key, value; gboolean ret = FALSE; GAtResult result; g_hash_table_iter_init(&iter, chat->notify_list); result.lines = 0; result.final_or_pdu = 0; chat->in_notify = TRUE; while (g_hash_table_iter_next(&iter, &key, &value)) { notify = value; if (!g_str_has_prefix(line, key)) continue; if (notify->pdu) { chat->pdu_notify = line; if (chat->syntax->set_hint) chat->syntax->set_hint(chat->syntax, G_AT_SYNTAX_EXPECT_PDU); return TRUE; } if (result.lines == NULL) result.lines = g_slist_prepend(NULL, line); g_slist_foreach(notify->nodes, at_notify_call_callback, &result); ret = TRUE; } chat->in_notify = FALSE; if (ret) { g_slist_free(result.lines); g_free(line); at_chat_unregister_all(chat, FALSE, node_is_destroyed, NULL); } return ret; } static void at_chat_finish_command(struct at_chat *p, gboolean ok, char *final) { struct at_command *cmd = g_queue_pop_head(p->command_queue); GSList *response_lines; /* Cannot happen, but lets be paranoid */ if (cmd == NULL) return; p->cmd_bytes_written = 0; if (g_queue_peek_head(p->command_queue)) chat_wakeup_writer(p); response_lines = p->response_lines; p->response_lines = NULL; if (cmd->callback) { GAtResult result; response_lines = g_slist_reverse(response_lines); result.final_or_pdu = final; result.lines = response_lines; cmd->callback(ok, &result, cmd->user_data); } g_slist_foreach(response_lines, (GFunc)g_free, NULL); g_slist_free(response_lines); g_free(final); at_command_destroy(cmd); } static struct terminator_info terminator_table[] = { { "OK", -1, TRUE }, { "ERROR", -1, FALSE }, { "NO DIALTONE", -1, FALSE }, { "BUSY", -1, FALSE }, { "NO CARRIER", -1, FALSE }, { "CONNECT", 7, TRUE }, { "NO ANSWER", -1, FALSE }, { "+CMS ERROR:", 11, FALSE }, { "+CME ERROR:", 11, FALSE }, { "+EXT ERROR:", 11, FALSE } }; static void at_chat_add_terminator(struct at_chat *chat, char *terminator, int len, gboolean success) { struct terminator_info *info = g_new0(struct terminator_info, 1); info->terminator = g_strdup(terminator); info->len = len; info->success = success; chat->terminator_list = g_slist_prepend(chat->terminator_list, info); } static void at_chat_blacklist_terminator(struct at_chat *chat, GAtChatTerminator terminator) { chat->terminator_blacklist |= 1 << terminator; } static gboolean check_terminator(struct terminator_info *info, char *line) { if (info->len == -1 && !strcmp(line, info->terminator)) return TRUE; if (info->len > 0 && !strncmp(line, info->terminator, info->len)) return TRUE; return FALSE; } static gboolean at_chat_handle_command_response(struct at_chat *p, struct at_command *cmd, char *line) { int i; int size = sizeof(terminator_table) / sizeof(struct terminator_info); int hint; GSList *l; for (i = 0; i < size; i++) { struct terminator_info *info = &terminator_table[i]; if (check_terminator(info, line) && (p->terminator_blacklist & 1 << i) == 0) { at_chat_finish_command(p, info->success, line); return TRUE; } } for (l = p->terminator_list; l; l = l->next) { struct terminator_info *info = l->data; if (check_terminator(info, line)) { at_chat_finish_command(p, info->success, line); return TRUE; } } if (cmd->prefixes) { int n; for (n = 0; cmd->prefixes[n]; n++) if (g_str_has_prefix(line, cmd->prefixes[n])) goto out; return FALSE; } out: if (cmd->listing && (cmd->flags & COMMAND_FLAG_EXPECT_PDU)) hint = G_AT_SYNTAX_EXPECT_PDU; else hint = G_AT_SYNTAX_EXPECT_MULTILINE; if (p->syntax->set_hint) p->syntax->set_hint(p->syntax, hint); if (cmd->listing && (cmd->flags & COMMAND_FLAG_EXPECT_PDU)) { p->pdu_notify = line; return TRUE; } if (cmd->listing) { GAtResult result; result.lines = g_slist_prepend(NULL, line); result.final_or_pdu = NULL; cmd->listing(&result, cmd->user_data); g_slist_free(result.lines); g_free(line); } else p->response_lines = g_slist_prepend(p->response_lines, line); return TRUE; } static void have_line(struct at_chat *p, char *str) { /* We're not going to copy terminal */ struct at_command *cmd; if (str == NULL) return; /* Check for echo, this should not happen, but lets be paranoid */ if (!strncmp(str, "AT", 2)) goto done; cmd = g_queue_peek_head(p->command_queue); if (cmd && p->cmd_bytes_written > 0) { char c = cmd->cmd[p->cmd_bytes_written - 1]; /* We check that we have submitted a terminator, in which case * a command might have failed or completed successfully * * In the generic case, \r is at the end of the command, so we * know the entire command has been submitted. In the case of * commands like CMGS, every \r or Ctrl-Z might result in a * final response from the modem, so we check this as well. */ if ((c == '\r' || c == 26) && at_chat_handle_command_response(p, cmd, str)) return; } if (at_chat_match_notify(p, str) == TRUE) return; done: /* No matches & no commands active, ignore line */ g_free(str); } static void have_notify_pdu(struct at_chat *p, char *pdu, GAtResult *result) { GHashTableIter iter; struct at_notify *notify; char *prefix; gpointer key, value; gboolean called = FALSE; p->in_notify = TRUE; g_hash_table_iter_init(&iter, p->notify_list); while (g_hash_table_iter_next(&iter, &key, &value)) { prefix = key; notify = value; if (!g_str_has_prefix(p->pdu_notify, prefix)) continue; if (!notify->pdu) continue; g_slist_foreach(notify->nodes, at_notify_call_callback, result); called = TRUE; } p->in_notify = FALSE; if (called) at_chat_unregister_all(p, FALSE, node_is_destroyed, NULL); } static void have_pdu(struct at_chat *p, char *pdu) { struct at_command *cmd; GAtResult result; gboolean listing_pdu = FALSE; if (pdu == NULL) goto error; result.lines = g_slist_prepend(NULL, p->pdu_notify); result.final_or_pdu = pdu; cmd = g_queue_peek_head(p->command_queue); if (cmd && (cmd->flags & COMMAND_FLAG_EXPECT_PDU) && p->cmd_bytes_written > 0) { char c = cmd->cmd[p->cmd_bytes_written - 1]; if (c == '\r') listing_pdu = TRUE; } if (listing_pdu) { cmd->listing(&result, cmd->user_data); if (p->syntax->set_hint) p->syntax->set_hint(p->syntax, G_AT_SYNTAX_EXPECT_MULTILINE); } else have_notify_pdu(p, pdu, &result); g_slist_free(result.lines); error: g_free(p->pdu_notify); p->pdu_notify = NULL; if (pdu) g_free(pdu); } static char *extract_line(struct at_chat *p, struct ring_buffer *rbuf) { unsigned int wrap = ring_buffer_len_no_wrap(rbuf); unsigned int pos = 0; unsigned char *buf = ring_buffer_read_ptr(rbuf, pos); gboolean in_string = FALSE; int strip_front = 0; int line_length = 0; char *line; while (pos < p->read_so_far) { if (in_string == FALSE && (*buf == '\r' || *buf == '\n')) { if (!line_length) strip_front += 1; else break; } else { if (*buf == '"') in_string = !in_string; line_length += 1; } buf += 1; pos += 1; if (pos == wrap) buf = ring_buffer_read_ptr(rbuf, pos); } line = g_try_new(char, line_length + 1); if (line == NULL) { ring_buffer_drain(rbuf, p->read_so_far); return NULL; } ring_buffer_drain(rbuf, strip_front); ring_buffer_read(rbuf, line, line_length); ring_buffer_drain(rbuf, p->read_so_far - strip_front - line_length); line[line_length] = '\0'; return line; } static void new_bytes(struct ring_buffer *rbuf, gpointer user_data) { struct at_chat *p = user_data; unsigned int len = ring_buffer_len(rbuf); unsigned int wrap = ring_buffer_len_no_wrap(rbuf); unsigned char *buf = ring_buffer_read_ptr(rbuf, p->read_so_far); GAtSyntaxResult result; p->in_read_handler = TRUE; while (p->suspended == FALSE && (p->read_so_far < len)) { gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far); result = p->syntax->feed(p->syntax, (char *)buf, &rbytes); buf += rbytes; p->read_so_far += rbytes; if (p->read_so_far == wrap) { buf = ring_buffer_read_ptr(rbuf, p->read_so_far); wrap = len; } if (result == G_AT_SYNTAX_RESULT_UNSURE) continue; switch (result) { case G_AT_SYNTAX_RESULT_LINE: case G_AT_SYNTAX_RESULT_MULTILINE: have_line(p, extract_line(p, rbuf)); break; case G_AT_SYNTAX_RESULT_PDU: have_pdu(p, extract_line(p, rbuf)); break; case G_AT_SYNTAX_RESULT_PROMPT: chat_wakeup_writer(p); ring_buffer_drain(rbuf, p->read_so_far); break; default: ring_buffer_drain(rbuf, p->read_so_far); break; } len -= p->read_so_far; wrap -= p->read_so_far; p->read_so_far = 0; } p->in_read_handler = FALSE; if (p->destroyed) g_free(p); } static void wakeup_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct at_chat *chat = user_data; if (ok == FALSE) return; if (chat->debugf) chat->debugf("Finally woke up the modem\n", chat->debug_data); g_source_remove(chat->timeout_source); chat->timeout_source = 0; } static gboolean wakeup_no_response(gpointer user_data) { struct at_chat *chat = user_data; struct at_command *cmd = g_queue_peek_head(chat->command_queue); if (chat->debugf) chat->debugf("Wakeup got no response\n", chat->debug_data); if (cmd == NULL) return FALSE; at_chat_finish_command(chat, FALSE, NULL); cmd = at_command_create(0, chat->wakeup, none_prefix, 0, NULL, wakeup_cb, chat, NULL, TRUE); if (cmd == NULL) { chat->timeout_source = 0; return FALSE; } g_queue_push_head(chat->command_queue, cmd); return TRUE; } static gboolean can_write_data(gpointer data) { struct at_chat *chat = data; struct at_command *cmd; gsize bytes_written; gsize towrite; gsize len; char *cr; gboolean wakeup_first = FALSE; #ifdef WRITE_SCHEDULER_DEBUG int limiter; #endif /* Grab the first command off the queue and write as * much of it as we can */ cmd = g_queue_peek_head(chat->command_queue); /* For some reason command queue is empty, cancel write watcher */ if (cmd == NULL) return FALSE; len = strlen(cmd->cmd); /* For some reason write watcher fired, but we've already * written the entire command out to the io channel, * cancel write watcher */ if (chat->cmd_bytes_written >= len) return FALSE; if (chat->wakeup) { if (chat->wakeup_timer == NULL) { wakeup_first = TRUE; chat->wakeup_timer = g_timer_new(); } else if (g_timer_elapsed(chat->wakeup_timer, NULL) > chat->inactivity_time) wakeup_first = TRUE; } if (chat->cmd_bytes_written == 0 && wakeup_first == TRUE) { cmd = at_command_create(0, chat->wakeup, none_prefix, 0, NULL, wakeup_cb, chat, NULL, TRUE); if (cmd == NULL) return FALSE; g_queue_push_head(chat->command_queue, cmd); len = strlen(chat->wakeup); chat->timeout_source = g_timeout_add(chat->wakeup_timeout, wakeup_no_response, chat); } towrite = len - chat->cmd_bytes_written; cr = strchr(cmd->cmd + chat->cmd_bytes_written, '\r'); if (cr) towrite = cr - (cmd->cmd + chat->cmd_bytes_written) + 1; #ifdef WRITE_SCHEDULER_DEBUG limiter = towrite; if (limiter > 5) limiter = 5; #endif bytes_written = g_at_io_write(chat->io, cmd->cmd + chat->cmd_bytes_written, #ifdef WRITE_SCHEDULER_DEBUG limiter #else towrite #endif ); if (bytes_written == 0) return FALSE; chat->cmd_bytes_written += bytes_written; if (bytes_written < towrite) return TRUE; /* * If we're expecting a short prompt, set the hint for all lines * sent to the modem except the last */ if ((cmd->flags & COMMAND_FLAG_EXPECT_SHORT_PROMPT) && chat->cmd_bytes_written < len && chat->syntax->set_hint) chat->syntax->set_hint(chat->syntax, G_AT_SYNTAX_EXPECT_SHORT_PROMPT); /* Full command submitted, update timer */ if (chat->wakeup_timer) g_timer_start(chat->wakeup_timer); return FALSE; } static void chat_wakeup_writer(struct at_chat *chat) { g_at_io_set_write_handler(chat->io, can_write_data, chat); } static void at_chat_suspend(struct at_chat *chat) { chat->suspended = TRUE; g_at_io_set_write_handler(chat->io, NULL, NULL); g_at_io_set_read_handler(chat->io, NULL, NULL); g_at_io_set_debug(chat->io, NULL, NULL); } static void at_chat_resume(struct at_chat *chat) { chat->suspended = FALSE; if (g_at_io_get_channel(chat->io) == NULL) { io_disconnect(chat); return; } g_at_io_set_disconnect_function(chat->io, io_disconnect, chat); g_at_io_set_debug(chat->io, chat->debugf, chat->debug_data); g_at_io_set_read_handler(chat->io, new_bytes, chat); if (g_queue_get_length(chat->command_queue) > 0) chat_wakeup_writer(chat); } static void at_chat_unref(struct at_chat *chat) { gboolean is_zero; is_zero = g_atomic_int_dec_and_test(&chat->ref_count); if (is_zero == FALSE) return; if (chat->io) { at_chat_suspend(chat); g_at_io_unref(chat->io); chat->io = NULL; chat_cleanup(chat); } if (chat->in_read_handler) chat->destroyed = TRUE; else g_free(chat); } static gboolean at_chat_set_disconnect_function(struct at_chat *chat, GAtDisconnectFunc disconnect, gpointer user_data) { chat->user_disconnect = disconnect; chat->user_disconnect_data = user_data; return TRUE; } static gboolean at_chat_set_debug(struct at_chat *chat, GAtDebugFunc func, gpointer user_data) { chat->debugf = func; chat->debug_data = user_data; if (chat->io) g_at_io_set_debug(chat->io, func, user_data); return TRUE; } static gboolean at_chat_set_wakeup_command(struct at_chat *chat, const char *cmd, unsigned int timeout, unsigned int msec) { if (chat->wakeup) g_free(chat->wakeup); chat->wakeup = g_strdup(cmd); chat->inactivity_time = (gdouble)msec / 1000; chat->wakeup_timeout = timeout; return TRUE; } static guint at_chat_send_common(struct at_chat *chat, guint gid, const char *cmd, const char **prefix_list, guint flags, GAtNotifyFunc listing, GAtResultFunc func, gpointer user_data, GDestroyNotify notify) { struct at_command *c; if (chat == NULL || chat->command_queue == NULL) return 0; c = at_command_create(gid, cmd, prefix_list, flags, listing, func, user_data, notify, FALSE); if (c == NULL) return 0; c->id = chat->next_cmd_id++; g_queue_push_tail(chat->command_queue, c); if (g_queue_get_length(chat->command_queue) == 1) chat_wakeup_writer(chat); return c->id; } static struct at_notify *at_notify_create(struct at_chat *chat, const char *prefix, gboolean pdu) { struct at_notify *notify; char *key; key = g_strdup(prefix); if (key == NULL) return 0; notify = g_try_new0(struct at_notify, 1); if (notify == NULL) { g_free(key); return 0; } notify->pdu = pdu; g_hash_table_insert(chat->notify_list, key, notify); return notify; } static gboolean at_chat_cancel(struct at_chat *chat, guint group, guint id) { GList *l; struct at_command *c; if (chat->command_queue == NULL) return FALSE; l = g_queue_find_custom(chat->command_queue, GUINT_TO_POINTER(id), at_command_compare_by_id); if (l == NULL) return FALSE; c = l->data; if (c->gid != group) return FALSE; if (c == g_queue_peek_head(chat->command_queue) && chat->cmd_bytes_written > 0) { /* We can't actually remove it since it is most likely * already in progress, just null out the callback * so it won't be called */ c->callback = NULL; } else { at_command_destroy(c); g_queue_remove(chat->command_queue, c); } return TRUE; } static gboolean at_chat_cancel_group(struct at_chat *chat, guint group) { int n = 0; struct at_command *c; if (chat->command_queue == NULL) return FALSE; while ((c = g_queue_peek_nth(chat->command_queue, n)) != NULL) { if (c->id == 0 || c->gid != group) { n += 1; continue; } if (n == 0 && chat->cmd_bytes_written > 0) { c->callback = NULL; n += 1; continue; } at_command_destroy(c); g_queue_remove(chat->command_queue, c); } return TRUE; } static gpointer at_chat_get_userdata(struct at_chat *chat, guint group, guint id) { GList *l; struct at_command *c; if (chat->command_queue == NULL) return NULL; l = g_queue_find_custom(chat->command_queue, GUINT_TO_POINTER(id), at_command_compare_by_id); if (l == NULL) return NULL; c = l->data; if (c->gid != group) return NULL; return c->user_data; } static guint at_chat_register(struct at_chat *chat, guint group, const char *prefix, GAtNotifyFunc func, gboolean expect_pdu, gpointer user_data, GDestroyNotify destroy_notify) { struct at_notify *notify; struct at_notify_node *node; if (chat->notify_list == NULL) return 0; if (func == NULL) return 0; if (prefix == NULL || strlen(prefix) == 0) return 0; notify = g_hash_table_lookup(chat->notify_list, prefix); if (notify == NULL) notify = at_notify_create(chat, prefix, expect_pdu); if (notify == NULL || notify->pdu != expect_pdu) return 0; node = g_try_new0(struct at_notify_node, 1); if (node == NULL) return 0; node->id = chat->next_notify_id++; node->gid = group; node->callback = func; node->user_data = user_data; node->notify = destroy_notify; notify->nodes = g_slist_prepend(notify->nodes, node); return node->id; } static gboolean at_chat_unregister(struct at_chat *chat, gboolean mark_only, guint group, guint id) { GHashTableIter iter; struct at_notify *notify; struct at_notify_node *node; gpointer key, value; GSList *l; if (chat->notify_list == NULL) return FALSE; g_hash_table_iter_init(&iter, chat->notify_list); while (g_hash_table_iter_next(&iter, &key, &value)) { notify = value; l = g_slist_find_custom(notify->nodes, GUINT_TO_POINTER(id), at_notify_node_compare_by_id); if (l == NULL) continue; node = l->data; if (node->gid != group) return FALSE; if (mark_only) { node->destroyed = TRUE; return TRUE; } at_notify_node_destroy(node, NULL); notify->nodes = g_slist_remove(notify->nodes, node); if (notify->nodes == NULL) g_hash_table_iter_remove(&iter); return TRUE; } return FALSE; } static gboolean node_compare_by_group(struct at_notify_node *node, gpointer userdata) { guint group = GPOINTER_TO_UINT(userdata); if (node->gid == group) return TRUE; return FALSE; } static struct at_chat *create_chat(GIOChannel *channel, GIOFlags flags, GAtSyntax *syntax) { struct at_chat *chat; if (channel == NULL) return NULL; if (syntax == NULL) return NULL; chat = g_try_new0(struct at_chat, 1); if (chat == NULL) return chat; chat->ref_count = 1; chat->next_cmd_id = 1; chat->next_notify_id = 1; chat->debugf = NULL; if (flags & G_IO_FLAG_NONBLOCK) chat->io = g_at_io_new(channel); else chat->io = g_at_io_new_blocking(channel); if (chat->io == NULL) goto error; g_at_io_set_disconnect_function(chat->io, io_disconnect, chat); chat->command_queue = g_queue_new(); if (chat->command_queue == NULL) goto error; chat->notify_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, at_notify_destroy); g_at_io_set_read_handler(chat->io, new_bytes, chat); chat->syntax = g_at_syntax_ref(syntax); return chat; error: g_at_io_unref(chat->io); if (chat->command_queue) g_queue_free(chat->command_queue); if (chat->notify_list) g_hash_table_destroy(chat->notify_list); g_free(chat); return NULL; } static GAtChat *g_at_chat_new_common(GIOChannel *channel, GIOFlags flags, GAtSyntax *syntax) { GAtChat *chat; chat = g_try_new0(GAtChat, 1); if (chat == NULL) return NULL; chat->parent = create_chat(channel, flags, syntax); if (chat->parent == NULL) { g_free(chat); return NULL; } chat->group = chat->parent->next_gid++; chat->ref_count = 1; return chat; } GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax) { return g_at_chat_new_common(channel, G_IO_FLAG_NONBLOCK, syntax); } GAtChat *g_at_chat_new_blocking(GIOChannel *channel, GAtSyntax *syntax) { return g_at_chat_new_common(channel, 0, syntax); } GAtChat *g_at_chat_clone(GAtChat *clone) { GAtChat *chat; if (clone == NULL) return NULL; chat = g_try_new0(GAtChat, 1); if (chat == NULL) return NULL; chat->parent = clone->parent; chat->group = chat->parent->next_gid++; chat->ref_count = 1; g_atomic_int_inc(&chat->parent->ref_count); if (clone->slave != NULL) chat->slave = g_at_chat_clone(clone->slave); return chat; } GAtChat *g_at_chat_set_slave(GAtChat *chat, GAtChat *slave) { if (chat == NULL) return NULL; if (chat->slave != NULL) g_at_chat_unref(chat->slave); if (slave != NULL) chat->slave = g_at_chat_ref(slave); else chat->slave = NULL; return chat->slave; } GAtChat *g_at_chat_get_slave(GAtChat *chat) { if (chat == NULL) return NULL; return chat->slave; } GIOChannel *g_at_chat_get_channel(GAtChat *chat) { if (chat == NULL || chat->parent->io == NULL) return NULL; return g_at_io_get_channel(chat->parent->io); } GAtIO *g_at_chat_get_io(GAtChat *chat) { if (chat == NULL) return NULL; return chat->parent->io; } GAtChat *g_at_chat_ref(GAtChat *chat) { if (chat == NULL) return NULL; g_atomic_int_inc(&chat->ref_count); return chat; } void g_at_chat_suspend(GAtChat *chat) { if (chat == NULL) return; at_chat_suspend(chat->parent); } void g_at_chat_resume(GAtChat *chat) { if (chat == NULL) return; at_chat_resume(chat->parent); } void g_at_chat_unref(GAtChat *chat) { gboolean is_zero; if (chat == NULL) return; is_zero = g_atomic_int_dec_and_test(&chat->ref_count); if (is_zero == FALSE) return; if (chat->slave != NULL) g_at_chat_unref(chat->slave); at_chat_cancel_group(chat->parent, chat->group); g_at_chat_unregister_all(chat); at_chat_unref(chat->parent); g_free(chat); } gboolean g_at_chat_set_disconnect_function(GAtChat *chat, GAtDisconnectFunc disconnect, gpointer user_data) { if (chat == NULL || chat->group != 0) return FALSE; return at_chat_set_disconnect_function(chat->parent, disconnect, user_data); } gboolean g_at_chat_set_debug(GAtChat *chat, GAtDebugFunc func, gpointer user_data) { if (chat == NULL || chat->group != 0) return FALSE; return at_chat_set_debug(chat->parent, func, user_data); } void g_at_chat_add_terminator(GAtChat *chat, char *terminator, int len, gboolean success) { if (chat == NULL || chat->group != 0) return; at_chat_add_terminator(chat->parent, terminator, len, success); } void g_at_chat_blacklist_terminator(GAtChat *chat, GAtChatTerminator terminator) { if (chat == NULL || chat->group != 0) return; at_chat_blacklist_terminator(chat->parent, terminator); } gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd, unsigned int timeout, unsigned int msec) { if (chat == NULL || chat->group != 0) return FALSE; return at_chat_set_wakeup_command(chat->parent, cmd, timeout, msec); } guint g_at_chat_send(GAtChat *chat, const char *cmd, const char **prefix_list, GAtResultFunc func, gpointer user_data, GDestroyNotify notify) { return at_chat_send_common(chat->parent, chat->group, cmd, prefix_list, 0, NULL, func, user_data, notify); } guint g_at_chat_send_listing(GAtChat *chat, const char *cmd, const char **prefix_list, GAtNotifyFunc listing, GAtResultFunc func, gpointer user_data, GDestroyNotify notify) { if (listing == NULL) return 0; return at_chat_send_common(chat->parent, chat->group, cmd, prefix_list, 0, listing, func, user_data, notify); } guint g_at_chat_send_pdu_listing(GAtChat *chat, const char *cmd, const char **prefix_list, GAtNotifyFunc listing, GAtResultFunc func, gpointer user_data, GDestroyNotify notify) { if (listing == NULL) return 0; return at_chat_send_common(chat->parent, chat->group, cmd, prefix_list, COMMAND_FLAG_EXPECT_PDU, listing, func, user_data, notify); } guint g_at_chat_send_and_expect_short_prompt(GAtChat *chat, const char *cmd, const char **prefix_list, GAtResultFunc func, gpointer user_data, GDestroyNotify notify) { return at_chat_send_common(chat->parent, chat->group, cmd, prefix_list, COMMAND_FLAG_EXPECT_SHORT_PROMPT, NULL, func, user_data, notify); } gboolean g_at_chat_cancel(GAtChat *chat, guint id) { /* We use id 0 for wakeup commands */ if (chat == NULL || id == 0) return FALSE; return at_chat_cancel(chat->parent, chat->group, id); } gboolean g_at_chat_cancel_all(GAtChat *chat) { if (chat == NULL) return FALSE; return at_chat_cancel_group(chat->parent, chat->group); } gpointer g_at_chat_get_userdata(GAtChat *chat, guint id) { if (chat == NULL) return NULL; return at_chat_get_userdata(chat->parent, chat->group, id); } guint g_at_chat_register(GAtChat *chat, const char *prefix, GAtNotifyFunc func, gboolean expect_pdu, gpointer user_data, GDestroyNotify destroy_notify) { if (chat == NULL) return 0; return at_chat_register(chat->parent, chat->group, prefix, func, expect_pdu, user_data, destroy_notify); } gboolean g_at_chat_unregister(GAtChat *chat, guint id) { if (chat == NULL) return FALSE; return at_chat_unregister(chat->parent, chat->parent->in_notify, chat->group, id); } gboolean g_at_chat_unregister_all(GAtChat *chat) { if (chat == NULL) return FALSE; return at_chat_unregister_all(chat->parent, chat->parent->in_notify, node_compare_by_group, GUINT_TO_POINTER(chat->group)); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/crc-ccitt.c0000644000015600001650000000565012671500024022037 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include "crc-ccitt.h" const guint16 crc_ccitt_table[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatsyntax.c0000644000015600001650000002234212671500024022203 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include #include "gatsyntax.h" enum GSMV1_STATE { GSMV1_STATE_IDLE = 0, GSMV1_STATE_INITIAL_CR, GSMV1_STATE_INITIAL_LF, GSMV1_STATE_RESPONSE, GSMV1_STATE_RESPONSE_STRING, GSMV1_STATE_TERMINATOR_CR, GSMV1_STATE_GUESS_MULTILINE_RESPONSE, GSMV1_STATE_MULTILINE_RESPONSE, GSMV1_STATE_MULTILINE_TERMINATOR_CR, GSMV1_STATE_PDU_CHECK_EXTRA_CR, GSMV1_STATE_PDU_CHECK_EXTRA_LF, GSMV1_STATE_PDU, GSMV1_STATE_PDU_CR, GSMV1_STATE_PROMPT, GSMV1_STATE_ECHO, GSMV1_STATE_PPP_DATA, GSMV1_STATE_SHORT_PROMPT, GSMV1_STATE_SHORT_PROMPT_CR, }; enum GSM_PERMISSIVE_STATE { GSM_PERMISSIVE_STATE_IDLE = 0, GSM_PERMISSIVE_STATE_RESPONSE, GSM_PERMISSIVE_STATE_RESPONSE_STRING, GSM_PERMISSIVE_STATE_GUESS_PDU, GSM_PERMISSIVE_STATE_PDU, GSM_PERMISSIVE_STATE_PROMPT, GSM_PERMISSIVE_STATE_GUESS_SHORT_PROMPT, GSM_PERMISSIVE_STATE_SHORT_PROMPT, }; static void gsmv1_hint(GAtSyntax *syntax, GAtSyntaxExpectHint hint) { switch (hint) { case G_AT_SYNTAX_EXPECT_PDU: syntax->state = GSMV1_STATE_PDU_CHECK_EXTRA_CR; break; case G_AT_SYNTAX_EXPECT_MULTILINE: syntax->state = GSMV1_STATE_GUESS_MULTILINE_RESPONSE; break; case G_AT_SYNTAX_EXPECT_SHORT_PROMPT: syntax->state = GSMV1_STATE_SHORT_PROMPT; break; default: break; }; } static GAtSyntaxResult gsmv1_feed(GAtSyntax *syntax, const char *bytes, gsize *len) { gsize i = 0; GAtSyntaxResult res = G_AT_SYNTAX_RESULT_UNSURE; while (i < *len) { char byte = bytes[i]; switch (syntax->state) { case GSMV1_STATE_IDLE: if (byte == '\r') syntax->state = GSMV1_STATE_INITIAL_CR; else if (byte == '~') syntax->state = GSMV1_STATE_PPP_DATA; else syntax->state = GSMV1_STATE_ECHO; break; case GSMV1_STATE_INITIAL_CR: if (byte == '\n') syntax->state = GSMV1_STATE_INITIAL_LF; else if (byte == '\r') { syntax->state = GSMV1_STATE_IDLE; return G_AT_SYNTAX_RESULT_UNRECOGNIZED; } else syntax->state = GSMV1_STATE_ECHO; break; case GSMV1_STATE_INITIAL_LF: if (byte == '\r') syntax->state = GSMV1_STATE_TERMINATOR_CR; else if (byte == '>') syntax->state = GSMV1_STATE_PROMPT; else if (byte == '"') syntax->state = GSMV1_STATE_RESPONSE_STRING; else syntax->state = GSMV1_STATE_RESPONSE; break; case GSMV1_STATE_RESPONSE: if (byte == '\r') syntax->state = GSMV1_STATE_TERMINATOR_CR; else if (byte == '"') syntax->state = GSMV1_STATE_RESPONSE_STRING; break; case GSMV1_STATE_RESPONSE_STRING: if (byte == '"') syntax->state = GSMV1_STATE_RESPONSE; break; case GSMV1_STATE_TERMINATOR_CR: syntax->state = GSMV1_STATE_IDLE; if (byte == '\n') { i += 1; res = G_AT_SYNTAX_RESULT_LINE; } else res = G_AT_SYNTAX_RESULT_UNRECOGNIZED; goto out; case GSMV1_STATE_GUESS_MULTILINE_RESPONSE: if (byte == '\r') syntax->state = GSMV1_STATE_INITIAL_CR; else syntax->state = GSMV1_STATE_MULTILINE_RESPONSE; break; case GSMV1_STATE_MULTILINE_RESPONSE: if (byte == '\r') syntax->state = GSMV1_STATE_MULTILINE_TERMINATOR_CR; break; case GSMV1_STATE_MULTILINE_TERMINATOR_CR: syntax->state = GSMV1_STATE_IDLE; if (byte == '\n') { i += 1; res = G_AT_SYNTAX_RESULT_MULTILINE; } else res = G_AT_SYNTAX_RESULT_UNRECOGNIZED; goto out; /* Some 27.007 compliant modems still get this wrong. They * insert an extra CRLF between the command and he PDU, * in effect making them two separate lines. We try to * handle this case gracefully */ case GSMV1_STATE_PDU_CHECK_EXTRA_CR: if (byte == '\r') syntax->state = GSMV1_STATE_PDU_CHECK_EXTRA_LF; else syntax->state = GSMV1_STATE_PDU; break; case GSMV1_STATE_PDU_CHECK_EXTRA_LF: res = G_AT_SYNTAX_RESULT_UNRECOGNIZED; syntax->state = GSMV1_STATE_PDU; if (byte == '\n') i += 1; goto out; case GSMV1_STATE_PDU: if (byte == '\r') syntax->state = GSMV1_STATE_PDU_CR; break; case GSMV1_STATE_PDU_CR: syntax->state = GSMV1_STATE_IDLE; if (byte == '\n') { i += 1; res = G_AT_SYNTAX_RESULT_PDU; } else res = G_AT_SYNTAX_RESULT_UNRECOGNIZED; goto out; case GSMV1_STATE_PROMPT: if (byte == ' ') { syntax->state = GSMV1_STATE_IDLE; i += 1; res = G_AT_SYNTAX_RESULT_PROMPT; goto out; } syntax->state = GSMV1_STATE_RESPONSE; return G_AT_SYNTAX_RESULT_UNSURE; case GSMV1_STATE_ECHO: /* This handles the case of echo of the PDU terminated * by CtrlZ character */ if (byte == 26 || byte == '\r') { syntax->state = GSMV1_STATE_IDLE; res = G_AT_SYNTAX_RESULT_UNRECOGNIZED; i += 1; goto out; } break; case GSMV1_STATE_PPP_DATA: if (byte == '~') { syntax->state = GSMV1_STATE_IDLE; res = G_AT_SYNTAX_RESULT_UNRECOGNIZED; i += 1; goto out; } break; case GSMV1_STATE_SHORT_PROMPT: if (byte == '\r') syntax->state = GSMV1_STATE_SHORT_PROMPT_CR; else syntax->state = GSMV1_STATE_ECHO; break; case GSMV1_STATE_SHORT_PROMPT_CR: if (byte == '\n') { syntax->state = GSMV1_STATE_IDLE; i += 1; res = G_AT_SYNTAX_RESULT_PROMPT; goto out; } syntax->state = GSMV1_STATE_RESPONSE; return G_AT_SYNTAX_RESULT_UNSURE; default: break; }; i += 1; } out: *len = i; return res; } static void gsm_permissive_hint(GAtSyntax *syntax, GAtSyntaxExpectHint hint) { if (hint == G_AT_SYNTAX_EXPECT_PDU) syntax->state = GSM_PERMISSIVE_STATE_GUESS_PDU; else if (hint == G_AT_SYNTAX_EXPECT_SHORT_PROMPT) syntax->state = GSM_PERMISSIVE_STATE_GUESS_SHORT_PROMPT; } static GAtSyntaxResult gsm_permissive_feed(GAtSyntax *syntax, const char *bytes, gsize *len) { gsize i = 0; GAtSyntaxResult res = G_AT_SYNTAX_RESULT_UNSURE; while (i < *len) { char byte = bytes[i]; switch (syntax->state) { case GSM_PERMISSIVE_STATE_IDLE: if (byte == '\r' || byte == '\n') /* ignore */; else if (byte == '>') syntax->state = GSM_PERMISSIVE_STATE_PROMPT; else if (byte == '"') syntax->state = GSM_PERMISSIVE_STATE_RESPONSE_STRING; else syntax->state = GSM_PERMISSIVE_STATE_RESPONSE; break; case GSM_PERMISSIVE_STATE_RESPONSE: if (byte == '\r') { syntax->state = GSM_PERMISSIVE_STATE_IDLE; i += 1; res = G_AT_SYNTAX_RESULT_LINE; goto out; } else if (byte == '"') syntax->state = GSM_PERMISSIVE_STATE_RESPONSE_STRING; break; case GSM_PERMISSIVE_STATE_RESPONSE_STRING: if (byte == '"') syntax->state = GSM_PERMISSIVE_STATE_RESPONSE; break; case GSM_PERMISSIVE_STATE_GUESS_PDU: if (byte != '\r' && byte != '\n') syntax->state = GSM_PERMISSIVE_STATE_PDU; break; case GSM_PERMISSIVE_STATE_PDU: if (byte == '\r') { syntax->state = GSM_PERMISSIVE_STATE_IDLE; i += 1; res = G_AT_SYNTAX_RESULT_PDU; goto out; } break; case GSM_PERMISSIVE_STATE_PROMPT: if (byte == ' ') { syntax->state = GSM_PERMISSIVE_STATE_IDLE; i += 1; res = G_AT_SYNTAX_RESULT_PROMPT; goto out; } syntax->state = GSM_PERMISSIVE_STATE_RESPONSE; return G_AT_SYNTAX_RESULT_UNSURE; case GSM_PERMISSIVE_STATE_GUESS_SHORT_PROMPT: if (byte == '\n') /* ignore */; else if (byte == '\r') syntax->state = GSM_PERMISSIVE_STATE_SHORT_PROMPT; else syntax->state = GSM_PERMISSIVE_STATE_RESPONSE; break; case GSM_PERMISSIVE_STATE_SHORT_PROMPT: if (byte == '\n') { syntax->state = GSM_PERMISSIVE_STATE_IDLE; i += 1; res = G_AT_SYNTAX_RESULT_PROMPT; goto out; } syntax->state = GSM_PERMISSIVE_STATE_RESPONSE; return G_AT_SYNTAX_RESULT_UNSURE; default: break; }; i += 1; } out: *len = i; return res; } GAtSyntax *g_at_syntax_new_full(GAtSyntaxFeedFunc feed, GAtSyntaxSetHintFunc hint, int initial_state) { GAtSyntax *syntax; syntax = g_new0(GAtSyntax, 1); syntax->feed = feed; syntax->set_hint = hint; syntax->state = initial_state; syntax->ref_count = 1; return syntax; } GAtSyntax *g_at_syntax_new_gsmv1(void) { return g_at_syntax_new_full(gsmv1_feed, gsmv1_hint, GSMV1_STATE_IDLE); } GAtSyntax *g_at_syntax_new_gsm_permissive(void) { return g_at_syntax_new_full(gsm_permissive_feed, gsm_permissive_hint, GSM_PERMISSIVE_STATE_IDLE); } GAtSyntax *g_at_syntax_ref(GAtSyntax *syntax) { if (syntax == NULL) return NULL; g_atomic_int_inc(&syntax->ref_count); return syntax; } void g_at_syntax_unref(GAtSyntax *syntax) { gboolean is_zero; if (syntax == NULL) return; is_zero = g_atomic_int_dec_and_test(&syntax->ref_count); if (is_zero) g_free(syntax); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/ppp_lcp.c0000644000015600001650000002226012671500024021615 0ustar pbuserpbgroup00000000000000/* * * PPP library with GLib integration * * Copyright (C) 2009-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 #endif #include #include #include #include #include #include #include #include "gatppp.h" #include "ppp.h" #define LCP_SUPPORTED_CODES ((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_NAK) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_REJECT) | \ (1 << PPPCP_CODE_TYPE_TERMINATE_REQUEST) | \ (1 << PPPCP_CODE_TYPE_TERMINATE_ACK) | \ (1 << PPPCP_CODE_TYPE_CODE_REJECT) | \ (1 << PPPCP_CODE_TYPE_PROTOCOL_REJECT) | \ (1 << PPPCP_CODE_TYPE_ECHO_REQUEST) | \ (1 << PPPCP_CODE_TYPE_ECHO_REPLY) | \ (1 << PPPCP_CODE_TYPE_DISCARD_REQUEST)) enum lcp_options { RESERVED = 0, MRU = 1, ACCM = 2, AUTH_PROTO = 3, QUAL_PROTO = 4, MAGIC_NUMBER = 5, DEPRECATED_QUAL_PROTO = 6, PFC = 7, ACFC = 8, }; /* Maximum size of all options, we only ever request ACCM, MRU, ACFC and PFC */ #define MAX_CONFIG_OPTION_SIZE 14 #define REQ_OPTION_ACCM 0x1 #define REQ_OPTION_MRU 0x2 #define REQ_OPTION_ACFC 0x4 #define REQ_OPTION_PFC 0x8 struct lcp_data { guint8 options[MAX_CONFIG_OPTION_SIZE]; guint16 options_len; guint8 req_options; guint32 accm; /* ACCM value */ guint16 mru; }; static void lcp_generate_config_options(struct lcp_data *lcp) { guint16 len = 0; if (lcp->req_options & REQ_OPTION_ACCM) { guint32 accm; accm = htonl(lcp->accm); lcp->options[len] = ACCM; lcp->options[len + 1] = 6; memcpy(lcp->options + len + 2, &accm, sizeof(accm)); len += 6; } if (lcp->req_options & REQ_OPTION_MRU) { guint16 mru; mru = htons(lcp->mru); lcp->options[len] = MRU; lcp->options[len + 1] = 4; memcpy(lcp->options + len + 2, &mru, sizeof(mru)); len += 4; } if (lcp->req_options & REQ_OPTION_ACFC) { lcp->options[len] = ACFC; lcp->options[len + 1] = 2; len += 2; } if (lcp->req_options & REQ_OPTION_PFC) { lcp->options[len] = PFC; lcp->options[len + 1] = 2; len += 2; } lcp->options_len = len; } static void lcp_reset_config_options(struct lcp_data *lcp) { /* Using the default ACCM */ lcp_generate_config_options(lcp); } /* * signal the Up event to the NCP */ static void lcp_up(struct pppcp_data *pppcp) { ppp_lcp_up_notify(pppcp_get_ppp(pppcp)); } /* * signal the Down event to the NCP */ static void lcp_down(struct pppcp_data *pppcp) { struct lcp_data *lcp = pppcp_get_data(pppcp); lcp_reset_config_options(lcp); pppcp_set_local_options(pppcp, lcp->options, lcp->options_len); ppp_lcp_down_notify(pppcp_get_ppp(pppcp)); } /* * Indicate that the lower layer is not needed * Should trigger Down event */ static void lcp_finished(struct pppcp_data *pppcp) { ppp_lcp_finished_notify(pppcp_get_ppp(pppcp)); } static void lcp_rca(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { struct ppp_option_iter iter; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { const guint8 *data = ppp_option_iter_get_data(&iter); switch (ppp_option_iter_get_type(&iter)) { case ACCM: /* * RFC1662 Section 7.1 * The Configuration Option is used to inform the peer * which control characters MUST remain mapped when * the peer sends them. */ ppp_set_recv_accm(pppcp_get_ppp(pppcp), get_host_long(data)); break; default: break; } } } static void lcp_rcn_nak(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { struct lcp_data *lcp = pppcp_get_data(pppcp); struct ppp_option_iter iter; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { const guint8 *data = ppp_option_iter_get_data(&iter); switch (ppp_option_iter_get_type(&iter)) { case MRU: { guint16 mru = get_host_short(data); if (mru < 2048) { lcp->mru = get_host_short(data); lcp->req_options |= REQ_OPTION_MRU; } break; } default: break; } } lcp_generate_config_options(lcp); pppcp_set_local_options(pppcp, lcp->options, lcp->options_len); } static void lcp_rcn_rej(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { } static enum rcr_result lcp_rcr(struct pppcp_data *pppcp, const struct pppcp_packet *packet, guint8 **new_options, guint16 *new_len) { GAtPPP *ppp = pppcp_get_ppp(pppcp); struct ppp_option_iter iter; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { switch (ppp_option_iter_get_type(&iter)) { case AUTH_PROTO: { const guint8 *option_data = ppp_option_iter_get_data(&iter); guint16 proto = get_host_short(option_data); guint8 method = option_data[2]; guint8 *option; switch (g_at_ppp_get_auth_method(ppp)) { case G_AT_PPP_AUTH_METHOD_CHAP: if (proto == CHAP_PROTOCOL && method == MD5) break; /* * Try to suggest CHAP/MD5. * Just reject if we run out of memory. */ option = g_try_malloc0(5); if (option == NULL) return RCR_REJECT; option[0] = AUTH_PROTO; option[1] = 5; put_network_short(&option[2], CHAP_PROTOCOL); option[4] = MD5; *new_options = option; *new_len = 5; return RCR_NAK; case G_AT_PPP_AUTH_METHOD_PAP: if (proto == PAP_PROTOCOL) break; /* * Try to suggest PAP. * Just reject if we run out of memory. */ option = g_try_malloc0(4); if (option == NULL) return RCR_REJECT; option[0] = AUTH_PROTO; option[1] = 4; put_network_short(&option[2], PAP_PROTOCOL); *new_options = option; *new_len = 4; return RCR_NAK; } break; } case ACCM: case PFC: case ACFC: case MRU: break; case MAGIC_NUMBER: { guint32 magic = get_host_long(ppp_option_iter_get_data(&iter)); if (magic == 0) return RCR_REJECT; break; } default: return RCR_REJECT; } } /* All options were found acceptable, apply them here and return */ ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { switch (ppp_option_iter_get_type(&iter)) { case ACCM: /* * RFC1662 Section 7.1 * The Configuration Option is used to inform the peer * which control characters MUST remain mapped when * the peer sends them. */ ppp_set_xmit_accm(ppp, get_host_long(ppp_option_iter_get_data(&iter))); break; case AUTH_PROTO: ppp_set_auth(ppp, ppp_option_iter_get_data(&iter)); break; case MRU: ppp_set_mtu(ppp, ppp_option_iter_get_data(&iter)); break; case MAGIC_NUMBER: /* don't care */ break; case PFC: { struct lcp_data *lcp = pppcp_get_data(pppcp); if (lcp->req_options & REQ_OPTION_PFC) ppp_set_xmit_pfc(ppp, TRUE); break; } case ACFC: { struct lcp_data *lcp = pppcp_get_data(pppcp); if (lcp->req_options & REQ_OPTION_ACFC) ppp_set_xmit_acfc(ppp, TRUE); break; } } } return RCR_ACCEPT; } struct pppcp_proto lcp_proto = { .proto = LCP_PROTOCOL, .name = "lcp", .supported_codes = LCP_SUPPORTED_CODES, .this_layer_up = lcp_up, .this_layer_down = lcp_down, .this_layer_finished = lcp_finished, .rca = lcp_rca, .rcn_nak = lcp_rcn_nak, .rcn_rej = lcp_rcn_rej, .rcr = lcp_rcr, }; void lcp_free(struct pppcp_data *pppcp) { struct lcp_data *lcp = pppcp_get_data(pppcp); g_free(lcp); pppcp_free(pppcp); } struct pppcp_data *lcp_new(GAtPPP *ppp, gboolean is_server) { struct pppcp_data *pppcp; struct lcp_data *lcp; lcp = g_try_new0(struct lcp_data, 1); if (lcp == NULL) return NULL; pppcp = pppcp_new(ppp, &lcp_proto, is_server, 0); if (pppcp == NULL) { g_free(lcp); return NULL; } pppcp_set_data(pppcp, lcp); lcp_reset_config_options(lcp); pppcp_set_local_options(pppcp, lcp->options, lcp->options_len); return pppcp; } void lcp_set_acfc_enabled(struct pppcp_data *pppcp, gboolean enabled) { struct lcp_data *lcp = pppcp_get_data(pppcp); guint8 old = lcp->req_options; if (enabled == TRUE) lcp->req_options |= REQ_OPTION_ACFC; else lcp->req_options &= ~REQ_OPTION_ACFC; if (lcp->req_options == old) return; lcp_generate_config_options(lcp); pppcp_set_local_options(pppcp, lcp->options, lcp->options_len); } void lcp_set_pfc_enabled(struct pppcp_data *pppcp, gboolean enabled) { struct lcp_data *lcp = pppcp_get_data(pppcp); guint8 old = lcp->req_options; if (enabled == TRUE) lcp->req_options |= REQ_OPTION_PFC; else lcp->req_options &= ~REQ_OPTION_PFC; if (lcp->req_options == old) return; lcp_generate_config_options(lcp); pppcp_set_local_options(pppcp, lcp->options, lcp->options_len); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatmux.h0000644000015600001650000000631512671500024021475 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2009 Trolltech ASA. * * 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 * */ #ifndef __GATMUX_H #define __GATMUX_H #ifdef __cplusplus extern "C" { #endif #include "gatchat.h" struct _GAtMux; typedef struct _GAtMux GAtMux; typedef struct _GAtMuxDriver GAtMuxDriver; typedef enum _GAtMuxChannelStatus GAtMuxChannelStatus; typedef void (*GAtMuxSetupFunc)(GAtMux *mux, gpointer user_data); enum _GAtMuxDlcStatus { G_AT_MUX_DLC_STATUS_RTC = 0x02, G_AT_MUX_DLC_STATUS_RTR = 0x04, G_AT_MUX_DLC_STATUS_IC = 0x08, G_AT_MUX_DLC_STATUS_DV = 0x80, }; struct _GAtMuxDriver { void (*remove)(GAtMux *mux); gboolean (*startup)(GAtMux *mux); gboolean (*shutdown)(GAtMux *mux); gboolean (*open_dlc)(GAtMux *mux, guint8 dlc); gboolean (*close_dlc)(GAtMux *mux, guint8 dlc); void (*set_status)(GAtMux *mux, guint8 dlc, guint8 status); void (*write)(GAtMux *mux, guint8 dlc, const void *data, int towrite); int (*feed_data)(GAtMux *mux, void *data, int len); }; GAtMux *g_at_mux_new(GIOChannel *channel, const GAtMuxDriver *driver); GAtMux *g_at_mux_new_gsm0710_basic(GIOChannel *channel, int framesize); GAtMux *g_at_mux_new_gsm0710_advanced(GIOChannel *channel, int framesize); GAtMux *g_at_mux_ref(GAtMux *mux); void g_at_mux_unref(GAtMux *mux); gboolean g_at_mux_start(GAtMux *mux); gboolean g_at_mux_shutdown(GAtMux *mux); gboolean g_at_mux_set_disconnect_function(GAtMux *mux, GAtDisconnectFunc disconnect, gpointer user_data); gboolean g_at_mux_set_debug(GAtMux *mux, GAtDebugFunc func, gpointer user_data); GIOChannel *g_at_mux_create_channel(GAtMux *mux); /*! * Multiplexer driver integration functions */ void g_at_mux_set_dlc_status(GAtMux *mux, guint8 dlc, int status); void g_at_mux_feed_dlc_data(GAtMux *mux, guint8 dlc, const void *data, int tofeed); int g_at_mux_raw_write(GAtMux *mux, const void *data, int towrite); void g_at_mux_set_data(GAtMux *mux, void *data); void *g_at_mux_get_data(GAtMux *mux); /*! * Uses the passed in GAtChat to setup a GSM 07.10 style multiplexer on the * channel used by GAtChat. This function queries the multiplexer capability, * preferring advanced mode over basic. If supported, the best available * multiplexer mode is entered. If this is successful, the chat is * shutdown and unrefed. The chat's channel will be transferred to the * resulting multiplexer object. */ gboolean g_at_mux_setup_gsm0710(GAtChat *chat, GAtMuxSetupFunc notify, gpointer user_data, GDestroyNotify destroy); #ifdef __cplusplus } #endif #endif /* __GATMUX_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatio.h0000644000015600001650000000362012671500024021267 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 * */ #ifndef __GATIO_H #define __GATIO_H #ifdef __cplusplus extern "C" { #endif #include "gat.h" struct _GAtIO; typedef struct _GAtIO GAtIO; struct ring_buffer; typedef void (*GAtIOReadFunc)(struct ring_buffer *buffer, gpointer user_data); typedef gboolean (*GAtIOWriteFunc)(gpointer user_data); GAtIO *g_at_io_new(GIOChannel *channel); GAtIO *g_at_io_new_blocking(GIOChannel *channel); GIOChannel *g_at_io_get_channel(GAtIO *io); GAtIO *g_at_io_ref(GAtIO *io); void g_at_io_unref(GAtIO *io); gboolean g_at_io_set_read_handler(GAtIO *io, GAtIOReadFunc read_handler, gpointer user_data); gboolean g_at_io_set_write_handler(GAtIO *io, GAtIOWriteFunc write_handler, gpointer user_data); void g_at_io_set_write_done(GAtIO *io, GAtDisconnectFunc func, gpointer user_data); void g_at_io_drain_ring_buffer(GAtIO *io, guint len); gsize g_at_io_write(GAtIO *io, const gchar *data, gsize count); gboolean g_at_io_set_disconnect_function(GAtIO *io, GAtDisconnectFunc disconnect, gpointer user_data); gboolean g_at_io_set_debug(GAtIO *io, GAtDebugFunc func, gpointer user_data); #ifdef __cplusplus } #endif #endif /* __GATIO_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/ppp_net.c0000644000015600001650000001206512671500024021627 0ustar pbuserpbgroup00000000000000/* * * PPP library with GLib integration * * Copyright (C) 2009-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 #endif #include #include #include #include #include #include #include #include #include #include "gatutil.h" #include "gatppp.h" #include "ppp.h" #define MAX_PACKET 1500 struct ppp_net { GAtPPP *ppp; char *if_name; GIOChannel *channel; guint watch; gint mtu; struct ppp_header *ppp_packet; }; gboolean ppp_net_set_mtu(struct ppp_net *net, guint16 mtu) { struct ifreq ifr; int sk, err; if (net == NULL || mtu > MAX_PACKET) return FALSE; net->mtu = mtu; sk = socket(AF_INET, SOCK_DGRAM, 0); if (sk < 0) return FALSE; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, net->if_name, IFNAMSIZ - 1); ifr.ifr_mtu = mtu; err = ioctl(sk, SIOCSIFMTU, (void *) &ifr); close(sk); if (err < 0) return FALSE; return TRUE; } void ppp_net_process_packet(struct ppp_net *net, const guint8 *packet, gsize plen) { GIOStatus status; gsize bytes_written; guint16 len; if (plen < 4) return; /* find the length of the packet to transmit */ len = get_host_short(&packet[2]); status = g_io_channel_write_chars(net->channel, (gchar *) packet, MIN(len, plen), &bytes_written, NULL); if (status != G_IO_STATUS_NORMAL) return; } /* * packets received by the tun interface need to be written to * the modem. So, just read a packet, write out to the modem */ static gboolean ppp_net_callback(GIOChannel *channel, GIOCondition cond, gpointer userdata) { struct ppp_net *net = (struct ppp_net *) userdata; GIOStatus status; gsize bytes_read; gchar *buf = (gchar *) net->ppp_packet->info; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) return FALSE; if (cond & G_IO_IN) { /* leave space to add PPP protocol field */ status = g_io_channel_read_chars(channel, buf, net->mtu, &bytes_read, NULL); if (bytes_read > 0) ppp_transmit(net->ppp, (guint8 *) net->ppp_packet, bytes_read); if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) return FALSE; } return TRUE; } const char *ppp_net_get_interface(struct ppp_net *net) { return net->if_name; } struct ppp_net *ppp_net_new(GAtPPP *ppp, int fd) { struct ppp_net *net; GIOChannel *channel = NULL; struct ifreq ifr; int err; net = g_try_new0(struct ppp_net, 1); if (net == NULL) goto badalloc; net->ppp_packet = ppp_packet_new(MAX_PACKET, PPP_IP_PROTO); if (net->ppp_packet == NULL) goto error; /* * If the fd value is still the default one, * open the tun interface and configure it. */ memset(&ifr, 0, sizeof(ifr)); if (fd < 0) { /* open a tun interface */ fd = open("/dev/net/tun", O_RDWR); if (fd < 0) { ppp_debug(ppp, "Couldn't open tun device. " "Do you run oFono as root and do you " "have the TUN module loaded?"); goto error; } ifr.ifr_flags = IFF_TUN | IFF_NO_PI; strcpy(ifr.ifr_name, "ppp%d"); err = ioctl(fd, TUNSETIFF, (void *) &ifr); if (err < 0) goto error; } else { err = ioctl(fd, TUNGETIFF, (void *) &ifr); if (err < 0) goto error; } net->if_name = strdup(ifr.ifr_name); /* create a channel for reading and writing to this interface */ channel = g_io_channel_unix_new(fd); if (channel == NULL) goto error; if (!g_at_util_setup_io(channel, 0)) goto error; g_io_channel_set_buffered(channel, FALSE); net->channel = channel; net->watch = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, ppp_net_callback, net); net->ppp = ppp; net->mtu = MAX_PACKET; return net; error: if (channel) g_io_channel_unref(channel); g_free(net->if_name); g_free(net->ppp_packet); g_free(net); badalloc: if (fd >= 0) close(fd); return NULL; } void ppp_net_free(struct ppp_net *net) { if (net->watch) { g_source_remove(net->watch); net->watch = 0; } g_io_channel_unref(net->channel); g_free(net->ppp_packet); g_free(net->if_name); g_free(net); } void ppp_net_suspend_interface(struct ppp_net *net) { if (net == NULL || net->channel == NULL) return; if (net->watch == 0) return; g_source_remove(net->watch); net->watch = 0; } void ppp_net_resume_interface(struct ppp_net *net) { if (net == NULL || net->channel == NULL) return; net->watch = g_io_add_watch(net->channel, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, ppp_net_callback, net); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatmux.c0000644000015600001650000006505212671500024021473 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. * Copyright (C) 2009 Trolltech ASA. * * 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 #endif #include #include #include #include #include #include #include "ringbuffer.h" #include "gatmux.h" #include "gsm0710.h" static const char *cmux_prefix[] = { "+CMUX:", NULL }; static const char *none_prefix[] = { NULL }; typedef struct _GAtMuxChannel GAtMuxChannel; typedef struct _GAtMuxWatch GAtMuxWatch; typedef void (*GAtMuxWriteFrame)(GAtMux *mux, guint8 dlc, guint8 control, const guint8 *data, int len); /* While 63 channels are theoretically possible, channel 62 and 63 is reserved * by 27.010 for use as the beginning of frame and end of frame flags. * Refer to Section 5.6 in 27.007 */ #define MAX_CHANNELS 61 #define BITMAP_SIZE 8 #define MUX_CHANNEL_BUFFER_SIZE 4096 #define MUX_BUFFER_SIZE 4096 struct _GAtMuxChannel { GIOChannel channel; GAtMux *mux; GIOCondition condition; struct ring_buffer *buffer; GSList *sources; gboolean throttled; guint dlc; }; struct _GAtMuxWatch { GSource source; GIOChannel *channel; GIOCondition condition; }; struct _GAtMux { gint ref_count; /* Ref count */ guint read_watch; /* GSource read id, 0 if none */ guint write_watch; /* GSource write id, 0 if none */ GIOChannel *channel; /* main serial channel */ GAtDisconnectFunc user_disconnect; /* user disconnect func */ gpointer user_disconnect_data; /* user disconnect data */ GAtDebugFunc debugf; /* debugging output function */ gpointer debug_data; /* Data to pass to debug func */ GAtMuxChannel *dlcs[MAX_CHANNELS]; /* DLCs opened by the MUX */ guint8 newdata[BITMAP_SIZE]; /* Channels that got new data */ const GAtMuxDriver *driver; /* Driver functions */ void *driver_data; /* Driver data */ char buf[MUX_BUFFER_SIZE]; /* Buffer on the main mux */ int buf_used; /* Bytes of buf being used */ gboolean shutdown; }; struct mux_setup_data { GAtChat *chat; GAtMuxSetupFunc func; gpointer user; GDestroyNotify destroy; guint mode; guint frame_size; }; static inline void debug(GAtMux *mux, const char *format, ...) { char str[256]; va_list ap; if (mux->debugf == NULL) return; va_start(ap, format); if (vsnprintf(str, sizeof(str), format, ap) > 0) mux->debugf(str, mux->debug_data); va_end(ap); } static void dispatch_sources(GAtMuxChannel *channel, GIOCondition condition) { GAtMuxWatch *source; GSList *c; GSList *p; GSList *t; p = NULL; c = channel->sources; while (c) { gboolean destroy = FALSE; source = c->data; debug(channel->mux, "checking source: %p", source); if (condition & source->condition) { gpointer user_data = NULL; GSourceFunc callback = NULL; GSourceCallbackFuncs *cb_funcs; gpointer cb_data; gboolean (*dispatch) (GSource *, GSourceFunc, gpointer); debug(channel->mux, "dispatching source: %p", source); dispatch = source->source.source_funcs->dispatch; cb_funcs = source->source.callback_funcs; cb_data = source->source.callback_data; if (cb_funcs) cb_funcs->ref(cb_data); if (cb_funcs) cb_funcs->get(cb_data, (GSource *) source, &callback, &user_data); destroy = !dispatch((GSource *) source, callback, user_data); if (cb_funcs) cb_funcs->unref(cb_data); } if (destroy) { debug(channel->mux, "removing source: %p", source); g_source_destroy((GSource *) source); if (p) p->next = c->next; else channel->sources = c->next; t = c; c = c->next; g_slist_free_1(t); } else { p = c; c = c->next; } } } static gboolean received_data(GIOChannel *channel, GIOCondition cond, gpointer data) { GAtMux *mux = data; int i; GIOStatus status; gsize bytes_read; if (cond & G_IO_NVAL) return FALSE; debug(mux, "received data"); bytes_read = 0; status = g_io_channel_read_chars(mux->channel, mux->buf + mux->buf_used, sizeof(mux->buf) - mux->buf_used, &bytes_read, NULL); mux->buf_used += bytes_read; if (bytes_read > 0 && mux->driver->feed_data) { int nread; memset(mux->newdata, 0, BITMAP_SIZE); nread = mux->driver->feed_data(mux, mux->buf, mux->buf_used); mux->buf_used -= nread; if (mux->buf_used > 0) memmove(mux->buf, mux->buf + nread, mux->buf_used); for (i = 1; i <= MAX_CHANNELS; i++) { int offset = i / 8; int bit = i % 8; if (!(mux->newdata[offset] & (1 << bit))) continue; debug(mux, "dispatching sources for channel: %p", mux->dlcs[i-1]); dispatch_sources(mux->dlcs[i-1], G_IO_IN); } } if (cond & (G_IO_HUP | G_IO_ERR)) return FALSE; if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) return FALSE; if (mux->buf_used == sizeof(mux->buf)) return FALSE; return TRUE; } static void write_watcher_destroy_notify(gpointer user_data) { GAtMux *mux = user_data; mux->write_watch = 0; } static gboolean can_write_data(GIOChannel *chan, GIOCondition cond, gpointer data) { GAtMux *mux = data; int dlc; if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) return FALSE; debug(mux, "can write data"); for (dlc = 0; dlc < MAX_CHANNELS; dlc += 1) { GAtMuxChannel *channel = mux->dlcs[dlc]; if (channel == NULL) continue; debug(mux, "checking channel for write: %p", channel); if (channel->throttled) continue; debug(mux, "dispatching write sources: %p", channel); dispatch_sources(channel, G_IO_OUT); } for (dlc = 0; dlc < MAX_CHANNELS; dlc += 1) { GAtMuxChannel *channel = mux->dlcs[dlc]; GSList *l; GAtMuxWatch *source; if (channel == NULL) continue; if (channel->throttled) continue; for (l = channel->sources; l; l = l->next) { source = l->data; if (source->condition & G_IO_OUT) return TRUE; } } return FALSE; } static void wakeup_writer(GAtMux *mux) { if (mux->write_watch != 0) return; debug(mux, "waking up writer"); mux->write_watch = g_io_add_watch_full(mux->channel, G_PRIORITY_DEFAULT, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, can_write_data, mux, write_watcher_destroy_notify); } int g_at_mux_raw_write(GAtMux *mux, const void *data, int towrite) { gssize count = towrite; gsize bytes_written; g_io_channel_write_chars(mux->channel, (gchar *) data, count, &bytes_written, NULL); return bytes_written; } void g_at_mux_feed_dlc_data(GAtMux *mux, guint8 dlc, const void *data, int tofeed) { GAtMuxChannel *channel; int written; int offset; int bit; debug(mux, "deliver_data: dlc: %hu", dlc); if (dlc < 1 || dlc > MAX_CHANNELS) return; channel = mux->dlcs[dlc-1]; if (channel == NULL) return; written = ring_buffer_write(channel->buffer, data, tofeed); if (written < 0) return; offset = dlc / 8; bit = dlc % 8; mux->newdata[offset] |= 1 << bit; channel->condition |= G_IO_IN; } void g_at_mux_set_dlc_status(GAtMux *mux, guint8 dlc, int status) { GAtMuxChannel *channel; debug(mux, "got status %d, for channel %hu", status, dlc); if (dlc < 1 || dlc > MAX_CHANNELS) return; channel = mux->dlcs[dlc-1]; if (channel == NULL) return; if (status & G_AT_MUX_DLC_STATUS_RTR) { GSList *l; mux->dlcs[dlc-1]->throttled = FALSE; debug(mux, "setting throttled to FALSE"); for (l = mux->dlcs[dlc-1]->sources; l; l = l->next) { GAtMuxWatch *source = l->data; if (source->condition & G_IO_OUT) { wakeup_writer(mux); break; } } } else mux->dlcs[dlc-1]->throttled = TRUE; } void g_at_mux_set_data(GAtMux *mux, void *data) { if (mux == NULL) return; mux->driver_data = data; } void *g_at_mux_get_data(GAtMux *mux) { if (mux == NULL) return NULL; return mux->driver_data; } static gboolean watch_check(GSource *source) { return FALSE; } static gboolean watch_prepare(GSource *source, gint *timeout) { *timeout = -1; return FALSE; } static gboolean watch_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { GIOFunc func = (GIOFunc) callback; GAtMuxWatch *watch = (GAtMuxWatch *) source; GAtMuxChannel *channel = (GAtMuxChannel *) watch->channel; if (func == NULL) return FALSE; return func(watch->channel, channel->condition & watch->condition, user_data); } static void watch_finalize(GSource *source) { GAtMuxWatch *watch = (GAtMuxWatch *) source; g_io_channel_unref(watch->channel); } static GSourceFuncs watch_funcs = { watch_prepare, watch_check, watch_dispatch, watch_finalize }; static GIOStatus channel_read(GIOChannel *channel, gchar *buf, gsize count, gsize *bytes_read, GError **err) { GAtMuxChannel *mux_channel = (GAtMuxChannel *) channel; unsigned int avail = ring_buffer_len_no_wrap(mux_channel->buffer); if (avail > count) avail = count; *bytes_read = ring_buffer_read(mux_channel->buffer, buf, avail); if (*bytes_read == 0) return G_IO_STATUS_AGAIN; return G_IO_STATUS_NORMAL; } static GIOStatus channel_write(GIOChannel *channel, const gchar *buf, gsize count, gsize *bytes_written, GError **err) { GAtMuxChannel *mux_channel = (GAtMuxChannel *) channel; GAtMux *mux = mux_channel->mux; if (mux->driver->write) mux->driver->write(mux, mux_channel->dlc, buf, count); *bytes_written = count; return G_IO_STATUS_NORMAL; } static GIOStatus channel_seek(GIOChannel *channel, gint64 offset, GSeekType type, GError **err) { return G_IO_STATUS_NORMAL; } static GIOStatus channel_close(GIOChannel *channel, GError **err) { GAtMuxChannel *mux_channel = (GAtMuxChannel *) channel; GAtMux *mux = mux_channel->mux; debug(mux, "closing channel: %d", mux_channel->dlc); dispatch_sources(mux_channel, G_IO_NVAL); if (mux->driver->close_dlc) mux->driver->close_dlc(mux, mux_channel->dlc); mux->dlcs[mux_channel->dlc - 1] = NULL; return G_IO_STATUS_NORMAL; } static void channel_free(GIOChannel *channel) { GAtMuxChannel *mux_channel = (GAtMuxChannel *) channel; ring_buffer_free(mux_channel->buffer); g_free(channel); } static GSource *channel_create_watch(GIOChannel *channel, GIOCondition condition) { GSource *source; GAtMuxWatch *watch; GAtMuxChannel *dlc = (GAtMuxChannel *) channel; GAtMux *mux = dlc->mux; source = g_source_new(&watch_funcs, sizeof(GAtMuxWatch)); watch = (GAtMuxWatch *) source; watch->channel = channel; g_io_channel_ref(channel); watch->condition = condition; if ((watch->condition & G_IO_OUT) && dlc->throttled == FALSE) wakeup_writer(mux); debug(mux, "creating source: %p, channel: %p, writer: %d, reader: %d", watch, channel, condition & G_IO_OUT, condition & G_IO_IN); dlc->sources = g_slist_prepend(dlc->sources, watch); return source; } static GIOStatus channel_set_flags(GIOChannel *channel, GIOFlags flags, GError **err) { return G_IO_STATUS_NORMAL; } static GIOFlags channel_get_flags(GIOChannel *channel) { GIOFlags flags = 0; return flags; } static GIOFuncs channel_funcs = { channel_read, channel_write, channel_seek, channel_close, channel_create_watch, channel_free, channel_set_flags, channel_get_flags, }; GAtMux *g_at_mux_new(GIOChannel *channel, const GAtMuxDriver *driver) { GAtMux *mux; if (channel == NULL) return NULL; mux = g_try_new0(GAtMux, 1); if (mux == NULL) return NULL; mux->ref_count = 1; mux->driver = driver; mux->shutdown = TRUE; mux->channel = channel; g_io_channel_ref(channel); g_io_channel_set_close_on_unref(channel, TRUE); return mux; } GAtMux *g_at_mux_ref(GAtMux *mux) { if (mux == NULL) return NULL; g_atomic_int_inc(&mux->ref_count); return mux; } void g_at_mux_unref(GAtMux *mux) { if (mux == NULL) return; if (g_atomic_int_dec_and_test(&mux->ref_count)) { g_at_mux_shutdown(mux); g_io_channel_unref(mux->channel); if (mux->driver->remove) mux->driver->remove(mux); g_free(mux); } } gboolean g_at_mux_start(GAtMux *mux) { if (mux->channel == NULL) return FALSE; if (mux->driver->startup == NULL) return FALSE; if (mux->driver->startup(mux) == FALSE) return FALSE; mux->read_watch = g_io_add_watch_full(mux->channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, received_data, mux, NULL); mux->shutdown = FALSE; return TRUE; } gboolean g_at_mux_shutdown(GAtMux *mux) { int i; if (mux->shutdown == TRUE) return FALSE; if (mux->channel == NULL) return FALSE; if (mux->read_watch > 0) g_source_remove(mux->read_watch); for (i = 0; i < MAX_CHANNELS; i++) { if (mux->dlcs[i] == NULL) continue; channel_close((GIOChannel *) mux->dlcs[i], NULL); } if (mux->driver->shutdown) mux->driver->shutdown(mux); mux->shutdown = TRUE; return TRUE; } gboolean g_at_mux_set_disconnect_function(GAtMux *mux, GAtDisconnectFunc disconnect, gpointer user_data) { if (mux == NULL) return FALSE; mux->user_disconnect = disconnect; mux->user_disconnect_data = user_data; return TRUE; } gboolean g_at_mux_set_debug(GAtMux *mux, GAtDebugFunc func, gpointer user_data) { if (mux == NULL) return FALSE; mux->debugf = func; mux->debug_data = user_data; return TRUE; } GIOChannel *g_at_mux_create_channel(GAtMux *mux) { GAtMuxChannel *mux_channel; GIOChannel *channel; int i; for (i = 0; i < MAX_CHANNELS; i++) { if (mux->dlcs[i] == NULL) break; } if (i == MAX_CHANNELS) return NULL; mux_channel = g_try_new0(GAtMuxChannel, 1); if (mux_channel == NULL) return NULL; if (mux->driver->open_dlc) mux->driver->open_dlc(mux, i+1); channel = (GIOChannel *) mux_channel; g_io_channel_init(channel); channel->close_on_unref = TRUE; channel->funcs = &channel_funcs; channel->is_seekable = FALSE; channel->is_readable = TRUE; channel->is_writeable = TRUE; channel->do_encode = FALSE; mux_channel->mux = mux; mux_channel->dlc = i+1; mux_channel->buffer = ring_buffer_new(MUX_CHANNEL_BUFFER_SIZE); mux_channel->throttled = FALSE; mux->dlcs[i] = mux_channel; debug(mux, "created channel %p, dlc: %d", channel, i+1); return channel; } static void msd_free(gpointer user_data) { struct mux_setup_data *msd = user_data; if (msd->chat) g_at_chat_unref(msd->chat); g_free(msd); } static void mux_setup_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct mux_setup_data *msd = user_data; GIOFlags flags; GIOChannel *channel; GAtMux *mux = NULL; if (!ok) goto error; channel = g_at_chat_get_channel(msd->chat); channel = g_io_channel_ref(channel); g_at_chat_unref(msd->chat); msd->chat = NULL; flags = g_io_channel_get_flags(channel) | G_IO_FLAG_NONBLOCK; g_io_channel_set_flags(channel, flags, NULL); g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); if (msd->mode == 0) mux = g_at_mux_new_gsm0710_basic(channel, msd->frame_size); else mux = g_at_mux_new_gsm0710_advanced(channel, msd->frame_size); g_io_channel_unref(channel); error: msd->func(mux, msd->user); if (msd->destroy) msd->destroy(msd->user); } static void mux_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct mux_setup_data *msd = user_data; struct mux_setup_data *nmsd; GAtResultIter iter; int min, max; int speed; char buf[64]; /* CMUX query not supported, abort */ if (!ok) goto error; g_at_result_iter_init(&iter, result); if (!g_at_result_iter_next(&iter, "+CMUX:")) goto error; /* Mode */ if (!g_at_result_iter_open_list(&iter)) goto error; if (!g_at_result_iter_next_range(&iter, &min, &max)) goto error; if (!g_at_result_iter_close_list(&iter)) goto error; if (min <= 1 && 1 <= max) msd->mode = 1; else if (min <= 0 && 0 <= max) msd->mode = 0; else goto error; /* Subset */ if (!g_at_result_iter_open_list(&iter)) goto error; if (!g_at_result_iter_next_range(&iter, &min, &max)) goto error; if (!g_at_result_iter_close_list(&iter)) goto error; if (min > 0) goto error; /* Speed, pick highest */ if (g_at_result_iter_open_list(&iter)) { if (!g_at_result_iter_next_range(&iter, &min, &max)) goto error; if (!g_at_result_iter_close_list(&iter)) goto error; speed = max; } else { if (!g_at_result_iter_skip_next(&iter)) goto error; /* not available/used */ speed = -1; } /* Frame size, pick defaults */ if (!g_at_result_iter_open_list(&iter)) goto error; if (!g_at_result_iter_next_range(&iter, &min, &max)) goto error; if (!g_at_result_iter_close_list(&iter)) goto error; if (msd->mode == 0) { if (min > 31 || max < 31) goto error; msd->frame_size = 31; } else if (msd->mode == 1) { if (min > 64 || max < 64) goto error; msd->frame_size = 64; } else goto error; nmsd = g_memdup(msd, sizeof(struct mux_setup_data)); g_at_chat_ref(nmsd->chat); if (speed < 0) sprintf(buf, "AT+CMUX=%u,0,,%u", msd->mode, msd->frame_size); else sprintf(buf, "AT+CMUX=%u,0,%u,%u", msd->mode, speed, msd->frame_size); if (g_at_chat_send(msd->chat, buf, none_prefix, mux_setup_cb, nmsd, msd_free) > 0) return; msd_free(nmsd); error: msd->func(NULL, msd->user); if (msd->destroy) msd->destroy(msd->user); } gboolean g_at_mux_setup_gsm0710(GAtChat *chat, GAtMuxSetupFunc notify, gpointer user_data, GDestroyNotify destroy) { struct mux_setup_data *msd; if (chat == NULL) return FALSE; if (notify == NULL) return FALSE; msd = g_new0(struct mux_setup_data, 1); msd->chat = g_at_chat_ref(chat); msd->func = notify; msd->user = user_data; msd->destroy = destroy; if (g_at_chat_send(chat, "AT+CMUX=?", cmux_prefix, mux_query_cb, msd, msd_free) > 0) return TRUE; if (msd) msd_free(msd); return FALSE; } #define GSM0710_BUFFER_SIZE 4096 struct gsm0710_data { int frame_size; }; /* Process an incoming GSM 07.10 packet */ static gboolean gsm0710_packet(GAtMux *mux, int dlc, guint8 control, const unsigned char *data, int len, GAtMuxWriteFrame write_frame) { if (control == 0xEF || control == 0x03) { if (dlc >= 1 && dlc <= 63) { g_at_mux_feed_dlc_data(mux, dlc, data, len); return TRUE; } if (dlc == 0) { /* An embedded command or response on channel 0 */ if (len >= 2 && data[0] == GSM0710_STATUS_SET) { return gsm0710_packet(mux, dlc, GSM0710_STATUS_ACK, data + 2, len - 2, write_frame); } else if (len >= 2 && data[0] == 0x43) { /* Test command from other side - send the same bytes back */ unsigned char *resp = alloca(len); memcpy(resp, data, len); resp[0] = 0x41; /* Clear the C/R bit in the response */ write_frame(mux, 0, GSM0710_DATA, resp, len); } } } else if (control == GSM0710_STATUS_ACK && dlc == 0) { unsigned char resp[33]; /* Status change message */ if (len >= 2) { /* Handle status changes on other channels */ dlc = ((data[0] & 0xFC) >> 2); if (dlc >= 1 && dlc <= 63) g_at_mux_set_dlc_status(mux, dlc, data[1]); } /* Send the response to the status change request to ACK it */ debug(mux, "received status line signal, sending response"); if (len > 31) len = 31; resp[0] = GSM0710_STATUS_ACK; resp[1] = ((len << 1) | 0x01); memcpy(resp + 2, data, len); write_frame(mux, 0, GSM0710_DATA, resp, len + 2); } return TRUE; } static void gsm0710_basic_write_frame(GAtMux *mux, guint8 dlc, guint8 control, const guint8 *data, int towrite) { struct gsm0710_data *gd = g_at_mux_get_data(mux); guint8 *frame = alloca(gd->frame_size + 7); int frame_size; frame_size = gsm0710_basic_fill_frame(frame, dlc, control, data, towrite); g_at_mux_raw_write(mux, frame, frame_size); } #define COMPOSE_STATUS_FRAME(data, dlc, status) \ guint8 data[4]; \ data[0] = GSM0710_STATUS_SET; \ data[1] = 0x03; \ data[2] = ((dlc << 2) | 0x03); \ data[3] = status static void gsm0710_basic_remove(GAtMux *mux) { struct gsm0710_data *gd = g_at_mux_get_data(mux); g_free(gd); g_at_mux_set_data(mux, NULL); } static gboolean gsm0710_basic_startup(GAtMux *mux) { guint8 frame[6]; int frame_size; frame_size = gsm0710_basic_fill_frame(frame, 0, GSM0710_OPEN_CHANNEL, NULL, 0); g_at_mux_raw_write(mux, frame, frame_size); return TRUE; } static gboolean gsm0710_basic_shutdown(GAtMux *mux) { guint8 frame[6]; int frame_size; frame_size = gsm0710_basic_fill_frame(frame, 0, GSM0710_CLOSE_CHANNEL, NULL, 0); g_at_mux_raw_write(mux, frame, frame_size); return TRUE; } static gboolean gsm0710_basic_open_dlc(GAtMux *mux, guint8 dlc) { guint8 frame[6]; int frame_size; frame_size = gsm0710_basic_fill_frame(frame, dlc, GSM0710_OPEN_CHANNEL, NULL, 0); g_at_mux_raw_write(mux, frame, frame_size); return TRUE; } static gboolean gsm0710_basic_close_dlc(GAtMux *mux, guint8 dlc) { guint8 frame[6]; int frame_size; frame_size = gsm0710_basic_fill_frame(frame, dlc, GSM0710_CLOSE_CHANNEL, NULL, 0); g_at_mux_raw_write(mux, frame, frame_size); return TRUE; } static int gsm0710_basic_feed_data(GAtMux *mux, void *data, int len) { int total = 0; int nread; guint8 dlc; guint8 ctrl; guint8 *frame; int frame_len; do { frame = NULL; nread = gsm0710_basic_extract_frame(data, len, &dlc, &ctrl, &frame, &frame_len); total += nread; data += nread; len -= nread; if (frame == NULL) break; gsm0710_packet(mux, dlc, ctrl, frame, frame_len, gsm0710_basic_write_frame); } while (nread > 0); return total; } static void gsm0710_basic_set_status(GAtMux *mux, guint8 dlc, guint8 status) { struct gsm0710_data *gd = g_at_mux_get_data(mux); guint8 *frame = alloca(gd->frame_size + 7); int frame_size; COMPOSE_STATUS_FRAME(data, dlc, status); frame_size = gsm0710_basic_fill_frame(frame, 0, GSM0710_DATA, data, 4); g_at_mux_raw_write(mux, frame, frame_size); } static void gsm0710_basic_write(GAtMux *mux, guint8 dlc, const void *data, int towrite) { struct gsm0710_data *gd = g_at_mux_get_data(mux); guint8 *frame = alloca(gd->frame_size + 7); int max; int frame_size; while (towrite > 0) { max = MIN(towrite, gd->frame_size); frame_size = gsm0710_basic_fill_frame(frame, dlc, GSM0710_DATA, data, max); g_at_mux_raw_write(mux, frame, frame_size); data = data + max; towrite -= max; } } static GAtMuxDriver gsm0710_basic_driver = { .remove = gsm0710_basic_remove, .startup = gsm0710_basic_startup, .shutdown = gsm0710_basic_shutdown, .open_dlc = gsm0710_basic_open_dlc, .close_dlc = gsm0710_basic_close_dlc, .feed_data = gsm0710_basic_feed_data, .set_status = gsm0710_basic_set_status, .write = gsm0710_basic_write, }; GAtMux *g_at_mux_new_gsm0710_basic(GIOChannel *channel, int frame_size) { GAtMux *mux; struct gsm0710_data *gd; mux = g_at_mux_new(channel, &gsm0710_basic_driver); if (mux == NULL) return NULL; gd = g_new0(struct gsm0710_data, 1); gd->frame_size = frame_size; g_at_mux_set_data(mux, gd); return mux; } static void gsm0710_advanced_write_frame(GAtMux *mux, guint8 dlc, guint8 control, const guint8 *data, int towrite) { struct gsm0710_data *gd = g_at_mux_get_data(mux); guint8 *frame = alloca(gd->frame_size * 2 + 7); int frame_size; frame_size = gsm0710_advanced_fill_frame(frame, dlc, control, data, towrite); g_at_mux_raw_write(mux, frame, frame_size); } static void gsm0710_advanced_remove(GAtMux *mux) { struct gsm0710_data *gd = g_at_mux_get_data(mux); g_free(gd); g_at_mux_set_data(mux, NULL); } static gboolean gsm0710_advanced_startup(GAtMux *mux) { guint8 frame[8]; /* Account for escapes */ int frame_size; frame_size = gsm0710_advanced_fill_frame(frame, 0, GSM0710_OPEN_CHANNEL, NULL, 0); g_at_mux_raw_write(mux, frame, frame_size); return TRUE; } static gboolean gsm0710_advanced_shutdown(GAtMux *mux) { guint8 frame[8]; /* Account for escapes */ int frame_size; frame_size = gsm0710_advanced_fill_frame(frame, 0, GSM0710_CLOSE_CHANNEL, NULL, 0); g_at_mux_raw_write(mux, frame, frame_size); return TRUE; } static gboolean gsm0710_advanced_open_dlc(GAtMux *mux, guint8 dlc) { guint8 frame[8]; /* Account for escapes */ int frame_size; frame_size = gsm0710_advanced_fill_frame(frame, dlc, GSM0710_OPEN_CHANNEL, NULL, 0); g_at_mux_raw_write(mux, frame, frame_size); return TRUE; } static gboolean gsm0710_advanced_close_dlc(GAtMux *mux, guint8 dlc) { guint8 frame[8]; /* Account for escapes */ int frame_size; frame_size = gsm0710_advanced_fill_frame(frame, dlc, GSM0710_CLOSE_CHANNEL, NULL, 0); g_at_mux_raw_write(mux, frame, frame_size); return TRUE; } static int gsm0710_advanced_feed_data(GAtMux *mux, void *data, int len) { int total = 0; int nread; guint8 dlc; guint8 ctrl; guint8 *frame; int frame_len; do { frame = NULL; nread = gsm0710_advanced_extract_frame(data, len, &dlc, &ctrl, &frame, &frame_len); total += nread; data += nread; len -= nread; if (frame == NULL) break; gsm0710_packet(mux, dlc, ctrl, frame, frame_len, gsm0710_advanced_write_frame); } while (nread > 0); return total; } static void gsm0710_advanced_set_status(GAtMux *mux, guint8 dlc, guint8 status) { struct gsm0710_data *gd = g_at_mux_get_data(mux); guint8 *frame = alloca(gd->frame_size * 2 + 7); int frame_size; COMPOSE_STATUS_FRAME(data, dlc, status); frame_size = gsm0710_advanced_fill_frame(frame, 0, GSM0710_DATA, data, 4); g_at_mux_raw_write(mux, frame, frame_size); } static void gsm0710_advanced_write(GAtMux *mux, guint8 dlc, const void *data, int towrite) { struct gsm0710_data *gd = g_at_mux_get_data(mux); guint8 *frame = alloca(gd->frame_size * 2 + 7); int max; int frame_size; while (towrite > 0) { max = MIN(towrite, gd->frame_size); frame_size = gsm0710_advanced_fill_frame(frame, dlc, GSM0710_DATA, data, max); g_at_mux_raw_write(mux, frame, frame_size); data = data + max; towrite -= max; } } static GAtMuxDriver gsm0710_advanced_driver = { .remove = gsm0710_advanced_remove, .startup = gsm0710_advanced_startup, .shutdown = gsm0710_advanced_shutdown, .open_dlc = gsm0710_advanced_open_dlc, .close_dlc = gsm0710_advanced_close_dlc, .feed_data = gsm0710_advanced_feed_data, .set_status = gsm0710_advanced_set_status, .write = gsm0710_advanced_write, }; GAtMux *g_at_mux_new_gsm0710_advanced(GIOChannel *channel, int frame_size) { GAtMux *mux; struct gsm0710_data *gd; mux = g_at_mux_new(channel, &gsm0710_advanced_driver); if (mux == NULL) return NULL; gd = g_new0(struct gsm0710_data, 1); gd->frame_size = frame_size; g_at_mux_set_data(mux, gd); return mux; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/test-server.c0000644000015600001650000006471712671500024022460 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gatserver.h" #include "gatppp.h" #include "ringbuffer.h" #define DEFAULT_TCP_PORT 12346 #define DEFAULT_SOCK_PATH "./server_sock" #define IFCONFIG_PATH "/sbin/ifconfig" static gboolean data_mode = FALSE; static int modem_mode = 0; static int modem_creg = 0; static int modem_cgreg = 0; static int network_status = 4; static int network_attach = 0; struct sock_server{ int server_sock; }; static GMainLoop *mainloop; static GAtServer *main_server; static GAtPPP *ppp; unsigned int server_watch; static gboolean server_cleanup(void) { if (server_watch) g_source_remove(server_watch); if (ppp) { g_at_ppp_unref(ppp); ppp = NULL; } g_at_server_unref(main_server); main_server = NULL; unlink(DEFAULT_SOCK_PATH); g_main_loop_quit(mainloop); return FALSE; } static void server_debug(const char *str, void *data) { g_print("%s: %s\n", (char *) data, str); } static gboolean execute(const char *cmd) { int status; status = system(cmd); if (status < 0) { g_print("Failed to execute command: %s\n", strerror(errno)); return FALSE; } return TRUE; } static void ppp_connect(const char *iface, const char *local, const char *peer, const char *dns1, const char *dns2, gpointer user) { char buf[512]; g_print("Network Device: %s\n", iface); g_print("IP Address: %s\n", local); g_print("Peer IP Address: %s\n", peer); g_print("Primary DNS Server: %s\n", dns1); g_print("Secondary DNS Server: %s\n", dns2); snprintf(buf, sizeof(buf), "%s %s up", IFCONFIG_PATH, iface); execute(buf); snprintf(buf, sizeof(buf), "%s %s %s pointopoint %s", IFCONFIG_PATH, iface, local, peer); execute(buf); snprintf(buf, sizeof(buf), "echo 1 > /proc/sys/net/ipv4/ip_forward"); execute(buf); } static void ppp_disconnect(GAtPPPDisconnectReason reason, gpointer user) { GAtServer *server = user; g_print("PPP Link down: %d\n", reason); g_at_ppp_unref(ppp); ppp = NULL; if (reason == G_AT_PPP_REASON_LINK_DEAD) { g_at_server_unref(server); server = NULL; return; } g_at_server_resume(server); g_at_server_set_debug(server, server_debug, "Server"); g_at_server_send_final(server, G_AT_SERVER_RESULT_NO_CARRIER); data_mode = FALSE; } static void open_ppp(gpointer user) { GAtIO *io = g_at_server_get_io(main_server); g_at_server_suspend(main_server); g_at_ppp_listen(ppp, io); } static gboolean setup_ppp(GAtServer *server) { /* open ppp */ ppp = g_at_ppp_server_new("192.168.1.1"); if (ppp == NULL) return FALSE; g_at_ppp_set_debug(ppp, server_debug, "PPP"); g_at_ppp_set_credentials(ppp, "", ""); /* set connect and disconnect callbacks */ g_at_ppp_set_connect_function(ppp, ppp_connect, server); g_at_ppp_set_disconnect_function(ppp, ppp_disconnect, server); g_at_ppp_set_server_info(ppp, "192.168.1.2", "10.10.10.10", "10.10.10.11"); return TRUE; } static void cgmi_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: g_at_server_send_info(server, "oFono", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); }; } static void cgmm_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: g_at_server_send_info(server, "oFono pre-1.0", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); }; } static void cgmr_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[256]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: sprintf(buf, "oFono pre-1.0 version: %s", VERSION); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); }; } static void cgsn_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: g_at_server_send_info(server, "123456789", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); }; } static gboolean send_ok(gpointer user) { GAtServer *server = user; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); return FALSE; } static void cfun_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[12]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_info(server, "+CFUN: (0-1)", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: snprintf(buf, sizeof(buf), "+CFUN: %d", modem_mode); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SET: { GAtResultIter iter; int mode; g_at_result_iter_init(&iter, cmd); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &mode) == FALSE) goto error; if (mode != 0 && mode != 1) goto error; if (modem_mode == mode) { g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; } modem_mode = mode; g_timeout_add_seconds(1, send_ok, server); break; } default: goto error; }; return; error: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); } static void cpin_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { if (modem_mode == 0) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_info(server, "+CPIN: (READY)", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_info(server, "+CPIN: READY", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static gboolean do_netreg(gpointer user) { GAtServer *server = user; char buf[32]; if (data_mode) return FALSE; network_status = 1; switch (modem_creg) { case 1: case 2: snprintf(buf, sizeof(buf), "+CREG: %d", network_status); g_at_server_send_unsolicited(server, buf); break; } return FALSE; } static void cops_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { if (modem_mode == 0) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } switch (type) { case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_timeout_add_seconds(3, send_ok, server); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_info(server, "+COPS: 0", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SET: { GAtServerResult result; GAtResultIter iter; int mode; g_at_result_iter_init(&iter, cmd); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &mode) == TRUE) { if (mode == 0) { g_timeout_add_seconds(2, do_netreg, server); result = G_AT_SERVER_RESULT_OK; } else result = G_AT_SERVER_RESULT_ERROR; } else result = G_AT_SERVER_RESULT_ERROR; g_at_server_send_final(server, result); break; } default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void creg_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[20]; if (modem_mode == 0) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } switch (type) { case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_info(server, "+CREG: (0-2)", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: snprintf(buf, sizeof(buf), "+CREG: %d,%d", modem_creg, network_status); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SET: { GAtResultIter iter; int mode; g_at_result_iter_init(&iter, cmd); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &mode) == FALSE) goto error; if (mode != 0 && mode != 1 && mode != 2) goto error; modem_creg = mode; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; } default: goto error; }; return; error: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); } static void cgreg_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[20]; if (modem_mode == 0) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } switch (type) { case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_info(server, "+CGREG: (0-2)", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: snprintf(buf, sizeof(buf), "+CGREG: %d,%d", modem_cgreg, network_status); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SET: { GAtResultIter iter; int mode; g_at_result_iter_init(&iter, cmd); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &mode) == FALSE) goto error; if (mode != 0 && mode != 1 && mode != 2) goto error; modem_cgreg = mode; g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; } default: goto error; }; return; error: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); } static void cgatt_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[12]; if (modem_mode == 0) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } switch (type) { case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_info(server, "+CGATT: (0-1)", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: snprintf(buf, sizeof(buf), "+CGATT: %d", network_attach); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SET: { GAtResultIter iter; int mode; g_at_result_iter_init(&iter, cmd); g_at_result_iter_next(&iter, ""); if (g_at_result_iter_next_number(&iter, &mode) == FALSE) goto error; if (mode != 0 && mode != 1) goto error; if (network_attach == mode) { g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; } network_attach = mode; g_timeout_add_seconds(1, send_ok, server); break; } default: goto error; }; return; error: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); } static void cgdata_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { GAtIO *io; if (modem_mode == 0) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: if (setup_ppp(server) == FALSE) goto error; g_at_server_send_intermediate(server, "CONNECT"); data_mode = TRUE; io = g_at_server_get_io(server); g_at_io_set_write_done(io, open_ppp, server); break; default: error: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void cgdcont_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { if (modem_mode == 0) { g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); return; } switch (type) { case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; case G_AT_SERVER_REQUEST_TYPE_SET: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void cimi_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY: g_at_server_send_info(server, "246813579", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void csms_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_info(server, "+CSMS: 0,1,1,1", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_info(server, "+CSMS: (0)", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void cmgf_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_info(server, "+CMGF: 0", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_info(server, "+CMGF: (0,1)", TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void cpms_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[2048]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: sprintf(buf, "+CPMS: (\"SM\",\"ME\"),(\"SM\",\"ME\"),(\"SM\")"); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void cnmi_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[2048]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: sprintf(buf, "+CNMI: (0,1,2,3),(0,1),(0,1,2),(0),(0,1)"); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void cscs_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[2048]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: sprintf(buf, "+CSCS: \"GSM\",\"IRA\",\"UCS2\""); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void cmgl_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void cpbs_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { char buf[2048]; switch (type) { case G_AT_SERVER_REQUEST_TYPE_SET: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_QUERY: g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; case G_AT_SERVER_REQUEST_TYPE_SUPPORT: sprintf(buf, "+CPBS: (\"FD\",\"SM\",\"SN\")"); g_at_server_send_info(server, buf, TRUE); g_at_server_send_final(server, G_AT_SERVER_RESULT_OK); break; default: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); break; } } static void dial_cb(GAtServer *server, GAtServerRequestType type, GAtResult *cmd, gpointer user) { GAtResultIter iter; const char *dial_str; char c; if (type != G_AT_SERVER_REQUEST_TYPE_SET) goto error; g_at_result_iter_init(&iter, cmd); g_at_result_iter_next(&iter, ""); dial_str = g_at_result_iter_raw_line(&iter); if (dial_str == NULL) goto error; g_print("dial call %s\n", dial_str); c = *dial_str; if (c == '*' || c == '#' || c == 'T' || c == 't') { GAtIO *io = g_at_server_get_io(server); if (setup_ppp(server) == FALSE) goto error; g_at_server_send_intermediate(server, "CONNECT"); data_mode = TRUE; g_at_io_set_write_done(io, open_ppp, server); } return; error: g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR); } static void add_handler(GAtServer *server) { g_at_server_set_debug(server, server_debug, "Server"); g_at_server_register(server, "+CGMI", cgmi_cb, NULL, NULL); g_at_server_register(server, "+CGMM", cgmm_cb, NULL, NULL); g_at_server_register(server, "+CGMR", cgmr_cb, NULL, NULL); g_at_server_register(server, "+CGSN", cgsn_cb, NULL, NULL); g_at_server_register(server, "+CFUN", cfun_cb, NULL, NULL); g_at_server_register(server, "+CPIN", cpin_cb, NULL, NULL); g_at_server_register(server, "+COPS", cops_cb, NULL, NULL); g_at_server_register(server, "+CREG", creg_cb, NULL, NULL); g_at_server_register(server, "+CGREG", cgreg_cb, NULL, NULL); g_at_server_register(server, "+CGATT", cgatt_cb, NULL, NULL); g_at_server_register(server, "+CGDATA", cgdata_cb, NULL, NULL); g_at_server_register(server, "+CGDCONT", cgdcont_cb, NULL, NULL); g_at_server_register(server, "+CIMI", cimi_cb, NULL, NULL); g_at_server_register(server, "+CSMS", csms_cb, NULL, NULL); g_at_server_register(server, "+CMGF", cmgf_cb, NULL, NULL); g_at_server_register(server, "+CPMS", cpms_cb, NULL, NULL); g_at_server_register(server, "+CNMI", cnmi_cb, NULL, NULL); g_at_server_register(server, "+CSCS", cscs_cb, NULL, NULL); g_at_server_register(server, "+CMGL", cmgl_cb, NULL, NULL); g_at_server_register(server, "+CPBS", cpbs_cb, NULL, NULL); g_at_server_register(server, "D", dial_cb, NULL, NULL); } static void server_destroy(gpointer user) { struct sock_server *data = user; g_free(data); } static void set_raw_mode(int fd) { struct termios ti; memset(&ti, 0, sizeof(ti)); tcgetattr(fd, &ti); tcflush(fd, TCIOFLUSH); cfmakeraw(&ti); tcsetattr(fd, TCSANOW, &ti); } static gboolean create_tty(const char *modem_path) { int master, slave; char pty_name[256]; GIOChannel *server_io; if (modem_path == NULL) return FALSE; if (openpty(&master, &slave, pty_name, NULL, NULL) < 0) return FALSE; set_raw_mode(slave); g_print("new pty is created at %s\n", pty_name); server_io = g_io_channel_unix_new(master); main_server = g_at_server_new(server_io); if (main_server == NULL) { g_io_channel_shutdown(server_io, FALSE, NULL); g_io_channel_unref(server_io); return FALSE; } g_io_channel_unref(server_io); return TRUE; } static gboolean on_socket_connected(GIOChannel *chan, GIOCondition cond, gpointer user) { struct sockaddr saddr; unsigned int len = sizeof(saddr); int fd; GIOChannel *client_io = NULL; struct sock_server *data = user; if (cond != G_IO_IN) goto error; fd = accept(data->server_sock, &saddr, &len); if (fd == -1) goto error; client_io = g_io_channel_unix_new(fd); main_server = g_at_server_new(client_io); g_io_channel_unref(client_io); if (main_server == NULL) goto error; add_handler(main_server); return TRUE; error: g_free(data); return FALSE; } static struct sock_server *socket_common(int sk, struct sockaddr *addr, const char *modem_path) { struct sock_server *sock; int reuseaddr = 1; setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)); if (bind(sk, addr, sizeof(struct sockaddr)) < 0) { g_print("Can't bind socket: %s (%d)", strerror(errno), errno); close(sk); return NULL; } if (listen(sk, 1) < 0) { g_print("Can't listen on socket: %s (%d)", strerror(errno), errno); close(sk); return NULL; } sock = g_try_new0(struct sock_server, 1); if (sock == NULL) return FALSE; sock->server_sock = sk; return sock; } static gboolean create_tcp(const char *modem_path, int port) { struct sockaddr_in addr; int sk; struct sock_server *server; GIOChannel *server_io; if (modem_path == NULL) return FALSE; sk = socket(PF_INET, SOCK_STREAM, 0); if (sk < 0) { g_print("Can't create tcp/ip socket: %s (%d)\n", strerror(errno), errno); return FALSE; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); server = socket_common(sk, (struct sockaddr *) &addr, modem_path); if (server == NULL) return FALSE; g_print("new tcp is created at tcp port %d\n", port); server_io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(server_io, TRUE); server_watch = g_io_add_watch_full(server_io, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, on_socket_connected, server, server_destroy); g_io_channel_unref(server_io); return TRUE; } static gboolean create_unix(const char *modem_path, const char *sock_path) { struct sockaddr_un addr; int sk; struct sock_server *server; GIOChannel *server_io; if (modem_path == NULL) return FALSE; sk = socket(AF_UNIX, SOCK_STREAM, 0); if (sk < 0) { g_print("Can't create unix socket: %s (%d)\n", strerror(errno), errno); return FALSE; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1); /* Unlink any existing socket for this session */ unlink(addr.sun_path); server = socket_common(sk, (struct sockaddr *) &addr, modem_path); if (server == NULL) return FALSE; g_print("new unix socket is created at %s\n", sock_path); server_io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(server_io, TRUE); server_watch = g_io_add_watch_full(server_io, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, on_socket_connected, server, server_destroy); g_io_channel_unref(server_io); return TRUE; } static void test_server(int type) { switch (type) { case 0: if (create_tty("/phonesim1") == FALSE) exit(1); add_handler(main_server); break; case 1: if (create_tcp("/phonesim1", DEFAULT_TCP_PORT) == FALSE) exit(1); break; case 2: if (create_unix("/phonesim1", DEFAULT_SOCK_PATH) == FALSE) exit(1); break; } } static gboolean signal_cb(GIOChannel *channel, GIOCondition cond, gpointer data) { int signal_fd = GPOINTER_TO_INT(data); struct signalfd_siginfo si; ssize_t res; if (cond & (G_IO_NVAL | G_IO_ERR)) return FALSE; res = read(signal_fd, &si, sizeof(si)); if (res != sizeof(si)) return FALSE; switch (si.ssi_signo) { case SIGINT: server_cleanup(); break; case SIGTERM: server_cleanup(); break; default: break; } return TRUE; } static int create_signal_io(void) { sigset_t mask; GIOChannel *signal_io; int signal_fd, signal_source; sigemptyset(&mask); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGINT); if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { g_error("Can't set signal mask"); return 1; } signal_fd = signalfd(-1, &mask, 0); if (signal_fd < 0) { g_error("Can't create signal filedescriptor"); return 1; } signal_io = g_io_channel_unix_new(signal_fd); g_io_channel_set_close_on_unref(signal_io, TRUE); signal_source = g_io_add_watch(signal_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, signal_cb, GINT_TO_POINTER(signal_fd)); g_io_channel_unref(signal_io); return signal_source; } static void usage(void) { g_print("test-server - AT Server testing\n" "Usage:\n"); g_print("\ttest-server [-t type]\n"); g_print("Types:\n" "\t0: Pseudo TTY port (default)\n" "\t1: TCP sock at port 12346)\n" "\t2: Unix sock at ./server_sock\n"); } int main(int argc, char **argv) { int opt, signal_source; int type = 0; while ((opt = getopt(argc, argv, "ht:")) != EOF) { switch (opt) { case 't': type = atoi(optarg); break; case 'h': usage(); exit(1); break; default: break; } } test_server(type); signal_source = create_signal_io(); mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); g_main_loop_unref(mainloop); g_source_remove(signal_source); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gathdlc.c0000644000015600001650000003403612671500024021572 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include #include #include #include #include #include #include #include #include "crc-ccitt.h" #include "ringbuffer.h" #include "gatio.h" #include "gatutil.h" #include "gathdlc.h" #define BUFFER_SIZE (2 * 2048) #define MAX_BUFFERS 64 /* Maximum number of in-flight write buffers */ #define HDLC_OVERHEAD 256 /* Rough estimate of HDLC protocol overhead */ #define HDLC_FLAG 0x7e /* Flag sequence */ #define HDLC_ESCAPE 0x7d /* Asynchronous control escape */ #define HDLC_TRANS 0x20 /* Asynchronous transparency modifier */ #define HDLC_INITFCS 0xffff /* Initial FCS value */ #define HDLC_GOODFCS 0xf0b8 /* Good final FCS value */ #define HDLC_FCS(fcs, c) crc_ccitt_byte(fcs, c) #define GUARD_TIMEOUT 1000 /* Pause time before and after '+++' sequence */ struct _GAtHDLC { gint ref_count; GAtIO *io; GQueue *write_queue; /* Write buffer queue */ unsigned char *decode_buffer; guint decode_offset; guint16 decode_fcs; gboolean decode_escape; guint32 xmit_accm[8]; guint32 recv_accm; GAtReceiveFunc receive_func; gpointer receive_data; GAtDebugFunc debugf; gpointer debug_data; int record_fd; gboolean in_read_handler; gboolean destroyed; gboolean wakeup_sent; gboolean start_frame_marker; gboolean no_carrier_detect; GAtSuspendFunc suspend_func; gpointer suspend_data; guint suspend_source; GTimer *timer; guint num_plus; }; static inline void hdlc_record(GAtHDLC *hdlc, gboolean in, guint8 *data, guint16 length) { guint16 len = htons(length); guint32 ts; struct timeval now; unsigned char id; int err; g_at_util_debug_hexdump(in, data, length, hdlc->debugf, hdlc->debug_data); if (hdlc->record_fd < 0) return; if (length == 0) return; gettimeofday(&now, NULL); ts = htonl(now.tv_sec & 0xffffffff); id = 0x07; err = write(hdlc->record_fd, &id, 1); if (err < 0) return; err = write(hdlc->record_fd, &ts, 4); if (err < 0) return; id = in ? 0x02 : 0x01; err = write(hdlc->record_fd, &id, 1); if (err < 0) return; err = write(hdlc->record_fd, &len, 2); if (err < 0) return; err = write(hdlc->record_fd, data, length); if (err < 0) return; } void g_at_hdlc_set_recording(GAtHDLC *hdlc, const char *filename) { if (hdlc == NULL) return; if (hdlc->record_fd > fileno(stderr)) { close(hdlc->record_fd); hdlc->record_fd = -1; } if (filename == NULL) return; hdlc->record_fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); } void g_at_hdlc_set_recv_accm(GAtHDLC *hdlc, guint32 accm) { if (hdlc == NULL) return; hdlc->recv_accm = accm; } guint32 g_at_hdlc_get_recv_accm(GAtHDLC *hdlc) { if (hdlc == NULL) return 0; return hdlc->recv_accm; } void g_at_hdlc_set_suspend_function(GAtHDLC *hdlc, GAtSuspendFunc func, gpointer user_data) { if (hdlc == NULL) return; if (func == NULL) { if (hdlc->timer) { g_timer_destroy(hdlc->timer); hdlc->timer = NULL; } if (hdlc->suspend_source > 0) { g_source_remove(hdlc->suspend_source); hdlc->suspend_source = 0; } } else hdlc->timer = g_timer_new(); hdlc->suspend_func = func; hdlc->suspend_data = user_data; } static gboolean hdlc_suspend(gpointer user_data) { GAtHDLC *hdlc = user_data; g_at_io_drain_ring_buffer(hdlc->io, 3); g_at_io_set_write_handler(hdlc->io, NULL, NULL); g_at_io_set_read_handler(hdlc->io, NULL, NULL); if (hdlc->suspend_func) hdlc->suspend_func(hdlc->suspend_data); hdlc->suspend_source = 0; return FALSE; } static gboolean check_escape(GAtHDLC *hdlc, struct ring_buffer *rbuf) { unsigned int len = ring_buffer_len(rbuf); unsigned int wrap = ring_buffer_len_no_wrap(rbuf); unsigned char *buf = ring_buffer_read_ptr(rbuf, 0); unsigned int pos = 0; unsigned int elapsed = g_timer_elapsed(hdlc->timer, NULL) * 1000; unsigned int num_plus = 0; gboolean guard_timeout = FALSE; if (elapsed >= GUARD_TIMEOUT) guard_timeout = TRUE; while (pos < len && pos < 3) { if (*buf != '+') break; num_plus++; buf++; pos++; if (pos == wrap) buf = ring_buffer_read_ptr(rbuf, pos); } if (num_plus != len) return FALSE; /* We got some escape chars, but no guard timeout first */ if (guard_timeout == FALSE && hdlc->num_plus == 0) return FALSE; if (num_plus != 3) { hdlc->num_plus = num_plus; return TRUE; } hdlc->num_plus = 0; hdlc->suspend_source = g_timeout_add(GUARD_TIMEOUT, hdlc_suspend, hdlc); return TRUE; } static void new_bytes(struct ring_buffer *rbuf, gpointer user_data) { GAtHDLC *hdlc = user_data; unsigned int len = ring_buffer_len(rbuf); unsigned int wrap = ring_buffer_len_no_wrap(rbuf); unsigned char *buf = ring_buffer_read_ptr(rbuf, 0); unsigned int pos = 0; /* * We delete the the paused_timeout_cb or hdlc_suspend as soons as * we read a data. */ if (hdlc->suspend_source > 0) { g_source_remove(hdlc->suspend_source); hdlc->suspend_source = 0; g_timer_start(hdlc->timer); } else if (hdlc->timer) { gboolean escaping = check_escape(hdlc, rbuf); g_timer_start(hdlc->timer); if (escaping) return; } hdlc_record(hdlc, TRUE, buf, wrap); hdlc->in_read_handler = TRUE; while (pos < len) { /* * We try to detect NO CARRIER conditions here. We * (ab) use the fact that a HDLC_FLAG must be followed * by the Address or Protocol fields, depending on whether * ACFC is enabled. */ if (hdlc->no_carrier_detect && hdlc->decode_offset == 0 && *buf == '\r') break; if (hdlc->decode_escape == TRUE) { unsigned char val = *buf ^ HDLC_TRANS; hdlc->decode_buffer[hdlc->decode_offset++] = val; hdlc->decode_fcs = HDLC_FCS(hdlc->decode_fcs, val); hdlc->decode_escape = FALSE; } else if (*buf == HDLC_ESCAPE) { hdlc->decode_escape = TRUE; } else if (*buf == HDLC_FLAG) { if (hdlc->receive_func && hdlc->decode_offset > 2 && hdlc->decode_fcs == HDLC_GOODFCS) { hdlc->receive_func(hdlc->decode_buffer, hdlc->decode_offset - 2, hdlc->receive_data); if (hdlc->destroyed) goto out; } hdlc->decode_fcs = HDLC_INITFCS; hdlc->decode_offset = 0; } else if (*buf >= 0x20 || (hdlc->recv_accm & (1 << *buf)) == 0) { hdlc->decode_buffer[hdlc->decode_offset++] = *buf; hdlc->decode_fcs = HDLC_FCS(hdlc->decode_fcs, *buf); } buf++; pos++; if (pos == wrap) { buf = ring_buffer_read_ptr(rbuf, pos); hdlc_record(hdlc, TRUE, buf, len - wrap); } } out: ring_buffer_drain(rbuf, pos); hdlc->in_read_handler = FALSE; if (hdlc->destroyed) g_free(hdlc); } GAtHDLC *g_at_hdlc_new_from_io(GAtIO *io) { GAtHDLC *hdlc; struct ring_buffer* write_buffer; if (io == NULL) return NULL; hdlc = g_try_new0(GAtHDLC, 1); if (hdlc == NULL) return NULL; hdlc->ref_count = 1; hdlc->decode_fcs = HDLC_INITFCS; hdlc->decode_offset = 0; hdlc->decode_escape = FALSE; hdlc->xmit_accm[0] = ~0U; hdlc->xmit_accm[3] = 0x60000000; /* 0x7d, 0x7e */ hdlc->recv_accm = ~0U; write_buffer = ring_buffer_new(BUFFER_SIZE); if (!write_buffer) goto error; hdlc->write_queue = g_queue_new(); if (!hdlc->write_queue) goto error; g_queue_push_tail(hdlc->write_queue, write_buffer); hdlc->decode_buffer = g_try_malloc(BUFFER_SIZE); if (!hdlc->decode_buffer) goto error; hdlc->record_fd = -1; hdlc->io = g_at_io_ref(io); g_at_io_set_read_handler(hdlc->io, new_bytes, hdlc); return hdlc; error: if (hdlc->write_queue) g_queue_free(hdlc->write_queue); if (write_buffer) ring_buffer_free(write_buffer); g_free(hdlc->decode_buffer); g_free(hdlc); return NULL; } GAtHDLC *g_at_hdlc_new(GIOChannel *channel) { GAtIO *io; GAtHDLC *hdlc; io = g_at_io_new(channel); if (io == NULL) return NULL; hdlc = g_at_hdlc_new_from_io(io); g_at_io_unref(io); return hdlc; } GAtHDLC *g_at_hdlc_ref(GAtHDLC *hdlc) { if (hdlc == NULL) return NULL; g_atomic_int_inc(&hdlc->ref_count); return hdlc; } void g_at_hdlc_unref(GAtHDLC *hdlc) { struct ring_buffer *write_buffer; if (hdlc == NULL) return; if (g_atomic_int_dec_and_test(&hdlc->ref_count) == FALSE) return; if (hdlc->record_fd > fileno(stderr)) { close(hdlc->record_fd); hdlc->record_fd = -1; } g_at_io_set_write_handler(hdlc->io, NULL, NULL); g_at_io_set_read_handler(hdlc->io, NULL, NULL); if (hdlc->suspend_source > 0) g_source_remove(hdlc->suspend_source); g_at_io_unref(hdlc->io); hdlc->io = NULL; while ((write_buffer = g_queue_pop_head(hdlc->write_queue))) ring_buffer_free(write_buffer); g_queue_free(hdlc->write_queue); g_free(hdlc->decode_buffer); g_timer_destroy(hdlc->timer); if (hdlc->in_read_handler) hdlc->destroyed = TRUE; else g_free(hdlc); } void g_at_hdlc_set_debug(GAtHDLC *hdlc, GAtDebugFunc func, gpointer user_data) { if (hdlc == NULL) return; hdlc->debugf = func; hdlc->debug_data = user_data; } void g_at_hdlc_set_receive(GAtHDLC *hdlc, GAtReceiveFunc func, gpointer user_data) { if (hdlc == NULL) return; hdlc->receive_func = func; hdlc->receive_data = user_data; } static gboolean can_write_data(gpointer data) { GAtHDLC *hdlc = data; unsigned int len; unsigned char *buf; gsize bytes_written; struct ring_buffer* write_buffer; /* Write data out from the head of the queue */ write_buffer = g_queue_peek_head(hdlc->write_queue); len = ring_buffer_len_no_wrap(write_buffer); buf = ring_buffer_read_ptr(write_buffer, 0); bytes_written = g_at_io_write(hdlc->io, (gchar *) buf, len); hdlc_record(hdlc, FALSE, buf, bytes_written); ring_buffer_drain(write_buffer, bytes_written); if (ring_buffer_len(write_buffer) > 0) return TRUE; /* All data in current buffer is written, free it * unless it's the last buffer in the queue. */ if ((ring_buffer_len(write_buffer) == 0) && (g_queue_get_length(hdlc->write_queue) > 1)) { write_buffer = g_queue_pop_head(hdlc->write_queue); ring_buffer_free(write_buffer); write_buffer = g_queue_peek_head(hdlc->write_queue); } if (ring_buffer_len(write_buffer) > 0) return TRUE; return FALSE; } void g_at_hdlc_set_xmit_accm(GAtHDLC *hdlc, guint32 accm) { if (hdlc == NULL) return; hdlc->xmit_accm[0] = accm; } guint32 g_at_hdlc_get_xmit_accm(GAtHDLC *hdlc) { if (hdlc == NULL) return 0; return hdlc->xmit_accm[0]; } GAtIO *g_at_hdlc_get_io(GAtHDLC *hdlc) { if (hdlc == NULL) return NULL; return hdlc->io; } #define NEED_ESCAPE(xmit_accm, c) xmit_accm[c >> 5] & (1 << (c & 0x1f)) gboolean g_at_hdlc_send(GAtHDLC *hdlc, const unsigned char *data, gsize size) { struct ring_buffer* write_buffer = g_queue_peek_tail(hdlc->write_queue); unsigned int avail = ring_buffer_avail(write_buffer); unsigned int wrap = ring_buffer_avail_no_wrap(write_buffer); unsigned char *buf; unsigned char tail[2]; unsigned int i = 0; guint16 fcs = HDLC_INITFCS; gboolean escape = FALSE; gsize pos = 0; if (avail < size + HDLC_OVERHEAD) { if (g_queue_get_length(hdlc->write_queue) > MAX_BUFFERS) return FALSE; /* Too many pending buffers */ write_buffer = ring_buffer_new(BUFFER_SIZE); if (write_buffer == NULL) return FALSE; g_queue_push_tail(hdlc->write_queue, write_buffer); avail = ring_buffer_avail(write_buffer); wrap = ring_buffer_avail_no_wrap(write_buffer); } i = 0; buf = ring_buffer_write_ptr(write_buffer, 0); if (hdlc->start_frame_marker == TRUE) { /* Protocol requires 0x7e as start marker */ if (pos + 1 > avail) return FALSE; *buf++ = HDLC_FLAG; pos++; if (pos == wrap) buf = ring_buffer_write_ptr(write_buffer, pos); } else if (hdlc->wakeup_sent == FALSE) { /* Write an initial 0x7e as wakeup character */ *buf++ = HDLC_FLAG; pos++; hdlc->wakeup_sent = TRUE; } while (pos < avail && i < size) { if (escape == TRUE) { fcs = HDLC_FCS(fcs, data[i]); *buf = data[i++] ^ HDLC_TRANS; escape = FALSE; } else if (NEED_ESCAPE(hdlc->xmit_accm, data[i])) { *buf = HDLC_ESCAPE; escape = TRUE; } else { fcs = HDLC_FCS(fcs, data[i]); *buf = data[i++]; } buf++; pos++; if (pos == wrap) buf = ring_buffer_write_ptr(write_buffer, pos); } if (i < size) return FALSE; fcs ^= HDLC_INITFCS; tail[0] = fcs & 0xff; tail[1] = fcs >> 8; i = 0; while (pos < avail && i < sizeof(tail)) { if (escape == TRUE) { *buf = tail[i++] ^ HDLC_TRANS; escape = FALSE; } else if (NEED_ESCAPE(hdlc->xmit_accm, tail[i])) { *buf = HDLC_ESCAPE; escape = TRUE; } else { *buf = tail[i++]; } buf++; pos++; if (pos == wrap) buf = ring_buffer_write_ptr(write_buffer, pos); } if (i < sizeof(tail)) return FALSE; if (pos + 1 > avail) return FALSE; /* Add 0x7e as end marker */ *buf = HDLC_FLAG; pos++; ring_buffer_write_advance(write_buffer, pos); g_at_io_set_write_handler(hdlc->io, can_write_data, hdlc); return TRUE; } void g_at_hdlc_set_start_frame_marker(GAtHDLC *hdlc, gboolean marker) { if (hdlc == NULL) return; hdlc->start_frame_marker = marker; } void g_at_hdlc_set_no_carrier_detect(GAtHDLC *hdlc, gboolean detect) { if (hdlc == NULL) return; hdlc->no_carrier_detect = detect; } void g_at_hdlc_suspend(GAtHDLC *hdlc) { if (hdlc == NULL) return; g_at_io_set_write_handler(hdlc->io, NULL, NULL); g_at_io_set_read_handler(hdlc->io, NULL, NULL); } void g_at_hdlc_resume(GAtHDLC *hdlc) { if (hdlc == NULL) return; g_at_io_set_read_handler(hdlc->io, new_bytes, hdlc); if (g_queue_get_length(hdlc->write_queue) > 0) g_at_io_set_write_handler(hdlc->io, can_write_data, hdlc); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gattty.c0000644000015600001650000001450712671500024021501 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include #include #include #include #include #include "gattty.h" static gboolean set_baud(const char *baud, struct termios *ti) { speed_t speed; if (g_str_equal(baud, "300")) speed = B300; else if (g_str_equal(baud, "1200")) speed = B1200; else if (g_str_equal(baud, "2400")) speed = B2400; else if (g_str_equal(baud, "4800")) speed = B4800; else if (g_str_equal(baud, "9600")) speed = B9600; else if (g_str_equal(baud, "19200")) speed = B19200; else if (g_str_equal(baud, "38400")) speed = B38400; else if (g_str_equal(baud, "57600")) speed = B57600; else if (g_str_equal(baud, "115200")) speed = B115200; else if (g_str_equal(baud, "230400")) speed = B230400; else if (g_str_equal(baud, "460800")) speed = B460800; else if (g_str_equal(baud, "500000")) speed = B500000; else if (g_str_equal(baud, "576000")) speed = B576000; else if (g_str_equal(baud, "921600")) speed = B921600; else if (g_str_equal(baud, "1000000")) speed = B1000000; else if (g_str_equal(baud, "1152000")) speed = B1152000; else if (g_str_equal(baud, "1500000")) speed = B1500000; else if (g_str_equal(baud, "2000000")) speed = B2000000; #ifdef B2500000 else if (g_str_equal(baud, "2500000")) speed = B2500000; #endif #ifdef B3000000 else if (g_str_equal(baud, "3000000")) speed = B3000000; #endif #ifdef B3500000 else if (g_str_equal(baud, "3500000")) speed = B3500000; #endif #ifdef B4000000 else if (g_str_equal(baud, "4000000")) speed = B4000000; #endif else return FALSE; cfsetospeed(ti, speed); cfsetispeed(ti, speed); return TRUE; } static gboolean set_read(const char *bits, struct termios *ti) { if (g_str_equal(bits, "off")) ti->c_cflag &= ~(CREAD); else if (g_str_equal(bits, "on")) ti->c_cflag |= CREAD; else return FALSE; return TRUE; } static gboolean set_stop_bits(const char *bits, struct termios *ti) { if (g_str_equal(bits, "1")) ti->c_cflag &= ~(CSTOPB); else if (g_str_equal(bits, "2")) ti->c_cflag |= CSTOPB; else return FALSE; return TRUE; } static gboolean set_data_bits(const char *bits, struct termios *ti) { if (g_str_equal(bits, "7")) { ti->c_cflag &= ~(CSIZE); ti->c_cflag |= CS7; } else if (g_str_equal(bits, "8")) { ti->c_cflag &= ~(CSIZE); ti->c_cflag |= CS8; } else return FALSE; return TRUE; } static gboolean set_parity(const char *parity, struct termios *ti) { if (g_str_equal(parity, "none")) ti->c_cflag &= ~(PARENB); else if (g_str_equal(parity, "even")) { ti->c_cflag |= PARENB; ti->c_cflag &= ~(PARODD); } else if (g_str_equal(parity, "odd")) { ti->c_cflag |= PARENB; ti->c_cflag |= PARODD; } else return FALSE; return TRUE; } static gboolean set_xonxoff(const char *xonxoff, struct termios *ti) { if (g_str_equal(xonxoff, "on")) { ti->c_iflag |= (IXON | IXOFF | IXANY); ti->c_cc[VSTART] = 17; ti->c_cc[VSTOP] = 19; } else if (g_str_equal(xonxoff, "off")) ti->c_iflag &= ~(IXON | IXOFF | IXANY); else return FALSE; return TRUE; } static gboolean set_rtscts(const char *rtscts, struct termios *ti) { if (g_str_equal(rtscts, "on")) ti->c_cflag |= CRTSCTS; else if (g_str_equal(rtscts, "off")) ti->c_cflag &= ~(CRTSCTS); else return FALSE; return TRUE; } static gboolean set_local(const char *local, struct termios *ti) { if (g_str_equal(local, "on")) ti->c_cflag |= CLOCAL; else if (g_str_equal(local, "off")) ti->c_cflag &= ~(CLOCAL); else return FALSE; return TRUE; } static int open_device(const char *tty, GHashTable *options) { struct termios ti; int fd; /* Switch TTY to raw mode */ memset(&ti, 0, sizeof(ti)); cfmakeraw(&ti); if (options) { GHashTableIter iter; const char *key; const char *value; g_hash_table_iter_init (&iter, options); while (g_hash_table_iter_next(&iter, (void *) &key, (void *) &value)) { gboolean ok = FALSE; if (g_str_equal(key, "Baud")) ok = set_baud(value, &ti); else if (g_str_equal(key, "StopBits")) ok = set_stop_bits(value, &ti); else if (g_str_equal(key, "DataBits")) ok = set_data_bits(value, &ti); else if (g_str_equal(key, "Parity")) ok = set_parity(value, &ti); else if (g_str_equal(key, "XonXoff")) ok = set_xonxoff(value, &ti); else if (g_str_equal(key, "RtsCts")) ok = set_rtscts(value, &ti); else if (g_str_equal(key, "Local")) ok = set_local(value, &ti); else if (g_str_equal(key, "Read")) ok = set_read(value, &ti); if (ok == FALSE) return -1; } } fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd < 0) return -1; tcflush(fd, TCIOFLUSH); tcsetattr(fd, TCSANOW, &ti); return fd; } GIOChannel *g_at_tty_open(const char *tty, GHashTable *options) { GIOChannel *channel; int fd; fd = open_device(tty, options); if (fd < 0) return NULL; channel = g_io_channel_unix_new(fd); if (channel == NULL) { close(fd); return NULL; } g_io_channel_set_close_on_unref(channel, TRUE); return channel; } GIOChannel *g_at_tty_open_qcdm(const char *tty) { GIOChannel *channel; struct termios ti; int fd; fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd < 0) return NULL; /* Switch TTY to raw mode */ memset(&ti, 0, sizeof(ti)); cfmakeraw(&ti); /* No parity, 1 stop bit */ ti.c_cflag &= ~(CSIZE | CSTOPB | PARENB); ti.c_cflag |= (B115200 | CS8); if (tcsetattr(fd, TCSANOW, &ti) < 0) { close(fd); return NULL; } channel = g_io_channel_unix_new(fd); if (channel == NULL) { close(fd); return NULL; } g_io_channel_set_close_on_unref(channel, TRUE); return channel; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/ringbuffer.h0000644000015600001650000000644412671500024022324 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 * */ struct ring_buffer; /*! * Creates a new ring buffer with capacity size */ struct ring_buffer *ring_buffer_new(unsigned int size); /*! * Frees the resources allocated for the ring buffer */ void ring_buffer_free(struct ring_buffer *buf); /*! * Returns the capacity of the ring buffer */ int ring_buffer_capacity(struct ring_buffer *buf); /*! * Resets the ring buffer, all data inside the buffer is lost */ void ring_buffer_reset(struct ring_buffer *buf); /*! * Writes data of size len into the ring buffer buf. Returns -1 if the * write failed or the number of bytes written */ int ring_buffer_write(struct ring_buffer *buf, const void *data, unsigned int len); /*! * Advances the write counter by len, this is meant to be used with * the ring_buffer_write_ptr function. Returns the number of bytes * actually advanced (the capacity of the buffer) */ int ring_buffer_write_advance(struct ring_buffer *buf, unsigned int len); /*! * Returns the write pointer with write offset specified by offset. Careful * not to write past the end of the buffer. Use the ring_buffer_avail_no_wrap * function, and ring_buffer_write_advance. */ unsigned char *ring_buffer_write_ptr(struct ring_buffer *buf, unsigned int offset); /*! * Returns the number of free bytes available in the buffer */ int ring_buffer_avail(struct ring_buffer *buf); /*! * Returns the number of free bytes available in the buffer without wrapping */ int ring_buffer_avail_no_wrap(struct ring_buffer *buf); /*! * Reads data from the ring buffer buf into memory region pointed to by data. * A maximum of len bytes will be read. Returns -1 if the read failed or * the number of bytes read */ int ring_buffer_read(struct ring_buffer *buf, void *data, unsigned int len); /*! * Returns the read pointer with read offset specified by offset. No bounds * checking is performed. Be careful not to read past the end of the buffer. * Use the ring_buffer_len_no_wrap function, and ring_buffer_drain. */ unsigned char *ring_buffer_read_ptr(struct ring_buffer *buf, unsigned int offset); /*! * Returns the number of bytes currently available to be read in the buffer */ int ring_buffer_len(struct ring_buffer *buf); /*! * Returns the number of bytes currently available to be read in the buffer * without wrapping. */ int ring_buffer_len_no_wrap(struct ring_buffer *buf); /*! * Drains the ring buffer of len bytes. Returns the number of bytes the * read counter was actually advanced. */ int ring_buffer_drain(struct ring_buffer *buf, unsigned int len); ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatppp.h0000644000015600001650000000622412671500024021462 0ustar pbuserpbgroup00000000000000/* * * PPP library with GLib integration * * Copyright (C) 2009-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 * */ #ifndef __G_AT_PPP_H #define __G_AT_PPP_H #ifdef __cplusplus extern "C" { #endif #include "gat.h" #include "gathdlc.h" struct _GAtPPP; typedef struct _GAtPPP GAtPPP; typedef enum _GAtPPPDisconnectReason { G_AT_PPP_REASON_UNKNOWN, G_AT_PPP_REASON_AUTH_FAIL, /* Failed to authenticate */ G_AT_PPP_REASON_IPCP_FAIL, /* Failed to negotiate IPCP */ G_AT_PPP_REASON_NET_FAIL, /* Failed to create tun */ G_AT_PPP_REASON_PEER_CLOSED, /* Peer initiated a close */ G_AT_PPP_REASON_LINK_DEAD, /* Link to the peer died */ G_AT_PPP_REASON_LOCAL_CLOSE, /* Normal user close */ } GAtPPPDisconnectReason; typedef enum _GAtPPPAuthMethod { G_AT_PPP_AUTH_METHOD_CHAP, G_AT_PPP_AUTH_METHOD_PAP, } GAtPPPAuthMethod; typedef void (*GAtPPPConnectFunc)(const char *iface, const char *local, const char *peer, const char *dns1, const char *dns2, gpointer user_data); typedef void (*GAtPPPDisconnectFunc)(GAtPPPDisconnectReason reason, gpointer user_data); GAtPPP *g_at_ppp_new(void); GAtPPP *g_at_ppp_server_new(const char *local); GAtPPP *g_at_ppp_server_new_full(const char *local, int fd); gboolean g_at_ppp_open(GAtPPP *ppp, GAtIO *io); gboolean g_at_ppp_listen(GAtPPP *ppp, GAtIO *io); void g_at_ppp_set_connect_function(GAtPPP *ppp, GAtPPPConnectFunc callback, gpointer user_data); void g_at_ppp_set_disconnect_function(GAtPPP *ppp, GAtPPPDisconnectFunc func, gpointer user_data); void g_at_ppp_set_suspend_function(GAtPPP *ppp, GAtSuspendFunc func, gpointer user_data); void g_at_ppp_set_debug(GAtPPP *ppp, GAtDebugFunc func, gpointer user_data); void g_at_ppp_shutdown(GAtPPP *ppp); void g_at_ppp_suspend(GAtPPP *ppp); void g_at_ppp_resume(GAtPPP *ppp); void g_at_ppp_ref(GAtPPP *ppp); void g_at_ppp_unref(GAtPPP *ppp); gboolean g_at_ppp_set_credentials(GAtPPP *ppp, const char *username, const char *passwd); const char *g_at_ppp_get_username(GAtPPP *ppp); const char *g_at_ppp_get_password(GAtPPP *ppp); gboolean g_at_ppp_set_auth_method(GAtPPP *ppp, GAtPPPAuthMethod method); GAtPPPAuthMethod g_at_ppp_get_auth_method(GAtPPP *ppp); void g_at_ppp_set_recording(GAtPPP *ppp, const char *filename); void g_at_ppp_set_server_info(GAtPPP *ppp, const char *remote_ip, const char *dns1, const char *dns2); void g_at_ppp_set_acfc_enabled(GAtPPP *ppp, gboolean enabled); void g_at_ppp_set_pfc_enabled(GAtPPP *ppp, gboolean enabled); #ifdef __cplusplus } #endif #endif /* __G_AT_PPP_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gat.h0000644000015600001650000000226512671500024020743 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 * */ #ifndef __GAT_H #define __GAT_H #include #ifdef __cplusplus extern "C" { #endif typedef void (*GAtDisconnectFunc)(gpointer user_data); typedef void (*GAtReceiveFunc)(const unsigned char *data, gsize size, gpointer user_data); typedef void (*GAtDebugFunc)(const char *str, gpointer user_data); typedef void (*GAtSuspendFunc)(gpointer user_data); #ifdef __cplusplus } #endif #endif /* __GAT_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatsyntax.h0000644000015600001650000000466112671500024022214 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 * */ #ifndef __GATSYNTAX_H #define __GATSYNTAX_H #ifdef __cplusplus extern "C" { #endif enum _GAtSyntaxExpectHint { G_AT_SYNTAX_EXPECT_PDU, G_AT_SYNTAX_EXPECT_MULTILINE, G_AT_SYNTAX_EXPECT_PROMPT, G_AT_SYNTAX_EXPECT_SHORT_PROMPT }; typedef enum _GAtSyntaxExpectHint GAtSyntaxExpectHint; enum _GAtSyntaxResult { G_AT_SYNTAX_RESULT_UNRECOGNIZED, G_AT_SYNTAX_RESULT_UNSURE, G_AT_SYNTAX_RESULT_LINE, G_AT_SYNTAX_RESULT_MULTILINE, G_AT_SYNTAX_RESULT_PDU, G_AT_SYNTAX_RESULT_PROMPT, }; typedef enum _GAtSyntaxResult GAtSyntaxResult; typedef struct _GAtSyntax GAtSyntax; typedef void (*GAtSyntaxSetHintFunc)(GAtSyntax *syntax, GAtSyntaxExpectHint hint); typedef GAtSyntaxResult (*GAtSyntaxFeedFunc)(GAtSyntax *syntax, const char *bytes, gsize *len); struct _GAtSyntax { gint ref_count; int state; GAtSyntaxSetHintFunc set_hint; GAtSyntaxFeedFunc feed; }; GAtSyntax *g_at_syntax_new_full(GAtSyntaxFeedFunc feed, GAtSyntaxSetHintFunc hint, int initial_state); /* This syntax implements very strict checking of 27.007 standard, which means * it might not work with a majority of modems. However, it does handle echo * properly and can be used to detect a modem's deviations from the relevant * standards. */ GAtSyntax *g_at_syntax_new_gsmv1(void); /* This syntax implements an extremely lax parser that can handle a variety * of modems. Unfortunately it does not deal with echo at all, so echo must * be explicitly turned off before using the parser */ GAtSyntax *g_at_syntax_new_gsm_permissive(void); GAtSyntax *g_at_syntax_ref(GAtSyntax *syntax); void g_at_syntax_unref(GAtSyntax *syntax); #ifdef __cplusplus } #endif #endif /* __GATSYNTAX_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatrawip.h0000644000015600001650000000263012671500024022002 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 * */ #ifndef __G_AT_RAWIP_H #define __G_AT_RAWIP_H #ifdef __cplusplus extern "C" { #endif #include "gat.h" #include "gatio.h" struct _GAtRawIP; typedef struct _GAtRawIP GAtRawIP; GAtRawIP *g_at_rawip_new(GIOChannel *channel); GAtRawIP *g_at_rawip_new_from_io(GAtIO *io); GAtRawIP *g_at_rawip_ref(GAtRawIP *rawip); void g_at_rawip_unref(GAtRawIP *rawip); void g_at_rawip_open(GAtRawIP *rawip); void g_at_rawip_shutdown(GAtRawIP *rawip); const char *g_at_rawip_get_interface(GAtRawIP *rawip); void g_at_rawip_set_debug(GAtRawIP *rawip, GAtDebugFunc func, gpointer user_data); #ifdef __cplusplus } #endif #endif /* __G_AT_RAWIP_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/ppp_auth.c0000644000015600001650000001532412671500024022003 0ustar pbuserpbgroup00000000000000/* * * PPP library with GLib integration * * Copyright (C) 2009-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 #endif #include #include #include #include #include #include #include #include "gatppp.h" #include "ppp.h" struct chap_header { guint8 code; guint8 identifier; guint16 length; guint8 data[0]; } __attribute__((packed)); struct ppp_chap { guint8 method; GAtPPP *ppp; }; enum chap_code { CHALLENGE = 1, RESPONSE, SUCCESS, FAILURE }; struct pap_header { guint8 code; guint8 identifier; guint16 length; guint8 data[0]; } __attribute__((packed)); struct ppp_pap { GAtPPP *ppp; struct ppp_header *authreq; guint16 authreq_len; guint retry_timer; guint retries; }; enum pap_code { PAP_REQUEST = 1, PAP_ACK, PAP_NAK }; /* * RFC 1334 2.1.1: * The Authenticate-Request packet MUST be repeated until a valid * reply packet is received, or an optional retry counter expires. * * If we don't get a reply after this many attempts, we can safely * assume we're never going to get one. */ #define PAP_MAX_RETRY 3 /* attempts */ #define PAP_TIMEOUT 10 /* seconds */ static void chap_process_challenge(struct ppp_chap *chap, const guint8 *packet) { const struct chap_header *header = (const struct chap_header *) packet; struct chap_header *response; GChecksum *checksum; const char *secret = g_at_ppp_get_password(chap->ppp); const char *username = g_at_ppp_get_username(chap->ppp); guint16 response_length; struct ppp_header *ppp_packet; gsize digest_len; /* create a checksum over id, secret, and challenge */ checksum = g_checksum_new(chap->method); if (checksum == NULL) return; g_checksum_update(checksum, &header->identifier, 1); if (secret) g_checksum_update(checksum, (guchar *) secret, strlen(secret)); g_checksum_update(checksum, &header->data[1], header->data[0]); /* transmit a response packet */ /* * allocate space for the header, the checksum, and the ppp header, * and the value size byte */ digest_len = g_checksum_type_get_length(chap->method); response_length = digest_len + sizeof(*header) + 1; if (username != NULL) response_length += strlen(username); ppp_packet = ppp_packet_new(response_length, CHAP_PROTOCOL); if (ppp_packet == NULL) goto challenge_out; response = (struct chap_header *) &ppp_packet->info; if (response) { response->code = RESPONSE; response->identifier = header->identifier; response->length = htons(response_length); g_checksum_get_digest(checksum, response->data + 1, &digest_len); response->data[0] = digest_len; /* leave the name empty? */ } if (username != NULL) memcpy(response->data + digest_len + 1, username, strlen(username)); /* transmit the packet */ ppp_transmit(chap->ppp, (guint8 *) ppp_packet, response_length); g_free(ppp_packet); challenge_out: g_checksum_free(checksum); } /* * parse the packet */ void ppp_chap_process_packet(struct ppp_chap *chap, const guint8 *new_packet, gsize len) { guint8 code; if (len < sizeof(struct chap_header)) return; code = new_packet[0]; switch (code) { case CHALLENGE: chap_process_challenge(chap, new_packet); break; case RESPONSE: break; case SUCCESS: ppp_auth_notify(chap->ppp, TRUE); break; case FAILURE: ppp_auth_notify(chap->ppp, FALSE); break; default: break; } } void ppp_chap_free(struct ppp_chap *chap) { g_free(chap); } struct ppp_chap *ppp_chap_new(GAtPPP *ppp, guint8 method) { struct ppp_chap *chap; if (method != MD5) return NULL; chap = g_try_new0(struct ppp_chap, 1); if (chap == NULL) return NULL; chap->ppp = ppp; chap->method = G_CHECKSUM_MD5; return chap; } void ppp_pap_process_packet(struct ppp_pap *pap, const guint8 *new_packet, gsize len) { guint8 code; if (len < sizeof(struct pap_header)) return; code = new_packet[0]; switch (code) { case PAP_ACK: g_source_remove(pap->retry_timer); pap->retry_timer = 0; ppp_auth_notify(pap->ppp, TRUE); break; case PAP_NAK: g_source_remove(pap->retry_timer); pap->retry_timer = 0; ppp_auth_notify(pap->ppp, FALSE); break; default: break; } } static gboolean ppp_pap_timeout(gpointer user_data) { struct ppp_pap *pap = (struct ppp_pap *)user_data; struct pap_header *authreq; if (++pap->retries >= PAP_MAX_RETRY) { pap->retry_timer = 0; ppp_auth_notify(pap->ppp, FALSE); return FALSE; } /* * RFC 1334 2.2.1: * The Identifier field MUST be changed each time an * Authenticate-Request packet is issued. */ authreq = (struct pap_header *)&pap->authreq->info; authreq->identifier++; ppp_transmit(pap->ppp, (guint8 *)pap->authreq, pap->authreq_len); return TRUE; } gboolean ppp_pap_start(struct ppp_pap *pap) { struct pap_header *authreq; struct ppp_header *packet; const char *username = g_at_ppp_get_username(pap->ppp); const char *password = g_at_ppp_get_password(pap->ppp); guint16 length; length = sizeof(*authreq) + strlen(username) + strlen(password) + 2; packet = ppp_packet_new(length, PAP_PROTOCOL); if (packet == NULL) return FALSE; pap->authreq = packet; pap->authreq_len = length; authreq = (struct pap_header *)&packet->info; authreq->code = PAP_REQUEST; authreq->identifier = 1; authreq->length = htons(length); authreq->data[0] = (unsigned char) strlen(username); memcpy(authreq->data + 1, username, strlen(username)); authreq->data[strlen(username) + 1] = (unsigned char)strlen(password); memcpy(authreq->data + 1 + strlen(username) + 1, password, strlen(password)); /* Transmit the packet and schedule a retry. */ ppp_transmit(pap->ppp, (guint8 *)packet, length); pap->retries = 0; pap->retry_timer = g_timeout_add_seconds(PAP_TIMEOUT, ppp_pap_timeout, pap); return TRUE; } void ppp_pap_free(struct ppp_pap *pap) { if (pap->retry_timer != 0) g_source_remove(pap->retry_timer); if (pap->authreq != NULL) g_free(pap->authreq); g_free(pap); } struct ppp_pap *ppp_pap_new(GAtPPP *ppp) { struct ppp_pap *pap; pap = g_try_new0(struct ppp_pap, 1); if (pap == NULL) return NULL; pap->ppp = ppp; return pap; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatppp.c0000644000015600001650000004454612671500024021466 0ustar pbuserpbgroup00000000000000/* * * PPP library with GLib integration * * Copyright (C) 2009-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 #endif #include #include #include #include #include #include #include #include #include #include "gatutil.h" #include "gathdlc.h" #include "gatppp.h" #include "crc-ccitt.h" #include "ppp.h" #define DEFAULT_MRU 1500 #define DEFAULT_MTU 1500 #define PPP_ADDR_FIELD 0xff #define PPP_CTRL 0x03 #define GUARD_TIMEOUTS 1500 enum ppp_phase { PPP_PHASE_DEAD = 0, /* Link dead */ PPP_PHASE_ESTABLISHMENT, /* LCP started */ PPP_PHASE_AUTHENTICATION, /* Auth started */ PPP_PHASE_NETWORK, /* IPCP started */ PPP_PHASE_LINK_UP, /* IPCP negotiation ok, link up */ PPP_PHASE_TERMINATION, /* LCP Terminate phase */ }; struct _GAtPPP { gint ref_count; enum ppp_phase phase; struct pppcp_data *lcp; struct pppcp_data *ipcp; struct ppp_net *net; struct ppp_chap *chap; struct ppp_pap *pap; GAtHDLC *hdlc; gint mru; gint mtu; char username[256]; char password[256]; GAtPPPAuthMethod auth_method; GAtPPPConnectFunc connect_cb; gpointer connect_data; GAtPPPDisconnectFunc disconnect_cb; gpointer disconnect_data; GAtPPPDisconnectReason disconnect_reason; GAtDebugFunc debugf; gpointer debug_data; gboolean sta_pending; guint ppp_dead_source; GAtSuspendFunc suspend_func; gpointer suspend_data; int fd; guint guard_timeout_source; gboolean suspended; gboolean xmit_acfc; gboolean xmit_pfc; }; void ppp_debug(GAtPPP *ppp, const char *str) { if (ppp == NULL || ppp->debugf == NULL) return; ppp->debugf(str, ppp->debug_data); } static gboolean ppp_dead(gpointer userdata) { GAtPPP *ppp = userdata; DBG(ppp, ""); ppp->ppp_dead_source = 0; /* notify interested parties */ if (ppp->disconnect_cb) ppp->disconnect_cb(ppp->disconnect_reason, ppp->disconnect_data); return FALSE; } static void sta_sent(gpointer userdata) { GAtPPP *ppp = userdata; DBG(ppp, ""); ppp->sta_pending = FALSE; if (ppp->phase == PPP_PHASE_DEAD) ppp_dead(ppp); } struct ppp_header *ppp_packet_new(gsize infolen, guint16 protocol) { struct ppp_header *ppp_packet; ppp_packet = g_try_malloc0(infolen + sizeof(*ppp_packet)); if (ppp_packet == NULL) return NULL; ppp_packet->proto = htons(protocol); ppp_packet->address = PPP_ADDR_FIELD; ppp_packet->control = PPP_CTRL; return ppp_packet; } /* * Silently discard packets which are received when they shouldn't be */ static inline gboolean ppp_drop_packet(GAtPPP *ppp, guint16 protocol) { switch (ppp->phase) { case PPP_PHASE_ESTABLISHMENT: case PPP_PHASE_TERMINATION: if (protocol != LCP_PROTOCOL) return TRUE; break; case PPP_PHASE_AUTHENTICATION: if (protocol != LCP_PROTOCOL && protocol != CHAP_PROTOCOL && protocol != PAP_PROTOCOL) return TRUE; break; case PPP_PHASE_DEAD: return TRUE; case PPP_PHASE_NETWORK: if (protocol != LCP_PROTOCOL && protocol != CHAP_PROTOCOL && protocol != PAP_PROTOCOL && protocol != IPCP_PROTO) return TRUE; break; case PPP_PHASE_LINK_UP: break; } return FALSE; } static void ppp_receive(const unsigned char *buf, gsize len, void *data) { GAtPPP *ppp = data; unsigned int offset = 0; guint16 protocol; const guint8 *packet; if (len == 0) return; if (buf[0] == PPP_ADDR_FIELD && len >= 2 && buf[1] == PPP_CTRL) offset = 2; if (len < offset + 1) return; /* From RFC 1661: * the Protocol field uses an extension mechanism consistent with the * ISO 3309 extension mechanism for the Address field; the Least * Significant Bit (LSB) of each octet is used to indicate extension * of the Protocol field. A binary "0" as the LSB indicates that the * Protocol field continues with the following octet. The presence * of a binary "1" as the LSB marks the last octet of the Protocol * field. * * To check for compression we simply check the LSB of the first * protocol byte. */ if (buf[offset] & 0x1) { protocol = buf[offset]; offset += 1; } else { if (len < offset + 2) return; protocol = get_host_short(buf + offset); offset += 2; } if (ppp_drop_packet(ppp, protocol)) return; packet = buf + offset; switch (protocol) { case PPP_IP_PROTO: ppp_net_process_packet(ppp->net, packet, len - offset); break; case LCP_PROTOCOL: pppcp_process_packet(ppp->lcp, packet, len - offset); break; case IPCP_PROTO: pppcp_process_packet(ppp->ipcp, packet, len - offset); break; case PAP_PROTOCOL: if (ppp->pap) ppp_pap_process_packet(ppp->pap, packet, len - offset); else pppcp_send_protocol_reject(ppp->lcp, buf, len); break; case CHAP_PROTOCOL: if (ppp->chap) { ppp_chap_process_packet(ppp->chap, packet, len - offset); break; } /* fall through */ default: pppcp_send_protocol_reject(ppp->lcp, buf, len); break; }; } static void ppp_send_lcp_frame(GAtPPP *ppp, guint8 *packet, guint infolen) { struct ppp_header *header = (struct ppp_header *) packet; guint8 code; guint32 xmit_accm = 0; gboolean sta = FALSE; gboolean lcp; /* * all LCP Link Configuration, Link Termination, and Code-Reject * packets must be sent with the default sending ACCM */ code = pppcp_get_code(packet); lcp = code > 0 && code < 8; /* * If we're going down, we try to make sure to send the final * ack before informing the upper layers via the ppp_disconnect * function. Once we enter PPP_DEAD phase, no further packets * will be sent */ if (code == PPPCP_CODE_TYPE_TERMINATE_ACK) sta = TRUE; if (lcp) { xmit_accm = g_at_hdlc_get_xmit_accm(ppp->hdlc); g_at_hdlc_set_xmit_accm(ppp->hdlc, ~0U); } header->address = PPP_ADDR_FIELD; header->control = PPP_CTRL; if (g_at_hdlc_send(ppp->hdlc, packet, infolen + sizeof(*header)) == TRUE) { if (sta) { GAtIO *io = g_at_hdlc_get_io(ppp->hdlc); ppp->sta_pending = TRUE; g_at_io_set_write_done(io, sta_sent, ppp); } } else DBG(ppp, "Failed to send a frame\n"); if (lcp) g_at_hdlc_set_xmit_accm(ppp->hdlc, xmit_accm); } static void ppp_send_acfc_frame(GAtPPP *ppp, guint8 *packet, guint infolen) { struct ppp_header *header = (struct ppp_header *) packet; guint offset = 0; if (ppp->xmit_acfc) offset = 2; /* We remove the only address and control field */ if (g_at_hdlc_send(ppp->hdlc, packet + offset, infolen + sizeof(*header) - offset) == FALSE) DBG(ppp, "Failed to send a frame\n"); } static void ppp_send_acfc_pfc_frame(GAtPPP *ppp, guint8 *packet, guint infolen) { struct ppp_header *header = (struct ppp_header *) packet; guint offset = 0; if (ppp->xmit_acfc && ppp->xmit_pfc) offset = 3; else if (ppp->xmit_acfc) offset = 2; else if (ppp->xmit_pfc) { /* Shuffle AC bytes in place of the first protocol byte */ packet[2] = packet[1]; packet[1] = packet[0]; offset = 1; } if (g_at_hdlc_send(ppp->hdlc, packet + offset, infolen + sizeof(*header) - offset) == FALSE) DBG(ppp, "Failed to send a frame\n"); } /* * transmit out through the lower layer interface * * infolen - length of the information part of the packet */ void ppp_transmit(GAtPPP *ppp, guint8 *packet, guint infolen) { guint16 proto = ppp_proto(packet); if (proto == LCP_PROTOCOL) { ppp_send_lcp_frame(ppp, packet, infolen); return; } /* * If the upper 8 bits of the protocol are 0, then send * with PFC if enabled */ if ((proto & 0xff00) == 0) ppp_send_acfc_pfc_frame(ppp, packet, infolen); else ppp_send_acfc_frame(ppp, packet, infolen); } static inline void ppp_enter_phase(GAtPPP *ppp, enum ppp_phase phase) { DBG(ppp, "%d", phase); ppp->phase = phase; if (phase == PPP_PHASE_DEAD && ppp->sta_pending == FALSE) ppp->ppp_dead_source = g_idle_add(ppp_dead, ppp); } void ppp_set_auth(GAtPPP *ppp, const guint8* auth_data) { guint16 proto = get_host_short(auth_data); switch (proto) { case PAP_PROTOCOL: if (ppp->pap) ppp_pap_free(ppp->pap); ppp->pap = ppp_pap_new(ppp); break; case CHAP_PROTOCOL: if (ppp->chap) ppp_chap_free(ppp->chap); ppp->chap = ppp_chap_new(ppp, auth_data[2]); break; default: DBG(ppp, "unknown authentication proto"); break; } } void ppp_auth_notify(GAtPPP *ppp, gboolean success) { if (success == FALSE) { ppp->disconnect_reason = G_AT_PPP_REASON_AUTH_FAIL; pppcp_signal_close(ppp->lcp); return; } ppp_enter_phase(ppp, PPP_PHASE_NETWORK); /* Send UP & OPEN events to the IPCP layer */ pppcp_signal_open(ppp->ipcp); pppcp_signal_up(ppp->ipcp); } void ppp_ipcp_up_notify(GAtPPP *ppp, const char *local, const char *peer, const char *dns1, const char *dns2) { ppp->net = ppp_net_new(ppp, ppp->fd); /* * ppp_net_new took control over the fd, whatever happens is out of * our hands now */ ppp->fd = -1; if (ppp->net == NULL) { ppp->disconnect_reason = G_AT_PPP_REASON_NET_FAIL; pppcp_signal_close(ppp->lcp); return; } if (ppp_net_set_mtu(ppp->net, ppp->mtu) == FALSE) DBG(ppp, "Unable to set MTU"); ppp_enter_phase(ppp, PPP_PHASE_LINK_UP); if (ppp->connect_cb) ppp->connect_cb(ppp_net_get_interface(ppp->net), local, peer, dns1, dns2, ppp->connect_data); } void ppp_ipcp_down_notify(GAtPPP *ppp) { /* Most likely we failed to create the interface */ if (ppp->net == NULL) return; ppp_net_free(ppp->net); ppp->net = NULL; } void ppp_ipcp_finished_notify(GAtPPP *ppp) { if (ppp->phase != PPP_PHASE_NETWORK) return; /* Our IPCP parameter negotiation failed */ ppp->disconnect_reason = G_AT_PPP_REASON_IPCP_FAIL; pppcp_signal_close(ppp->ipcp); pppcp_signal_close(ppp->lcp); } void ppp_lcp_up_notify(GAtPPP *ppp) { if (ppp->chap != NULL) { /* Wait for the peer to send us a challenge. */ ppp_enter_phase(ppp, PPP_PHASE_AUTHENTICATION); return; } else if (ppp->pap != NULL) { /* Try to send an Authenticate-Request and wait for reply. */ if (ppp_pap_start(ppp->pap) == TRUE) ppp_enter_phase(ppp, PPP_PHASE_AUTHENTICATION); else /* It'll never work out. */ ppp_auth_notify(ppp, FALSE); return; } /* Otherwise proceed as if auth succeeded */ ppp_auth_notify(ppp, TRUE); } void ppp_lcp_down_notify(GAtPPP *ppp) { if (ppp->phase == PPP_PHASE_NETWORK || ppp->phase == PPP_PHASE_LINK_UP) pppcp_signal_down(ppp->ipcp); if (ppp->disconnect_reason == G_AT_PPP_REASON_UNKNOWN) ppp->disconnect_reason = G_AT_PPP_REASON_PEER_CLOSED; ppp_enter_phase(ppp, PPP_PHASE_TERMINATION); } void ppp_lcp_finished_notify(GAtPPP *ppp) { ppp_enter_phase(ppp, PPP_PHASE_DEAD); } void ppp_set_recv_accm(GAtPPP *ppp, guint32 accm) { g_at_hdlc_set_recv_accm(ppp->hdlc, accm); } void ppp_set_xmit_accm(GAtPPP *ppp, guint32 accm) { g_at_hdlc_set_xmit_accm(ppp->hdlc, accm); } /* * The only time we use other than default MTU is when we are in * the network phase. */ void ppp_set_mtu(GAtPPP *ppp, const guint8 *data) { guint16 mtu = get_host_short(data); ppp->mtu = mtu; } void ppp_set_xmit_acfc(GAtPPP *ppp, gboolean acfc) { ppp->xmit_acfc = acfc; } void ppp_set_xmit_pfc(GAtPPP *ppp, gboolean pfc) { ppp->xmit_pfc = pfc; } static void io_disconnect(gpointer user_data) { GAtPPP *ppp = user_data; if (ppp->phase == PPP_PHASE_DEAD) return; ppp->disconnect_reason = G_AT_PPP_REASON_LINK_DEAD; pppcp_signal_down(ppp->lcp); pppcp_signal_close(ppp->lcp); } static void ppp_proxy_suspend_net_interface(gpointer user_data) { GAtPPP *ppp = user_data; ppp->suspended = TRUE; ppp_net_suspend_interface(ppp->net); if (ppp->suspend_func) ppp->suspend_func(ppp->suspend_data); } gboolean g_at_ppp_listen(GAtPPP *ppp, GAtIO *io) { ppp->hdlc = g_at_hdlc_new_from_io(io); if (ppp->hdlc == NULL) return FALSE; ppp->suspended = FALSE; g_at_hdlc_set_receive(ppp->hdlc, ppp_receive, ppp); g_at_hdlc_set_suspend_function(ppp->hdlc, ppp_proxy_suspend_net_interface, ppp); g_at_io_set_disconnect_function(io, io_disconnect, ppp); ppp_enter_phase(ppp, PPP_PHASE_ESTABLISHMENT); return TRUE; } /* Administrative Open */ gboolean g_at_ppp_open(GAtPPP *ppp, GAtIO *io) { ppp->hdlc = g_at_hdlc_new_from_io(io); if (ppp->hdlc == NULL) return FALSE; ppp->suspended = FALSE; g_at_hdlc_set_receive(ppp->hdlc, ppp_receive, ppp); g_at_hdlc_set_suspend_function(ppp->hdlc, ppp_proxy_suspend_net_interface, ppp); g_at_hdlc_set_no_carrier_detect(ppp->hdlc, TRUE); g_at_io_set_disconnect_function(io, io_disconnect, ppp); /* send an UP & OPEN events to the lcp layer */ pppcp_signal_up(ppp->lcp); pppcp_signal_open(ppp->lcp); ppp_enter_phase(ppp, PPP_PHASE_ESTABLISHMENT); return TRUE; } gboolean g_at_ppp_set_credentials(GAtPPP *ppp, const char *username, const char *password) { if (username && strlen(username) > 255) return FALSE; if (password && strlen(password) > 255) return FALSE; memset(ppp->username, 0, sizeof(ppp->username)); memset(ppp->password, 0, sizeof(ppp->password)); if (username) strcpy(ppp->username, username); if (password) strcpy(ppp->password, password); return TRUE; } const char *g_at_ppp_get_username(GAtPPP *ppp) { return ppp->username; } const char *g_at_ppp_get_password(GAtPPP *ppp) { return ppp->password; } gboolean g_at_ppp_set_auth_method(GAtPPP *ppp, GAtPPPAuthMethod method) { if (method != G_AT_PPP_AUTH_METHOD_CHAP && method != G_AT_PPP_AUTH_METHOD_PAP) return FALSE; ppp->auth_method = method; return TRUE; } GAtPPPAuthMethod g_at_ppp_get_auth_method(GAtPPP *ppp) { return ppp->auth_method; } void g_at_ppp_set_recording(GAtPPP *ppp, const char *filename) { if (ppp == NULL) return; g_at_hdlc_set_recording(ppp->hdlc, filename); } void g_at_ppp_set_connect_function(GAtPPP *ppp, GAtPPPConnectFunc func, gpointer user_data) { if (func == NULL) return; ppp->connect_cb = func; ppp->connect_data = user_data; } void g_at_ppp_set_disconnect_function(GAtPPP *ppp, GAtPPPDisconnectFunc func, gpointer user_data) { if (func == NULL) return; ppp->disconnect_cb = func; ppp->disconnect_data = user_data; } void g_at_ppp_set_debug(GAtPPP *ppp, GAtDebugFunc func, gpointer user_data) { if (ppp == NULL) return; ppp->debugf = func; ppp->debug_data = user_data; } void g_at_ppp_set_suspend_function(GAtPPP *ppp, GAtSuspendFunc func, gpointer user_data) { if (ppp == NULL) return; ppp->suspend_func = func; ppp->suspend_data = user_data; if (ppp->hdlc != NULL) g_at_hdlc_set_suspend_function(ppp->hdlc, ppp_proxy_suspend_net_interface, ppp); } void g_at_ppp_shutdown(GAtPPP *ppp) { if (ppp->phase == PPP_PHASE_DEAD || ppp->phase == PPP_PHASE_TERMINATION) return; ppp->disconnect_reason = G_AT_PPP_REASON_LOCAL_CLOSE; pppcp_signal_close(ppp->lcp); } static gboolean call_suspend_cb(gpointer user_data) { GAtPPP *ppp = user_data; ppp->guard_timeout_source = 0; if (ppp->suspend_func) ppp->suspend_func(ppp->suspend_data); return FALSE; } static gboolean send_escape_sequence(gpointer user_data) { GAtPPP *ppp = user_data; GAtIO *io = g_at_hdlc_get_io(ppp->hdlc); g_at_io_write(io, "+++", 3); ppp->guard_timeout_source = g_timeout_add(GUARD_TIMEOUTS, call_suspend_cb, ppp); return FALSE; } void g_at_ppp_suspend(GAtPPP *ppp) { if (ppp == NULL) return; ppp->suspended = TRUE; ppp_net_suspend_interface(ppp->net); g_at_hdlc_suspend(ppp->hdlc); ppp->guard_timeout_source = g_timeout_add(GUARD_TIMEOUTS, send_escape_sequence, ppp); } void g_at_ppp_resume(GAtPPP *ppp) { if (ppp == NULL) return; if (g_at_hdlc_get_io(ppp->hdlc) == NULL) { io_disconnect(ppp); return; } ppp->suspended = FALSE; g_at_io_set_disconnect_function(g_at_hdlc_get_io(ppp->hdlc), io_disconnect, ppp); ppp_net_resume_interface(ppp->net); g_at_hdlc_resume(ppp->hdlc); } void g_at_ppp_ref(GAtPPP *ppp) { g_atomic_int_inc(&ppp->ref_count); } void g_at_ppp_unref(GAtPPP *ppp) { gboolean is_zero; if (ppp == NULL) return; is_zero = g_atomic_int_dec_and_test(&ppp->ref_count); if (is_zero == FALSE) return; if (ppp->suspended == FALSE) g_at_io_set_disconnect_function(g_at_hdlc_get_io(ppp->hdlc), NULL, NULL); if (ppp->net) ppp_net_free(ppp->net); else if (ppp->fd >= 0) close(ppp->fd); if (ppp->pap) ppp_pap_free(ppp->pap); if (ppp->chap) ppp_chap_free(ppp->chap); lcp_free(ppp->lcp); ipcp_free(ppp->ipcp); if (ppp->ppp_dead_source) { g_source_remove(ppp->ppp_dead_source); ppp->ppp_dead_source = 0; } if (ppp->guard_timeout_source) { g_source_remove(ppp->guard_timeout_source); ppp->guard_timeout_source = 0; } g_at_hdlc_unref(ppp->hdlc); g_free(ppp); } void g_at_ppp_set_server_info(GAtPPP *ppp, const char *remote, const char *dns1, const char *dns2) { guint32 r = 0; guint32 d1 = 0; guint32 d2 = 0; inet_pton(AF_INET, remote, &r); inet_pton(AF_INET, dns1, &d1); inet_pton(AF_INET, dns2, &d2); ipcp_set_server_info(ppp->ipcp, r, d1, d2); } void g_at_ppp_set_acfc_enabled(GAtPPP *ppp, gboolean enabled) { lcp_set_acfc_enabled(ppp->lcp, enabled); } void g_at_ppp_set_pfc_enabled(GAtPPP *ppp, gboolean enabled) { lcp_set_pfc_enabled(ppp->lcp, enabled); } static GAtPPP *ppp_init_common(gboolean is_server, guint32 ip) { GAtPPP *ppp; ppp = g_try_malloc0(sizeof(GAtPPP)); if (ppp == NULL) return NULL; ppp->ref_count = 1; ppp->suspended = TRUE; ppp->fd = -1; /* set options to defaults */ ppp->mru = DEFAULT_MRU; ppp->mtu = DEFAULT_MTU; /* initialize the lcp state */ ppp->lcp = lcp_new(ppp, is_server); /* initialize IPCP state */ ppp->ipcp = ipcp_new(ppp, is_server, ip); /* chap authentication by default */ ppp->auth_method = G_AT_PPP_AUTH_METHOD_CHAP; return ppp; } GAtPPP *g_at_ppp_new(void) { return ppp_init_common(FALSE, 0); } GAtPPP *g_at_ppp_server_new_full(const char *local, int fd) { GAtPPP *ppp; guint32 ip; if (local == NULL) ip = 0; else if (inet_pton(AF_INET, local, &ip) != 1) return NULL; ppp = ppp_init_common(TRUE, ip); if (ppp != NULL) ppp->fd = fd; return ppp; } GAtPPP *g_at_ppp_server_new(const char *local) { return g_at_ppp_server_new_full(local, -1); } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gathdlc.h0000644000015600001650000000404412671500024021573 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 * */ #ifndef __G_AT_HDLC_H #define __G_AT_HDLC_H #include "gat.h" #include "gatio.h" #ifdef __cplusplus extern "C" { #endif struct _GAtHDLC; typedef struct _GAtHDLC GAtHDLC; GAtHDLC *g_at_hdlc_new(GIOChannel *channel); GAtHDLC *g_at_hdlc_new_from_io(GAtIO *io); GAtHDLC *g_at_hdlc_ref(GAtHDLC *hdlc); void g_at_hdlc_unref(GAtHDLC *hdlc); void g_at_hdlc_set_debug(GAtHDLC *hdlc, GAtDebugFunc func, gpointer user_data); void g_at_hdlc_set_xmit_accm(GAtHDLC *hdlc, guint32 accm); guint32 g_at_hdlc_get_xmit_accm(GAtHDLC *hdlc); void g_at_hdlc_set_recv_accm(GAtHDLC *hdlc, guint32 accm); guint32 g_at_hdlc_get_recv_accm(GAtHDLC *hdlc); void g_at_hdlc_set_receive(GAtHDLC *hdlc, GAtReceiveFunc func, gpointer user_data); gboolean g_at_hdlc_send(GAtHDLC *hdlc, const unsigned char *data, gsize size); void g_at_hdlc_set_recording(GAtHDLC *hdlc, const char *filename); GAtIO *g_at_hdlc_get_io(GAtHDLC *hdlc); void g_at_hdlc_set_start_frame_marker(GAtHDLC *hdlc, gboolean marker); void g_at_hdlc_set_no_carrier_detect(GAtHDLC *hdlc, gboolean detect); void g_at_hdlc_set_suspend_function(GAtHDLC *hdlc, GAtSuspendFunc func, gpointer user_data); void g_at_hdlc_suspend(GAtHDLC *hdlc); void g_at_hdlc_resume(GAtHDLC *hdlc); #ifdef __cplusplus } #endif #endif /* __G_AT_HDLC_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatutil.h0000644000015600001650000000253012671500024021634 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 * */ #ifndef __GATUTIL_H #define __GATUTIL_H #include "gat.h" #ifdef __cplusplus extern "C" { #endif void g_at_util_debug_chat(gboolean in, const char *str, gsize len, GAtDebugFunc debugf, gpointer user_data); void g_at_util_debug_dump(gboolean in, const unsigned char *buf, gsize len, GAtDebugFunc debugf, gpointer user_data); void g_at_util_debug_hexdump(gboolean in, const unsigned char *buf, gsize len, GAtDebugFunc debugf, gpointer user_data); gboolean g_at_util_setup_io(GIOChannel *io, GIOFlags flags); #ifdef __cplusplus } #endif #endif /* __GATUTIL_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatutil.c0000644000015600001650000001064012671500024021630 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 #endif #include #include #include #include #include "gatutil.h" void g_at_util_debug_chat(gboolean in, const char *str, gsize len, GAtDebugFunc debugf, gpointer user_data) { char type = in ? '<' : '>'; gsize escaped = 2; /* Enough for '<', ' ' */ char *escaped_str; const char *esc = ""; gsize esc_size = strlen(esc); const char *ctrlz = ""; gsize ctrlz_size = strlen(ctrlz); gsize i; if (debugf == NULL || !len) return; for (i = 0; i < len; i++) { char c = str[i]; if (g_ascii_isprint(c)) escaped += 1; else if (c == '\r' || c == '\t' || c == '\n') escaped += 2; else if (c == 26) escaped += ctrlz_size; else if (c == 25) escaped += esc_size; else escaped += 4; } escaped_str = g_try_malloc(escaped + 1); if (escaped_str == NULL) return; escaped_str[0] = type; escaped_str[1] = ' '; escaped_str[2] = '\0'; escaped_str[escaped] = '\0'; for (escaped = 2, i = 0; i < len; i++) { unsigned char c = str[i]; switch (c) { case '\r': escaped_str[escaped++] = '\\'; escaped_str[escaped++] = 'r'; break; case '\t': escaped_str[escaped++] = '\\'; escaped_str[escaped++] = 't'; break; case '\n': escaped_str[escaped++] = '\\'; escaped_str[escaped++] = 'n'; break; case 26: strncpy(&escaped_str[escaped], ctrlz, ctrlz_size); escaped += ctrlz_size; break; case 25: strncpy(&escaped_str[escaped], esc, esc_size); escaped += esc_size; break; default: if (g_ascii_isprint(c)) escaped_str[escaped++] = c; else { escaped_str[escaped++] = '\\'; escaped_str[escaped++] = '0' + ((c >> 6) & 07); escaped_str[escaped++] = '0' + ((c >> 3) & 07); escaped_str[escaped++] = '0' + (c & 07); } } } debugf(escaped_str, user_data); g_free(escaped_str); } void g_at_util_debug_dump(gboolean in, const unsigned char *buf, gsize len, GAtDebugFunc debugf, gpointer user_data) { char type = in ? '<' : '>'; GString *str; gsize i; if (debugf == NULL || !len) return; str = g_string_sized_new(1 + (len * 2)); if (str == NULL) return; g_string_append_c(str, type); for (i = 0; i < len; i++) g_string_append_printf(str, " %02x", buf[i]); debugf(str->str, user_data); g_string_free(str, TRUE); } void g_at_util_debug_hexdump(gboolean in, const unsigned char *buf, gsize len, GAtDebugFunc debugf, gpointer user_data) { static const char hexdigits[] = "0123456789abcdef"; char str[68]; gsize i; if (debugf == NULL || !len) return; str[0] = in ? '<' : '>'; for (i = 0; i < len; i++) { str[((i % 16) * 3) + 1] = ' '; str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4]; str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf]; str[(i % 16) + 51] = g_ascii_isprint(buf[i]) ? buf[i] : '.'; if ((i + 1) % 16 == 0) { str[49] = ' '; str[50] = ' '; str[67] = '\0'; debugf(str, user_data); str[0] = ' '; } } if (i % 16 > 0) { gsize j; for (j = (i % 16); j < 16; j++) { str[(j * 3) + 1] = ' '; str[(j * 3) + 2] = ' '; str[(j * 3) + 3] = ' '; str[j + 51] = ' '; } str[49] = ' '; str[50] = ' '; str[67] = '\0'; debugf(str, user_data); } } gboolean g_at_util_setup_io(GIOChannel *io, GIOFlags flags) { GIOFlags io_flags; if (g_io_channel_set_encoding(io, NULL, NULL) != G_IO_STATUS_NORMAL) return FALSE; g_io_channel_set_buffered(io, FALSE); if (flags & G_IO_FLAG_SET_MASK) { io_flags = g_io_channel_get_flags(io); io_flags |= (flags & G_IO_FLAG_SET_MASK); if (g_io_channel_set_flags(io, io_flags, NULL) != G_IO_STATUS_NORMAL) return FALSE; } g_io_channel_set_close_on_unref(io, TRUE); return TRUE; } ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gattty.h0000644000015600001650000000272012671500024021500 0ustar pbuserpbgroup00000000000000/* * * AT chat library with GLib integration * * 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 * */ #ifndef __GATTTY_H #define __GATTTY_H #ifdef __cplusplus extern "C" { #endif /*! * Opens a serial port given by tty. If options is NULL, then the serial port * is opened in raw mode. Otherwise the options are parsed and set accordingly * * The following keys / values are recognized (all strings) * * "Baud" - "300", "600", etc * "Stopbits" - "1", "2" * "Databits" - "7", "8" * "Parity" - "none", "odd", "even" * "XonXoff" - "on", "off" * "RtsCts" - "on", "off" * "Local" - "on", "off" * "Read" - "on, "off" */ GIOChannel *g_at_tty_open(const char *tty, GHashTable *options); GIOChannel *g_at_tty_open_qcdm(const char *tty); #ifdef __cplusplus } #endif #endif /* __GATTTY_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/gatserver.h0000644000015600001650000001026212671500024022166 0ustar pbuserpbgroup00000000000000/* * * AT Server library with GLib integration * * 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 * */ #ifndef __GATSERVER_H #define __GATSERVER_H #ifdef __cplusplus extern "C" { #endif #include "gatresult.h" #include "gatutil.h" #include "gatio.h" struct _GAtServer; typedef struct _GAtServer GAtServer; /* V.250 Table 1/V.250 Result codes */ enum _GAtServerResult { G_AT_SERVER_RESULT_OK = 0, G_AT_SERVER_RESULT_CONNECT = 1, G_AT_SERVER_RESULT_RING = 2, G_AT_SERVER_RESULT_NO_CARRIER = 3, G_AT_SERVER_RESULT_ERROR = 4, G_AT_SERVER_RESULT_NO_DIALTONE = 6, G_AT_SERVER_RESULT_BUSY = 7, G_AT_SERVER_RESULT_NO_ANSWER = 8, G_AT_SERVER_RESULT_EXT_ERROR = 256, }; typedef enum _GAtServerResult GAtServerResult; /* Types of AT command: * COMMAND_ONLY: command without any sub-parameters, e.g. ATA, AT+CLCC * QUERY: command followed by '?', e.g. AT+CPIN? * SUPPORT: command followed by '=?', e.g. AT+CSMS=? * SET: command followed by '=', e.g. AT+CLIP=1 * or, basic command followed with sub-parameters, e.g. ATD12345; */ enum _GAtServerRequestType { G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY, G_AT_SERVER_REQUEST_TYPE_QUERY, G_AT_SERVER_REQUEST_TYPE_SUPPORT, G_AT_SERVER_REQUEST_TYPE_SET, }; typedef enum _GAtServerRequestType GAtServerRequestType; typedef void (*GAtServerNotifyFunc)(GAtServer *server, GAtServerRequestType type, GAtResult *result, gpointer user_data); typedef void (*GAtServerFinishFunc)(GAtServer *server, gpointer user_data); GAtServer *g_at_server_new(GIOChannel *io); GIOChannel *g_at_server_get_channel(GAtServer *server); GAtIO *g_at_server_get_io(GAtServer *server); GAtServer *g_at_server_ref(GAtServer *server); void g_at_server_suspend(GAtServer *server); void g_at_server_resume(GAtServer *server); void g_at_server_unref(GAtServer *server); gboolean g_at_server_shutdown(GAtServer *server); gboolean g_at_server_set_echo(GAtServer *server, gboolean echo); gboolean g_at_server_set_disconnect_function(GAtServer *server, GAtDisconnectFunc disconnect, gpointer user_data); gboolean g_at_server_set_debug(GAtServer *server, GAtDebugFunc func, gpointer user_data); gboolean g_at_server_register(GAtServer *server, const char *prefix, GAtServerNotifyFunc notify, gpointer user_data, GDestroyNotify destroy_notify); gboolean g_at_server_unregister(GAtServer *server, const char *prefix); /* Send a final result code. E.g. G_AT_SERVER_RESULT_NO_DIALTONE */ void g_at_server_send_final(GAtServer *server, GAtServerResult result); /* Send an extended final result code. E.g. +CME ERROR: SIM failure. */ void g_at_server_send_ext_final(GAtServer *server, const char *result); /* Send an intermediate result code to report the progress. E.g. CONNECT */ void g_at_server_send_intermediate(GAtServer *server, const char *result); /* Send an unsolicited result code. E.g. RING */ void g_at_server_send_unsolicited(GAtServer *server, const char *result); /* * Send a single response line for the command. The line should be no longer * than 2048 characters. If the response contains multiple lines, use * FALSE for the 'last' parameter for lines 1 .. n -1, and 'TRUE' for the last * line. This is required for formatting of 27.007 compliant multi-line * responses. */ void g_at_server_send_info(GAtServer *server, const char *line, gboolean last); gboolean g_at_server_set_finish_callback(GAtServer *server, GAtServerFinishFunc finishf, gpointer user_data); gboolean g_at_server_command_pending(GAtServer *server); #ifdef __cplusplus } #endif #endif /* __GATSERVER_H */ ofono-1.17.bzr6912+16.04.20160314.3/gatchat/ppp_ipv6cp.c0000644000015600001650000002150612671500024022250 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2009-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 #endif #include #include #include #include #include #include #include "gatppp.h" #include "ppp.h" #define IPV6CP_SUPPORTED_CODES ((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_NAK) | \ (1 << PPPCP_CODE_TYPE_CONFIGURE_REJECT) | \ (1 << PPPCP_CODE_TYPE_TERMINATE_REQUEST) | \ (1 << PPPCP_CODE_TYPE_TERMINATE_ACK) | \ (1 << PPPCP_CODE_TYPE_CODE_REJECT)) #define OPTION_COPY(_options, _len, _req, _type, _var, _opt_len) \ if (_req) { \ _options[_len] = _type; \ _options[_len + 1] = _opt_len + 2; \ memcpy(_options + _len + 2, _var, _opt_len); \ _len += _opt_len + 2; \ } /* We request only IPv6 Interface Id */ #define IPV6CP_MAX_CONFIG_OPTION_SIZE 10 #define IPV6CP_MAX_FAILURE 3 #define IPV6CP_ERROR ipv6cp_error_quark() enum ipv6cp_option_types { IPV6CP_INTERFACE_ID = 1, }; struct ipv6cp_data { guint8 options[IPV6CP_MAX_CONFIG_OPTION_SIZE]; guint16 options_len; guint8 req_options; guint64 local_addr; guint64 peer_addr; gboolean is_server; }; static GQuark ipv6cp_error_quark(void) { return g_quark_from_static_string("ipv6cp"); } static void ipv6cp_generate_config_options(struct ipv6cp_data *ipv6cp) { guint16 len = 0; OPTION_COPY(ipv6cp->options, len, ipv6cp->req_options & IPV6CP_INTERFACE_ID, IPV6CP_INTERFACE_ID, &ipv6cp->local_addr, sizeof(ipv6cp->local_addr)); ipv6cp->options_len = len; } static void ipv6cp_reset_config_options(struct ipv6cp_data *ipv6cp) { ipv6cp->req_options = IPV6CP_INTERFACE_ID; ipv6cp_generate_config_options(ipv6cp); } static void ipv6cp_up(struct pppcp_data *pppcp) { } static void ipv6cp_down(struct pppcp_data *pppcp) { struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp); ipv6cp_reset_config_options(ipv6cp); pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len); } static void ipv6cp_finished(struct pppcp_data *pppcp) { } static enum rcr_result ipv6cp_server_rcr(struct ipv6cp_data *ipv6cp, const struct pppcp_packet *packet, guint8 **new_options, guint16 *new_len) { struct ppp_option_iter iter; guint8 nak_options[IPV6CP_MAX_CONFIG_OPTION_SIZE]; guint16 len = 0; guint8 *rej_options = NULL; guint16 rej_len = 0; guint64 addr; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { guint8 type = ppp_option_iter_get_type(&iter); const void *data = ppp_option_iter_get_data(&iter); switch (type) { case IPV6CP_INTERFACE_ID: memcpy(&addr, data, sizeof(addr)); OPTION_COPY(nak_options, len, addr != ipv6cp->peer_addr || addr == 0, type, &ipv6cp->peer_addr, ppp_option_iter_get_length(&iter)); break; default: if (rej_options == NULL) { guint16 max_len = ntohs(packet->length) - 4; rej_options = g_new0(guint8, max_len); } OPTION_COPY(rej_options, rej_len, rej_options != NULL, type, data, ppp_option_iter_get_length(&iter)); break; } } if (rej_len > 0) { *new_len = rej_len; *new_options = rej_options; return RCR_REJECT; } if (len > 0) { *new_len = len; *new_options = g_memdup(nak_options, len); return RCR_NAK; } return RCR_ACCEPT; } static enum rcr_result ipv6cp_client_rcr(struct ipv6cp_data *ipv6cp, const struct pppcp_packet *packet, guint8 **new_options, guint16 *new_len) { struct ppp_option_iter iter; guint8 *options = NULL; guint8 len = 0; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { guint8 type = ppp_option_iter_get_type(&iter); const void *data = ppp_option_iter_get_data(&iter); switch (type) { case IPV6CP_INTERFACE_ID: memcpy(&ipv6cp->peer_addr, data, sizeof(ipv6cp->peer_addr)); if (ipv6cp->peer_addr != 0) break; /* * Fall through, reject zero Interface ID */ default: if (options == NULL) { guint16 max_len = ntohs(packet->length) - 4; options = g_new0(guint8, max_len); } OPTION_COPY(options, len, options != NULL, type, data, ppp_option_iter_get_length(&iter)); break; } } if (len > 0) { *new_len = len; *new_options = options; return RCR_REJECT; } return RCR_ACCEPT; } static enum rcr_result ipv6cp_rcr(struct pppcp_data *pppcp, const struct pppcp_packet *packet, guint8 **new_options, guint16 *new_len) { struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp); if (ipv6cp->is_server) return ipv6cp_server_rcr(ipv6cp, packet, new_options, new_len); else return ipv6cp_client_rcr(ipv6cp, packet, new_options, new_len); } static void ipv6cp_rca(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp); struct ppp_option_iter iter; if (ipv6cp->is_server) return; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { const guint8 *data = ppp_option_iter_get_data(&iter); switch (ppp_option_iter_get_type(&iter)) { case IPV6CP_INTERFACE_ID: memcpy(&ipv6cp->local_addr, data, sizeof(ipv6cp->local_addr)); break; default: break; } } } static void ipv6cp_rcn_nak(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp); struct ppp_option_iter iter; if (ipv6cp->is_server) return; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { const guint8 *data = ppp_option_iter_get_data(&iter); switch (ppp_option_iter_get_type(&iter)) { case IPV6CP_INTERFACE_ID: ipv6cp->req_options |= IPV6CP_INTERFACE_ID; memcpy(&ipv6cp->local_addr, data, sizeof(ipv6cp->local_addr)); break; default: break; } } ipv6cp_generate_config_options(ipv6cp); pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len); } static void ipv6cp_rcn_rej(struct pppcp_data *pppcp, const struct pppcp_packet *packet) { struct ipv6cp_data *ipv6cp = pppcp_get_data(pppcp); struct ppp_option_iter iter; ppp_option_iter_init(&iter, packet); while (ppp_option_iter_next(&iter) == TRUE) { switch (ppp_option_iter_get_type(&iter)) { case IPV6CP_INTERFACE_ID: ipv6cp->req_options &= ~IPV6CP_INTERFACE_ID; break; default: break; } } ipv6cp_generate_config_options(ipv6cp); pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len); } struct pppcp_proto ipv6cp_proto = { .proto = IPV6CP_PROTO, .name = "ipv6cp", .supported_codes = IPV6CP_SUPPORTED_CODES, .this_layer_up = ipv6cp_up, .this_layer_down = ipv6cp_down, .this_layer_finished = ipv6cp_finished, .rca = ipv6cp_rca, .rcn_nak = ipv6cp_rcn_nak, .rcn_rej = ipv6cp_rcn_rej, .rcr = ipv6cp_rcr, }; struct pppcp_data *ipv6cp_new(GAtPPP *ppp, gboolean is_server, const char *local, const char *peer, GError **error) { struct ipv6cp_data *ipv6cp; struct pppcp_data *pppcp; struct in6_addr local_addr; struct in6_addr peer_addr; if (local == NULL) memset(&local_addr, 0, sizeof(local_addr)); else if (inet_pton(AF_INET6, local, &local_addr) != 1) { g_set_error(error, IPV6CP_ERROR, errno, "Unable to set local Interface ID: %s", strerror(errno)); return NULL; } if (peer == NULL) memset(&peer_addr, 0, sizeof(peer_addr)); else if (inet_pton(AF_INET6, peer, &peer_addr) != 1) { g_set_error(error, IPV6CP_ERROR, errno, "Unable to set peer Interface ID: %s", g_strerror(errno)); return NULL; } ipv6cp = g_try_new0(struct ipv6cp_data, 1); if (ipv6cp == NULL) return NULL; pppcp = pppcp_new(ppp, &ipv6cp_proto, FALSE, IPV6CP_MAX_FAILURE); if (pppcp == NULL) { g_free(ipv6cp); return NULL; } memcpy(&ipv6cp->local_addr, &local_addr.s6_addr[8], sizeof(ipv6cp->local_addr)); memcpy(&ipv6cp->peer_addr, &peer_addr.s6_addr[8], sizeof(ipv6cp->peer_addr)); ipv6cp->is_server = is_server; pppcp_set_data(pppcp, ipv6cp); ipv6cp_reset_config_options(ipv6cp); pppcp_set_local_options(pppcp, ipv6cp->options, ipv6cp->options_len); return pppcp; } void ipv6cp_free(struct pppcp_data *data) { struct ipv6cp_data *ipv6cp = pppcp_get_data(data); g_free(ipv6cp); pppcp_free(data); } ofono-1.17.bzr6912+16.04.20160314.3/btio/0000755000015600001650000000000012671500304017335 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/btio/btio.h0000644000015600001650000000532512671500024020447 0ustar pbuserpbgroup00000000000000/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2009-2010 Marcel Holtmann * Copyright (C) 2009-2010 Nokia Corporation * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef BT_IO_H #define BT_IO_H #include typedef enum { BT_IO_ERROR_DISCONNECTED, BT_IO_ERROR_CONNECT_FAILED, BT_IO_ERROR_FAILED, BT_IO_ERROR_INVALID_ARGS, } BtIOError; #define BT_IO_ERROR bt_io_error_quark() GQuark bt_io_error_quark(void); typedef enum { BT_IO_L2RAW, BT_IO_L2CAP, BT_IO_L2ERTM, BT_IO_RFCOMM, BT_IO_SCO, } BtIOType; typedef enum { BT_IO_OPT_INVALID = 0, BT_IO_OPT_SOURCE, BT_IO_OPT_SOURCE_BDADDR, BT_IO_OPT_DEST, BT_IO_OPT_DEST_BDADDR, BT_IO_OPT_DEFER_TIMEOUT, BT_IO_OPT_SEC_LEVEL, BT_IO_OPT_KEY_SIZE, BT_IO_OPT_CHANNEL, BT_IO_OPT_SOURCE_CHANNEL, BT_IO_OPT_DEST_CHANNEL, BT_IO_OPT_PSM, BT_IO_OPT_CID, BT_IO_OPT_MTU, BT_IO_OPT_OMTU, BT_IO_OPT_IMTU, BT_IO_OPT_MASTER, BT_IO_OPT_HANDLE, BT_IO_OPT_CLASS, BT_IO_OPT_MODE, BT_IO_OPT_FLUSHABLE, BT_IO_OPT_PRIORITY, } BtIOOption; typedef enum { BT_IO_SEC_SDP = 0, BT_IO_SEC_LOW, BT_IO_SEC_MEDIUM, BT_IO_SEC_HIGH, } BtIOSecLevel; typedef enum { BT_IO_MODE_BASIC = 0, BT_IO_MODE_RETRANS, BT_IO_MODE_FLOWCTL, BT_IO_MODE_ERTM, BT_IO_MODE_STREAMING } BtIOMode; typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data); typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data); gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError **err); gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err, BtIOOption opt1, ...); gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err, BtIOOption opt1, ...); GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError **err, BtIOOption opt1, ...); GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect, BtIOConfirm confirm, gpointer user_data, GDestroyNotify destroy, GError **err, BtIOOption opt1, ...); #endif ofono-1.17.bzr6912+16.04.20160314.3/btio/btio.c0000644000015600001650000007734712671500024020457 0ustar pbuserpbgroup00000000000000/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2009-2010 Marcel Holtmann * Copyright (C) 2009-2010 Nokia Corporation * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "btio.h" #ifndef BT_FLUSHABLE #define BT_FLUSHABLE 8 #endif #define ERROR_FAILED(gerr, str, err) \ g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \ str ": %s (%d)", strerror(err), err) #define DEFAULT_DEFER_TIMEOUT 30 struct set_opts { bdaddr_t src; bdaddr_t dst; int defer; int sec_level; uint8_t channel; uint16_t psm; uint16_t cid; uint16_t mtu; uint16_t imtu; uint16_t omtu; int master; uint8_t mode; int flushable; uint32_t priority; }; struct connect { BtIOConnect connect; gpointer user_data; GDestroyNotify destroy; }; struct accept { BtIOConnect connect; gpointer user_data; GDestroyNotify destroy; }; struct server { BtIOConnect connect; BtIOConfirm confirm; gpointer user_data; GDestroyNotify destroy; }; static void server_remove(struct server *server) { if (server->destroy) server->destroy(server->user_data); g_free(server); } static void connect_remove(struct connect *conn) { if (conn->destroy) conn->destroy(conn->user_data); g_free(conn); } static void accept_remove(struct accept *accept) { if (accept->destroy) accept->destroy(accept->user_data); g_free(accept); } static gboolean check_nval(GIOChannel *io) { struct pollfd fds; memset(&fds, 0, sizeof(fds)); fds.fd = g_io_channel_unix_get_fd(io); fds.events = POLLNVAL; if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL)) return TRUE; return FALSE; } static gboolean accept_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct accept *accept = user_data; GError *err = NULL; /* If the user aborted this accept attempt */ if ((cond & G_IO_NVAL) || check_nval(io)) return FALSE; if (cond & (G_IO_HUP | G_IO_ERR)) g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED, "HUP or ERR on socket"); accept->connect(io, err, accept->user_data); g_clear_error(&err); return FALSE; } static gboolean connect_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct connect *conn = user_data; GError *gerr = NULL; /* If the user aborted this connect attempt */ if ((cond & G_IO_NVAL) || check_nval(io)) return FALSE; if (cond & G_IO_OUT) { int err, sk_err = 0, sock = g_io_channel_unix_get_fd(io); socklen_t len = sizeof(sk_err); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) err = -errno; else err = -sk_err; if (err < 0) g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED, "%s (%d)", strerror(-err), -err); } else if (cond & (G_IO_HUP | G_IO_ERR)) g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED, "HUP or ERR on socket"); conn->connect(io, gerr, conn->user_data); if (gerr) g_error_free(gerr); return FALSE; } static gboolean server_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct server *server = user_data; int srv_sock, cli_sock; GIOChannel *cli_io; /* If the user closed the server */ if ((cond & G_IO_NVAL) || check_nval(io)) return FALSE; srv_sock = g_io_channel_unix_get_fd(io); cli_sock = accept(srv_sock, NULL, NULL); if (cli_sock < 0) return TRUE; cli_io = g_io_channel_unix_new(cli_sock); g_io_channel_set_close_on_unref(cli_io, TRUE); g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL); if (server->confirm) server->confirm(cli_io, server->user_data); else server->connect(cli_io, NULL, server->user_data); g_io_channel_unref(cli_io); return TRUE; } static void server_add(GIOChannel *io, BtIOConnect connect, BtIOConfirm confirm, gpointer user_data, GDestroyNotify destroy) { struct server *server; GIOCondition cond; server = g_new0(struct server, 1); server->connect = connect; server->confirm = confirm; server->user_data = user_data; server->destroy = destroy; cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server, (GDestroyNotify) server_remove); } static void connect_add(GIOChannel *io, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy) { struct connect *conn; GIOCondition cond; conn = g_new0(struct connect, 1); conn->connect = connect; conn->user_data = user_data; conn->destroy = destroy; cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn, (GDestroyNotify) connect_remove); } static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy) { struct accept *accept; GIOCondition cond; accept = g_new0(struct accept, 1); accept->connect = connect; accept->user_data = user_data; accept->destroy = destroy; cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept, (GDestroyNotify) accept_remove); } static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm, uint16_t cid, GError **err) { struct sockaddr_l2 addr; memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, src); if (cid) addr.l2_cid = htobs(cid); else addr.l2_psm = htobs(psm); if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { int error = -errno; ERROR_FAILED(err, "l2cap_bind", errno); return error; } return 0; } static int l2cap_connect(int sock, const bdaddr_t *dst, uint16_t psm, uint16_t cid) { int err; struct sockaddr_l2 addr; memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, dst); if (cid) addr.l2_cid = htobs(cid); else addr.l2_psm = htobs(psm); err = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) return -errno; return 0; } static int l2cap_set_master(int sock, int master) { int flags; socklen_t len; len = sizeof(flags); if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0) return -errno; if (master) { if (flags & L2CAP_LM_MASTER) return 0; flags |= L2CAP_LM_MASTER; } else { if (!(flags & L2CAP_LM_MASTER)) return 0; flags &= ~L2CAP_LM_MASTER; } if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0) return -errno; return 0; } static int rfcomm_set_master(int sock, int master) { int flags; socklen_t len; len = sizeof(flags); if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0) return -errno; if (master) { if (flags & RFCOMM_LM_MASTER) return 0; flags |= RFCOMM_LM_MASTER; } else { if (!(flags & RFCOMM_LM_MASTER)) return 0; flags &= ~RFCOMM_LM_MASTER; } if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0) return -errno; return 0; } static int l2cap_set_lm(int sock, int level) { int lm_map[] = { 0, L2CAP_LM_AUTH, L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT, L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE, }, opt = lm_map[level]; if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) return -errno; return 0; } static int rfcomm_set_lm(int sock, int level) { int lm_map[] = { 0, RFCOMM_LM_AUTH, RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT, RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE, }, opt = lm_map[level]; if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0) return -errno; return 0; } static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err) { struct bt_security sec; int ret; if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) { g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, "Valid security level range is %d-%d", BT_SECURITY_LOW, BT_SECURITY_HIGH); return FALSE; } memset(&sec, 0, sizeof(sec)); sec.level = level; if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, sizeof(sec)) == 0) return TRUE; if (errno != ENOPROTOOPT) { ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno); return FALSE; } if (type == BT_IO_L2CAP) ret = l2cap_set_lm(sock, level); else ret = rfcomm_set_lm(sock, level); if (ret < 0) { ERROR_FAILED(err, "setsockopt(LM)", -ret); return FALSE; } return TRUE; } static int l2cap_get_lm(int sock, int *sec_level) { int opt; socklen_t len; len = sizeof(opt); if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0) return -errno; *sec_level = 0; if (opt & L2CAP_LM_AUTH) *sec_level = BT_SECURITY_LOW; if (opt & L2CAP_LM_ENCRYPT) *sec_level = BT_SECURITY_MEDIUM; if (opt & L2CAP_LM_SECURE) *sec_level = BT_SECURITY_HIGH; return 0; } static int rfcomm_get_lm(int sock, int *sec_level) { int opt; socklen_t len; len = sizeof(opt); if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0) return -errno; *sec_level = 0; if (opt & RFCOMM_LM_AUTH) *sec_level = BT_SECURITY_LOW; if (opt & RFCOMM_LM_ENCRYPT) *sec_level = BT_SECURITY_MEDIUM; if (opt & RFCOMM_LM_SECURE) *sec_level = BT_SECURITY_HIGH; return 0; } static gboolean get_sec_level(int sock, BtIOType type, int *level, GError **err) { struct bt_security sec; socklen_t len; int ret; memset(&sec, 0, sizeof(sec)); len = sizeof(sec); if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) { *level = sec.level; return TRUE; } if (errno != ENOPROTOOPT) { ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno); return FALSE; } if (type == BT_IO_L2CAP) ret = l2cap_get_lm(sock, level); else ret = rfcomm_get_lm(sock, level); if (ret < 0) { ERROR_FAILED(err, "getsockopt(LM)", -ret); return FALSE; } return TRUE; } static int l2cap_set_flushable(int sock, gboolean flushable) { int f; f = flushable; if (setsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, sizeof(f)) < 0) return -errno; return 0; } static int set_priority(int sock, uint32_t prio) { if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0) return -errno; return 0; } static gboolean get_key_size(int sock, int *size, GError **err) { struct bt_security sec; socklen_t len; memset(&sec, 0, sizeof(sec)); len = sizeof(sec); if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) { *size = sec.key_size; return TRUE; } return FALSE; } static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, uint16_t omtu, uint8_t mode, int master, int flushable, uint32_t priority, GError **err) { if (imtu || omtu || mode) { struct l2cap_options l2o; socklen_t len; memset(&l2o, 0, sizeof(l2o)); len = sizeof(l2o); if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) { ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno); return FALSE; } if (imtu) l2o.imtu = imtu; if (omtu) l2o.omtu = omtu; if (mode) l2o.mode = mode; if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) { ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno); return FALSE; } } if (master >= 0 && l2cap_set_master(sock, master) < 0) { ERROR_FAILED(err, "l2cap_set_master", errno); return FALSE; } if (flushable >= 0 && l2cap_set_flushable(sock, flushable) < 0) { ERROR_FAILED(err, "l2cap_set_flushable", errno); return FALSE; } if (priority > 0 && set_priority(sock, priority) < 0) { ERROR_FAILED(err, "set_priority", errno); return FALSE; } if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err)) return FALSE; return TRUE; } static int rfcomm_bind(int sock, const bdaddr_t *src, uint8_t channel, GError **err) { struct sockaddr_rc addr; memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, src); addr.rc_channel = channel; if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { int error = -errno; ERROR_FAILED(err, "rfcomm_bind", errno); return error; } return 0; } static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel) { int err; struct sockaddr_rc addr; memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, dst); addr.rc_channel = channel; err = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) return -errno; return 0; } static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err) { if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err)) return FALSE; if (master >= 0 && rfcomm_set_master(sock, master) < 0) { ERROR_FAILED(err, "rfcomm_set_master", errno); return FALSE; } return TRUE; } static int sco_bind(int sock, const bdaddr_t *src, GError **err) { struct sockaddr_sco addr; memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, src); if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { int error = -errno; ERROR_FAILED(err, "sco_bind", errno); return error; } return 0; } static int sco_connect(int sock, const bdaddr_t *dst) { struct sockaddr_sco addr; int err; memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, dst); err = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) return -errno; return 0; } static gboolean sco_set(int sock, uint16_t mtu, GError **err) { struct sco_options sco_opt; socklen_t len; if (!mtu) return TRUE; len = sizeof(sco_opt); memset(&sco_opt, 0, len); if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) { ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno); return FALSE; } sco_opt.mtu = mtu; if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, sizeof(sco_opt)) < 0) { ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno); return FALSE; } return TRUE; } static gboolean parse_set_opts(struct set_opts *opts, GError **err, BtIOOption opt1, va_list args) { BtIOOption opt = opt1; const char *str; memset(opts, 0, sizeof(*opts)); /* Set defaults */ opts->defer = DEFAULT_DEFER_TIMEOUT; opts->master = -1; opts->mode = L2CAP_MODE_BASIC; opts->flushable = -1; opts->priority = 0; while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: str = va_arg(args, const char *); str2ba(str, &opts->src); break; case BT_IO_OPT_SOURCE_BDADDR: bacpy(&opts->src, va_arg(args, const bdaddr_t *)); break; case BT_IO_OPT_DEST: str2ba(va_arg(args, const char *), &opts->dst); break; case BT_IO_OPT_DEST_BDADDR: bacpy(&opts->dst, va_arg(args, const bdaddr_t *)); break; case BT_IO_OPT_DEFER_TIMEOUT: opts->defer = va_arg(args, int); break; case BT_IO_OPT_SEC_LEVEL: opts->sec_level = va_arg(args, int); break; case BT_IO_OPT_CHANNEL: opts->channel = va_arg(args, int); break; case BT_IO_OPT_PSM: opts->psm = va_arg(args, int); break; case BT_IO_OPT_CID: opts->cid = va_arg(args, int); break; case BT_IO_OPT_MTU: opts->mtu = va_arg(args, int); opts->imtu = opts->mtu; opts->omtu = opts->mtu; break; case BT_IO_OPT_OMTU: opts->omtu = va_arg(args, int); if (!opts->mtu) opts->mtu = opts->omtu; break; case BT_IO_OPT_IMTU: opts->imtu = va_arg(args, int); if (!opts->mtu) opts->mtu = opts->imtu; break; case BT_IO_OPT_MASTER: opts->master = va_arg(args, gboolean); break; case BT_IO_OPT_MODE: opts->mode = va_arg(args, int); break; case BT_IO_OPT_FLUSHABLE: opts->flushable = va_arg(args, gboolean); break; case BT_IO_OPT_PRIORITY: opts->priority = va_arg(args, int); break; default: g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, "Unknown option %d", opt); return FALSE; } opt = va_arg(args, int); } return TRUE; } static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst, socklen_t len, GError **err) { socklen_t olen; memset(src, 0, len); olen = len; if (getsockname(sock, src, &olen) < 0) { ERROR_FAILED(err, "getsockname", errno); return FALSE; } memset(dst, 0, len); olen = len; if (getpeername(sock, dst, &olen) < 0) { ERROR_FAILED(err, "getpeername", errno); return FALSE; } return TRUE; } static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class) { struct l2cap_conninfo info; socklen_t len; len = sizeof(info); if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0) return -errno; if (handle) *handle = info.hci_handle; if (dev_class) memcpy(dev_class, info.dev_class, 3); return 0; } static int l2cap_get_flushable(int sock, gboolean *flushable) { int f; socklen_t len; f = 0; len = sizeof(f); if (getsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, &len) < 0) return -errno; if (f) *flushable = TRUE; else *flushable = FALSE; return 0; } static int get_priority(int sock, uint32_t *prio) { socklen_t len; len = sizeof(*prio); if (getsockopt(sock, SOL_SOCKET, SO_PRIORITY, prio, &len) < 0) return -errno; return 0; } static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1, va_list args) { BtIOOption opt = opt1; struct sockaddr_l2 src, dst; struct l2cap_options l2o; int flags; uint8_t dev_class[3]; uint16_t handle = 0; socklen_t len; gboolean flushable = FALSE; uint32_t priority; len = sizeof(l2o); memset(&l2o, 0, len); if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) { ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno); return FALSE; } if (!get_peers(sock, (struct sockaddr *) &src, (struct sockaddr *) &dst, sizeof(src), err)) return FALSE; while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: ba2str(&src.l2_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_SOURCE_BDADDR: bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr); break; case BT_IO_OPT_DEST: ba2str(&dst.l2_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr); break; case BT_IO_OPT_DEFER_TIMEOUT: len = sizeof(int); if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, va_arg(args, int *), &len) < 0) { ERROR_FAILED(err, "getsockopt(DEFER_SETUP)", errno); return FALSE; } break; case BT_IO_OPT_SEC_LEVEL: if (!get_sec_level(sock, BT_IO_L2CAP, va_arg(args, int *), err)) return FALSE; break; case BT_IO_OPT_KEY_SIZE: if (!get_key_size(sock, va_arg(args, int *), err)) return FALSE; break; case BT_IO_OPT_PSM: *(va_arg(args, uint16_t *)) = src.l2_psm ? btohs(src.l2_psm) : btohs(dst.l2_psm); break; case BT_IO_OPT_CID: *(va_arg(args, uint16_t *)) = src.l2_cid ? btohs(src.l2_cid) : btohs(dst.l2_cid); break; case BT_IO_OPT_OMTU: *(va_arg(args, uint16_t *)) = l2o.omtu; break; case BT_IO_OPT_IMTU: *(va_arg(args, uint16_t *)) = l2o.imtu; break; case BT_IO_OPT_MASTER: len = sizeof(flags); if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0) { ERROR_FAILED(err, "getsockopt(L2CAP_LM)", errno); return FALSE; } *(va_arg(args, gboolean *)) = (flags & L2CAP_LM_MASTER) ? TRUE : FALSE; break; case BT_IO_OPT_HANDLE: if (l2cap_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "L2CAP_CONNINFO", errno); return FALSE; } *(va_arg(args, uint16_t *)) = handle; break; case BT_IO_OPT_CLASS: if (l2cap_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "L2CAP_CONNINFO", errno); return FALSE; } memcpy(va_arg(args, uint8_t *), dev_class, 3); break; case BT_IO_OPT_MODE: *(va_arg(args, uint8_t *)) = l2o.mode; break; case BT_IO_OPT_FLUSHABLE: if (l2cap_get_flushable(sock, &flushable) < 0) { ERROR_FAILED(err, "get_flushable", errno); return FALSE; } *(va_arg(args, gboolean *)) = flushable; break; case BT_IO_OPT_PRIORITY: if (get_priority(sock, &priority) < 0) { ERROR_FAILED(err, "get_priority", errno); return FALSE; } *(va_arg(args, uint32_t *)) = priority; break; default: g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, "Unknown option %d", opt); return FALSE; } opt = va_arg(args, int); } return TRUE; } static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class) { struct rfcomm_conninfo info; socklen_t len; len = sizeof(info); if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0) return -errno; if (handle) *handle = info.hci_handle; if (dev_class) memcpy(dev_class, info.dev_class, 3); return 0; } static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1, va_list args) { BtIOOption opt = opt1; struct sockaddr_rc src, dst; int flags; socklen_t len; uint8_t dev_class[3]; uint16_t handle = 0; if (!get_peers(sock, (struct sockaddr *) &src, (struct sockaddr *) &dst, sizeof(src), err)) return FALSE; while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: ba2str(&src.rc_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_SOURCE_BDADDR: bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr); break; case BT_IO_OPT_DEST: ba2str(&dst.rc_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr); break; case BT_IO_OPT_DEFER_TIMEOUT: len = sizeof(int); if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, va_arg(args, int *), &len) < 0) { ERROR_FAILED(err, "getsockopt(DEFER_SETUP)", errno); return FALSE; } break; case BT_IO_OPT_SEC_LEVEL: if (!get_sec_level(sock, BT_IO_RFCOMM, va_arg(args, int *), err)) return FALSE; break; case BT_IO_OPT_CHANNEL: *(va_arg(args, uint8_t *)) = src.rc_channel ? src.rc_channel : dst.rc_channel; break; case BT_IO_OPT_SOURCE_CHANNEL: *(va_arg(args, uint8_t *)) = src.rc_channel; break; case BT_IO_OPT_DEST_CHANNEL: *(va_arg(args, uint8_t *)) = dst.rc_channel; break; case BT_IO_OPT_MASTER: len = sizeof(flags); if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0) { ERROR_FAILED(err, "getsockopt(RFCOMM_LM)", errno); return FALSE; } *(va_arg(args, gboolean *)) = (flags & RFCOMM_LM_MASTER) ? TRUE : FALSE; break; case BT_IO_OPT_HANDLE: if (rfcomm_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "RFCOMM_CONNINFO", errno); return FALSE; } *(va_arg(args, uint16_t *)) = handle; break; case BT_IO_OPT_CLASS: if (rfcomm_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "RFCOMM_CONNINFO", errno); return FALSE; } memcpy(va_arg(args, uint8_t *), dev_class, 3); break; default: g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, "Unknown option %d", opt); return FALSE; } opt = va_arg(args, int); } return TRUE; } static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class) { struct sco_conninfo info; socklen_t len; len = sizeof(info); if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0) return -errno; if (handle) *handle = info.hci_handle; if (dev_class) memcpy(dev_class, info.dev_class, 3); return 0; } static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args) { BtIOOption opt = opt1; struct sockaddr_sco src, dst; struct sco_options sco_opt; socklen_t len; uint8_t dev_class[3]; uint16_t handle = 0; len = sizeof(sco_opt); memset(&sco_opt, 0, len); if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) { ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno); return FALSE; } if (!get_peers(sock, (struct sockaddr *) &src, (struct sockaddr *) &dst, sizeof(src), err)) return FALSE; while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: ba2str(&src.sco_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_SOURCE_BDADDR: bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr); break; case BT_IO_OPT_DEST: ba2str(&dst.sco_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr); break; case BT_IO_OPT_MTU: case BT_IO_OPT_IMTU: case BT_IO_OPT_OMTU: *(va_arg(args, uint16_t *)) = sco_opt.mtu; break; case BT_IO_OPT_HANDLE: if (sco_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "SCO_CONNINFO", errno); return FALSE; } *(va_arg(args, uint16_t *)) = handle; break; case BT_IO_OPT_CLASS: if (sco_get_info(sock, &handle, dev_class) < 0) { ERROR_FAILED(err, "SCO_CONNINFO", errno); return FALSE; } memcpy(va_arg(args, uint8_t *), dev_class, 3); break; default: g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, "Unknown option %d", opt); return FALSE; } opt = va_arg(args, int); } return TRUE; } static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err, BtIOOption opt1, va_list args) { int sock; sock = g_io_channel_unix_get_fd(io); switch (type) { case BT_IO_L2RAW: case BT_IO_L2CAP: case BT_IO_L2ERTM: return l2cap_get(sock, err, opt1, args); case BT_IO_RFCOMM: return rfcomm_get(sock, err, opt1, args); case BT_IO_SCO: return sco_get(sock, err, opt1, args); } g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, "Unknown BtIO type %d", type); return FALSE; } gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError **err) { int sock; char c; struct pollfd pfd; sock = g_io_channel_unix_get_fd(io); memset(&pfd, 0, sizeof(pfd)); pfd.fd = sock; pfd.events = POLLOUT; if (poll(&pfd, 1, 0) < 0) { ERROR_FAILED(err, "poll", errno); return FALSE; } if (!(pfd.revents & POLLOUT)) { if (read(sock, &c, 1) < 0) { ERROR_FAILED(err, "read", errno); return FALSE; } } accept_add(io, connect, user_data, destroy); return TRUE; } gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err, BtIOOption opt1, ...) { va_list args; gboolean ret; struct set_opts opts; int sock; va_start(args, opt1); ret = parse_set_opts(&opts, err, opt1, args); va_end(args); if (!ret) return ret; sock = g_io_channel_unix_get_fd(io); switch (type) { case BT_IO_L2RAW: case BT_IO_L2CAP: case BT_IO_L2ERTM: return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu, opts.mode, opts.master, opts.flushable, opts.priority, err); case BT_IO_RFCOMM: return rfcomm_set(sock, opts.sec_level, opts.master, err); case BT_IO_SCO: return sco_set(sock, opts.mtu, err); } g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, "Unknown BtIO type %d", type); return FALSE; } gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err, BtIOOption opt1, ...) { va_list args; gboolean ret; va_start(args, opt1); ret = get_valist(io, type, err, opt1, args); va_end(args); return ret; } static GIOChannel *create_io(BtIOType type, gboolean server, struct set_opts *opts, GError **err) { int sock; GIOChannel *io; switch (type) { case BT_IO_L2RAW: sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP); if (sock < 0) { ERROR_FAILED(err, "socket(RAW, L2CAP)", errno); return NULL; } if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0, opts->cid, err) < 0) goto failed; if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, -1, 0, err)) goto failed; break; case BT_IO_L2CAP: sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (sock < 0) { ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno); return NULL; } if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0, opts->cid, err) < 0) goto failed; if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu, opts->mode, opts->master, opts->flushable, opts->priority, err)) goto failed; break; case BT_IO_L2ERTM: sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_L2CAP); if (sock < 0) { ERROR_FAILED(err, "socket(STREAM, L2CAP)", errno); return NULL; } if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0, opts->cid, err) < 0) goto failed; if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu, opts->mode, opts->master, opts->flushable, opts->priority, err)) goto failed; break; case BT_IO_RFCOMM: sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sock < 0) { ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno); return NULL; } if (rfcomm_bind(sock, &opts->src, server ? opts->channel : 0, err) < 0) goto failed; if (!rfcomm_set(sock, opts->sec_level, opts->master, err)) goto failed; break; case BT_IO_SCO: sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); if (sock < 0) { ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno); return NULL; } if (sco_bind(sock, &opts->src, err) < 0) goto failed; if (!sco_set(sock, opts->mtu, err)) goto failed; break; default: g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, "Unknown BtIO type %d", type); return NULL; } io = g_io_channel_unix_new(sock); g_io_channel_set_close_on_unref(io, TRUE); g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); return io; failed: close(sock); return NULL; } GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError **gerr, BtIOOption opt1, ...) { GIOChannel *io; va_list args; struct set_opts opts; int err, sock; gboolean ret; va_start(args, opt1); ret = parse_set_opts(&opts, gerr, opt1, args); va_end(args); if (ret == FALSE) return NULL; io = create_io(type, FALSE, &opts, gerr); if (io == NULL) return NULL; sock = g_io_channel_unix_get_fd(io); switch (type) { case BT_IO_L2RAW: err = l2cap_connect(sock, &opts.dst, 0, opts.cid); break; case BT_IO_L2CAP: case BT_IO_L2ERTM: err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid); break; case BT_IO_RFCOMM: err = rfcomm_connect(sock, &opts.dst, opts.channel); break; case BT_IO_SCO: err = sco_connect(sock, &opts.dst); break; default: g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, "Unknown BtIO type %d", type); return NULL; } if (err < 0) { g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED, "connect: %s (%d)", strerror(-err), -err); g_io_channel_unref(io); return NULL; } connect_add(io, connect, user_data, destroy); return io; } GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect, BtIOConfirm confirm, gpointer user_data, GDestroyNotify destroy, GError **err, BtIOOption opt1, ...) { GIOChannel *io; va_list args; struct set_opts opts; int sock; gboolean ret; if (type == BT_IO_L2RAW) { g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, "Server L2CAP RAW sockets not supported"); return NULL; } va_start(args, opt1); ret = parse_set_opts(&opts, err, opt1, args); va_end(args); if (ret == FALSE) return NULL; io = create_io(type, TRUE, &opts, err); if (io == NULL) return NULL; sock = g_io_channel_unix_get_fd(io); if (confirm) setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer, sizeof(opts.defer)); if (listen(sock, 5) < 0) { ERROR_FAILED(err, "listen", errno); g_io_channel_unref(io); return NULL; } server_add(io, connect, confirm, user_data, destroy); return io; } GQuark bt_io_error_quark(void) { return g_quark_from_static_string("bt-io-error-quark"); } ofono-1.17.bzr6912+16.04.20160314.3/gdbus/0000755000015600001650000000000012671500304017504 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/gdbus/mainloop.c0000644000015600001650000002002312671500024021462 0ustar pbuserpbgroup00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "gdbus.h" #define info(fmt...) #define error(fmt...) #define debug(fmt...) struct timeout_handler { guint id; DBusTimeout *timeout; }; struct watch_info { guint id; DBusWatch *watch; DBusConnection *conn; }; struct disconnect_data { GDBusWatchFunction function; void *user_data; }; static gboolean disconnected_signal(DBusConnection *conn, DBusMessage *msg, void *data) { struct disconnect_data *dc_data = data; error("Got disconnected from the system message bus"); dc_data->function(conn, dc_data->user_data); dbus_connection_unref(conn); return TRUE; } static gboolean message_dispatch(void *data) { DBusConnection *conn = data; /* Dispatch messages */ while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS); dbus_connection_unref(conn); return FALSE; } static inline void queue_dispatch(DBusConnection *conn, DBusDispatchStatus status) { if (status == DBUS_DISPATCH_DATA_REMAINS) g_idle_add(message_dispatch, dbus_connection_ref(conn)); } static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) { struct watch_info *info = data; unsigned int flags = 0; DBusDispatchStatus status; DBusConnection *conn; if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE; if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE; if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP; if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR; /* Protect connection from being destroyed by dbus_watch_handle */ conn = dbus_connection_ref(info->conn); dbus_watch_handle(info->watch, flags); status = dbus_connection_get_dispatch_status(conn); queue_dispatch(conn, status); dbus_connection_unref(conn); return TRUE; } static void watch_info_free(void *data) { struct watch_info *info = data; if (info->id > 0) { g_source_remove(info->id); info->id = 0; } dbus_connection_unref(info->conn); g_free(info); } static dbus_bool_t add_watch(DBusWatch *watch, void *data) { DBusConnection *conn = data; GIOCondition cond = G_IO_HUP | G_IO_ERR; GIOChannel *chan; struct watch_info *info; unsigned int flags; int fd; if (!dbus_watch_get_enabled(watch)) return TRUE; info = g_new0(struct watch_info, 1); fd = dbus_watch_get_unix_fd(watch); chan = g_io_channel_unix_new(fd); info->watch = watch; info->conn = dbus_connection_ref(conn); dbus_watch_set_data(watch, info, watch_info_free); flags = dbus_watch_get_flags(watch); if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN; if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT; info->id = g_io_add_watch(chan, cond, watch_func, info); g_io_channel_unref(chan); return TRUE; } static void remove_watch(DBusWatch *watch, void *data) { if (dbus_watch_get_enabled(watch)) return; /* will trigger watch_info_free() */ dbus_watch_set_data(watch, NULL, NULL); } static void watch_toggled(DBusWatch *watch, void *data) { /* Because we just exit on OOM, enable/disable is * no different from add/remove */ if (dbus_watch_get_enabled(watch)) add_watch(watch, data); else remove_watch(watch, data); } static gboolean timeout_handler_dispatch(gpointer data) { struct timeout_handler *handler = data; handler->id = 0; /* if not enabled should not be polled by the main loop */ if (!dbus_timeout_get_enabled(handler->timeout)) return FALSE; dbus_timeout_handle(handler->timeout); return FALSE; } static void timeout_handler_free(void *data) { struct timeout_handler *handler = data; if (handler->id > 0) { g_source_remove(handler->id); handler->id = 0; } g_free(handler); } static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { int interval = dbus_timeout_get_interval(timeout); struct timeout_handler *handler; if (!dbus_timeout_get_enabled(timeout)) return TRUE; handler = g_new0(struct timeout_handler, 1); handler->timeout = timeout; dbus_timeout_set_data(timeout, handler, timeout_handler_free); handler->id = g_timeout_add(interval, timeout_handler_dispatch, handler); return TRUE; } static void remove_timeout(DBusTimeout *timeout, void *data) { /* will trigger timeout_handler_free() */ dbus_timeout_set_data(timeout, NULL, NULL); } static void timeout_toggled(DBusTimeout *timeout, void *data) { if (dbus_timeout_get_enabled(timeout)) add_timeout(timeout, data); else remove_timeout(timeout, data); } static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *data) { if (!dbus_connection_get_is_connected(conn)) return; queue_dispatch(conn, status); } static inline void setup_dbus_with_main_loop(DBusConnection *conn) { dbus_connection_set_watch_functions(conn, add_watch, remove_watch, watch_toggled, conn, NULL); dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, timeout_toggled, NULL, NULL); dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL); } static gboolean setup_bus(DBusConnection *conn, const char *name, DBusError *error) { gboolean result; DBusDispatchStatus status; if (name != NULL) { result = g_dbus_request_name(conn, name, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return FALSE; } if (result == FALSE) return FALSE; } setup_dbus_with_main_loop(conn); status = dbus_connection_get_dispatch_status(conn); queue_dispatch(conn, status); return TRUE; } DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name, DBusError *error) { DBusConnection *conn; conn = dbus_bus_get(type, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return NULL; } if (conn == NULL) return NULL; if (setup_bus(conn, name, error) == FALSE) { dbus_connection_unref(conn); return NULL; } return conn; } DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name, DBusError *error) { DBusConnection *conn; conn = dbus_bus_get_private(type, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return NULL; } if (conn == NULL) return NULL; if (setup_bus(conn, name, error) == FALSE) { dbus_connection_close(conn); dbus_connection_unref(conn); return NULL; } return conn; } gboolean g_dbus_request_name(DBusConnection *connection, const char *name, DBusError *error) { int result; result = dbus_bus_request_name(connection, name, DBUS_NAME_FLAG_DO_NOT_QUEUE, error); if (error != NULL) { if (dbus_error_is_set(error) == TRUE) return FALSE; } if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { if (error != NULL) dbus_set_error(error, name, "Name already in use"); return FALSE; } return TRUE; } gboolean g_dbus_set_disconnect_function(DBusConnection *connection, GDBusWatchFunction function, void *user_data, DBusFreeFunction destroy) { struct disconnect_data *dc_data; dc_data = g_new0(struct disconnect_data, 1); dc_data->function = function; dc_data->user_data = user_data; dbus_connection_set_exit_on_disconnect(connection, FALSE); if (g_dbus_add_signal_watch(connection, NULL, NULL, DBUS_INTERFACE_LOCAL, "Disconnected", disconnected_signal, dc_data, g_free) == 0) { error("Failed to add watch for D-Bus Disconnected signal"); g_free(dc_data); return FALSE; } return TRUE; } ofono-1.17.bzr6912+16.04.20160314.3/gdbus/gdbus.h0000644000015600001650000003172512671500024020770 0ustar pbuserpbgroup00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef __GDBUS_H #define __GDBUS_H #ifdef __cplusplus extern "C" { #endif #include #include typedef enum GDBusMethodFlags GDBusMethodFlags; typedef enum GDBusSignalFlags GDBusSignalFlags; typedef enum GDBusPropertyFlags GDBusPropertyFlags; typedef enum GDBusSecurityFlags GDBusSecurityFlags; typedef struct GDBusArgInfo GDBusArgInfo; typedef struct GDBusMethodTable GDBusMethodTable; typedef struct GDBusSignalTable GDBusSignalTable; typedef struct GDBusPropertyTable GDBusPropertyTable; typedef struct GDBusSecurityTable GDBusSecurityTable; typedef void (* GDBusWatchFunction) (DBusConnection *connection, void *user_data); typedef void (* GDBusMessageFunction) (DBusConnection *connection, DBusMessage *message, void *user_data); typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection, DBusMessage *message, void *user_data); DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name, DBusError *error); DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name, DBusError *error); gboolean g_dbus_request_name(DBusConnection *connection, const char *name, DBusError *error); gboolean g_dbus_set_disconnect_function(DBusConnection *connection, GDBusWatchFunction function, void *user_data, DBusFreeFunction destroy); typedef void (* GDBusDestroyFunction) (void *user_data); typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection, DBusMessage *message, void *user_data); typedef gboolean (*GDBusPropertyGetter)(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data); typedef guint32 GDBusPendingPropertySet; typedef void (*GDBusPropertySetter)(const GDBusPropertyTable *property, DBusMessageIter *value, GDBusPendingPropertySet id, void *data); typedef gboolean (*GDBusPropertyExists)(const GDBusPropertyTable *property, void *data); typedef guint32 GDBusPendingReply; typedef void (* GDBusSecurityFunction) (DBusConnection *connection, const char *action, gboolean interaction, GDBusPendingReply pending); enum GDBusFlags { G_DBUS_FLAG_ENABLE_EXPERIMENTAL = (1 << 0), }; enum GDBusMethodFlags { G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0), G_DBUS_METHOD_FLAG_NOREPLY = (1 << 1), G_DBUS_METHOD_FLAG_ASYNC = (1 << 2), G_DBUS_METHOD_FLAG_EXPERIMENTAL = (1 << 3), }; enum GDBusSignalFlags { G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0), G_DBUS_SIGNAL_FLAG_EXPERIMENTAL = (1 << 1), }; enum GDBusPropertyFlags { G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0), G_DBUS_PROPERTY_FLAG_EXPERIMENTAL = (1 << 1), }; enum GDBusSecurityFlags { G_DBUS_SECURITY_FLAG_DEPRECATED = (1 << 0), G_DBUS_SECURITY_FLAG_BUILTIN = (1 << 1), G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2), }; struct GDBusArgInfo { const char *name; const char *signature; }; struct GDBusMethodTable { const char *name; GDBusMethodFunction function; GDBusMethodFlags flags; unsigned int privilege; const GDBusArgInfo *in_args; const GDBusArgInfo *out_args; }; struct GDBusSignalTable { const char *name; GDBusSignalFlags flags; const GDBusArgInfo *args; }; struct GDBusPropertyTable { const char *name; const char *type; GDBusPropertyGetter get; GDBusPropertySetter set; GDBusPropertyExists exists; GDBusPropertyFlags flags; }; struct GDBusSecurityTable { unsigned int privilege; const char *action; GDBusSecurityFlags flags; GDBusSecurityFunction function; }; #define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } } #define GDBUS_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function #define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_ASYNC #define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_DEPRECATED #define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED #define GDBUS_EXPERIMENTAL_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_EXPERIMENTAL #define GDBUS_EXPERIMENTAL_ASYNC_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_EXPERIMENTAL #define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \ .name = _name, \ .in_args = _in_args, \ .out_args = _out_args, \ .function = _function, \ .flags = G_DBUS_METHOD_FLAG_NOREPLY #define GDBUS_SIGNAL(_name, _args) \ .name = _name, \ .args = _args #define GDBUS_DEPRECATED_SIGNAL(_name, _args) \ .name = _name, \ .args = _args, \ .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED #define GDBUS_EXPERIMENTAL_SIGNAL(_name, _args) \ .name = _name, \ .args = _args, \ .flags = G_DBUS_SIGNAL_FLAG_EXPERIMENTAL void g_dbus_set_flags(int flags); int g_dbus_get_flags(void); gboolean g_dbus_register_interface(DBusConnection *connection, const char *path, const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, const GDBusPropertyTable *properties, void *user_data, GDBusDestroyFunction destroy); gboolean g_dbus_unregister_interface(DBusConnection *connection, const char *path, const char *name); gboolean g_dbus_register_security(const GDBusSecurityTable *security); gboolean g_dbus_unregister_security(const GDBusSecurityTable *security); void g_dbus_pending_success(DBusConnection *connection, GDBusPendingReply pending); void g_dbus_pending_error(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, ...) __attribute__((format(printf, 4, 5))); void g_dbus_pending_error_valist(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, va_list args); DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name, const char *format, ...) __attribute__((format(printf, 3, 4))); DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, const char *format, va_list args); DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...); DBusMessage *g_dbus_create_reply_valist(DBusMessage *message, int type, va_list args); gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message); gboolean g_dbus_send_message_with_reply(DBusConnection *connection, DBusMessage *message, DBusPendingCall **call, int timeout); gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, ...) __attribute__((format(printf, 4, 5))); gboolean g_dbus_send_error_valist(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, va_list args); gboolean g_dbus_send_reply(DBusConnection *connection, DBusMessage *message, int type, ...); gboolean g_dbus_send_reply_valist(DBusConnection *connection, DBusMessage *message, int type, va_list args); gboolean g_dbus_emit_signal(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, ...); gboolean g_dbus_emit_signal_valist(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, va_list args); guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, GDBusWatchFunction connect, GDBusWatchFunction disconnect, void *user_data, GDBusDestroyFunction destroy); guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, GDBusWatchFunction function, void *user_data, GDBusDestroyFunction destroy); guint g_dbus_add_signal_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, const char *member, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy); guint g_dbus_add_properties_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy); gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag); void g_dbus_remove_all_watches(DBusConnection *connection); void g_dbus_pending_property_success(GDBusPendingPropertySet id); void g_dbus_pending_property_error_valist(GDBusPendingReply id, const char *name, const char *format, va_list args); void g_dbus_pending_property_error(GDBusPendingReply id, const char *name, const char *format, ...); void g_dbus_emit_property_changed(DBusConnection *connection, const char *path, const char *interface, const char *name); gboolean g_dbus_get_properties(DBusConnection *connection, const char *path, const char *interface, DBusMessageIter *iter); gboolean g_dbus_attach_object_manager(DBusConnection *connection); gboolean g_dbus_detach_object_manager(DBusConnection *connection); typedef struct GDBusClient GDBusClient; typedef struct GDBusProxy GDBusProxy; GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path, const char *interface); GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy); void g_dbus_proxy_unref(GDBusProxy *proxy); const char *g_dbus_proxy_get_path(GDBusProxy *proxy); const char *g_dbus_proxy_get_interface(GDBusProxy *proxy); gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name, DBusMessageIter *iter); gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name); typedef void (* GDBusResultFunction) (const DBusError *error, void *user_data); gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy, const char *name, int type, const void *value, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy); gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy, const char *name, int type, const void *value, size_t size, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy); typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data); typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data); gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method, GDBusSetupFunction setup, GDBusReturnFunction function, void *user_data, GDBusDestroyFunction destroy); typedef void (* GDBusClientFunction) (GDBusClient *client, void *user_data); typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data); typedef void (* GDBusPropertyFunction) (GDBusProxy *proxy, const char *name, DBusMessageIter *iter, void *user_data); gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy, GDBusPropertyFunction function, void *user_data); gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy, GDBusProxyFunction destroy, void *user_data); GDBusClient *g_dbus_client_new(DBusConnection *connection, const char *service, const char *path); GDBusClient *g_dbus_client_new_full(DBusConnection *connection, const char *service, const char *path, const char *root_path); GDBusClient *g_dbus_client_ref(GDBusClient *client); void g_dbus_client_unref(GDBusClient *client); gboolean g_dbus_client_set_connect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data); gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data); gboolean g_dbus_client_set_signal_watch(GDBusClient *client, GDBusMessageFunction function, void *user_data); gboolean g_dbus_client_set_ready_watch(GDBusClient *client, GDBusClientFunction ready, void *user_data); gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, GDBusProxyFunction proxy_added, GDBusProxyFunction proxy_removed, GDBusPropertyFunction property_changed, void *user_data); #ifdef __cplusplus } #endif #endif /* __GDBUS_H */ ofono-1.17.bzr6912+16.04.20160314.3/gdbus/object.c0000644000015600001650000012712012671500024021120 0ustar pbuserpbgroup00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "gdbus.h" #define info(fmt...) #define error(fmt...) #define debug(fmt...) #define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager" #ifndef DBUS_ERROR_UNKNOWN_PROPERTY #define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty" #endif #ifndef DBUS_ERROR_PROPERTY_READ_ONLY #define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly" #endif struct generic_data { unsigned int refcount; DBusConnection *conn; char *path; GSList *interfaces; GSList *objects; GSList *added; GSList *removed; guint process_id; gboolean pending_prop; char *introspect; struct generic_data *parent; }; struct interface_data { char *name; const GDBusMethodTable *methods; const GDBusSignalTable *signals; const GDBusPropertyTable *properties; GSList *pending_prop; void *user_data; GDBusDestroyFunction destroy; }; struct security_data { GDBusPendingReply pending; DBusMessage *message; const GDBusMethodTable *method; void *iface_user_data; }; struct property_data { DBusConnection *conn; GDBusPendingPropertySet id; DBusMessage *message; }; static int global_flags = 0; static struct generic_data *root; static GSList *pending = NULL; static gboolean process_changes(gpointer user_data); static void process_properties_from_interface(struct generic_data *data, struct interface_data *iface); static void process_property_changes(struct generic_data *data); static void print_arguments(GString *gstr, const GDBusArgInfo *args, const char *direction) { for (; args && args->name; args++) { g_string_append_printf(gstr, "name, args->signature); if (direction) g_string_append_printf(gstr, " direction=\"%s\"/>\n", direction); else g_string_append_printf(gstr, "/>\n"); } } #define G_DBUS_ANNOTATE(name_, value_) \ "" #define G_DBUS_ANNOTATE_DEPRECATED \ G_DBUS_ANNOTATE("Deprecated", "true") #define G_DBUS_ANNOTATE_NOREPLY \ G_DBUS_ANNOTATE("Method.NoReply", "true") static gboolean check_experimental(int flags, int flag) { if (!(flags & flag)) return FALSE; return !(global_flags & G_DBUS_FLAG_ENABLE_EXPERIMENTAL); } static void generate_interface_xml(GString *gstr, struct interface_data *iface) { const GDBusMethodTable *method; const GDBusSignalTable *signal; const GDBusPropertyTable *property; for (method = iface->methods; method && method->name; method++) { if (check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) continue; g_string_append_printf(gstr, "", method->name); print_arguments(gstr, method->in_args, "in"); print_arguments(gstr, method->out_args, "out"); if (method->flags & G_DBUS_METHOD_FLAG_DEPRECATED) g_string_append_printf(gstr, G_DBUS_ANNOTATE_DEPRECATED); if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) g_string_append_printf(gstr, G_DBUS_ANNOTATE_NOREPLY); g_string_append_printf(gstr, ""); } for (signal = iface->signals; signal && signal->name; signal++) { if (check_experimental(signal->flags, G_DBUS_SIGNAL_FLAG_EXPERIMENTAL)) continue; g_string_append_printf(gstr, "", signal->name); print_arguments(gstr, signal->args, NULL); if (signal->flags & G_DBUS_SIGNAL_FLAG_DEPRECATED) g_string_append_printf(gstr, G_DBUS_ANNOTATE_DEPRECATED); g_string_append_printf(gstr, "\n"); } for (property = iface->properties; property && property->name; property++) { if (check_experimental(property->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) continue; g_string_append_printf(gstr, "", property->name, property->type, property->get ? "read" : "", property->set ? "write" : ""); if (property->flags & G_DBUS_PROPERTY_FLAG_DEPRECATED) g_string_append_printf(gstr, G_DBUS_ANNOTATE_DEPRECATED); g_string_append_printf(gstr, ""); } } static void generate_introspection_xml(DBusConnection *conn, struct generic_data *data, const char *path) { GSList *list; GString *gstr; char **children; int i; g_free(data->introspect); gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE); g_string_append_printf(gstr, ""); for (list = data->interfaces; list; list = list->next) { struct interface_data *iface = list->data; g_string_append_printf(gstr, "", iface->name); generate_interface_xml(gstr, iface); g_string_append_printf(gstr, ""); } if (!dbus_connection_list_registered(conn, path, &children)) goto done; for (i = 0; children[i]; i++) g_string_append_printf(gstr, "", children[i]); dbus_free_string_array(children); done: g_string_append_printf(gstr, ""); data->introspect = g_string_free(gstr, FALSE); } static DBusMessage *introspect(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; DBusMessage *reply; if (data->introspect == NULL) generate_introspection_xml(connection, data, dbus_message_get_path(message)); reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect, DBUS_TYPE_INVALID); return reply; } static DBusHandlerResult process_message(DBusConnection *connection, DBusMessage *message, const GDBusMethodTable *method, void *iface_user_data) { DBusMessage *reply; reply = method->function(connection, message, iface_user_data); if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) { if (reply != NULL) dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) { if (reply == NULL) return DBUS_HANDLER_RESULT_HANDLED; } if (reply == NULL) return DBUS_HANDLER_RESULT_NEED_MEMORY; g_dbus_send_message(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } static GDBusPendingReply next_pending = 1; static GSList *pending_security = NULL; static const GDBusSecurityTable *security_table = NULL; void g_dbus_pending_success(DBusConnection *connection, GDBusPendingReply pending) { GSList *list; for (list = pending_security; list; list = list->next) { struct security_data *secdata = list->data; if (secdata->pending != pending) continue; pending_security = g_slist_remove(pending_security, secdata); process_message(connection, secdata->message, secdata->method, secdata->iface_user_data); dbus_message_unref(secdata->message); g_free(secdata); return; } } void g_dbus_pending_error_valist(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, va_list args) { GSList *list; for (list = pending_security; list; list = list->next) { struct security_data *secdata = list->data; if (secdata->pending != pending) continue; pending_security = g_slist_remove(pending_security, secdata); g_dbus_send_error_valist(connection, secdata->message, name, format, args); dbus_message_unref(secdata->message); g_free(secdata); return; } } void g_dbus_pending_error(DBusConnection *connection, GDBusPendingReply pending, const char *name, const char *format, ...) { va_list args; va_start(args, format); g_dbus_pending_error_valist(connection, pending, name, format, args); va_end(args); } int polkit_check_authorization(DBusConnection *conn, const char *action, gboolean interaction, void (*function) (dbus_bool_t authorized, void *user_data), void *user_data, int timeout); struct builtin_security_data { DBusConnection *conn; GDBusPendingReply pending; }; static void builtin_security_result(dbus_bool_t authorized, void *user_data) { struct builtin_security_data *data = user_data; if (authorized == TRUE) g_dbus_pending_success(data->conn, data->pending); else g_dbus_pending_error(data->conn, data->pending, DBUS_ERROR_AUTH_FAILED, NULL); g_free(data); } static void builtin_security_function(DBusConnection *conn, const char *action, gboolean interaction, GDBusPendingReply pending) { struct builtin_security_data *data; data = g_new0(struct builtin_security_data, 1); data->conn = conn; data->pending = pending; if (polkit_check_authorization(conn, action, interaction, builtin_security_result, data, 30000) < 0) g_dbus_pending_error(conn, pending, NULL, NULL); } static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg, const GDBusMethodTable *method, void *iface_user_data) { const GDBusSecurityTable *security; for (security = security_table; security && security->privilege; security++) { struct security_data *secdata; gboolean interaction; if (security->privilege != method->privilege) continue; secdata = g_new(struct security_data, 1); secdata->pending = next_pending++; secdata->message = dbus_message_ref(msg); secdata->method = method; secdata->iface_user_data = iface_user_data; pending_security = g_slist_prepend(pending_security, secdata); if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION) interaction = TRUE; else interaction = FALSE; if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) && security->function) security->function(conn, security->action, interaction, secdata->pending); else builtin_security_function(conn, security->action, interaction, secdata->pending); return TRUE; } return FALSE; } static GDBusPendingPropertySet next_pending_property = 1; static GSList *pending_property_set; static struct property_data *remove_pending_property_data( GDBusPendingPropertySet id) { struct property_data *propdata; GSList *l; for (l = pending_property_set; l != NULL; l = l->next) { propdata = l->data; if (propdata->id != id) continue; break; } if (l == NULL) return NULL; pending_property_set = g_slist_delete_link(pending_property_set, l); return propdata; } void g_dbus_pending_property_success(GDBusPendingPropertySet id) { struct property_data *propdata; propdata = remove_pending_property_data(id); if (propdata == NULL) return; g_dbus_send_reply(propdata->conn, propdata->message, DBUS_TYPE_INVALID); dbus_message_unref(propdata->message); g_free(propdata); } void g_dbus_pending_property_error_valist(GDBusPendingReply id, const char *name, const char *format, va_list args) { struct property_data *propdata; propdata = remove_pending_property_data(id); if (propdata == NULL) return; g_dbus_send_error_valist(propdata->conn, propdata->message, name, format, args); dbus_message_unref(propdata->message); g_free(propdata); } void g_dbus_pending_property_error(GDBusPendingReply id, const char *name, const char *format, ...) { va_list args; va_start(args, format); g_dbus_pending_property_error_valist(id, name, format, args); va_end(args); } static void reset_parent(gpointer data, gpointer user_data) { struct generic_data *child = data; struct generic_data *parent = user_data; child->parent = parent; } static void append_property(struct interface_data *iface, const GDBusPropertyTable *p, DBusMessageIter *dict) { DBusMessageIter entry, value; dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &p->name); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, p->type, &value); p->get(p, &value, iface->user_data); dbus_message_iter_close_container(&entry, &value); dbus_message_iter_close_container(dict, &entry); } static void append_properties(struct interface_data *data, DBusMessageIter *iter) { DBusMessageIter dict; const GDBusPropertyTable *p; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); for (p = data->properties; p && p->name; p++) { if (check_experimental(p->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) continue; if (p->get == NULL) continue; if (p->exists != NULL && !p->exists(p, data->user_data)) continue; append_property(data, p, &dict); } dbus_message_iter_close_container(iter, &dict); } static void append_interface(gpointer data, gpointer user_data) { struct interface_data *iface = data; DBusMessageIter *array = user_data; DBusMessageIter entry; dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &iface->name); append_properties(data, &entry); dbus_message_iter_close_container(array, &entry); } static void emit_interfaces_added(struct generic_data *data) { DBusMessage *signal; DBusMessageIter iter, array; if (root == NULL || data == root) return; signal = dbus_message_new_signal(root->path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &data->path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); g_slist_foreach(data->added, append_interface, &array); g_slist_free(data->added); data->added = NULL; dbus_message_iter_close_container(&iter, &array); /* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */ dbus_connection_send(data->conn, signal, NULL); dbus_message_unref(signal); } static struct interface_data *find_interface(GSList *interfaces, const char *name) { GSList *list; if (name == NULL) return NULL; for (list = interfaces; list; list = list->next) { struct interface_data *iface = list->data; if (!strcmp(name, iface->name)) return iface; } return NULL; } static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args, DBusMessage *message) { const char *sig = dbus_message_get_signature(message); const char *p = NULL; for (; args && args->signature && *sig; args++) { p = args->signature; for (; *sig && *p; sig++, p++) { if (*p != *sig) return FALSE; } } if (*sig || (p && *p) || (args && args->signature)) return FALSE; return TRUE; } static void add_pending(struct generic_data *data) { if (data->process_id > 0) return; data->process_id = g_idle_add(process_changes, data); pending = g_slist_append(pending, data); } static gboolean remove_interface(struct generic_data *data, const char *name) { struct interface_data *iface; iface = find_interface(data->interfaces, name); if (iface == NULL) return FALSE; process_properties_from_interface(data, iface); data->interfaces = g_slist_remove(data->interfaces, iface); if (iface->destroy) { iface->destroy(iface->user_data); iface->user_data = NULL; } /* * Interface being removed was just added, on the same mainloop * iteration? Don't send any signal */ if (g_slist_find(data->added, iface)) { data->added = g_slist_remove(data->added, iface); g_free(iface->name); g_free(iface); return TRUE; } if (data->parent == NULL) { g_free(iface->name); g_free(iface); return TRUE; } data->removed = g_slist_prepend(data->removed, iface->name); g_free(iface); add_pending(data); return TRUE; } static struct generic_data *invalidate_parent_data(DBusConnection *conn, const char *child_path) { struct generic_data *data = NULL, *child = NULL, *parent = NULL; char *parent_path, *slash; parent_path = g_strdup(child_path); slash = strrchr(parent_path, '/'); if (slash == NULL) goto done; if (slash == parent_path && parent_path[1] != '\0') parent_path[1] = '\0'; else *slash = '\0'; if (!strlen(parent_path)) goto done; if (dbus_connection_get_object_path_data(conn, parent_path, (void *) &data) == FALSE) { goto done; } parent = invalidate_parent_data(conn, parent_path); if (data == NULL) { data = parent; if (data == NULL) goto done; } g_free(data->introspect); data->introspect = NULL; if (!dbus_connection_get_object_path_data(conn, child_path, (void *) &child)) goto done; if (child == NULL || g_slist_find(data->objects, child) != NULL) goto done; data->objects = g_slist_prepend(data->objects, child); child->parent = data; done: g_free(parent_path); return data; } static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable *properties, const char *name) { const GDBusPropertyTable *p; for (p = properties; p && p->name; p++) { if (strcmp(name, p->name) != 0) continue; if (check_experimental(p->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) break; return p; } return NULL; } static DBusMessage *properties_get(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; struct interface_data *iface; const GDBusPropertyTable *property; const char *interface, *name; DBusMessageIter iter, value; DBusMessage *reply; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) return NULL; iface = find_interface(data->interfaces, interface); if (iface == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such interface '%s'", interface); property = find_property(iface->properties, name); if (property == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such property '%s'", name); if (property->exists != NULL && !property->exists(property, iface->user_data)) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such property '%s'", name); if (property->get == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Property '%s' is not readable", name); reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, property->type, &value); if (!property->get(property, &value, iface->user_data)) { dbus_message_unref(reply); return NULL; } dbus_message_iter_close_container(&iter, &value); return reply; } static DBusMessage *properties_get_all(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; struct interface_data *iface; const char *interface; DBusMessageIter iter; DBusMessage *reply; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) return NULL; iface = find_interface(data->interfaces, interface); if (iface == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such interface '%s'", interface); reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); append_properties(iface, &iter); return reply; } static DBusMessage *properties_set(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; DBusMessageIter iter, sub; struct interface_data *iface; const GDBusPropertyTable *property; const char *name, *interface; struct property_data *propdata; gboolean valid_signature; char *signature; if (!dbus_message_iter_init(message, &iter)) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No arguments given"); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); dbus_message_iter_get_basic(&iter, &interface); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid argument type: '%c'", dbus_message_iter_get_arg_type(&iter)); dbus_message_iter_recurse(&iter, &sub); iface = find_interface(data->interfaces, interface); if (iface == NULL) return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS, "No such interface '%s'", interface); property = find_property(iface->properties, name); if (property == NULL) return g_dbus_create_error(message, DBUS_ERROR_UNKNOWN_PROPERTY, "No such property '%s'", name); if (property->set == NULL) return g_dbus_create_error(message, DBUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable", name); if (property->exists != NULL && !property->exists(property, iface->user_data)) return g_dbus_create_error(message, DBUS_ERROR_UNKNOWN_PROPERTY, "No such property '%s'", name); signature = dbus_message_iter_get_signature(&sub); valid_signature = strcmp(signature, property->type) ? FALSE : TRUE; dbus_free(signature); if (!valid_signature) return g_dbus_create_error(message, DBUS_ERROR_INVALID_SIGNATURE, "Invalid signature for '%s'", name); propdata = g_new(struct property_data, 1); propdata->id = next_pending_property++; propdata->message = dbus_message_ref(message); propdata->conn = connection; pending_property_set = g_slist_prepend(pending_property_set, propdata); property->set(property, &sub, propdata->id, iface->user_data); return NULL; } static const GDBusMethodTable properties_methods[] = { { GDBUS_METHOD("Get", GDBUS_ARGS({ "interface", "s" }, { "name", "s" }), GDBUS_ARGS({ "value", "v" }), properties_get) }, { GDBUS_ASYNC_METHOD("Set", GDBUS_ARGS({ "interface", "s" }, { "name", "s" }, { "value", "v" }), NULL, properties_set) }, { GDBUS_METHOD("GetAll", GDBUS_ARGS({ "interface", "s" }), GDBUS_ARGS({ "properties", "a{sv}" }), properties_get_all) }, { } }; static const GDBusSignalTable properties_signals[] = { { GDBUS_SIGNAL("PropertiesChanged", GDBUS_ARGS({ "interface", "s" }, { "changed_properties", "a{sv}" }, { "invalidated_properties", "as"})) }, { } }; static void append_name(gpointer data, gpointer user_data) { char *name = data; DBusMessageIter *iter = user_data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name); } static void emit_interfaces_removed(struct generic_data *data) { DBusMessage *signal; DBusMessageIter iter, array; if (root == NULL || data == root) return; signal = dbus_message_new_signal(root->path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved"); if (signal == NULL) return; dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &data->path); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array); g_slist_foreach(data->removed, append_name, &array); g_slist_free_full(data->removed, g_free); data->removed = NULL; dbus_message_iter_close_container(&iter, &array); /* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */ dbus_connection_send(data->conn, signal, NULL); dbus_message_unref(signal); } static void remove_pending(struct generic_data *data) { if (data->process_id > 0) { g_source_remove(data->process_id); data->process_id = 0; } pending = g_slist_remove(pending, data); } static gboolean process_changes(gpointer user_data) { struct generic_data *data = user_data; remove_pending(data); if (data->added != NULL) emit_interfaces_added(data); /* Flush pending properties */ if (data->pending_prop == TRUE) process_property_changes(data); if (data->removed != NULL) emit_interfaces_removed(data); data->process_id = 0; return FALSE; } static void generic_unregister(DBusConnection *connection, void *user_data) { struct generic_data *data = user_data; struct generic_data *parent = data->parent; if (parent != NULL) parent->objects = g_slist_remove(parent->objects, data); if (data->process_id > 0) { g_source_remove(data->process_id); data->process_id = 0; process_changes(data); } g_slist_foreach(data->objects, reset_parent, data->parent); g_slist_free(data->objects); dbus_connection_unref(data->conn); g_free(data->introspect); g_free(data->path); g_free(data); } static DBusHandlerResult generic_message(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; struct interface_data *iface; const GDBusMethodTable *method; const char *interface; interface = dbus_message_get_interface(message); iface = find_interface(data->interfaces, interface); if (iface == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; for (method = iface->methods; method && method->name && method->function; method++) { if (dbus_message_is_method_call(message, iface->name, method->name) == FALSE) continue; if (check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_dbus_args_have_signature(method->in_args, message) == FALSE) continue; if (check_privilege(connection, message, method, iface->user_data) == TRUE) return DBUS_HANDLER_RESULT_HANDLED; return process_message(connection, message, method, iface->user_data); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static DBusObjectPathVTable generic_table = { .unregister_function = generic_unregister, .message_function = generic_message, }; static const GDBusMethodTable introspect_methods[] = { { GDBUS_METHOD("Introspect", NULL, GDBUS_ARGS({ "xml", "s" }), introspect) }, { } }; static void append_interfaces(struct generic_data *data, DBusMessageIter *iter) { DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); g_slist_foreach(data->interfaces, append_interface, &array); dbus_message_iter_close_container(iter, &array); } static void append_object(gpointer data, gpointer user_data) { struct generic_data *child = data; DBusMessageIter *array = user_data; DBusMessageIter entry; dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &child->path); append_interfaces(child, &entry); dbus_message_iter_close_container(array, &entry); g_slist_foreach(child->objects, append_object, user_data); } static DBusMessage *get_objects(DBusConnection *connection, DBusMessage *message, void *user_data) { struct generic_data *data = user_data; DBusMessage *reply; DBusMessageIter iter; DBusMessageIter array; reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); g_slist_foreach(data->objects, append_object, &array); dbus_message_iter_close_container(&iter, &array); return reply; } static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetManagedObjects", NULL, GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) }, { } }; static const GDBusSignalTable manager_signals[] = { { GDBUS_SIGNAL("InterfacesAdded", GDBUS_ARGS({ "object", "o" }, { "interfaces", "a{sa{sv}}" })) }, { GDBUS_SIGNAL("InterfacesRemoved", GDBUS_ARGS({ "object", "o" }, { "interfaces", "as" })) }, { } }; static gboolean add_interface(struct generic_data *data, const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, const GDBusPropertyTable *properties, void *user_data, GDBusDestroyFunction destroy) { struct interface_data *iface; const GDBusMethodTable *method; const GDBusSignalTable *signal; const GDBusPropertyTable *property; for (method = methods; method && method->name; method++) { if (!check_experimental(method->flags, G_DBUS_METHOD_FLAG_EXPERIMENTAL)) goto done; } for (signal = signals; signal && signal->name; signal++) { if (!check_experimental(signal->flags, G_DBUS_SIGNAL_FLAG_EXPERIMENTAL)) goto done; } for (property = properties; property && property->name; property++) { if (!check_experimental(property->flags, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL)) goto done; } /* Nothing to register */ return FALSE; done: iface = g_new0(struct interface_data, 1); iface->name = g_strdup(name); iface->methods = methods; iface->signals = signals; iface->properties = properties; iface->user_data = user_data; iface->destroy = destroy; data->interfaces = g_slist_append(data->interfaces, iface); if (data->parent == NULL) return TRUE; data->added = g_slist_append(data->added, iface); add_pending(data); return TRUE; } static struct generic_data *object_path_ref(DBusConnection *connection, const char *path) { struct generic_data *data; if (dbus_connection_get_object_path_data(connection, path, (void *) &data) == TRUE) { if (data != NULL) { data->refcount++; return data; } } data = g_new0(struct generic_data, 1); data->conn = dbus_connection_ref(connection); data->path = g_strdup(path); data->refcount = 1; data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE ""); if (!dbus_connection_register_object_path(connection, path, &generic_table, data)) { dbus_connection_unref(data->conn); g_free(data->path); g_free(data->introspect); g_free(data); return NULL; } invalidate_parent_data(connection, path); add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, introspect_methods, NULL, NULL, data, NULL); return data; } static void object_path_unref(DBusConnection *connection, const char *path) { struct generic_data *data = NULL; if (dbus_connection_get_object_path_data(connection, path, (void *) &data) == FALSE) return; if (data == NULL) return; data->refcount--; if (data->refcount > 0) return; remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE); remove_interface(data, DBUS_INTERFACE_PROPERTIES); invalidate_parent_data(data->conn, data->path); dbus_connection_unregister_object_path(data->conn, data->path); } static gboolean check_signal(DBusConnection *conn, const char *path, const char *interface, const char *name, const GDBusArgInfo **args) { struct generic_data *data = NULL; struct interface_data *iface; const GDBusSignalTable *signal; *args = NULL; if (!dbus_connection_get_object_path_data(conn, path, (void *) &data) || data == NULL) { error("dbus_connection_emit_signal: path %s isn't registered", path); return FALSE; } iface = find_interface(data->interfaces, interface); if (iface == NULL) { error("dbus_connection_emit_signal: %s does not implement %s", path, interface); return FALSE; } for (signal = iface->signals; signal && signal->name; signal++) { if (strcmp(signal->name, name) != 0) continue; if (signal->flags & G_DBUS_SIGNAL_FLAG_EXPERIMENTAL) { const char *env = g_getenv("GDBUS_EXPERIMENTAL"); if (g_strcmp0(env, "1") != 0) break; } *args = signal->args; return TRUE; } error("No signal named %s on interface %s", name, interface); return FALSE; } gboolean g_dbus_register_interface(DBusConnection *connection, const char *path, const char *name, const GDBusMethodTable *methods, const GDBusSignalTable *signals, const GDBusPropertyTable *properties, void *user_data, GDBusDestroyFunction destroy) { struct generic_data *data; data = object_path_ref(connection, path); if (data == NULL) return FALSE; if (find_interface(data->interfaces, name)) { object_path_unref(connection, path); return FALSE; } if (!add_interface(data, name, methods, signals, properties, user_data, destroy)) { object_path_unref(connection, path); return FALSE; } if (properties != NULL && !find_interface(data->interfaces, DBUS_INTERFACE_PROPERTIES)) add_interface(data, DBUS_INTERFACE_PROPERTIES, properties_methods, properties_signals, NULL, data, NULL); g_free(data->introspect); data->introspect = NULL; return TRUE; } gboolean g_dbus_unregister_interface(DBusConnection *connection, const char *path, const char *name) { struct generic_data *data = NULL; if (path == NULL) return FALSE; if (dbus_connection_get_object_path_data(connection, path, (void *) &data) == FALSE) return FALSE; if (data == NULL) return FALSE; if (remove_interface(data, name) == FALSE) return FALSE; g_free(data->introspect); data->introspect = NULL; object_path_unref(connection, data->path); return TRUE; } gboolean g_dbus_register_security(const GDBusSecurityTable *security) { if (security_table != NULL) return FALSE; security_table = security; return TRUE; } gboolean g_dbus_unregister_security(const GDBusSecurityTable *security) { security_table = NULL; return TRUE; } DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, const char *format, va_list args) { char str[1024]; if (format) vsnprintf(str, sizeof(str), format, args); else str[0] = '\0'; return dbus_message_new_error(message, name, str); } DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name, const char *format, ...) { va_list args; DBusMessage *reply; va_start(args, format); reply = g_dbus_create_error_valist(message, name, format, args); va_end(args); return reply; } DBusMessage *g_dbus_create_reply_valist(DBusMessage *message, int type, va_list args) { DBusMessage *reply; reply = dbus_message_new_method_return(message); if (reply == NULL) return NULL; if (dbus_message_append_args_valist(reply, type, args) == FALSE) { dbus_message_unref(reply); return NULL; } return reply; } DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...) { va_list args; DBusMessage *reply; va_start(args, type); reply = g_dbus_create_reply_valist(message, type, args); va_end(args); return reply; } static void g_dbus_flush(DBusConnection *connection) { GSList *l; for (l = pending; l;) { struct generic_data *data = l->data; l = l->next; if (data->conn != connection) continue; process_changes(data); } } gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message) { dbus_bool_t result = FALSE; if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL) dbus_message_set_no_reply(message, TRUE); else if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) { const char *path = dbus_message_get_path(message); const char *interface = dbus_message_get_interface(message); const char *name = dbus_message_get_member(message); const GDBusArgInfo *args; if (!check_signal(connection, path, interface, name, &args)) goto out; } /* Flush pending signal to guarantee message order */ g_dbus_flush(connection); result = dbus_connection_send(connection, message, NULL); out: dbus_message_unref(message); return result; } gboolean g_dbus_send_message_with_reply(DBusConnection *connection, DBusMessage *message, DBusPendingCall **call, int timeout) { dbus_bool_t ret; /* Flush pending signal to guarantee message order */ g_dbus_flush(connection); ret = dbus_connection_send_with_reply(connection, message, call, timeout); if (ret == TRUE && call != NULL && *call == NULL) { error("Unable to send message (passing fd blocked?)"); return FALSE; } return ret; } gboolean g_dbus_send_error_valist(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, va_list args) { DBusMessage *error; error = g_dbus_create_error_valist(message, name, format, args); if (error == NULL) return FALSE; return g_dbus_send_message(connection, error); } gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message, const char *name, const char *format, ...) { va_list args; gboolean result; va_start(args, format); result = g_dbus_send_error_valist(connection, message, name, format, args); va_end(args); return result; } gboolean g_dbus_send_reply_valist(DBusConnection *connection, DBusMessage *message, int type, va_list args) { DBusMessage *reply; reply = dbus_message_new_method_return(message); if (reply == NULL) return FALSE; if (dbus_message_append_args_valist(reply, type, args) == FALSE) { dbus_message_unref(reply); return FALSE; } return g_dbus_send_message(connection, reply); } gboolean g_dbus_send_reply(DBusConnection *connection, DBusMessage *message, int type, ...) { va_list args; gboolean result; va_start(args, type); result = g_dbus_send_reply_valist(connection, message, type, args); va_end(args); return result; } gboolean g_dbus_emit_signal(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, ...) { va_list args; gboolean result; va_start(args, type); result = g_dbus_emit_signal_valist(connection, path, interface, name, type, args); va_end(args); return result; } gboolean g_dbus_emit_signal_valist(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, va_list args) { DBusMessage *signal; dbus_bool_t ret; const GDBusArgInfo *args_info; if (!check_signal(connection, path, interface, name, &args_info)) return FALSE; signal = dbus_message_new_signal(path, interface, name); if (signal == NULL) { error("Unable to allocate new %s.%s signal", interface, name); return FALSE; } ret = dbus_message_append_args_valist(signal, type, args); if (!ret) goto fail; if (g_dbus_args_have_signature(args_info, signal) == FALSE) { error("%s.%s: got unexpected signature '%s'", interface, name, dbus_message_get_signature(signal)); ret = FALSE; goto fail; } return g_dbus_send_message(connection, signal); fail: dbus_message_unref(signal); return ret; } static void process_properties_from_interface(struct generic_data *data, struct interface_data *iface) { GSList *l; DBusMessage *signal; DBusMessageIter iter, dict, array; GSList *invalidated; data->pending_prop = FALSE; if (iface->pending_prop == NULL) return; signal = dbus_message_new_signal(data->path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged"); if (signal == NULL) { error("Unable to allocate new " DBUS_INTERFACE_PROPERTIES ".PropertiesChanged signal"); return; } iface->pending_prop = g_slist_reverse(iface->pending_prop); dbus_message_iter_init_append(signal, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface->name); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); invalidated = NULL; for (l = iface->pending_prop; l != NULL; l = l->next) { GDBusPropertyTable *p = l->data; if (p->get == NULL) continue; if (p->exists != NULL && !p->exists(p, iface->user_data)) { invalidated = g_slist_prepend(invalidated, p); continue; } append_property(iface, p, &dict); } dbus_message_iter_close_container(&iter, &dict); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array); for (l = invalidated; l != NULL; l = g_slist_next(l)) { GDBusPropertyTable *p = l->data; dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &p->name); } g_slist_free(invalidated); dbus_message_iter_close_container(&iter, &array); g_slist_free(iface->pending_prop); iface->pending_prop = NULL; /* Use dbus_connection_send to avoid recursive calls to g_dbus_flush */ dbus_connection_send(data->conn, signal, NULL); dbus_message_unref(signal); } static void process_property_changes(struct generic_data *data) { GSList *l; for (l = data->interfaces; l != NULL; l = l->next) { struct interface_data *iface = l->data; process_properties_from_interface(data, iface); } } void g_dbus_emit_property_changed(DBusConnection *connection, const char *path, const char *interface, const char *name) { const GDBusPropertyTable *property; struct generic_data *data; struct interface_data *iface; if (path == NULL) return; if (!dbus_connection_get_object_path_data(connection, path, (void **) &data) || data == NULL) return; iface = find_interface(data->interfaces, interface); if (iface == NULL) return; /* * If ObjectManager is attached, don't emit property changed if * interface is not yet published */ if (root && g_slist_find(data->added, iface)) return; property = find_property(iface->properties, name); if (property == NULL) { error("Could not find property %s in %p", name, iface->properties); return; } if (g_slist_find(iface->pending_prop, (void *) property) != NULL) return; data->pending_prop = TRUE; iface->pending_prop = g_slist_prepend(iface->pending_prop, (void *) property); add_pending(data); } gboolean g_dbus_get_properties(DBusConnection *connection, const char *path, const char *interface, DBusMessageIter *iter) { struct generic_data *data; struct interface_data *iface; if (path == NULL) return FALSE; if (!dbus_connection_get_object_path_data(connection, path, (void **) &data) || data == NULL) return FALSE; iface = find_interface(data->interfaces, interface); if (iface == NULL) return FALSE; append_properties(iface, iter); return TRUE; } gboolean g_dbus_attach_object_manager(DBusConnection *connection) { struct generic_data *data; data = object_path_ref(connection, "/"); if (data == NULL) return FALSE; add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER, manager_methods, manager_signals, NULL, data, NULL); root = data; return TRUE; } gboolean g_dbus_detach_object_manager(DBusConnection *connection) { if (!g_dbus_unregister_interface(connection, "/", DBUS_INTERFACE_OBJECT_MANAGER)) return FALSE; root = NULL; return TRUE; } void g_dbus_set_flags(int flags) { global_flags = flags; } int g_dbus_get_flags(void) { return global_flags; } ofono-1.17.bzr6912+16.04.20160314.3/gdbus/polkit.c0000644000015600001650000001262012671500024021152 0ustar pbuserpbgroup00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include int polkit_check_authorization(DBusConnection *conn, const char *action, gboolean interaction, void (*function) (dbus_bool_t authorized, void *user_data), void *user_data, int timeout); static void add_dict_with_string_value(DBusMessageIter *iter, const char *key, const char *str) { DBusMessageIter dict, entry, value; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &value); dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str); dbus_message_iter_close_container(&entry, &value); dbus_message_iter_close_container(&dict, &entry); dbus_message_iter_close_container(iter, &dict); } static void add_empty_string_dict(DBusMessageIter *iter) { DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); dbus_message_iter_close_container(iter, &dict); } static void add_arguments(DBusConnection *conn, DBusMessageIter *iter, const char *action, dbus_uint32_t flags) { const char *busname = dbus_bus_get_unique_name(conn); const char *kind = "system-bus-name"; const char *cancel = ""; DBusMessageIter subject; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &subject); dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind); add_dict_with_string_value(&subject, "name", busname); dbus_message_iter_close_container(iter, &subject); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action); add_empty_string_dict(iter); dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel); } static dbus_bool_t parse_result(DBusMessageIter *iter) { DBusMessageIter result; dbus_bool_t authorized, challenge; dbus_message_iter_recurse(iter, &result); dbus_message_iter_get_basic(&result, &authorized); dbus_message_iter_get_basic(&result, &challenge); return authorized; } struct authorization_data { void (*function) (dbus_bool_t authorized, void *user_data); void *user_data; }; static void authorization_reply(DBusPendingCall *call, void *user_data) { struct authorization_data *data = user_data; DBusMessage *reply; DBusMessageIter iter; dbus_bool_t authorized = FALSE; reply = dbus_pending_call_steal_reply(call); if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) goto done; if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE) goto done; dbus_message_iter_init(reply, &iter); authorized = parse_result(&iter); done: if (data->function != NULL) data->function(authorized, data->user_data); dbus_message_unref(reply); dbus_pending_call_unref(call); } #define AUTHORITY_DBUS "org.freedesktop.PolicyKit1" #define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority" #define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority" int polkit_check_authorization(DBusConnection *conn, const char *action, gboolean interaction, void (*function) (dbus_bool_t authorized, void *user_data), void *user_data, int timeout) { struct authorization_data *data; DBusMessage *msg; DBusMessageIter iter; DBusPendingCall *call; dbus_uint32_t flags = 0x00000000; if (conn == NULL) return -EINVAL; data = dbus_malloc0(sizeof(*data)); if (data == NULL) return -ENOMEM; msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH, AUTHORITY_INTF, "CheckAuthorization"); if (msg == NULL) { dbus_free(data); return -ENOMEM; } if (interaction == TRUE) flags |= 0x00000001; if (action == NULL) action = "org.freedesktop.policykit.exec"; dbus_message_iter_init_append(msg, &iter); add_arguments(conn, &iter, action, flags); if (dbus_connection_send_with_reply(conn, msg, &call, timeout) == FALSE) { dbus_message_unref(msg); dbus_free(data); return -EIO; } if (call == NULL) { dbus_message_unref(msg); dbus_free(data); return -EIO; } data->function = function; data->user_data = user_data; dbus_pending_call_set_notify(call, authorization_reply, data, dbus_free); dbus_message_unref(msg); return 0; } ofono-1.17.bzr6912+16.04.20160314.3/gdbus/watch.c0000644000015600001650000004426112671500024020764 0ustar pbuserpbgroup00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "gdbus.h" #define info(fmt...) #define error(fmt...) #define debug(fmt...) static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *user_data); static guint listener_id = 0; static GSList *listeners = NULL; struct service_data { DBusConnection *conn; DBusPendingCall *call; char *name; const char *owner; guint id; struct filter_callback *callback; }; struct filter_callback { GDBusWatchFunction conn_func; GDBusWatchFunction disc_func; GDBusSignalFunction signal_func; GDBusDestroyFunction destroy_func; struct service_data *data; void *user_data; guint id; }; struct filter_data { DBusConnection *connection; DBusHandleMessageFunction handle_func; char *name; char *owner; char *path; char *interface; char *member; char *argument; GSList *callbacks; GSList *processed; guint name_watch; gboolean lock; gboolean registered; }; static struct filter_data *filter_data_find_match(DBusConnection *connection, const char *name, const char *owner, const char *path, const char *interface, const char *member, const char *argument) { GSList *current; for (current = listeners; current != NULL; current = current->next) { struct filter_data *data = current->data; if (connection != data->connection) continue; if (g_strcmp0(name, data->name) != 0) continue; if (g_strcmp0(owner, data->owner) != 0) continue; if (g_strcmp0(path, data->path) != 0) continue; if (g_strcmp0(interface, data->interface) != 0) continue; if (g_strcmp0(member, data->member) != 0) continue; if (g_strcmp0(argument, data->argument) != 0) continue; return data; } return NULL; } static struct filter_data *filter_data_find(DBusConnection *connection) { GSList *current; for (current = listeners; current != NULL; current = current->next) { struct filter_data *data = current->data; if (connection != data->connection) continue; return data; } return NULL; } static void format_rule(struct filter_data *data, char *rule, size_t size) { const char *sender; int offset; offset = snprintf(rule, size, "type='signal'"); sender = data->name ? : data->owner; if (sender) offset += snprintf(rule + offset, size - offset, ",sender='%s'", sender); if (data->path) offset += snprintf(rule + offset, size - offset, ",path='%s'", data->path); if (data->interface) offset += snprintf(rule + offset, size - offset, ",interface='%s'", data->interface); if (data->member) offset += snprintf(rule + offset, size - offset, ",member='%s'", data->member); if (data->argument) snprintf(rule + offset, size - offset, ",arg0='%s'", data->argument); } static gboolean add_match(struct filter_data *data, DBusHandleMessageFunction filter) { DBusError err; char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; format_rule(data, rule, sizeof(rule)); dbus_error_init(&err); dbus_bus_add_match(data->connection, rule, &err); if (dbus_error_is_set(&err)) { error("Adding match rule \"%s\" failed: %s", rule, err.message); dbus_error_free(&err); return FALSE; } data->handle_func = filter; data->registered = TRUE; return TRUE; } static gboolean remove_match(struct filter_data *data) { DBusError err; char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; format_rule(data, rule, sizeof(rule)); dbus_error_init(&err); dbus_bus_remove_match(data->connection, rule, &err); if (dbus_error_is_set(&err)) { error("Removing owner match rule for %s failed: %s", rule, err.message); dbus_error_free(&err); return FALSE; } return TRUE; } static struct filter_data *filter_data_get(DBusConnection *connection, DBusHandleMessageFunction filter, const char *sender, const char *path, const char *interface, const char *member, const char *argument) { struct filter_data *data; const char *name = NULL, *owner = NULL; if (filter_data_find(connection) == NULL) { if (!dbus_connection_add_filter(connection, message_filter, NULL, NULL)) { error("dbus_connection_add_filter() failed"); return NULL; } } if (sender == NULL) goto proceed; if (sender[0] == ':') owner = sender; else name = sender; proceed: data = filter_data_find_match(connection, name, owner, path, interface, member, argument); if (data) return data; data = g_new0(struct filter_data, 1); data->connection = dbus_connection_ref(connection); data->name = g_strdup(name); data->owner = g_strdup(owner); data->path = g_strdup(path); data->interface = g_strdup(interface); data->member = g_strdup(member); data->argument = g_strdup(argument); if (!add_match(data, filter)) { g_free(data); return NULL; } listeners = g_slist_append(listeners, data); return data; } static struct filter_callback *filter_data_find_callback( struct filter_data *data, guint id) { GSList *l; for (l = data->callbacks; l; l = l->next) { struct filter_callback *cb = l->data; if (cb->id == id) return cb; } for (l = data->processed; l; l = l->next) { struct filter_callback *cb = l->data; if (cb->id == id) return cb; } return NULL; } static void filter_data_free(struct filter_data *data) { GSList *l; /* Remove filter if there are no listeners left for the connection */ if (filter_data_find(data->connection) == NULL) dbus_connection_remove_filter(data->connection, message_filter, NULL); for (l = data->callbacks; l != NULL; l = l->next) g_free(l->data); g_slist_free(data->callbacks); g_dbus_remove_watch(data->connection, data->name_watch); g_free(data->name); g_free(data->owner); g_free(data->path); g_free(data->interface); g_free(data->member); g_free(data->argument); dbus_connection_unref(data->connection); g_free(data); } static void filter_data_call_and_free(struct filter_data *data) { GSList *l; for (l = data->callbacks; l != NULL; l = l->next) { struct filter_callback *cb = l->data; if (cb->disc_func) cb->disc_func(data->connection, cb->user_data); if (cb->destroy_func) cb->destroy_func(cb->user_data); g_free(cb); } filter_data_free(data); } static struct filter_callback *filter_data_add_callback( struct filter_data *data, GDBusWatchFunction connect, GDBusWatchFunction disconnect, GDBusSignalFunction signal, GDBusDestroyFunction destroy, void *user_data) { struct filter_callback *cb = NULL; cb = g_new0(struct filter_callback, 1); cb->conn_func = connect; cb->disc_func = disconnect; cb->signal_func = signal; cb->destroy_func = destroy; cb->user_data = user_data; cb->id = ++listener_id; if (data->lock) data->processed = g_slist_append(data->processed, cb); else data->callbacks = g_slist_append(data->callbacks, cb); return cb; } static void service_data_free(struct service_data *data) { struct filter_callback *callback = data->callback; dbus_connection_unref(data->conn); if (data->call) dbus_pending_call_unref(data->call); if (data->id) g_source_remove(data->id); g_free(data->name); g_free(data); callback->data = NULL; } /* Returns TRUE if data is freed */ static gboolean filter_data_remove_callback(struct filter_data *data, struct filter_callback *cb) { data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_remove(data->processed, cb); /* Cancel pending operations */ if (cb->data) { if (cb->data->call) dbus_pending_call_cancel(cb->data->call); service_data_free(cb->data); } if (cb->destroy_func) cb->destroy_func(cb->user_data); g_free(cb); /* Don't remove the filter if other callbacks exist or data is lock * processing callbacks */ if (data->callbacks || data->lock) return FALSE; if (data->registered && !remove_match(data)) return FALSE; listeners = g_slist_remove(listeners, data); filter_data_free(data); return TRUE; } static DBusHandlerResult signal_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { struct filter_data *data = user_data; struct filter_callback *cb; while (data->callbacks) { cb = data->callbacks->data; if (cb->signal_func && !cb->signal_func(connection, message, cb->user_data)) { if (filter_data_remove_callback(data, cb)) break; continue; } /* Check if the watch was removed/freed by the callback * function */ if (!g_slist_find(data->callbacks, cb)) continue; data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_append(data->processed, cb); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static void update_name_cache(const char *name, const char *owner) { GSList *l; for (l = listeners; l != NULL; l = l->next) { struct filter_data *data = l->data; if (g_strcmp0(data->name, name) != 0) continue; g_free(data->owner); data->owner = g_strdup(owner); } } static const char *check_name_cache(const char *name) { GSList *l; for (l = listeners; l != NULL; l = l->next) { struct filter_data *data = l->data; if (g_strcmp0(data->name, name) != 0) continue; return data->owner; } return NULL; } static DBusHandlerResult service_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { struct filter_data *data = user_data; struct filter_callback *cb; char *name, *old, *new; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) { error("Invalid arguments for NameOwnerChanged signal"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } update_name_cache(name, new); while (data->callbacks) { cb = data->callbacks->data; if (*new == '\0') { if (cb->disc_func) cb->disc_func(connection, cb->user_data); } else { if (cb->conn_func) cb->conn_func(connection, cb->user_data); } /* Check if the watch was removed/freed by the callback * function */ if (!g_slist_find(data->callbacks, cb)) continue; /* Only auto remove if it is a bus name watch */ if (data->argument[0] == ':' && (cb->conn_func == NULL || cb->disc_func == NULL)) { if (filter_data_remove_callback(data, cb)) break; continue; } data->callbacks = g_slist_remove(data->callbacks, cb); data->processed = g_slist_append(data->processed, cb); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { struct filter_data *data; const char *sender, *path, *iface, *member, *arg = NULL; GSList *current, *delete_listener = NULL; /* Only filter signals */ if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; sender = dbus_message_get_sender(message); path = dbus_message_get_path(message); iface = dbus_message_get_interface(message); member = dbus_message_get_member(message); dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); /* If sender != NULL it is always the owner */ for (current = listeners; current != NULL; current = current->next) { data = current->data; if (connection != data->connection) continue; if (!sender && data->owner) continue; if (data->owner && g_str_equal(sender, data->owner) == FALSE) continue; if (data->path && g_str_equal(path, data->path) == FALSE) continue; if (data->interface && g_str_equal(iface, data->interface) == FALSE) continue; if (data->member && g_str_equal(member, data->member) == FALSE) continue; if (data->argument && g_str_equal(arg, data->argument) == FALSE) continue; if (data->handle_func) { data->lock = TRUE; data->handle_func(connection, message, data); data->callbacks = data->processed; data->processed = NULL; data->lock = FALSE; } if (!data->callbacks) delete_listener = g_slist_prepend(delete_listener, current); } if (delete_listener == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; for (current = delete_listener; current != NULL; current = delete_listener->next) { GSList *l = current->data; data = l->data; /* Has any other callback added callbacks back to this data? */ if (data->callbacks != NULL) continue; remove_match(data); listeners = g_slist_delete_link(listeners, l); filter_data_free(data); } g_slist_free(delete_listener); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static gboolean update_service(void *user_data) { struct service_data *data = user_data; struct filter_callback *cb = data->callback; DBusConnection *conn; conn = dbus_connection_ref(data->conn); service_data_free(data); if (cb->conn_func) cb->conn_func(conn, cb->user_data); dbus_connection_unref(conn); return FALSE; } static void service_reply(DBusPendingCall *call, void *user_data) { struct service_data *data = user_data; DBusMessage *reply; DBusError err; reply = dbus_pending_call_steal_reply(call); if (reply == NULL) return; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply)) goto fail; if (dbus_message_get_args(reply, &err, DBUS_TYPE_STRING, &data->owner, DBUS_TYPE_INVALID) == FALSE) goto fail; update_service(data); goto done; fail: error("%s", err.message); dbus_error_free(&err); service_data_free(data); done: dbus_message_unref(reply); } static void check_service(DBusConnection *connection, const char *name, struct filter_callback *callback) { DBusMessage *message; struct service_data *data; data = g_try_malloc0(sizeof(*data)); if (data == NULL) { error("Can't allocate data structure"); return; } data->conn = dbus_connection_ref(connection); data->name = g_strdup(name); data->callback = callback; callback->data = data; data->owner = check_name_cache(name); if (data->owner != NULL) { data->id = g_idle_add(update_service, data); return; } message = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); if (message == NULL) { error("Can't allocate new message"); g_free(data); return; } dbus_message_append_args(message, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); if (dbus_connection_send_with_reply(connection, message, &data->call, -1) == FALSE) { error("Failed to execute method call"); g_free(data); goto done; } if (data->call == NULL) { error("D-Bus connection not available"); g_free(data); goto done; } dbus_pending_call_set_notify(data->call, service_reply, data, NULL); done: dbus_message_unref(message); } guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, GDBusWatchFunction connect, GDBusWatchFunction disconnect, void *user_data, GDBusDestroyFunction destroy) { struct filter_data *data; struct filter_callback *cb; if (name == NULL) return 0; data = filter_data_get(connection, service_filter, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "NameOwnerChanged", name); if (data == NULL) return 0; cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy, user_data); if (cb == NULL) return 0; if (connect) check_service(connection, name, cb); return cb->id; } guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, GDBusWatchFunction func, void *user_data, GDBusDestroyFunction destroy) { return g_dbus_add_service_watch(connection, name, NULL, func, user_data, destroy); } guint g_dbus_add_signal_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, const char *member, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy) { struct filter_data *data; struct filter_callback *cb; data = filter_data_get(connection, signal_filter, sender, path, interface, member, NULL); if (data == NULL) return 0; cb = filter_data_add_callback(data, NULL, NULL, function, destroy, user_data); if (cb == NULL) return 0; if (data->name != NULL && data->name_watch == 0) data->name_watch = g_dbus_add_service_watch(connection, data->name, NULL, NULL, NULL, NULL); return cb->id; } guint g_dbus_add_properties_watch(DBusConnection *connection, const char *sender, const char *path, const char *interface, GDBusSignalFunction function, void *user_data, GDBusDestroyFunction destroy) { struct filter_data *data; struct filter_callback *cb; data = filter_data_get(connection, signal_filter, sender, path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", interface); if (data == NULL) return 0; cb = filter_data_add_callback(data, NULL, NULL, function, destroy, user_data); if (cb == NULL) return 0; if (data->name != NULL && data->name_watch == 0) data->name_watch = g_dbus_add_service_watch(connection, data->name, NULL, NULL, NULL, NULL); return cb->id; } gboolean g_dbus_remove_watch(DBusConnection *connection, guint id) { struct filter_data *data; struct filter_callback *cb; GSList *ldata; if (id == 0) return FALSE; for (ldata = listeners; ldata; ldata = ldata->next) { data = ldata->data; cb = filter_data_find_callback(data, id); if (cb) { filter_data_remove_callback(data, cb); return TRUE; } } return FALSE; } void g_dbus_remove_all_watches(DBusConnection *connection) { struct filter_data *data; while ((data = filter_data_find(connection))) { listeners = g_slist_remove(listeners, data); filter_data_call_and_free(data); } } ofono-1.17.bzr6912+16.04.20160314.3/gdbus/client.c0000644000015600001650000007617512671500024021145 0ustar pbuserpbgroup00000000000000/* * * D-Bus helper library * * Copyright (C) 2004-2011 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "gdbus.h" #define METHOD_CALL_TIMEOUT (300 * 1000) #ifndef DBUS_INTERFACE_OBJECT_MANAGER #define DBUS_INTERFACE_OBJECT_MANAGER DBUS_INTERFACE_DBUS ".ObjectManager" #endif struct GDBusClient { int ref_count; DBusConnection *dbus_conn; char *service_name; char *base_path; char *root_path; guint watch; guint added_watch; guint removed_watch; GPtrArray *match_rules; DBusPendingCall *pending_call; DBusPendingCall *get_objects_call; GDBusWatchFunction connect_func; void *connect_data; GDBusWatchFunction disconn_func; gboolean connected; void *disconn_data; GDBusMessageFunction signal_func; void *signal_data; GDBusProxyFunction proxy_added; GDBusProxyFunction proxy_removed; GDBusClientFunction ready; void *ready_data; GDBusPropertyFunction property_changed; void *user_data; GList *proxy_list; }; struct GDBusProxy { int ref_count; GDBusClient *client; char *obj_path; char *interface; GHashTable *prop_list; guint watch; GDBusPropertyFunction prop_func; void *prop_data; GDBusProxyFunction removed_func; void *removed_data; }; struct prop_entry { char *name; int type; DBusMessage *msg; }; static void modify_match_reply(DBusPendingCall *call, void *user_data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) dbus_error_free(&error); dbus_message_unref(reply); } static gboolean modify_match(DBusConnection *conn, const char *member, const char *rule) { DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, member); if (msg == NULL) return FALSE; dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return FALSE; } dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } static void iter_append_iter(DBusMessageIter *base, DBusMessageIter *iter) { int type; type = dbus_message_iter_get_arg_type(iter); if (dbus_type_is_basic(type)) { const void *value; dbus_message_iter_get_basic(iter, &value); dbus_message_iter_append_basic(base, type, &value); } else if (dbus_type_is_container(type)) { DBusMessageIter iter_sub, base_sub; char *sig; dbus_message_iter_recurse(iter, &iter_sub); switch (type) { case DBUS_TYPE_ARRAY: case DBUS_TYPE_VARIANT: sig = dbus_message_iter_get_signature(&iter_sub); break; default: sig = NULL; break; } dbus_message_iter_open_container(base, type, sig, &base_sub); if (sig != NULL) dbus_free(sig); while (dbus_message_iter_get_arg_type(&iter_sub) != DBUS_TYPE_INVALID) { iter_append_iter(&base_sub, &iter_sub); dbus_message_iter_next(&iter_sub); } dbus_message_iter_close_container(base, &base_sub); } } static void prop_entry_update(struct prop_entry *prop, DBusMessageIter *iter) { DBusMessage *msg; DBusMessageIter base; msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); if (msg == NULL) return; dbus_message_iter_init_append(msg, &base); iter_append_iter(&base, iter); if (prop->msg != NULL) dbus_message_unref(prop->msg); prop->msg = dbus_message_copy(msg); dbus_message_unref(msg); } static struct prop_entry *prop_entry_new(const char *name, DBusMessageIter *iter) { struct prop_entry *prop; prop = g_try_new0(struct prop_entry, 1); if (prop == NULL) return NULL; prop->name = g_strdup(name); prop->type = dbus_message_iter_get_arg_type(iter); prop_entry_update(prop, iter); return prop; } static void prop_entry_free(gpointer data) { struct prop_entry *prop = data; if (prop->msg != NULL) dbus_message_unref(prop->msg); g_free(prop->name); g_free(prop); } static void add_property(GDBusProxy *proxy, const char *name, DBusMessageIter *iter, gboolean send_changed) { GDBusClient *client = proxy->client; DBusMessageIter value; struct prop_entry *prop; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) return; dbus_message_iter_recurse(iter, &value); prop = g_hash_table_lookup(proxy->prop_list, name); if (prop != NULL) { prop_entry_update(prop, &value); goto done; } prop = prop_entry_new(name, &value); if (prop == NULL) return; g_hash_table_replace(proxy->prop_list, prop->name, prop); done: if (proxy->prop_func) proxy->prop_func(proxy, name, &value, proxy->prop_data); if (client == NULL || send_changed == FALSE) return; if (client->property_changed) client->property_changed(proxy, name, &value, client->user_data); } static void update_properties(GDBusProxy *proxy, DBusMessageIter *iter, gboolean send_changed) { DBusMessageIter dict; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry; const char *name; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) break; dbus_message_iter_get_basic(&entry, &name); dbus_message_iter_next(&entry); add_property(proxy, name, &entry, send_changed); dbus_message_iter_next(&dict); } } static void get_all_properties_reply(DBusPendingCall *call, void *user_data) { GDBusProxy *proxy = user_data; GDBusClient *client = proxy->client; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusMessageIter iter; DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) { dbus_error_free(&error); goto done; } dbus_message_iter_init(reply, &iter); update_properties(proxy, &iter, FALSE); done: if (g_list_find(client->proxy_list, proxy) == NULL) { if (client->proxy_added) client->proxy_added(proxy, client->user_data); client->proxy_list = g_list_append(client->proxy_list, proxy); } dbus_message_unref(reply); g_dbus_client_unref(client); } static void get_all_properties(GDBusProxy *proxy) { GDBusClient *client = proxy->client; const char *service_name = client->service_name; DBusMessage *msg; DBusPendingCall *call; msg = dbus_message_new_method_call(service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "GetAll"); if (msg == NULL) return; dbus_message_append_args(msg, DBUS_TYPE_STRING, &proxy->interface, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); return; } g_dbus_client_ref(client); dbus_pending_call_set_notify(call, get_all_properties_reply, proxy, NULL); dbus_pending_call_unref(call); dbus_message_unref(msg); } static GDBusProxy *proxy_lookup(GDBusClient *client, const char *path, const char *interface) { GList *list; for (list = g_list_first(client->proxy_list); list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; if (g_str_equal(proxy->interface, interface) == TRUE && g_str_equal(proxy->obj_path, path) == TRUE) return proxy; } return NULL; } static gboolean properties_changed(DBusConnection *conn, DBusMessage *msg, void *user_data) { GDBusProxy *proxy = user_data; GDBusClient *client = proxy->client; DBusMessageIter iter, entry; const char *interface; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return TRUE; dbus_message_iter_get_basic(&iter, &interface); dbus_message_iter_next(&iter); update_properties(proxy, &iter, TRUE); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return TRUE; dbus_message_iter_recurse(&iter, &entry); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *name; dbus_message_iter_get_basic(&entry, &name); g_hash_table_remove(proxy->prop_list, name); if (proxy->prop_func) proxy->prop_func(proxy, name, NULL, proxy->prop_data); if (client->property_changed) client->property_changed(proxy, name, NULL, client->user_data); dbus_message_iter_next(&entry); } return TRUE; } static GDBusProxy *proxy_new(GDBusClient *client, const char *path, const char *interface) { GDBusProxy *proxy; proxy = g_try_new0(GDBusProxy, 1); if (proxy == NULL) return NULL; proxy->client = client; proxy->obj_path = g_strdup(path); proxy->interface = g_strdup(interface); proxy->prop_list = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, prop_entry_free); proxy->watch = g_dbus_add_properties_watch(client->dbus_conn, client->service_name, proxy->obj_path, proxy->interface, properties_changed, proxy, NULL); return g_dbus_proxy_ref(proxy); } static void proxy_free(gpointer data) { GDBusProxy *proxy = data; if (proxy->client) { GDBusClient *client = proxy->client; if (client->proxy_removed) client->proxy_removed(proxy, client->user_data); g_dbus_remove_watch(client->dbus_conn, proxy->watch); g_hash_table_remove_all(proxy->prop_list); proxy->client = NULL; } if (proxy->removed_func) proxy->removed_func(proxy, proxy->removed_data); g_dbus_proxy_unref(proxy); } static void proxy_remove(GDBusClient *client, const char *path, const char *interface) { GList *list; for (list = g_list_first(client->proxy_list); list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; if (g_str_equal(proxy->interface, interface) == TRUE && g_str_equal(proxy->obj_path, path) == TRUE) { client->proxy_list = g_list_delete_link(client->proxy_list, list); proxy_free(proxy); break; } } } GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path, const char *interface) { GDBusProxy *proxy; if (client == NULL) return NULL; proxy = proxy_lookup(client, path, interface); if (proxy) return g_dbus_proxy_ref(proxy); proxy = proxy_new(client, path, interface); if (proxy == NULL) return NULL; get_all_properties(proxy); return g_dbus_proxy_ref(proxy); } GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy) { if (proxy == NULL) return NULL; __sync_fetch_and_add(&proxy->ref_count, 1); return proxy; } void g_dbus_proxy_unref(GDBusProxy *proxy) { if (proxy == NULL) return; if (__sync_sub_and_fetch(&proxy->ref_count, 1) > 0) return; g_hash_table_destroy(proxy->prop_list); g_free(proxy->obj_path); g_free(proxy->interface); g_free(proxy); } const char *g_dbus_proxy_get_path(GDBusProxy *proxy) { if (proxy == NULL) return NULL; return proxy->obj_path; } const char *g_dbus_proxy_get_interface(GDBusProxy *proxy) { if (proxy == NULL) return NULL; return proxy->interface; } gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name, DBusMessageIter *iter) { struct prop_entry *prop; if (proxy == NULL || name == NULL) return FALSE; prop = g_hash_table_lookup(proxy->prop_list, name); if (prop == NULL) return FALSE; if (prop->msg == NULL) return FALSE; if (dbus_message_iter_init(prop->msg, iter) == FALSE) return FALSE; return TRUE; } struct refresh_property_data { GDBusProxy *proxy; char *name; }; static void refresh_property_free(gpointer user_data) { struct refresh_property_data *data = user_data; g_free(data->name); g_free(data); } static void refresh_property_reply(DBusPendingCall *call, void *user_data) { struct refresh_property_data *data = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == FALSE) { DBusMessageIter iter; dbus_message_iter_init(reply, &iter); add_property(data->proxy, data->name, &iter, TRUE); } else dbus_error_free(&error); dbus_message_unref(reply); } gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name) { struct refresh_property_data *data; GDBusClient *client; DBusMessage *msg; DBusMessageIter iter; DBusPendingCall *call; if (proxy == NULL || name == NULL) return FALSE; client = proxy->client; if (client == NULL) return FALSE; data = g_try_new0(struct refresh_property_data, 1); if (data == NULL) return FALSE; data->proxy = proxy; data->name = g_strdup(name); msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Get"); if (msg == NULL) { refresh_property_free(data); return FALSE; } dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &proxy->interface); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); refresh_property_free(data); return FALSE; } dbus_pending_call_set_notify(call, refresh_property_reply, data, refresh_property_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } struct set_property_data { GDBusResultFunction function; void *user_data; GDBusDestroyFunction destroy; }; static void set_property_reply(DBusPendingCall *call, void *user_data) { struct set_property_data *data = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; dbus_error_init(&error); dbus_set_error_from_message(&error, reply); if (data->function) data->function(&error, data->user_data); if (data->destroy) data->destroy(data->user_data); dbus_error_free(&error); dbus_message_unref(reply); } gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy, const char *name, int type, const void *value, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy) { struct set_property_data *data; GDBusClient *client; DBusMessage *msg; DBusMessageIter iter, variant; DBusPendingCall *call; char type_as_str[2]; if (proxy == NULL || name == NULL || value == NULL) return FALSE; if (dbus_type_is_basic(type) == FALSE) return FALSE; client = proxy->client; if (client == NULL) return FALSE; data = g_try_new0(struct set_property_data, 1); if (data == NULL) return FALSE; data->function = function; data->user_data = user_data; data->destroy = destroy; msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set"); if (msg == NULL) { g_free(data); return FALSE; } type_as_str[0] = (char) type; type_as_str[1] = '\0'; dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &proxy->interface); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, type_as_str, &variant); dbus_message_iter_append_basic(&variant, type, value); dbus_message_iter_close_container(&iter, &variant); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); g_free(data); return FALSE; } dbus_pending_call_set_notify(call, set_property_reply, data, g_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy, const char *name, int type, const void *value, size_t size, GDBusResultFunction function, void *user_data, GDBusDestroyFunction destroy) { struct set_property_data *data; GDBusClient *client; DBusMessage *msg; DBusMessageIter iter, variant, array; DBusPendingCall *call; char array_sig[3]; char type_sig[2]; if (!proxy || !name || !value) return FALSE; if (!dbus_type_is_basic(type)) return FALSE; client = proxy->client; if (!client) return FALSE; data = g_try_new0(struct set_property_data, 1); if (!data) return FALSE; data->function = function; data->user_data = user_data; data->destroy = destroy; msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set"); if (!msg) { g_free(data); return FALSE; } array_sig[0] = DBUS_TYPE_ARRAY; array_sig[1] = (char) type; array_sig[2] = '\0'; type_sig[0] = (char) type; type_sig[1] = '\0'; dbus_message_iter_init_append(msg, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &proxy->interface); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name); dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, array_sig, &variant); dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY, type_sig, &array); if (dbus_type_is_fixed(type)) dbus_message_iter_append_fixed_array(&array, type, &value, size); else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) { const char **str = (const char **) value; size_t i; for (i = 0; i < size; i++) dbus_message_iter_append_basic(&array, type, &str[i]); } dbus_message_iter_close_container(&variant, &array); dbus_message_iter_close_container(&iter, &variant); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, -1) == FALSE) { dbus_message_unref(msg); g_free(data); return FALSE; } dbus_pending_call_set_notify(call, set_property_reply, data, g_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } struct method_call_data { GDBusReturnFunction function; void *user_data; GDBusDestroyFunction destroy; }; static void method_call_reply(DBusPendingCall *call, void *user_data) { struct method_call_data *data = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); if (data->function) data->function(reply, data->user_data); if (data->destroy) data->destroy(data->user_data); dbus_message_unref(reply); } gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method, GDBusSetupFunction setup, GDBusReturnFunction function, void *user_data, GDBusDestroyFunction destroy) { struct method_call_data *data; GDBusClient *client; DBusMessage *msg; DBusPendingCall *call; if (proxy == NULL || method == NULL) return FALSE; client = proxy->client; if (client == NULL) return FALSE; data = g_try_new0(struct method_call_data, 1); if (data == NULL) return FALSE; data->function = function; data->user_data = user_data; data->destroy = destroy; msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, proxy->interface, method); if (msg == NULL) { g_free(data); return FALSE; } if (setup) { DBusMessageIter iter; dbus_message_iter_init_append(msg, &iter); setup(&iter, data->user_data); } if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, METHOD_CALL_TIMEOUT) == FALSE) { dbus_message_unref(msg); g_free(data); return FALSE; } dbus_pending_call_set_notify(call, method_call_reply, data, g_free); dbus_pending_call_unref(call); dbus_message_unref(msg); return TRUE; } gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy, GDBusPropertyFunction function, void *user_data) { if (proxy == NULL) return FALSE; proxy->prop_func = function; proxy->prop_data = user_data; return TRUE; } gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy, GDBusProxyFunction function, void *user_data) { if (proxy == NULL) return FALSE; proxy->removed_func = function; proxy->removed_data = user_data; return TRUE; } static void refresh_properties(GDBusClient *client) { GList *list; for (list = g_list_first(client->proxy_list); list; list = g_list_next(list)) { GDBusProxy *proxy = list->data; get_all_properties(proxy); } } static void parse_properties(GDBusClient *client, const char *path, const char *interface, DBusMessageIter *iter) { GDBusProxy *proxy; if (g_str_equal(interface, DBUS_INTERFACE_INTROSPECTABLE) == TRUE) return; if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE) return; proxy = proxy_lookup(client, path, interface); if (proxy) { update_properties(proxy, iter, FALSE); return; } proxy = proxy_new(client, path, interface); if (proxy == NULL) return; update_properties(proxy, iter, FALSE); if (client->proxy_added) client->proxy_added(proxy, client->user_data); client->proxy_list = g_list_append(client->proxy_list, proxy); } static void parse_interfaces(GDBusClient *client, const char *path, DBusMessageIter *iter) { DBusMessageIter dict; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry; const char *interface; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) break; dbus_message_iter_get_basic(&entry, &interface); dbus_message_iter_next(&entry); parse_properties(client, path, interface, &entry); dbus_message_iter_next(&dict); } } static gboolean interfaces_added(DBusConnection *conn, DBusMessage *msg, void *user_data) { GDBusClient *client = user_data; DBusMessageIter iter; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) return TRUE; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); g_dbus_client_ref(client); parse_interfaces(client, path, &iter); g_dbus_client_unref(client); return TRUE; } static gboolean interfaces_removed(DBusConnection *conn, DBusMessage *msg, void *user_data) { GDBusClient *client = user_data; DBusMessageIter iter, entry; const char *path; if (dbus_message_iter_init(msg, &iter) == FALSE) return TRUE; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH) return TRUE; dbus_message_iter_get_basic(&iter, &path); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return TRUE; dbus_message_iter_recurse(&iter, &entry); g_dbus_client_ref(client); while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) { const char *interface; dbus_message_iter_get_basic(&entry, &interface); proxy_remove(client, path, interface); dbus_message_iter_next(&entry); } g_dbus_client_unref(client); return TRUE; } static void parse_managed_objects(GDBusClient *client, DBusMessage *msg) { DBusMessageIter iter, dict; if (dbus_message_iter_init(msg, &iter) == FALSE) return; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) return; dbus_message_iter_recurse(&iter, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry; const char *path; dbus_message_iter_recurse(&dict, &entry); if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_OBJECT_PATH) break; dbus_message_iter_get_basic(&entry, &path); dbus_message_iter_next(&entry); parse_interfaces(client, path, &entry); dbus_message_iter_next(&dict); } if (client->ready) client->ready(client, client->ready_data); } static void get_managed_objects_reply(DBusPendingCall *call, void *user_data) { GDBusClient *client = user_data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError error; g_dbus_client_ref(client); dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply) == TRUE) { dbus_error_free(&error); goto done; } parse_managed_objects(client, reply); done: dbus_message_unref(reply); dbus_pending_call_unref(client->get_objects_call); client->get_objects_call = NULL; g_dbus_client_unref(client); } static void get_managed_objects(GDBusClient *client) { DBusMessage *msg; if (!client->connected) return; if ((!client->proxy_added && !client->proxy_removed) || !client->root_path) { refresh_properties(client); return; } if (client->get_objects_call != NULL) return; msg = dbus_message_new_method_call(client->service_name, client->root_path, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects"); if (msg == NULL) return; dbus_message_append_args(msg, DBUS_TYPE_INVALID); if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &client->get_objects_call, -1) == FALSE) { dbus_message_unref(msg); return; } dbus_pending_call_set_notify(client->get_objects_call, get_managed_objects_reply, client, NULL); dbus_message_unref(msg); } static void service_connect(DBusConnection *conn, void *user_data) { GDBusClient *client = user_data; g_dbus_client_ref(client); client->connected = TRUE; if (client->connect_func) client->connect_func(conn, client->connect_data); get_managed_objects(client); g_dbus_client_unref(client); } static void service_disconnect(DBusConnection *conn, void *user_data) { GDBusClient *client = user_data; client->connected = FALSE; g_list_free_full(client->proxy_list, proxy_free); client->proxy_list = NULL; if (client->disconn_func) client->disconn_func(conn, client->disconn_data); } static DBusHandlerResult message_filter(DBusConnection *connection, DBusMessage *message, void *user_data) { GDBusClient *client = user_data; const char *sender, *path, *interface; if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; sender = dbus_message_get_sender(message); if (sender == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; path = dbus_message_get_path(message); interface = dbus_message_get_interface(message); if (g_str_has_prefix(path, client->base_path) == FALSE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (client->signal_func) client->signal_func(connection, message, client->signal_data); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } GDBusClient *g_dbus_client_new(DBusConnection *connection, const char *service, const char *path) { return g_dbus_client_new_full(connection, service, path, "/"); } GDBusClient *g_dbus_client_new_full(DBusConnection *connection, const char *service, const char *path, const char *root_path) { GDBusClient *client; unsigned int i; if (!connection || !service) return NULL; client = g_try_new0(GDBusClient, 1); if (client == NULL) return NULL; if (dbus_connection_add_filter(connection, message_filter, client, NULL) == FALSE) { g_free(client); return NULL; } client->dbus_conn = dbus_connection_ref(connection); client->service_name = g_strdup(service); client->base_path = g_strdup(path); client->root_path = g_strdup(root_path); client->connected = FALSE; client->match_rules = g_ptr_array_sized_new(1); g_ptr_array_set_free_func(client->match_rules, g_free); client->watch = g_dbus_add_service_watch(connection, service, service_connect, service_disconnect, client, NULL); if (!root_path) return g_dbus_client_ref(client); client->added_watch = g_dbus_add_signal_watch(connection, service, client->root_path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded", interfaces_added, client, NULL); client->removed_watch = g_dbus_add_signal_watch(connection, service, client->root_path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved", interfaces_removed, client, NULL); g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal'," "sender='%s',path_namespace='%s'", client->service_name, client->base_path)); for (i = 0; i < client->match_rules->len; i++) { modify_match(client->dbus_conn, "AddMatch", g_ptr_array_index(client->match_rules, i)); } return g_dbus_client_ref(client); } GDBusClient *g_dbus_client_ref(GDBusClient *client) { if (client == NULL) return NULL; __sync_fetch_and_add(&client->ref_count, 1); return client; } void g_dbus_client_unref(GDBusClient *client) { unsigned int i; if (client == NULL) return; if (__sync_sub_and_fetch(&client->ref_count, 1) > 0) return; if (client->pending_call != NULL) { dbus_pending_call_cancel(client->pending_call); dbus_pending_call_unref(client->pending_call); } if (client->get_objects_call != NULL) { dbus_pending_call_cancel(client->get_objects_call); dbus_pending_call_unref(client->get_objects_call); } for (i = 0; i < client->match_rules->len; i++) { modify_match(client->dbus_conn, "RemoveMatch", g_ptr_array_index(client->match_rules, i)); } g_ptr_array_free(client->match_rules, TRUE); dbus_connection_remove_filter(client->dbus_conn, message_filter, client); g_list_free_full(client->proxy_list, proxy_free); /* * Don't call disconn_func twice if disconnection * was previously reported. */ if (client->disconn_func && client->connected) client->disconn_func(client->dbus_conn, client->disconn_data); g_dbus_remove_watch(client->dbus_conn, client->watch); g_dbus_remove_watch(client->dbus_conn, client->added_watch); g_dbus_remove_watch(client->dbus_conn, client->removed_watch); dbus_connection_unref(client->dbus_conn); g_free(client->service_name); g_free(client->base_path); g_free(client->root_path); g_free(client); } gboolean g_dbus_client_set_connect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data) { if (client == NULL) return FALSE; client->connect_func = function; client->connect_data = user_data; return TRUE; } gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client, GDBusWatchFunction function, void *user_data) { if (client == NULL) return FALSE; client->disconn_func = function; client->disconn_data = user_data; return TRUE; } gboolean g_dbus_client_set_signal_watch(GDBusClient *client, GDBusMessageFunction function, void *user_data) { if (client == NULL) return FALSE; client->signal_func = function; client->signal_data = user_data; return TRUE; } gboolean g_dbus_client_set_ready_watch(GDBusClient *client, GDBusClientFunction ready, void *user_data) { if (client == NULL) return FALSE; client->ready = ready; client->ready_data = user_data; return TRUE; } gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client, GDBusProxyFunction proxy_added, GDBusProxyFunction proxy_removed, GDBusPropertyFunction property_changed, void *user_data) { if (client == NULL) return FALSE; client->proxy_added = proxy_added; client->proxy_removed = proxy_removed; client->property_changed = property_changed; client->user_data = user_data; if (proxy_added || proxy_removed || property_changed) get_managed_objects(client); return TRUE; } ofono-1.17.bzr6912+16.04.20160314.3/examples/0000755000015600001650000000000012671500304020216 5ustar pbuserpbgroup00000000000000ofono-1.17.bzr6912+16.04.20160314.3/examples/provision.c0000644000015600001650000000571612671500024022422 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include static int example_provision_get_settings(const char *mcc, const char *mnc, const char *spn, const char *imsi, const char *gid1, struct ofono_gprs_provision_data **settings, int *count) { ofono_debug("Provisioning..."); *count = 0; *settings = NULL; ofono_info("Provisioning for MCC %s, MNC %s, SPN '%s', IMSI '%s', " "GID1 '%s'", mcc, mnc, spn, imsi, gid1); if (strcmp(mcc, "246") != 0 || strcmp(mnc, "81") != 0 || g_strcmp0(spn, "oFono") != 0) return -ENOENT; ofono_debug("Creating example settings for phonesim"); *settings = g_try_new0(struct ofono_gprs_provision_data, 2); if (*settings == NULL) return -ENOMEM; *count = 2; /* Internet context settings */ (*settings)[0].proto = OFONO_GPRS_PROTO_IP; (*settings)[0].type = OFONO_GPRS_CONTEXT_TYPE_INTERNET; (*settings)[0].name = g_strdup("Phonesim Internet"); (*settings)[0].apn = g_strdup("internetapn"); /* MMS context settings */ (*settings)[1].proto = OFONO_GPRS_PROTO_IP; (*settings)[1].type = OFONO_GPRS_CONTEXT_TYPE_MMS; (*settings)[1].name = g_strdup("Phonesim MMS"); (*settings)[1].apn = g_strdup("mmsapn"); (*settings)[1].username = g_strdup("mmsuser"); (*settings)[1].password = g_strdup("mmspass"); (*settings)[1].message_proxy = g_strdup("10.11.12.13:8080"); (*settings)[1].message_center = g_strdup("http://mms.example.com:8000"); return 0; } static struct ofono_gprs_provision_driver example_driver = { .name = "Example GPRS context provisioning", .priority = OFONO_PLUGIN_PRIORITY_LOW, .get_settings = example_provision_get_settings, }; static int example_provision_init(void) { return ofono_gprs_provision_driver_register(&example_driver); } static void example_provision_exit(void) { ofono_gprs_provision_driver_unregister(&example_driver); } OFONO_PLUGIN_DEFINE(example_provision, "Example Provisioning Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, example_provision_init, example_provision_exit) ofono-1.17.bzr6912+16.04.20160314.3/examples/history.c0000644000015600001650000001351512671500024022067 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "common.h" static int example_history_probe(struct ofono_history_context *context) { ofono_debug("Example History Probe for modem: %p", context->modem); return 0; } static void example_history_remove(struct ofono_history_context *context) { ofono_debug("Example History Remove for modem: %p", context->modem); } static void example_history_call_ended(struct ofono_history_context *context, const struct ofono_call *call, time_t start, time_t end) { const char *from = "Unknown"; char buf[128]; ofono_debug("Call Ended on modem: %p", context->modem); if (call->type != 0) return; ofono_debug("Voice Call, %s", call->direction ? "Incoming" : "Outgoing"); if (call->clip_validity == 0) from = phone_number_to_string(&call->phone_number); if (call->direction == 0) ofono_debug("To: %s", from); else ofono_debug("From: %s", from); if (call->cnap_validity == 0) ofono_debug("Name from Network: %s\n", call->name); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&start)); buf[127] = '\0'; ofono_debug("StartTime: %s", buf); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&end)); buf[127] = '\0'; ofono_debug("EndTime: %s", buf); } static void example_history_call_missed(struct ofono_history_context *context, const struct ofono_call *call, time_t when) { const char *from = "Unknown"; char buf[128]; ofono_debug("Call Missed on modem: %p", context->modem); if (call->type != 0) return; ofono_debug("Voice Call, %s", call->direction ? "Incoming" : "Outgoing"); if (call->clip_validity == 0) from = phone_number_to_string(&call->phone_number); ofono_debug("From: %s", from); if (call->cnap_validity == 0) ofono_debug("Name from Network: %s\n", call->name); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&when)); buf[127] = '\0'; ofono_debug("When: %s", buf); } static void example_history_sms_received(struct ofono_history_context *context, const struct ofono_uuid *uuid, const char *from, const struct tm *remote, const struct tm *local, const char *text) { char buf[128]; ofono_debug("Incoming SMS on modem: %p", context->modem); ofono_debug("InternalMessageId: %s", ofono_uuid_to_str(uuid)); ofono_debug("From: %s", from); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", local); buf[127] = '\0'; ofono_debug("Local Sent Time: %s", buf); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", remote); buf[127] = '\0'; ofono_debug("Remote Sent Time: %s", buf); ofono_debug("Text: %s", text); } static void example_history_sms_send_pending(struct ofono_history_context *context, const struct ofono_uuid *uuid, const char *to, time_t when, const char *text) { char buf[128]; ofono_debug("Sending SMS on modem: %p", context->modem); ofono_debug("InternalMessageId: %s", ofono_uuid_to_str(uuid)); ofono_debug("To: %s:", to); strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&when)); buf[127] = '\0'; ofono_debug("Local Time: %s", buf); ofono_debug("Text: %s", text); } static void example_history_sms_send_status( struct ofono_history_context *context, const struct ofono_uuid *uuid, time_t when, enum ofono_history_sms_status s) { char buf[128]; strftime(buf, 127, "%Y-%m-%dT%H:%M:%S%z", localtime(&when)); buf[127] = '\0'; switch (s) { case OFONO_HISTORY_SMS_STATUS_PENDING: break; case OFONO_HISTORY_SMS_STATUS_SUBMITTED: ofono_debug("SMS %s submitted successfully", ofono_uuid_to_str(uuid)); ofono_debug("Submission Time: %s", buf); break; case OFONO_HISTORY_SMS_STATUS_SUBMIT_FAILED: ofono_debug("Sending SMS %s failed", ofono_uuid_to_str(uuid)); ofono_debug("Failure Time: %s", buf); break; case OFONO_HISTORY_SMS_STATUS_SUBMIT_CANCELLED: ofono_debug("Submission of SMS %s was canceled", ofono_uuid_to_str(uuid)); ofono_debug("Cancel time: %s", buf); break; case OFONO_HISTORY_SMS_STATUS_DELIVERED: ofono_debug("SMS delivered, msg_id: %s, time: %s", ofono_uuid_to_str(uuid), buf); break; case OFONO_HISTORY_SMS_STATUS_DELIVER_FAILED: ofono_debug("SMS undeliverable, msg_id: %s, time: %s", ofono_uuid_to_str(uuid), buf); break; default: break; } } static struct ofono_history_driver example_driver = { .name = "Example Call History", .probe = example_history_probe, .remove = example_history_remove, .call_ended = example_history_call_ended, .call_missed = example_history_call_missed, .sms_received = example_history_sms_received, .sms_send_pending = example_history_sms_send_pending, .sms_send_status = example_history_sms_send_status, }; static int example_history_init(void) { return ofono_history_driver_register(&example_driver); } static void example_history_exit(void) { ofono_history_driver_unregister(&example_driver); } OFONO_PLUGIN_DEFINE(example_history, "Example Call History Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, example_history_init, example_history_exit) ofono-1.17.bzr6912+16.04.20160314.3/examples/nettime.c0000644000015600001650000000454612671500024022037 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "common.h" static int example_nettime_probe(struct ofono_nettime_context *context) { ofono_debug("Example Network Time Probe for modem: %p", context->modem); return 0; } static void example_nettime_remove(struct ofono_nettime_context *context) { ofono_debug("Example Network Time Remove for modem: %p", context->modem); } static void example_nettime_info_received(struct ofono_nettime_context *context, struct ofono_network_time *info) { if (info == NULL) return; ofono_debug("Received a network time notification on modem: %p", context->modem); ofono_debug("Time: %04d-%02d-%02d %02d:%02d:%02d%c%02d:%02d (DST=%d)", info->year, info->mon, info->mday, info->hour, info->min, info->sec, info->utcoff > 0 ? '+' : '-', info->utcoff / 3600, (info->utcoff % 3600) / 60, info->dst); } static struct ofono_nettime_driver example_driver = { .name = "Example Network Time", .probe = example_nettime_probe, .remove = example_nettime_remove, .info_received = example_nettime_info_received, }; static int example_nettime_init(void) { return ofono_nettime_driver_register(&example_driver); } static void example_nettime_exit(void) { ofono_nettime_driver_unregister(&example_driver); } OFONO_PLUGIN_DEFINE(example_nettime, "Example Network Time Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, example_nettime_init, example_nettime_exit) ofono-1.17.bzr6912+16.04.20160314.3/examples/emulator.c0000644000015600001650000001132412671500024022212 0ustar pbuserpbgroup00000000000000/* * * 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 #endif #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include "ofono.h" #define DUN_PORT 12346 #define HFP_PORT 12347 static unsigned int modemwatch_id; guint server_watch; static GList *modems; static gboolean on_socket_connected(GIOChannel *chan, GIOCondition cond, gpointer user) { struct sockaddr saddr; unsigned int len = sizeof(saddr); int fd; struct ofono_emulator *em; GList *last; struct ofono_modem *modem; if (cond != G_IO_IN) return FALSE; fd = accept(g_io_channel_unix_get_fd(chan), &saddr, &len); if (fd == -1) return FALSE; /* * Pick the last one to avoid creating a new list with one modem just * for the call to ofono_emulator_create() (in this case we only support * registering one modem in the emulator) */ last = g_list_last(modems); modem = last->data; DBG("Picked modem %p for emulator", modem); em = ofono_emulator_create(last, GPOINTER_TO_INT(user)); if (em == NULL) close(fd); else ofono_emulator_register(em, fd); return TRUE; } static gboolean create_tcp(short port, enum ofono_emulator_type type) { struct sockaddr_in addr; int sk; int reuseaddr = 1; GIOChannel *server; sk = socket(PF_INET, SOCK_STREAM, 0); if (sk < 0) return FALSE; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)); if (bind(sk, (struct sockaddr *) &addr, sizeof(struct sockaddr)) < 0) goto err; if (listen(sk, 1) < 0) goto err; server = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(server, TRUE); server_watch = g_io_add_watch_full(server, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, on_socket_connected, GINT_TO_POINTER(type), NULL); g_io_channel_unref(server); DBG("Created server_watch: %u", server_watch); return TRUE; err: close(sk); return FALSE; } static void powered_watch(struct ofono_modem *modem, gboolean powered, void *user) { if (powered == FALSE) { DBG("Removing modem %p from the list", modem); modems = g_list_remove(modems, modem); if (modems == NULL && server_watch > 0) { DBG("Removing server watch: %u", server_watch); g_source_remove(server_watch); server_watch = 0; } } else { DBG("Adding modem %p to the list", modem); modems = g_list_append(modems, modem); if (modems->next == NULL) { create_tcp(DUN_PORT, OFONO_EMULATOR_TYPE_DUN); create_tcp(HFP_PORT, OFONO_EMULATOR_TYPE_HFP); } } } static void modem_watch(struct ofono_modem *modem, gboolean added, void *user) { DBG("modem: %p, added: %d", modem, added); if (added == FALSE) { DBG("Removing modem %p from the list", modem); modems = g_list_remove(modems, modem); return; } if (ofono_modem_get_powered(modem) == TRUE) { DBG("Adding modem %p to the list", modem); modems = g_list_append(modems, modem); if (modems->next == NULL) { create_tcp(DUN_PORT, OFONO_EMULATOR_TYPE_DUN); create_tcp(HFP_PORT, OFONO_EMULATOR_TYPE_HFP); } } __ofono_modem_add_powered_watch(modem, powered_watch, NULL, NULL); } static void call_modemwatch(struct ofono_modem *modem, void *user) { modem_watch(modem, TRUE, user); } static int example_emulator_init(void) { DBG(""); modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL); __ofono_modem_foreach(call_modemwatch, NULL); return 0; } static void example_emulator_exit(void) { DBG(""); __ofono_modemwatch_remove(modemwatch_id); g_list_free(modems); if (server_watch) g_source_remove(server_watch); } OFONO_PLUGIN_DEFINE(example_emulator, "Example AT Modem Emulator Plugin", VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT, example_emulator_init, example_emulator_exit) ofono-1.17.bzr6912+16.04.20160314.3/examples/private-network.c0000644000015600001650000000603312671500024023524 0ustar pbuserpbgroup00000000000000/* * * oFono - Open Source Telephony * * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). * * 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 #endif #include #include #include #include #include #include #include #include #include #define OFONO_API_SUBJECT_TO_CHANGE #include #include #include #include #include #define SERVER_ADDRESS "192.168.1.1" #define DNS_SERVER_1 "10.10.10.10" #define DNS_SERVER_2 "10.10.10.11" #define PEER_ADDRESS_PREFIX "192.168.1." static int next_peer = 2; struct req_data { ofono_private_network_cb_t cb; void *userdata; }; static gboolean request_cb(gpointer data) { struct req_data *rd = data; struct ofono_private_network_settings pns; struct ifreq ifr; int fd, err; char ip[16]; fd = open("/dev/net/tun", O_RDWR); if (fd < 0) goto error; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; strcpy(ifr.ifr_name, "ppp%d"); err = ioctl(fd, TUNSETIFF, (void *) &ifr); if (err < 0) goto error; sprintf(ip, "%s%d", PEER_ADDRESS_PREFIX, next_peer++); pns.fd = fd; pns.server_ip = SERVER_ADDRESS; pns.peer_ip = ip; pns.primary_dns = DNS_SERVER_1; pns.secondary_dns = DNS_SERVER_2; rd->cb(&pns, rd->userdata); return FALSE; error: if (fd >= 0) close(fd); rd->cb(NULL, rd->userdata); return FALSE; } static int example_request(ofono_private_network_cb_t cb, void *data) { struct req_data *rd = g_new0(struct req_data, 1); DBG(""); rd->cb = cb; rd->userdata = data; return g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 2, request_cb, rd, (GDestroyNotify) g_free); } static void example_release(int id) { DBG(""); g_source_remove(id); } static struct ofono_private_network_driver example_driver = { .name = "Example Private Network Driver", .request = example_request, .release = example_release, }; static int example_private_network_init(void) { return ofono_private_network_driver_register(&example_driver); } static void example_private_network_exit(void) { ofono_private_network_driver_unregister(&example_driver); } OFONO_PLUGIN_DEFINE(example_private_network, "Example Private Network Plugin", VERSION, OFONO_PLUGIN_PRIORITY_LOW, example_private_network_init, example_private_network_exit) ofono-1.17.bzr6912+16.04.20160314.3/ChangeLog0000644000015600001650000006672212671500024020166 0ustar pbuserpbgroup00000000000000ver 1.17: Fix issue with alphanumeric TP-OA handling. Fix issue with push notification origin port. Fix issue with reading of EF_MWIS records. Fix issue with handling AT+CPINR results. Fix issue with SIM state polling for Sierra modems. Fix issue with HFP handling and AT command prefixes. Fix issue with HFP and extra CCWA event handling. Fix issue with HFP call state and +CHUP errors. ver 1.16: Fix issue with PIN retry handling. Fix issue with HFP and multiple calls. Add support for Distracted Driving Reduction. Add support for available technologies property. Add support for Telit location reporting driver. Add support for u-blox SARA-U270 modems. Add support for Quectel UC15 modems. ver 1.15: Fix issue with EF_PNN access affecting PLMN display. Fix issue with SIM detection and Telit HE910 modems. Fix issue with Mobile Provider Database provisioning. Fix issue with bit-shifting and ID mapping allocations. Fix issue with Handsfree and unsolicited notifications. Fix issue with Handsfree and three way calling feature. Add support for Handsfree subscriber number feature. Add support for Handsfree multiple DTMF characters. Add support for PAP authentication. ver 1.14: Add support for Apple Siri specific Handsfree commands. Add support for provisioning of MMSC and Message Proxy. Add support for Telit HE910 modems. ver 1.13: Fix issue with parsing SS control strings. Fix issue with error reporting and Sierra modems. Fix issue with GPRS activation and SIM900 modems. Fix issue with serial receiver and SIM900 modems. Fix issue with signal strength and SIM900 modems. Fix issue with AT+CNMI handling and SIM900 modems. Fix issue with broken +CMER behavior and MBM modems. Fix issue with handling +CIEV and release and swap. Add support for Handsfree profile 1.6 functionality. Add support for Handsfree audio interface. ver 1.12: Fix issue with alpha ID and self explanatory icons. Fix issue with SIM Refresh handling and resetting state. Fix issue with SMS initiated by STK proactive command. Fix issue with CBS treating carriage return as padding. Fix issue with USSD terminated by network notification. Add support for battery charge level with Handsfree devices. Add support for technology and band changes with IFX modems. Add support for SIM file handling with Qualcomm QMI modems. Add support for SIM file system 2G and 3G path handling. Add support for SIM Toolkit end-to-end testing. ver 1.11: Fix issue with Bluetooth disconnect handling. Fix issue with handling EFspn with filler characters. Fix issue with processing multiple *EMRDY notifications. Fix issue with wrong data bearer property signal. Add support for data bearer reporting and Telit modems. Add support for SIM status notification and Telit modems. Add support for PIN retry counter status and Telit modems. Add support for long phone number format and SIM Toolkit. Add support for RequestQuickDigit to SIM Toolkit agent. ver 1.10: Update multiple descriptions of the API documentation. Add support for ReleaseAndSwap call handling. ver 1.9: Fix issue with missing CSSI and CSSU support for IFX modems. Fix issue with GPRS shutdown handling when network is lost. Fix issue with GSM to UTF-8 conversion mechanism. ver 1.8: Fix issue with STK sync return from envelope callback. Fix issue with missing NULL pointer check in GAtServer. Add support for extended USSD error reporting. Add support for obtaining IMSI via EF reading. Add support for Qualcomm QMI protocol handling. Add support for Qualcomm GOBI based devices. ver 1.7: Add support for Bluetooth DUN daemon (dundee). Add support for Wavecom Q2403/Q2686 modems. Add support for Nvidia Icera modems. ver 1.6: Fix issue with elementary files updates. Fix issue with emulator ringing notification. Fix issue with GTA04 modem and CLCC behavior. Fix issue with Huawei modem voice call timing. Fix issue with ZTE modem and SIM busy condition. Add support for radio settings of ZTE modem. Add support for USSD and voice calls of SIM900 modem. Add support for online/offline handling of SpeedUp modem. Add support for Sierra Wireless DirectIP modem. ver 1.5: Fix issue with USSD handling and Infineon modem. Fix issue with signal strength reporting and Infineon modem. Add support for Assisted Satellite Navigation and Infineon modem. Add support for IPv6/dual contexts and Infineon modem. Add support for SIM retry counters and SIM Com modem. Add support for SMS handling and SIM Com modem. ver 1.4: Fix issue with new SPN watch semantics. Fix issue with handling malformed emergency numbers. Fix issue with missing XSIMSTATE query for Infineon. Add support for new Infineon voice settings handling. ver 1.3: Add support for CDMA PIN management. Add support for CDMA provider name and SID. Add support for Huawei USSD 8-bit PDU mode. Add support for SIMCom SIM900 modems. ver 1.2: Fix issue with PIN type string for network PUK. Fix issue with voice dialing and Qualcomm MSM modems. Fix issue with Option HSO and SIM card detection. Add support for Option HSO voice call handling. Add support for Huawei modem capabilities check. Add support for Huawei unified GSM/UMTS and CDMA driver. ver 1.1: Fix issue with Telit modem and signal strength indication. Fix issue with Bluetooth and outstanding Connect/Disconnect. Fix issue with Handsfree support and hanging up all calls. Add support for more advanced Handsfree features. Add support for exposing Bluetooth address information. Add support for Mobile Provider Database provisioning. Add support for CPHS SPN and short-SPN identifiers. Add support for CDMA signal strength notification. Add support for CDMA dormant notification. Add support for CDMA network registration. Add support for CDMA call waiting feature. Add support for PPP IPv6 Control Protocol. ver 1.0: Fix issue with phonebook driver and SIM busy. Add support for SIM Access Profile client. Add support for 27.007 SIM Toolkit commands. Add support for Huawei CDMA data modems. Add support for Huawei GPRS bearer notifications. Add support for Huawei technology reporting. Add support for ZTE network time reports. ver 0.53: Add support for disabling data carrier detect. Add support for username/password settings for CDMA. Add support for Huawei network time reports. Add support for Huawei CDMA modems. Add support for SpeedUp CDMA modems. Add support for ZTE MF631 and MF688 modems. ver 0.52: Add support for SIM Toolkit user confirmation handling. Add support for ZTE MF180, MF190, MF637 and MF668 modems. Add support for Huawei E173 modems. Add support for various SpeedUp modems. ver 0.51: Fix issue with alignment and STK event lists. Fix issue with alignment and STK channel data length. Fix issue with Linktop device handling functionality. Fix issue with detection of HP HS2330 devices. Add support for UICC SIM driver for ISI modems. Add support for ACFC and PFC options for PPP. ver 0.50: Fix issue with STK respond on exit flag handling. Fix issue with STK and canceling pending DTMF tones. Fix issue with IPv4 gateway setting and Ericsson modems. Add support for handling IFX emergency number list. Add support for Telit UC864-G devices. ver 0.49: Fix issue with missing signal on context removal. Fix issue with missing cleanup for GPRS interfaces. Fix issue with online setting when not powered. Fix issue with memory leak in GAtChat notifiers. Fix issue with PPP Protocol-Reject packet handling. Add support for PPP escape sequence handling. Add support for initial SMS handling for CDMA. ver 0.48: Fix issue with crash due to not stopped PPP timers. Fix issue with offline mode handling and Huawei modem. Fix issue with missing check for Huawei modem device open. Fix issue with USSD and use of non-cloned GAtChat object. ver 0.47: Fix issue with entering offline mode prematurely. Add support for CPHS CSP network registration handling. ver 0.46: Fix issue with operator name reading and older ISI modems. Fix issue with networking registration and older ISI modems. Fix issue with missing handling of PIN/SIM states and ISI modems. Fix issue with voice call state reporting and ISI modems. Fix issue with STK handling of environment variables. Fix issue with STK and empty URL for launch browser. Fix issue with voice call pause character validation. Fix issue with buffer length and long phone numbers. Fix issue with SMS sending retries and network timeout. Fix issue with missing SMS submit canceled history status. Add support for cancellation of SMS submission. Add support for handling SIM Toolkit display action commands. Add support for handling call forwarding and SIM refresh. Add support for handling EFimg and EFiidf changes. Add support for handling EFmsisdn and EFsdn changes. Add support for handling emergency calls without SIM. Add support for handling emergency calls without PIN. Add support for handling emergency number updates. Add support for assisted satellite navigation interface. Add support for IPv6 contexts and ISI modems. Add support for dual-stack GPRS contexts. Add limited support for CDMA connection manager interface. ver 0.45: Fix issue with SIM Toolkit null data object. Fix issue with SIM filesystem and modem release. Fix issue with disconnect handling and Huawei modems. Add support for improved SSN and voicecall handling. Add support for SIM Toolkit Refresh handled by the modem. Add support for multiple AT channels and STE modems. Add support for ISI drivers and wgmodem2.5 handling. Add support for optimized ringbuffer operations. Add support for optimized PPP buffer management. ver 0.44: Fix issue with presence detection of Bluetooth daemon. Fix issue with HDLC processing and PPP server. Fix issue with SIM state and PIN2/PUK2 handling. Fix issue with potential SIM lockout condition. Add support for basic handling of SIM Toolkit Refresh. Add support for location reporting interface. Add support for GPS engine and MBM modems. Add support for CNAP handling and ISI modems. Add support for multiple contexts and STE modems. Add support for ST-Ericsson U8500 modem. ver 0.43: Fix issue with PPP transmit ACCM and receive ACCM mixup. Fix issue with PPP not using default ACCM in transmit. Fix issue with PPP interface and EM770W modem. Add support for basic modem emulator interfaces. Add support for handling ATS5 command feature. Add support for Linktop LW27x data cards. ver 0.42: Fix issue with ECT pre-conditions check. Add support for watching SIM file changes. Add support for using SIM codes longer than 8 digits. Add support for SPN handling with GPRS provisioning. Add support for better handling COLP with IFX modem. Add support for CNAP handling with IFX modem. Remove support for +CSSI type SS notifications. ver 0.41: Fix issue with SIM callback handling. Fix issue with XTMS handling and IFX modem. Add support for alphabets and SMS encoding. Add support for generic PIN retries handling. Add support for PIN retries and MBM modem. Add support for radio settings and MBM modem. Add support for cell broadcast and STE modem. Add support for handling ECAV status Released. ver 0.40: Fix issue with MessageCenter and MessageProxy settings. Fix issue with voice call support and Calypso modem. Fix issue with user busy release and ISI modem. Fix issue with DTMF sending and ISI modem. Add support for handling long phone numbers. Add support for persisting outgoing messages. Add support for GPRS provision infrastructure. Add support for proper GPRS handling in offline mode. Add support for handling Launch Browser proactive command. Remove support for deprecated deregister method. ver 0.39: Fix issue with not handling empty EFecc properly. Fix issue with string length and DTMF handling. Fix issue with missing info for terminal busy result. Fix issue with signal strength handling and IFX modem. Fix handling of SIM Toolkit enabling and IFX modem. Add support for packet switched bearer notifications. Add support for handling called line identification. Add support for PIN retry counter interface. Add support for ST-Ericsson modem init daemon. Add support for Cinterion TC65 modem. Add support for simple ISI client interface. ver 0.38: Change CalledLine* to ConnectedLine* properties. Fix issue with calling presentation property. Fix issue with network time and ISI modems. Fix issue with timezone reporting and HSO modems. Fix issue with SIM ready status and HSO modems. Fix issue with hidden caller ID and STE modems. Fix issue with handling of STK Setup Menu. Fix issue with missing STK text and icon checks. Fix issue with missing signal strength query. ver 0.37: Fix issue with parsing of un-quoted CREG / CGREG. Fix issue with call forwarding for data and fax. Fix issue with too short timeout for DisplayText. Fix issue with handling zero length text strings. Fix issue with decoding of optional SMS elements. Fix issue with charset and MWI DCS decoding. Fix issue with WAP push notification handling. Fix issue with calling handling and ISI modem. Fix issue with network interfaces and STE modem. Fix issue with SIM state notification of Huawei modem. Add support for radio settings handling and Huawei modem. Add support for provide local info proactive command. Add support for calling name presentation properties. Add support for modem lockdown handling and property. Add support for handling silent modem reset trigger. Add support for frequency band selection interface. Add support for text telephony interface. ver 0.36: Fix issue with CLIR Invocation and Suppression. Fix issue with power/online transition with ZTE devices. Fix segmentation fault when removing Nokia Datacard. Add support for Nokia CS-17 dongles. Add support for Ericsson F5521gw devices. Add support for CAIF network interface management. Add support for COLR in generic AT modem driver. Add support for SMS Point-to-Point download to UICC. Add support for checking specific service availability. Add support for handling null text field for STK. ver 0.35: Fix issue with FDN and BDN enabled checks. Fix issue with capabilities and Phonet support. Fix issue with timeout for ISI network deregistration. Add support for Push Notification interface. Add support for Smart Messaging interface. Remove generic AT command modem plugin. ver 0.34: Fix issue with sim_fs_op_error handling. Fix issue with not handling GPRS context driver failures. Add support for multiple GPRS context activations. Add support for deactivating all GPRS contexts. Add support for configuring MMS context settings. Add support for barred dialing indication property. Add support for fast dormancy settings property. Add support for handling Play Tone proactive command. Add support for indicating handled STK proactive commands. Add support for two active GPRS contexts with MBM modems. Add support for time zone reporting with Ericsson MBM modems. Add support for detecting IFX modems stuck in multiplexer mode. Add support for IFX using up to three active GPRS contexts. Add support for IFX device shutdown when DLC disconnects. Add support for Phonesim specific configuration files. Remove deprecated modem.conf support. ver 0.33: Fix wrong string to enum mapping of radio settings. Fix issue with MMI code to bearer class mappings. Fix issue with setting correct phase from EFphase. Fix issue with phonebook handling and Infineon modems. Fix issue with STK session end handling and Infineon modems. Fix issue with SMS handling and ISI modems. Fix issue with setting SCA type and ISI modems. Add support for FastDormancy property. Add support for FixedDialing property to indicate FDN. Add support for Infineon specific M-RAW_IP GPRS context. Add support for handling Send DTMF proactive command. Add support for handling SIM Toolkit text attributes. ver 0.32: Fix issue with AT+VTS not using quotes. Fix issue with entering PUK and Infineon modems. Fix issue with SIM hotswap and Infineon modems. Fix issue with hangup active and ISI modems. Fix issue with logic to validate USSD strings. Add support for call in progress logic to USSD handling. Add support for detecting FDN enabled SIM cards. Add support for accessing SIM icon storage. ver 0.31: Fix issue with signal strength reporting for ISI modems. Fix issue with GPRS detach reporting for ISI modems. Fix issue with single voice call termination handling. Fix issue with Huawei modem driver and release of voice calls. Fix issue with Infineon modem driver not sending AT+CHUP. Fix issue with Infineon SIM ready checking and newer firmware. Add support for Infineon specific model detection handling. Add support for Infineon specific audio configuration. Add support for audio settings interface. Add support for generic ISI modem driver. Add support for N900 specific ISI modem driver. ver 0.30: Fix issue with 8-bit port handling of SMS. Fix issue with CBS decoding and ISI modem driver. Fix issue with CBS topic settings and ISI modem driver. Fix issue with username and password order for Option HSO. Fix wrong power and reset paths of Calypso support. Add Infineon modem plugin support. Add support for Infineon specific voice call handling. Add support for Infineon specific SIM ready handling. Add support for Infineon signal strength reporting. Add support for Infineon CNMA without PDU. Add support for Infineon radio settings. Add support for Huawei specific voice call handling. Add Huawei audio utility for voice routing. ver 0.29: Fix issue with Huawei devices initial SIM state. Fix issue with Huawei devices and online support. Fix SIM Toolkit User Cancel response to Set Up Call. Add support for handling of Send USSD proactive command. Add support for Language Notification proactive command. Add support for UCS2 to GSM 7bit conversions. Add support for parsing CSCS queries. Add support for USSD encoding function. Add support for GPRS suspended notifications. Add support for messaging D-Bus interface. ver 0.28: Update modem manager D-Bus API. Add support for online feature for ZTE devices. Add support for online feature for Huawei devices. Add support for online feature for Novatel devices. Add support for online feature for Option HSO devices. Add support for online feature for Ericsson MBM devices. Add support for online feature for ST-Ericsson devices. Add support for using 8-bit SMS reference numbers by default. Fix wrong code point in Portuguese alphabet table. Fix issue with EFiidf reads larger than 256 bytes. ver 0.27: Update network registration D-Bus API. Update voice call manager D-Bus API. Update connection manager D-Bus API. Update message manager D-Bus API. Fix issue with GPRS attach/detach logic. Fix issue with GPRS context IP configuration and ISI modems. Fix issue with call forwarding and ISI modems. Fix issue with LockedPins in case SIM wants a PUK first. Fix issue with missing reset of MNC length on SIM removal. Fix issue with SIM state logic of Huawei devices. Fix issue with SIM Toolkit and GSMv1 parser for MBM devices. Add more features for SIM Toolkit agent support. Add SIM Toolkit support for Calypso modem. Add SIM Toolkit support for ST-Ericsson devices. Add support for radio settings of ST-Ericsson devices. Add support for hangup all voice calls functionality. Add support for reading EFust, EFest and EFimg. Add support for adding a default empty PDP context. Add support for embedded \r and \n in responses. Add support for cloning GAtChat instances. Add support for Nokia Datacard devices. Add support for ZTE based devices. Add support for creating backtraces. ver 0.26: Fix busy loop in PPP disconnect with Huawei modem. Add support for MCC/MNC via network registration interface. Add support for SIM Toolkit agent interface. Add initial support for IPv6 PDP context. ver 0.25: Fix issue with PPP IPCP and too short timeouts. Fix issue with Calypso modem and DTMF chars. Fix issue with detection of some Huawei devices. Fix issue with SIM polling and Ericsson MBM devices. Fix potential overflow with SMS and GSM extension chars. Add support for OFONO_ERROR_TYPE_SIM for negative SIM status. Add support for display text decoding. Add support for idle text proactive command. Add support for SMS proactive commands. ver 0.24: Fix race condition with GRPS attach operation. Fix some issues with Option based devices. Fix Huawei TTY hangup on context termination. Fix crash within HDLC handling. Fix incorrect packet length within PPP. Add support for PPP server side. Add support for decoding USSD PDUs. Add support for SMS status report assembly. Add support for SMS bearer settings. Add initial support for Bluetooth plugin. ver 0.23: Fix issue with operator info when not registered. Fix issue with clean PPP shutdown on device removal. Add support for status report notification via CDSI. Add better support for Huawei E160 and E176 devices. Add full GPRS support for Novatel based devices. Add support for Novatel specific radio settings. Add support for Option specific radio settings. Add support for setting CBS topics on Qualcomm devices. ver 0.22: Fix issue with VPATH builds. Fix issue with SMS and more than 31 fragments. Add even more SIM Toolkit parsing support. Add support for modem online property. ver 0.21: Add more parsing support for SIM Toolkit. Add support for SIM insertion/removal events. Add support for NITZ (network time) events. Add support for reading EF_ICCID information. Add support for advanced PPP integration. Add support for HDLC specific abstraction. Add support for simpler Technology values. Add support for separate MCC/MNC SIM properties. Add support for GPRS context with ISI modems. Add support for SMS handling with ISI modems. Add support for Wavecom WMP100 based devices. Add support for Option iCON 451 based devices. Add support for Huawei E1552 HSDPA USB devices. Add support for Dell 5530 based devices. ver 0.20: Fix issue with empty operator names. Add missing API documentation. Add support for Huawei EM770 modem. Add more detailed parsing support for SIM Toolkit. Add additional functionality for AT command server. Add initial PPP implementation. ver 0.19: Fix parsing of EFspdi for PLMN list. Fix issues with Bluetooth handsfree handling. Fix non-blocking handling for AT command server. Add support for network-initiated USSD requests. Add utility functions for SIM Toolkit support. ver 0.18: Fix handling of GPRS attach logic. Fix handling of username/password settings for STE/MBM modems. Add support for Bluetooth Handsfree handling. Add support for USSD_STATE_USER_ACTION. Add radio settings atom and driver API. Add framework for AT command server. ver 0.17: Add support for ST-Ericsson based modems. Add support for processing CBS even if no EFcbmid. Add support for devices with CSCB mode 0 only. Add support for Handsfree devices via BlueZ. Add CID and LAC quirk handling for Huawei modems. ver 0.16: Fix unregister of operators with null MCC/MNC. Fix CPHS mailbox usage for 3GPP SIM cards. Add support for persistent CBS Topics list. Add support for persistent primary context identifiers. Add support for SIM cache indexing by phase. Add netmask to HSO GPRS context driver. ver 0.15: Fix missing netmask value for newer MBM devices. Fix concatenation of datagram SMS messages. Add support for 51.011 EFecc format. Add support for Powered property to CbsManager. Add utility for checking if CBS topic is in range. ver 0.14: Fix some issues with modem shutdown behavior. Fix reset of context settings when deactivated. Fix signal strength handling for Calypso modem. Add proper signal strength handling for HSO modem. Add support for enabling HSO Speech Services. Add modem description for newer MBM devices. Add clip_timeout for HFP incoming call handling. Add poll_clcc for HFP multiparty calls. Add utility for testing GSM GPRS dialing. ver 0.13: Add better support for call id allocation. Add CLCC query when initializing modem. Add DTMF tone sending support for HFP modem. Add support for modem disabling on shutdown. ver 0.12: Fix various issues with Calypso modem driver. Fix order of CMER and CIND in SLC connection. Fix issue with SMS references stored as 8 bits. Add static IP configuration for newer MBM devices. Add context status polling for older MBM devices. Add username/password support for MBM devices. Add support for Huawei specific error terminator. Add support for weird naming of Dell 5530 devices. Add udev rules for Option GI0201 and GTM382 modems. ver 0.11: Fix issue with repeated CCWA notifications. Fix issue with double-swap when 3-way dialing. Add CLCC polling for better multiparty call support. Add GPRS context driver for Option HSO devices. Add support for GPRS interface configuration. ver 0.10: Fix issues with correct ATD handling. Fix issues with indication handling. Add support for SMS history capability. Add basic save/restore support for GPRS settings. Add three-way calling support to HFP voice driver. Add call volume support to HFP modem plugin. Add initial support for Palm Pre modems. ver 0.9: Fix issues with voice call dialing logic. Fix issues with USSD decoding support. Add initial GPRS support for MBM modems. Add mode property to network registration. Add support for advanced options in modem.conf file. Add voice call driver for Bluetooth Handsfree. ver 0.8: Fix crash when internal structures differ. Fix issues with handling empty text messages. Add driver model for TTY multiplexer support. Add support for multiplexer usage with Calypso modems. Add support for PhoNet/ISI call barring, forwarding and waiting. Add support for PhoNet/ISI voice call handling. ver 0.7: Fix handling of empty SMS text messages. Fix GAtChat's next_hexstring to handle optional quotes. Fix generic SIM driver to work correctly with 3G SIM cards. Add utility functions to parse 2G and 3G get response data. Add call volume interface to adjust speaker and mic volume. Add support for basic elementary file database. ver 0.6: Fix build issue with example history plugin. Fix segmentation fault from SIM reading on Calypso modem. Add more scripts for SMS and voice call testing. ver 0.5: Fix reading of left overs in ME storage on startup. Fix parsing of Enhanced Voicemail notifications. Add reading of various CBS related EFs. Add ability to expire PLMN wide messages. Add support for national language variants. Add support for PIN and PUK handling. Add support for TI Calypso modem. Add initial support for Novatel based devices. Add initial support for Huawei based devices. Add initial support for Option HSO based devices. Add initial support for TTY multiplexing. ver 0.4: Add atom framework and update all drivers. Add support for modem driver framework. Add support for static modem configuration. Add support for specialized phone simulator driver. Add support for HTC G1 modem devices. Add support for Ericsson MBM devices. Add support for AT command PDU listing. Add support for low-level PhoNet/ISI pipe endpoints. Add support for full non-recursive build. ver 0.3: Fix support for phonebook reading. Fix some issues with network registration. Fix some issues with MSISDN handling. Fix some issues with SIM storage support. Add caching for EF-PNN and EF-OPL SIM files. Add support for SIM ADN type number handling. Add support for tracking message waiting indications. Add support for user-provided AT parsers. Add initial drafts of API documentation. ver 0.2: Add more detailed handling for network names. Add character set support for phonebook. Add SIM file reading and writing utilities. Add experimental support for MT incoming SMS store. Add special support for ti_calypso based devices. ver 0.1: Initial public release.