Home | History | Annotate | Download | only in telephony
      1 /* Copyright (C) 2007-2008 The Android Open Source Project
      2 **
      3 ** This software is licensed under the terms of the GNU General Public
      4 ** License version 2, as published by the Free Software Foundation, and
      5 ** may be copied, distributed, and modified under those terms.
      6 **
      7 ** This program is distributed in the hope that it will be useful,
      8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 ** GNU General Public License for more details.
     11 */
     12 #include "android/android.h"
     13 #include "android_modem.h"
     14 #include "android/config-file.h"
     15 #include "android/config/config.h"
     16 #include "android/snapshot.h"
     17 #include "android/utils/debug.h"
     18 #include "android/utils/timezone.h"
     19 #include "android/utils/system.h"
     20 #include "android/utils/bufprint.h"
     21 #include "android/utils/path.h"
     22 #include "hw/hw.h"
     23 #include "qemu-common.h"
     24 #include "sim_card.h"
     25 #include "sysdeps.h"
     26 #include <memory.h>
     27 #include <stdarg.h>
     28 #include <time.h>
     29 #include <assert.h>
     30 #include <stdio.h>
     31 #include "sms.h"
     32 #include "remote_call.h"
     33 
     34 #define  DEBUG  1
     35 
     36 #if  1
     37 #  define  D_ACTIVE  VERBOSE_CHECK(modem)
     38 #else
     39 #  define  D_ACTIVE  DEBUG
     40 #endif
     41 
     42 #if 1
     43 #  define  R_ACTIVE  VERBOSE_CHECK(radio)
     44 #else
     45 #  define  R_ACTIVE  DEBUG
     46 #endif
     47 
     48 #if DEBUG
     49 #  define  D(...)   do { if (D_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0)
     50 #  define  R(...)   do { if (R_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0)
     51 #else
     52 #  define  D(...)   ((void)0)
     53 #  define  R(...)   ((void)0)
     54 #endif
     55 
     56 #define  CALL_DELAY_DIAL   1000
     57 #define  CALL_DELAY_ALERT  1000
     58 
     59 /* the Android GSM stack checks that the operator's name has changed
     60  * when roaming is on. If not, it will not update the Roaming status icon
     61  *
     62  * this means that we need to emulate two distinct operators:
     63  * - the first one for the 'home' registration state, must also correspond
     64  *   to the emulated user's IMEI
     65  *
     66  * - the second one for the 'roaming' registration state, must have a
     67  *   different name and MCC/MNC
     68  */
     69 
     70 #define  OPERATOR_HOME_INDEX 0
     71 #define  OPERATOR_HOME_MCC   310
     72 #define  OPERATOR_HOME_MNC   260
     73 #define  OPERATOR_HOME_NAME  "Android"
     74 #define  OPERATOR_HOME_MCCMNC  STRINGIFY(OPERATOR_HOME_MCC) \
     75                                STRINGIFY(OPERATOR_HOME_MNC)
     76 
     77 #define  OPERATOR_ROAMING_INDEX 1
     78 #define  OPERATOR_ROAMING_MCC   310
     79 #define  OPERATOR_ROAMING_MNC   295
     80 #define  OPERATOR_ROAMING_NAME  "TelKila"
     81 #define  OPERATOR_ROAMING_MCCMNC  STRINGIFY(OPERATOR_ROAMING_MCC) \
     82                                   STRINGIFY(OPERATOR_ROAMING_MNC)
     83 
     84 static const char* _amodem_switch_technology(AModem modem, AModemTech newtech, int32_t newpreferred);
     85 static int _amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ss);
     86 static int _amodem_set_cdma_prl_version( AModem modem, int prlVersion);
     87 
     88 #if DEBUG
     89 static const char*  quote( const char*  line )
     90 {
     91     static char  temp[1024];
     92     const char*  hexdigits = "0123456789abcdef";
     93     char*        p = temp;
     94     int          c;
     95 
     96     while ((c = *line++) != 0) {
     97         c &= 255;
     98         if (c >= 32 && c < 127) {
     99             *p++ = c;
    100         }
    101         else if (c == '\r') {
    102             memcpy( p, "<CR>", 4 );
    103             p += 4;
    104         }
    105         else if (c == '\n') {
    106             memcpy( p, "<LF>", 4 );strcat( p, "<LF>" );
    107             p += 4;
    108         }
    109         else {
    110             p[0] = '\\';
    111             p[1] = 'x';
    112             p[2] = hexdigits[ (c) >> 4 ];
    113             p[3] = hexdigits[ (c) & 15 ];
    114             p += 4;
    115         }
    116     }
    117     *p = 0;
    118     return temp;
    119 }
    120 #endif
    121 
    122 extern AModemTech
    123 android_parse_modem_tech( const char * tech )
    124 {
    125     const struct { const char* name; AModemTech  tech; }  techs[] = {
    126         { "gsm", A_TECH_GSM },
    127         { "wcdma", A_TECH_WCDMA },
    128         { "cdma", A_TECH_CDMA },
    129         { "evdo", A_TECH_EVDO },
    130         { "lte", A_TECH_LTE },
    131         { NULL, 0 }
    132     };
    133     int  nn;
    134 
    135     for (nn = 0; techs[nn].name; nn++) {
    136         if (!strcmp(tech, techs[nn].name))
    137             return techs[nn].tech;
    138     }
    139     /* not found */
    140     return A_TECH_UNKNOWN;
    141 }
    142 
    143 extern ADataNetworkType
    144 android_parse_network_type( const char*  speed )
    145 {
    146     const struct { const char* name; ADataNetworkType  type; }  types[] = {
    147         { "gprs",  A_DATA_NETWORK_GPRS },
    148         { "edge",  A_DATA_NETWORK_EDGE },
    149         { "umts",  A_DATA_NETWORK_UMTS },
    150         { "hsdpa", A_DATA_NETWORK_UMTS },  /* not handled yet by Android GSM framework */
    151         { "full",  A_DATA_NETWORK_UMTS },
    152         { "lte",   A_DATA_NETWORK_LTE },
    153         { "cdma",  A_DATA_NETWORK_CDMA1X },
    154         { "evdo",  A_DATA_NETWORK_EVDO },
    155         { NULL, 0 }
    156     };
    157     int  nn;
    158 
    159     for (nn = 0; types[nn].name; nn++) {
    160         if (!strcmp(speed, types[nn].name))
    161             return types[nn].type;
    162     }
    163     /* not found, be conservative */
    164     return A_DATA_NETWORK_GPRS;
    165 }
    166 
    167 /* 'mode' for +CREG/+CGREG commands */
    168 typedef enum {
    169     A_REGISTRATION_UNSOL_DISABLED     = 0,
    170     A_REGISTRATION_UNSOL_ENABLED      = 1,
    171     A_REGISTRATION_UNSOL_ENABLED_FULL = 2
    172 } ARegistrationUnsolMode;
    173 
    174 /* Operator selection mode, see +COPS commands */
    175 typedef enum {
    176     A_SELECTION_AUTOMATIC,
    177     A_SELECTION_MANUAL,
    178     A_SELECTION_DEREGISTRATION,
    179     A_SELECTION_SET_FORMAT,
    180     A_SELECTION_MANUAL_AUTOMATIC
    181 } AOperatorSelection;
    182 
    183 /* Operator status, see +COPS commands */
    184 typedef enum {
    185     A_STATUS_UNKNOWN = 0,
    186     A_STATUS_AVAILABLE,
    187     A_STATUS_CURRENT,
    188     A_STATUS_DENIED
    189 } AOperatorStatus;
    190 
    191 typedef struct {
    192     AOperatorStatus  status;
    193     char             name[3][16];
    194 } AOperatorRec, *AOperator;
    195 
    196 typedef struct AVoiceCallRec {
    197     ACallRec    call;
    198     SysTimer    timer;
    199     AModem      modem;
    200     char        is_remote;
    201 } AVoiceCallRec, *AVoiceCall;
    202 
    203 #define  MAX_OPERATORS  4
    204 
    205 typedef enum {
    206     A_DATA_IP = 0,
    207     A_DATA_PPP
    208 } ADataType;
    209 
    210 #define  A_DATA_APN_SIZE  32
    211 
    212 typedef struct {
    213     int        id;
    214     int        active;
    215     ADataType  type;
    216     char       apn[ A_DATA_APN_SIZE ];
    217 
    218 } ADataContextRec, *ADataContext;
    219 
    220 /* the spec says that there can only be a max of 4 contexts */
    221 #define  MAX_DATA_CONTEXTS  4
    222 #define  MAX_CALLS          4
    223 #define  MAX_EMERGENCY_NUMBERS 16
    224 
    225 
    226 #define  A_MODEM_SELF_SIZE   3
    227 
    228 
    229 typedef struct AModemRec_
    230 {
    231     /* Legacy support */
    232     char          supportsNetworkDataType;
    233 
    234     /* Radio state */
    235     ARadioState   radio_state;
    236     int           area_code;
    237     int           cell_id;
    238     int           base_port;
    239 
    240     int           rssi;
    241     int           ber;
    242 
    243     /* SMS */
    244     int           wait_sms;
    245 
    246     /* SIM card */
    247     ASimCard      sim;
    248 
    249     /* voice and data network registration */
    250     ARegistrationUnsolMode   voice_mode;
    251     ARegistrationState       voice_state;
    252     ARegistrationUnsolMode   data_mode;
    253     ARegistrationState       data_state;
    254     ADataNetworkType         data_network;
    255 
    256     /* operator names */
    257     AOperatorSelection  oper_selection_mode;
    258     ANameIndex          oper_name_index;
    259     int                 oper_index;
    260     int                 oper_count;
    261     AOperatorRec        operators[ MAX_OPERATORS ];
    262 
    263     /* data connection contexts */
    264     ADataContextRec     data_contexts[ MAX_DATA_CONTEXTS ];
    265 
    266     /* active calls */
    267     AVoiceCallRec       calls[ MAX_CALLS ];
    268     int                 call_count;
    269 
    270     /* unsolicited callback */  /* XXX: TODO: use this */
    271     AModemUnsolFunc     unsol_func;
    272     void*               unsol_opaque;
    273 
    274     SmsReceiver         sms_receiver;
    275 
    276     int                 out_size;
    277     char                out_buff[1024];
    278 
    279     /*
    280      * Hold non-volatile ram configuration for modem
    281      */
    282     AConfig *nvram_config;
    283     char *nvram_config_filename;
    284 
    285     AModemTech technology;
    286     /*
    287      * This is are really 4 byte-sized prioritized masks.
    288      * Byte order gives the priority for the specific bitmask.
    289      * Each bit position in each of the masks is indexed by the different
    290      * A_TECH_XXXX values.
    291      * e.g. 0x01 means only GSM is set (bit index 0), whereas 0x0f
    292      * means that GSM,WCDMA,CDMA and EVDO are set
    293      */
    294     int32_t preferred_mask;
    295     ACdmaSubscriptionSource subscription_source;
    296     ACdmaRoamingPref roaming_pref;
    297     int in_emergency_mode;
    298     int prl_version;
    299 
    300     const char *emergency_numbers[MAX_EMERGENCY_NUMBERS];
    301 } AModemRec;
    302 
    303 
    304 static void
    305 amodem_unsol( AModem  modem, const char* format, ... )
    306 {
    307     if (modem->unsol_func) {
    308         va_list  args;
    309         va_start(args, format);
    310         vsnprintf( modem->out_buff, sizeof(modem->out_buff), format, args );
    311         va_end(args);
    312 
    313         modem->unsol_func( modem->unsol_opaque, modem->out_buff );
    314     }
    315 }
    316 
    317 void
    318 amodem_receive_sms( AModem  modem, SmsPDU  sms )
    319 {
    320 #define  SMS_UNSOL_HEADER  "+CMT: 0\r\n"
    321 
    322     if (modem->unsol_func) {
    323         int    len, max;
    324         char*  p;
    325 
    326         strcpy( modem->out_buff, SMS_UNSOL_HEADER );
    327         p   = modem->out_buff + (sizeof(SMS_UNSOL_HEADER)-1);
    328         max = sizeof(modem->out_buff) - 3 - (sizeof(SMS_UNSOL_HEADER)-1);
    329         len = smspdu_to_hex( sms, p, max );
    330         if (len > max) /* too long */
    331             return;
    332         p[len]   = '\r';
    333         p[len+1] = '\n';
    334         p[len+2] = 0;
    335 
    336         R( "SMS>> %s\n", p );
    337 
    338         modem->unsol_func( modem->unsol_opaque, modem->out_buff );
    339     }
    340 }
    341 
    342 static const char*
    343 amodem_printf( AModem  modem, const char*  format, ... )
    344 {
    345     va_list  args;
    346     va_start(args, format);
    347     vsnprintf( modem->out_buff, sizeof(modem->out_buff), format, args );
    348     va_end(args);
    349 
    350     return modem->out_buff;
    351 }
    352 
    353 static void
    354 amodem_begin_line( AModem  modem )
    355 {
    356     modem->out_size = 0;
    357 }
    358 
    359 static void
    360 amodem_add_line( AModem  modem, const char*  format, ... )
    361 {
    362     va_list  args;
    363     va_start(args, format);
    364     modem->out_size += vsnprintf( modem->out_buff + modem->out_size,
    365                                   sizeof(modem->out_buff) - modem->out_size,
    366                                   format, args );
    367     va_end(args);
    368 }
    369 
    370 static const char*
    371 amodem_end_line( AModem  modem )
    372 {
    373     modem->out_buff[ modem->out_size ] = 0;
    374     return modem->out_buff;
    375 }
    376 
    377 #define NV_OPER_NAME_INDEX                     "oper_name_index"
    378 #define NV_OPER_INDEX                          "oper_index"
    379 #define NV_SELECTION_MODE                      "selection_mode"
    380 #define NV_OPER_COUNT                          "oper_count"
    381 #define NV_MODEM_TECHNOLOGY                    "modem_technology"
    382 #define NV_PREFERRED_MODE                      "preferred_mode"
    383 #define NV_CDMA_SUBSCRIPTION_SOURCE            "cdma_subscription_source"
    384 #define NV_CDMA_ROAMING_PREF                   "cdma_roaming_pref"
    385 #define NV_IN_ECBM                             "in_ecbm"
    386 #define NV_EMERGENCY_NUMBER_FMT                    "emergency_number_%d"
    387 #define NV_PRL_VERSION                         "prl_version"
    388 #define NV_SREGISTER                           "sregister"
    389 
    390 #define MAX_KEY_NAME 40
    391 
    392 static AConfig *
    393 amodem_load_nvram( AModem modem )
    394 {
    395     AConfig* root = aconfig_node(NULL, NULL);
    396     D("Using config file: %s\n", modem->nvram_config_filename);
    397     if (aconfig_load_file(root, modem->nvram_config_filename)) {
    398         D("Unable to load config\n");
    399         aconfig_set(root, NV_MODEM_TECHNOLOGY, "gsm");
    400         aconfig_save_file(root, modem->nvram_config_filename);
    401     }
    402     return root;
    403 }
    404 
    405 static int
    406 amodem_nvram_get_int( AModem modem, const char *nvname, int defval)
    407 {
    408     int value;
    409     char strval[MAX_KEY_NAME + 1];
    410     char *newvalue;
    411 
    412     value = aconfig_int(modem->nvram_config, nvname, defval);
    413     snprintf(strval, MAX_KEY_NAME, "%d", value);
    414     D("Setting value of %s to %d (%s)",nvname, value, strval);
    415     newvalue = strdup(strval);
    416     if (!newvalue) {
    417         newvalue = "";
    418     }
    419     aconfig_set(modem->nvram_config, nvname, newvalue);
    420 
    421     return value;
    422 }
    423 
    424 const char *
    425 amodem_nvram_get_str( AModem modem, const char *nvname, const char *defval)
    426 {
    427     const char *value;
    428 
    429     value = aconfig_str(modem->nvram_config, nvname, defval);
    430 
    431     if (!value) {
    432         if (!defval)
    433             return NULL;
    434         value = defval;
    435     }
    436 
    437     aconfig_set(modem->nvram_config, nvname, value);
    438 
    439     return value;
    440 }
    441 
    442 static ACdmaSubscriptionSource _amodem_get_cdma_subscription_source( AModem modem )
    443 {
    444    int iss = -1;
    445    iss = amodem_nvram_get_int( modem, NV_CDMA_SUBSCRIPTION_SOURCE, A_SUBSCRIPTION_RUIM );
    446    if (iss >= A_SUBSCRIPTION_UNKNOWN || iss < 0) {
    447        iss = A_SUBSCRIPTION_RUIM;
    448    }
    449 
    450    return iss;
    451 }
    452 
    453 static ACdmaRoamingPref _amodem_get_cdma_roaming_preference( AModem modem )
    454 {
    455    int rp = -1;
    456    rp = amodem_nvram_get_int( modem, NV_CDMA_ROAMING_PREF, A_ROAMING_PREF_ANY );
    457    if (rp >= A_ROAMING_PREF_UNKNOWN || rp < 0) {
    458        rp = A_ROAMING_PREF_ANY;
    459    }
    460 
    461    return rp;
    462 }
    463 
    464 static void
    465 amodem_reset( AModem  modem )
    466 {
    467     const char *tmp;
    468     int i;
    469     modem->nvram_config = amodem_load_nvram(modem);
    470     modem->radio_state = A_RADIO_STATE_OFF;
    471     modem->wait_sms    = 0;
    472 
    473     modem->rssi= 7;    // Two signal strength bars
    474     modem->ber = 99;   // Means 'unknown'
    475 
    476     modem->oper_name_index     = amodem_nvram_get_int(modem, NV_OPER_NAME_INDEX, 2);
    477     modem->oper_selection_mode = amodem_nvram_get_int(modem, NV_SELECTION_MODE, A_SELECTION_AUTOMATIC);
    478     modem->oper_index          = amodem_nvram_get_int(modem, NV_OPER_INDEX, 0);
    479     modem->oper_count          = amodem_nvram_get_int(modem, NV_OPER_COUNT, 2);
    480     modem->in_emergency_mode   = amodem_nvram_get_int(modem, NV_IN_ECBM, 0);
    481     modem->prl_version         = amodem_nvram_get_int(modem, NV_PRL_VERSION, 0);
    482 
    483     modem->emergency_numbers[0] = "911";
    484     char key_name[MAX_KEY_NAME + 1];
    485     for (i = 1; i < MAX_EMERGENCY_NUMBERS; i++) {
    486         snprintf(key_name,MAX_KEY_NAME, NV_EMERGENCY_NUMBER_FMT, i);
    487         modem->emergency_numbers[i] = amodem_nvram_get_str(modem,key_name, NULL);
    488     }
    489 
    490     modem->area_code = -1;
    491     modem->cell_id   = -1;
    492 
    493     strcpy( modem->operators[0].name[0], OPERATOR_HOME_NAME );
    494     strcpy( modem->operators[0].name[1], OPERATOR_HOME_NAME );
    495     strcpy( modem->operators[0].name[2], OPERATOR_HOME_MCCMNC );
    496 
    497     modem->operators[0].status        = A_STATUS_AVAILABLE;
    498 
    499     strcpy( modem->operators[1].name[0], OPERATOR_ROAMING_NAME );
    500     strcpy( modem->operators[1].name[1], OPERATOR_ROAMING_NAME );
    501     strcpy( modem->operators[1].name[2], OPERATOR_ROAMING_MCCMNC );
    502 
    503     modem->operators[1].status        = A_STATUS_AVAILABLE;
    504 
    505     modem->voice_mode   = A_REGISTRATION_UNSOL_ENABLED_FULL;
    506     modem->voice_state  = A_REGISTRATION_HOME;
    507     modem->data_mode    = A_REGISTRATION_UNSOL_ENABLED_FULL;
    508     modem->data_state   = A_REGISTRATION_HOME;
    509     modem->data_network = A_DATA_NETWORK_UMTS;
    510 
    511     tmp = amodem_nvram_get_str( modem, NV_MODEM_TECHNOLOGY, "gsm" );
    512     modem->technology = android_parse_modem_tech( tmp );
    513     if (modem->technology == A_TECH_UNKNOWN) {
    514         modem->technology = aconfig_int( modem->nvram_config, NV_MODEM_TECHNOLOGY, A_TECH_GSM );
    515     }
    516     // Support GSM, WCDMA, CDMA, EvDo
    517     modem->preferred_mask = amodem_nvram_get_int( modem, NV_PREFERRED_MODE, 0x0f );
    518 
    519     modem->subscription_source = _amodem_get_cdma_subscription_source( modem );
    520     modem->roaming_pref = _amodem_get_cdma_roaming_preference( modem );
    521 }
    522 
    523 static AVoiceCall amodem_alloc_call( AModem   modem );
    524 static void amodem_free_call( AModem  modem, AVoiceCall  call );
    525 
    526 #define MODEM_DEV_STATE_SAVE_VERSION 1
    527 
    528 static void  android_modem_state_save(QEMUFile *f, void  *opaque)
    529 {
    530     AModem modem = opaque;
    531 
    532     // TODO: save more than just calls and call_count - rssi, power, etc.
    533 
    534     qemu_put_byte(f, modem->call_count);
    535 
    536     int nn;
    537     for (nn = modem->call_count - 1; nn >= 0; nn--) {
    538       AVoiceCall  vcall = modem->calls + nn;
    539       // Note: not saving timers or remote calls.
    540       ACall       call  = &vcall->call;
    541       qemu_put_byte( f, call->dir );
    542       qemu_put_byte( f, call->state );
    543       qemu_put_byte( f, call->mode );
    544       qemu_put_be32( f, call->multi );
    545       qemu_put_buffer( f, (uint8_t *)call->number, A_CALL_NUMBER_MAX_SIZE+1 );
    546     }
    547 }
    548 
    549 static int  android_modem_state_load(QEMUFile *f, void  *opaque, int version_id)
    550 {
    551     if (version_id != MODEM_DEV_STATE_SAVE_VERSION)
    552       return -1;
    553 
    554     AModem modem = opaque;
    555 
    556     // In case there are timers or remote calls.
    557     int nn;
    558     for (nn = modem->call_count - 1; nn >= 0; nn--) {
    559       amodem_free_call( modem, modem->calls + nn);
    560     }
    561 
    562     int call_count = qemu_get_byte(f);
    563     for (nn = call_count; nn > 0; nn--) {
    564       AVoiceCall vcall = amodem_alloc_call( modem );
    565       ACall      call  = &vcall->call;
    566       call->dir   = qemu_get_byte( f );
    567       call->state = qemu_get_byte( f );
    568       call->mode  = qemu_get_byte( f );
    569       call->multi = qemu_get_be32( f );
    570       qemu_get_buffer( f, (uint8_t *)call->number, A_CALL_NUMBER_MAX_SIZE+1 );
    571     }
    572 
    573     return 0; // >=0 Happy
    574 }
    575 
    576 static AModemRec   _android_modem[1];
    577 
    578 AModem
    579 amodem_create( int  base_port, AModemUnsolFunc  unsol_func, void*  unsol_opaque )
    580 {
    581     AModem  modem = _android_modem;
    582     char nvfname[MAX_PATH];
    583     char *start = nvfname;
    584     char *end = start + sizeof(nvfname);
    585 
    586     modem->base_port    = base_port;
    587     start = bufprint_config_file( start, end, "modem-nv-ram-" );
    588     start = bufprint( start, end, "%d", modem->base_port );
    589     modem->nvram_config_filename = strdup( nvfname );
    590 
    591     amodem_reset( modem );
    592     modem->supportsNetworkDataType = 1;
    593     modem->unsol_func   = unsol_func;
    594     modem->unsol_opaque = unsol_opaque;
    595 
    596     modem->sim = asimcard_create(base_port);
    597 
    598     sys_main_init();
    599     register_savevm(NULL,
    600                     "android_modem",
    601                     0,
    602                     MODEM_DEV_STATE_SAVE_VERSION,
    603                     android_modem_state_save,
    604                     android_modem_state_load,
    605                     modem);
    606 
    607     aconfig_save_file( modem->nvram_config, modem->nvram_config_filename );
    608     return  modem;
    609 }
    610 
    611 void
    612 amodem_set_legacy( AModem  modem )
    613 {
    614     modem->supportsNetworkDataType = 0;
    615 }
    616 
    617 void
    618 amodem_destroy( AModem  modem )
    619 {
    620     asimcard_destroy( modem->sim );
    621     modem->sim = NULL;
    622 }
    623 
    624 
    625 static int
    626 amodem_has_network( AModem  modem )
    627 {
    628     return !(modem->radio_state == A_RADIO_STATE_OFF   ||
    629              modem->oper_index < 0                  ||
    630              modem->oper_index >= modem->oper_count ||
    631              modem->oper_selection_mode == A_SELECTION_DEREGISTRATION );
    632 }
    633 
    634 
    635 ARadioState
    636 amodem_get_radio_state( AModem modem )
    637 {
    638     return modem->radio_state;
    639 }
    640 
    641 void
    642 amodem_set_radio_state( AModem modem, ARadioState  state )
    643 {
    644     modem->radio_state = state;
    645 }
    646 
    647 ASimCard
    648 amodem_get_sim( AModem  modem )
    649 {
    650     return  modem->sim;
    651 }
    652 
    653 ARegistrationState
    654 amodem_get_voice_registration( AModem  modem )
    655 {
    656     return modem->voice_state;
    657 }
    658 
    659 void
    660 amodem_set_voice_registration( AModem  modem, ARegistrationState  state )
    661 {
    662     modem->voice_state = state;
    663 
    664     if (state == A_REGISTRATION_HOME)
    665         modem->oper_index = OPERATOR_HOME_INDEX;
    666     else if (state == A_REGISTRATION_ROAMING)
    667         modem->oper_index = OPERATOR_ROAMING_INDEX;
    668 
    669     switch (modem->voice_mode) {
    670         case A_REGISTRATION_UNSOL_ENABLED:
    671             amodem_unsol( modem, "+CREG: %d,%d\r",
    672                           modem->voice_mode, modem->voice_state );
    673             break;
    674 
    675         case A_REGISTRATION_UNSOL_ENABLED_FULL:
    676             amodem_unsol( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"\r",
    677                           modem->voice_mode, modem->voice_state,
    678                           modem->area_code & 0xffff, modem->cell_id & 0xffff);
    679             break;
    680         default:
    681             ;
    682     }
    683 }
    684 
    685 ARegistrationState
    686 amodem_get_data_registration( AModem  modem )
    687 {
    688     return modem->data_state;
    689 }
    690 
    691 void
    692 amodem_set_data_registration( AModem  modem, ARegistrationState  state )
    693 {
    694     modem->data_state = state;
    695 
    696     switch (modem->data_mode) {
    697         case A_REGISTRATION_UNSOL_ENABLED:
    698             amodem_unsol( modem, "+CGREG: %d,%d\r",
    699                           modem->data_mode, modem->data_state );
    700             break;
    701 
    702         case A_REGISTRATION_UNSOL_ENABLED_FULL:
    703             if (modem->supportsNetworkDataType)
    704                 amodem_unsol( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"\r",
    705                             modem->data_mode, modem->data_state,
    706                             modem->area_code & 0xffff, modem->cell_id & 0xffff,
    707                             modem->data_network );
    708             else
    709                 amodem_unsol( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\"\r",
    710                             modem->data_mode, modem->data_state,
    711                             modem->area_code & 0xffff, modem->cell_id & 0xffff );
    712             break;
    713 
    714         default:
    715             ;
    716     }
    717 }
    718 
    719 static int
    720 amodem_nvram_set( AModem modem, const char *name, const char *value )
    721 {
    722     aconfig_set(modem->nvram_config, name, value);
    723     return 0;
    724 }
    725 static AModemTech
    726 tech_from_network_type( ADataNetworkType type )
    727 {
    728     switch (type) {
    729         case A_DATA_NETWORK_GPRS:
    730         case A_DATA_NETWORK_EDGE:
    731         case A_DATA_NETWORK_UMTS:
    732             // TODO: Add A_TECH_WCDMA
    733             return A_TECH_GSM;
    734         case A_DATA_NETWORK_LTE:
    735             return A_TECH_LTE;
    736         case A_DATA_NETWORK_CDMA1X:
    737         case A_DATA_NETWORK_EVDO:
    738             return A_TECH_CDMA;
    739         case A_DATA_NETWORK_UNKNOWN:
    740             return A_TECH_UNKNOWN;
    741     }
    742     return A_TECH_UNKNOWN;
    743 }
    744 
    745 void
    746 amodem_set_data_network_type( AModem  modem, ADataNetworkType   type )
    747 {
    748     AModemTech modemTech;
    749     modem->data_network = type;
    750     amodem_set_data_registration( modem, modem->data_state );
    751     modemTech = tech_from_network_type(type);
    752     if (modem->unsol_func && modemTech != A_TECH_UNKNOWN) {
    753         if (_amodem_switch_technology( modem, modemTech, modem->preferred_mask )) {
    754             modem->unsol_func( modem->unsol_opaque, modem->out_buff );
    755         }
    756     }
    757 }
    758 
    759 int
    760 amodem_get_operator_name ( AModem  modem, ANameIndex  index, char*  buffer, int  buffer_size )
    761 {
    762     AOperator  oper;
    763     int        len;
    764 
    765     if ( (unsigned)modem->oper_index >= (unsigned)modem->oper_count ||
    766          (unsigned)index > 2 )
    767         return 0;
    768 
    769     oper = modem->operators + modem->oper_index;
    770     len  = strlen(oper->name[index]) + 1;
    771 
    772     if (buffer_size > len)
    773         buffer_size = len;
    774 
    775     if (buffer_size > 0) {
    776         memcpy( buffer, oper->name[index], buffer_size-1 );
    777         buffer[buffer_size] = 0;
    778     }
    779     return len;
    780 }
    781 
    782 /* reset one operator name from a user-provided buffer, set buffer_size to -1 for zero-terminated strings */
    783 void
    784 amodem_set_operator_name( AModem  modem, ANameIndex  index, const char*  buffer, int  buffer_size )
    785 {
    786     AOperator  oper;
    787     int        avail;
    788 
    789     if ( (unsigned)modem->oper_index >= (unsigned)modem->oper_count ||
    790          (unsigned)index > 2 )
    791         return;
    792 
    793     oper = modem->operators + modem->oper_index;
    794 
    795     avail = sizeof(oper->name[0]);
    796     if (buffer_size < 0)
    797         buffer_size = strlen(buffer);
    798     if (buffer_size > avail-1)
    799         buffer_size = avail-1;
    800     memcpy( oper->name[index], buffer, buffer_size );
    801     oper->name[index][buffer_size] = 0;
    802 }
    803 
    804 /** CALLS
    805  **/
    806 int
    807 amodem_get_call_count( AModem  modem )
    808 {
    809     return modem->call_count;
    810 }
    811 
    812 ACall
    813 amodem_get_call( AModem  modem, int  index )
    814 {
    815     if ((unsigned)index >= (unsigned)modem->call_count)
    816         return NULL;
    817 
    818     return &modem->calls[index].call;
    819 }
    820 
    821 static AVoiceCall
    822 amodem_alloc_call( AModem   modem )
    823 {
    824     AVoiceCall  call  = NULL;
    825     int         count = modem->call_count;
    826 
    827     if (count < MAX_CALLS) {
    828         int  id;
    829 
    830         /* find a valid id for this call */
    831         for (id = 0; id < modem->call_count; id++) {
    832             int  found = 0;
    833             int  nn;
    834             for (nn = 0; nn < count; nn++) {
    835                 if ( modem->calls[nn].call.id == (id+1) ) {
    836                     found = 1;
    837                     break;
    838                 }
    839             }
    840             if (!found)
    841                 break;
    842         }
    843         call          = modem->calls + count;
    844         call->call.id = id + 1;
    845         call->modem   = modem;
    846 
    847         modem->call_count += 1;
    848     }
    849     return call;
    850 }
    851 
    852 
    853 static void
    854 amodem_free_call( AModem  modem, AVoiceCall  call )
    855 {
    856     int  nn;
    857 
    858     if (call->timer) {
    859         sys_timer_destroy( call->timer );
    860         call->timer = NULL;
    861     }
    862 
    863     if (call->is_remote) {
    864         remote_call_cancel( call->call.number, modem->base_port );
    865         call->is_remote = 0;
    866     }
    867 
    868     for (nn = 0; nn < modem->call_count; nn++) {
    869         if ( modem->calls + nn == call )
    870             break;
    871     }
    872     assert( nn < modem->call_count );
    873 
    874     memmove( modem->calls + nn,
    875              modem->calls + nn + 1,
    876              (modem->call_count - 1 - nn)*sizeof(*call) );
    877 
    878     modem->call_count -= 1;
    879 }
    880 
    881 
    882 static AVoiceCall
    883 amodem_find_call( AModem  modem, int  id )
    884 {
    885     int  nn;
    886 
    887     for (nn = 0; nn < modem->call_count; nn++) {
    888         AVoiceCall call = modem->calls + nn;
    889         if (call->call.id == id)
    890             return call;
    891     }
    892     return NULL;
    893 }
    894 
    895 static void
    896 amodem_send_calls_update( AModem  modem )
    897 {
    898    /* despite its name, this really tells the system that the call
    899     * state has changed */
    900     amodem_unsol( modem, "RING\r" );
    901 }
    902 
    903 
    904 int
    905 amodem_add_inbound_call( AModem  modem, const char*  number )
    906 {
    907     AVoiceCall  vcall = amodem_alloc_call( modem );
    908     ACall       call  = &vcall->call;
    909     int         len;
    910 
    911     if (call == NULL)
    912         return -1;
    913 
    914     call->dir   = A_CALL_INBOUND;
    915     call->state = A_CALL_INCOMING;
    916     call->mode  = A_CALL_VOICE;
    917     call->multi = 0;
    918 
    919     vcall->is_remote = (remote_number_string_to_port(number) > 0);
    920 
    921     len  = strlen(number);
    922     if (len >= sizeof(call->number))
    923         len = sizeof(call->number)-1;
    924 
    925     memcpy( call->number, number, len );
    926     call->number[len] = 0;
    927 
    928     amodem_send_calls_update( modem );
    929     return 0;
    930 }
    931 
    932 ACall
    933 amodem_find_call_by_number( AModem  modem, const char*  number )
    934 {
    935     AVoiceCall  vcall = modem->calls;
    936     AVoiceCall  vend  = vcall + modem->call_count;
    937 
    938     if (!number)
    939         return NULL;
    940 
    941     for ( ; vcall < vend; vcall++ )
    942         if ( !strcmp(vcall->call.number, number) )
    943             return &vcall->call;
    944 
    945     return  NULL;
    946 }
    947 
    948 void
    949 amodem_set_signal_strength( AModem modem, int rssi, int ber )
    950 {
    951     modem->rssi = rssi;
    952     modem->ber = ber;
    953 }
    954 
    955 static void
    956 acall_set_state( AVoiceCall    call, ACallState  state )
    957 {
    958     if (state != call->call.state)
    959     {
    960         if (call->is_remote)
    961         {
    962             const char*  number = call->call.number;
    963             int          port   = call->modem->base_port;
    964 
    965             switch (state) {
    966                 case A_CALL_HELD:
    967                     remote_call_other( number, port, REMOTE_CALL_HOLD );
    968                     break;
    969 
    970                 case A_CALL_ACTIVE:
    971                     remote_call_other( number, port, REMOTE_CALL_ACCEPT );
    972                     break;
    973 
    974                 default: ;
    975             }
    976         }
    977         call->call.state = state;
    978     }
    979 }
    980 
    981 
    982 int
    983 amodem_update_call( AModem  modem, const char*  fromNumber, ACallState  state )
    984 {
    985     AVoiceCall  vcall = (AVoiceCall) amodem_find_call_by_number(modem, fromNumber);
    986 
    987     if (vcall == NULL)
    988         return -1;
    989 
    990     acall_set_state( vcall, state );
    991     amodem_send_calls_update(modem);
    992     return 0;
    993 }
    994 
    995 
    996 int
    997 amodem_disconnect_call( AModem  modem, const char*  number )
    998 {
    999     AVoiceCall  vcall = (AVoiceCall) amodem_find_call_by_number(modem, number);
   1000 
   1001     if (!vcall)
   1002         return -1;
   1003 
   1004     amodem_free_call( modem, vcall );
   1005     amodem_send_calls_update(modem);
   1006     return 0;
   1007 }
   1008 
   1009 /** COMMAND HANDLERS
   1010  **/
   1011 
   1012 static const char*
   1013 unknownCommand( const char*  cmd, AModem  modem )
   1014 {
   1015     modem=modem;
   1016     fprintf(stderr, ">>> unknown command '%s'\n", cmd );
   1017     return "ERROR: unknown command\r";
   1018 }
   1019 
   1020 /*
   1021  * Tell whether the specified tech is valid for the preferred mask.
   1022  * @pmask: The preferred mask
   1023  * @tech: The AModemTech we try to validate
   1024  * return: If the specified technology is not set in any of the 4
   1025  *         bitmasks, return 0.
   1026  *         Otherwise, return a non-zero value.
   1027  */
   1028 static int matchPreferredMask( int32_t pmask, AModemTech tech )
   1029 {
   1030     int ret = 0;
   1031     int i;
   1032     for ( i=3; i >= 0 ; i-- ) {
   1033         if (pmask & (1 << (tech + i*8 ))) {
   1034             ret = 1;
   1035             break;
   1036         }
   1037     }
   1038     return ret;
   1039 }
   1040 
   1041 static AModemTech
   1042 chooseTechFromMask( AModem modem, int32_t preferred )
   1043 {
   1044     int i, j;
   1045 
   1046     /* TODO: Current implementation will only return the highest priority,
   1047      * lowest numbered technology that is set in the mask.
   1048      * However the implementation could be changed to consider currently
   1049      * available networks set from the console (or somewhere else)
   1050      */
   1051     for ( i=3 ; i >= 0; i-- ) {
   1052         for ( j=0 ; j < A_TECH_UNKNOWN ; j++ ) {
   1053             if (preferred & (1 << (j + 8 * i)))
   1054                 return (AModemTech) j;
   1055         }
   1056     }
   1057     assert("This should never happen" == 0);
   1058     // This should never happen. Just to please the compiler.
   1059     return A_TECH_UNKNOWN;
   1060 }
   1061 
   1062 static const char*
   1063 _amodem_switch_technology( AModem modem, AModemTech newtech, int32_t newpreferred )
   1064 {
   1065     D("_amodem_switch_technology: oldtech: %d, newtech %d, preferred: %d. newpreferred: %d\n",
   1066                       modem->technology, newtech, modem->preferred_mask,newpreferred);
   1067     const char *ret = "+CTEC: DONE";
   1068     assert( modem );
   1069 
   1070     if (!newpreferred) {
   1071         return "ERROR: At least one technology must be enabled";
   1072     }
   1073     if (modem->preferred_mask != newpreferred) {
   1074         char value[MAX_KEY_NAME + 1];
   1075         modem->preferred_mask = newpreferred;
   1076         snprintf(value, MAX_KEY_NAME, "%d", newpreferred);
   1077         amodem_nvram_set(modem, NV_PREFERRED_MODE, value);
   1078         if (!matchPreferredMask(modem->preferred_mask, newtech)) {
   1079             newtech = chooseTechFromMask(modem, newpreferred);
   1080         }
   1081     }
   1082 
   1083     if (modem->technology != newtech) {
   1084         modem->technology = newtech;
   1085         ret = amodem_printf(modem, "+CTEC: %d", modem->technology);
   1086     }
   1087     return ret;
   1088 }
   1089 
   1090 static int
   1091 parsePreferred( const char *str, int *preferred )
   1092 {
   1093     char *endptr = NULL;
   1094     int result = 0;
   1095     if (!str || !*str) { *preferred = 0; return 0; }
   1096     if (*str == '"') str ++;
   1097     if (!*str) return 0;
   1098 
   1099     result = strtol(str, &endptr, 16);
   1100     if (*endptr && *endptr != '"') {
   1101         return 0;
   1102     }
   1103     if (preferred)
   1104         *preferred = result;
   1105     return 1;
   1106 }
   1107 
   1108 void
   1109 amodem_set_cdma_prl_version( AModem modem, int prlVersion)
   1110 {
   1111     D("amodem_set_prl_version()\n");
   1112     if (!_amodem_set_cdma_prl_version( modem, prlVersion)) {
   1113         amodem_unsol(modem, "+WPRL: %d", prlVersion);
   1114     }
   1115 }
   1116 
   1117 static int
   1118 _amodem_set_cdma_prl_version( AModem modem, int prlVersion)
   1119 {
   1120     D("_amodem_set_cdma_prl_version");
   1121     if (modem->prl_version != prlVersion) {
   1122         modem->prl_version = prlVersion;
   1123         return 0;
   1124     }
   1125     return -1;
   1126 }
   1127 
   1128 void
   1129 amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ss)
   1130 {
   1131     D("amodem_set_cdma_subscription_source()\n");
   1132     if (!_amodem_set_cdma_subscription_source( modem, ss)) {
   1133         amodem_unsol(modem, "+CCSS: %d", (int)ss);
   1134     }
   1135 }
   1136 
   1137 #define MAX_INT_DIGITS 10
   1138 static int
   1139 _amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ss)
   1140 {
   1141     D("_amodem_set_cdma_subscription_source()\n");
   1142     char value[MAX_INT_DIGITS + 1];
   1143 
   1144     if (ss != modem->subscription_source) {
   1145         snprintf( value, MAX_INT_DIGITS + 1, "%d", ss );
   1146         amodem_nvram_set( modem, NV_CDMA_SUBSCRIPTION_SOURCE, value );
   1147         modem->subscription_source = ss;
   1148         return 0;
   1149     }
   1150     return -1;
   1151 }
   1152 
   1153 static const char*
   1154 handleSubscriptionSource( const char*  cmd, AModem  modem )
   1155 {
   1156     int newsource;
   1157     // TODO: Actually change subscription depending on source
   1158     D("handleSubscriptionSource(%s)\n",cmd);
   1159 
   1160     assert( !memcmp( "+CCSS", cmd, 5 ) );
   1161     cmd += 5;
   1162     if (cmd[0] == '?') {
   1163         return amodem_printf( modem, "+CCSS: %d", modem->subscription_source );
   1164     } else if (cmd[0] == '=') {
   1165         switch (cmd[1]) {
   1166             case '0':
   1167             case '1':
   1168                 newsource = (ACdmaSubscriptionSource)cmd[1] - '0';
   1169                 _amodem_set_cdma_subscription_source( modem, newsource );
   1170                 return amodem_printf( modem, "+CCSS: %d", modem->subscription_source );
   1171                 break;
   1172         }
   1173     }
   1174     return amodem_printf( modem, "ERROR: Invalid subscription source");
   1175 }
   1176 
   1177 static const char*
   1178 handleRoamPref( const char * cmd, AModem modem )
   1179 {
   1180     int roaming_pref = -1;
   1181     char *endptr = NULL;
   1182     D("handleRoamPref(%s)\n", cmd);
   1183     assert( !memcmp( "+WRMP", cmd, 5 ) );
   1184     cmd += 5;
   1185     if (cmd[0] == '?') {
   1186         return amodem_printf( modem, "+WRMP: %d", modem->roaming_pref );
   1187     }
   1188 
   1189     if (!strcmp( cmd, "=?")) {
   1190         return amodem_printf( modem, "+WRMP: 0,1,2" );
   1191     } else if (cmd[0] == '=') {
   1192         cmd ++;
   1193         roaming_pref = strtol( cmd, &endptr, 10 );
   1194          // Make sure the rest of the command is the number
   1195          // (if *endptr is null, it means strtol processed the whole string as a number)
   1196         if(endptr && !*endptr) {
   1197             modem->roaming_pref = roaming_pref;
   1198             aconfig_set( modem->nvram_config, NV_CDMA_ROAMING_PREF, cmd );
   1199             aconfig_save_file( modem->nvram_config, modem->nvram_config_filename );
   1200             return NULL;
   1201         }
   1202     }
   1203     return amodem_printf( modem, "ERROR");
   1204 }
   1205 static const char*
   1206 handleTech( const char*  cmd, AModem  modem )
   1207 {
   1208     AModemTech newtech = modem->technology;
   1209     int pt = modem->preferred_mask;
   1210     int havenewtech = 0;
   1211     D("handleTech. cmd: %s\n", cmd);
   1212     assert( !memcmp( "+CTEC", cmd, 5 ) );
   1213     cmd += 5;
   1214     if (cmd[0] == '?') {
   1215         return amodem_printf( modem, "+CTEC: %d,%x",modem->technology, modem->preferred_mask );
   1216     }
   1217     amodem_begin_line( modem );
   1218     if (!strcmp( cmd, "=?")) {
   1219         return amodem_printf( modem, "+CTEC: 0,1,2,3" );
   1220     }
   1221     else if (cmd[0] == '=') {
   1222         switch (cmd[1]) {
   1223             case '0':
   1224             case '1':
   1225             case '2':
   1226             case '3':
   1227                 havenewtech = 1;
   1228                 newtech = cmd[1] - '0';
   1229                 cmd += 1;
   1230                 break;
   1231         }
   1232         cmd += 1;
   1233     }
   1234     if (havenewtech) {
   1235         D( "cmd: %s\n", cmd );
   1236         if (cmd[0] == ',' && ! parsePreferred( ++cmd, &pt ))
   1237             return amodem_printf( modem, "ERROR: invalid preferred mode" );
   1238         return _amodem_switch_technology( modem, newtech, pt );
   1239     }
   1240     return amodem_printf( modem, "ERROR: %s: Unknown Technology", cmd + 1 );
   1241 }
   1242 
   1243 static const char*
   1244 handleEmergencyMode( const char* cmd, AModem modem )
   1245 {
   1246     long arg;
   1247     char *endptr = NULL;
   1248     assert ( !memcmp( "+WSOS", cmd, 5 ) );
   1249     cmd += 5;
   1250     if (cmd[0] == '?') {
   1251         return amodem_printf( modem, "+WSOS: %d", modem->in_emergency_mode);
   1252     }
   1253 
   1254     if (cmd[0] == '=') {
   1255         if (cmd[1] == '?') {
   1256             return amodem_printf(modem, "+WSOS: (0)");
   1257         }
   1258         if (cmd[1] == 0) {
   1259             return amodem_printf(modem, "ERROR");
   1260         }
   1261         arg = strtol(cmd+1, &endptr, 10);
   1262 
   1263         if (!endptr || endptr[0] != 0) {
   1264             return amodem_printf(modem, "ERROR");
   1265         }
   1266 
   1267         arg = arg? 1 : 0;
   1268 
   1269         if ((!arg) != (!modem->in_emergency_mode)) {
   1270             modem->in_emergency_mode = arg;
   1271             return amodem_printf(modem, "+WSOS: %d", arg);
   1272         }
   1273     }
   1274     return amodem_printf(modem, "ERROR");
   1275 }
   1276 
   1277 static const char*
   1278 handlePrlVersion( const char* cmd, AModem modem )
   1279 {
   1280     assert ( !memcmp( "+WPRL", cmd, 5 ) );
   1281     cmd += 5;
   1282     if (cmd[0] == '?') {
   1283         return amodem_printf( modem, "+WPRL: %d", modem->prl_version);
   1284     }
   1285 
   1286     return amodem_printf(modem, "ERROR");
   1287 }
   1288 
   1289 static const char*
   1290 handleRadioPower( const char*  cmd, AModem  modem )
   1291 {
   1292     if ( !strcmp( cmd, "+CFUN=0" ) )
   1293     {
   1294         /* turn radio off */
   1295         modem->radio_state = A_RADIO_STATE_OFF;
   1296     }
   1297     else if ( !strcmp( cmd, "+CFUN=1" ) )
   1298     {
   1299         /* turn radio on */
   1300         modem->radio_state = A_RADIO_STATE_ON;
   1301     }
   1302     return NULL;
   1303 }
   1304 
   1305 static const char*
   1306 handleRadioPowerReq( const char*  cmd, AModem  modem )
   1307 {
   1308     if (modem->radio_state != A_RADIO_STATE_OFF)
   1309         return "+CFUN: 1";
   1310     else
   1311         return "+CFUN: 0";
   1312 }
   1313 
   1314 static const char*
   1315 handleSIMStatusReq( const char*  cmd, AModem  modem )
   1316 {
   1317     const char*  answer = NULL;
   1318 
   1319     switch (asimcard_get_status(modem->sim)) {
   1320         case A_SIM_STATUS_ABSENT:    answer = "+CPIN: ABSENT"; break;
   1321         case A_SIM_STATUS_READY:     answer = "+CPIN: READY"; break;
   1322         case A_SIM_STATUS_NOT_READY: answer = "+CMERROR: NOT READY"; break;
   1323         case A_SIM_STATUS_PIN:       answer = "+CPIN: SIM PIN"; break;
   1324         case A_SIM_STATUS_PUK:       answer = "+CPIN: SIM PUK"; break;
   1325         case A_SIM_STATUS_NETWORK_PERSONALIZATION: answer = "+CPIN: PH-NET PIN"; break;
   1326         default:
   1327             answer = "ERROR: internal error";
   1328     }
   1329     return answer;
   1330 }
   1331 
   1332 /* TODO: Will we need this?
   1333 static const char*
   1334 handleSRegister( const char * cmd, AModem modem )
   1335 {
   1336     char *end;
   1337     assert( cmd[0] == 'S' || cmd[0] == 's' );
   1338 
   1339     ++ cmd;
   1340 
   1341     int l = strtol(cmd, &end, 10);
   1342 } */
   1343 
   1344 static const char*
   1345 handleNetworkRegistration( const char*  cmd, AModem  modem )
   1346 {
   1347     if ( !memcmp( cmd, "+CREG", 5 ) ) {
   1348         cmd += 5;
   1349         if (cmd[0] == '?') {
   1350             if (modem->voice_mode == A_REGISTRATION_UNSOL_ENABLED_FULL)
   1351                 return amodem_printf( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"",
   1352                                        modem->voice_mode, modem->voice_state,
   1353                                        modem->area_code, modem->cell_id );
   1354             else
   1355                 return amodem_printf( modem, "+CREG: %d,%d",
   1356                                        modem->voice_mode, modem->voice_state );
   1357         } else if (cmd[0] == '=') {
   1358             switch (cmd[1]) {
   1359                 case '0':
   1360                     modem->voice_mode  = A_REGISTRATION_UNSOL_DISABLED;
   1361                     break;
   1362 
   1363                 case '1':
   1364                     modem->voice_mode  = A_REGISTRATION_UNSOL_ENABLED;
   1365                     break;
   1366 
   1367                 case '2':
   1368                     modem->voice_mode = A_REGISTRATION_UNSOL_ENABLED_FULL;
   1369                     break;
   1370 
   1371                 case '?':
   1372                     return "+CREG: (0-2)";
   1373 
   1374                 default:
   1375                     return "ERROR: BAD COMMAND";
   1376             }
   1377         } else {
   1378             assert( 0 && "unreachable" );
   1379         }
   1380     } else if ( !memcmp( cmd, "+CGREG", 6 ) ) {
   1381         cmd += 6;
   1382         if (cmd[0] == '?') {
   1383             if (modem->supportsNetworkDataType)
   1384                 return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"",
   1385                                     modem->data_mode, modem->data_state,
   1386                                     modem->area_code, modem->cell_id,
   1387                                     modem->data_network );
   1388             else
   1389                 return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\"",
   1390                                     modem->data_mode, modem->data_state,
   1391                                     modem->area_code, modem->cell_id );
   1392         } else if (cmd[0] == '=') {
   1393             switch (cmd[1]) {
   1394                 case '0':
   1395                     modem->data_mode  = A_REGISTRATION_UNSOL_DISABLED;
   1396                     break;
   1397 
   1398                 case '1':
   1399                     modem->data_mode  = A_REGISTRATION_UNSOL_ENABLED;
   1400                     break;
   1401 
   1402                 case '2':
   1403                     modem->data_mode = A_REGISTRATION_UNSOL_ENABLED_FULL;
   1404                     break;
   1405 
   1406                 case '?':
   1407                     return "+CGREG: (0-2)";
   1408 
   1409                 default:
   1410                     return "ERROR: BAD COMMAND";
   1411             }
   1412         } else {
   1413             assert( 0 && "unreachable" );
   1414         }
   1415     }
   1416     return NULL;
   1417 }
   1418 
   1419 static const char*
   1420 handleSetDialTone( const char*  cmd, AModem  modem )
   1421 {
   1422     /* XXX: TODO */
   1423     return NULL;
   1424 }
   1425 
   1426 static const char*
   1427 handleDeleteSMSonSIM( const char*  cmd, AModem  modem )
   1428 {
   1429     /* XXX: TODO */
   1430     return NULL;
   1431 }
   1432 
   1433 static const char*
   1434 handleSIM_IO( const char*  cmd, AModem  modem )
   1435 {
   1436     return asimcard_io( modem->sim, cmd );
   1437 }
   1438 
   1439 
   1440 static const char*
   1441 handleOperatorSelection( const char*  cmd, AModem  modem )
   1442 {
   1443     assert( !memcmp( "+COPS", cmd, 5 ) );
   1444     cmd += 5;
   1445     if (cmd[0] == '?') { /* ask for current operator */
   1446         AOperator  oper = &modem->operators[ modem->oper_index ];
   1447 
   1448         if ( !amodem_has_network( modem ) )
   1449         {
   1450             /* this error code means "no network" */
   1451             return amodem_printf( modem, "+CME ERROR: 30" );
   1452         }
   1453 
   1454         oper = &modem->operators[ modem->oper_index ];
   1455 
   1456         if ( modem->oper_name_index == 2 )
   1457             return amodem_printf( modem, "+COPS: %d,2,%s",
   1458                                   modem->oper_selection_mode,
   1459                                   oper->name[2] );
   1460 
   1461         return amodem_printf( modem, "+COPS: %d,%d,\"%s\"",
   1462                               modem->oper_selection_mode,
   1463                               modem->oper_name_index,
   1464                               oper->name[ modem->oper_name_index ] );
   1465     }
   1466     else if (cmd[0] == '=' && cmd[1] == '?') {  /* ask for all available operators */
   1467         const char*  comma = "+COPS: ";
   1468         int          nn;
   1469         amodem_begin_line( modem );
   1470         for (nn = 0; nn < modem->oper_count; nn++) {
   1471             AOperator  oper = &modem->operators[nn];
   1472             amodem_add_line( modem, "%s(%d,\"%s\",\"%s\",\"%s\")", comma,
   1473                              oper->status, oper->name[0], oper->name[1], oper->name[2] );
   1474             comma = ", ";
   1475         }
   1476         return amodem_end_line( modem );
   1477     }
   1478     else if (cmd[0] == '=') {
   1479         switch (cmd[1]) {
   1480             case '0':
   1481                 modem->oper_selection_mode = A_SELECTION_AUTOMATIC;
   1482                 return NULL;
   1483 
   1484             case '1':
   1485                 {
   1486                     int  format, nn, len, found = -1;
   1487 
   1488                     if (cmd[2] != ',')
   1489                         goto BadCommand;
   1490                     format = cmd[3] - '0';
   1491                     if ( (unsigned)format > 2 )
   1492                         goto BadCommand;
   1493                     if (cmd[4] != ',')
   1494                         goto BadCommand;
   1495                     cmd += 5;
   1496                     len  = strlen(cmd);
   1497                     if (*cmd == '"') {
   1498                         cmd++;
   1499                         len -= 2;
   1500                     }
   1501                     if (len <= 0)
   1502                         goto BadCommand;
   1503 
   1504                     for (nn = 0; nn < modem->oper_count; nn++) {
   1505                         AOperator    oper = modem->operators + nn;
   1506                         char*        name = oper->name[ format ];
   1507 
   1508                         if ( !memcpy( name, cmd, len ) && name[len] == 0 ) {
   1509                             found = nn;
   1510                             break;
   1511                         }
   1512                     }
   1513 
   1514                     if (found < 0) {
   1515                         /* Selection failed */
   1516                         return "+CME ERROR: 529";
   1517                     } else if (modem->operators[found].status == A_STATUS_DENIED) {
   1518                         /* network not allowed */
   1519                         return "+CME ERROR: 32";
   1520                     }
   1521                     modem->oper_index = found;
   1522 
   1523                     /* set the voice and data registration states to home or roaming
   1524                      * depending on the operator index
   1525                      */
   1526                     if (found == OPERATOR_HOME_INDEX) {
   1527                         modem->voice_state = A_REGISTRATION_HOME;
   1528                         modem->data_state  = A_REGISTRATION_HOME;
   1529                     } else if (found == OPERATOR_ROAMING_INDEX) {
   1530                         modem->voice_state = A_REGISTRATION_ROAMING;
   1531                         modem->data_state  = A_REGISTRATION_ROAMING;
   1532                     }
   1533                     return NULL;
   1534                 }
   1535 
   1536             case '2':
   1537                 modem->oper_selection_mode = A_SELECTION_DEREGISTRATION;
   1538                 return NULL;
   1539 
   1540             case '3':
   1541                 {
   1542                     int format;
   1543 
   1544                     if (cmd[2] != ',')
   1545                         goto BadCommand;
   1546 
   1547                     format = cmd[3] - '0';
   1548                     if ( (unsigned)format > 2 )
   1549                         goto BadCommand;
   1550 
   1551                     modem->oper_name_index = format;
   1552                     return NULL;
   1553                 }
   1554             default:
   1555                 ;
   1556         }
   1557     }
   1558 BadCommand:
   1559     return unknownCommand(cmd,modem);
   1560 }
   1561 
   1562 static const char*
   1563 handleRequestOperator( const char*  cmd, AModem  modem )
   1564 {
   1565     AOperator  oper;
   1566     cmd=cmd;
   1567 
   1568     if ( !amodem_has_network(modem) )
   1569         return "+CME ERROR: 30";
   1570 
   1571     oper = modem->operators + modem->oper_index;
   1572     modem->oper_name_index = 2;
   1573     return amodem_printf( modem, "+COPS: 0,0,\"%s\"\r"
   1574                           "+COPS: 0,1,\"%s\"\r"
   1575                           "+COPS: 0,2,\"%s\"",
   1576                           oper->name[0], oper->name[1], oper->name[2] );
   1577 }
   1578 
   1579 static const char*
   1580 handleSendSMStoSIM( const char*  cmd, AModem  modem )
   1581 {
   1582     /* XXX: TODO */
   1583     return "ERROR: unimplemented";
   1584 }
   1585 
   1586 static const char*
   1587 handleSendSMS( const char*  cmd, AModem  modem )
   1588 {
   1589     modem->wait_sms = 1;
   1590     return "> ";
   1591 }
   1592 
   1593 #if 0
   1594 static void
   1595 sms_address_dump( SmsAddress  address, FILE*  out )
   1596 {
   1597     int  nn, len = address->len;
   1598 
   1599     if (address->toa == 0x91) {
   1600         fprintf( out, "+" );
   1601     }
   1602     for (nn = 0; nn < len; nn += 2)
   1603     {
   1604         static const char  dialdigits[16] = "0123456789*#,N%";
   1605         int  c = address->data[nn/2];
   1606 
   1607         fprintf( out, "%c", dialdigits[c & 0xf] );
   1608         if (nn+1 >= len)
   1609             break;
   1610 
   1611         fprintf( out, "%c", dialdigits[(c >> 4) & 0xf] );
   1612     }
   1613 }
   1614 
   1615 static void
   1616 smspdu_dump( SmsPDU  pdu, FILE*  out )
   1617 {
   1618     SmsAddressRec    address;
   1619     unsigned char    temp[256];
   1620     int              len;
   1621 
   1622     if (pdu == NULL) {
   1623         fprintf( out, "SMS PDU is (null)\n" );
   1624         return;
   1625     }
   1626 
   1627     fprintf( out, "SMS PDU type:       " );
   1628     switch (smspdu_get_type(pdu)) {
   1629         case SMS_PDU_DELIVER: fprintf(out, "DELIVER"); break;
   1630         case SMS_PDU_SUBMIT:  fprintf(out, "SUBMIT"); break;
   1631         case SMS_PDU_STATUS_REPORT: fprintf(out, "STATUS_REPORT"); break;
   1632         default: fprintf(out, "UNKNOWN");
   1633     }
   1634     fprintf( out, "\n        sender:   " );
   1635     if (smspdu_get_sender_address(pdu, &address) < 0)
   1636         fprintf( out, "(N/A)" );
   1637     else
   1638         sms_address_dump(&address, out);
   1639     fprintf( out, "\n        receiver: " );
   1640     if (smspdu_get_receiver_address(pdu, &address) < 0)
   1641         fprintf(out, "(N/A)");
   1642     else
   1643         sms_address_dump(&address, out);
   1644     fprintf( out, "\n        text:     " );
   1645     len = smspdu_get_text_message( pdu, temp, sizeof(temp)-1 );
   1646     if (len > sizeof(temp)-1 )
   1647         len = sizeof(temp)-1;
   1648     fprintf( out, "'%.*s'\n", len, temp );
   1649 }
   1650 #endif
   1651 
   1652 static const char*
   1653 handleSendSMSText( const char*  cmd, AModem  modem )
   1654 {
   1655 #if 1
   1656     SmsAddressRec  address;
   1657     char           temp[16];
   1658     char           number[16];
   1659     int            numlen;
   1660     int            len = strlen(cmd);
   1661     SmsPDU         pdu;
   1662 
   1663     /* get rid of trailing escape */
   1664     if (len > 0 && cmd[len-1] == 0x1a)
   1665         len -= 1;
   1666 
   1667     pdu = smspdu_create_from_hex( cmd, len );
   1668     if (pdu == NULL) {
   1669         D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd);
   1670         return "+CMS ERROR: INVALID SMS PDU";
   1671     }
   1672     if (smspdu_get_receiver_address(pdu, &address) < 0) {
   1673         D("%s: could not get SMS receiver address from '%s'\n",
   1674           __FUNCTION__, cmd);
   1675         return "+CMS ERROR: BAD SMS RECEIVER ADDRESS";
   1676     }
   1677 
   1678     do {
   1679         int  index;
   1680 
   1681         numlen = sms_address_to_str( &address, temp, sizeof(temp) );
   1682         if (numlen > sizeof(temp)-1)
   1683             break;
   1684         temp[numlen] = 0;
   1685 
   1686         /* Converts 4, 7, and 10 digits number to 11 digits */
   1687         if (numlen == 10 && !strncmp(temp, PHONE_PREFIX+1, 6)) {
   1688             memcpy( number, PHONE_PREFIX, 1 );
   1689             memcpy( number+1, temp, numlen );
   1690             number[numlen+1] = 0;
   1691         } else if (numlen == 7 && !strncmp(temp, PHONE_PREFIX+4, 3)) {
   1692             memcpy( number, PHONE_PREFIX, 4 );
   1693             memcpy( number+4, temp, numlen );
   1694             number[numlen+4] = 0;
   1695         } else if (numlen == 4) {
   1696             memcpy( number, PHONE_PREFIX, 7 );
   1697             memcpy( number+7, temp, numlen );
   1698             number[numlen+7] = 0;
   1699         } else {
   1700             memcpy( number, temp, numlen );
   1701             number[numlen] = 0;
   1702         }
   1703 
   1704         if ( remote_number_string_to_port( number ) < 0 )
   1705             break;
   1706 
   1707         if (modem->sms_receiver == NULL) {
   1708             modem->sms_receiver = sms_receiver_create();
   1709             if (modem->sms_receiver == NULL) {
   1710                 D( "%s: could not create SMS receiver\n", __FUNCTION__ );
   1711                 break;
   1712             }
   1713         }
   1714 
   1715         index = sms_receiver_add_submit_pdu( modem->sms_receiver, pdu );
   1716         if (index < 0) {
   1717             D( "%s: could not add submit PDU\n", __FUNCTION__ );
   1718             break;
   1719         }
   1720         /* the PDU is now owned by the receiver */
   1721         pdu = NULL;
   1722 
   1723         if (index > 0) {
   1724             SmsAddressRec  from[1];
   1725             char           temp[12];
   1726             SmsPDU*        deliver;
   1727             int            nn;
   1728 
   1729             snprintf( temp, sizeof(temp), PHONE_PREFIX "%d", modem->base_port );
   1730             sms_address_from_str( from, temp, strlen(temp) );
   1731 
   1732             deliver = sms_receiver_create_deliver( modem->sms_receiver, index, from );
   1733             if (deliver == NULL) {
   1734                 D( "%s: could not create deliver PDUs for SMS index %d\n",
   1735                    __FUNCTION__, index );
   1736                 break;
   1737             }
   1738 
   1739             for (nn = 0; deliver[nn] != NULL; nn++) {
   1740                 if ( remote_call_sms( number, modem->base_port, deliver[nn] ) < 0 ) {
   1741                     D( "%s: could not send SMS PDU to remote emulator\n",
   1742                        __FUNCTION__ );
   1743                     break;
   1744                 }
   1745             }
   1746 
   1747             smspdu_free_list(deliver);
   1748         }
   1749 
   1750     } while (0);
   1751 
   1752     if (pdu != NULL)
   1753         smspdu_free(pdu);
   1754 
   1755 #elif 1
   1756     SmsAddressRec  address;
   1757     char           number[16];
   1758     int            numlen;
   1759     int            len = strlen(cmd);
   1760     SmsPDU         pdu;
   1761 
   1762     /* get rid of trailing escape */
   1763     if (len > 0 && cmd[len-1] == 0x1a)
   1764         len -= 1;
   1765 
   1766     pdu = smspdu_create_from_hex( cmd, len );
   1767     if (pdu == NULL) {
   1768         D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd);
   1769         return "+CMS ERROR: INVALID SMS PDU";
   1770     }
   1771     if (smspdu_get_receiver_address(pdu, &address) < 0) {
   1772         D("%s: could not get SMS receiver address from '%s'\n",
   1773           __FUNCTION__, cmd);
   1774         return "+CMS ERROR: BAD SMS RECEIVER ADDRESS";
   1775     }
   1776     do {
   1777         numlen = sms_address_to_str( &address, number, sizeof(number) );
   1778         if (numlen > sizeof(number)-1)
   1779             break;
   1780 
   1781         number[numlen] = 0;
   1782         if ( remote_number_string_to_port( number ) < 0 )
   1783             break;
   1784 
   1785         if ( remote_call_sms( number, modem->base_port, pdu ) < 0 )
   1786         {
   1787             D("%s: could not send SMS PDU to remote emulator\n",
   1788               __FUNCTION__);
   1789             return "+CMS ERROR: NO EMULATOR RECEIVER";
   1790         }
   1791     } while (0);
   1792 #else
   1793     fprintf(stderr, "SMS<< %s\n", cmd);
   1794     SmsPDU  pdu = smspdu_create_from_hex( cmd, strlen(cmd) );
   1795     if (pdu == NULL) {
   1796         fprintf(stderr, "invalid SMS PDU ?: '%s'\n", cmd);
   1797     } else {
   1798         smspdu_dump(pdu, stderr);
   1799     }
   1800 #endif
   1801     return "+CMGS: 0\rOK\r";
   1802 }
   1803 
   1804 static const char*
   1805 handleChangeOrEnterPIN( const char*  cmd, AModem  modem )
   1806 {
   1807     assert( !memcmp( cmd, "+CPIN=", 6 ) );
   1808     cmd += 6;
   1809 
   1810     switch (asimcard_get_status(modem->sim)) {
   1811         case A_SIM_STATUS_ABSENT:
   1812             return "+CME ERROR: SIM ABSENT";
   1813 
   1814         case A_SIM_STATUS_NOT_READY:
   1815             return "+CME ERROR: SIM NOT READY";
   1816 
   1817         case A_SIM_STATUS_READY:
   1818             /* this may be a request to change the PIN */
   1819             {
   1820                 if (strlen(cmd) == 9 && cmd[4] == ',') {
   1821                     char  pin[5];
   1822                     memcpy( pin, cmd, 4 ); pin[4] = 0;
   1823 
   1824                     if ( !asimcard_check_pin( modem->sim, pin ) )
   1825                         return "+CME ERROR: BAD PIN";
   1826 
   1827                     memcpy( pin, cmd+5, 4 );
   1828                     asimcard_set_pin( modem->sim, pin );
   1829                     return "+CPIN: READY";
   1830                 }
   1831             }
   1832             break;
   1833 
   1834         case A_SIM_STATUS_PIN:   /* waiting for PIN */
   1835             if ( asimcard_check_pin( modem->sim, cmd ) )
   1836                 return "+CPIN: READY";
   1837             else
   1838                 return "+CME ERROR: BAD PIN";
   1839 
   1840         case A_SIM_STATUS_PUK:
   1841             if (strlen(cmd) == 9 && cmd[4] == ',') {
   1842                 char  puk[5];
   1843                 memcpy( puk, cmd, 4 );
   1844                 puk[4] = 0;
   1845                 if ( asimcard_check_puk( modem->sim, puk, cmd+5 ) )
   1846                     return "+CPIN: READY";
   1847                 else
   1848                     return "+CME ERROR: BAD PUK";
   1849             }
   1850             return "+CME ERROR: BAD PUK";
   1851 
   1852         default:
   1853             return "+CPIN: PH-NET PIN";
   1854     }
   1855 
   1856     return "+CME ERROR: BAD FORMAT";
   1857 }
   1858 
   1859 
   1860 static const char*
   1861 handleListCurrentCalls( const char*  cmd, AModem  modem )
   1862 {
   1863     int  nn;
   1864     amodem_begin_line( modem );
   1865     for (nn = 0; nn < modem->call_count; nn++) {
   1866         AVoiceCall  vcall = modem->calls + nn;
   1867         ACall       call  = &vcall->call;
   1868         if (call->mode == A_CALL_VOICE)
   1869             amodem_add_line( modem, "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n",
   1870                              call->id, call->dir, call->state, call->mode,
   1871                              call->multi, call->number, 129 );
   1872     }
   1873     return amodem_end_line( modem );
   1874 }
   1875 
   1876 /* Add a(n unsolicited) time response.
   1877  *
   1878  * retrieve the current time and zone in a format suitable
   1879  * for %CTZV: unsolicited message
   1880  *  "yy/mm/dd,hh:mm:ss(+/-)tz"
   1881  *   mm is 0-based
   1882  *   tz is in number of quarter-hours
   1883  *
   1884  * it seems reference-ril doesn't parse the comma (,) as anything else than a token
   1885  * separator, so use a column (:) instead, the Java parsing code won't see a difference
   1886  *
   1887  */
   1888 static void
   1889 amodem_addTimeUpdate( AModem  modem )
   1890 {
   1891     time_t       now = time(NULL);
   1892     struct tm    utc, local;
   1893     long         e_local, e_utc;
   1894     long         tzdiff;
   1895     char         tzname[64];
   1896 
   1897     tzset();
   1898 
   1899     utc   = *gmtime( &now );
   1900     local = *localtime( &now );
   1901 
   1902     e_local = local.tm_min + 60*(local.tm_hour + 24*local.tm_yday);
   1903     e_utc   = utc.tm_min   + 60*(utc.tm_hour   + 24*utc.tm_yday);
   1904 
   1905     if ( utc.tm_year < local.tm_year )
   1906         e_local += 24*60;
   1907     else if ( utc.tm_year > local.tm_year )
   1908         e_utc += 24*60;
   1909 
   1910     tzdiff = e_local - e_utc;  /* timezone offset in minutes */
   1911 
   1912    /* retrieve a zoneinfo-compatible name for the host timezone
   1913     */
   1914     {
   1915         char*  end = tzname + sizeof(tzname);
   1916         char*  p = bufprint_zoneinfo_timezone( tzname, end );
   1917         if (p >= end)
   1918             strcpy(tzname, "Unknown/Unknown");
   1919 
   1920         /* now replace every / in the timezone name by a "!"
   1921          * that's because the code that reads the CTZV line is
   1922          * dumb and treats a / as a field separator...
   1923          */
   1924         p = tzname;
   1925         while (1) {
   1926             p = strchr(p, '/');
   1927             if (p == NULL)
   1928                 break;
   1929             *p = '!';
   1930             p += 1;
   1931         }
   1932     }
   1933 
   1934    /* as a special extension, we append the name of the host's time zone to the
   1935     * string returned with %CTZ. the system should contain special code to detect
   1936     * and deal with this case (since it normally relied on the operator's country code
   1937     * which is hard to simulate on a general-purpose computer
   1938     */
   1939     amodem_add_line( modem, "%%CTZV: %02d/%02d/%02d:%02d:%02d:%02d%c%d:%d:%s\r\n",
   1940              (utc.tm_year + 1900) % 100, utc.tm_mon + 1, utc.tm_mday,
   1941              utc.tm_hour, utc.tm_min, utc.tm_sec,
   1942              (tzdiff >= 0) ? '+' : '-', (tzdiff >= 0 ? tzdiff : -tzdiff) / 15,
   1943              (local.tm_isdst > 0),
   1944              tzname );
   1945 }
   1946 
   1947 static const char*
   1948 handleEndOfInit( const char*  cmd, AModem  modem )
   1949 {
   1950     amodem_begin_line( modem );
   1951     amodem_addTimeUpdate( modem );
   1952     return amodem_end_line( modem );
   1953 }
   1954 
   1955 
   1956 static const char*
   1957 handleListPDPContexts( const char*  cmd, AModem  modem )
   1958 {
   1959     int  nn;
   1960     assert( !memcmp( cmd, "+CGACT?", 7 ) );
   1961     amodem_begin_line( modem );
   1962     for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) {
   1963         ADataContext  data = modem->data_contexts + nn;
   1964         if (!data->active)
   1965             continue;
   1966         amodem_add_line( modem, "+CGACT: %d,%d\r\n", data->id, data->active );
   1967     }
   1968     return amodem_end_line( modem );
   1969 }
   1970 
   1971 static const char*
   1972 handleDefinePDPContext( const char*  cmd, AModem  modem )
   1973 {
   1974     assert( !memcmp( cmd, "+CGDCONT=", 9 ) );
   1975     cmd += 9;
   1976     if (cmd[0] == '?') {
   1977         /* +CGDCONT=? is used to query the ranges of supported PDP Contexts.
   1978          * We only really support IP ones in the emulator, so don't try to
   1979          * fake PPP ones.
   1980          */
   1981         return "+CGDCONT: (1-1),\"IP\",,,(0-2),(0-4)\r\n";
   1982     } else {
   1983         /* template is +CGDCONT=<id>,"<type>","<apn>",,0,0 */
   1984         int              id = cmd[0] - '1';
   1985         ADataType        type;
   1986         char             apn[32];
   1987         ADataContext     data;
   1988 
   1989         if ((unsigned)id > 3)
   1990             goto BadCommand;
   1991 
   1992         if ( !memcmp( cmd+1, ",\"IP\",\"", 7 ) ) {
   1993             type = A_DATA_IP;
   1994             cmd += 8;
   1995         } else if ( !memcmp( cmd+1, ",\"PPP\",\"", 8 ) ) {
   1996             type = A_DATA_PPP;
   1997             cmd += 9;
   1998         } else
   1999             goto BadCommand;
   2000 
   2001         {
   2002             const char*  p = strchr( cmd, '"' );
   2003             int          len;
   2004             if (p == NULL)
   2005                 goto BadCommand;
   2006             len = (int)( p - cmd );
   2007             if (len > sizeof(apn)-1 )
   2008                 len = sizeof(apn)-1;
   2009             memcpy( apn, cmd, len );
   2010             apn[len] = 0;
   2011         }
   2012 
   2013         data = modem->data_contexts + id;
   2014 
   2015         data->id     = id + 1;
   2016         data->active = 1;
   2017         data->type   = type;
   2018         memcpy( data->apn, apn, sizeof(data->apn) );
   2019     }
   2020     return NULL;
   2021 BadCommand:
   2022     return "ERROR: BAD COMMAND";
   2023 }
   2024 
   2025 static const char*
   2026 handleQueryPDPContext( const char* cmd, AModem modem )
   2027 {
   2028     int  nn;
   2029     amodem_begin_line(modem);
   2030     for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) {
   2031         ADataContext  data = modem->data_contexts + nn;
   2032         if (!data->active)
   2033             continue;
   2034         amodem_add_line( modem, "+CGDCONT: %d,\"%s\",\"%s\",\"%s\",0,0\r\n",
   2035                          data->id,
   2036                          data->type == A_DATA_IP ? "IP" : "PPP",
   2037                          data->apn,
   2038                          /* Note: For now, hard-code the IP address of our
   2039                           *       network interface
   2040                           */
   2041                          data->type == A_DATA_IP ? "10.0.2.15" : "");
   2042     }
   2043     return amodem_end_line(modem);
   2044 }
   2045 
   2046 static const char*
   2047 handleStartPDPContext( const char*  cmd, AModem  modem )
   2048 {
   2049     /* XXX: TODO: handle PDP start appropriately */
   2050     return NULL;
   2051 }
   2052 
   2053 
   2054 static void
   2055 remote_voice_call_event( void*  _vcall, int  success )
   2056 {
   2057     AVoiceCall  vcall = _vcall;
   2058     AModem      modem = vcall->modem;
   2059 
   2060     /* NOTE: success only means we could send the "gsm in new" command
   2061      * to the remote emulator, nothing more */
   2062 
   2063     if (!success) {
   2064         /* aargh, the remote emulator probably quitted at that point */
   2065         amodem_free_call(modem, vcall);
   2066         amodem_send_calls_update(modem);
   2067     }
   2068 }
   2069 
   2070 
   2071 static void
   2072 voice_call_event( void*  _vcall )
   2073 {
   2074     AVoiceCall  vcall = _vcall;
   2075     ACall       call  = &vcall->call;
   2076 
   2077     switch (call->state) {
   2078         case A_CALL_DIALING:
   2079             call->state = A_CALL_ALERTING;
   2080 
   2081             if (vcall->is_remote) {
   2082                 if ( remote_call_dial( call->number,
   2083                                        vcall->modem->base_port,
   2084                                        remote_voice_call_event, vcall ) < 0 )
   2085                 {
   2086                    /* we could not connect, probably because the corresponding
   2087                     * emulator is not running, so simply destroy this call.
   2088                     * XXX: should we send some sort of message to indicate BAD NUMBER ? */
   2089                     /* it seems the Android code simply waits for changes in the list   */
   2090                     amodem_free_call( vcall->modem, vcall );
   2091                 }
   2092             } else {
   2093                /* this is not a remote emulator number, so just simulate
   2094                 * a small ringing delay */
   2095                 sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_ALERT,
   2096                                voice_call_event, vcall );
   2097             }
   2098             break;
   2099 
   2100         case A_CALL_ALERTING:
   2101             call->state = A_CALL_ACTIVE;
   2102             break;
   2103 
   2104         default:
   2105             assert( 0 && "unreachable event call state" );
   2106     }
   2107     amodem_send_calls_update(vcall->modem);
   2108 }
   2109 
   2110 static int amodem_is_emergency( AModem modem, const char *number )
   2111 {
   2112     int i;
   2113 
   2114     if (!number) return 0;
   2115     for (i = 0; i < MAX_EMERGENCY_NUMBERS; i++) {
   2116         if ( modem->emergency_numbers[i] && !strcmp( number, modem->emergency_numbers[i] )) break;
   2117     }
   2118 
   2119     if (i < MAX_EMERGENCY_NUMBERS) return 1;
   2120 
   2121     return 0;
   2122 }
   2123 
   2124 static const char*
   2125 handleDial( const char*  cmd, AModem  modem )
   2126 {
   2127     AVoiceCall  vcall = amodem_alloc_call( modem );
   2128     ACall       call  = &vcall->call;
   2129     int         len;
   2130 
   2131     if (call == NULL)
   2132         return "ERROR: TOO MANY CALLS";
   2133 
   2134     assert( cmd[0] == 'D' );
   2135     call->dir   = A_CALL_OUTBOUND;
   2136     call->state = A_CALL_DIALING;
   2137     call->mode  = A_CALL_VOICE;
   2138     call->multi = 0;
   2139 
   2140     cmd += 1;
   2141     len  = strlen(cmd);
   2142     if (len > 0 && cmd[len-1] == ';')
   2143         len--;
   2144     if (len >= sizeof(call->number))
   2145         len = sizeof(call->number)-1;
   2146 
   2147     /* Converts 4, 7, and 10 digits number to 11 digits */
   2148     if (len == 10 && !strncmp(cmd, PHONE_PREFIX+1, 6)) {
   2149         memcpy( call->number, PHONE_PREFIX, 1 );
   2150         memcpy( call->number+1, cmd, len );
   2151         call->number[len+1] = 0;
   2152     } else if (len == 7 && !strncmp(cmd, PHONE_PREFIX+4, 3)) {
   2153         memcpy( call->number, PHONE_PREFIX, 4 );
   2154         memcpy( call->number+4, cmd, len );
   2155         call->number[len+4] = 0;
   2156     } else if (len == 4) {
   2157         memcpy( call->number, PHONE_PREFIX, 7 );
   2158         memcpy( call->number+7, cmd, len );
   2159         call->number[len+7] = 0;
   2160     } else {
   2161         memcpy( call->number, cmd, len );
   2162         call->number[len] = 0;
   2163     }
   2164 
   2165     amodem_begin_line( modem );
   2166     if (amodem_is_emergency(modem, call->number)) {
   2167         modem->in_emergency_mode = 1;
   2168         amodem_add_line( modem, "+WSOS: 1" );
   2169     }
   2170     vcall->is_remote = (remote_number_string_to_port(call->number) > 0);
   2171 
   2172     vcall->timer = sys_timer_create();
   2173     sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_DIAL,
   2174                    voice_call_event, vcall );
   2175 
   2176     return amodem_end_line( modem );
   2177 }
   2178 
   2179 
   2180 static const char*
   2181 handleAnswer( const char*  cmd, AModem  modem )
   2182 {
   2183     int  nn;
   2184     for (nn = 0; nn < modem->call_count; nn++) {
   2185         AVoiceCall  vcall = modem->calls + nn;
   2186         ACall       call  = &vcall->call;
   2187 
   2188         if (cmd[0] == 'A') {
   2189             if (call->state == A_CALL_INCOMING) {
   2190                 acall_set_state( vcall, A_CALL_ACTIVE );
   2191             }
   2192             else if (call->state == A_CALL_ACTIVE) {
   2193                 acall_set_state( vcall, A_CALL_HELD );
   2194             }
   2195         } else if (cmd[0] == 'H') {
   2196             /* ATH: hangup, since user is busy */
   2197             if (call->state == A_CALL_INCOMING) {
   2198                 amodem_free_call( modem, vcall );
   2199                 break;
   2200             }
   2201         }
   2202     }
   2203     return NULL;
   2204 }
   2205 
   2206 int android_snapshot_update_time = 1;
   2207 int android_snapshot_update_time_request = 0;
   2208 
   2209 static const char*
   2210 handleSignalStrength( const char*  cmd, AModem  modem )
   2211 {
   2212     amodem_begin_line( modem );
   2213 
   2214     /* Sneak time updates into the SignalStrength request, because it's periodic.
   2215      * Ideally, we'd be able to prod the guest into asking immediately on restore
   2216      * from snapshot, but that'd require a driver.
   2217      */
   2218     if ( android_snapshot_update_time && android_snapshot_update_time_request ) {
   2219       amodem_addTimeUpdate( modem );
   2220       android_snapshot_update_time_request = 0;
   2221     }
   2222 
   2223     // rssi = 0 (<-113dBm) 1 (<-111) 2-30 (<-109--53) 31 (>=-51) 99 (?!)
   2224     // ber (bit error rate) - always 99 (unknown), apparently.
   2225     // TODO: return 99 if modem->radio_state==A_RADIO_STATE_OFF, once radio_state is in snapshot.
   2226     int rssi = modem->rssi;
   2227     int ber = modem->ber;
   2228     rssi = (0 > rssi && rssi > 31) ? 99 : rssi ;
   2229     ber = (0 > ber && ber > 7 ) ? 99 : ber;
   2230     amodem_add_line( modem, "+CSQ: %i,%i,85,130,90,6,4,25,9,50,68,12\r\n", rssi, ber );
   2231     return amodem_end_line( modem );
   2232 }
   2233 
   2234 static const char*
   2235 handleHangup( const char*  cmd, AModem  modem )
   2236 {
   2237     if ( !memcmp(cmd, "+CHLD=", 6) ) {
   2238         int  nn;
   2239         cmd += 6;
   2240         switch (cmd[0]) {
   2241             case '0':  /* release all held, and set busy for waiting calls */
   2242                 for (nn = 0; nn < modem->call_count; nn++) {
   2243                     AVoiceCall  vcall = modem->calls + nn;
   2244                     ACall       call  = &vcall->call;
   2245                     if (call->mode != A_CALL_VOICE)
   2246                         continue;
   2247                     if (call->state == A_CALL_HELD    ||
   2248                         call->state == A_CALL_WAITING ||
   2249                         call->state == A_CALL_INCOMING) {
   2250                         amodem_free_call(modem, vcall);
   2251                         nn--;
   2252                     }
   2253                 }
   2254                 break;
   2255 
   2256             case '1':
   2257                 if (cmd[1] == 0) { /* release all active, accept held one */
   2258                     for (nn = 0; nn < modem->call_count; nn++) {
   2259                         AVoiceCall  vcall = modem->calls + nn;
   2260                         ACall       call  = &vcall->call;
   2261                         if (call->mode != A_CALL_VOICE)
   2262                             continue;
   2263                         if (call->state == A_CALL_ACTIVE) {
   2264                             amodem_free_call(modem, vcall);
   2265                             nn--;
   2266                         }
   2267                         else if (call->state == A_CALL_HELD     ||
   2268                                  call->state == A_CALL_WAITING) {
   2269                             acall_set_state( vcall, A_CALL_ACTIVE );
   2270                         }
   2271                     }
   2272                 } else {  /* release specific call */
   2273                     int  id = cmd[1] - '0';
   2274                     AVoiceCall  vcall = amodem_find_call( modem, id );
   2275                     if (vcall != NULL)
   2276                         amodem_free_call( modem, vcall );
   2277                 }
   2278                 break;
   2279 
   2280             case '2':
   2281                 if (cmd[1] == 0) {  /* place all active on hold, accept held or waiting one */
   2282                     for (nn = 0; nn < modem->call_count; nn++) {
   2283                         AVoiceCall  vcall = modem->calls + nn;
   2284                         ACall       call  = &vcall->call;
   2285                         if (call->mode != A_CALL_VOICE)
   2286                             continue;
   2287                         if (call->state == A_CALL_ACTIVE) {
   2288                             acall_set_state( vcall, A_CALL_HELD );
   2289                         }
   2290                         else if (call->state == A_CALL_HELD     ||
   2291                                  call->state == A_CALL_WAITING) {
   2292                             acall_set_state( vcall, A_CALL_ACTIVE );
   2293                         }
   2294                     }
   2295                 } else {  /* place all active on hold, except a specific one */
   2296                     int   id = cmd[1] - '0';
   2297                     for (nn = 0; nn < modem->call_count; nn++) {
   2298                         AVoiceCall  vcall = modem->calls + nn;
   2299                         ACall       call  = &vcall->call;
   2300                         if (call->mode != A_CALL_VOICE)
   2301                             continue;
   2302                         if (call->state == A_CALL_ACTIVE && call->id != id) {
   2303                             acall_set_state( vcall, A_CALL_HELD );
   2304                         }
   2305                     }
   2306                 }
   2307                 break;
   2308 
   2309             case '3':  /* add a held call to the conversation */
   2310                 for (nn = 0; nn < modem->call_count; nn++) {
   2311                     AVoiceCall  vcall = modem->calls + nn;
   2312                     ACall       call  = &vcall->call;
   2313                     if (call->mode != A_CALL_VOICE)
   2314                         continue;
   2315                     if (call->state == A_CALL_HELD) {
   2316                         acall_set_state( vcall, A_CALL_ACTIVE );
   2317                         break;
   2318                     }
   2319                 }
   2320                 break;
   2321 
   2322             case '4':  /* connect the two calls */
   2323                 for (nn = 0; nn < modem->call_count; nn++) {
   2324                     AVoiceCall  vcall = modem->calls + nn;
   2325                     ACall       call  = &vcall->call;
   2326                     if (call->mode != A_CALL_VOICE)
   2327                         continue;
   2328                     if (call->state == A_CALL_HELD) {
   2329                         acall_set_state( vcall, A_CALL_ACTIVE );
   2330                         break;
   2331                     }
   2332                 }
   2333                 break;
   2334         }
   2335     }
   2336     else
   2337         return "ERROR: BAD COMMAND";
   2338 
   2339     return NULL;
   2340 }
   2341 
   2342 
   2343 /* a function used to deal with a non-trivial request */
   2344 typedef const char*  (*ResponseHandler)(const char*  cmd, AModem  modem);
   2345 
   2346 static const struct {
   2347     const char*      cmd;     /* command coming from libreference-ril.so, if first
   2348                                  character is '!', then the rest is a prefix only */
   2349 
   2350     const char*      answer;  /* default answer, NULL if needs specific handling or
   2351                                  if OK is good enough */
   2352 
   2353     ResponseHandler  handler; /* specific handler, ignored if 'answer' is not NULL,
   2354                                  NULL if OK is good enough */
   2355 } sDefaultResponses[] =
   2356 {
   2357     /* see onRadioPowerOn() */
   2358     { "%CPHS=1", NULL, NULL },
   2359     { "%CTZV=1", NULL, NULL },
   2360 
   2361     /* see onSIMReady() */
   2362     { "+CSMS=1", "+CSMS: 1, 1, 1", NULL },
   2363     { "+CNMI=1,2,2,1,1", NULL, NULL },
   2364 
   2365     /* see requestRadioPower() */
   2366     { "+CFUN=0", NULL, handleRadioPower },
   2367     { "+CFUN=1", NULL, handleRadioPower },
   2368 
   2369     { "+CTEC=?", "+CTEC: 0,1,2,3", NULL }, /* Query available Techs */
   2370     { "!+CTEC", NULL, handleTech }, /* Set/get current Technology and preferred mode */
   2371 
   2372     { "+WRMP=?", "+WRMP: 0,1,2", NULL }, /* Query Roam Preference */
   2373     { "!+WRMP", NULL, handleRoamPref }, /* Set/get Roam Preference */
   2374 
   2375     { "+CCSS=?", "+CTEC: 0,1", NULL }, /* Query available subscription sources */
   2376     { "!+CCSS", NULL, handleSubscriptionSource }, /* Set/Get current subscription source */
   2377 
   2378     { "+WSOS=?", "+WSOS: 0", NULL}, /* Query supported +WSOS values */
   2379     { "!+WSOS=", NULL, handleEmergencyMode },
   2380 
   2381     { "+WPRL?", NULL, handlePrlVersion }, /* Query the current PRL version */
   2382 
   2383     /* see requestOrSendPDPContextList() */
   2384     { "+CGACT?", NULL, handleListPDPContexts },
   2385 
   2386     /* see requestOperator() */
   2387     { "+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", NULL, handleRequestOperator },
   2388 
   2389     /* see requestQueryNetworkSelectionMode() */
   2390     { "!+COPS", NULL, handleOperatorSelection },
   2391 
   2392     /* see requestGetCurrentCalls() */
   2393     { "+CLCC", NULL, handleListCurrentCalls },
   2394 
   2395     /* see requestWriteSmsToSim() */
   2396     { "!+CMGW=", NULL, handleSendSMStoSIM },
   2397 
   2398     /* see requestHangup() */
   2399     { "!+CHLD=", NULL, handleHangup },
   2400 
   2401     /* see requestSignalStrength() */
   2402     { "+CSQ", NULL, handleSignalStrength },
   2403 
   2404     /* see requestRegistrationState() */
   2405     { "!+CREG", NULL, handleNetworkRegistration },
   2406     { "!+CGREG", NULL, handleNetworkRegistration },
   2407 
   2408     /* see requestSendSMS() */
   2409     { "!+CMGS=", NULL, handleSendSMS },
   2410 
   2411     /* see requestSetupDefaultPDP() */
   2412     { "%CPRIM=\"GMM\",\"CONFIG MULTISLOT_CLASS=<10>\"", NULL, NULL },
   2413     { "%DATA=2,\"UART\",1,,\"SER\",\"UART\",0", NULL, NULL },
   2414 
   2415     { "!+CGDCONT=", NULL, handleDefinePDPContext },
   2416     { "+CGDCONT?", NULL, handleQueryPDPContext },
   2417 
   2418     { "+CGQREQ=1", NULL, NULL },
   2419     { "+CGQMIN=1", NULL, NULL },
   2420     { "+CGEREP=1,0", NULL, NULL },
   2421     { "+CGACT=1,0", NULL, NULL },
   2422     { "D*99***1#", NULL, handleStartPDPContext },
   2423 
   2424     /* see requestDial() */
   2425     { "!D", NULL, handleDial },  /* the code says that success/error is ignored, the call state will
   2426                               be polled through +CLCC instead */
   2427 
   2428     /* see requestSMSAcknowledge() */
   2429     { "+CNMA=1", NULL, NULL },
   2430     { "+CNMA=2", NULL, NULL },
   2431 
   2432     /* see requestSIM_IO() */
   2433     { "!+CRSM=", NULL, handleSIM_IO },
   2434 
   2435     /* see onRequest() */
   2436     { "+CHLD=0", NULL, handleHangup },
   2437     { "+CHLD=1", NULL, handleHangup },
   2438     { "+CHLD=2", NULL, handleHangup },
   2439     { "+CHLD=3", NULL, handleHangup },
   2440     { "A", NULL, handleAnswer },  /* answer the call */
   2441     { "H", NULL, handleAnswer },  /* user is busy */
   2442     { "!+VTS=", NULL, handleSetDialTone },
   2443     { "+CIMI", OPERATOR_HOME_MCCMNC "000000000", NULL },   /* request internation subscriber identification number */
   2444     { "+CGSN", "000000000000000", NULL },   /* request model version */
   2445     { "+CUSD=2",NULL, NULL }, /* Cancel USSD */
   2446     { "+COPS=0", NULL, handleOperatorSelection }, /* set network selection to automatic */
   2447     { "!+CMGD=", NULL, handleDeleteSMSonSIM }, /* delete SMS on SIM */
   2448     { "!+CPIN=", NULL, handleChangeOrEnterPIN },
   2449 
   2450     /* see getSIMStatus() */
   2451     { "+CPIN?", NULL, handleSIMStatusReq },
   2452     { "+CNMI?", "+CNMI: 1,2,2,1,1", NULL },
   2453 
   2454     /* see isRadioOn() */
   2455     { "+CFUN?", NULL, handleRadioPowerReq },
   2456 
   2457     /* see initializeCallback() */
   2458     { "E0Q0V1", NULL, NULL },
   2459     { "S0=0", NULL, NULL },
   2460     { "+CMEE=1", NULL, NULL },
   2461     { "+CCWA=1", NULL, NULL },
   2462     { "+CMOD=0", NULL, NULL },
   2463     { "+CMUT=0", NULL, NULL },
   2464     { "+CSSN=0,1", NULL, NULL },
   2465     { "+COLP=0", NULL, NULL },
   2466     { "+CSCS=\"HEX\"", NULL, NULL },
   2467     { "+CUSD=1", NULL, NULL },
   2468     { "+CGEREP=1,0", NULL, NULL },
   2469     { "+CMGF=0", NULL, handleEndOfInit },  /* now is a goof time to send the current tme and timezone */
   2470     { "%CPI=3", NULL, NULL },
   2471     { "%CSTAT=1", NULL, NULL },
   2472 
   2473     /* end of list */
   2474     {NULL, NULL, NULL}
   2475 };
   2476 
   2477 
   2478 #define  REPLY(str)  do { const char*  s = (str); R(">> %s\n", quote(s)); return s; } while (0)
   2479 
   2480 const char*  amodem_send( AModem  modem, const char*  cmd )
   2481 {
   2482     const char*  answer;
   2483 
   2484     if ( modem->wait_sms != 0 ) {
   2485         modem->wait_sms = 0;
   2486         R( "SMS<< %s\n", quote(cmd) );
   2487         answer = handleSendSMSText( cmd, modem );
   2488         REPLY(answer);
   2489     }
   2490 
   2491     /* everything that doesn't start with 'AT' is not a command, right ? */
   2492     if ( cmd[0] != 'A' || cmd[1] != 'T' || cmd[2] == 0 ) {
   2493         /* R( "-- %s\n", quote(cmd) ); */
   2494         return NULL;
   2495     }
   2496     R( "<< %s\n", quote(cmd) );
   2497 
   2498     cmd += 2;
   2499 
   2500     /* TODO: implement command handling */
   2501     {
   2502         int  nn, found = 0;
   2503 
   2504         for (nn = 0; ; nn++) {
   2505             const char*  scmd = sDefaultResponses[nn].cmd;
   2506 
   2507             if (!scmd) /* end of list */
   2508                 break;
   2509 
   2510             if (scmd[0] == '!') { /* prefix match */
   2511                 int  len = strlen(++scmd);
   2512 
   2513                 if ( !memcmp( scmd, cmd, len ) ) {
   2514                     found = 1;
   2515                     break;
   2516                 }
   2517             } else { /* full match */
   2518                 if ( !strcmp( scmd, cmd ) ) {
   2519                     found = 1;
   2520                     break;
   2521                 }
   2522             }
   2523         }
   2524 
   2525         if ( !found )
   2526         {
   2527             D( "** UNSUPPORTED COMMAND **\n" );
   2528             REPLY( "ERROR: UNSUPPORTED" );
   2529         }
   2530         else
   2531         {
   2532             const char*      answer  = sDefaultResponses[nn].answer;
   2533             ResponseHandler  handler = sDefaultResponses[nn].handler;
   2534 
   2535             if ( answer != NULL ) {
   2536                 REPLY( amodem_printf( modem, "%s\rOK", answer ) );
   2537             }
   2538 
   2539             if (handler == NULL) {
   2540                 REPLY( "OK" );
   2541             }
   2542 
   2543             answer = handler( cmd, modem );
   2544             if (answer == NULL)
   2545                 REPLY( "OK" );
   2546 
   2547             if ( !memcmp( answer, "> ", 2 )     ||
   2548                  !memcmp( answer, "ERROR", 5 )  ||
   2549                  !memcmp( answer, "+CME ERROR", 6 ) )
   2550             {
   2551                 REPLY( answer );
   2552             }
   2553 
   2554             if (answer != modem->out_buff)
   2555                 REPLY( amodem_printf( modem, "%s\rOK", answer ) );
   2556 
   2557             strcat( modem->out_buff, "\rOK" );
   2558             REPLY( answer );
   2559         }
   2560     }
   2561 }
   2562