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.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( "android_modem", 0, MODEM_DEV_STATE_SAVE_VERSION,
    600                       android_modem_state_save,
    601                       android_modem_state_load, modem);
    602 
    603     aconfig_save_file( modem->nvram_config, modem->nvram_config_filename );
    604     return  modem;
    605 }
    606 
    607 void
    608 amodem_set_legacy( AModem  modem )
    609 {
    610     modem->supportsNetworkDataType = 0;
    611 }
    612 
    613 void
    614 amodem_destroy( AModem  modem )
    615 {
    616     asimcard_destroy( modem->sim );
    617     modem->sim = NULL;
    618 }
    619 
    620 
    621 static int
    622 amodem_has_network( AModem  modem )
    623 {
    624     return !(modem->radio_state == A_RADIO_STATE_OFF   ||
    625              modem->oper_index < 0                  ||
    626              modem->oper_index >= modem->oper_count ||
    627              modem->oper_selection_mode == A_SELECTION_DEREGISTRATION );
    628 }
    629 
    630 
    631 ARadioState
    632 amodem_get_radio_state( AModem modem )
    633 {
    634     return modem->radio_state;
    635 }
    636 
    637 void
    638 amodem_set_radio_state( AModem modem, ARadioState  state )
    639 {
    640     modem->radio_state = state;
    641 }
    642 
    643 ASimCard
    644 amodem_get_sim( AModem  modem )
    645 {
    646     return  modem->sim;
    647 }
    648 
    649 ARegistrationState
    650 amodem_get_voice_registration( AModem  modem )
    651 {
    652     return modem->voice_state;
    653 }
    654 
    655 void
    656 amodem_set_voice_registration( AModem  modem, ARegistrationState  state )
    657 {
    658     modem->voice_state = state;
    659 
    660     if (state == A_REGISTRATION_HOME)
    661         modem->oper_index = OPERATOR_HOME_INDEX;
    662     else if (state == A_REGISTRATION_ROAMING)
    663         modem->oper_index = OPERATOR_ROAMING_INDEX;
    664 
    665     switch (modem->voice_mode) {
    666         case A_REGISTRATION_UNSOL_ENABLED:
    667             amodem_unsol( modem, "+CREG: %d,%d\r",
    668                           modem->voice_mode, modem->voice_state );
    669             break;
    670 
    671         case A_REGISTRATION_UNSOL_ENABLED_FULL:
    672             amodem_unsol( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"\r",
    673                           modem->voice_mode, modem->voice_state,
    674                           modem->area_code & 0xffff, modem->cell_id & 0xffff);
    675             break;
    676         default:
    677             ;
    678     }
    679 }
    680 
    681 ARegistrationState
    682 amodem_get_data_registration( AModem  modem )
    683 {
    684     return modem->data_state;
    685 }
    686 
    687 void
    688 amodem_set_data_registration( AModem  modem, ARegistrationState  state )
    689 {
    690     modem->data_state = state;
    691 
    692     switch (modem->data_mode) {
    693         case A_REGISTRATION_UNSOL_ENABLED:
    694             amodem_unsol( modem, "+CGREG: %d,%d\r",
    695                           modem->data_mode, modem->data_state );
    696             break;
    697 
    698         case A_REGISTRATION_UNSOL_ENABLED_FULL:
    699             if (modem->supportsNetworkDataType)
    700                 amodem_unsol( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"\r",
    701                             modem->data_mode, modem->data_state,
    702                             modem->area_code & 0xffff, modem->cell_id & 0xffff,
    703                             modem->data_network );
    704             else
    705                 amodem_unsol( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\"\r",
    706                             modem->data_mode, modem->data_state,
    707                             modem->area_code & 0xffff, modem->cell_id & 0xffff );
    708             break;
    709 
    710         default:
    711             ;
    712     }
    713 }
    714 
    715 static int
    716 amodem_nvram_set( AModem modem, const char *name, const char *value )
    717 {
    718     aconfig_set(modem->nvram_config, name, value);
    719     return 0;
    720 }
    721 static AModemTech
    722 tech_from_network_type( ADataNetworkType type )
    723 {
    724     switch (type) {
    725         case A_DATA_NETWORK_GPRS:
    726         case A_DATA_NETWORK_EDGE:
    727         case A_DATA_NETWORK_UMTS:
    728             // TODO: Add A_TECH_WCDMA
    729             return A_TECH_GSM;
    730         case A_DATA_NETWORK_LTE:
    731             return A_TECH_LTE;
    732         case A_DATA_NETWORK_CDMA1X:
    733         case A_DATA_NETWORK_EVDO:
    734             return A_TECH_CDMA;
    735         case A_DATA_NETWORK_UNKNOWN:
    736             return A_TECH_UNKNOWN;
    737     }
    738     return A_TECH_UNKNOWN;
    739 }
    740 
    741 void
    742 amodem_set_data_network_type( AModem  modem, ADataNetworkType   type )
    743 {
    744     AModemTech modemTech;
    745     modem->data_network = type;
    746     amodem_set_data_registration( modem, modem->data_state );
    747     modemTech = tech_from_network_type(type);
    748     if (modem->unsol_func && modemTech != A_TECH_UNKNOWN) {
    749         if (_amodem_switch_technology( modem, modemTech, modem->preferred_mask )) {
    750             modem->unsol_func( modem->unsol_opaque, modem->out_buff );
    751         }
    752     }
    753 }
    754 
    755 int
    756 amodem_get_operator_name ( AModem  modem, ANameIndex  index, char*  buffer, int  buffer_size )
    757 {
    758     AOperator  oper;
    759     int        len;
    760 
    761     if ( (unsigned)modem->oper_index >= (unsigned)modem->oper_count ||
    762          (unsigned)index > 2 )
    763         return 0;
    764 
    765     oper = modem->operators + modem->oper_index;
    766     len  = strlen(oper->name[index]) + 1;
    767 
    768     if (buffer_size > len)
    769         buffer_size = len;
    770 
    771     if (buffer_size > 0) {
    772         memcpy( buffer, oper->name[index], buffer_size-1 );
    773         buffer[buffer_size] = 0;
    774     }
    775     return len;
    776 }
    777 
    778 /* reset one operator name from a user-provided buffer, set buffer_size to -1 for zero-terminated strings */
    779 void
    780 amodem_set_operator_name( AModem  modem, ANameIndex  index, const char*  buffer, int  buffer_size )
    781 {
    782     AOperator  oper;
    783     int        avail;
    784 
    785     if ( (unsigned)modem->oper_index >= (unsigned)modem->oper_count ||
    786          (unsigned)index > 2 )
    787         return;
    788 
    789     oper = modem->operators + modem->oper_index;
    790 
    791     avail = sizeof(oper->name[0]);
    792     if (buffer_size < 0)
    793         buffer_size = strlen(buffer);
    794     if (buffer_size > avail-1)
    795         buffer_size = avail-1;
    796     memcpy( oper->name[index], buffer, buffer_size );
    797     oper->name[index][buffer_size] = 0;
    798 }
    799 
    800 /** CALLS
    801  **/
    802 int
    803 amodem_get_call_count( AModem  modem )
    804 {
    805     return modem->call_count;
    806 }
    807 
    808 ACall
    809 amodem_get_call( AModem  modem, int  index )
    810 {
    811     if ((unsigned)index >= (unsigned)modem->call_count)
    812         return NULL;
    813 
    814     return &modem->calls[index].call;
    815 }
    816 
    817 static AVoiceCall
    818 amodem_alloc_call( AModem   modem )
    819 {
    820     AVoiceCall  call  = NULL;
    821     int         count = modem->call_count;
    822 
    823     if (count < MAX_CALLS) {
    824         int  id;
    825 
    826         /* find a valid id for this call */
    827         for (id = 0; id < modem->call_count; id++) {
    828             int  found = 0;
    829             int  nn;
    830             for (nn = 0; nn < count; nn++) {
    831                 if ( modem->calls[nn].call.id == (id+1) ) {
    832                     found = 1;
    833                     break;
    834                 }
    835             }
    836             if (!found)
    837                 break;
    838         }
    839         call          = modem->calls + count;
    840         call->call.id = id + 1;
    841         call->modem   = modem;
    842 
    843         modem->call_count += 1;
    844     }
    845     return call;
    846 }
    847 
    848 
    849 static void
    850 amodem_free_call( AModem  modem, AVoiceCall  call )
    851 {
    852     int  nn;
    853 
    854     if (call->timer) {
    855         sys_timer_destroy( call->timer );
    856         call->timer = NULL;
    857     }
    858 
    859     if (call->is_remote) {
    860         remote_call_cancel( call->call.number, modem->base_port );
    861         call->is_remote = 0;
    862     }
    863 
    864     for (nn = 0; nn < modem->call_count; nn++) {
    865         if ( modem->calls + nn == call )
    866             break;
    867     }
    868     assert( nn < modem->call_count );
    869 
    870     memmove( modem->calls + nn,
    871              modem->calls + nn + 1,
    872              (modem->call_count - 1 - nn)*sizeof(*call) );
    873 
    874     modem->call_count -= 1;
    875 }
    876 
    877 
    878 static AVoiceCall
    879 amodem_find_call( AModem  modem, int  id )
    880 {
    881     int  nn;
    882 
    883     for (nn = 0; nn < modem->call_count; nn++) {
    884         AVoiceCall call = modem->calls + nn;
    885         if (call->call.id == id)
    886             return call;
    887     }
    888     return NULL;
    889 }
    890 
    891 static void
    892 amodem_send_calls_update( AModem  modem )
    893 {
    894    /* despite its name, this really tells the system that the call
    895     * state has changed */
    896     amodem_unsol( modem, "RING\r" );
    897 }
    898 
    899 
    900 int
    901 amodem_add_inbound_call( AModem  modem, const char*  number )
    902 {
    903     AVoiceCall  vcall = amodem_alloc_call( modem );
    904     ACall       call  = &vcall->call;
    905     int         len;
    906 
    907     if (call == NULL)
    908         return -1;
    909 
    910     call->dir   = A_CALL_INBOUND;
    911     call->state = A_CALL_INCOMING;
    912     call->mode  = A_CALL_VOICE;
    913     call->multi = 0;
    914 
    915     vcall->is_remote = (remote_number_string_to_port(number) > 0);
    916 
    917     len  = strlen(number);
    918     if (len >= sizeof(call->number))
    919         len = sizeof(call->number)-1;
    920 
    921     memcpy( call->number, number, len );
    922     call->number[len] = 0;
    923 
    924     amodem_send_calls_update( modem );
    925     return 0;
    926 }
    927 
    928 ACall
    929 amodem_find_call_by_number( AModem  modem, const char*  number )
    930 {
    931     AVoiceCall  vcall = modem->calls;
    932     AVoiceCall  vend  = vcall + modem->call_count;
    933 
    934     if (!number)
    935         return NULL;
    936 
    937     for ( ; vcall < vend; vcall++ )
    938         if ( !strcmp(vcall->call.number, number) )
    939             return &vcall->call;
    940 
    941     return  NULL;
    942 }
    943 
    944 void
    945 amodem_set_signal_strength( AModem modem, int rssi, int ber )
    946 {
    947     modem->rssi = rssi;
    948     modem->ber = ber;
    949 }
    950 
    951 static void
    952 acall_set_state( AVoiceCall    call, ACallState  state )
    953 {
    954     if (state != call->call.state)
    955     {
    956         if (call->is_remote)
    957         {
    958             const char*  number = call->call.number;
    959             int          port   = call->modem->base_port;
    960 
    961             switch (state) {
    962                 case A_CALL_HELD:
    963                     remote_call_other( number, port, REMOTE_CALL_HOLD );
    964                     break;
    965 
    966                 case A_CALL_ACTIVE:
    967                     remote_call_other( number, port, REMOTE_CALL_ACCEPT );
    968                     break;
    969 
    970                 default: ;
    971             }
    972         }
    973         call->call.state = state;
    974     }
    975 }
    976 
    977 
    978 int
    979 amodem_update_call( AModem  modem, const char*  fromNumber, ACallState  state )
    980 {
    981     AVoiceCall  vcall = (AVoiceCall) amodem_find_call_by_number(modem, fromNumber);
    982 
    983     if (vcall == NULL)
    984         return -1;
    985 
    986     acall_set_state( vcall, state );
    987     amodem_send_calls_update(modem);
    988     return 0;
    989 }
    990 
    991 
    992 int
    993 amodem_disconnect_call( AModem  modem, const char*  number )
    994 {
    995     AVoiceCall  vcall = (AVoiceCall) amodem_find_call_by_number(modem, number);
    996 
    997     if (!vcall)
    998         return -1;
    999 
   1000     amodem_free_call( modem, vcall );
   1001     amodem_send_calls_update(modem);
   1002     return 0;
   1003 }
   1004 
   1005 /** COMMAND HANDLERS
   1006  **/
   1007 
   1008 static const char*
   1009 unknownCommand( const char*  cmd, AModem  modem )
   1010 {
   1011     modem=modem;
   1012     fprintf(stderr, ">>> unknown command '%s'\n", cmd );
   1013     return "ERROR: unknown command\r";
   1014 }
   1015 
   1016 /*
   1017  * Tell whether the specified tech is valid for the preferred mask.
   1018  * @pmask: The preferred mask
   1019  * @tech: The AModemTech we try to validate
   1020  * return: If the specified technology is not set in any of the 4
   1021  *         bitmasks, return 0.
   1022  *         Otherwise, return a non-zero value.
   1023  */
   1024 static int matchPreferredMask( int32_t pmask, AModemTech tech )
   1025 {
   1026     int ret = 0;
   1027     int i;
   1028     for ( i=3; i >= 0 ; i-- ) {
   1029         if (pmask & (1 << (tech + i*8 ))) {
   1030             ret = 1;
   1031             break;
   1032         }
   1033     }
   1034     return ret;
   1035 }
   1036 
   1037 static AModemTech
   1038 chooseTechFromMask( AModem modem, int32_t preferred )
   1039 {
   1040     int i, j;
   1041 
   1042     /* TODO: Current implementation will only return the highest priority,
   1043      * lowest numbered technology that is set in the mask.
   1044      * However the implementation could be changed to consider currently
   1045      * available networks set from the console (or somewhere else)
   1046      */
   1047     for ( i=3 ; i >= 0; i-- ) {
   1048         for ( j=0 ; j < A_TECH_UNKNOWN ; j++ ) {
   1049             if (preferred & (1 << (j + 8 * i)))
   1050                 return (AModemTech) j;
   1051         }
   1052     }
   1053     assert("This should never happen" == 0);
   1054     // This should never happen. Just to please the compiler.
   1055     return A_TECH_UNKNOWN;
   1056 }
   1057 
   1058 static const char*
   1059 _amodem_switch_technology( AModem modem, AModemTech newtech, int32_t newpreferred )
   1060 {
   1061     D("_amodem_switch_technology: oldtech: %d, newtech %d, preferred: %d. newpreferred: %d\n",
   1062                       modem->technology, newtech, modem->preferred_mask,newpreferred);
   1063     const char *ret = "+CTEC: DONE";
   1064     assert( modem );
   1065 
   1066     if (!newpreferred) {
   1067         return "ERROR: At least one technology must be enabled";
   1068     }
   1069     if (modem->preferred_mask != newpreferred) {
   1070         char value[MAX_KEY_NAME + 1];
   1071         modem->preferred_mask = newpreferred;
   1072         snprintf(value, MAX_KEY_NAME, "%d", newpreferred);
   1073         amodem_nvram_set(modem, NV_PREFERRED_MODE, value);
   1074         if (!matchPreferredMask(modem->preferred_mask, newtech)) {
   1075             newtech = chooseTechFromMask(modem, newpreferred);
   1076         }
   1077     }
   1078 
   1079     if (modem->technology != newtech) {
   1080         modem->technology = newtech;
   1081         ret = amodem_printf(modem, "+CTEC: %d", modem->technology);
   1082     }
   1083     return ret;
   1084 }
   1085 
   1086 static int
   1087 parsePreferred( const char *str, int *preferred )
   1088 {
   1089     char *endptr = NULL;
   1090     int result = 0;
   1091     if (!str || !*str) { *preferred = 0; return 0; }
   1092     if (*str == '"') str ++;
   1093     if (!*str) return 0;
   1094 
   1095     result = strtol(str, &endptr, 16);
   1096     if (*endptr && *endptr != '"') {
   1097         return 0;
   1098     }
   1099     if (preferred)
   1100         *preferred = result;
   1101     return 1;
   1102 }
   1103 
   1104 void
   1105 amodem_set_cdma_prl_version( AModem modem, int prlVersion)
   1106 {
   1107     D("amodem_set_prl_version()\n");
   1108     if (!_amodem_set_cdma_prl_version( modem, prlVersion)) {
   1109         amodem_unsol(modem, "+WPRL: %d", prlVersion);
   1110     }
   1111 }
   1112 
   1113 static int
   1114 _amodem_set_cdma_prl_version( AModem modem, int prlVersion)
   1115 {
   1116     D("_amodem_set_cdma_prl_version");
   1117     if (modem->prl_version != prlVersion) {
   1118         modem->prl_version = prlVersion;
   1119         return 0;
   1120     }
   1121     return -1;
   1122 }
   1123 
   1124 void
   1125 amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ss)
   1126 {
   1127     D("amodem_set_cdma_subscription_source()\n");
   1128     if (!_amodem_set_cdma_subscription_source( modem, ss)) {
   1129         amodem_unsol(modem, "+CCSS: %d", (int)ss);
   1130     }
   1131 }
   1132 
   1133 #define MAX_INT_DIGITS 10
   1134 static int
   1135 _amodem_set_cdma_subscription_source( AModem modem, ACdmaSubscriptionSource ss)
   1136 {
   1137     D("_amodem_set_cdma_subscription_source()\n");
   1138     char value[MAX_INT_DIGITS + 1];
   1139 
   1140     if (ss != modem->subscription_source) {
   1141         snprintf( value, MAX_INT_DIGITS + 1, "%d", ss );
   1142         amodem_nvram_set( modem, NV_CDMA_SUBSCRIPTION_SOURCE, value );
   1143         modem->subscription_source = ss;
   1144         return 0;
   1145     }
   1146     return -1;
   1147 }
   1148 
   1149 static const char*
   1150 handleSubscriptionSource( const char*  cmd, AModem  modem )
   1151 {
   1152     int newsource;
   1153     // TODO: Actually change subscription depending on source
   1154     D("handleSubscriptionSource(%s)\n",cmd);
   1155 
   1156     assert( !memcmp( "+CCSS", cmd, 5 ) );
   1157     cmd += 5;
   1158     if (cmd[0] == '?') {
   1159         return amodem_printf( modem, "+CCSS: %d", modem->subscription_source );
   1160     } else if (cmd[0] == '=') {
   1161         switch (cmd[1]) {
   1162             case '0':
   1163             case '1':
   1164                 newsource = (ACdmaSubscriptionSource)cmd[1] - '0';
   1165                 _amodem_set_cdma_subscription_source( modem, newsource );
   1166                 return amodem_printf( modem, "+CCSS: %d", modem->subscription_source );
   1167                 break;
   1168         }
   1169     }
   1170     return amodem_printf( modem, "ERROR: Invalid subscription source");
   1171 }
   1172 
   1173 static const char*
   1174 handleRoamPref( const char * cmd, AModem modem )
   1175 {
   1176     int roaming_pref = -1;
   1177     char *endptr = NULL;
   1178     D("handleRoamPref(%s)\n", cmd);
   1179     assert( !memcmp( "+WRMP", cmd, 5 ) );
   1180     cmd += 5;
   1181     if (cmd[0] == '?') {
   1182         return amodem_printf( modem, "+WRMP: %d", modem->roaming_pref );
   1183     }
   1184 
   1185     if (!strcmp( cmd, "=?")) {
   1186         return amodem_printf( modem, "+WRMP: 0,1,2" );
   1187     } else if (cmd[0] == '=') {
   1188         cmd ++;
   1189         roaming_pref = strtol( cmd, &endptr, 10 );
   1190          // Make sure the rest of the command is the number
   1191          // (if *endptr is null, it means strtol processed the whole string as a number)
   1192         if(endptr && !*endptr) {
   1193             modem->roaming_pref = roaming_pref;
   1194             aconfig_set( modem->nvram_config, NV_CDMA_ROAMING_PREF, cmd );
   1195             aconfig_save_file( modem->nvram_config, modem->nvram_config_filename );
   1196             return NULL;
   1197         }
   1198     }
   1199     return amodem_printf( modem, "ERROR");
   1200 }
   1201 static const char*
   1202 handleTech( const char*  cmd, AModem  modem )
   1203 {
   1204     AModemTech newtech = modem->technology;
   1205     int pt = modem->preferred_mask;
   1206     int havenewtech = 0;
   1207     D("handleTech. cmd: %s\n", cmd);
   1208     assert( !memcmp( "+CTEC", cmd, 5 ) );
   1209     cmd += 5;
   1210     if (cmd[0] == '?') {
   1211         return amodem_printf( modem, "+CTEC: %d,%x",modem->technology, modem->preferred_mask );
   1212     }
   1213     amodem_begin_line( modem );
   1214     if (!strcmp( cmd, "=?")) {
   1215         return amodem_printf( modem, "+CTEC: 0,1,2,3" );
   1216     }
   1217     else if (cmd[0] == '=') {
   1218         switch (cmd[1]) {
   1219             case '0':
   1220             case '1':
   1221             case '2':
   1222             case '3':
   1223                 havenewtech = 1;
   1224                 newtech = cmd[1] - '0';
   1225                 cmd += 1;
   1226                 break;
   1227         }
   1228         cmd += 1;
   1229     }
   1230     if (havenewtech) {
   1231         D( "cmd: %s\n", cmd );
   1232         if (cmd[0] == ',' && ! parsePreferred( ++cmd, &pt ))
   1233             return amodem_printf( modem, "ERROR: invalid preferred mode" );
   1234         return _amodem_switch_technology( modem, newtech, pt );
   1235     }
   1236     return amodem_printf( modem, "ERROR: %s: Unknown Technology", cmd + 1 );
   1237 }
   1238 
   1239 static const char*
   1240 handleEmergencyMode( const char* cmd, AModem modem )
   1241 {
   1242     long arg;
   1243     char *endptr = NULL;
   1244     assert ( !memcmp( "+WSOS", cmd, 5 ) );
   1245     cmd += 5;
   1246     if (cmd[0] == '?') {
   1247         return amodem_printf( modem, "+WSOS: %d", modem->in_emergency_mode);
   1248     }
   1249 
   1250     if (cmd[0] == '=') {
   1251         if (cmd[1] == '?') {
   1252             return amodem_printf(modem, "+WSOS: (0)");
   1253         }
   1254         if (cmd[1] == 0) {
   1255             return amodem_printf(modem, "ERROR");
   1256         }
   1257         arg = strtol(cmd+1, &endptr, 10);
   1258 
   1259         if (!endptr || endptr[0] != 0) {
   1260             return amodem_printf(modem, "ERROR");
   1261         }
   1262 
   1263         arg = arg? 1 : 0;
   1264 
   1265         if ((!arg) != (!modem->in_emergency_mode)) {
   1266             modem->in_emergency_mode = arg;
   1267             return amodem_printf(modem, "+WSOS: %d", arg);
   1268         }
   1269     }
   1270     return amodem_printf(modem, "ERROR");
   1271 }
   1272 
   1273 static const char*
   1274 handlePrlVersion( const char* cmd, AModem modem )
   1275 {
   1276     assert ( !memcmp( "+WPRL", cmd, 5 ) );
   1277     cmd += 5;
   1278     if (cmd[0] == '?') {
   1279         return amodem_printf( modem, "+WPRL: %d", modem->prl_version);
   1280     }
   1281 
   1282     return amodem_printf(modem, "ERROR");
   1283 }
   1284 
   1285 static const char*
   1286 handleRadioPower( const char*  cmd, AModem  modem )
   1287 {
   1288     if ( !strcmp( cmd, "+CFUN=0" ) )
   1289     {
   1290         /* turn radio off */
   1291         modem->radio_state = A_RADIO_STATE_OFF;
   1292     }
   1293     else if ( !strcmp( cmd, "+CFUN=1" ) )
   1294     {
   1295         /* turn radio on */
   1296         modem->radio_state = A_RADIO_STATE_ON;
   1297     }
   1298     return NULL;
   1299 }
   1300 
   1301 static const char*
   1302 handleRadioPowerReq( const char*  cmd, AModem  modem )
   1303 {
   1304     if (modem->radio_state != A_RADIO_STATE_OFF)
   1305         return "+CFUN: 1";
   1306     else
   1307         return "+CFUN: 0";
   1308 }
   1309 
   1310 static const char*
   1311 handleSIMStatusReq( const char*  cmd, AModem  modem )
   1312 {
   1313     const char*  answer = NULL;
   1314 
   1315     switch (asimcard_get_status(modem->sim)) {
   1316         case A_SIM_STATUS_ABSENT:    answer = "+CPIN: ABSENT"; break;
   1317         case A_SIM_STATUS_READY:     answer = "+CPIN: READY"; break;
   1318         case A_SIM_STATUS_NOT_READY: answer = "+CMERROR: NOT READY"; break;
   1319         case A_SIM_STATUS_PIN:       answer = "+CPIN: SIM PIN"; break;
   1320         case A_SIM_STATUS_PUK:       answer = "+CPIN: SIM PUK"; break;
   1321         case A_SIM_STATUS_NETWORK_PERSONALIZATION: answer = "+CPIN: PH-NET PIN"; break;
   1322         default:
   1323             answer = "ERROR: internal error";
   1324     }
   1325     return answer;
   1326 }
   1327 
   1328 /* TODO: Will we need this?
   1329 static const char*
   1330 handleSRegister( const char * cmd, AModem modem )
   1331 {
   1332     char *end;
   1333     assert( cmd[0] == 'S' || cmd[0] == 's' );
   1334 
   1335     ++ cmd;
   1336 
   1337     int l = strtol(cmd, &end, 10);
   1338 } */
   1339 
   1340 static const char*
   1341 handleNetworkRegistration( const char*  cmd, AModem  modem )
   1342 {
   1343     if ( !memcmp( cmd, "+CREG", 5 ) ) {
   1344         cmd += 5;
   1345         if (cmd[0] == '?') {
   1346             if (modem->voice_mode == A_REGISTRATION_UNSOL_ENABLED_FULL)
   1347                 return amodem_printf( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"",
   1348                                        modem->voice_mode, modem->voice_state,
   1349                                        modem->area_code, modem->cell_id );
   1350             else
   1351                 return amodem_printf( modem, "+CREG: %d,%d",
   1352                                        modem->voice_mode, modem->voice_state );
   1353         } else if (cmd[0] == '=') {
   1354             switch (cmd[1]) {
   1355                 case '0':
   1356                     modem->voice_mode  = A_REGISTRATION_UNSOL_DISABLED;
   1357                     break;
   1358 
   1359                 case '1':
   1360                     modem->voice_mode  = A_REGISTRATION_UNSOL_ENABLED;
   1361                     break;
   1362 
   1363                 case '2':
   1364                     modem->voice_mode = A_REGISTRATION_UNSOL_ENABLED_FULL;
   1365                     break;
   1366 
   1367                 case '?':
   1368                     return "+CREG: (0-2)";
   1369 
   1370                 default:
   1371                     return "ERROR: BAD COMMAND";
   1372             }
   1373         } else {
   1374             assert( 0 && "unreachable" );
   1375         }
   1376     } else if ( !memcmp( cmd, "+CGREG", 6 ) ) {
   1377         cmd += 6;
   1378         if (cmd[0] == '?') {
   1379             if (modem->supportsNetworkDataType)
   1380                 return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"",
   1381                                     modem->data_mode, modem->data_state,
   1382                                     modem->area_code, modem->cell_id,
   1383                                     modem->data_network );
   1384             else
   1385                 return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\"",
   1386                                     modem->data_mode, modem->data_state,
   1387                                     modem->area_code, modem->cell_id );
   1388         } else if (cmd[0] == '=') {
   1389             switch (cmd[1]) {
   1390                 case '0':
   1391                     modem->data_mode  = A_REGISTRATION_UNSOL_DISABLED;
   1392                     break;
   1393 
   1394                 case '1':
   1395                     modem->data_mode  = A_REGISTRATION_UNSOL_ENABLED;
   1396                     break;
   1397 
   1398                 case '2':
   1399                     modem->data_mode = A_REGISTRATION_UNSOL_ENABLED_FULL;
   1400                     break;
   1401 
   1402                 case '?':
   1403                     return "+CGREG: (0-2)";
   1404 
   1405                 default:
   1406                     return "ERROR: BAD COMMAND";
   1407             }
   1408         } else {
   1409             assert( 0 && "unreachable" );
   1410         }
   1411     }
   1412     return NULL;
   1413 }
   1414 
   1415 static const char*
   1416 handleSetDialTone( const char*  cmd, AModem  modem )
   1417 {
   1418     /* XXX: TODO */
   1419     return NULL;
   1420 }
   1421 
   1422 static const char*
   1423 handleDeleteSMSonSIM( const char*  cmd, AModem  modem )
   1424 {
   1425     /* XXX: TODO */
   1426     return NULL;
   1427 }
   1428 
   1429 static const char*
   1430 handleSIM_IO( const char*  cmd, AModem  modem )
   1431 {
   1432     return asimcard_io( modem->sim, cmd );
   1433 }
   1434 
   1435 
   1436 static const char*
   1437 handleOperatorSelection( const char*  cmd, AModem  modem )
   1438 {
   1439     assert( !memcmp( "+COPS", cmd, 5 ) );
   1440     cmd += 5;
   1441     if (cmd[0] == '?') { /* ask for current operator */
   1442         AOperator  oper = &modem->operators[ modem->oper_index ];
   1443 
   1444         if ( !amodem_has_network( modem ) )
   1445         {
   1446             /* this error code means "no network" */
   1447             return amodem_printf( modem, "+CME ERROR: 30" );
   1448         }
   1449 
   1450         oper = &modem->operators[ modem->oper_index ];
   1451 
   1452         if ( modem->oper_name_index == 2 )
   1453             return amodem_printf( modem, "+COPS: %d,2,%s",
   1454                                   modem->oper_selection_mode,
   1455                                   oper->name[2] );
   1456 
   1457         return amodem_printf( modem, "+COPS: %d,%d,\"%s\"",
   1458                               modem->oper_selection_mode,
   1459                               modem->oper_name_index,
   1460                               oper->name[ modem->oper_name_index ] );
   1461     }
   1462     else if (cmd[0] == '=' && cmd[1] == '?') {  /* ask for all available operators */
   1463         const char*  comma = "+COPS: ";
   1464         int          nn;
   1465         amodem_begin_line( modem );
   1466         for (nn = 0; nn < modem->oper_count; nn++) {
   1467             AOperator  oper = &modem->operators[nn];
   1468             amodem_add_line( modem, "%s(%d,\"%s\",\"%s\",\"%s\")", comma,
   1469                              oper->status, oper->name[0], oper->name[1], oper->name[2] );
   1470             comma = ", ";
   1471         }
   1472         return amodem_end_line( modem );
   1473     }
   1474     else if (cmd[0] == '=') {
   1475         switch (cmd[1]) {
   1476             case '0':
   1477                 modem->oper_selection_mode = A_SELECTION_AUTOMATIC;
   1478                 return NULL;
   1479 
   1480             case '1':
   1481                 {
   1482                     int  format, nn, len, found = -1;
   1483 
   1484                     if (cmd[2] != ',')
   1485                         goto BadCommand;
   1486                     format = cmd[3] - '0';
   1487                     if ( (unsigned)format > 2 )
   1488                         goto BadCommand;
   1489                     if (cmd[4] != ',')
   1490                         goto BadCommand;
   1491                     cmd += 5;
   1492                     len  = strlen(cmd);
   1493                     if (*cmd == '"') {
   1494                         cmd++;
   1495                         len -= 2;
   1496                     }
   1497                     if (len <= 0)
   1498                         goto BadCommand;
   1499 
   1500                     for (nn = 0; nn < modem->oper_count; nn++) {
   1501                         AOperator    oper = modem->operators + nn;
   1502                         char*        name = oper->name[ format ];
   1503 
   1504                         if ( !memcpy( name, cmd, len ) && name[len] == 0 ) {
   1505                             found = nn;
   1506                             break;
   1507                         }
   1508                     }
   1509 
   1510                     if (found < 0) {
   1511                         /* Selection failed */
   1512                         return "+CME ERROR: 529";
   1513                     } else if (modem->operators[found].status == A_STATUS_DENIED) {
   1514                         /* network not allowed */
   1515                         return "+CME ERROR: 32";
   1516                     }
   1517                     modem->oper_index = found;
   1518 
   1519                     /* set the voice and data registration states to home or roaming
   1520                      * depending on the operator index
   1521                      */
   1522                     if (found == OPERATOR_HOME_INDEX) {
   1523                         modem->voice_state = A_REGISTRATION_HOME;
   1524                         modem->data_state  = A_REGISTRATION_HOME;
   1525                     } else if (found == OPERATOR_ROAMING_INDEX) {
   1526                         modem->voice_state = A_REGISTRATION_ROAMING;
   1527                         modem->data_state  = A_REGISTRATION_ROAMING;
   1528                     }
   1529                     return NULL;
   1530                 }
   1531 
   1532             case '2':
   1533                 modem->oper_selection_mode = A_SELECTION_DEREGISTRATION;
   1534                 return NULL;
   1535 
   1536             case '3':
   1537                 {
   1538                     int format;
   1539 
   1540                     if (cmd[2] != ',')
   1541                         goto BadCommand;
   1542 
   1543                     format = cmd[3] - '0';
   1544                     if ( (unsigned)format > 2 )
   1545                         goto BadCommand;
   1546 
   1547                     modem->oper_name_index = format;
   1548                     return NULL;
   1549                 }
   1550             default:
   1551                 ;
   1552         }
   1553     }
   1554 BadCommand:
   1555     return unknownCommand(cmd,modem);
   1556 }
   1557 
   1558 static const char*
   1559 handleRequestOperator( const char*  cmd, AModem  modem )
   1560 {
   1561     AOperator  oper;
   1562     cmd=cmd;
   1563 
   1564     if ( !amodem_has_network(modem) )
   1565         return "+CME ERROR: 30";
   1566 
   1567     oper = modem->operators + modem->oper_index;
   1568     modem->oper_name_index = 2;
   1569     return amodem_printf( modem, "+COPS: 0,0,\"%s\"\r"
   1570                           "+COPS: 0,1,\"%s\"\r"
   1571                           "+COPS: 0,2,\"%s\"",
   1572                           oper->name[0], oper->name[1], oper->name[2] );
   1573 }
   1574 
   1575 static const char*
   1576 handleSendSMStoSIM( const char*  cmd, AModem  modem )
   1577 {
   1578     /* XXX: TODO */
   1579     return "ERROR: unimplemented";
   1580 }
   1581 
   1582 static const char*
   1583 handleSendSMS( const char*  cmd, AModem  modem )
   1584 {
   1585     modem->wait_sms = 1;
   1586     return "> ";
   1587 }
   1588 
   1589 #if 0
   1590 static void
   1591 sms_address_dump( SmsAddress  address, FILE*  out )
   1592 {
   1593     int  nn, len = address->len;
   1594 
   1595     if (address->toa == 0x91) {
   1596         fprintf( out, "+" );
   1597     }
   1598     for (nn = 0; nn < len; nn += 2)
   1599     {
   1600         static const char  dialdigits[16] = "0123456789*#,N%";
   1601         int  c = address->data[nn/2];
   1602 
   1603         fprintf( out, "%c", dialdigits[c & 0xf] );
   1604         if (nn+1 >= len)
   1605             break;
   1606 
   1607         fprintf( out, "%c", dialdigits[(c >> 4) & 0xf] );
   1608     }
   1609 }
   1610 
   1611 static void
   1612 smspdu_dump( SmsPDU  pdu, FILE*  out )
   1613 {
   1614     SmsAddressRec    address;
   1615     unsigned char    temp[256];
   1616     int              len;
   1617 
   1618     if (pdu == NULL) {
   1619         fprintf( out, "SMS PDU is (null)\n" );
   1620         return;
   1621     }
   1622 
   1623     fprintf( out, "SMS PDU type:       " );
   1624     switch (smspdu_get_type(pdu)) {
   1625         case SMS_PDU_DELIVER: fprintf(out, "DELIVER"); break;
   1626         case SMS_PDU_SUBMIT:  fprintf(out, "SUBMIT"); break;
   1627         case SMS_PDU_STATUS_REPORT: fprintf(out, "STATUS_REPORT"); break;
   1628         default: fprintf(out, "UNKNOWN");
   1629     }
   1630     fprintf( out, "\n        sender:   " );
   1631     if (smspdu_get_sender_address(pdu, &address) < 0)
   1632         fprintf( out, "(N/A)" );
   1633     else
   1634         sms_address_dump(&address, out);
   1635     fprintf( out, "\n        receiver: " );
   1636     if (smspdu_get_receiver_address(pdu, &address) < 0)
   1637         fprintf(out, "(N/A)");
   1638     else
   1639         sms_address_dump(&address, out);
   1640     fprintf( out, "\n        text:     " );
   1641     len = smspdu_get_text_message( pdu, temp, sizeof(temp)-1 );
   1642     if (len > sizeof(temp)-1 )
   1643         len = sizeof(temp)-1;
   1644     fprintf( out, "'%.*s'\n", len, temp );
   1645 }
   1646 #endif
   1647 
   1648 static const char*
   1649 handleSendSMSText( const char*  cmd, AModem  modem )
   1650 {
   1651 #if 1
   1652     SmsAddressRec  address;
   1653     char           temp[16];
   1654     char           number[16];
   1655     int            numlen;
   1656     int            len = strlen(cmd);
   1657     SmsPDU         pdu;
   1658 
   1659     /* get rid of trailing escape */
   1660     if (len > 0 && cmd[len-1] == 0x1a)
   1661         len -= 1;
   1662 
   1663     pdu = smspdu_create_from_hex( cmd, len );
   1664     if (pdu == NULL) {
   1665         D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd);
   1666         return "+CMS ERROR: INVALID SMS PDU";
   1667     }
   1668     if (smspdu_get_receiver_address(pdu, &address) < 0) {
   1669         D("%s: could not get SMS receiver address from '%s'\n",
   1670           __FUNCTION__, cmd);
   1671         return "+CMS ERROR: BAD SMS RECEIVER ADDRESS";
   1672     }
   1673 
   1674     do {
   1675         int  index;
   1676 
   1677         numlen = sms_address_to_str( &address, temp, sizeof(temp) );
   1678         if (numlen > sizeof(temp)-1)
   1679             break;
   1680         temp[numlen] = 0;
   1681 
   1682         /* Converts 4, 7, and 10 digits number to 11 digits */
   1683         if (numlen == 10 && !strncmp(temp, PHONE_PREFIX+1, 6)) {
   1684             memcpy( number, PHONE_PREFIX, 1 );
   1685             memcpy( number+1, temp, numlen );
   1686             number[numlen+1] = 0;
   1687         } else if (numlen == 7 && !strncmp(temp, PHONE_PREFIX+4, 3)) {
   1688             memcpy( number, PHONE_PREFIX, 4 );
   1689             memcpy( number+4, temp, numlen );
   1690             number[numlen+4] = 0;
   1691         } else if (numlen == 4) {
   1692             memcpy( number, PHONE_PREFIX, 7 );
   1693             memcpy( number+7, temp, numlen );
   1694             number[numlen+7] = 0;
   1695         } else {
   1696             memcpy( number, temp, numlen );
   1697             number[numlen] = 0;
   1698         }
   1699 
   1700         if ( remote_number_string_to_port( number ) < 0 )
   1701             break;
   1702 
   1703         if (modem->sms_receiver == NULL) {
   1704             modem->sms_receiver = sms_receiver_create();
   1705             if (modem->sms_receiver == NULL) {
   1706                 D( "%s: could not create SMS receiver\n", __FUNCTION__ );
   1707                 break;
   1708             }
   1709         }
   1710 
   1711         index = sms_receiver_add_submit_pdu( modem->sms_receiver, pdu );
   1712         if (index < 0) {
   1713             D( "%s: could not add submit PDU\n", __FUNCTION__ );
   1714             break;
   1715         }
   1716         /* the PDU is now owned by the receiver */
   1717         pdu = NULL;
   1718 
   1719         if (index > 0) {
   1720             SmsAddressRec  from[1];
   1721             char           temp[12];
   1722             SmsPDU*        deliver;
   1723             int            nn;
   1724 
   1725             snprintf( temp, sizeof(temp), PHONE_PREFIX "%d", modem->base_port );
   1726             sms_address_from_str( from, temp, strlen(temp) );
   1727 
   1728             deliver = sms_receiver_create_deliver( modem->sms_receiver, index, from );
   1729             if (deliver == NULL) {
   1730                 D( "%s: could not create deliver PDUs for SMS index %d\n",
   1731                    __FUNCTION__, index );
   1732                 break;
   1733             }
   1734 
   1735             for (nn = 0; deliver[nn] != NULL; nn++) {
   1736                 if ( remote_call_sms( number, modem->base_port, deliver[nn] ) < 0 ) {
   1737                     D( "%s: could not send SMS PDU to remote emulator\n",
   1738                        __FUNCTION__ );
   1739                     break;
   1740                 }
   1741             }
   1742 
   1743             smspdu_free_list(deliver);
   1744         }
   1745 
   1746     } while (0);
   1747 
   1748     if (pdu != NULL)
   1749         smspdu_free(pdu);
   1750 
   1751 #elif 1
   1752     SmsAddressRec  address;
   1753     char           number[16];
   1754     int            numlen;
   1755     int            len = strlen(cmd);
   1756     SmsPDU         pdu;
   1757 
   1758     /* get rid of trailing escape */
   1759     if (len > 0 && cmd[len-1] == 0x1a)
   1760         len -= 1;
   1761 
   1762     pdu = smspdu_create_from_hex( cmd, len );
   1763     if (pdu == NULL) {
   1764         D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd);
   1765         return "+CMS ERROR: INVALID SMS PDU";
   1766     }
   1767     if (smspdu_get_receiver_address(pdu, &address) < 0) {
   1768         D("%s: could not get SMS receiver address from '%s'\n",
   1769           __FUNCTION__, cmd);
   1770         return "+CMS ERROR: BAD SMS RECEIVER ADDRESS";
   1771     }
   1772     do {
   1773         numlen = sms_address_to_str( &address, number, sizeof(number) );
   1774         if (numlen > sizeof(number)-1)
   1775             break;
   1776 
   1777         number[numlen] = 0;
   1778         if ( remote_number_string_to_port( number ) < 0 )
   1779             break;
   1780 
   1781         if ( remote_call_sms( number, modem->base_port, pdu ) < 0 )
   1782         {
   1783             D("%s: could not send SMS PDU to remote emulator\n",
   1784               __FUNCTION__);
   1785             return "+CMS ERROR: NO EMULATOR RECEIVER";
   1786         }
   1787     } while (0);
   1788 #else
   1789     fprintf(stderr, "SMS<< %s\n", cmd);
   1790     SmsPDU  pdu = smspdu_create_from_hex( cmd, strlen(cmd) );
   1791     if (pdu == NULL) {
   1792         fprintf(stderr, "invalid SMS PDU ?: '%s'\n", cmd);
   1793     } else {
   1794         smspdu_dump(pdu, stderr);
   1795     }
   1796 #endif
   1797     return "+CMGS: 0\rOK\r";
   1798 }
   1799 
   1800 static const char*
   1801 handleChangeOrEnterPIN( const char*  cmd, AModem  modem )
   1802 {
   1803     assert( !memcmp( cmd, "+CPIN=", 6 ) );
   1804     cmd += 6;
   1805 
   1806     switch (asimcard_get_status(modem->sim)) {
   1807         case A_SIM_STATUS_ABSENT:
   1808             return "+CME ERROR: SIM ABSENT";
   1809 
   1810         case A_SIM_STATUS_NOT_READY:
   1811             return "+CME ERROR: SIM NOT READY";
   1812 
   1813         case A_SIM_STATUS_READY:
   1814             /* this may be a request to change the PIN */
   1815             {
   1816                 if (strlen(cmd) == 9 && cmd[4] == ',') {
   1817                     char  pin[5];
   1818                     memcpy( pin, cmd, 4 ); pin[4] = 0;
   1819 
   1820                     if ( !asimcard_check_pin( modem->sim, pin ) )
   1821                         return "+CME ERROR: BAD PIN";
   1822 
   1823                     memcpy( pin, cmd+5, 4 );
   1824                     asimcard_set_pin( modem->sim, pin );
   1825                     return "+CPIN: READY";
   1826                 }
   1827             }
   1828             break;
   1829 
   1830         case A_SIM_STATUS_PIN:   /* waiting for PIN */
   1831             if ( asimcard_check_pin( modem->sim, cmd ) )
   1832                 return "+CPIN: READY";
   1833             else
   1834                 return "+CME ERROR: BAD PIN";
   1835 
   1836         case A_SIM_STATUS_PUK:
   1837             if (strlen(cmd) == 9 && cmd[4] == ',') {
   1838                 char  puk[5];
   1839                 memcpy( puk, cmd, 4 );
   1840                 puk[4] = 0;
   1841                 if ( asimcard_check_puk( modem->sim, puk, cmd+5 ) )
   1842                     return "+CPIN: READY";
   1843                 else
   1844                     return "+CME ERROR: BAD PUK";
   1845             }
   1846             return "+CME ERROR: BAD PUK";
   1847 
   1848         default:
   1849             return "+CPIN: PH-NET PIN";
   1850     }
   1851 
   1852     return "+CME ERROR: BAD FORMAT";
   1853 }
   1854 
   1855 
   1856 static const char*
   1857 handleListCurrentCalls( const char*  cmd, AModem  modem )
   1858 {
   1859     int  nn;
   1860     amodem_begin_line( modem );
   1861     for (nn = 0; nn < modem->call_count; nn++) {
   1862         AVoiceCall  vcall = modem->calls + nn;
   1863         ACall       call  = &vcall->call;
   1864         if (call->mode == A_CALL_VOICE)
   1865             amodem_add_line( modem, "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n",
   1866                              call->id, call->dir, call->state, call->mode,
   1867                              call->multi, call->number, 129 );
   1868     }
   1869     return amodem_end_line( modem );
   1870 }
   1871 
   1872 /* Add a(n unsolicited) time response.
   1873  *
   1874  * retrieve the current time and zone in a format suitable
   1875  * for %CTZV: unsolicited message
   1876  *  "yy/mm/dd,hh:mm:ss(+/-)tz"
   1877  *   mm is 0-based
   1878  *   tz is in number of quarter-hours
   1879  *
   1880  * it seems reference-ril doesn't parse the comma (,) as anything else than a token
   1881  * separator, so use a column (:) instead, the Java parsing code won't see a difference
   1882  *
   1883  */
   1884 static void
   1885 amodem_addTimeUpdate( AModem  modem )
   1886 {
   1887     time_t       now = time(NULL);
   1888     struct tm    utc, local;
   1889     long         e_local, e_utc;
   1890     long         tzdiff;
   1891     char         tzname[64];
   1892 
   1893     tzset();
   1894 
   1895     utc   = *gmtime( &now );
   1896     local = *localtime( &now );
   1897 
   1898     e_local = local.tm_min + 60*(local.tm_hour + 24*local.tm_yday);
   1899     e_utc   = utc.tm_min   + 60*(utc.tm_hour   + 24*utc.tm_yday);
   1900 
   1901     if ( utc.tm_year < local.tm_year )
   1902         e_local += 24*60;
   1903     else if ( utc.tm_year > local.tm_year )
   1904         e_utc += 24*60;
   1905 
   1906     tzdiff = e_local - e_utc;  /* timezone offset in minutes */
   1907 
   1908    /* retrieve a zoneinfo-compatible name for the host timezone
   1909     */
   1910     {
   1911         char*  end = tzname + sizeof(tzname);
   1912         char*  p = bufprint_zoneinfo_timezone( tzname, end );
   1913         if (p >= end)
   1914             strcpy(tzname, "Unknown/Unknown");
   1915 
   1916         /* now replace every / in the timezone name by a "!"
   1917          * that's because the code that reads the CTZV line is
   1918          * dumb and treats a / as a field separator...
   1919          */
   1920         p = tzname;
   1921         while (1) {
   1922             p = strchr(p, '/');
   1923             if (p == NULL)
   1924                 break;
   1925             *p = '!';
   1926             p += 1;
   1927         }
   1928     }
   1929 
   1930    /* as a special extension, we append the name of the host's time zone to the
   1931     * string returned with %CTZ. the system should contain special code to detect
   1932     * and deal with this case (since it normally relied on the operator's country code
   1933     * which is hard to simulate on a general-purpose computer
   1934     */
   1935     amodem_add_line( modem, "%%CTZV: %02d/%02d/%02d:%02d:%02d:%02d%c%d:%d:%s\r\n",
   1936              (utc.tm_year + 1900) % 100, utc.tm_mon + 1, utc.tm_mday,
   1937              utc.tm_hour, utc.tm_min, utc.tm_sec,
   1938              (tzdiff >= 0) ? '+' : '-', (tzdiff >= 0 ? tzdiff : -tzdiff) / 15,
   1939              (local.tm_isdst > 0),
   1940              tzname );
   1941 }
   1942 
   1943 static const char*
   1944 handleEndOfInit( const char*  cmd, AModem  modem )
   1945 {
   1946     amodem_begin_line( modem );
   1947     amodem_addTimeUpdate( modem );
   1948     return amodem_end_line( modem );
   1949 }
   1950 
   1951 
   1952 static const char*
   1953 handleListPDPContexts( const char*  cmd, AModem  modem )
   1954 {
   1955     int  nn;
   1956     assert( !memcmp( cmd, "+CGACT?", 7 ) );
   1957     amodem_begin_line( modem );
   1958     for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) {
   1959         ADataContext  data = modem->data_contexts + nn;
   1960         if (!data->active)
   1961             continue;
   1962         amodem_add_line( modem, "+CGACT: %d,%d\r\n", data->id, data->active );
   1963     }
   1964     return amodem_end_line( modem );
   1965 }
   1966 
   1967 static const char*
   1968 handleDefinePDPContext( const char*  cmd, AModem  modem )
   1969 {
   1970     assert( !memcmp( cmd, "+CGDCONT=", 9 ) );
   1971     cmd += 9;
   1972     if (cmd[0] == '?') {
   1973         /* +CGDCONT=? is used to query the ranges of supported PDP Contexts.
   1974          * We only really support IP ones in the emulator, so don't try to
   1975          * fake PPP ones.
   1976          */
   1977         return "+CGDCONT: (1-1),\"IP\",,,(0-2),(0-4)\r\n";
   1978     } else {
   1979         /* template is +CGDCONT=<id>,"<type>","<apn>",,0,0 */
   1980         int              id = cmd[0] - '1';
   1981         ADataType        type;
   1982         char             apn[32];
   1983         ADataContext     data;
   1984 
   1985         if ((unsigned)id > 3)
   1986             goto BadCommand;
   1987 
   1988         if ( !memcmp( cmd+1, ",\"IP\",\"", 7 ) ) {
   1989             type = A_DATA_IP;
   1990             cmd += 8;
   1991         } else if ( !memcmp( cmd+1, ",\"PPP\",\"", 8 ) ) {
   1992             type = A_DATA_PPP;
   1993             cmd += 9;
   1994         } else
   1995             goto BadCommand;
   1996 
   1997         {
   1998             const char*  p = strchr( cmd, '"' );
   1999             int          len;
   2000             if (p == NULL)
   2001                 goto BadCommand;
   2002             len = (int)( p - cmd );
   2003             if (len > sizeof(apn)-1 )
   2004                 len = sizeof(apn)-1;
   2005             memcpy( apn, cmd, len );
   2006             apn[len] = 0;
   2007         }
   2008 
   2009         data = modem->data_contexts + id;
   2010 
   2011         data->id     = id + 1;
   2012         data->active = 1;
   2013         data->type   = type;
   2014         memcpy( data->apn, apn, sizeof(data->apn) );
   2015     }
   2016     return NULL;
   2017 BadCommand:
   2018     return "ERROR: BAD COMMAND";
   2019 }
   2020 
   2021 static const char*
   2022 handleQueryPDPContext( const char* cmd, AModem modem )
   2023 {
   2024     int  nn;
   2025     amodem_begin_line(modem);
   2026     for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) {
   2027         ADataContext  data = modem->data_contexts + nn;
   2028         if (!data->active)
   2029             continue;
   2030         amodem_add_line( modem, "+CGDCONT: %d,\"%s\",\"%s\",\"%s\",0,0\r\n",
   2031                          data->id,
   2032                          data->type == A_DATA_IP ? "IP" : "PPP",
   2033                          data->apn,
   2034                          /* Note: For now, hard-code the IP address of our
   2035                           *       network interface
   2036                           */
   2037                          data->type == A_DATA_IP ? "10.0.2.15" : "");
   2038     }
   2039     return amodem_end_line(modem);
   2040 }
   2041 
   2042 static const char*
   2043 handleStartPDPContext( const char*  cmd, AModem  modem )
   2044 {
   2045     /* XXX: TODO: handle PDP start appropriately */
   2046     return NULL;
   2047 }
   2048 
   2049 
   2050 static void
   2051 remote_voice_call_event( void*  _vcall, int  success )
   2052 {
   2053     AVoiceCall  vcall = _vcall;
   2054     AModem      modem = vcall->modem;
   2055 
   2056     /* NOTE: success only means we could send the "gsm in new" command
   2057      * to the remote emulator, nothing more */
   2058 
   2059     if (!success) {
   2060         /* aargh, the remote emulator probably quitted at that point */
   2061         amodem_free_call(modem, vcall);
   2062         amodem_send_calls_update(modem);
   2063     }
   2064 }
   2065 
   2066 
   2067 static void
   2068 voice_call_event( void*  _vcall )
   2069 {
   2070     AVoiceCall  vcall = _vcall;
   2071     ACall       call  = &vcall->call;
   2072 
   2073     switch (call->state) {
   2074         case A_CALL_DIALING:
   2075             call->state = A_CALL_ALERTING;
   2076 
   2077             if (vcall->is_remote) {
   2078                 if ( remote_call_dial( call->number,
   2079                                        vcall->modem->base_port,
   2080                                        remote_voice_call_event, vcall ) < 0 )
   2081                 {
   2082                    /* we could not connect, probably because the corresponding
   2083                     * emulator is not running, so simply destroy this call.
   2084                     * XXX: should we send some sort of message to indicate BAD NUMBER ? */
   2085                     /* it seems the Android code simply waits for changes in the list   */
   2086                     amodem_free_call( vcall->modem, vcall );
   2087                 }
   2088             } else {
   2089                /* this is not a remote emulator number, so just simulate
   2090                 * a small ringing delay */
   2091                 sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_ALERT,
   2092                                voice_call_event, vcall );
   2093             }
   2094             break;
   2095 
   2096         case A_CALL_ALERTING:
   2097             call->state = A_CALL_ACTIVE;
   2098             break;
   2099 
   2100         default:
   2101             assert( 0 && "unreachable event call state" );
   2102     }
   2103     amodem_send_calls_update(vcall->modem);
   2104 }
   2105 
   2106 static int amodem_is_emergency( AModem modem, const char *number )
   2107 {
   2108     int i;
   2109 
   2110     if (!number) return 0;
   2111     for (i = 0; i < MAX_EMERGENCY_NUMBERS; i++) {
   2112         if ( modem->emergency_numbers[i] && !strcmp( number, modem->emergency_numbers[i] )) break;
   2113     }
   2114 
   2115     if (i < MAX_EMERGENCY_NUMBERS) return 1;
   2116 
   2117     return 0;
   2118 }
   2119 
   2120 static const char*
   2121 handleDial( const char*  cmd, AModem  modem )
   2122 {
   2123     AVoiceCall  vcall = amodem_alloc_call( modem );
   2124     ACall       call  = &vcall->call;
   2125     int         len;
   2126 
   2127     if (call == NULL)
   2128         return "ERROR: TOO MANY CALLS";
   2129 
   2130     assert( cmd[0] == 'D' );
   2131     call->dir   = A_CALL_OUTBOUND;
   2132     call->state = A_CALL_DIALING;
   2133     call->mode  = A_CALL_VOICE;
   2134     call->multi = 0;
   2135 
   2136     cmd += 1;
   2137     len  = strlen(cmd);
   2138     if (len > 0 && cmd[len-1] == ';')
   2139         len--;
   2140     if (len >= sizeof(call->number))
   2141         len = sizeof(call->number)-1;
   2142 
   2143     /* Converts 4, 7, and 10 digits number to 11 digits */
   2144     if (len == 10 && !strncmp(cmd, PHONE_PREFIX+1, 6)) {
   2145         memcpy( call->number, PHONE_PREFIX, 1 );
   2146         memcpy( call->number+1, cmd, len );
   2147         call->number[len+1] = 0;
   2148     } else if (len == 7 && !strncmp(cmd, PHONE_PREFIX+4, 3)) {
   2149         memcpy( call->number, PHONE_PREFIX, 4 );
   2150         memcpy( call->number+4, cmd, len );
   2151         call->number[len+4] = 0;
   2152     } else if (len == 4) {
   2153         memcpy( call->number, PHONE_PREFIX, 7 );
   2154         memcpy( call->number+7, cmd, len );
   2155         call->number[len+7] = 0;
   2156     } else {
   2157         memcpy( call->number, cmd, len );
   2158         call->number[len] = 0;
   2159     }
   2160 
   2161     amodem_begin_line( modem );
   2162     if (amodem_is_emergency(modem, call->number)) {
   2163         modem->in_emergency_mode = 1;
   2164         amodem_add_line( modem, "+WSOS: 1" );
   2165     }
   2166     vcall->is_remote = (remote_number_string_to_port(call->number) > 0);
   2167 
   2168     vcall->timer = sys_timer_create();
   2169     sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_DIAL,
   2170                    voice_call_event, vcall );
   2171 
   2172     return amodem_end_line( modem );
   2173 }
   2174 
   2175 
   2176 static const char*
   2177 handleAnswer( const char*  cmd, AModem  modem )
   2178 {
   2179     int  nn;
   2180     for (nn = 0; nn < modem->call_count; nn++) {
   2181         AVoiceCall  vcall = modem->calls + nn;
   2182         ACall       call  = &vcall->call;
   2183 
   2184         if (cmd[0] == 'A') {
   2185             if (call->state == A_CALL_INCOMING) {
   2186                 acall_set_state( vcall, A_CALL_ACTIVE );
   2187             }
   2188             else if (call->state == A_CALL_ACTIVE) {
   2189                 acall_set_state( vcall, A_CALL_HELD );
   2190             }
   2191         } else if (cmd[0] == 'H') {
   2192             /* ATH: hangup, since user is busy */
   2193             if (call->state == A_CALL_INCOMING) {
   2194                 amodem_free_call( modem, vcall );
   2195                 break;
   2196             }
   2197         }
   2198     }
   2199     return NULL;
   2200 }
   2201 
   2202 int android_snapshot_update_time = 1;
   2203 int android_snapshot_update_time_request = 0;
   2204 
   2205 static const char*
   2206 handleSignalStrength( const char*  cmd, AModem  modem )
   2207 {
   2208     amodem_begin_line( modem );
   2209 
   2210     /* Sneak time updates into the SignalStrength request, because it's periodic.
   2211      * Ideally, we'd be able to prod the guest into asking immediately on restore
   2212      * from snapshot, but that'd require a driver.
   2213      */
   2214     if ( android_snapshot_update_time && android_snapshot_update_time_request ) {
   2215       amodem_addTimeUpdate( modem );
   2216       android_snapshot_update_time_request = 0;
   2217     }
   2218 
   2219     // rssi = 0 (<-113dBm) 1 (<-111) 2-30 (<-109--53) 31 (>=-51) 99 (?!)
   2220     // ber (bit error rate) - always 99 (unknown), apparently.
   2221     // TODO: return 99 if modem->radio_state==A_RADIO_STATE_OFF, once radio_state is in snapshot.
   2222     int rssi = modem->rssi;
   2223     int ber = modem->ber;
   2224     rssi = (0 > rssi && rssi > 31) ? 99 : rssi ;
   2225     ber = (0 > ber && ber > 7 ) ? 99 : ber;
   2226     amodem_add_line( modem, "+CSQ: %i,%i\r\n", rssi, ber );
   2227     return amodem_end_line( modem );
   2228 }
   2229 
   2230 static const char*
   2231 handleHangup( const char*  cmd, AModem  modem )
   2232 {
   2233     if ( !memcmp(cmd, "+CHLD=", 6) ) {
   2234         int  nn;
   2235         cmd += 6;
   2236         switch (cmd[0]) {
   2237             case '0':  /* release all held, and set busy for waiting calls */
   2238                 for (nn = 0; nn < modem->call_count; nn++) {
   2239                     AVoiceCall  vcall = modem->calls + nn;
   2240                     ACall       call  = &vcall->call;
   2241                     if (call->mode != A_CALL_VOICE)
   2242                         continue;
   2243                     if (call->state == A_CALL_HELD    ||
   2244                         call->state == A_CALL_WAITING ||
   2245                         call->state == A_CALL_INCOMING) {
   2246                         amodem_free_call(modem, vcall);
   2247                         nn--;
   2248                     }
   2249                 }
   2250                 break;
   2251 
   2252             case '1':
   2253                 if (cmd[1] == 0) { /* release all active, accept held one */
   2254                     for (nn = 0; nn < modem->call_count; nn++) {
   2255                         AVoiceCall  vcall = modem->calls + nn;
   2256                         ACall       call  = &vcall->call;
   2257                         if (call->mode != A_CALL_VOICE)
   2258                             continue;
   2259                         if (call->state == A_CALL_ACTIVE) {
   2260                             amodem_free_call(modem, vcall);
   2261                             nn--;
   2262                         }
   2263                         else if (call->state == A_CALL_HELD     ||
   2264                                  call->state == A_CALL_WAITING) {
   2265                             acall_set_state( vcall, A_CALL_ACTIVE );
   2266                         }
   2267                     }
   2268                 } else {  /* release specific call */
   2269                     int  id = cmd[1] - '0';
   2270                     AVoiceCall  vcall = amodem_find_call( modem, id );
   2271                     if (vcall != NULL)
   2272                         amodem_free_call( modem, vcall );
   2273                 }
   2274                 break;
   2275 
   2276             case '2':
   2277                 if (cmd[1] == 0) {  /* place all active on hold, accept held or waiting one */
   2278                     for (nn = 0; nn < modem->call_count; nn++) {
   2279                         AVoiceCall  vcall = modem->calls + nn;
   2280                         ACall       call  = &vcall->call;
   2281                         if (call->mode != A_CALL_VOICE)
   2282                             continue;
   2283                         if (call->state == A_CALL_ACTIVE) {
   2284                             acall_set_state( vcall, A_CALL_HELD );
   2285                         }
   2286                         else if (call->state == A_CALL_HELD     ||
   2287                                  call->state == A_CALL_WAITING) {
   2288                             acall_set_state( vcall, A_CALL_ACTIVE );
   2289                         }
   2290                     }
   2291                 } else {  /* place all active on hold, except a specific one */
   2292                     int   id = cmd[1] - '0';
   2293                     for (nn = 0; nn < modem->call_count; nn++) {
   2294                         AVoiceCall  vcall = modem->calls + nn;
   2295                         ACall       call  = &vcall->call;
   2296                         if (call->mode != A_CALL_VOICE)
   2297                             continue;
   2298                         if (call->state == A_CALL_ACTIVE && call->id != id) {
   2299                             acall_set_state( vcall, A_CALL_HELD );
   2300                         }
   2301                     }
   2302                 }
   2303                 break;
   2304 
   2305             case '3':  /* add a held call to the conversation */
   2306                 for (nn = 0; nn < modem->call_count; nn++) {
   2307                     AVoiceCall  vcall = modem->calls + nn;
   2308                     ACall       call  = &vcall->call;
   2309                     if (call->mode != A_CALL_VOICE)
   2310                         continue;
   2311                     if (call->state == A_CALL_HELD) {
   2312                         acall_set_state( vcall, A_CALL_ACTIVE );
   2313                         break;
   2314                     }
   2315                 }
   2316                 break;
   2317 
   2318             case '4':  /* connect the two calls */
   2319                 for (nn = 0; nn < modem->call_count; nn++) {
   2320                     AVoiceCall  vcall = modem->calls + nn;
   2321                     ACall       call  = &vcall->call;
   2322                     if (call->mode != A_CALL_VOICE)
   2323                         continue;
   2324                     if (call->state == A_CALL_HELD) {
   2325                         acall_set_state( vcall, A_CALL_ACTIVE );
   2326                         break;
   2327                     }
   2328                 }
   2329                 break;
   2330         }
   2331     }
   2332     else
   2333         return "ERROR: BAD COMMAND";
   2334 
   2335     return NULL;
   2336 }
   2337 
   2338 
   2339 /* a function used to deal with a non-trivial request */
   2340 typedef const char*  (*ResponseHandler)(const char*  cmd, AModem  modem);
   2341 
   2342 static const struct {
   2343     const char*      cmd;     /* command coming from libreference-ril.so, if first
   2344                                  character is '!', then the rest is a prefix only */
   2345 
   2346     const char*      answer;  /* default answer, NULL if needs specific handling or
   2347                                  if OK is good enough */
   2348 
   2349     ResponseHandler  handler; /* specific handler, ignored if 'answer' is not NULL,
   2350                                  NULL if OK is good enough */
   2351 } sDefaultResponses[] =
   2352 {
   2353     /* see onRadioPowerOn() */
   2354     { "%CPHS=1", NULL, NULL },
   2355     { "%CTZV=1", NULL, NULL },
   2356 
   2357     /* see onSIMReady() */
   2358     { "+CSMS=1", "+CSMS: 1, 1, 1", NULL },
   2359     { "+CNMI=1,2,2,1,1", NULL, NULL },
   2360 
   2361     /* see requestRadioPower() */
   2362     { "+CFUN=0", NULL, handleRadioPower },
   2363     { "+CFUN=1", NULL, handleRadioPower },
   2364 
   2365     { "+CTEC=?", "+CTEC: 0,1,2,3", NULL }, /* Query available Techs */
   2366     { "!+CTEC", NULL, handleTech }, /* Set/get current Technology and preferred mode */
   2367 
   2368     { "+WRMP=?", "+WRMP: 0,1,2", NULL }, /* Query Roam Preference */
   2369     { "!+WRMP", NULL, handleRoamPref }, /* Set/get Roam Preference */
   2370 
   2371     { "+CCSS=?", "+CTEC: 0,1", NULL }, /* Query available subscription sources */
   2372     { "!+CCSS", NULL, handleSubscriptionSource }, /* Set/Get current subscription source */
   2373 
   2374     { "+WSOS=?", "+WSOS: 0", NULL}, /* Query supported +WSOS values */
   2375     { "!+WSOS=", NULL, handleEmergencyMode },
   2376 
   2377     { "+WPRL?", NULL, handlePrlVersion }, /* Query the current PRL version */
   2378 
   2379     /* see requestOrSendPDPContextList() */
   2380     { "+CGACT?", NULL, handleListPDPContexts },
   2381 
   2382     /* see requestOperator() */
   2383     { "+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", NULL, handleRequestOperator },
   2384 
   2385     /* see requestQueryNetworkSelectionMode() */
   2386     { "!+COPS", NULL, handleOperatorSelection },
   2387 
   2388     /* see requestGetCurrentCalls() */
   2389     { "+CLCC", NULL, handleListCurrentCalls },
   2390 
   2391     /* see requestWriteSmsToSim() */
   2392     { "!+CMGW=", NULL, handleSendSMStoSIM },
   2393 
   2394     /* see requestHangup() */
   2395     { "!+CHLD=", NULL, handleHangup },
   2396 
   2397     /* see requestSignalStrength() */
   2398     { "+CSQ", NULL, handleSignalStrength },
   2399 
   2400     /* see requestRegistrationState() */
   2401     { "!+CREG", NULL, handleNetworkRegistration },
   2402     { "!+CGREG", NULL, handleNetworkRegistration },
   2403 
   2404     /* see requestSendSMS() */
   2405     { "!+CMGS=", NULL, handleSendSMS },
   2406 
   2407     /* see requestSetupDefaultPDP() */
   2408     { "%CPRIM=\"GMM\",\"CONFIG MULTISLOT_CLASS=<10>\"", NULL, NULL },
   2409     { "%DATA=2,\"UART\",1,,\"SER\",\"UART\",0", NULL, NULL },
   2410 
   2411     { "!+CGDCONT=", NULL, handleDefinePDPContext },
   2412     { "+CGDCONT?", NULL, handleQueryPDPContext },
   2413 
   2414     { "+CGQREQ=1", NULL, NULL },
   2415     { "+CGQMIN=1", NULL, NULL },
   2416     { "+CGEREP=1,0", NULL, NULL },
   2417     { "+CGACT=1,0", NULL, NULL },
   2418     { "D*99***1#", NULL, handleStartPDPContext },
   2419 
   2420     /* see requestDial() */
   2421     { "!D", NULL, handleDial },  /* the code says that success/error is ignored, the call state will
   2422                               be polled through +CLCC instead */
   2423 
   2424     /* see requestSMSAcknowledge() */
   2425     { "+CNMA=1", NULL, NULL },
   2426     { "+CNMA=2", NULL, NULL },
   2427 
   2428     /* see requestSIM_IO() */
   2429     { "!+CRSM=", NULL, handleSIM_IO },
   2430 
   2431     /* see onRequest() */
   2432     { "+CHLD=0", NULL, handleHangup },
   2433     { "+CHLD=1", NULL, handleHangup },
   2434     { "+CHLD=2", NULL, handleHangup },
   2435     { "+CHLD=3", NULL, handleHangup },
   2436     { "A", NULL, handleAnswer },  /* answer the call */
   2437     { "H", NULL, handleAnswer },  /* user is busy */
   2438     { "!+VTS=", NULL, handleSetDialTone },
   2439     { "+CIMI", OPERATOR_HOME_MCCMNC "000000000", NULL },   /* request internation subscriber identification number */
   2440     { "+CGSN", "000000000000000", NULL },   /* request model version */
   2441     { "+CUSD=2",NULL, NULL }, /* Cancel USSD */
   2442     { "+COPS=0", NULL, handleOperatorSelection }, /* set network selection to automatic */
   2443     { "!+CMGD=", NULL, handleDeleteSMSonSIM }, /* delete SMS on SIM */
   2444     { "!+CPIN=", NULL, handleChangeOrEnterPIN },
   2445 
   2446     /* see getSIMStatus() */
   2447     { "+CPIN?", NULL, handleSIMStatusReq },
   2448     { "+CNMI?", "+CNMI: 1,2,2,1,1", NULL },
   2449 
   2450     /* see isRadioOn() */
   2451     { "+CFUN?", NULL, handleRadioPowerReq },
   2452 
   2453     /* see initializeCallback() */
   2454     { "E0Q0V1", NULL, NULL },
   2455     { "S0=0", NULL, NULL },
   2456     { "+CMEE=1", NULL, NULL },
   2457     { "+CCWA=1", NULL, NULL },
   2458     { "+CMOD=0", NULL, NULL },
   2459     { "+CMUT=0", NULL, NULL },
   2460     { "+CSSN=0,1", NULL, NULL },
   2461     { "+COLP=0", NULL, NULL },
   2462     { "+CSCS=\"HEX\"", NULL, NULL },
   2463     { "+CUSD=1", NULL, NULL },
   2464     { "+CGEREP=1,0", NULL, NULL },
   2465     { "+CMGF=0", NULL, handleEndOfInit },  /* now is a goof time to send the current tme and timezone */
   2466     { "%CPI=3", NULL, NULL },
   2467     { "%CSTAT=1", NULL, NULL },
   2468 
   2469     /* end of list */
   2470     {NULL, NULL, NULL}
   2471 };
   2472 
   2473 
   2474 #define  REPLY(str)  do { const char*  s = (str); R(">> %s\n", quote(s)); return s; } while (0)
   2475 
   2476 const char*  amodem_send( AModem  modem, const char*  cmd )
   2477 {
   2478     const char*  answer;
   2479 
   2480     if ( modem->wait_sms != 0 ) {
   2481         modem->wait_sms = 0;
   2482         R( "SMS<< %s\n", quote(cmd) );
   2483         answer = handleSendSMSText( cmd, modem );
   2484         REPLY(answer);
   2485     }
   2486 
   2487     /* everything that doesn't start with 'AT' is not a command, right ? */
   2488     if ( cmd[0] != 'A' || cmd[1] != 'T' || cmd[2] == 0 ) {
   2489         /* R( "-- %s\n", quote(cmd) ); */
   2490         return NULL;
   2491     }
   2492     R( "<< %s\n", quote(cmd) );
   2493 
   2494     cmd += 2;
   2495 
   2496     /* TODO: implement command handling */
   2497     {
   2498         int  nn, found = 0;
   2499 
   2500         for (nn = 0; ; nn++) {
   2501             const char*  scmd = sDefaultResponses[nn].cmd;
   2502 
   2503             if (!scmd) /* end of list */
   2504                 break;
   2505 
   2506             if (scmd[0] == '!') { /* prefix match */
   2507                 int  len = strlen(++scmd);
   2508 
   2509                 if ( !memcmp( scmd, cmd, len ) ) {
   2510                     found = 1;
   2511                     break;
   2512                 }
   2513             } else { /* full match */
   2514                 if ( !strcmp( scmd, cmd ) ) {
   2515                     found = 1;
   2516                     break;
   2517                 }
   2518             }
   2519         }
   2520 
   2521         if ( !found )
   2522         {
   2523             D( "** UNSUPPORTED COMMAND **\n" );
   2524             REPLY( "ERROR: UNSUPPORTED" );
   2525         }
   2526         else
   2527         {
   2528             const char*      answer  = sDefaultResponses[nn].answer;
   2529             ResponseHandler  handler = sDefaultResponses[nn].handler;
   2530 
   2531             if ( answer != NULL ) {
   2532                 REPLY( amodem_printf( modem, "%s\rOK", answer ) );
   2533             }
   2534 
   2535             if (handler == NULL) {
   2536                 REPLY( "OK" );
   2537             }
   2538 
   2539             answer = handler( cmd, modem );
   2540             if (answer == NULL)
   2541                 REPLY( "OK" );
   2542 
   2543             if ( !memcmp( answer, "> ", 2 )     ||
   2544                  !memcmp( answer, "ERROR", 5 )  ||
   2545                  !memcmp( answer, "+CME ERROR", 6 ) )
   2546             {
   2547                 REPLY( answer );
   2548             }
   2549 
   2550             if (answer != modem->out_buff)
   2551                 REPLY( amodem_printf( modem, "%s\rOK", answer ) );
   2552 
   2553             strcat( modem->out_buff, "\rOK" );
   2554             REPLY( answer );
   2555         }
   2556     }
   2557 }
   2558