aboutsummaryrefslogtreecommitdiffstats
path: root/telephony
diff options
context:
space:
mode:
authorJaime Lopez <jaimel@codeaurora.org>2010-07-21 18:03:58 -0700
committerJean-Baptiste Queru <jbq@google.com>2010-10-08 15:41:44 -0700
commit1a000857bb7eef298efa787699050884f92311fe (patch)
tree5151062b946b7970eadfeae38e7a837ffa9021f8 /telephony
parent7bf9d7ffaee95963a1f6535494eedfc09097271b (diff)
downloadexternal_qemu-1a000857bb7eef298efa787699050884f92311fe.zip
external_qemu-1a000857bb7eef298efa787699050884f92311fe.tar.gz
external_qemu-1a000857bb7eef298efa787699050884f92311fe.tar.bz2
qemu: Multimode support
Preliminary implementation of a Multimode modem - Added the +CTEC AT command which allows querying and setting the current technology - Added preliminary NVRAM file support Add cdma subscription source support - Add support to save the subscription source preference to nv. - Add command 'cdma ssource' to switch subscription source from the emulator console Implement AT+WRMP command Implement the AT+WRMP command to set and get the roaming preference Implement Emergency Callback Mode Implement the +WSOS command and unsol to notify Emergency Callback Mode status Implement +WPRL AT Command Enable PRL update notification by implementing the AT+WPRL query command Implement a console command to send +WPRL unsolicited response Change-Id: I5c036c1b0832b94c0b7675931f6a18b5d9ca7436
Diffstat (limited to 'telephony')
-rw-r--r--telephony/android_modem.c564
-rw-r--r--telephony/android_modem.h42
2 files changed, 578 insertions, 28 deletions
diff --git a/telephony/android_modem.c b/telephony/android_modem.c
index 2f4c98e..ddfef94 100644
--- a/telephony/android_modem.c
+++ b/telephony/android_modem.c
@@ -11,9 +11,12 @@
*/
#include "android/android.h"
#include "android_modem.h"
+#include "android/config.h"
#include "android/utils/debug.h"
#include "android/utils/timezone.h"
#include "android/utils/system.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/path.h"
#include "sim_card.h"
#include "sysdeps.h"
#include <memory.h>
@@ -74,6 +77,10 @@
#define OPERATOR_ROAMING_MCCMNC STRINGIFY(OPERATOR_ROAMING_MCC) \
STRINGIFY(OPERATOR_ROAMING_MNC)
+static const char* _amodem_switch_technology(AModem modem, AModemTech newtech, int32_t newpreferred);
+static int _amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ss);
+static int _amodem_set_cdma_prl_version( AModem modem, int prlVersion);
+
#if DEBUG
static const char* quote( const char* line )
{
@@ -108,25 +115,49 @@ static const char* quote( const char* line )
}
#endif
-extern AGprsNetworkType
+extern AModemTech
+android_parse_modem_tech( const char * tech )
+{
+ const struct { const char* name; AModemTech tech; } techs[] = {
+ { "gsm", A_TECH_GSM },
+ { "wcdma", A_TECH_WCDMA },
+ { "cdma", A_TECH_CDMA },
+ { "evdo", A_TECH_EVDO },
+ { "lte", A_TECH_LTE },
+ { NULL, 0 }
+ };
+ int nn;
+
+ for (nn = 0; techs[nn].name; nn++) {
+ if (!strcmp(tech, techs[nn].name))
+ return techs[nn].tech;
+ }
+ /* not found */
+ return A_TECH_UNKNOWN;
+}
+
+extern ADataNetworkType
android_parse_network_type( const char* speed )
{
- const struct { const char* name; AGprsNetworkType type; } types[] = {
- { "gprs", A_GPRS_NETWORK_GPRS },
- { "edge", A_GPRS_NETWORK_EDGE },
- { "umts", A_GPRS_NETWORK_UMTS },
- { "hsdpa", A_GPRS_NETWORK_UMTS }, /* not handled yet by Android GSM framework */
- { "full", A_GPRS_NETWORK_UMTS },
- { NULL, 0 }
+ const struct { const char* name; ADataNetworkType type; } types[] = {
+ { "gprs", A_DATA_NETWORK_GPRS },
+ { "edge", A_DATA_NETWORK_EDGE },
+ { "umts", A_DATA_NETWORK_UMTS },
+ { "hsdpa", A_DATA_NETWORK_UMTS }, /* not handled yet by Android GSM framework */
+ { "full", A_DATA_NETWORK_UMTS },
+ { "lte", A_DATA_NETWORK_LTE },
+ { "cdma", A_DATA_NETWORK_CDMA1X },
+ { "evdo", A_DATA_NETWORK_EVDO },
+ { NULL, 0 }
};
int nn;
for (nn = 0; types[nn].name; nn++) {
- if ( !strcmp(speed, types[nn].name) )
+ if (!strcmp(speed, types[nn].name))
return types[nn].type;
}
/* not found, be conservative */
- return A_GPRS_NETWORK_GPRS;
+ return A_DATA_NETWORK_GPRS;
}
/* 'mode' for +CREG/+CGREG commands */
@@ -185,9 +216,12 @@ typedef struct {
/* the spec says that there can only be a max of 4 contexts */
#define MAX_DATA_CONTEXTS 4
#define MAX_CALLS 4
+#define MAX_EMERGENCY_NUMBERS 16
+
#define A_MODEM_SELF_SIZE 3
+
typedef struct AModemRec_
{
/* Legacy support */
@@ -210,7 +244,7 @@ typedef struct AModemRec_
ARegistrationState voice_state;
ARegistrationUnsolMode data_mode;
ARegistrationState data_state;
- AGprsNetworkType data_network;
+ ADataNetworkType data_network;
/* operator names */
AOperatorSelection oper_selection_mode;
@@ -235,6 +269,28 @@ typedef struct AModemRec_
int out_size;
char out_buff[1024];
+ /*
+ * Hold non-volatile ram configuration for modem
+ */
+ AConfig *nvram_config;
+ char *nvram_config_filename;
+
+ AModemTech technology;
+ /*
+ * This is are really 4 byte-sized prioritized masks.
+ * Byte order gives the priority for the specific bitmask.
+ * Each bit position in each of the masks is indexed by the different
+ * A_TECH_XXXX values.
+ * e.g. 0x01 means only GSM is set (bit index 0), whereas 0x0f
+ * means that GSM,WCDMA,CDMA and EVDO are set
+ */
+ int32_t preferred_mask;
+ ACdmaSubscriptionSource subscription_source;
+ ACdmaRoamingPref roaming_pref;
+ int in_emergency_mode;
+ int prl_version;
+
+ const char *emergency_numbers[MAX_EMERGENCY_NUMBERS];
} AModemRec;
@@ -311,16 +367,115 @@ amodem_end_line( AModem modem )
return modem->out_buff;
}
+#define NV_OPER_NAME_INDEX "oper_name_index"
+#define NV_OPER_INDEX "oper_index"
+#define NV_SELECTION_MODE "selection_mode"
+#define NV_OPER_COUNT "oper_count"
+#define NV_MODEM_TECHNOLOGY "modem_technology"
+#define NV_PREFERRED_MODE "preferred_mode"
+#define NV_CDMA_SUBSCRIPTION_SOURCE "cdma_subscription_source"
+#define NV_CDMA_ROAMING_PREF "cdma_roaming_pref"
+#define NV_IN_ECBM "in_ecbm"
+#define NV_EMERGENCY_NUMBER_FMT "emergency_number_%d"
+#define NV_PRL_VERSION "prl_version"
+#define NV_SREGISTER "sregister"
+
+#define MAX_KEY_NAME 40
+
+static AConfig *
+amodem_load_nvram( AModem modem )
+{
+ AConfig* root = aconfig_node(NULL, NULL);
+ D("Using config file: %s\n", modem->nvram_config_filename);
+ if (aconfig_load_file(root, modem->nvram_config_filename)) {
+ D("Unable to load config\n");
+ aconfig_set(root, NV_MODEM_TECHNOLOGY, "gsm");
+ aconfig_save_file(root, modem->nvram_config_filename);
+ }
+ return root;
+}
+
+static int
+amodem_nvram_get_int( AModem modem, const char *nvname, int defval)
+{
+ int value;
+ char strval[MAX_KEY_NAME + 1];
+ char *newvalue;
+
+ value = aconfig_int(modem->nvram_config, nvname, defval);
+ snprintf(strval, MAX_KEY_NAME, "%d", value);
+ D("Setting value of %s to %d (%s)",nvname, value, strval);
+ newvalue = strdup(strval);
+ if (!newvalue) {
+ newvalue = "";
+ }
+ aconfig_set(modem->nvram_config, nvname, newvalue);
+
+ return value;
+}
+
+const char *
+amodem_nvram_get_str( AModem modem, const char *nvname, const char *defval)
+{
+ const char *value;
+
+ value = aconfig_str(modem->nvram_config, nvname, defval);
+
+ if (!value) {
+ if (!defval)
+ return NULL;
+ value = defval;
+ }
+
+ aconfig_set(modem->nvram_config, nvname, value);
+
+ return value;
+}
+
+static ACdmaSubscriptionSource _amodem_get_cdma_subscription_source( AModem modem )
+{
+ int iss = -1;
+ iss = amodem_nvram_get_int( modem, NV_CDMA_SUBSCRIPTION_SOURCE, A_SUBSCRIPTION_RUIM );
+ if (iss >= A_SUBSCRIPTION_UNKNOWN || iss < 0) {
+ iss = A_SUBSCRIPTION_RUIM;
+ }
+
+ return iss;
+}
+
+static ACdmaRoamingPref _amodem_get_cdma_roaming_preference( AModem modem )
+{
+ int rp = -1;
+ rp = amodem_nvram_get_int( modem, NV_CDMA_ROAMING_PREF, A_ROAMING_PREF_ANY );
+ if (rp >= A_ROAMING_PREF_UNKNOWN || rp < 0) {
+ rp = A_ROAMING_PREF_ANY;
+ }
+
+ return rp;
+}
+
static void
amodem_reset( AModem modem )
{
+ const char *tmp;
+ int i;
+ modem->nvram_config = amodem_load_nvram(modem);
modem->radio_state = A_RADIO_STATE_OFF;
modem->wait_sms = 0;
- modem->oper_name_index = 2;
- modem->oper_selection_mode = A_SELECTION_AUTOMATIC;
- modem->oper_index = 0;
- modem->oper_count = 2;
+ modem->oper_name_index = amodem_nvram_get_int(modem, NV_OPER_NAME_INDEX, 2);
+ modem->oper_selection_mode = amodem_nvram_get_int(modem, NV_SELECTION_MODE, A_SELECTION_AUTOMATIC);
+ modem->oper_index = amodem_nvram_get_int(modem, NV_OPER_INDEX, 0);
+ modem->oper_count = amodem_nvram_get_int(modem, NV_OPER_COUNT, 2);
+ modem->in_emergency_mode = amodem_nvram_get_int(modem, NV_IN_ECBM, 0);
+ modem->prl_version = amodem_nvram_get_int(modem, NV_PRL_VERSION, 0);
+
+ modem->emergency_numbers[0] = "911";
+ char key_name[MAX_KEY_NAME + 1];
+ for (i = 1; i < MAX_EMERGENCY_NUMBERS; i++) {
+ snprintf(key_name,MAX_KEY_NAME, NV_EMERGENCY_NUMBER_FMT, i);
+ modem->emergency_numbers[i] = amodem_nvram_get_str(modem,key_name, NULL);
+ }
modem->area_code = -1;
modem->cell_id = -1;
@@ -341,7 +496,18 @@ amodem_reset( AModem modem )
modem->voice_state = A_REGISTRATION_HOME;
modem->data_mode = A_REGISTRATION_UNSOL_ENABLED_FULL;
modem->data_state = A_REGISTRATION_HOME;
- modem->data_network = A_GPRS_NETWORK_UMTS;
+ modem->data_network = A_DATA_NETWORK_UMTS;
+
+ tmp = amodem_nvram_get_str( modem, NV_MODEM_TECHNOLOGY, "gsm" );
+ modem->technology = android_parse_modem_tech( tmp );
+ if (modem->technology == A_TECH_UNKNOWN) {
+ modem->technology = aconfig_int( modem->nvram_config, NV_MODEM_TECHNOLOGY, A_TECH_GSM );
+ }
+ // Support GSM, WCDMA, CDMA, EvDo
+ modem->preferred_mask = amodem_nvram_get_int( modem, NV_PREFERRED_MODE, 0x0f );
+
+ modem->subscription_source = _amodem_get_cdma_subscription_source( modem );
+ modem->roaming_pref = _amodem_get_cdma_roaming_preference( modem );
}
static AModemRec _android_modem[1];
@@ -350,10 +516,17 @@ AModem
amodem_create( int base_port, AModemUnsolFunc unsol_func, void* unsol_opaque )
{
AModem modem = _android_modem;
+ char nvfname[MAX_PATH];
+ char *start = nvfname;
+ char *end = start + sizeof(nvfname);
+
+ modem->base_port = base_port;
+ start = bufprint_config_file( start, end, "modem-nv-ram-" );
+ start = bufprint( start, end, "%d", modem->base_port );
+ modem->nvram_config_filename = strdup( nvfname );
amodem_reset( modem );
modem->supportsNetworkDataType = 1;
- modem->base_port = base_port;
modem->unsol_func = unsol_func;
modem->unsol_opaque = unsol_opaque;
@@ -361,6 +534,7 @@ amodem_create( int base_port, AModemUnsolFunc unsol_func, void* unsol_opaque
sys_main_init();
+ aconfig_save_file( modem->nvram_config, modem->nvram_config_filename );
return modem;
}
@@ -472,11 +646,44 @@ amodem_set_data_registration( AModem modem, ARegistrationState state )
}
}
+static int
+amodem_nvram_set( AModem modem, const char *name, const char *value )
+{
+ aconfig_set(modem->nvram_config, name, value);
+ return 0;
+}
+static AModemTech
+tech_from_network_type( ADataNetworkType type )
+{
+ switch (type) {
+ case A_DATA_NETWORK_GPRS:
+ case A_DATA_NETWORK_EDGE:
+ case A_DATA_NETWORK_UMTS:
+ // TODO: Add A_TECH_WCDMA
+ return A_TECH_GSM;
+ case A_DATA_NETWORK_LTE:
+ return A_TECH_LTE;
+ case A_DATA_NETWORK_CDMA1X:
+ case A_DATA_NETWORK_EVDO:
+ return A_TECH_CDMA;
+ case A_DATA_NETWORK_UNKNOWN:
+ return A_TECH_UNKNOWN;
+ }
+ return A_TECH_UNKNOWN;
+}
+
void
-amodem_set_data_network_type( AModem modem, AGprsNetworkType type )
+amodem_set_data_network_type( AModem modem, ADataNetworkType type )
{
+ AModemTech modemTech;
modem->data_network = type;
amodem_set_data_registration( modem, modem->data_state );
+ modemTech = tech_from_network_type(type);
+ if (modem->unsol_func && modemTech != A_TECH_UNKNOWN) {
+ if (_amodem_switch_technology( modem, modemTech, modem->preferred_mask )) {
+ modem->unsol_func( modem->unsol_opaque, modem->out_buff );
+ }
+ }
}
int
@@ -734,6 +941,277 @@ unknownCommand( const char* cmd, AModem modem )
return "ERROR: unknown command\r";
}
+/*
+ * Tell whether the specified tech is valid for the preferred mask.
+ * @pmask: The preferred mask
+ * @tech: The AModemTech we try to validate
+ * return: If the specified technology is not set in any of the 4
+ * bitmasks, return 0.
+ * Otherwise, return a non-zero value.
+ */
+static int matchPreferredMask( int32_t pmask, AModemTech tech )
+{
+ int ret = 0;
+ int i;
+ for ( i=3; i >= 0 ; i-- ) {
+ if (pmask & (1 << (tech + i*8 ))) {
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+static AModemTech
+chooseTechFromMask( AModem modem, int32_t preferred )
+{
+ int i, j;
+
+ /* TODO: Current implementation will only return the highest priority,
+ * lowest numbered technology that is set in the mask.
+ * However the implementation could be changed to consider currently
+ * available networks set from the console (or somewhere else)
+ */
+ for ( i=3 ; i >= 0; i-- ) {
+ for ( j=0 ; j < A_TECH_UNKNOWN ; j++ ) {
+ if (preferred & (1 << (j + 8 * i)))
+ return (AModemTech) j;
+ }
+ }
+ assert("This should never happen" == 0);
+ // This should never happen. Just to please the compiler.
+ return A_TECH_UNKNOWN;
+}
+
+static const char*
+_amodem_switch_technology( AModem modem, AModemTech newtech, int32_t newpreferred )
+{
+ D("_amodem_switch_technology: oldtech: %d, newtech %d, preferred: %d. newpreferred: %d\n",
+ modem->technology, newtech, modem->preferred_mask,newpreferred);
+ const char *ret = "+CTEC: DONE";
+ assert( modem );
+
+ if (!newpreferred) {
+ return "ERROR: At least one technology must be enabled";
+ }
+ if (modem->preferred_mask != newpreferred) {
+ char value[MAX_KEY_NAME + 1];
+ modem->preferred_mask = newpreferred;
+ snprintf(value, MAX_KEY_NAME, "%d", newpreferred);
+ amodem_nvram_set(modem, NV_PREFERRED_MODE, value);
+ if (!matchPreferredMask(modem->preferred_mask, newtech)) {
+ newtech = chooseTechFromMask(modem, newpreferred);
+ }
+ }
+
+ if (modem->technology != newtech) {
+ modem->technology = newtech;
+ ret = amodem_printf(modem, "+CTEC: %d", modem->technology);
+ }
+ return ret;
+}
+
+static int
+parsePreferred( const char *str, int *preferred )
+{
+ char *endptr = NULL;
+ int result = 0;
+ if (!str || !*str) { *preferred = 0; return 0; }
+ if (*str == '"') str ++;
+ if (!*str) return 0;
+
+ result = strtol(str, &endptr, 16);
+ if (*endptr && *endptr != '"') {
+ return 0;
+ }
+ if (preferred)
+ *preferred = result;
+ return 1;
+}
+
+void
+amodem_set_cdma_prl_version( AModem modem, int prlVersion)
+{
+ D("amodem_set_prl_version()\n");
+ if (!_amodem_set_cdma_prl_version( modem, prlVersion)) {
+ amodem_unsol(modem, "+WPRL: %d", prlVersion);
+ }
+}
+
+static int
+_amodem_set_cdma_prl_version( AModem modem, int prlVersion)
+{
+ D("_amodem_set_cdma_prl_version");
+ if (modem->prl_version != prlVersion) {
+ modem->prl_version = prlVersion;
+ return 0;
+ }
+ return -1;
+}
+
+void
+amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ss)
+{
+ D("amodem_set_cdma_subscription_source()\n");
+ if (!_amodem_set_cdma_subscription_source( modem, ss)) {
+ amodem_unsol(modem, "+CCSS: %d", (int)ss);
+ }
+}
+
+#define MAX_INT_DIGITS 10
+static int
+_amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ss)
+{
+ D("_amodem_set_cdma_subscription_source()\n");
+ char value[MAX_INT_DIGITS + 1];
+
+ if (ss != modem->subscription_source) {
+ snprintf( value, MAX_INT_DIGITS + 1, "%d", ss );
+ amodem_nvram_set( modem, NV_CDMA_SUBSCRIPTION_SOURCE, value );
+ modem->subscription_source = ss;
+ return 0;
+ }
+ return -1;
+}
+
+static const char*
+handleSubscriptionSource( const char* cmd, AModem modem )
+{
+ int newsource;
+ // TODO: Actually change subscription depending on source
+ D("handleSubscriptionSource(%s)\n",cmd);
+
+ assert( !memcmp( "+CCSS", cmd, 5 ) );
+ cmd += 5;
+ if (cmd[0] == '?') {
+ return amodem_printf( modem, "+CCSS: %d", modem->subscription_source );
+ } else if (cmd[0] == '=') {
+ switch (cmd[1]) {
+ case '0':
+ case '1':
+ newsource = (ACdmaSubscriptionSource)cmd[1] - '0';
+ _amodem_set_cdma_subscription_source( modem, newsource );
+ return amodem_printf( modem, "+CCSS: %d", modem->subscription_source );
+ break;
+ }
+ }
+ return amodem_printf( modem, "ERROR: Invalid subscription source");
+}
+
+static const char*
+handleRoamPref( const char * cmd, AModem modem )
+{
+ int roaming_pref = -1;
+ char *endptr = NULL;
+ D("handleRoamPref(%s)\n", cmd);
+ assert( !memcmp( "+WRMP", cmd, 5 ) );
+ cmd += 5;
+ if (cmd[0] == '?') {
+ return amodem_printf( modem, "+WRMP: %d", modem->roaming_pref );
+ }
+
+ if (!strcmp( cmd, "=?")) {
+ return amodem_printf( modem, "+WRMP: 0,1,2" );
+ } else if (cmd[0] == '=') {
+ cmd ++;
+ roaming_pref = strtol( cmd, &endptr, 10 );
+ // Make sure the rest of the command is the number
+ // (if *endptr is null, it means strtol processed the whole string as a number)
+ if(endptr && !*endptr) {
+ modem->roaming_pref = roaming_pref;
+ aconfig_set( modem->nvram_config, NV_CDMA_ROAMING_PREF, cmd );
+ aconfig_save_file( modem->nvram_config, modem->nvram_config_filename );
+ return NULL;
+ }
+ }
+ return amodem_printf( modem, "ERROR");
+}
+static const char*
+handleTech( const char* cmd, AModem modem )
+{
+ AModemTech newtech = modem->technology;
+ int pt = modem->preferred_mask;
+ int havenewtech = 0;
+ D("handleTech. cmd: %s\n", cmd);
+ assert( !memcmp( "+CTEC", cmd, 5 ) );
+ cmd += 5;
+ if (cmd[0] == '?') {
+ return amodem_printf( modem, "+CTEC: %d,%x",modem->technology, modem->preferred_mask );
+ }
+ amodem_begin_line( modem );
+ if (!strcmp( cmd, "=?")) {
+ return amodem_printf( modem, "+CTEC: 0,1,2,3" );
+ }
+ else if (cmd[0] == '=') {
+ switch (cmd[1]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ havenewtech = 1;
+ newtech = cmd[1] - '0';
+ cmd += 1;
+ break;
+ }
+ cmd += 1;
+ }
+ if (havenewtech) {
+ D( "cmd: %s\n", cmd );
+ if (cmd[0] == ',' && ! parsePreferred( ++cmd, &pt ))
+ return amodem_printf( modem, "ERROR: invalid preferred mode" );
+ return _amodem_switch_technology( modem, newtech, pt );
+ }
+ return amodem_printf( modem, "ERROR: %s: Unknown Technology", cmd + 1 );
+}
+
+static const char*
+handleEmergencyMode( const char* cmd, AModem modem )
+{
+ long arg;
+ char *endptr = NULL;
+ assert ( !memcmp( "+WSOS", cmd, 5 ) );
+ cmd += 5;
+ if (cmd[0] == '?') {
+ return amodem_printf( modem, "+WSOS: %d", modem->in_emergency_mode);
+ }
+
+ if (cmd[0] == '=') {
+ if (cmd[1] == '?') {
+ return amodem_printf(modem, "+WSOS: (0)");
+ }
+ if (cmd[1] == 0) {
+ return amodem_printf(modem, "ERROR");
+ }
+ arg = strtol(cmd+1, &endptr, 10);
+
+ if (!endptr || endptr[0] != 0) {
+ return amodem_printf(modem, "ERROR");
+ }
+
+ arg = arg? 1 : 0;
+
+ if ((!arg) != (!modem->in_emergency_mode)) {
+ modem->in_emergency_mode = arg;
+ return amodem_printf(modem, "+WSOS: %d", arg);
+ }
+ }
+ return amodem_printf(modem, "ERROR");
+}
+
+static const char*
+handlePrlVersion( const char* cmd, AModem modem )
+{
+ long arg;
+ char *endptr = NULL;
+ assert ( !memcmp( "+WPRL", cmd, 5 ) );
+ cmd += 5;
+ if (cmd[0] == '?') {
+ return amodem_printf( modem, "+WPRL: %d", modem->prl_version);
+ }
+
+ return amodem_printf(modem, "ERROR");
+}
+
static const char*
handleRadioPower( const char* cmd, AModem modem )
{
@@ -754,9 +1232,9 @@ static const char*
handleRadioPowerReq( const char* cmd, AModem modem )
{
if (modem->radio_state != A_RADIO_STATE_OFF)
- return "+CFUN=1";
+ return "+CFUN: 1";
else
- return "+CFUN=0";
+ return "+CFUN: 0";
}
static const char*
@@ -777,6 +1255,18 @@ handleSIMStatusReq( const char* cmd, AModem modem )
return answer;
}
+/* TODO: Will we need this?
+static const char*
+handleSRegister( const char * cmd, AModem modem )
+{
+ char *end;
+ assert( cmd[0] == 'S' || cmd[0] == 's' );
+
+ ++ cmd;
+
+ int l = strtol(cmd, &end, 10);
+} */
+
static const char*
handleNetworkRegistration( const char* cmd, AModem modem )
{
@@ -1530,6 +2020,19 @@ voice_call_event( void* _vcall )
amodem_send_calls_update(vcall->modem);
}
+static int amodem_is_emergency( AModem modem, const char *number )
+{
+ int i;
+
+ if (!number) return 0;
+ for (i = 0; i < MAX_EMERGENCY_NUMBERS; i++) {
+ if ( modem->emergency_numbers[i] && !strcmp( number, modem->emergency_numbers[i] )) break;
+ }
+
+ if (i < MAX_EMERGENCY_NUMBERS) return 1;
+
+ return 0;
+}
static const char*
handleDial( const char* cmd, AModem modem )
@@ -1572,13 +2075,18 @@ handleDial( const char* cmd, AModem modem )
call->number[len] = 0;
}
+ amodem_begin_line( modem );
+ if (amodem_is_emergency(modem, call->number)) {
+ modem->in_emergency_mode = 1;
+ amodem_add_line( modem, "+WSOS: 1" );
+ }
vcall->is_remote = (remote_number_string_to_port(call->number) > 0);
vcall->timer = sys_timer_create();
sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_DIAL,
voice_call_event, vcall );
- return NULL;
+ return amodem_end_line( modem );
}
@@ -1743,6 +2251,20 @@ static const struct {
{ "+CFUN=0", NULL, handleRadioPower },
{ "+CFUN=1", NULL, handleRadioPower },
+ { "+CTEC=?", "+CTEC: 0,1,2,3", NULL }, /* Query available Techs */
+ { "!+CTEC", NULL, handleTech }, /* Set/get current Technology and preferred mode */
+
+ { "+WRMP=?", "+WRMP: 0,1,2", NULL }, /* Query Roam Preference */
+ { "!+WRMP", NULL, handleRoamPref }, /* Set/get Roam Preference */
+
+ { "+CCSS=?", "+CTEC: 0,1", NULL }, /* Query available subscription sources */
+ { "!+CCSS", NULL, handleSubscriptionSource }, /* Set/Get current subscription source */
+
+ { "+WSOS=?", "+WSOS: 0", NULL}, /* Query supported +WSOS values */
+ { "!+WSOS=", NULL, handleEmergencyMode },
+
+ { "+WPRL?", NULL, handlePrlVersion }, /* Query the current PRL version */
+
/* see requestOrSendPDPContextList() */
{ "+CGACT?", "", handleListPDPContexts },
diff --git a/telephony/android_modem.h b/telephony/android_modem.h
index 828da42..4409a7d 100644
--- a/telephony/android_modem.h
+++ b/telephony/android_modem.h
@@ -60,20 +60,48 @@ typedef enum {
} ARegistrationState;
typedef enum {
- A_GPRS_NETWORK_UNKNOWN = 0,
- A_GPRS_NETWORK_GPRS,
- A_GPRS_NETWORK_EDGE,
- A_GPRS_NETWORK_UMTS
-} AGprsNetworkType;
+ A_DATA_NETWORK_UNKNOWN = 0,
+ A_DATA_NETWORK_GPRS,
+ A_DATA_NETWORK_EDGE,
+ A_DATA_NETWORK_UMTS,
+ A_DATA_NETWORK_LTE,
+ A_DATA_NETWORK_CDMA1X,
+ A_DATA_NETWORK_EVDO, // TODO: Should REV0, REVA and REVB be added?
+} ADataNetworkType;
+// TODO: Merge the usage of these two structs and rename ADataNetworkType
+typedef enum {
+ A_TECH_GSM = 0,
+ A_TECH_WCDMA,
+ A_TECH_CDMA,
+ A_TECH_EVDO,
+ A_TECH_LTE,
+ A_TECH_UNKNOWN // This must always be the last value in the enum
+} AModemTech;
+
+typedef enum {
+ A_SUBSCRIPTION_NVRAM = 0,
+ A_SUBSCRIPTION_RUIM,
+ A_SUBSCRIPTION_UNKNOWN // This must always be the last value in the enum
+} ACdmaSubscriptionSource;
+
+typedef enum {
+ A_ROAMING_PREF_HOME = 0,
+ A_ROAMING_PREF_AFFILIATED,
+ A_ROAMING_PREF_ANY,
+ A_ROAMING_PREF_UNKNOWN // This must always be the last value in the enum
+} ACdmaRoamingPref;
extern ARegistrationState amodem_get_voice_registration( AModem modem );
extern void amodem_set_voice_registration( AModem modem, ARegistrationState state );
extern ARegistrationState amodem_get_data_registration( AModem modem );
extern void amodem_set_data_registration( AModem modem, ARegistrationState state );
-extern void amodem_set_data_network_type( AModem modem, AGprsNetworkType type );
+extern void amodem_set_data_network_type( AModem modem, ADataNetworkType type );
-extern AGprsNetworkType android_parse_network_type( const char* speed );
+extern ADataNetworkType android_parse_network_type( const char* speed );
+extern AModemTech android_parse_modem_tech( const char* tech );
+extern void amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ssource );
+extern void amodem_set_cdma_prl_version( AModem modem, int prlVersion);
/** OPERATOR NAMES